diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 19c9bd00d..993c31ea3 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -13,7 +13,7 @@ Please DO NOT OPEN AN ISSUE: - If your issue is a flashing issue, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) - If your issue is compilation problem, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) - If your issue has been addressed before (i.e., duplicated issue), please ask in the original issue - - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the [FAQ](https://tasmota.github.io/docs/#/help/FAQ) and [Troubleshooting](https://tasmota.github.io/docs/#/help/Troubleshooting) + - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the [FAQ](https://tasmota.github.io/docs/FAQ) and [Troubleshooting](https://tasmota.github.io/docs/Troubleshooting) Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. @@ -29,7 +29,7 @@ _Make sure your have performed every step and checked the applicable boxes befor - [ ] Read the [Contributing Guide and Policy](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md) and [the Code of Conduct](https://github.com/arendst/Tasmota/blob/development/CODE_OF_CONDUCT.md) - [ ] Searched the problem in [issues](https://github.com/arendst/Tasmota/issues) -- [ ] Searched the problem in the [docs](https://tasmota.github.io/docs/#/help/FAQ) +- [ ] Searched the problem in the [docs](https://tasmota.github.io/docs/FAQ) - [ ] Searched the problem in the [forum](https://groups.google.com/d/forum/sonoffusers) - [ ] Searched the problem in the [chat](https://discord.gg/Ks2Kzd4) - [ ] Device used (e.g., Sonoff Basic): _____ diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 9c78f2c84..225df7f86 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,7 @@ blank_issues_enabled: false contact_links: - name: Tasmota Docs - url: https://tasmota.github.io/docs/#/ + url: https://tasmota.github.io/docs about: All the information related to Tasmota - name: Tasmota Support Chat url: https://discord.gg/Ks2Kzd4 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2839ba02b..8599df428 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,6 +6,8 @@ - [ ] The pull request is done against the latest dev branch - [ ] Only relevant files were touched - [ ] Only one feature/fix was added per PR. - - [ ] The code change is tested and works on core Tasmota_core_stage - - [ ] The code change pass travis tests. **Your PR cannot be merged unless tests pass** + - [ ] The code change is tested and works on core ESP8266 V.2.7.1 + - [ ] The code change is tested and works on core ESP32 V.1.12.0 - [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). + +_NOTE: The code change must pass CI tests. **Your PR cannot be merged unless tests pass**_ diff --git a/.github/issue-close-app.yml b/.github/issue-close-app.yml index d222d8980..6a6947a9c 100644 --- a/.github/issue-close-app.yml +++ b/.github/issue-close-app.yml @@ -19,7 +19,7 @@ comment: >- [Support Information](https://github.com/arendst/Sonoff-Tasmota/blob/development/SUPPORT.md) - [Wiki](https://tasmota.github.io/docs/#/) for more information. + [Wiki](https://tasmota.github.io/docs/) for more information. [Chat](https://discord.gg/Ks2Kzd4) for more user experience. diff --git a/.github/workflows/CI_github.yml b/.github/workflows/CI_github.yml new file mode 100644 index 000000000..806952c3a --- /dev/null +++ b/.github/workflows/CI_github.yml @@ -0,0 +1,441 @@ +name: Tasmota CI + +on: [push, pull_request] + +jobs: + tasmota: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota + + tasmota-minimal: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-minimal + + tasmota-lite: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-lite + + tasmota-knx: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-knx + + tasmota-sensors: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-sensors + + + tasmota-display: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-display + + tasmota-ir: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-ir + + tasmota-BG: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-BG + + tasmota-BR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-BR + + tasmota-CN: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-CN + + tasmota-CZ: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-CZ + + tasmota-DE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-DE + + tasmota-ES: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-ES + + + tasmota-FR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-FR + + tasmota-GR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-GR + + tasmota-HE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-HE + + tasmota-HU: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-HU + + tasmota-IT: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-IT + + tasmota-KO: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-KO + + tasmota-NL: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-NL + + tasmota-PL: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-PL + + tasmota-PT: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-PT + + tasmota-RO: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-RO + + tasmota-RU: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-RU + + tasmota-SE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-SE + + tasmota-SK: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-SK + + tasmota-TR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-TR + + tasmota-TW: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-TW + + tasmota-UK: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-UK diff --git a/.github/workflows/CI_github_ESP32.yml b/.github/workflows/CI_github_ESP32.yml new file mode 100644 index 000000000..28221fb20 --- /dev/null +++ b/.github/workflows/CI_github_ESP32.yml @@ -0,0 +1,516 @@ +name: Tasmota ESP32 CI + +on: [push, pull_request] + +jobs: + tasmota32: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32 + + + tasmota32-webcam: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-webcam + + tasmota32-minimal: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-minimal + + tasmota32-lite: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-lite + + tasmota32-knx: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-knx + + tasmota32-sensors: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-sensors + + tasmota32-display: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-display + + tasmota32-ir: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-ir + + tasmota32-BG: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-BG + + tasmota32-BR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-BR + + tasmota32-CN: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-CN + + tasmota32-CZ: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-CZ + + tasmota32-DE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-DE + + tasmota32-ES: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-ES + + + tasmota32-FR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-FR + + tasmota32-GR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-GR + + tasmota32-HE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-HE + + tasmota32-HU: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-HU + + tasmota32-IT: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-IT + + tasmota32-KO: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-KO + + tasmota32-NL: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-NL + + tasmota32-PL: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-PL + + tasmota32-PT: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-PT + + tasmota32-RO: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-RO + + tasmota32-RU: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-RU + + tasmota32-SE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-SE + + tasmota32-SK: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-SK + + tasmota32-TR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-TR + + tasmota32-TW: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-TW + + tasmota32-UK: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + cp platformio_override_sample.ini platformio_override.ini + platformio run -e tasmota32-UK diff --git a/.gitignore b/.gitignore index 500f720ec..5baf6b919 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ tasmota*.bin tasmota*.bin.gz tasmota*.map platformio_override.ini +platformio_tasmota_cenv.ini ## Visual Studio Code specific ###### .vscode @@ -24,3 +25,4 @@ platformio_override.ini .vscode/c_cpp_properties.json .vscode/launch.json *.bak +*.code-workspace diff --git a/.travis.yml b/.travis.yml.off similarity index 100% rename from .travis.yml rename to .travis.yml.off diff --git a/API.md b/API.md index f10ae2c93..7bb77735b 100644 --- a/API.md +++ b/API.md @@ -2,7 +2,7 @@ # Basic API information -Tasmota can easily be extended by developers using provided function pointers as callback Ids. This document lists the available callback function Ids. Read [Sensor API](https://tasmota.github.io/docs/#/Sensor-API) for more information. +Tasmota can easily be extended by developers using provided function pointers as callback Ids. This document lists the available callback function Ids. Read [Sensor API](https://tasmota.github.io/docs/Sensor-API) for more information. Callback availability can be checked by searching for either XdrvCall, XsnsCall, XdspCall, XnrgCall and XlgtCall. diff --git a/BUILDS.md b/BUILDS.md index 0134d5fd7..7e56619f8 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -2,7 +2,7 @@ | Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks |-----------------------|---------|-------|--------|-----|---------|----|---------|-------- -| MY_LANGUAGE en-GB | x | x | x | x | x | x | x | +| MY_LANGUAGE en_GB | x | x | x | x | x | x | x | | USE_ARDUINO_OTA | - | - | - | - | - | - | - | | USE_DOMOTICZ | - | - | x | x | x | x | - | | USE_HOME_ASSISTANT | - | - | x | x | x | x | - | @@ -71,7 +71,7 @@ | | | | | | | | | | USE_ADC_VCC | x | x | - | - | - | - | - | | USE_COUNTER | - | - | x | x | x | x | x | -| USE_DS18x20 | - | - | x | x | x | - | x | +| USE_DS18x20 | - | - | x | x | x | x | x | | USE_DHT | - | - | x | x | x | x | x | | USE_MAX31855 | - | - | - | - | x | - | - | | USE_MAX31865 | - | - | - | - | - | - | - | @@ -117,6 +117,8 @@ | USE_AHT1x | - | - | - | - | - | - | - | | USE_HDC1080 | - | - | - | - | - | - | - | | USE_WEMOS_MOTOR_V1 | - | - | - | - | x | - | - | +| USE_IAQ | - | - | - | - | x | - | - | +| USE_AS3935 | - | - | - | - | x | - | - | | | | | | | | | | | Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks | USE_SPI | - | - | - | - | - | - | x | @@ -134,6 +136,8 @@ | USE_GPS | - | - | - | - | - | - | - | | USE_HM10 | - | - | - | - | x | - | - | | USE_HRXL | - | - | - | - | x | - | - | +| USE_TASMOTA_SLAVE | - | - | - | - | - | - | - | +| USE_OPENTHERM | - | - | - | - | - | - | - | | | | | | | | | | | USE_NRF24 | - | - | - | - | - | - | - | | USE_MIBLE | - | - | - | - | - | - | - | @@ -147,11 +151,11 @@ | USE_TM1638 | - | - | - | - | x | - | - | | USE_HX711 | - | - | - | - | x | - | - | | USE_TX2x_WIND_SENSOR | - | - | - | - | - | - | - | +| USE_WINDMETER | - | - | - | - | - | - | - | | USE_RC_SWITCH | - | - | - | - | x | - | - | | USE_RF_SENSOR | - | - | - | - | x | - | - | AlectoV2 only | USE_HRE | - | - | - | - | x | - | - | | USE_A4988_STEPPER | - | - | - | - | - | - | - | -| USE_TASMOTA_SLAVE | - | - | - | - | - | - | - | Experimental | | | | | | | | | | Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks | USE_DISPLAY | - | - | - | - | - | - | x | @@ -165,3 +169,10 @@ | USE_DISPLAY_ILI9488 | - | - | - | - | - | - | - | | USE_DISPLAY_SSD1351 | - | - | - | - | - | - | - | | USE_DISPLAY_RA8876 | - | - | - | - | - | - | - | + +## Additional Features and Sensors on ESP32 + +| Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | webcam | Remarks +|-----------------------|---------|-------|--------|-----|---------|----|---------|--------|-------- +| USE_MI_ESP32 | - | - | - | - | - | - | - | - | +| USE_WEBCAM | - | - | - | - | - | - | - | x | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 43cead24a..26db830ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Everybody is welcome and invited to contribute to Tasmota Project by: * Testing newly released features and reporting issues. * Providing Pull Requests (Features, Proof of Concepts, Language files or Fixes) -* Contributing missing documentation for features and devices in our [documentation](https://tasmota.github.io/docs/#/Contributing) +* Contributing missing documentation for features and devices in our [documentation](https://tasmota.github.io/docs/Contributing) This document describes rules that are in effect for this repository, meant for handling issues by contributors in the issue tracker and PRs. diff --git a/Device_Groups.md b/Device_Groups.md deleted file mode 100644 index 40bca68a7..000000000 --- a/Device_Groups.md +++ /dev/null @@ -1,43 +0,0 @@ -# Device Groups - -The device groups module provides a framework to allow multiple devices to be in a group with values such as power, light color/temperature/brightness, PWM values, sensor values, etc. shared with other devices in the group. For example, with multiple light modules in a device group, the light settings can be changed on one module and the settings will automatically be changed on the other light modules. Dimmer switch modules could be in a device group with light modules and the dimmer switch could control the power, brightness and colors of all the lights in the group. Multiple dimmer switches could be in a device group to form a 3-way/4-way dimmer switch. - -UDP multicasts, followed by UDP unicasts if necessary, are used to send updates to all devices so updates are fast. There is no need for an MQTT server but all the devices in a group must be on the same IP network. - -To include device groups support in the build, define USE_DEVICE_GROUPS in your user_config_override. This adds 3.5K to the code size. All devices in a group must be running firmware with device group support and have device groups enabled. - -To enable device groups, execute the command SetOption85 1. - - -## Device Groups Operation - -The device group name is the MQTT group topic set with the GroupTopic command. All devices in the same IP network with the same group topic are in the same group. Some modules may define additional device groups. For example, if Remote Device Mode is enabled, the PWM Dimmer module defines three devices groups. - -The items that are sent to the group and the items that are received from the group are selected with the DevGroupShare command. By default all items are sent and received from the group. An example of when the DevGroupShare command would be used is when you have a group of lights that you control with a dimmer switch and home automation software. You want the dimmer switch to be able to control all items. The home automation software controls each light individually. When it controls the whole group, it actually sends command to each light in the group. If you use the home automation software to turn an individual light on or off or change it’s brightness, color or scheme, you do not want the change to be replicated to the other lights. In this case, you would set the incoming and outgoing item masks to 0xffffffff (all items) on the dimmer switch (DevGroupShare 0xffffffff,0xffffffff) and set the incoming item mask to 0xffffffff and outgoing item mask to 0 on all the lights (DevGroupShare 0xffffffff,0). - - -### Commands - - - - - - - - - - - - - - - -
Command - Parameters -
DevGroupShare - <in>,<out> = set incoming and outgoing shared item mask (default = 0xffffffff,0xffffffff)
-1 = Power, 2 = Light brightness, 4 = Light fade/speed, 8 = Light scheme, 16 = Light color, 32 = Dimmer settings (presets) -
GroupTopic<x> - 1 = reset device group <x> MQTT group topic to firmware default (MQTT_GRPTOPIC) and restart
-<value> = set device group <x> MQTT group topic (32 chars max) and restart -
\ No newline at end of file diff --git a/I2CDEVICES.md b/I2CDEVICES.md index 75c0a1a9a..cb42d97a4 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -67,3 +67,6 @@ Index | Define | Driver | Device | Address(es) | Description 43 | USE_AHT1x | xsns_63 | AHT10/15 | 0x38 | Temperature and humidity sensor 44 | USE_WEMOS_MOTOR_V1 | xdrv_34 | | 0x2D - 0x30 | WEMOS motor shield v1.0.0 (6612FNG) 45 | USE_HDC1080 | xsns_65 | HDC1080 | 0x40 | Temperature and Humidity sensor + 46 | USE_IAQ | xsns_66 | IAQ | 0x5a | Air quality sensor + 47 | USE_DISPLAY_SEVENSEG| xdsp_11 | HT16K33 | 0x70 - 0x77 | Seven segment LED + 48 | USE_AS3935 | xsns_67 | AS3935 | 0x03 | Franklin Lightning Sensor \ No newline at end of file diff --git a/MODULES.md b/MODULES.md index b47769998..e16dec401 100644 --- a/MODULES.md +++ b/MODULES.md @@ -23,7 +23,7 @@ Module | Description 17 WiOn | WiOn Wifi Smart Socket 18 Generic | Any ESP8266/ESP8285 device like WeMos and NodeMCU 19 Sonoff Dev | Sonoff Dev Wifi Development Board -20 H801 | H801 Wifi RGBWW Led Controller +20 H801 | H801 Wifi 5 Channel LED Controller 21 Sonoff SC | Sonoff SC Wifi Environmental Monitor 22 Sonoff BN-SZ | Sonoff BN-SZ01 Wifi Ceiling Led (Retired) 23 Sonoff 4CH Pro | Sonoff 4CH Pro 4-gang Wifi Smart Switch @@ -57,7 +57,7 @@ Module | Description 51 OBI Socket | OBI Wifi Smart Socket 52 Teckin | Teckin SP22 Wifi Smart Switch with Energy Monitoring 53 AplicWDP303075 | Aplic WDP 303075 CSL Wifi Smart Switch with Energy Monitoring -54 Tuya Dimmer | MIUO (and other Tuya based) Wifi Dimmer for Incandescent Lights and Led +54 TuyaMCU | Devices with an MCU using Tuya communication protocol for control 55 Gosund SP1 v23 | Gosund SP1 v2.3 Wifi Smart Switch with Energy Monitoring 56 ARMTR Dimmer | ARMtronix Wifi dimmer for Incandescent Lights and Led 57 SK03 Outdoor | SK03 Outdoor Wifi Smart Switch with Energy Monitoring @@ -65,7 +65,7 @@ Module | Description 59 Teckin US | Teckin SP20 and ZooZee SA102 Wifi Smart Switch with Energy Monitoring 60 Manzoku strip | Manzoku Wifi Smart Power Strip with four Relays 61 OBI Socket 2 | OBI 2 Wifi Smart Socket -62 YTF IR Bridge | YTF Infra Red Wifi Bridge +62 YTF IR Bridge | YTF Universal IR Bridge 63 Digoo DG-SP202 | Digoo DG-SP202 Dual Wifi Smart Switch with Energy Monitoring 64 KA10 | Smanergy KA10 Wifi Smart Wall Switch with Energy Monitoring 65 Luminea ZX2820 | Luminea ZX2820 Wifi Smart Switch with Energy Monitoring diff --git a/PWM_Dimmer.md b/PWM_Dimmer.md deleted file mode 100644 index 7254a5da2..000000000 --- a/PWM_Dimmer.md +++ /dev/null @@ -1,94 +0,0 @@ -# PWM Dimmer - -The PWM Dimmer module adds support for Martin Jerry/acenx/Tessan/NTONPOWER SD0x PWM dimmer switches. The brightness of the load for these dimmers is controlled by a PWM GPIO pin. They typically have power, up and down buttons, a powered-on LED, five brightness LEDs and another status LED. Examples are:[ https://www.amazon.com/dp/B07FXYSVR1](https://www.amazon.com/dp/B07FXYSVR1),[ https://www.amazon.com/dp/B07V26Q3VD](https://www.amazon.com/dp/B07V26Q3VD),[ https://www.amazon.com/dp/B07K67D43J](https://www.amazon.com/dp/B07K67D43J),[ https://www.amazon.com/dp/B07TTGFWFM](https://www.amazon.com/dp/B07TTGFWFM) - -To include PWM dimmer support in the build, define USE_PWM_DIMMER in your user_config_override. This adds 4.5K to the code size. The light module is not required for PWM dimmer operation so you can #undef USE_LIGHT to reduce the firmware bin size. - -To enable PWM dimmer operation, select the PWM Dimmer module. - - -## PWM Dimmer Operation - -Pressing and releasing the power button toggles the power on/off. If the toggle turns the power on, the load is returned to the last brightness it was adjusted to. If Fade is enabled, the load is faded on/off at the rate defined by the Speed setting. - -When the power is on, holding the down or up button decreases/increases the brightness. The brightness can also be changed using just the power button. When the power is on, holding the power button alternately increases or decreases the brightness. Initially, holding the power button increases the brightness. Releasing and then holding the power button again decreases the brightness. - -When the power is off, pressing and releasing the down or up button turns the power on at a temporary brightness of the low/high levels set by the DimmerPreset command. Turning the power on at the low preset can also be accomplished by holding the power button while the power is off. The dimmer presets are intended to enable quickly turning on a light to a dim or bright level without changing the normal desired brightness. Turning the power on to a preset does not change the brightness the load will be set to when the switch is turned on the next time. For example, if the light is on and you adjust the brightness to 80 and then turn the light off, when you turn it back on, the brightness will return to 80. If you turn the power off again and then press the down button, the light will be turned on with a brightness of the low preset. If you then turn the light off and on again, the brightness will return to 80. - -When the power is off, holding the down or up button publishes an MQTT EVENT command. The topic follows the format of the Full Topic with a prefix of Event (ex. cmnd/LightSwitch1/EVENT). The MQTT payload is Trigger#, where # is 1 if the down button is held or 2 if the up button is held. These triggers can be used in rules on remote devices (ON Event#Trigger1) or by automation software to trigger automations such as scene changes. For example, the Event topic Trigger1 payload could trigger the automation software to turn on the previous scene in a list and the Trigger2 payload could trigger the automation software to turn on the next scene in a list. - -If there are LED’s defined in the template, they are turned on to indicate the current brightness. More LEDs are turned on at higher brightnesses. The LedTimeout command enables/disables an LED timeout. If LED timeout is enabled, the LED’s turn off five seconds after the last change in brightness. Note that the lowest LED and the blue power LED are always on when the power is on. The LED timeout can also be enabled/disabled by holding the power button while tapping (pressing and releasing quickly) the down button. - -The LEDLink LED can be used as a nightlight/powered-off indicator. The PoweredOffLed command enables/disables turning the LEDLink LED on when the power is off. The powered-off indicator can also be enabled/disabled by holding the power button and tapping the up button. - -Holding the power button, tapping the down button and then tapping or holding the down or up button sends a device group message to set RGB lights in the device group to the previous/next fixed color. The command is sent/value is adjusted once every .5 seconds for as long as the button is held. The color sequence as defined by the Light module is red, green, blue, orange, light green, light blue, amber, cyan, purple, yellow, pink, white using RGB channels, white using CT channels. - -Holding the power button, tapping the up button and then tapping or holding the down or up button publishes an MQTT Event command. The command is sent/value is adjusted once every .5 seconds for as long as the button is held. The MQTT topic is as described above. The MQTT payload is Trigger#, where # is 3 if the down button is held or 4 if the up button is held. - -Holding the down or up button alone for over 10 seconds executes the WiFiConfig 2 command. - -Pressing and releasing any button publishes an MQTT TOGGLE command for the button. Holding a button publishes an MQTT HOLD command followed by an MQTT OFF command when the button is released. - -When Device Groups are enabled, the PWM Dimmer brightness presets are kept in sync across all switches in the group. The powered-off LED and LED timeout settings are specific to each switch. Changing them does not replicate the change to the other switches in the group. - -### Commands - - - - - - - - - - - - - - - - - - - - - - -
Command - Parameters -
BriPreset - <low>,<high> = set brightness low and high presets
-1..255 = set brightness preset
-+ = increase brightness preset
-- = decrease brightness preset -
SetOption86 - Set brightness LED timeout
-0 = disable timeout (default)
-1 = enable timeout -
SetOption87 - Set powered-off LED (nightlight)
-0 = disable powered-off LED (default)
-1 = enable powered-off LED -
SetOption88 - Set remote device mode
-0 = disable remote device mode(default)
-1 = enable remote device mode -
- - - -### Remote Device Mode - -Remote device mode allows PWM Dimmer switches to control remote devices. With remote device mode enabled, each button controls a different device. Note that dimmer switches with toggle-style down/up buttons have limited functionality as remote device mode switches because you can not push the down and up buttons simultaneously. - -To include remote device mode support in the build, define USE_PWM_DIMMER_REMOTE in your user_config_override. Remote device mode support requires device group support so USE_DEVICE_GROUPS is automatically defined if USE_PWM_DIMMER_REMOTE is defined. Remote device mode support adds 0.7K to the code size in addition to the code size required for device groups support. - -To enable remote device mode, execute the command SetOption88 1 (the device will restart). Each remote device must be running firmware with device group support and have remote device support enabled. The remote devices do not need to be built with PWM dimmer support nor do they need to be switches. - -If a remote device is a PWM dimmer, the device acts like a 3-way dimmer switch would and may or may not have a load connected to it. It’s also possible to use a PWM dimmer switch without a load to act as a wall switch to control the power, brightness and color of one or more smart lights with Tasmota with device group support loaded on them. - -With remote device mode is enabled, button 1 is the power button for the local device while buttons 2 and 3 are the power buttons for remote devices. Group names for buttons 2 and 3 are set by the GroupTopic2 and GroupTopic3 commands respectively. Note that the button numbers are defined by the module template and can be in any physical order on the switch (button 1 can be defined as the top button, the middle button or the bottom button). Button combinations that publish MQTT Event commands use a topic in the format cmnd/%group-topic%/EVENT. - -Pressing and releasing a power button toggles the power on all devices in the group assigned to the button. When the power is on, holding the button alternately increases or decreases the brightness. When the power is off, holding the button turns the power on at a temporary brightness of the low level set by the BriPreset command. - -While holding a power button, the other two buttons act like the down and up buttons for the remote device. All the functions performed by the down and up buttons in non-remote device mode are available in remote device mode. While holding button 1, button 2 performs the functions of the down button and button 3 performs the functions of the up button. While holding button 2, button 1 performs the functions of the down button and button 3 performs the functions of the up button. While holding button 3, button 1 performs the functions of the down button and button 2 performs the functions of the up button. diff --git a/README.md b/README.md index 53f977ae2..8c8d7c024 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,10 @@ In addition to the [release webpage](https://github.com/arendst/Tasmota/releases ## Development -[![Dev Version](https://img.shields.io/badge/development%20version-v8.2.x.x-blue.svg)](https://github.com/arendst/Tasmota) +[![Dev Version](https://img.shields.io/badge/development%20version-v8.3.x.x-blue.svg)](https://github.com/arendst/Tasmota) [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) -[![Build Status](https://img.shields.io/travis/arendst/Tasmota.svg)](https://travis-ci.org/arendst/Tasmota) +[![Tasmota CI](https://github.com/arendst/Tasmota/workflows/Tasmota%20CI/badge.svg)](https://github.com/arendst/Tasmota/actions?query=workflow%3A%22Tasmota+CI%22) +[![Tasmota ESP32 CI](https://github.com/arendst/Tasmota/workflows/Tasmota%20ESP32%20CI/badge.svg)](https://github.com/arendst/Tasmota/actions?query=workflow%3A%22Tasmota+ESP32+CI%22) See [tasmota/CHANGELOG.md](tasmota/CHANGELOG.md) for detailed change information. @@ -42,10 +43,10 @@ We don't take any responsibility nor liability for using this software nor for t ## Note -Please do not ask to add new devices unless it requires additional code for new features. If the device is not listed as a module, try using [Templates](https://tasmota.github.io/docs/#/Templates) first. If it is not listed in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) create your own [Template](https://tasmota.github.io/docs/#/Templates?id=creating-your-template). +Please do not ask to add new devices unless it requires additional code for new features. If the device is not listed as a module, try using [Templates](https://tasmota.github.io/docs/Templates) first. If it is not listed in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) create your own [Template](https://tasmota.github.io/docs/Templates#creating-your-template). ## Quick Install -Download one of the released binaries from https://github.com/arendst/Tasmota/releases and flash it to your hardware [using our installation guide](https://tasmota.github.io/docs/#/installation/). +Download one of the released binaries from https://github.com/arendst/Tasmota/releases and flash it to your hardware [using our installation guide](https://tasmota.github.io/docs/Getting-Started). ## Important User Compilation Information If you want to compile Tasmota yourself keep in mind the following: @@ -60,7 +61,7 @@ Please refer to the installation and configuration articles in our [documentatio ## Migration Information -See [wiki migration path](https://tasmota.github.io/docs/#/Upgrading?id=migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: +See [wiki migration path](https://tasmota.github.io/docs/Upgrading#migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: 1. Migrate to **Sonoff-Tasmota 3.9.x** 2. Migrate to **Sonoff-Tasmota 4.x** @@ -77,15 +78,15 @@ See [wiki migration path](https://tasmota.github.io/docs/#/Upgrading?id=migratio -For a database of supported devices see [Tasmota Device Templates Repository](https://blakadder.github.io/templates) +For a database of supported devices see [Tasmota Device Templates Repository](https://templates.blakadder.com) If you're looking for support on **Tasmota** there are some options available: ### Documentation * [Documentation Site](https://tasmota.github.io/docs): For information on how to flash Tasmota, configure, use and expand it -* [FAQ and Troubleshooting](https://tasmota.github.io/docs/#/help/): For information on common problems and solutions. -* [Commands Information](https://tasmota.github.io/docs/#/Commands): For information on all the commands supported by Tasmota. +* [FAQ and Troubleshooting](https://tasmota.github.io/docs/FAQ/): For information on common problems and solutions. +* [Commands Information](https://tasmota.github.io/docs/Commands): For information on all the commands supported by Tasmota. ### Support's Community @@ -144,7 +145,9 @@ People helping to keep the show on the road: - Stefan Bode for his Shutter and Deep sleep drivers - Jacek Ziółkowski for his [TDM](https://github.com/jziolkowski/tdm) management tool and [Tasmotizer](https://github.com/tasmota/tasmotizer) flashing tool - Christian Staars for NRF24L01 and HM-10 Bluetooth sensor support -- Pail Diem for UDP Group communication support +- Paul Diem for UDP Group communication support +- Jörg Schüler-Maroldt for his initial ESP32 port +- Javier Arigita for his thermostat driver - Many more providing Tips, Wips, Pocs, PRs and Donations ## License diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 811941f43..9809eb09c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,7 +4,7 @@ ## Migration Information -See [migration path](https://tasmota.github.io/docs/#/Upgrading?id=migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: +See [migration path](https://tasmota.github.io/docs/Upgrading#migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: 1. Migrate to **Sonoff-Tasmota 3.9.x** 2. Migrate to **Sonoff-Tasmota 4.x** @@ -21,13 +21,13 @@ While fallback or downgrading is common practice it was never supported due to S ## Supported Core versions -This release will be supported from ESP8266/Arduino library Core version **2.6.3 + 372a3ec** due to reported security and stability issues on previous Core version. This will also support gzipped binaries. +This release will be supported from ESP8266/Arduino library Core version **2.7.0** due to reported security and stability issues on previous Core version. This will also support gzipped binaries. Although it might still compile on previous Core versions all support will be removed in the near future. ## Support of TLS -To save resources when TLS is enabled mDNS needs to be disabled. In addition to TLS using fingerprints now also user supplied CA certs and AWS IoT is supported. Read [full documentation](https://tasmota.github.io/docs/#/integrations/AWS-IoT) +To save resources when TLS is enabled mDNS needs to be disabled. In addition to TLS using fingerprints now also user supplied CA certs and AWS IoT is supported. Read [full documentation](https://tasmota.github.io/docs/AWS-IoT) ## Initial configuration tools @@ -35,7 +35,7 @@ For initial configuration this release supports Webserver based **WifiManager** ## Provided Binary Downloads -The following binary downloads have been compiled with ESP8266/Arduino library core version **2.6.3 + 372a3ec**. +The following binary downloads have been compiled with ESP8266/Arduino library core version **2.7.0**. - **tasmota.bin** = The Tasmota version with most drivers. **RECOMMENDED RELEASE BINARY** - **tasmota-BG.bin** to **tasmota-TW.bin** = The Tasmota version in different languages. @@ -52,76 +52,57 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 8.2.0 Elliot +### Version 8.3.0 Fred -- Change default my_user_config.h driver and sensor support removing most sensors and adding most drivers to tasmota.bin -- Change DHT driver (#7468, #7717) -- Change Lights: simplified gamma correction and 10 bits internal computation -- Change commands ``Prefix``, ``Ssid``, ``StateText``, ``NTPServer``, and ``FriendlyName`` displaying all items -- Change Zigbee command prefix from ``Zigbee*`` to ``Zb*`` -- Change MQTT message size with additional 200 characters -- Change display of some date and time messages from "Wed Feb 19 10:45:12 2020" to "2020-02-19T10:45:12" -- Change IRremoteESP8266 library updated to v2.7.4 -- Fix ``PowerDelta`` zero power detection (#7515) -- Fix ``White`` added to light status (#7142) -- Fix ``WakeUp `` ignores provided value (#7473) -- Fix ``RGBWWTable`` ignored (#7572) -- Fix commands ``Display`` and ``Counter`` from overruling command processing (#7322) -- Fix Sonoff Bridge, Sc, L1, iFan03 and CSE7766 serial interface to forced speed, config and disable logging -- Fix Improved fade linearity with gamma correction -- Fix PWM flickering at low levels (#7415) -- Fix LCD line and column positioning (#7387) -- Fix Display handling of hexadecimal escape characters (#7387) -- Fix exception 9 restart on log message in Ticker interrupt service routines NTP, Wemos and Hue emulation (#7496) -- Fix Hass sensor discovery by Federico Leoni (#7582, #7548) -- Fix MaxPower functionality (#7647) -- Fix relation between Wifi RSSI and signal strength -- Add command ``SetOption79 0/1`` to enable reset of counters at teleperiod time by Andre Thomas (#7355) -- Add command ``SetOption82 0/1`` to limit the CT range for Alexa to 200..380 -- Add command ``SetOption84 0/1`` to send AWS IoT device shadow updates (alternative to retained) -- Add commands ``SetOption85 0/1`` and ``DevGroupShare`` supporting UDP Group command using ``GroupTopic`` without MQTT by Paul Diem (#7790) -- Add command ``SetOption86 0/1`` for PWM dimmer to turn brightness LED's off 5 seconds after last change -- Add command ``SetOption87 0/1`` for PWM dimmer to turn red LED on when powered off -- Add command ``SetOption88 0/1`` for PWM dimmer to let buttons control remote devices -- Add command ``SetOption89 0/1`` for Zigbee distinct MQTT topics per device for SENSOR, allowing retained messages (#7835) -- Add command ``ShutterButton `` to control shutter(s) by to-scho (#7403) -- Add commands ``SwitchMode 8`` ToggleMulti, ``SwitchMode 9`` FollowMulti and ``SwitchMode 10`` FollowMultiInverted (#7522) -- Add commands ``SwitchMode 11`` PushHoldMulti and ``SwitchMode 12`` PushHoldInverted (#7603) -- Add commands ``SwitchMode 13`` PushOn and ``SwitchMode 14`` PushOnInverted (#7912) -- Add command ``Buzzer -1`` for infinite mode and command ``Buzzer -2`` for following led mode (#7623) -- Add command ``HumOffset -10.0 .. 10.0`` to set global humidity sensor offset (#7934) -- Add support for ``AdcParam`` parameters to control ADC0 Current Transformer Apparent Power formula by Jodi Dillon (#7100) -- Add optional parameter ```` to command ``Scheme , `` to control initial start color -- Add web page sliders when ``SetOption37 128`` is active allowing control of white(s) -- Add SerialConfig to ``Status 1`` -- Add BootCount Reset Time as BCResetTime to ``Status 1`` -- Add WifiPower to ``Status 5`` -- Add most SetOptions as defines to my_user_config.h -- Add optional Wifi AccessPoint passphrase define WIFI_AP_PASSPHRASE in my_user_config.h (#7690) -- Add SoftwareSerial to CSE7766 driver allowing different GPIOs (#7563) -- Add rule trigger on one level deeper using syntax with two ``#`` like ``on zbreceived#vibration_sensor#aqaracubeside=0 do ...`` -- Add Zigbee attribute decoder for Xiaomi Aqara Cube -- Add ``ZbZNPReceived``and ``ZbZCLReceived`` being published to MQTT when ``SetOption66 1`` -- Add Zigbee enhanced commands decoding, added ``ZbPing`` -- Add Zigbee features and improvements -- Add Zigbee support for Hue emulation by Stefan Hadinger -- Add HAss Discovery support for Button and Switch triggers by Federico Leoni (#7901) -- Add Dew Point to Temperature and Humidity sensors -- Add optional support for Prometheus using file xsns_91_prometheus.ino (#7216) -- Add support for gzipped binaries -- Add support for Romanian language translations by Augustin Marti -- Add support for sensors DS18x20 and DHT family on Shelly 1 and Shelly 1PM using Shelly Add-On adapter (#7469) -- Add support to BMP driver to enter reset state (sleep enable) when deep sleep is used in Tasmota -- Add support for DS1624, DS1621 Temperature sensor by Leonid Myravjev -- Add support for NRF24L01 as BLE-bridge for Mijia Bluetooth sensors by Christian Baars (#7394) -- Add support for MI-BLE sensors using HM-10 Bluetooth 4.0 module by Christian Staars (#7683) -- Add support for FiF LE-01MR energy meter by saper-2 (#7584) -- Add support for Sensors AHT10 and AHT15 by Martin Wagner (#7596) -- Add support for Wemos Motor Shield V1 by Denis Sborets (#7764) -- Add support for La Crosse TX23 Anemometer by Norbert Richter (#3146, #7765) -- Add support for Martin Jerry/acenx/Tessan/NTONPOWER SD0x PWM dimmer switches by Paul Diem (#7791) -- Add support for UDP Group control without MQTT by Paul Diem (#7790) -- Add support for Jarolift rollers by Keeloq algorithm -- Add support for MaxBotix HRXL-MaxSonar ultrasonic range finders by Jon Little (#7814) -- Add support for HDC1080 Temperature and Humidity sensor by Luis Teixeira (#7888) -- Add support for ElectriQ iQ-wifiMOODL RGBW light by Ian King (#7947) +- Breaking Change Device Groups multicast address and port (#8270) +- Change PWM implementation to Arduino #7231 removing support for Core versions before 2.6.3 +- Change default PWM Frequency to 977 Hz from 880 Hz +- Change minimum PWM Frequency from 100 Hz to 40 Hz +- Change flash access removing support for any Core before 2.6.3 +- Change HM-10 sensor type detection and add features (#7962) +- Change light scheme 2,3,4 cycle time speed from 24,48,72,... seconds to 4,6,12,24,36,48,... seconds (#8034) +- Change remove floating point libs from IRAM +- Change remove MQTT Info messages on restart for DeepSleep Wake (#8044) +- Change IRremoteESP8266 library updated to v2.7.6 +- Change HAss discovery by Federico Leoni (#8370) +- Fix possible Relay toggle on (OTA) restart +- Fix PWM flickering during wifi connection (#8046) +- Fix Zigbee sending wrong Sat value with Hue emulation +- Fix Zigbee crash with Occupancy sensor (#8089) +- Add Zigbee command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` +- Add Zigbee command ``ZbUnbind`` +- Add Zigbee command ``ZbBindState`` and ``manuf``attribute +- Add Zigbee command ``ZbConfig`` and configuration in Settings +- Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) +- Add commands ``NrfPage``, ``NrfIgnore``, ``NrfScan`` and ``NrfBeacon`` to NRF24 Bluetooth driver (#8075) +- Add commands ``GlobalTemp`` and ``GlobalHum`` to init sensor data (#8152) +- Add command ``SO`` as shortcut for command ``SetOption`` +- Add command ``SetOption41 `` to force sending gratuitous ARP every seconds +- Add command ``SetOption73 1`` for button decoupling and send multi-press and hold MQTT messages by Federico Leoni (#8235) +- Add command ``SetOption90 1`` to disable non-json MQTT messages (#8044) +- Add command ``SetOption91 1`` to enable fading at startup / power on +- Add command ``SetOption92 1`` to set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) +- Add command ``SetOption93 1`` to control caching of compressed rules +- Add command ``Sensor10 0/1/2`` to control BH1750 resolution - 0 = High (default), 1 = High2, 2 = Low (#8016) +- Add command ``Sensor10 31..254`` to control BH1750 measurement time which defaults to 69 (#8016) +- Add command ``Sensor18 0..32000`` to control PMS5003 sensor interval to extend lifetime by Gene Ruebsamen (#8128) +- Add command ``DevGroupName`` to specify up to four Device Group Names (#8087) +- Add command ``DevGroupSend`` to send an update to a Device Group (#8093) +- Add command ``Ping`` (#7176) +- Add command ``Palette`` to add the ability to specify a palette of colors (#8150) +- Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa +- Add support for 64x48 SSD1306 OLED (#6740) +- Add support for Seven Segment display using HT16K33 (#8116) +- Add support for up to four MQTT GroupTopics (#8014) +- Add support for longer template names +- Add support for an iAQ sensor (#8107) +- Add support for AS3935 Lightning Sensor by device111 (#8130) +- Add console command history (#7483, #8015) +- Add quick wifi reconnect using saved AP parameters when ``SetOption56 0`` (#3189) +- Add more accuracy to GPS NTP server (#8088) +- Add support for analog anemometer by Matteo Albinola (#8283) +- Add support for OpenTherm by Yuriy Sannikov (#8373) +- Add support for Thermostat control by arijav (#8212) +- Add experimental basic support for Tasmota on ESP32 based on work by Jörg Schüler-Maroldt +- Add automatic compression of Rules to achieve ~60% compression by Stefan Hadinger +- Add rule trigger at root level like ``on loadavg<50 do power 2 endon`` after ``state`` command diff --git a/TEMPLATES.md b/TEMPLATES.md index e34fb2ea5..25482b2c3 100644 --- a/TEMPLATES.md +++ b/TEMPLATES.md @@ -2,296 +2,21 @@ # Templates -Find below the available templates as of March 2020. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) +Find below the available templates as of May 2020. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) ## Aromatherapy Diffuser ``` Asakuki 500ml {"NAME":"Oil Diffuser","GPIO":[255,255,255,255,255,255,0,0,255,255,255,21,22],"FLAG":0,"BASE":18} +Blitzwolf BW-FUN3 400ml {"NAME":"Generic","GPIO":[255,255,255,255,255,255,255,255,255,255,255,255,255],"FLAG":0,"BASE":54} +Brilex 400ml Oil Diffuser {"NAME":"BrilexDiffuser","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} Essential Oil 400ml {"NAME":"XD800W","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} GD-30W 300ml {"NAME":"GD-30W","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} +Kbaybo 300ml {"NAME":"K-H25","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} Maxcio 400ml {"NAME":"MaxcioDiffuser","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} Mirabella Genio Glass Aroma Diffuser 250ml {"NAME":"Genio Diffuser","GPIO":[0,0,0,0,37,38,0,0,0,160,22,39,21],"FLAG":15,"BASE":18} Wood Grain 550ML {"NAME":"MY-KCL01800FB","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} ``` -## Bulb CCT -``` -AICase 800lm {"NAME":"AICase Smart L","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} -Ajax Online 380lm {"NAME":"AjaxOnline","GPIO":[17,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":38} -Ajax Online 7W Vintage {"NAME":"AjaxOnline-7W","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -Anoopsyche 9W 800lm {"NAME":"Anoop-CW-WW","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} -Arlec GLD112HA 806lm {"NAME":"Arlec CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48} -BrilliantSmart 20696 9W 900lm {"NAME":"Brilliant20696","GPIO":[0,0,0,0,0,0,0,0,37,0,38,0,0],"FLAG":0,"BASE":48} -BrilliantSmart 20697 9W 900lm {"NAME":"Brilliant20699","GPIO":[0,0,0,0,0,0,0,0,37,0,38,0,0],"FLAG":0,"BASE":48} -Bulbrite Solana A19 Edison Filament {"NAME":"BulbBrite01","GPIO":[0,0,0,0,0,0,0,0,37,0,38,0,0],"FLAG":0,"BASE":18} -Calex 429036 G125 1055lm {"NAME":"Calex G125 E27","GPIO":[0,0,0,0,0,140,0,0,38,0,37,142,141],"FLAG":0,"BASE":18} -Deltaco SH-LE14W 470lm {"NAME":"SH-LE14W","GPIO":[0,0,0,0,0,140,0,0,38,0,37,142,141],"FLAG":0,"BASE":18} -Deltaco SH-LE27W 810lm {"NAME":"SH-LE27W","GPIO":[0,0,0,0,0,140,0,0,38,0,37,142,141],"FLAG":0,"BASE":18} -DGM L-WT9W1 800lm {"NAME":"DGM L-WT9W1","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} -DORESshop A60 720lm Filament {"NAME":"DORESshop-A60","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -Energizer A19 10W {"NAME":"Energizer CCT ","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} -Geeni LUX 1050lm {"NAME":"Geeni-1050-WW","GPIO":[0,0,0,0,37,37,0,0,38,0,0,0,0],"FLAG":1,"BASE":18} -Globe 34919 ST19 Edison 500lm {"NAME":"Globe 34919","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -Hama 176550 806lm {"NAME":"Hama Bulb xW","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} -Hykker SL-0392 650lm {"NAME":"Hykker 7W","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} -Iotton 9W 700lm {"NAME":"Iotton Light","GPIO":[0,0,0,0,37,38,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -Kogan 10W Cool & Warm White 1050lm {"NAME":"Kogan White/Wa","GPIO":[0,0,0,0,37,40,0,0,41,38,39,0,0],"FLAG":0,"BASE":18} -LE LampUX 4.5W 410lm {"NAME":"LE LampUX","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":48} -ledscom.de 4.5W 430lm {"NAME":"GX53","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":0} -Lohas ZN070 720lm {"NAME":"Lohas ZN070","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -Lumiman A19 7.5W 800lm {"NAME":"Lumiman LM520","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} -Luminea ZX-2831 {"NAME":"Luminea CCT","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Merkury MI-BW905-999W 700lm {"NAME":"MI-BW905-999W","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} -Mimoodz 1050lm {"NAME":"ID Components","GPIO":[0,0,0,0,21,22,0,0,23,24,25,26,27],"FLAG":0,"BASE":18} -Mirabella Genio 1002336 800lm {"NAME":"GenioBulbCCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} -Mirabella Genio 1002337 800lm {"NAME":"GenioBulbCCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} -Mirabella Genio 9W 800lm {"NAME":"GenioBulbCCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} -Mirabella Genio I002745 SES 470lm {"NAME":"Mirabella Candle","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -Mirabella Genio I002746 500lm {"NAME":"GenioGU10","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -Mirabella Genio I002747 G95 470lm {"NAME":"Genio Filament","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -Nedis A60 800lm {"NAME":"WIFILW10WTE27","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} -Nedis C10 350lm {"NAME":"WIFILW10WTE14","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} -Nedis PAR16 330lm {"NAME":"Nedis WIFILW30","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} -Philips Zhirui Candle 250lm {"NAME":"Xiaomi Philips","GPIO":[0,0,0,0,0,0,0,0,38,0,0,37,0],"FLAG":0,"BASE":48} -Phillips Zhirui 450lm {"NAME":"Xiaomi Philips","GPIO":[0,0,0,0,0,0,0,0,38,0,0,37,0],"FLAG":0,"BASE":48} -Swisstone SH 330 806lm {"NAME":"SwisstoneSH330","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Wipro Garnet NS9100 810lm {"NAME":"WiproSmartBulb","GPIO":[0,0,0,0,38,38,0,0,37,37,0,0,0],"FLAG":0,"BASE":18} -Wyze WLPA19 A19 800lm {"NAME":"Wyze Bulb","GPIO":[0,0,0,0,0,0,0,0,0,37,47,0,0],"FLAG":0,"BASE":48} -``` - -## Bulb Cold White -``` -Feit Electric BR30/950CA/AG 650lm {"NAME":"Feit BR30 CW","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -KMC 70113 A19 7.5W {"NAME":"Generic","GPIO":[0,0,0,0,0,0,37,38,0,0,0,0,0],"FLAG":15,"BASE":18} -Lohas LZN127 G25 800lm {"NAME":"Lohas Globe","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} -Lohas ZN014 550lm {"NAME":"Lohas MR16","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -Lohas ZN124 980lm {"NAME":"Lohas LH-ZN124","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} -Mirabella Genio 1002334 800lm {"NAME":"GenioBulbCW","GPIO":[0,0,0,0,38,37,0,0,0,40,39,0,0],"FLAG":0,"BASE":18} -Mirabella Genio A70 1400lm {"NAME":"GenioB_CW2744","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -``` - -## Bulb Red Green Blue -``` -Deltaco SH-LE27RGB 810lm {"NAME":"SH-LE27RGB","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} -Halonix Prime Prizm 12W {"NAME":"Halonix Prizm ","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Jomarto 9W {"NAME":"Jomarto Wifi S","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":1,"BASE":18} -Lyasi PT-BW09 {"NAME":"Lyasi PT-BW09","GPIO":[17,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} -MoKo YX-L01C-E27 810lm {"NAME":"MOKO","GPIO":[17,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} -muvit IO miobulb001 600lm {"NAME":"miobulb001","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":15,"BASE":18} -Oobest ZN93028 11W {"NAME":"RGB Bulb 11W","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":1,"BASE":18} -Wipro Garnet NS7001 480lm {"NAME":"WiproSmartBulb","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -``` - -## Bulb Red Green Blue CCT -``` -Aigital LE13 800lm {"NAME":"Aigital 9W RGB","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} -Alfawise LE12 9W 900LM {"NAME":"Alfawise LE12 ","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} -Aoycocr JL81 5W 400lm {"NAME":"AoycocrJLB1","GPIO":[0,0,0,0,39,0,0,0,38,41,37,40,0],"FLAG":0,"BASE":18} -Aoycocr Q0 750lm {"NAME":"AoycocrA19","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} -Aoycocr Q10CWM 720lm {"NAME":"AoycocrBR30","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} -Arlec GLD122HA 806lm {"NAME":"Arlec RGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Aunics 7W 600lm {"NAME":"Aunics RGBW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Avatar ALB201W 720lm {"NAME":"AVATAR ALB201W","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} -Avatar ALS18L A60 800lm {"NAME":"Avatar E14 7W","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":20} -B.K.Licht BKL1253 9W 806lm {"NAME":"BKL1253","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} -Bakibo TB95 9W 1000lm {"NAME":"Bakibo A19 9W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -BlitzWolf BW-LT27 w/ remote 850lm {"NAME":"BW-LT27","GPIO":[0,0,0,0,41,38,0,0,39,51,40,37,0],"FLAG":0,"BASE":18} -BNeta IO-WIFI60-E27P 800lm {"NAME":"OM60/RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Bomcosy 600lm {"NAME":"Generic","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18} -Calex 429002 Reflector 350lm {"NAME":"Calex RGBW","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} -Calex 429004 A60 806lm {"NAME":"Calex E27 RGB ","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Cleverio 51395 806lm {"NAME":"CleverioE27RGB","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -CMARS 4W Reflector {"NAME":"RGBWW GU10","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":15,"BASE":18} -Dogain 320lm {"NAME":"DOGAIN","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -Emuni TB95 9W 850Lm {"NAME":"TB95","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Ener-J SHA5262 800lm {"NAME":"ENER-J RGBWWW ","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -Euri Lighting LIS-A1001 800lm {"NAME":"Euri Lighting ","GPIO":[0,0,0,0,37,40,0,0,38,41,39,37,0],"FLAG":0,"BASE":18} -EXUP C37 5W {"NAME":"EXUP","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":15,"BASE":18} -Feit Electric BPA800/RGBW/AG/2 800lm {"NAME":" BPA800/RGBW/AG/2","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Feit Electric BPA800/RGBW/AG/2(P) 800lm {"NAME":" BPA800/RGBW/AG/2P","GPIO":[0,0,0,0,37,38,0,0,141,142,140,0,0],"FLAG":0,"BASE":48} -Feit Electric OM100/RGBW/CA/AG 1600lm {"NAME":"OM100/RGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Feit Electric OM60/RGBW/15K/AG 800lm {"NAME":"FE-OM60-15K-AG","GPIO":[0,0,0,0,141,140,0,0,37,142,38,0,0],"FLAG":0,"BASE":18} -Feit Electric OM60/RGBW/CA/AG 800lm {"NAME":"OM60/RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Fulighture 9W 810lm {"NAME":"Fulighture 9W","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} -Geeni Prisma Plus 800lm {"NAME":"Geeni Prisma P","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Globe 34207 800lm {"NAME":"GlobeRGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Jeeo TF-QPZ13 800lm {"NAME":"Jeeo","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Julun JL-021 5W Candle {"NAME":"E14 RGBCCT","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -Kohree 600lm {"NAME":"Kohree VHP560","GPIO":[0,0,0,0,37,41,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} -LE lampUX A19 850lm {"NAME":"LE RGBWW 60W","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":1,"BASE":18} -Ledmundo 6W 600lm {"NAME":"LEDMUNDO 6W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Legelite 5W Candle {"NAME":"Legelite E12","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Legelite A60 7W {"NAME":"Legelite A60 7","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Legelite A60 7W 600lm {"NAME":"Legelite E26","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Lohas ZN031 650lm {"NAME":"Lohas ZN031","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} -Lohas ZN033 810lm {"NAME":"Lohas RGBWW","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} -Lohas ZN036 900 lm {"NAME":"Lohas RGBCCT","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} -Lohas ZN037 450lm {"NAME":"Lohas LH-ZN037","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":20} -Lumary 9W 800lm {"NAME":"Lumary / iLint","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} -LVWIT A60 8.5W 806lm {"NAME":"LVWIT A60 8.5W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -LVWIT A70 12W 1521lm {"NAME":"LVWIT A70 12W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Novostella HM-LB09 13W 1300lm {"NAME":"Novostella 13W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Novostella NTB10 9W 900lm {"NAME":"NTB10","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} -Novostella UT55505 7W 600lm {"NAME":"Novostella B22","GPIO":[0,0,0,0,37,41,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} -Novostella UT55508 12W 1150lm {"NAME":"Novostella","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Novostella UT55509 13W 1300lm {"NAME":"Novostella 13W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Ohlux A19 7W 600lm {"NAME":"OHLUX","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Ohlux BR30 10W 900lm {"NAME":"OHLUX","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Powertech SL225X 800lm {"NAME":"Jaycar SL225X","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Qualitel ALS08L 1100lm {"NAME":"Qualitel ALS08","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Reafoo A26 9W {"NAME":"ReaFooE26","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} -RYE 5W 450LM Candle {"NAME":"RYE Candlebra","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Saudio A19 7W 700lm {"NAME":"X002BU0DOL","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Slitinto TB95 9W 1000lm {"NAME":"Slitinto 9W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Sonoff B1 (R2) {"NAME":"Sonoff B1","GPIO":[17,0,0,0,0,0,0,0,143,0,144,0,0],"FLAG":0,"BASE":26} -Sunco G25 5W 450lm {"NAME":"Sunco G25","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} -Sunco PAR20 5W 400lm {"NAME":"Sunco PAR20","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} -Teckin SB50-2 800lm {"NAME":"Teckin SB50v3","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Teckin SB53 1300lm {"NAME":"Teckin SB53","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -V-Tac VT-5164 400lm {"NAME":"V-TAC VT5164","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} -Wipro Garnet NS9001 810lm {"NAME":"WiproSmartBulb","GPIO":[0,0,0,0,37,41,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} -ZZHXON 600lm {"NAME":"E27_RGB_Bulb","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -``` - -## Bulb Red Green Blue White -``` -3Stone EBE-QPW36 1050lm {"NAME":"3STONE","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":15,"BASE":18} -Accewit 7W 650lm {"NAME":"Accewit Bulb","GPIO":[0,0,0,0,0,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} -Aisirer 7W 580lm {"NAME":"Aisirer RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -Aisirer 7W 580lm {"NAME":"Aisirer RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -AL Above Lights 810lm {"NAME":"AL 810LM","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -Aoycocr Q3CM 230lm {"NAME":"Aoycocr_GU10","GPIO":[0,0,0,0,0,0,0,0,143,0,144,0,0],"FLAG":0,"BASE":27} -Avatar ALS08L A19 910lm {"NAME":"Avatar E27 7W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20} -Avatar ALS09L A60 900lm {"NAME":"Avatar E14 9W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20} -Avatar ALS11L PAR16 500lm {"NAME":"Avatar_GU10","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} -AWOW A60 9W 800lm {"NAME":"AWOW 9W RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -Axtee AI-003 A19 700lm {"NAME":"Axtee E26 7W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20} -Bawoo EUWL122130 925lm {"NAME":"Bawoo","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} -BlitzWolf BW-LT21 900lm {"NAME":"BlitzWolf LT21","GPIO":[0,0,0,0,41,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} -BNeta IO-WIFI-GU10 380lm {"NAME":"BNeta","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -BriHome 6,5W 500lm {"NAME":"BRI E27 6,5W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20} -Brilliant HK17653S72 350lm {"NAME":"HK17653S72","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -BrilliantSmart 20698 9W 800lm {"NAME":"Brilliant20698","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":0,"BASE":18} -BrilliantSmart 20699 9W 800lm {"NAME":"Brilliant20699","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":0,"BASE":18} -BrilliantSmart 20741 9W 750lm {"NAME":"Brilliant RGB+","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -BrizLabs A19 9W 806LM {"NAME":"TCP Smart RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -BrizLabs Candle 4,5W 350lm {"NAME":"BrizLabs RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -Brizlabs EBE-LZW10 350Lm {"NAME":"Brizlabs E14","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":15,"BASE":18} -BrizLabs EBE-SHW03 380lm {"NAME":"BrizLabs","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} -BTZ1 {"NAME":"WifiBulb","GPIO":[0,0,0,0,0,37,0,0,39,40,38,0,0],"FLAG":0,"BASE":18} -Calex 429008 B35 5W 470lm {"NAME":"Calex E14 RGBW","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} -Cleverio 51398 370lm {"NAME":"CleverioGU10","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Cocoon DY180363-B 800lm {"NAME":"Cocoon RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -electriQ 600lm {"NAME":"ElectricQ B22","GPIO":[0,0,0,0,37,41,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} -EleLight 350lm {"NAME":"EleLight 7wA19","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} -Esicoo 810lm {"NAME":"Esicoo Bulb","GPIO":[0,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} -Fcmila 10W {"NAME":"FCMILA LED E27","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -Fcmila 7W {"NAME":"FCMILA E27 0.1","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -Fcmila Spotlight 460lm {"NAME":"Fcmila LED 6W","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -Feit Electric BR30/RGBW/CA/AG 650lm {"NAME":"BR30/RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Fulighture A60 810lm {"NAME":"Fulighture A60","GPIO":[0,0,0,0,38,37,0,0,0,39,40,0,0],"FLAG":0,"BASE":18} -Geeni Prisma 1050lm {"NAME":"Geeni 1050 RGB","GPIO":[37,0,0,0,140,38,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} -Generic 10W {"NAME":"10W E27 RGBW","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} -Generic GU10 5W 450lm {"NAME":"RGBCW GU10","GPIO":[0,255,0,255,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":3} -Gosund WB3 8W 800lm {"NAME":"Gosund WB3","GPIO":[0,0,0,0,40,0,0,0,37,38,39,0,0],"FLAG":0,"BASE":18} -Hama 176531 1050lm {"NAME":"Hama Bulb RGBW","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} -Hama 176547 806lm {"NAME":"hama_rgb","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -Hykker SL-0492 810lm {"NAME":"Hykker RBGW 9W","GPIO":[0,0,0,0,0,40,0,0,37,0,39,38,0],"FLAG":0,"BASE":18} -iDigital Smart Home Wifi Globe 9W {"NAME":"iDigitalRGB+WW","GPIO":[0,0,0,0,0,40,0,0,37,0,39,38,0],"FLAG":0,"BASE":18} -Kainsy 600lm {"NAME":"KAINSY","GPIO":[17,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} -Koaanw 650lm {"NAME":"KOAANW Bulb","GPIO":[0,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} -Kogan 10W Ambient 1050lm {"NAME":"Kogan RGB","GPIO":[0,0,0,0,140,37,0,0,0,0,141,0,0],"FLAG":0,"BASE":18} -Kogan Ambient Candle {"NAME":"Kogan_E14","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -Kogan Ambient Spotlight 330lm {"NAME":"Kogan_GU10","GPIO":[0,0,0,0,39,40,0,0,37,0,38,0,0],"FLAG":0,"BASE":18} -Kuled 800lm {"NAME":"KULED 60W RGB","GPIO":[0,0,0,0,39,40,0,0,37,0,38,0,0],"FLAG":1,"BASE":18} -Laideyi 7W {"NAME":"7W-E14-RGBW-La","GPIO":[0,0,0,0,38,37,0,0,39,0,40,0,0],"FLAG":0,"BASE":18} -LeCardio 10W 980lm {"NAME":"LeCardio RGBW","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -Lohas ZN001 810lm {"NAME":"Lohas RGBW","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":18} -Lohas ZN001-E12 810lm {"NAME":"Lohas","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":26} -Lohas ZN005 420lm {"NAME":"Lohas E14 R50","GPIO":[17,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} -Lohas ZN006 1380lm {"NAME":"Lohas100 RGBW","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":18} -Lohas ZN012 450lm {"NAME":"LH-5W-ZN01204","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} -Lonsonho 900lm {"NAME":"RGB+W+C Bulb","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -LSC Smart Connect C45 400lm {"NAME":"LSC RGBCW E14","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} -LSC Smart Connect MR16 380lm {"NAME":"LSC RGBCW GU10","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} -Lumiman LM530 7.5W 800lm {"NAME":"Lumiman LM530","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18} -Lumiman LM530 7.5W 800lm {"NAME":"Lumiman LM530","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18} -Lumiman LM530 7.5W 800lm {"NAME":"LM530","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18} -Luminea ZX-2832 {"NAME":"Luminea RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":1,"BASE":18} -Luminea ZX-2986 1400lm {"NAME":"Luminea RGBW","GPIO":[0,0,0,0,37,41,0,0,0,38,39,40,0],"FLAG":0,"BASE":18} -LWE3 600lm {"NAME":"Linganzh LWE3 ","GPIO":[0,0,0,0,0,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} -Manzoku XS-001 1050lm {"NAME":"Manzoku RGBW","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -Maxcio YX-L01P-E27-2P 9W {"NAME":"Maxcio YXL01P","GPIO":[17,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} -Merkury MI-BW210-999W 1050lm {"NAME":"MI-BW210-999W","GPIO":[0,0,0,0,140,37,0,0,142,38,141,0,0],"FLAG":0,"BASE":48} -Merkury MI-BW904-999W 1050lm {"NAME":"MI-BW904-999W","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} -Merkury MI-BW904-999W v2 1050lm {"NAME":"MI-BW210-999W","GPIO":[0,0,0,0,38,37,0,0,141,142,140,0,0],"FLAG":0,"BASE":48} -Merkury MI-BW904-999W v3 {"NAME":"MI-BW904-999W","GPIO":[0,0,0,0,37,38,0,0,141,142,140,0,0],"FLAG":0,"BASE":69} -Merkury MI-BW906-999W BR30 750lm {"NAME":"MI-BW906-999W","GPIO":[0,0,0,0,38,37,0,0,141,142,140,0,0],"FLAG":0,"BASE":18} -Mirabella Genio 1002338 800lm {"NAME":"GenioBulbRGB","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":1,"BASE":18} -Mixigoo 950lm {"NAME":"Mixigoo Bulb","GPIO":[0,0,0,0,41,38,0,0,39,0,37,40,0],"FLAG":0,"BASE":18} -MoKo JL81 5W 400lm {"NAME":"MoKo E14","GPIO":[0,0,0,0,0,0,0,0,143,0,144,0,0],"FLAG":0,"BASE":27} -Nedis A60 800lm {"NAME":"Nedis RGBW","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":1,"BASE":18} -Nedis C10 350lm {"NAME":"Nedis WIFILC10","GPIO":[0,0,0,0,39,37,0,0,40,38,41,0,0],"FLAG":1,"BASE":18} -Nedis PAR16 330lm {"NAME":"Nedis GU10","GPIO":[0,0,0,0,39,37,0,0,40,38,41,0,0],"FLAG":0,"BASE":18} -NiteBird TT-WB4 800lm {"NAME":"NiteBird TT-WB","GPIO":[0,0,0,0,40,0,0,0,37,38,39,0,0],"FLAG":0,"BASE":18} -Orbecco 5W 400lm {"NAME":"Orbecco Bulb","GPIO":[0,0,0,0,0,0,0,0,143,0,144,0,0],"FLAG":0,"BASE":27} -Premier A19 10W {"NAME":"Premier Light","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":1,"BASE":18} -Riversong Juno 10W {"NAME":"Juno10","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} -Rogoei EBE-QPZ04 6.5W 450lm {"NAME":"EBE-QPZ04","GPIO":[0,0,0,0,180,0,0,0,0,0,181,0,0],"FLAG":0,"BASE":18} -Smart 810lm {"NAME":"OOOLED 60W RGB","GPIO":[0,0,0,0,39,40,0,0,37,0,38,0,0],"FLAG":1,"BASE":18} -Smartyfi 600lm {"NAME":"SMARTYFI 9W","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -Solimo 12W {"NAME":"Solimo RGBWW12","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Solimo 810lm {"NAME":"Solimo RGBWW 9","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} -Swisstone SH 340 806lm {"NAME":"SH 340","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":15,"BASE":18} -Syska 720lm {"NAME":"SyskaSmartBulb","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -TCP Smart 806lm {"NAME":"TCP Smart RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Teckin SB50 800lm {"NAME":"Teckin SB50","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -Teckin SB50 v2 800lm {"NAME":"Teckin SB50","GPIO":[0,0,0,0,37,0,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} -Teckin SB51 800lm {"NAME":"Teckin SB51","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} -TikLOk TL530 A19 7.5W 800lm {"NAME":"TikLOk WW-CW-L","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} -Utorch LE7 600lm {"NAME":"Utorch LE7","GPIO":[0,0,0,0,0,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} -V-Tac VT-5113 1055lm {"NAME":"V-TAC LED A60 ","GPIO":[0,0,0,0,180,0,0,0,0,0,181,0,0],"FLAG":0,"BASE":18} -Wallfire WF-05 {"NAME":"Wallfire E27","GPIO":[17,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} -Woopower 460lm {"NAME":"Woopower E14","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} -WOOX R4553 650lm {"NAME":"WOOX R4553","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -WOOX R5076 4W 350lm {"NAME":"WOOX R4553","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} -Woox R5077 {"NAME":"WOOX R5077","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -Zemismart 5W {"NAME":"Zemismart_GU10","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} -Zilotek A19 800lm {"NAME":"Zilotek RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} -``` - -## Bulb Warm White -``` -Aisirer 9W 806lm {"NAME":"Aisirer 9W","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -Aisirer 9W 806lm {"NAME":"AISIRER E26","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -Aisirer 9W 806lm {"NAME":"Aisirer 9W","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -Avatar ALS15L Candle {"NAME":"AVATAR","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} -Cleverio 51396 800lm {"NAME":"Cleverio E27","GPIO":[0,0,0,0,0,0,0,0,0,0,46,0,0],"FLAG":0,"BASE":18} -Connect Smart 10W {"NAME":"CSH-B22WW10W","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} -DORESshop B11 600lm Filament {"NAME":"Doresshop-cand","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} -DORESshop ST64 720lm Filament {"NAME":"DORESshop-ST64","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -Feit Electric BR30/927CA/AG 650lm {"NAME":"Feit BR30 WW","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -Feit Electric OM60/927CA/AG 800lm {"NAME":" Feit P_A800_2","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -Geeni LUX Edison {"NAME":"Geeni-Edison","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} -Globe 34209 800lm {"NAME":"Globe WW 800lm","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -Kogan ST-20 Filament {"NAME":"Kogan Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":15,"BASE":18} -LeDesign 8W ST21 Filament {"NAME":"Edison Bulb","GPIO":[0,0,0,0,0,0,0,0,0,37,0,0,0],"FLAG":0,"BASE":18} -LSC Smart Connect Filament A60 {"NAME":"LSC Filam E27","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -LSC Smart Connect Filament C35 {"NAME":"LSC Filam E14","GPIO":[0,255,0,255,0,0,0,0,38,0,37,0,0],"FLAG":15,"BASE":18} -LSC Smart Connect Filament G125 {"NAME":"LSC Filam Huge","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -LSC Smart Connect Filament ST64 {"NAME":"LSC Filam Big","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} -Lumary 6W 700lm Edison {"NAME":"Lumary TS3Y","GPIO":[0,0,0,0,0,0,0,0,0,37,0,0,0],"FLAG":0,"BASE":18} -Luminea ZX-2982 ST64 Filament {"NAME":"Luminea ZX2982","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} -Merkury MI-BW902-999W 800lm {"NAME":"MI-BW902-999W","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -Merkury MI-BW942-999W 800lm {"NAME":"MI-BW942-999W","GPIO":[0,0,0,0,37,0,0,0,0,52,0,0,0],"FLAG":0,"BASE":18} -Merkury MIC-BW902-999W {"NAME":"MI-BW902-999W","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} -Merkury Vintage Edison A19 {"NAME":"Merkury A19 Ed","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} -Nedis A60 Filament {"NAME":"WIFILF10WTA60","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} -Nedis G125 Filament {"NAME":"WIFILF10GDG125","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} -Nedis PAR16 330lm {"NAME":"Nedis WIFILW31","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -Sealight Vintage Edison A19 {"NAME":"SealightEdison","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -``` - ## Bulb Socket ``` Elegant Choice E27/E26 {"NAME":"name","GPIO":[0,0,0,0,0,0,0,0,0,0,0,21,0],"FLAG":0,"BASE":18} @@ -299,6 +24,59 @@ Slampher {"NAME":"Slampher","GPIO":[17,255,0,255,0,0,0,0,21,56,0 SmartBase E0260 {"NAME":"SmartBaseE0260","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} ``` +## CCT +``` +AICase 800lm {"NAME":"AICase Smart L","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +Ajax Online 380lm {"NAME":"AjaxOnline","GPIO":[17,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":38} +Ajax Online 7W Vintage {"NAME":"AjaxOnline-7W","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Anoopsyche 9W 800lm {"NAME":"Anoop-CW-WW","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +Arlec Smart 1350lm PAR38 {"NAME":"Arlec GLD302HA","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Arlec Smart 9.5W 806lm {"NAME":"Arlec GLD110HA","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48} +Arlec Smart 9.5W 806lm {"NAME":"Arlec CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48} +BrilliantSmart 20696 9W 900lm {"NAME":"Brilliant20696","GPIO":[0,0,0,0,0,0,0,0,37,0,38,0,0],"FLAG":0,"BASE":48} +BrilliantSmart 20697 9W 900lm {"NAME":"Brilliant20699","GPIO":[0,0,0,0,0,0,0,0,37,0,38,0,0],"FLAG":0,"BASE":48} +Bulbrite Solana A19 Edison Filament {"NAME":"BulbBrite01","GPIO":[0,0,0,0,0,0,0,0,37,0,38,0,0],"FLAG":0,"BASE":18} +Calex G125 7,5W 1055lm {"NAME":"Calex G125 E27","GPIO":[0,0,0,0,0,140,0,0,38,0,37,142,141],"FLAG":0,"BASE":18} +Calex G125 7.5W 1055lm Globe {"NAME":"Calex G125 E27","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Deltaco SH-LE14W 470lm {"NAME":"SH-LE14W","GPIO":[0,0,0,0,0,140,0,0,38,0,37,142,141],"FLAG":0,"BASE":18} +Deltaco SH-LE27W 810lm {"NAME":"SH-LE27W","GPIO":[0,0,0,0,0,140,0,0,38,0,37,142,141],"FLAG":0,"BASE":18} +DORESshop A60 720lm Filament {"NAME":"DORESshop-A60","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Energizer A19 10W {"NAME":"Energizer CCT ","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +Euri Lighting A19 10W 800lm {"NAME":"Euri Lighting ","GPIO":[0,0,0,0,37,38,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Geeni LUX 1050lm {"NAME":"Geeni-1050-WW","GPIO":[0,0,0,0,37,37,0,0,38,0,0,0,0],"FLAG":1,"BASE":18} +Globe 34208 A19 800lm {"NAME":"GlobeCCT","GPIO":[0,0,0,0,38,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Globe 34919 ST19 Edison 500lm {"NAME":"Globe 34919","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Hama 806lm {"NAME":"Hama 00176550","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +Hykker SL-0392 650lm {"NAME":"Hykker 7W","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +Iotton 9W 700lm {"NAME":"Iotton Light","GPIO":[0,0,0,0,37,38,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Kogan 10W Cool & Warm White 1050lm {"NAME":"Kogan White/Wa","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +LE lampUX 380lm Candle {"NAME":"LE Bulb","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +LE LampUX 4.5W 410lm {"NAME":"LE LampUX","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":48} +ledscom.de 4.5W 430lm {"NAME":"GX53","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":0} +Lohas ZN070 720lm {"NAME":"Lohas ZN070","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Lumiman A19 7.5W 800lm {"NAME":"Lumiman LM520","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +Luminea ZX-2831 {"NAME":"Luminea CCT","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Merkury MI-BW905-999W 700lm {"NAME":"MI-BW905-999W","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +Mimoodz 1050lm {"NAME":"ID Components","GPIO":[0,0,0,0,21,22,0,0,23,24,25,26,27],"FLAG":0,"BASE":18} +Mirabella Genio 9W 800lm {"NAME":"GenioBulbCCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +Mirabella Genio 9W 800lm {"NAME":"GenioBulbCCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +Mirabella Genio 9W 800lm {"NAME":"GenioBulbCCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +Mirabella Genio I002745 SES 470lm {"NAME":"Mirabella Candle","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Mirabella Genio I002746 500lm {"NAME":"GenioGU10","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Mirabella Genio I002747 G95 470lm {"NAME":"Genio Filament","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Nedis A60 800lm {"NAME":"WIFILW10WTE27","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +Nedis C10 350lm {"NAME":"WIFILW10WTE14","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +Nedis PAR16 330lm {"NAME":"Nedis WIFILW30","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +Nedis PAR16 4.5W 330lm 110 {"NAME":"WIFILW30","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +Philips Zhirui Candle 250lm {"NAME":"Xiaomi Philips","GPIO":[0,0,0,0,0,0,0,0,38,0,0,37,0],"FLAG":0,"BASE":48} +Phillips Zhirui 450lm {"NAME":"Xiaomi Philips","GPIO":[0,0,0,0,0,0,0,0,38,0,0,37,0],"FLAG":0,"BASE":48} +SmartDGM L-WT9W1 9W 800lm {"NAME":"L-WT9W1","GPIO":[0,0,0,0,0,37,0,0,9,38,0,0,0],"FLAG":0,"BASE":18} +Swisstone SH 330 806lm {"NAME":"SwisstoneSH330","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Vestaiot BR30 800lm {"NAME":"Vesta BR30 CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +Wipro Garnet NS9100 810lm {"NAME":"WiproSmartBulb","GPIO":[0,0,0,0,38,38,0,0,37,37,0,0,0],"FLAG":0,"BASE":18} +Wyze WLPA19 A19 800lm {"NAME":"Wyze Bulb","GPIO":[0,0,0,0,0,0,0,0,0,37,38,0,0],"FLAG":0,"BASE":48} +``` + ## Curtain Motor ``` Zemismart BCM300D-TY {"NAME":"Zemistart_Curt","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54} @@ -307,8 +85,11 @@ Zemismart BCM300D-TY {"NAME":"Zemistart_Curt","GPIO":[0,0,0,0,0,0,0,0,0,108, ## Curtain Switch ``` Anccy Relax {"NAME":"Tuya Shutter","GPIO":[157,0,54,10,22,19,0,0,17,21,53,23,52],"FLAG":0,"BASE":18} +Etersky WF-CS01 {"NAME":"Etersky","GPIO":[157,0,53,19,23,18,0,0,17,21,54,22,52],"FLAG":0,"BASE":18} Homecube Blinds and Curtains {"NAME":"Jinvoo Curtain","GPIO":[52,0,0,18,22,19,0,0,17,21,0,23,0],"FLAG":1,"BASE":18} -Jinvoo SM-SW101-C Curtain {"NAME":"Jinvoo Curtain","GPIO":[52,0,0,18,22,19,0,0,17,21,0,23,0],"FLAG":1,"BASE":18} +Jinvoo SM-SW101-C {"NAME":"Jinvoo Curtain","GPIO":[52,0,0,18,22,19,0,0,17,21,0,23,0],"FLAG":1,"BASE":18} +LoraTap SC400W-EU {"NAME":"Loratap SC400W","GPIO":[0,0,0,19,0,17,0,0,18,22,0,21,0],"FLAG":0,"BASE":18} +LoraTap SC411WSC-EU RF Remote {"NAME":"Loratap","GPIO":[0,0,0,19,23,17,0,0,18,22,0,21,0],"FLAG":0,"BASE":18} LoraTap SC511WSC Roller Shutter {"NAME":"SC511WSC","GPIO":[0,255,0,56,17,18,0,0,21,19,22,0,0],"FLAG":0,"BASE":18} Teekar SYS-CS 01 {"NAME":"Teekar-Tag","GPIO":[56,0,157,18,22,11,0,0,0,21,57,31,17],"FLAG":0,"BASE":18} Teepao {"NAME":"Taopao","GPIO":[255,255,255,18,22,19,0,0,255,21,255,255,17],"FLAG":1,"BASE":18} @@ -319,33 +100,86 @@ WF-CS02 {"NAME":"WF-CS02 Tuya","GPIO":[157,0,53,11,23,18,0,0,17 ## DIY ``` Adafruit HUZZAH {"NAME":"Huzzah","GPIO":[17,0,56,0,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":18} +ESP-01(S) {"NAME":"ESP01","GPIO":[255,255,255,255,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +ESP-M3 {"NAME":"ESP-M3","GPIO":[255,255,255,255,255,0,0,0,0,255,255,0,255],"FLAG":0,"BASE":18} Heltec WiFi Kit 8 {"NAME":"HTIT-W8266","GPIO":[255,255,255,255,6,5,0,0,255,255,255,255,162],"FLAG":15,"BASE":18} LC Tech relay and PZEM-004T {"NAME":"HW-655 PZEM","GPIO":[0,63,0,62,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} Luani HVIO {"NAME":"Luani HVIO","GPIO":[0,255,255,255,21,22,0,0,9,10,255,52,0],"FLAG":1,"BASE":35} +OLED Display Module 0.66" for Wemos D1 Mini {"NAME":"OLED 64x48","GPIO":[255,255,255,255,6,5,0,0,255,255,255,255,162],"FLAG":15,"BASE":18} Splatura USB Device Power Switch {"NAME":"Splatura USB","GPIO":[0,0,52,0,0,0,0,0,0,21,0,122,0],"FLAG":0,"BASE":18} SUPLA inCan by Espablo {"NAME":"Supla Espablo","GPIO":[0,255,4,255,17,21,0,0,255,22,255,0,52],"FLAG":1,"BASE":31} Witty Cloud {"NAME":"Witty Cloud","GPIO":[255,255,56,255,17,255,0,0,38,39,255,37,255],"FLAG":1,"BASE":32} ``` +## Dimmable +``` +Aisirer 9W 806lm {"NAME":"Aisirer 9W","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Aisirer 9W 806lm {"NAME":"AISIRER E26","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Aisirer 9W 806lm {"NAME":"Aisirer 9W","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Arlec Smart 4W 380lm Candle {"NAME":"Arlec Bulb 4W","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +Arlec Smart 9W 950lm 4000K {"NAME":"Arlec-GLD124HA","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Avatar ALS15L Candle {"NAME":"AVATAR","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Cleverio 51396 800lm {"NAME":"Cleverio E27","GPIO":[0,0,0,0,0,0,0,0,0,0,46,0,0],"FLAG":0,"BASE":18} +Connect Smart 10W {"NAME":"CSH-B22WW10W","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +DORESshop B11 600lm Filament {"NAME":"Doresshop-cand","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +DORESshop ST64 720lm Filament {"NAME":"DORESshop-ST64","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Feit Electric 800lm {"NAME":" Feit P_A800_2","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Feit Electric BR30 650lm {"NAME":"Feit BR30 WW","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Feit Electric BR30 650lm {"NAME":"Feit BR30 CW","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Geeni LUX Edison {"NAME":"Geeni-Edison","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +Globe 34209 800lm {"NAME":"Globe WW 800lm","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Hama 7W 900lm Filament {"NAME":"Hama Filament","GPIO":[255,255,255,255,255,255,255,255,255,255,46,255,255],"FLAG":15,"BASE":18} +KMC 70113 A19 7.5W {"NAME":"Generic","GPIO":[0,0,0,0,0,0,37,38,0,0,0,0,0],"FLAG":15,"BASE":18} +Kogan ST-20 Filament {"NAME":"Kogan Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":15,"BASE":18} +Krisbow Bohlam 14W {"NAME":"Krisbow SmartL","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48} +LeDesign 8W ST21 Filament {"NAME":"Edison Bulb","GPIO":[0,0,0,0,0,0,0,0,0,37,0,0,0],"FLAG":0,"BASE":18} +Lohas LZN127 G25 800lm {"NAME":"Lohas Globe","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +Lohas ZN014 550lm {"NAME":"Lohas MR16","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +Lohas ZN124 980lm {"NAME":"Lohas LH-ZN124","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +LSC Smart Connect Filament A60 {"NAME":"LSC Filam E27","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +LSC Smart Connect Filament C35 {"NAME":"LSC Filam E14","GPIO":[0,255,0,255,0,0,0,0,38,0,37,0,0],"FLAG":15,"BASE":18} +LSC Smart Connect Filament G125 {"NAME":"LSC Filam Huge","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +LSC Smart Connect Filament ST64 {"NAME":"LSC Filam Big","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Lumary 6W 700lm Edison {"NAME":"Lumary TS3Y","GPIO":[0,0,0,0,0,0,0,0,0,37,0,0,0],"FLAG":0,"BASE":18} +Luminea ZX-2880 A60 800lm {"NAME":"LAV-110.w","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Luminea ZX-2982 ST64 Filament {"NAME":"Luminea ZX2982","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +Merkury 9W 800lm {"NAME":"MI-BW320-999W","GPIO":[0,0,0,0,0,0,0,0,0,0,21,0,0],"FLAG":0,"BASE":18} +Merkury MI-BW902-999W 800lm {"NAME":"MI-BW902-999W","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Merkury MI-BW942-999W 800lm {"NAME":"MI-BW942-999W","GPIO":[0,0,0,0,37,0,0,0,0,52,0,0,0],"FLAG":0,"BASE":18} +Merkury MIC-BW902-999W {"NAME":"MI-BW902-999W","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +Merkury Vintage Edison A19 {"NAME":"Merkury A19 Ed","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +Mirabella Genio 800lm {"NAME":"GenioBulbCW","GPIO":[0,0,0,0,38,37,0,0,0,40,39,0,0],"FLAG":0,"BASE":18} +Mirabella Genio 9W 800lm {"NAME":"GenioB22","GPIO":[0,0,0,0,0,0,0,0,0,37,0,0,0],"FLAG":0,"BASE":18} +Mirabella Genio A70 1400lm {"NAME":"GenioB_CW2744","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Nedis A60 Filament {"NAME":"WIFILF10WTA60","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +Nedis G125 Filament {"NAME":"WIFILF10GDG125","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +Nedis PAR16 330lm {"NAME":"Nedis WIFILW31","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Sealight Vintage Edison A19 {"NAME":"SealightEdison","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +TCP Smart 810lm Filament {"NAME":"TCP Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,46,0,0],"FLAG":0,"BASE":18} +``` + ## Dimmer ``` +3A Smart Home HGZB-04D {"NAME":"HGZB-4D","GPIO":[255,255,255,255,255,255,0,0,255,255,54,255,255],"FLAG":0,"BASE":54} +Acenx SD03 {"NAME":"SD03","GPIO":[19,18,0,59,158,58,0,0,57,37,56,122,29],"FLAG":0,"BASE":73} Armtronix AC Dimmer One Triac Board {"NAME":"ARMTR Dimmer","GPIO":[0,148,0,149,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":56} Armtronix AC Dimmer Two Triac Board {"NAME":"ARMTR Dimmer","GPIO":[0,148,0,149,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":56} BrilliantSmart D350W {"NAME":"Generic","GPIO":[255,107,255,108,255,255,0,0,255,0,255,0,255],"FLAG":0,"BASE":54} CE Smart Home CFW500D-3W 3 Way {"NAME":"CE-WF500D-3W","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54} -CE Smart Home CWF500D-3W {"NAME":"CE-WF500D-3W","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54} CE Smart Home WF500D {"NAME":"CE-WF500D","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54} Eva Logik WF31 {"NAME":"WF31 Dimmer","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} EX-Store 2 Kanal RS232 V4 {"NAME":"EXS Dimmer","GPIO":[0,148,0,149,0,0,0,0,0,183,0,0,0],"FLAG":0,"BASE":72} +Feit Electric DIM/WIFI {"NAME":"Generic","GPIO":[255,107,255,108,255,255,0,0,255,0,255,0,255],"FLAG":0,"BASE":54} Gosund SW2 {"NAME":"Gosund Dimmer","GPIO":[255,148,255,149,17,0,255,255,56,158,37,255,255],"FLAG":0,"BASE":18} -Martin Jerry MJ-SD02 {"NAME":"MJ-SD02","GPIO":[19,17,0,33,34,32,255,255,31,37,30,127,29],"FLAG":15,"BASE":18} +Martin Jerry MJ-SD01 {"NAME":"MJ-SD02","GPIO":[19,18,0,59,158,58,0,0,57,37,56,122,29],"FLAG":0,"BASE":73} Moes DS01-1 {"NAME":"MOES DS01","GPIO":[255,255,255,255,255,255,0,0,255,108,255,107,255],"FLAG":0,"BASE":54} Moes MS-105-1 v2 {"NAME":"MS-105","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} PS-16-DZ {"NAME":"PS-16-DZ","GPIO":[255,148,255,149,255,255,0,0,255,52,255,255,255],"FLAG":0,"BASE":58} -QS-WiFi-D01 150W {"NAME":"WiFi-Dimmer","GPIO":[0,148,0,149,0,0,0,0,0,42,37,0,0],"FLAG":0,"BASE":18} +QS-WiFi-D01-TRIAC 150W {"NAME":"WiFi-Dimmer","GPIO":[0,148,0,149,0,0,0,0,0,42,37,0,0],"FLAG":0,"BASE":18} RJWF-02A {"NAME":"RJWF-02A","GPIO":[17,107,0,108,0,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":54} Sonoff D1 {"NAME":"Sonoff D1","GPIO":[255,148,0,149,0,0,0,0,0,56,0,0,0],"FLAG":0,"BASE":74} Teekar UIW001-1 {"NAME":"Teekar UIW001-","GPIO":[0,149,37,148,6,5,0,0,9,0,0,0,0],"FLAG":0,"BASE":18} +Tessan MJ-SD02 {"NAME":"MJ-SD02","GPIO":[19,18,0,59,158,58,0,0,57,37,56,122,29],"FLAG":0,"BASE":73} TreatLife DS01 {"NAME":"DS02S Dimmer","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} TreatLife DS02S {"NAME":"DS02S Dimmer","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} WF-DS01 {"NAME":"Dimmer WF-DS01","GPIO":[255,255,255,255,255,255,0,0,255,255,54,255,255],"FLAG":0,"BASE":54} @@ -356,21 +190,12 @@ Zemismart KS-7011 {"NAME":"KS-7011 Dimmer","GPIO":[255,107,255,108,255,25 ## Fan ``` Anko HEGSM40 {"NAME":"Anko HEGSM40","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54} -Arlec 45cm Smart DC Wall {"NAME":"Arlec 45cm Fan","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54} -Goldair SleepSmart GCPF315 {"NAME":"Goldair Fan","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54} -``` - -## Fan Controller -``` +Arlec Smart 45cm Smart DC Wall {"NAME":"Arlec 45cm Fan","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54} BrilliantSmart 99111 {"NAME":"Brilliant Fan","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} +Goldair SleepSmart GCPF315 {"NAME":"Goldair Fan","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54} Lucci Connect Remote Control {"NAME":"Lucci Fan","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54} -Sonoff iFan02 {"NAME":"Sonoff iFan02","GPIO":[17,255,0,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":44} -Sonoff iFan03 {"NAME":"SonoffiFan03","GPIO":[17,148,0,149,0,0,29,161,23,56,22,24,0],"FLAG":0,"BASE":71} -``` - -## Humidifier -``` -Proscenic 807C {"NAME":"Generic","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":15,"BASE":54} +Sonoff IFan02 {"NAME":"Sonoff iFan02","GPIO":[17,255,0,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":44} +Sonoff IFan03 {"NAME":"SonoffiFan03","GPIO":[17,148,0,149,0,0,29,161,23,56,22,24,0],"FLAG":0,"BASE":71} ``` ## IR Bridge @@ -383,16 +208,20 @@ Connect SmartHome Universal Smart IR Remote {"NAME":"CSH IR Bridge","GPIO":[255 Cusam CS-IRC-1 {"NAME":"YTF IR Bridge","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} Eachen IR DC6 {"NAME":"Eachen IR","GPIO":[0,0,0,0,56,51,0,0,255,17,8,0,0],"FLAG":0,"BASE":18} Geeklink GK01 {"NAME":"GL IR Blaster","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} +Jinvoo AC/TV Box Controller {"NAME":"Jinvoo IR Bridge","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} Mirabella Genio I002577 {"NAME":"Genio IR TxRx","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} +Nedis Universal Remote Control {"NAME":"Nedis IR Bridge","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} NEO Coolcam NAS-IR03W0 {"NAME":"Neo Coolcam IR","GPIO":[0,0,0,0,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} +RM mini {"NAME":"RM mini","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} STITCH by Monoprice 35753 {"NAME":"Stitch 35753","GPIO":[0,0,0,0,52,51,0,0,0,90,8,0,0],"FLAG":0,"BASE":18} +SZMDLX IR Remote Controller {"NAME":"SZMDLX WiFi IR","GPIO":[0,0,0,0,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} YTF Universal Remote {"NAME":"YTF IR Bridge","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} ``` ## Kettle ``` Kogan 1.7L Smart Glass {"NAME":"Kogan Kettle","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} -ProfiCook PC-WKS 1167 G 1.5 L {"NAME":"PC-WKS Kettle","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} +ProfiCook PC-WKS 1167G 1.5L {"NAME":"PC-WKS 1167G","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} ``` ## LED Controller @@ -401,6 +230,7 @@ Anncoe C350 RGB {"NAME":"TUYA LED","GPIO":[0,0,0,0,0,38,0,0,39,17,37,0, Arilux AL-LC01 {"NAME":"Arilux LC01","GPIO":[17,0,59,0,147,37,0,0,38,39,0,0,0],"FLAG":0,"BASE":37} Arilux AL-LC06 {"NAME":"Arilux LC06","GPIO":[17,0,0,0,0,0,0,0,38,39,37,41,40],"FLAG":0,"BASE":18} Arilux AL-LC11 {"NAME":"Arilux LC11","GPIO":[17,0,59,0,38,37,0,0,41,40,39,147,0],"FLAG":0,"BASE":38} +Arilux SL-LC 03 {"NAME":"Arilux LC03","GPIO":[0,0,0,0,51,38,0,0,37,39,0,40,0],"FLAG":0,"BASE":34} Arilux SL-LC 09 {"NAME":"Arilux LC09","GPIO":[0,0,0,0,106,37,0,0,39,0,38,0,0],"FLAG":0,"BASE":18} DD001-MINI(G)-IR-V08 {"NAME":"WIFI-RGB","GPIO":[0,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} Electrodragon ESP LED Strip Board, Mosfet Drive {"NAME":"LEDBoard RGBW","GPIO":[0,0,0,0,0,0,0,0,39,38,40,37,52],"FLAG":0,"BASE":18} @@ -408,12 +238,14 @@ H801 {"NAME":"H801","GPIO":[0,52,0,0,41,57,0,0,39,38,40,37,0 LEDEnet {"NAME":"LEDEnet","GPIO":[0,255,56,255,147,41,0,0,38,39,37,40,0],"FLAG":0,"BASE":34} Luminea ZX-2844 {"NAME":"Luminea ZX-284","GPIO":[40,0,0,0,0,39,0,0,38,17,37,0,0],"FLAG":0,"BASE":18} Luminea ZX-2844-675 {"NAME":"ZX-2844-675","GPIO":[17,0,0,0,38,40,0,0,37,0,39,0,0],"FLAG":0,"BASE":18} +Lustreon {"NAME":"Lustreon WiFi ","GPIO":[17,0,55,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":38} Magic UFO RGBW {"NAME":"Magic UFO","GPIO":[17,0,157,0,0,37,0,0,39,40,38,56,0],"FLAG":0,"BASE":18} MagicHome RGB ZJ-WFMN-A V1.1 {"NAME":"MagicHome RGB","GPIO":[0,0,0,0,0,37,0,0,38,39,0,0,0],"FLAG":0,"BASE":34} MagicHome RGBW ESP-IR-B-v2.3 {"NAME":"ESP-IR-B-v2.3","GPIO":[0,0,51,0,0,38,0,0,37,39,0,40,0],"FLAG":0,"BASE":18} MagicHome RGBW ZJ-WFMN-A V1.1 {"NAME":"MagicHome RGBW","GPIO":[0,0,0,0,51,38,0,0,37,39,0,40,0],"FLAG":0,"BASE":34} MagicHome RGBWW w/ RF {"NAME":"MagicHome RF","GPIO":[0,0,159,0,37,38,0,0,41,40,39,147,0],"FLAG":0,"BASE":38} MagicHome RGBWW w/ RF {"NAME":"MagicHome RF","GPIO":[0,0,0,0,147,40,0,0,38,39,37,41,159],"FLAG":0,"BASE":18} +MagicHome Single Color 5-28V {"NAME":"MagicHome","GPIO":[0,0,0,0,0,0,0,0,37,0,0,0,0],"FLAG":0,"BASE":18} MagicHome ZJ-ESP-IR-F V1 {"NAME":"ZJ-ESP-IR-F V1","GPIO":[0,0,0,0,51,38,0,0,37,39,0,40,0],"FLAG":0,"BASE":18} Maxonar Lightstrip Pro XS-SLD001 {"NAME":"Maxonar LED","GPIO":[0,0,0,0,0,37,0,0,39,17,38,0,0],"FLAG":0,"BASE":18} Nexlux {"NAME":"MagicHome V1.1","GPIO":[0,0,0,0,51,37,0,0,38,39,0,0,0],"FLAG":0,"BASE":34} @@ -423,15 +255,20 @@ ZJ-WF-ESP-A v1.1 {"NAME":"RGB2","GPIO":[0,0,0,0,0,0,0,0,38,37,39,0,0],"F ## LED Strip ``` -Arlec 2m LED Colour Changing Strip Light {"NAME":"Arlec ALD233AH","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Arlec Smart 2m LED Colour Changing Strip Light {"NAME":"Arlec ALD233AH","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} B.K. Licht BKL1268 2m RGB {"NAME":"RGBW-Strip","GPIO":[0,0,0,0,37,17,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} BlitzWolf BW-LT11 {"NAME":"BW-LT11 Strip","GPIO":[17,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} BrilliantSmart 20743 RGB+W {"NAME":"BrilliantStrip","GPIO":[17,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} Briloner 2256-150 RGB {"NAME":"Briloner2256-1","GPIO":[51,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} Cocoon Smart {"NAME":"Cocoon Smart","GPIO":[17,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +HitLights L1012V-MC1 {"NAME":"HitLights RBG","GPIO":[17,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Hykker 3m RGB {"NAME":"HYKKER Strip","GPIO":[0,0,0,0,0,37,0,0,39,17,38,0,0],"FLAG":0,"BASE":18} +LE LampUX 16.4ft RGB {"NAME":"LampUX","GPIO":[0,18,17,0,0,38,0,0,39,51,0,37,0],"FLAG":0,"BASE":18} LE LampUX 2m RGB TV Backlight {"NAME":"LE 904102","GPIO":[0,17,18,0,0,38,0,0,39,19,0,37,0],"FLAG":0,"BASE":18} -LE LampUX 5m RGB LED Strip {"NAME":"LampUX","GPIO":[17,0,0,0,0,38,0,0,39,0,0,37,0],"FLAG":0,"BASE":18} -LE lampUX 5m RGBW {"NAME":"LampUX","GPIO":[17,0,0,0,37,40,255,255,38,41,39,0,0],"FLAG":0,"BASE":18} +LE LampUX 5m RGB {"NAME":"LampUX","GPIO":[17,0,0,0,0,38,0,0,39,0,0,37,0],"FLAG":0,"BASE":18} +LE LampUX 5m RGB {"NAME":"LE LampUx","GPIO":[0,0,0,0,0,38,0,0,39,0,0,37,0],"FLAG":0,"BASE":34} +LE lampUX 5m RGBW {"NAME":"LampUX","GPIO":[0,0,17,0,0,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} +Lohas ZN022 5m RGBW {"NAME":"LOHAS M5-022","GPIO":[0,0,0,0,38,37,0,0,17,39,0,0,0],"FLAG":0,"BASE":18} LSC Smart Connect RGBW {"NAME":"LSC RGBW Strip","GPIO":[51,0,0,0,37,0,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} Lumary RGBCCT {"NAME":"Lumary LED","GPIO":[17,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} Lumary RGBCCT {"NAME":"Lumary LED","GPIO":[17,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} @@ -446,8 +283,9 @@ Zemismart RGBW {"NAME":"Zemismart LED","GPIO":[0,0,0,0,38,37,0,0,0,39, ## Light ``` -Arlec 15W Security Floodlight {"NAME":"ArlecFlood","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":15,"BASE":18} -Arlec 40W 4000lm LED Batten Light {"NAME":"Arlec Batten","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Arlec Smart 15W Security Floodlight {"NAME":"ArlecFlood","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":15,"BASE":18} +Arlec Smart 40W 4000lm LED Batten {"NAME":"Arlec Batten","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Arlec Smart Portable Floodlight 10.5W {"NAME":"Arlec GLD301HA","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} BlitzWolf BW-LT20 {"NAME":"BW-LT20","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":15,"BASE":18} Brilliant CORDIA Colour Temperature Changing LED Flush Ceiling Light {"NAME":"Brilliant Oyst","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} BrilliantSmart 20695 Downlight CCT {"NAME":"SmartCCTDwnLgt","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48} @@ -456,25 +294,32 @@ BrilliantSmart RGB Garden Kit {"NAME":"Brilliant Gard","GPIO":[0,0,0,0,37,0,0,0 Calex 429250 Ceiling {"NAME":"Calex_LED","GPIO":[0,0,0,0,0,0,0,0,52,37,38,54,0],"FLAG":0,"BASE":18} Connect SmartHome CSH-240RGB10W {"NAME":"Connect1","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} Connect SmartHome CSH-FSTN12 {"NAME":"CSH-FSTN12","GPIO":[0,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Deta 18W 1900lm T8 Tube {"NAME":"DETA Smart LED","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} Deta DET902HA 10W 940lm RGB+CCT {"NAME":"Deta DownLight","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} -Fcmila XDD-48W {"NAME":"XDD-48W","GPIO":[0,0,0,0,37,40,0,0,38,25,39,0,0],"FLAG":0,"BASE":18} -Hama 10W RGB+WW {"NAME":"Hama Smart WiF","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +electriQ MOODL Ambiance Lamp {"NAME":"ElectriQ MOODL","GPIO":[0,201,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Fcmila 48W RGBCCT Ceiling Lamp {"NAME":"XDD-48W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Feit Electric 6in. RGBW Recessed Downlight {"NAME":"Feit LEDR6/RGB","GPIO":[0,0,0,0,37,40,0,0,38,50,39,0,0],"FLAG":0,"BASE":48} Hyperikon 14W 1000lm 6" Downlight {"NAME":"HyperikonDL6","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +iHomma 6W RGBCCT Downlight {"NAME":"iHomma RGBWW","GPIO":[0,0,0,0,41,40,0,0,37,38,39,0,0],"FLAG":0,"BASE":18} iHomma Downlight {"NAME":"iHommaLEDDownl","GPIO":[0,0,0,0,0,40,0,0,37,38,39,0,0],"FLAG":0,"BASE":18} -Infray 9W 900LM {"NAME":"InfrayRGBCCT","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Kogan 9W Downlight RGBCCT {"NAME":"Kogan_SMARTLED","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} LE lampUX 15W RGBCCT Ceiling {"NAME":"LE lampUX 15W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} Lohas ZN026CL10 RGBCCT {"NAME":"Lohas LED Lamp","GPIO":[0,0,0,0,38,37,0,0,40,39,41,0,0],"FLAG":0,"BASE":18} +Lumary 18W RGBCCT Recessed Panel {"NAME":"LumaryDLghtRGB","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} Mi LED Desk Lamp MJTD01YL {"NAME":"Mi Desk Lamp","GPIO":[0,0,17,0,37,38,0,0,150,151,0,0,0],"FLAG":0,"BASE":66} Mirabella Genio I002741 {"NAME":"GenioDLightRGB","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} Mirabella Genio I002742 {"NAME":"GenioDLightCCT","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":48} Mirabella Genio I002798 Warm White Filament Festoon {"NAME":"GenioFestoon","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18} +Moes 7W RGBCCT Downlight {"NAME":"Moes Downlight","GPIO":[0,0,0,0,40,41,0,0,37,38,39,0,0],"FLAG":0,"BASE":18} Novostella UT88835 20W Floodlight {"NAME":"Novo 20W Flood","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +SMRTLite LED Panel {"NAME":"SMRTLite","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} Sonoff BN-SZ01 {"NAME":"Sonoff BN-SZ","GPIO":[0,0,0,0,0,0,0,0,37,56,0,0,0],"FLAG":0,"BASE":22} Spotlight 9cm RGB+W 7W {"NAME":"Spotlight RGBW","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} Teckin FL41 {"NAME":"Teckin FL41","GPIO":[0,0,0,0,0,17,0,0,0,0,21,0,0],"FLAG":0,"BASE":18} Utorch PZE-911 {"NAME":"Utorch PZE-911","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":1} Utorch UT40 {"NAME":"Utorch UT40","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":1} Verve Design Angie 18W Ceiling Light With RGB Ring {"NAME":"ACL12HA Light","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Verve Design Charlie 22W CCT Ceiling {"NAME":"Verve ACL01HA","GPIO":[0,0,0,0,0,37,0,0,0,47,0,0,0],"FLAG":0,"BASE":48} Verve Design Hana 24W CCT Ceiling {"NAME":"Verve ACL03HA","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":48} Wipro Next 20W Smart LED Batten {"NAME":"WIPROBatten","GPIO":[0,0,0,0,0,37,0,0,0,47,0,0,0],"FLAG":1,"BASE":18} Zemismart 4" 10W RGBCCT {"NAME":"ZemiDownLight4","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} @@ -482,6 +327,14 @@ Zemismart 4" 10W RGBW {"NAME":"ZemiDownLight","GPIO":[0,0,0,0,0,0,0,0,0,143,0 Zemismart 6" 14W RGBCCT {"NAME":"ZemiDownLight6","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} ``` +## Miscellaneous +``` +iLONDA Fish feeder {"NAME":"Feeder","GPIO":[0,0,0,0,17,56,0,0,42,0,21,0,0],"FLAG":0,"BASE":18} +Mosquito Killer Lamp {"NAME":"MosquitoKiller","GPIO":[17,0,0,0,0,0,0,0,37,56,0,0,0],"FLAG":0,"BASE":18} +Proscenic 807C Humidifier {"NAME":"Generic","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} +Sonoff RM433 RF Remote Controller {"NAME":"REQUIRES RF DEVICE"} +``` + ## Outdoor Plug ``` Acenx SOP04-US Dual {"NAME":"SOP04-US Dual","GPIO":[255,255,255,255,56,57,0,0,21,17,22,255,255],"FLAG":0,"BASE":18} @@ -495,22 +348,24 @@ C168 IP64 {"NAME":"C188","GPIO":[56,0,57,0,18,0,0,0,21,17,157,22, ECF-SOP03 {"NAME":"Outdoor3Outlet","GPIO":[0,0,0,23,56,0,0,0,21,17,22,0,0],"FLAG":0,"BASE":18} Ecoolbuy 4 socket {"NAME":"ECCOLBUY 4","GPIO":[0,0,0,0,22,23,0,0,21,57,17,0,24],"FLAG":0,"BASE":18} Feit Electric PLUG/WIFI/WP {"NAME":"Prime Smart ou","GPIO":[0,255,0,255,157,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Forrinx SH-18EU-A {"NAME":"SH-18EU-A","GPIO":[0,0,0,0,22,52,21,57,17,0,0,0,0],"FLAG":0,"BASE":18} Geeni Outdoor {"NAME":"Geeni Outdoor","GPIO":[17,0,0,0,0,57,0,0,0,52,21,0,0],"FLAG":0,"BASE":18} Geeni Outdoor DUO Dual Outlet {"NAME":"Geeni Dual Out","GPIO":[17,0,0,0,0,57,0,0,0,56,21,0,22],"FLAG":0,"BASE":18} HA109US {"NAME":"HA109US","GPIO":[17,0,0,0,52,53,0,0,21,0,22,0,0],"FLAG":0,"BASE":18} iClever IC-BS06 {"NAME":"iClever Switch","GPIO":[0,0,0,0,157,56,0,0,21,17,22,0,0],"FLAG":0,"BASE":18} King-Link C128 {"NAME":"King-Link C128","GPIO":[0,0,58,0,22,56,0,0,23,157,17,21,57],"FLAG":0,"BASE":18} +Kogan Energy Meter IP44 {"NAME":"Kogan Smart Sw IP44","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18} LEPOWER {"NAME":"LEPOWER Outdoo","GPIO":[255,255,255,255,56,57,0,0,21,17,22,255,255],"FLAG":0,"BASE":18} Luminea NX-4458 {"NAME":"Luminea NX4458","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":65} Maxcio EOP03-EU {"NAME":"Maxcio EOP03-EU","GPIO":[0,0,0,0,22,57,0,0,21,52,17,0,58],"FLAG":0,"BASE":18} Maxcio SOP02-US {"NAME":"Maxcio SOP02US","GPIO":[0,0,0,0,0,157,0,0,21,17,22,0,0],"FLAG":15,"BASE":18} Merkury MI-OW101-101W {"NAME":"Merkury Switch","GPIO":[17,255,255,255,0,56,0,0,0,54,21,255,255],"FLAG":1,"BASE":18} Minoston MP22W {"NAME":"Minoston MP22W","GPIO":[0,0,0,0,56,0,0,0,21,90,0,0,0],"FLAG":0,"BASE":18} -Nedis IP44 PO120 {"NAME":"WIFIPO120FWT","GPIO":[17,0,0,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49} +Nedis PO120 IP44 {"NAME":"WIFIPO120FWT","GPIO":[17,0,0,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49} Obi Stecker IP44 {"NAME":"OBI Socket 2","GPIO":[0,0,0,0,21,17,0,0,56,53,0,0,0],"FLAG":0,"BASE":61} Oittm Outdoor {"NAME":"Oittm Outdoor","GPIO":[17,0,0,0,0,0,0,0,0,0,56,21,255],"FLAG":0,"BASE":18} Peteme PS-1602 {"NAME":"Peteme Outdoor","GPIO":[17,0,0,0,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":18} -Prime CCRCWFIO2PK {"NAME":"Prime Outdoor","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Prime RCWFIO 2-Outlet {"NAME":"Prime Outdoor","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Signstek EOP03-EU {"NAME":"Signstek EOP03","GPIO":[0,0,0,0,56,57,0,0,21,17,22,0,0],"FLAG":15,"BASE":18} SK03 {"NAME":"SK03 Outdoor","GPIO":[17,0,0,0,133,132,0,0,131,57,56,21,0],"FLAG":0,"BASE":57} STITCH by Monoprice 35556 {"NAME":"STITCH 35556","GPIO":[255,255,255,255,22,57,0,0,21,56,17,255,255],"FLAG":0,"BASE":18} @@ -525,20 +380,23 @@ Ucomen PA-GEBA-01SWP {"NAME":"PA-GEBA-01SWP","GPIO":[0,0,0,0,52,57,0,0,21,17 ## Plug ``` 2nice SP111 {"NAME":"2NICE SP111","GPIO":[56,0,57,0,0,0,0,0,0,17,0,21,0],"FLAG":2,"BASE":18} -2nice UP111 {"NAME":"2NICE UP111","GPIO":[0,52,0,17,134,132,0,0,131,157,21,0,0],"FLAG":0,"BASE":18} +2nice UP111 {"NAME":"2NICE UP111","GPIO":[0,158,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18} 3Stone Mini {"NAME":"3Stone Smart P","GPIO":[0,17,0,0,0,0,0,0,0,57,21,0,0],"FLAG":0,"BASE":18} +7hSevenOn Home 10020 {"NAME":"7hSevenOn","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +7hSevenOn Home 10022 USB {"NAME":"7hSevenOn","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":18} Ablue WP1 {"NAME":"Ablue","GPIO":[57,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18} Aigoss 16A Mini {"NAME":"Aigoss Plug","GPIO":[255,255,0,255,52,21,0,0,54,255,17,0,255],"FLAG":15,"BASE":51} Aisirer AWP07L {"NAME":"AISIRER AWP07L","GPIO":[56,0,57,0,0,133,0,0,131,17,132,21,0],"FLAG":0,"BASE":18} Aisirer AWP07L v2 {"NAME":"AWP07L v2","GPIO":[0,17,57,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18} +Aisirer AWP07L v3 {"NAME":"AWP07L v3","GPIO":[0,17,52,0,134,132,0,0,130,53,21,0,0],"FLAG":0,"BASE":18} Aisirer AWP08L {"NAME":"AISIRER AWP08L","GPIO":[0,0,56,0,0,0,0,0,0,0,0,21,0],"FLAG":0,"BASE":18} Aisirer AWP08L v2 {"NAME":"AISIRER AWP08L","GPIO":[0,0,0,0,17,57,0,0,21,0,0,0,0],"FLAG":0,"BASE":18} -Aisirer JH-G018 v2 {"NAME":"AISIRER JH-G01","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} -Aisirer JH-G01B {"NAME":"AISIRER","GPIO":[0,0,0,0,21,0,0,0,56,17,0,0,0],"FLAG":1,"BASE":18} +Aisirer JH-G018 {"NAME":"AISIRER JH-G01","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Aisirer SWA11 {"NAME":"SWA11","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} Aisirer UK-1 {"NAME":"AISIRER","GPIO":[0,17,0,0,0,0,0,0,0,52,21,0,0],"FLAG":1,"BASE":18} Alexfirst TV-ASP801EU {"NAME":"Alexfirst","GPIO":[17,0,0,0,54,56,0,0,21,0,0,0,0],"FLAG":0,"BASE":18} Alfawise PE1004T {"NAME":"PE1004T","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Alfawise PF1006 {"NAME":"PF1006","GPIO":[0,0,17,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} Alfawise PME1606 {"NAME":"PME1606","GPIO":[0,0,0,17,133,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18} Amysen JSM-WF02 {"NAME":"Amysen JSMWF02","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} Amysen YX-WS01 {"NAME":"Amysen YX-WS01","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} @@ -555,29 +413,39 @@ Aoycocr U3S {"NAME":"Aoycocr U3S","GPIO":[56,255,57,255,0,134,0,0,1 Aoycocr X10S {"NAME":"Aoycocr X10S","GPIO":[56,0,57,0,21,134,0,0,131,17,132,0,0],"FLAG":0,"BASE":45} Aoycocr X5P {"NAME":"Aoycocr X5P","GPIO":[56,0,57,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} Aoycocr X6 {"NAME":"Aoycocr X6","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} -Arlec PC189HA {"NAME":"Arlec Single","GPIO":[0,0,0,0,57,0,0,0,21,0,90,0,0],"FLAG":0,"BASE":18} -Arlec PC190HA {"NAME":"Arlec-PC190HA","GPIO":[0,0,0,0,0,0,0,0,21,56,17,0,0],"FLAG":0,"BASE":18} +Aquiv S1 {"NAME":"Aquiv S1","GPIO":[0,0,157,0,56,0,0,0,21,17,0,0,0],"FLAG":15,"BASE":18} +Arlec 10m Smart Extension Lead {"NAME":"Arlec Ext Cord","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} +Arlec Smart 2.1A USB Charger {"NAME":"Arlec Single","GPIO":[0,0,0,0,57,0,0,0,21,0,90,0,0],"FLAG":0,"BASE":18} +Arlec Smart PC189HA {"NAME":"Arlec Single","GPIO":[0,0,0,0,57,0,0,0,21,0,90,0,0],"FLAG":0,"BASE":18} +Arlec Smart PC190HA {"NAME":"Arlec-PC190HA","GPIO":[0,0,0,0,0,0,0,0,21,56,17,0,0],"FLAG":0,"BASE":18} +Arlec Smart PC399HA Plug {"NAME":"PC399HA","GPIO":[0,0,0,17,134,132,0,0,131,158,21,0,0],"FLAG":0,"BASE":52} Arlec Twin PC288HA {"NAME":"Arlec Twin","GPIO":[0,17,0,22,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} Atomi AT1217 {"NAME":"AT1217","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} -Aukey SH-PA1 {"NAME":"AUKEY SH-PA1","GPIO":[0,0,57,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} +Aukey SH-PA1 {"NAME":"AUKEY SH-PA1","GPIO":[56,0,57,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} +Aukey SH-PA3 {"NAME":"Aukey SH-PA3","GPIO":[0,0,158,0,57,17,0,0,22,0,0,21,0],"FLAG":0,"BASE":18} Aunics Smart EU {"NAME":"AUNICS","GPIO":[0,0,0,17,134,132,0,0,131,157,21,0,0],"FLAG":0,"BASE":18} Aunics Smart IT {"NAME":"AUNICS","GPIO":[0,0,0,17,134,132,0,0,131,157,21,0,0],"FLAG":0,"BASE":18} Avatar AWP01L {"NAME":"AWP01L","GPIO":[0,0,0,0,0,56,0,0,0,17,21,0,0],"FLAG":0,"BASE":18} Avatar AWP02L-N {"NAME":"AWP02L-N","GPIO":[158,0,0,0,56,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} +Avatar AWP03L {"NAME":"AWP03L","GPIO":[0,0,0,0,0,0,0,0,21,17,56,57,0],"FLAG":0,"BASE":18} Avatar AWP04L {"NAME":"AWP04L","GPIO":[57,255,255,131,255,134,0,0,21,17,132,56,255],"FLAG":0,"BASE":18} -Avatar AWP07L {"NAME":"AWP07L","GPIO":[56,255,255,255,255,134,0,0,130,17,132,21,255],"FLAG":1,"BASE":18} -Avatar AWP07L v2 {"NAME":"AWP07L","GPIO":[56,255,255,255,255,134,255,255,131,17,132,21,255],"FLAG":0,"BASE":18} +Avatar AWP07L {"NAME":"AWP07L","GPIO":[56,255,255,255,255,134,255,255,131,17,132,21,255],"FLAG":0,"BASE":18} Avatar AWP08L {"NAME":"AWP08L","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} -Avatar AWP12L Capsule 2-in-1 {"NAME":"Dual Plug","GPIO":[0,0,0,0,0,0,0,0,21,17,0,22,0],"FLAG":0,"BASE":18} +Avatar AWP12L Capsule 2-in-1 {"NAME":"AWP12L","GPIO":[56,0,0,131,18,134,0,0,21,17,132,22,0],"FLAG":0,"BASE":18} Avatar AWP14H {"NAME":"Avatar UK 10A","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} Avatto JH-G01E {"NAME":"AVATTO JH-G01E","GPIO":[0,145,0,146,0,0,0,0,17,56,21,0,0],"FLAG":0,"BASE":41} Avatto NAS-WR01W 10A {"NAME":"AvattoNAS-WR01W","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Avatto OT06 16A {"NAME":"Avatto OT06","GPIO":[17,0,0,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49} -AWP02L-N {"NAME":"AWP02L-N","GPIO":[57,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18} +Avatto OT08 {"NAME":"Avatto OT08","GPIO":[37,0,39,0,38,134,0,0,130,132,21,0,0],"FLAG":0,"BASE":18} +Awow X5P {"NAME":"Awow","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} +AWP02L-N {"NAME":"AWP02L-N","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} AzpenHome Smart {"NAME":"Socket2Me","GPIO":[52,255,255,255,22,255,0,0,21,255,17,255,255],"FLAG":0,"BASE":18} Bakibo TP22Y {"NAME":"Bakibo TP22Y","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":52} +Bardi 16A {"NAME":"BARDI","GPIO":[56,0,0,0,0,134,0,0,21,17,132,57,131],"FLAG":0,"BASE":18} Bauhn ASPU-1019 {"NAME":"Buahn Smart Pl","GPIO":[0,0,0,0,21,22,0,0,0,56,17,0,0],"FLAG":0,"BASE":18} +Bearware 303492 3AC+2USB {"NAME":"Bearware 30349","GPIO":[0,56,0,17,22,23,0,0,24,21,157,0,0],"FLAG":0,"BASE":18} Bestek MRJ1011 {"NAME":"BestekMRJ1011","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":1} +BlitzWolf BW-SHP10 {"NAME":"BW-SHP10","GPIO":[0,0,0,0,157,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} BlitzWolf BW-SHP2 {"NAME":"BlitzWolf SHP","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} BlitzWolf BW-SHP3 {"NAME":"BlitzWolf SHP3","GPIO":[56,0,57,0,22,134,0,0,131,18,132,21,17],"FLAG":0,"BASE":45} BlitzWolf BW-SHP3 alt {"NAME":"BlitzWolf SHP3","GPIO":[18,56,0,131,134,132,0,0,17,0,22,21,0],"FLAG":0,"BASE":18} @@ -587,7 +455,9 @@ BlitzWolf BW-SHP6 10A {"NAME":"BW-SHP6 10A","GPIO":[158,255,56,255,0,134,0,0, Blitzwolf BW-SHP6 15A {"NAME":"Blitzwolf SHP6","GPIO":[56,255,158,255,132,134,0,0,131,17,0,21,0],"FLAG":0,"BASE":45} BlitzWolf BW-SHP7 {"NAME":"SHP7","GPIO":[17,158,57,131,134,132,0,0,18,56,21,0,22],"FLAG":0,"BASE":45} BN-LINK BNC-60/U133TJ-2P {"NAME":"BNC-60/U133TJ","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":18} +BNETA IoT {"NAME":"BNETA WifiPlug","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18} Brennenstuhl WA 3000 XS01 {"NAME":"WA 3000 XS01","GPIO":[0,0,0,0,21,17,0,0,158,52,0,0,0],"FLAG":0,"BASE":61} +Bright {"NAME":"Bright Wi-Fi Smart Plug","GPIO":[0,0,0,0,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18} Brilliant HK17654S05 {"NAME":"HK17654S05","GPIO":[17,255,255,255,133,132,255,255,131,56,21,255,255],"FLAG":0,"BASE":18} Brilliant Lighting BL20925 {"NAME":"BL20925","GPIO":[0,0,0,17,133,132,0,0,131,158,21,0,0],"FLAG":0,"BASE":52} BrilliantSmart 20676 USB Charger {"NAME":"Brilliant","GPIO":[0,0,0,0,0,21,0,0,0,52,90,0,0],"FLAG":0,"BASE":18} @@ -598,6 +468,7 @@ BSD29 {"NAME":"BSD29","GPIO":[0,0,0,131,134,132,0,0,21,17,56, BSD33 10A {"NAME":"BSD33 type 2","GPIO":[0,0,56,0,0,0,0,0,255,17,255,21,255],"FLAG":0,"BASE":18} BSD33 16A {"NAME":"Generic","GPIO":[0,255,0,131,134,132,0,0,21,17,56,0,0],"FLAG":0,"BASE":18} BSD34 {"NAME":"BSD34 Plug","GPIO":[0,0,0,0,0,0,0,0,21,17,56,0,0],"FLAG":0,"BASE":18} +BSD34-1-16A {"NAME":"BSD34-1 16A","GPIO":[0,53,0,131,134,132,0,0,21,17,52,0,0],"FLAG":0,"BASE":18} BSD48 16A {"NAME":"BSD48 Plug","GPIO":[0,52,0,0,0,0,0,0,21,17,57,0,0],"FLAG":0,"BASE":18} CE Smart Home LA-WF3 {"NAME":"CE LA-WF3","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} CE Smart Home LA-WF7 {"NAME":"LITESUN LA-WF7","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":18} @@ -612,6 +483,7 @@ DeLock 11826 {"NAME":"DeLock 11826","GPIO":[17,0,0,0,0,0,0,0,21,158, Deltaco SH-P01 {"NAME":"DELTACO SH-P01","GPIO":[0,0,0,0,0,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Deltaco SH-P01E {"NAME":"DELTACO SH-P01E","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":55} Deltaco SH-P02 {"NAME":"Deltaco SH-P02","GPIO":[18,0,0,0,134,132,0,0,131,56,21,22,17],"FLAG":0,"BASE":18} +DETA 62120HA Smart Plug Base {"NAME":"DetaPlugBase","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} Deta 6930HA {"NAME":"DetaSmartPlug","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} Digoo DG-SP01 {"NAME":"DG-SP01","GPIO":[255,17,255,21,56,37,0,0,38,39,40,255,255],"FLAG":0,"BASE":18} Digoo NX-SP202 {"NAME":"Generic_SP202","GPIO":[52,0,0,131,91,134,0,0,22,17,132,21,0],"FLAG":0,"BASE":63} @@ -632,12 +504,14 @@ Esicoo JSM-WF02 {"NAME":"Esicoo Plug","GPIO":[0,17,0,0,0,0,0,0,0,56,21, Esicoo YX-WS01 {"NAME":"Esicoo Plug","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":0,"BASE":18} Estink C178 {"NAME":"Estink C178","GPIO":[0,0,0,0,52,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Etekcity ESW01-USA {"NAME":"ESW01-USA","GPIO":[0,0,0,0,21,157,0,0,132,133,17,130,52],"FLAG":0,"BASE":55} +Etekcity ESW15-USA {"NAME":"ESW15-US","GPIO":[0,0,0,0,0,21,0,0,132,133,17,130,52],"FLAG":0,"BASE":18} Eva Logik NWF001 {"NAME":"EVA LOGIK Plug","GPIO":[255,17,255,255,255,255,0,0,255,52,21,255,255],"FLAG":0,"BASE":18} EVO-Smart JH-G01U {"NAME":"EVO JH-G01U","GPIO":[0,0,0,0,21,17,0,0,56,0,0,0,0],"FLAG":0,"BASE":18} Feit Electric PLUG/WIFI {"NAME":"Feit Wifi Plug","GPIO":[0,0,0,56,0,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18} FK-PW901U {"NAME":"FK-PW901U","GPIO":[56,255,255,255,255,23,0,0,21,17,24,22,255],"FLAG":0,"BASE":18} FLHS-ZN04 {"NAME":"FLHS-ZN04","GPIO":[57,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} Fontastic SH01 {"NAME":"Fontastic","GPIO":[255,0,255,0,56,0,0,0,21,17,0,0,0],"FLAG":1,"BASE":18} +Foval SM-PW701E {"NAME":"SM-PW701E","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} FrankEver FLHS-ZN04 {"NAME":"Israel plug","GPIO":[57,0,56,131,0,134,0,0,0,17,132,21,0],"FLAG":0,"BASE":45} GDTech W-US001 {"NAME":"GDTech W-US001","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":1,"BASE":18} GDTech W-US003 {"NAME":"W-US003","GPIO":[0,17,255,255,255,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} @@ -648,6 +522,7 @@ Geeni Switch Duo {"NAME":"Geeni Duo","GPIO":[0,0,0,0,18,22,0,0,17,52,21, Globe 50020 2 Outlet {"NAME":"Globe 50020","GPIO":[0,158,0,57,18,17,0,0,21,56,22,0,0],"FLAG":0,"BASE":18} Globe Smart {"NAME":"GlobeSmartPlug","GPIO":[0,0,0,0,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18} GoldenDot Mini {"NAME":"GoldenDot Mini","GPIO":[0,17,0,0,0,0,0,0,0,57,21,0,0],"FLAG":0,"BASE":52} +GoldenDot with ADC {"NAME":"W-US003-Power","GPIO":[56,0,0,0,0,0,0,0,0,17,0,21,0],"FLAG":7,"BASE":18} Gosund SP1 {"NAME":"Gosund SP1 v23","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":55} Gosund SP111 {"NAME":"Gosund SP111","GPIO":[56,0,57,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":18} Gosund SP111 v1.1 {"NAME":"SP111 v1.1","GPIO":[56,0,158,0,132,134,0,0,131,17,0,21,0],"FLAG":0,"BASE":45} @@ -663,7 +538,7 @@ Gosund WP5 {"NAME":"Gosund-WP5","GPIO":[255,255,255,255,17,255,0,0 Gosund WP6 {"NAME":"Gosund WP6","GPIO":[0,0,0,17,0,0,0,0,56,57,21,0,0],"FLAG":0,"BASE":18} Grefic TE101 {"NAME":"Grefic TE101","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":1} Gyman SM-PW701U {"NAME":"Gyman","GPIO":[255,255,157,255,56,255,0,0,21,17,255,255,255],"FLAG":0,"BASE":18} -Hama 176565 {"NAME":"hama","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":52} +Hama 16 A {"NAME":"Hama Plug","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":52} Hauppauge 01647 {"NAME":"SL-1642","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} HiHome WPP-10S1 {"NAME":"HIhome WPP-10S","GPIO":[56,0,158,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":49} HiHome WPP-10S2 {"NAME":"HiHome WPP-10S","GPIO":[17,0,0,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49} @@ -671,7 +546,7 @@ HiHome WPP-16S {"NAME":"HIhome WPP-16S","GPIO":[17,0,0,0,134,132,0,0,1 HiHome WPP-16T {"NAME":"HiHome WPP-16T","GPIO":[17,56,255,255,134,132,0,0,18,255,22,130,21],"FLAG":1,"BASE":18} HIPER IoT P01 {"NAME":"HIPER IoT P01","GPIO":[0,0,0,0,0,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} hiwild W-US002 {"NAME":"W-US002","GPIO":[0,17,0,0,0,0,0,0,0,52,21,0,158],"FLAG":0,"BASE":18} -Houzetek AWP07L {"NAME":"AWP07L","GPIO":[56,255,255,130,255,134,0,0,0,17,132,21,255],"FLAG":0,"BASE":18} +Houzetek AWP07L {"NAME":"AWP07L","GPIO":[56,0,0,130,0,134,0,0,0,17,132,21,0],"FLAG":0,"BASE":18} HS108 {"NAME":"HS108","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":1,"BASE":18} HS108A {"NAME":"HS108A","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":1,"BASE":18} HuaFan QinLu {"NAME":"Huafan SS","GPIO":[56,0,0,57,17,29,0,0,132,130,133,0,0],"FLAG":0,"BASE":24} @@ -681,10 +556,12 @@ Hyleton 313 {"NAME":"Hyleton 313","GPIO":[57,0,56,0,0,0,0,0,0,17,0, Hyleton 314 {"NAME":"hyleton-314","GPIO":[57,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} Hyleton 315 {"NAME":"hyleton-315","GPIO":[0,0,0,0,57,56,0,0,21,90,0,0,0],"FLAG":0,"BASE":18} Hyleton 317 {"NAME":"hyleton-317","GPIO":[56,0,57,0,58,0,0,0,0,90,0,21,0],"FLAG":0,"BASE":18} +iClever IC-BS08 {"NAME":"iClever BS08","GPIO":[0,0,0,0,157,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Ihommate ZCH-02 {"NAME":"ZCH-02","GPIO":[0,0,0,17,133,132,0,0,130,56,21,0,0],"FLAG":1,"BASE":18} iSwitch {"NAME":"Smart Plug XSA","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":0,"BASE":18} Jeeo TF-SH330 {"NAME":"Jeeo TF-SH330","GPIO":[56,0,0,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18} Jeeo TF-SH331W {"NAME":"Jeeo SH331W","GPIO":[56,0,158,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":18} +Jinli XS-SSA01 {"NAME":"JINLI","GPIO":[0,0,0,0,0,0,0,0,56,17,0,21,0],"FLAG":0,"BASE":18} Jinvoo SM-PW701U {"NAME":"SM-PW702","GPIO":[0,0,0,0,57,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Jinvoo SM-PW712UA 10A {"NAME":"SM-PW712UA","GPIO":[0,0,0,158,56,57,0,0,22,17,21,0,0],"FLAG":15,"BASE":18} Jinvoo SM-PW762U {"NAME":"SM-PW762U","GPIO":[0,0,0,0,158,56,0,0,21,17,22,0,0],"FLAG":0,"BASE":18} @@ -705,17 +582,18 @@ Koogeek KLSP1 {"NAME":"Koogeek-KLSP1","GPIO":[0,56,0,17,134,132,0,0,1 Koogeek KLSP2 {"NAME":"KOOGEEK KLSP2","GPIO":[57,145,56,146,0,22,0,0,0,0,21,0,17],"FLAG":0,"BASE":45} Koogeek KLUP1 {"NAME":"Koogeek-KLUP1","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18} Koogeek W-DEXI {"NAME":"W-DEXI","GPIO":[0,90,0,0,134,132,0,0,130,52,21,0,0],"FLAG":0,"BASE":18} +KULED K63 {"NAME":"KULED K63","GPIO":[0,0,0,0,21,17,0,0,56,0,0,0,0],"FLAG":0,"BASE":18} Laduo YX-DE01 {"NAME":"YX-DE01","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":0,"BASE":18} +Lenovo SE-341A {"NAME":"Lenovo SE-341A","GPIO":[0,0,0,0,17,21,0,0,158,0,56,0,0],"FLAG":0,"BASE":18} LESHP KS-501 {"NAME":"LESHP KS-501","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} Loetad EU3S 16A {"NAME":"Loetad EU3S 16","GPIO":[0,255,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":18} Lonsonho 10A Type E {"NAME":"Lonsonho10ALed","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} LoraTap SP400W-IT {"NAME":"LoraTap SP400W","GPIO":[0,0,0,0,157,56,0,0,21,17,0,0,0],"FLAG":15,"BASE":18} -LSC Smart Connect Power {"NAME":"LSC Smart Plug","GPIO":[255,255,255,255,56,255,0,0,21,255,17,255,255],"FLAG":0,"BASE":18} -LSC Smart Connect Power Plug v2 {"NAME":"LSC Smart Plug","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +LSC Smart Connect Power Plug {"NAME":"LSC Smart Plug","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Lumiman LM650 {"NAME":"Lumiman LM650","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Luminea NX-4491 {"NAME":"Luminea NX-449","GPIO":[56,0,158,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} -Luminea ZX-2820 {"NAME":"ZX2820-675","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} -Luminea ZX-2820 v2 {"NAME":"ZX2820-675","GPIO":[0,0,0,17,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":65} +Luminea NX-4541 {"NAME":"NX-4451","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":55} +Luminea ZX-2820 {"NAME":"ZX2820-675","GPIO":[0,0,0,17,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":65} Luminea ZX-2858 {"NAME":"Luminea SF-200","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} Martin Jerry XS-SSA01 {"NAME":"MJ_XS-SSA01","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} Maxcio W-DE004 {"NAME":"Maxcio W-DE004","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":1,"BASE":18} @@ -732,20 +610,25 @@ Merkury MI-WW102-199L {"NAME":"MIC-WW102","GPIO":[17,0,0,0,0,0,0,0,0,56,21,0, Merkury MI-WW105-199W {"NAME":"Merkury Switch","GPIO":[255,255,255,255,53,56,0,0,21,17,255,255,255],"FLAG":1,"BASE":18} Minleaf W-DEXI {"NAME":"W-DEXI","GPIO":[0,17,0,0,134,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18} Mirabella Genio 1002341 {"NAME":"Genio 1","GPIO":[0,0,56,0,0,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":1} +Mistral {"NAME":"Mistral Smart ","GPIO":[56,0,0,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} Moes NX-SP203 {"NAME":"Moes NX-SP203","GPIO":[52,0,0,0,17,134,0,0,21,18,0,22,0],"FLAG":0,"BASE":18} Moes WS-UEU {"NAME":"MoesHouse","GPIO":[0,0,0,21,17,0,0,0,57,0,0,0,0],"FLAG":0,"BASE":18} +MoKo YX-WS01A {"NAME":"MoKo Plug","GPIO":[0,17,0,0,0,0,0,0,0,57,21,0,0],"FLAG":0,"BASE":18} Nanxin NX-SM400 {"NAME":"NX-SM400","GPIO":[0,0,0,17,134,132,0,0,130,52,21,0,0],"FLAG":0,"BASE":18} +Naxa NSH-1000 {"NAME":"Naxa NSH-1000","GPIO":[0,0,0,0,17,0,0,0,57,56,21,0,0],"FLAG":0,"BASE":18} Nedis P110 {"NAME":"Nedis WIFIP110","GPIO":[17,0,0,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49} Nedis P130 {"NAME":"WIFIP130FWT","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} -NEO Coolcam 16A {"NAME":"Neo Coolcam 16","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49} -NEO Coolcam 16A v2 {"NAME":"Neo Coolcam 16","GPIO":[17,0,0,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49} NEO Coolcam NAS-WR01W {"NAME":"NAS-WR01W","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +NEO Coolcam NAS-WR01W 16A {"NAME":"Neo Coolcam 16","GPIO":[17,0,0,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49} Nishica SM-PW701I {"NAME":"SM-PW701I","GPIO":[255,255,255,255,255,255,255,255,21,52,17,255,255],"FLAG":15,"BASE":18} NX-SM112 {"NAME":"NX-SM112v3","GPIO":[0,0,0,0,134,132,0,0,158,17,130,21,0],"FLAG":0,"BASE":45} NX-SM200 {"NAME":"NX-SM200","GPIO":[56,0,0,0,0,134,0,0,21,17,132,57,131],"FLAG":0,"BASE":18} Obi Stecker {"NAME":"OBI Socket","GPIO":[255,255,0,255,52,21,0,0,54,255,17,0,255],"FLAG":1,"BASE":51} Obi Stecker 2 {"NAME":"OBI Socket 2","GPIO":[0,0,0,0,21,17,0,0,56,53,0,0,0],"FLAG":0,"BASE":61} Oittm Smart {"NAME":"Oittm","GPIO":[0,0,0,0,21,56,0,0,17,0,0,0,0],"FLAG":0,"BASE":1} +Olliwon {"NAME":"Olliwon","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} +Orbecco W-US009 {"NAME":"Orbecco Plug","GPIO":[0,17,0,0,0,0,0,0,0,52,21,0,0],"FLAG":0,"BASE":18} +Orvibo B25 {"NAME":"Orvibo B25","GPIO":[0,0,0,0,53,21,0,0,52,0,17,0,0],"FLAG":0,"BASE":18} Oukitel P1 {"NAME":"Oukitel P1Dual","GPIO":[52,255,0,255,0,0,0,0,22,17,0,21,0],"FLAG":0,"BASE":18} Oukitel P2 {"NAME":"Oukitel P2","GPIO":[0,0,57,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} Oukitel X6P {"NAME":"OUKITEL X6P","GPIO":[0,0,57,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} @@ -756,13 +639,17 @@ OxaOxe NX-SP202 v2 {"NAME":"oxaoxe-dold","GPIO":[56,0,0,131,17,134,0,0,21, Panamalar NX-SM200 {"NAME":"NX-SM200","GPIO":[0,0,0,0,56,134,0,0,131,17,132,21,0],"FLAG":1,"BASE":18} Positivo PPW1000 {"NAME":"PPW1000","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} Powertech {"NAME":"Jaycar","GPIO":[56,0,0,0,0,0,0,0,0,9,0,21,0],"FLAG":0,"BASE":6} +Powertech MS6104 {"NAME":"Jaycar MS6104","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":52} +Powrui 3-Outlet with 4 USB {"NAME":"POWRUI AHR-077","GPIO":[0,0,0,20,19,18,0,0,22,23,17,21,157],"FLAG":0,"BASE":18} Powrui AW-08 {"NAME":"POWRUI AW-08","GPIO":[0,0,0,0,9,21,0,0,0,52,57,0,0],"FLAG":1,"BASE":18} -Powrui Surge Protector 4USB+3AC {"NAME":"POWRUI AHR-077","GPIO":[0,0,0,0,19,18,0,0,22,23,17,21,157],"FLAG":0,"BASE":18} Premier PWIFPLG {"NAME":"Premier Plug","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":1,"BASE":18} Prime CCRCWFII113PK {"NAME":"Prime","GPIO":[0,0,0,0,57,56,0,0,21,122,0,0,0],"FLAG":0,"BASE":18} +Prime RCWFII11 {"NAME":"Prime Plug","GPIO":[0,0,0,0,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18} +PrimeCables Cab-LA-WF4 {"NAME":"Mini Smart Outlet Wifi Socket with Timer Function","GPIO":[0,0,0,56,57,0,0,0,0,0,0,21,17],"FLAG":0,"BASE":18} RenPho RF-SM004 {"NAME":"RenPho RFSM004","GPIO":[0,0,0,0,157,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} RSH-WS007-EU {"NAME":"RSH-WS007","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":1,"BASE":18} Shelly Plug S {"NAME":"Shelly Plug S","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":2,"BASE":45} +Silvergear Slimme Stekker {"NAME":"Silvergear SmartHomePlug","GPIO":[0,0,0,122,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} Slitinto NX-SM110 {"NAME":"Slitinto SM110","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} Slitinto NX-SM112 {"NAME":"NX-SM112","GPIO":[158,0,0,131,0,134,0,0,0,17,132,21,0],"FLAG":0,"BASE":45} Slitinto NX-SM112 v2 {"NAME":"Slitinto NX SM","GPIO":[56,255,158,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} @@ -770,8 +657,10 @@ Slitinto NX-SM200 {"NAME":"NX-SM200","GPIO":[0,0,0,17,134,132,0,0,0,52,21 Slitinto NX-SM2001 {"NAME":"NX-SM2001","GPIO":[0,0,0,17,134,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":45} Slitinto NX-SP201 Mini 2 in 1 {"NAME":"Slitinto Dual ","GPIO":[158,0,0,131,90,134,0,0,21,91,132,22,0],"FLAG":0,"BASE":18} Slitinto NX-SP202 {"NAME":"Slitinto SP202","GPIO":[17,0,0,0,134,132,0,0,131,52,22,21,91],"FLAG":0,"BASE":64} +SM-PW701K {"NAME":"SM-PW701K","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Smaho {"NAME":"SMAHO WiFi P.","GPIO":[17,0,0,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18} SmartDGM PP-W162 {"NAME":"SmartDGM Plug","GPIO":[0,0,0,17,134,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18} +SmartGrade AC 5008 {"NAME":"SmartGrade AC","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49} Sonoff S20 {"NAME":"Sonoff S20","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":8} Sonoff S22 TH {"NAME":"Sonoff S22","GPIO":[17,255,0,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":4} Sonoff S26 {"NAME":"Sonoff S26","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":8} @@ -782,6 +671,7 @@ SP201 Dual {"NAME":"SP-201","GPIO":[31,0,0,131,17,134,0,0,21,18,13 SPARKE JH-G01E 10A {"NAME":"SPARKE JH-G01E","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} SPC Clever 6201B {"NAME":"SPC Clever Plu","GPIO":[17,0,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} SS01 {"NAME":"Smart Plug SS0","GPIO":[255,255,255,255,255,255,0,0,21,17,255,255,255],"FLAG":1,"BASE":18} +Steren SHOME-100 {"NAME":"SHOME-100 V1.0","GPIO":[0,0,0,0,0,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} STITCH by Monoprice 27937 {"NAME":"Stitch 27937","GPIO":[17,0,56,0,133,132,0,0,131,0,21,0,57],"FLAG":0,"BASE":18} STITCH by Monoprice 35511 {"NAME":"Stitch 35511","GPIO":[56,0,57,0,0,133,0,0,0,17,132,21,131],"FLAG":0,"BASE":18} SuperNight Dual {"NAME":"SuperNight Dua","GPIO":[255,17,255,21,132,133,0,0,22,130,58,255,255],"FLAG":1,"BASE":18} @@ -793,8 +683,9 @@ SWA5 {"NAME":"Lingan SWA5","GPIO":[56,0,0,0,0,0,0,0,0,17,0,2 SWA9 {"NAME":"SWA9","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} SwissTone SH 100 {"NAME":"SwissTone","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Sygonix SY-4276902 {"NAME":"SYGONIX","GPIO":[0,0,0,17,133,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18} -TanTan WP3 {"NAME":"TanTan WP3","GPIO":[57,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} -TCP WISSINWUK {"NAME":"TCP_Plug","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":45} +TanTan WP2 {"NAME":"TanTan WP2","GPIO":[57,56,158,255,21,134,255,255,131,17,132,22,18],"FLAG":15,"BASE":18} +TanTan WP3 {"NAME":"TanTan WP3","GPIO":[0,0,56,0,17,0,0,0,57,0,21,0,0],"FLAG":0,"BASE":18} +TCP Smart WISSINWUK {"NAME":"TCP_Plug","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":45} Teckin SP10 {"NAME":"Teckin SP10","GPIO":[255,255,56,255,255,255,0,0,255,17,255,21,255],"FLAG":0,"BASE":18} Teckin SP20 {"NAME":"TECKIN SP20","GPIO":[56,255,57,255,21,134,0,0,131,17,132,0,0],"FLAG":0,"BASE":45} Teckin SP21 {"NAME":"Teckin SP21","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":45} @@ -818,6 +709,7 @@ Varna Crafts 16A {"NAME":"VC Plug","GPIO":[157,0,0,0,0,134,0,0,131,17,13 Vaupan X6P {"NAME":"Vaupan 10a X6P","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} Vingo {"NAME":"Karpal-01","GPIO":[0,0,0,0,0,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Vivanco 39625 Smart Home Power Adapter {"NAME":"Vivianco","GPIO":[0,0,0,17,133,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18} +Vivitar HA-1003 {"NAME":"Vivitar HA1003","GPIO":[158,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} Vivitar HA-1006 {"NAME":"HA-1006","GPIO":[0,0,0,0,56,0,0,0,21,90,0,0,0],"FLAG":0,"BASE":18} Vivitar HA-1006-AU {"NAME":"HA-1006-AU","GPIO":[0,0,0,0,56,0,0,0,21,90,0,0,0],"FLAG":0,"BASE":18} WAGA life CHCZ02MB {"NAME":"WAGA CHCZ02MB","GPIO":[57,0,0,131,0,134,0,0,21,17,132,56,0],"FLAG":0,"BASE":68} @@ -836,11 +728,12 @@ Wsiiroon {"NAME":"WSIIROON","GPIO":[0,0,0,0,17,56,0,0,21,0,0,0,0 Wsiiroon {"NAME":"Wsiiroon/Wsky","GPIO":[0,0,0,0,17,56,0,0,21,0,0,0,0],"FLAG":0,"BASE":18} Wyze WLPP1 {"NAME":"WyzePlugWLPP1","GPIO":[0,0,0,0,0,56,0,0,21,0,17,0,0],"FLAG":0,"BASE":18} Xenon SM-PW702-U {"NAME":"SM-PW702","GPIO":[0,0,0,0,57,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Xiaomi IMILAB ZNCZ05CM {"NAME":"Mi Smart Plug","GPIO":[17,0,0,0,56,21,0,0,0,158,0,0,0],"FLAG":0,"BASE":18} XS-A11 {"NAME":"THRUMM XS-A11","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} XS-A12 {"NAME":"XS-A12","GPIO":[37,0,39,0,38,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} XS-A14 {"NAME":"NETVIP XS-A14","GPIO":[37,0,38,0,0,17,0,0,39,21,0,0,0],"FLAG":0,"BASE":18} XS-A17 {"NAME":"XS-A18","GPIO":[37,0,38,0,0,39,0,0,0,17,0,21,0],"FLAG":0,"BASE":45} -XS-A18 {"NAME":"XS-A18","GPIO":[37,0,38,0,0,39,0,0,0,17,0,21,0],"FLAG":0,"BASE":45} +XS-A18 {"NAME":"XS-A18","GPIO":[56,0,53,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":45} XS-A23 {"NAME":"XS-A23","GPIO":[56,255,0,131,17,134,0,0,0,18,132,21,22],"FLAG":0,"BASE":45} XS-SSA01 {"NAME":"XS-SSA01","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":0,"BASE":18} XS-SSA01 v2 {"NAME":"XS-SSA01","GPIO":[255,17,255,255,255,255,0,0,56,255,255,21,255],"FLAG":0,"BASE":18} @@ -849,7 +742,10 @@ XS-SSA06 {"NAME":"XS-SSA06","GPIO":[37,0,38,0,0,39,0,0,0,90,0,21 Yagala SWA9 {"NAME":"SWA9","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} Yelomin JH-G01E {"NAME":"Yelomin","GPIO":[0,145,0,146,0,0,0,0,17,56,21,0,0],"FLAG":0,"BASE":18} YERON US101 {"NAME":"YERON_US101","GPIO":[255,255,255,17,133,132,0,0,131,56,21,255,255],"FLAG":0,"BASE":18} +YM-WS-1 Mini {"NAME":"YM-WS1","GPIO":[0,0,0,0,0,0,0,0,56,17,0,21,0],"FLAG":0,"BASE":18} YT-E003 {"NAME":"YT-E003-SP202","GPIO":[17,0,0,0,134,132,0,0,131,52,22,21,91],"FLAG":0,"BASE":64} +Yuanguo KS-501 {"NAME":"Yuanguo_KS-501","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":18} +YX-DE01 {"NAME":"YX-DE01","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} YX-WS02 {"NAME":"Amysen YX-WS02","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":1,"BASE":18} ZBR-001 {"NAME":"ZBR-001","GPIO":[17,255,255,255,133,132,0,0,130,56,21,255,57],"FLAG":1,"BASE":18} ZooZee SA101 {"NAME":"ZooZee","GPIO":[57,255,56,255,255,255,0,0,255,17,255,21,255],"FLAG":0,"BASE":18} @@ -861,14 +757,15 @@ ZSP-001 {"NAME":"ZSP-001","GPIO":[17,255,255,255,133,132,0,0,13 ## Power Strip ``` A0F0 ZLD-44EU-W {"NAME":"AOFO-4AC-4USB","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18} -ACENX 3AC+3USB {"NAME":"ACENX 3-Outlet","GPIO":[56,55,54,53,0,21,0,0,23,24,22,0,17],"FLAG":0,"BASE":18} +Acenx 3AC+3USB {"NAME":"ACENX 3-Outlet","GPIO":[56,55,54,53,0,21,0,0,23,24,22,0,17],"FLAG":0,"BASE":18} Annhome 3AC + 2USB {"NAME":"1200W WiFi SPS","GPIO":[32,0,0,0,57,52,0,0,21,17,22,23,33],"FLAG":0,"BASE":18} AOFO 3AC+4USB {"NAME":"AOFO","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18} AOFO 4AC+4USB {"NAME":"AOFO4AC4USB","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":0,"BASE":18} AOFO 4AC+4USB Tuya {"NAME":"AOFO-4AC-4USB","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":1,"BASE":54} AOFO 4AC+4USB UK {"NAME":"AOFO4AC4USB-UK","GPIO":[0,56,0,17,23,24,0,0,22,21,33,0,0],"FLAG":0,"BASE":18} -Arlec PB88UHA {"NAME":"ArlecPowerStri","GPIO":[0,0,0,255,22,21,0,0,24,23,0,0,0],"FLAG":0,"BASE":18} -Arlec PB89HA {"NAME":"Arlec PB89HA","GPIO":[255,255,255,255,22,21,0,0,24,23,255,255,255],"FLAG":15,"BASE":18} +Arlec Smart 3 Outlet Power Cube {"NAME":"Arlec Cube","GPIO":[0,17,0,0,0,0,0,0,0,52,21,0,0],"FLAG":0,"BASE":18} +Arlec Smart PB88UHA {"NAME":"Arlec PB88UHA","GPIO":[0,56,0,17,22,21,0,0,24,23,0,0,0],"FLAG":15,"BASE":18} +Arlec Smart PB89HA {"NAME":"Arlec PB89HA","GPIO":[0,56,0,17,22,21,0,0,24,23,0,0,0],"FLAG":0,"BASE":18} Bauhn ASPBU-1019 {"NAME":"Bauhn 3AC+3USB","GPIO":[0,157,0,0,22,21,0,0,0,23,17,0,0],"FLAG":0,"BASE":18} BlitzWolf BW-SHP9 {"NAME":"BlitzWolf SHP9","GPIO":[158,255,0,255,0,23,0,0,21,17,22,24,0],"FLAG":0,"BASE":45} BrilliantSmart 20691 Powerboard with USB Chargers {"NAME":"B_WiFi-4","GPIO":[56,0,0,57,29,17,0,0,31,30,32,0,25],"FLAG":1,"BASE":18} @@ -877,10 +774,12 @@ CE Smart Home Garden Stake LH-7-1 {"NAME":"CE Power Stake","GPIO":[0,0,0,0,56,5 CRST LTS-4G-W {"NAME":"CRST LTS-4G-W","GPIO":[0,0,0,0,24,0,0,0,22,23,21,0,0],"FLAG":0,"BASE":18} Deltaco SH-P03USB {"NAME":"Deltaco SH-P03","GPIO":[0,56,0,0,0,21,0,0,23,17,22,24,0],"FLAG":1,"BASE":18} Digoo DG-PS01 {"NAME":"Digoo DG-PS01","GPIO":[0,56,0,17,23,22,0,0,0,24,21,0,0],"FLAG":1,"BASE":18} -Geekbes 4AC+4USB {"NAME":"Geekbes 4xStri","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18} +Geekbes 4AC+4USB {"NAME":"Geekbes 4xStri","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18} Geeni Surge {"NAME":"Geeni GNCSW003","GPIO":[52,0,0,0,22,21,0,0,24,25,23,26,17],"FLAG":0,"BASE":18} Geeni Surge Mini {"NAME":"Geeni-GN-SW004","GPIO":[56,0,0,17,0,0,0,0,22,21,23,0,0],"FLAG":15,"BASE":18} +Gosund P1 {"NAME":"Gosund_P1","GPIO":[0,145,157,146,0,32,0,0,22,23,21,0,17],"FLAG":1,"BASE":18} Gousund WP9 {"NAME":"Gosund WP9","GPIO":[56,55,54,53,0,21,0,0,23,24,22,0,17],"FLAG":0,"BASE":18} +HIPER IoT PS44 {"NAME":"HIPER IoT PS44","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":0,"BASE":18} Hyleton 330 {"NAME":"Hyleton-330","GPIO":[57,0,0,56,29,17,0,0,31,30,32,0,25],"FLAG":0,"BASE":18} Hyleton 331 {"NAME":"HLT-331","GPIO":[52,255,255,57,29,17,0,0,31,30,32,255,58],"FLAG":0,"BASE":18} Hyleton 333 {"NAME":"HLT-333","GPIO":[52,0,0,57,29,17,0,0,31,30,0,0,24],"FLAG":0,"BASE":18} @@ -889,8 +788,10 @@ KMC 5 {"NAME":"KMC 5-Outlet","GPIO":[56,0,0,0,25,9,0,0,22,21, Kogan Power Strip USB Ports & Energy Meter {"NAME":"Generic","GPIO":[90,56,0,24,134,132,0,0,131,22,23,21,0],"FLAG":0,"BASE":18} Koogeek KLOE4 {"NAME":"Koogeek KLOE4","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18} LeFun SK2 {"NAME":"LeFun SK2","GPIO":[0,0,0,17,22,21,0,0,23,24,25,0,0],"FLAG":0,"BASE":18} +Luminea 3AC+4USB 16A {"NAME":"Luminea-NX4473","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":0,"BASE":18} Maxcio ZLD-34EU-W {"NAME":"MAXCIO","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18} Meross MSS425 {"NAME":"Meross MSS425","GPIO":[33,0,0,0,56,0,0,0,21,17,22,23,32],"FLAG":0,"BASE":18} +Mirabella Genio 4 Outlet Power Board with 2 USB {"NAME":"Genio i002340","GPIO":[56,0,0,0,21,22,0,0,23,17,24,25,0],"FLAG":0,"BASE":18} Mirabella Genio Powerboard I002578 {"NAME":"Genio Powerboa","GPIO":[21,52,0,0,23,22,0,0,25,17,26,24,0],"FLAG":0,"BASE":18} Nedis P310 {"NAME":"Nedis WIFIP310","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18} Powrui AHR-079 {"NAME":"Powrui Power S","GPIO":[56,0,0,17,19,21,0,0,23,20,22,24,18],"FLAG":0,"BASE":18} @@ -899,10 +800,12 @@ S2199EU {"NAME":"S2199EU","GPIO":[0,17,0,52,23,25,0,0,21,24,22, SA-P402A {"NAME":"SA-P402A","GPIO":[0,17,0,56,23,25,21,24,22,0,0,1,0],"FLAG":1,"BASE":18} STITCH by Monoprice 34082 {"NAME":"Stitch 34082","GPIO":[255,255,255,255,21,20,0,0,23,22,24,255,255],"FLAG":0,"BASE":18} SWB1 {"NAME":"SWB1","GPIO":[52,0,0,0,0,24,0,0,21,17,22,23,0],"FLAG":0,"BASE":18} +TCP Smart 4AC+USB {"NAME":"TCP WPS4WUK","GPIO":[255,56,0,17,23,24,0,0,22,21,25,0,0],"FLAG":15,"BASE":18} Teckin SS30 {"NAME":"Teckin SS30","GPIO":[52,255,255,57,29,17,0,0,31,30,32,255,25],"FLAG":0,"BASE":18} Tellur TLL331031 {"NAME":"Tellur","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18} -TESSAN A4L-BK {"NAME":"TESSAN A4L-BK","GPIO":[0,0,0,24,23,0,0,0,21,0,22,0,0],"FLAG":0,"BASE":18} +Tessan A4L-BK {"NAME":"TESSAN A4L-BK","GPIO":[0,0,0,24,23,0,0,0,21,0,22,0,0],"FLAG":0,"BASE":18} Tonbux SM-SO301-U {"NAME":"Tonbux SM-SO30","GPIO":[56,0,0,0,29,0,0,0,31,30,32,0,25],"FLAG":0,"BASE":18} +Useelink SM-SO301AU {"NAME":"Useelink","GPIO":[56,0,0,0,29,0,0,0,31,30,32,0,25],"FLAG":0,"BASE":18} Vivitar HA-1007 {"NAME":"Vivitar HA1007","GPIO":[56,0,0,0,30,17,0,0,32,31,33,0,21],"FLAG":15,"BASE":18} Vivitar HA-1007-AU {"NAME":"HA-1007-AU","GPIO":[56,17,0,58,29,57,0,0,31,30,32,0,25],"FLAG":0,"BASE":18} wesmartify essentials 4AC+4USB {"NAME":"essential_4_po","GPIO":[56,0,0,0,24,25,0,0,22,21,23,0,17],"FLAG":0,"BASE":18} @@ -914,14 +817,14 @@ XS-A25 {"NAME":"XS-A25","GPIO":[52,0,53,0,25,22,0,0,24,17,23,2 XS-A26 {"NAME":"XS-A26","GPIO":[52,0,53,0,25,22,0,0,24,17,23,21,0],"FLAG":0,"BASE":18} XS-A34 {"NAME":"XS-A24","GPIO":[52,0,53,0,25,22,0,0,24,17,23,21,0],"FLAG":0,"BASE":18} Yagala {"NAME":"Yagala","GPIO":[0,0,0,17,22,24,0,0,23,25,21,0,0],"FLAG":0,"BASE":18} -Yagala SWB3 {"NAME":"YAGALA SWB3","GPIO":[0,0,53,0,0,23,0,0,21,0,22,24,0],"FLAG":1,"BASE":18} -Yagala SWB3 v2 {"NAME":"YAGALA SWB3","GPIO":[157,0,53,0,0,23,0,0,21,17,22,24,0],"FLAG":0,"BASE":18} +Yagala SWB3 {"NAME":"YAGALA SWB3","GPIO":[157,0,53,0,0,23,0,0,21,17,22,24,0],"FLAG":0,"BASE":18} +Yagala Z1 {"NAME":"YAGALA Z1","GPIO":[0,157,0,17,22,24,0,0,23,25,21,0,0],"FLAG":0,"BASE":18} Yuanguo 4AC + 2 USB {"NAME":"YUANGUO","GPIO":[0,20,0,24,19,18,0,0,17,22,21,23,0],"FLAG":0,"BASE":1} Yuanguo 4AC+2USB {"NAME":"YUANGUO","GPIO":[13,20,0,24,19,18,0,0,17,22,21,23,157],"FLAG":0,"BASE":1} Zeoota PS022 {"NAME":"ZEOOTA 3x plus","GPIO":[0,57,0,56,22,21,0,0,17,23,24,0,0],"FLAG":1,"BASE":18} Zeoota ZLD-44EU-W {"NAME":"ZEOOTA-ZLD-44E","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18} ZLD-44EU-W {"NAME":"ZLD-44EU-W","GPIO":[17,255,255,255,22,21,18,19,23,24,25,0,0],"FLAG":0,"BASE":23} -ZLD-44USA-W {"NAME":"ZLD-44USA-W","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18} +ZLD-44USA-W {"NAME":"ZLD-44USA-W","GPIO":[0,56,0,17,22,21,0,0,23,24,33,0,0],"FLAG":0,"BASE":18} ZLD64-EU-W {"NAME":"ZLD64-EU-W","GPIO":[0,56,0,17,22,21,0,0,0,0,23,0,0],"FLAG":0,"BASE":18} ``` @@ -930,13 +833,246 @@ ZLD64-EU-W {"NAME":"ZLD64-EU-W","GPIO":[0,56,0,17,22,21,0,0,0,0,23 Sonoff RF Bridge 433 {"NAME":"Sonoff Bridge","GPIO":[17,148,255,149,255,255,0,0,255,56,255,0,0],"FLAG":0,"BASE":25} ``` +## RGB +``` +Deltaco SH-LE27RGB 810lm {"NAME":"SH-LE27RGB","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Halonix Prime Prizm 12W {"NAME":"Halonix Prizm ","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Jomarto 9W {"NAME":"Jomarto Wifi S","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":1,"BASE":18} +Lyasi PT-BW09 {"NAME":"Lyasi PT-BW09","GPIO":[17,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +MoKo YX-L01C-E27 810lm {"NAME":"MOKO","GPIO":[17,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +muvit IO miobulb001 600lm {"NAME":"miobulb001","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":15,"BASE":18} +Oobest ZN93028 11W {"NAME":"RGB Bulb 11W","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":1,"BASE":18} +Wipro Garnet NS7001 480lm {"NAME":"WiproSmartBulb","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +``` + +## RGBCCT +``` +Aigital LE13 800lm {"NAME":"Aigital 9W RGB","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} +Alfawise LE12 9W 900LM {"NAME":"Alfawise LE12 ","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} +Aoycocr JL81 5W 400lm {"NAME":"AoycocrJLB1","GPIO":[0,0,0,0,39,0,0,0,38,41,37,40,0],"FLAG":0,"BASE":18} +Aoycocr Q0 750lm {"NAME":"AoycocrA19","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} +Aoycocr Q10CWM BR30 9W 720lm {"NAME":"AoycocrBR30","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} +Arlec Smart 9.5W 806lm {"NAME":"Arlec RGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Arlec Smart 9.5W 806lm {"NAME":"Arlec RGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Aunics 7W 600lm {"NAME":"Aunics RGBW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Avatar ALB201W 720lm {"NAME":"AVATAR ALB201W","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} +Avatar ALS18L A60 800lm {"NAME":"Avatar E14 7W","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":20} +B.K.Licht BKL1253 9W 806lm {"NAME":"BKL1253","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Bakibo TB95 9W 1000lm {"NAME":"Bakibo A19 9W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +BAZZ BR30 650lm {"NAME":"BAZZrgb","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18} +BlitzWolf BW-LT27 w/ remote 850lm {"NAME":"BW-LT27","GPIO":[0,0,0,0,41,38,0,0,39,51,40,37,0],"FLAG":0,"BASE":18} +BNeta IO-WIFI60-E27P 800lm {"NAME":"OM60/RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Bomcosy 600lm {"NAME":"Generic","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18} +Calex 429002 Reflector 350lm {"NAME":"Calex RGBW","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} +Calex 429004 A60 806lm {"NAME":"Calex E27 RGB ","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Cleverio 51395 806lm {"NAME":"CleverioE27RGB","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +CMARS 4W Reflector {"NAME":"RGBWW GU10","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":15,"BASE":18} +Dogain 320lm {"NAME":"DOGAIN","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +Emuni TB95 9W 850Lm {"NAME":"TB95","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Ener-J SHA5262 800lm {"NAME":"ENER-J RGBWWW ","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +Euri Lighting 10W 800lm {"NAME":"Euri Lighting ","GPIO":[0,0,0,0,37,40,0,0,38,41,39,37,0],"FLAG":0,"BASE":18} +EXUP C37 5W {"NAME":"EXUP","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":15,"BASE":18} +Feit Electric A19 1600lm {"NAME":"OM100/RGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Feit Electric A19 800lm {"NAME":" BPA800/RGBW/AG/2","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Feit Electric A19 800lm {"NAME":" BPA800/RGBW/AG/2P","GPIO":[0,0,0,0,37,38,0,0,141,142,140,0,0],"FLAG":0,"BASE":48} +Feit Electric A19 800lm {"NAME":"FE-OM60-15K-AG","GPIO":[0,0,0,0,141,140,0,0,37,142,38,0,0],"FLAG":0,"BASE":18} +Feit Electric A19 800lm {"NAME":"OM60/RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Fitop 9W {"NAME":"E27RGBCCT9w","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":15,"BASE":18} +Fulighture 9W 810lm {"NAME":"Fulighture 9W","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Geeni Prisma Plus 800lm {"NAME":"Geeni Prisma P","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Globe 34207 800lm {"NAME":"GlobeRGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +HIPER IoT A61 {"NAME":"HIPER IoT A61","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Infray 9W 900LM {"NAME":"InfrayRGBCCT","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Jeeo TF-QPZ13 800lm {"NAME":"Jeeo","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Julun JL-021 5W Candle {"NAME":"E14 RGBCCT","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +Kogan 10W 1050lm {"NAME":"Kogan RGB+CCT","GPIO":[255,255,255,0,37,40,255,255,38,50,39,255,255],"FLAG":15,"BASE":18} +Kohree 600lm {"NAME":"Kohree VHP560","GPIO":[0,0,0,0,37,41,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} +LE lampUX 8.5W 806lm {"NAME":"lampUX","GPIO":[0,0,0,0,141,140,0,0,38,142,37,0,0],"FLAG":15,"BASE":18} +LE lampUX A19 850lm {"NAME":"LE RGBWW 60W","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":1,"BASE":18} +Ledmundo 6W 600lm {"NAME":"LEDMUNDO 6W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Legelite 5W Candle {"NAME":"Legelite E12","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Legelite A60 7W {"NAME":"Legelite A60 7","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Legelite A60 7W 600lm {"NAME":"Legelite E26","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Lohas ZN011 5W 420lm {"NAME":"LohasZN011","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Lohas ZN014-2 5W 380lm {"NAME":"Lohas ZN014-2","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Lohas ZN031 650lm {"NAME":"Lohas ZN031","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Lohas ZN033 810lm {"NAME":"Lohas RGBWW","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Lohas ZN036 900 lm {"NAME":"Lohas RGBCCT","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Lohas ZN037 450lm {"NAME":"Lohas LH-ZN037","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":20} +Lohas ZN039 BR40 1450lm {"NAME":"Lohas ZN039","GPIO":[0,0,0,0,39,37,0,0,41,38,40,0,0],"FLAG":0,"BASE":18} +Lumary 9W 800lm {"NAME":"Lumary / iLint","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +LVWIT A60 8.5W 806lm {"NAME":"LVWIT A60 8.5W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +LVWIT A70 12W 1521lm {"NAME":"LVWIT A70 12W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Novostella HM-LB09 13W 1300lm {"NAME":"Novostella 13W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Novostella NTB10 9W 900lm {"NAME":"NTB10","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} +Novostella UT55505 7W 600lm {"NAME":"Novostella B22","GPIO":[0,0,0,0,37,41,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} +Novostella UT55508 12W 1150lm {"NAME":"Novostella","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Novostella UT55509 13W 1300lm {"NAME":"Novostella 13W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Ohlux A19 7W 600lm {"NAME":"OHLUX","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Ohlux BR30 10W 900lm {"NAME":"OHLUX","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +oobest 7W {"NAME":"E27_RGBCW_Bulb","GPIO":[0,0,0,0,38,37,0,0,40,41,39,0,0],"FLAG":0,"BASE":18} +Positivo 10W 806lm {"NAME":"Positivo Bulb","GPIO":[0,0,0,0,37,40,0,0,38,50,39,0,0],"FLAG":0,"BASE":18} +Powertech SL225X 800lm {"NAME":"Jaycar SL225X","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Qualitel ALS08L 1100lm {"NAME":"Qualitel ALS08","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Reafoo A26 9W {"NAME":"ReaFooE26","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} +RYE 5W 450LM Candle {"NAME":"RYE Candlebra","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Saudio A19 7W 700lm {"NAME":"X002BU0DOL","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Sealight A19 9W 810lm {"NAME":"DGO/SEASTAR","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Slitinto TB95 9W 1000lm {"NAME":"Slitinto 9W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +SmartLED 9W 400lm {"NAME":"SmartLED","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Sonoff B1 (R2) {"NAME":"Sonoff B1","GPIO":[17,0,0,0,0,0,0,0,143,0,144,0,0],"FLAG":0,"BASE":26} +Spectrum Smart GLS 9W 850lm {"NAME":"SPECTR. RGBCCT","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} +Sunco G25 5W 450lm {"NAME":"Sunco G25","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Sunco PAR20 5W 400lm {"NAME":"Sunco PAR20","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Teckin SB50 v3 A19 800lm {"NAME":"Teckin SB50v3","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Teckin SB53 1300lm {"NAME":"Teckin SB53","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +V-Tac PAR16 4.5W 400lm 100 {"NAME":"V-TAC VT5164","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} +Vizia 5W GU10 {"NAME":"Vizia RGBWW","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":15,"BASE":18} +Wipro Garnet 9W 810lm {"NAME":"Wipro","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Zemismart 5W 480lm {"NAME":"Zemismart 5W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +ZZHXON 600lm {"NAME":"E27_RGB_Bulb","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +``` + +## RGBW +``` +3Stone EBE-QPW36 1050lm {"NAME":"3STONE","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":15,"BASE":18} +Accewit 7W 650lm {"NAME":"Accewit Bulb","GPIO":[0,0,0,0,0,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} +Aisirer 7W 580lm {"NAME":"Aisirer RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Aisirer 7W 580lm {"NAME":"Aisirer RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +AL Above Lights 810lm {"NAME":"AL 810LM","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Aoycocr Q3CM 230lm {"NAME":"Aoycocr_GU10","GPIO":[0,0,0,0,0,0,0,0,143,0,144,0,0],"FLAG":0,"BASE":27} +Aoycocr Q9WM A21 10W 900lm {"NAME":"Aoycocr Q9WM","GPIO":[0,0,0,0,0,39,0,0,38,40,37,41,0],"FLAG":0,"BASE":18} +Avatar ALS08L A19 910lm {"NAME":"Avatar E27 7W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20} +Avatar ALS09L A60 900lm {"NAME":"Avatar E14 9W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20} +Avatar ALS11L PAR16 500lm {"NAME":"Avatar_GU10","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +AWOW A60 9W 800lm {"NAME":"AWOW 9W RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Axtee AI-003 A19 700lm {"NAME":"Axtee E26 7W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20} +Bawoo EUWL122130 925lm {"NAME":"Bawoo","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} +BlitzWolf BW-LT21 900lm {"NAME":"BlitzWolf LT21","GPIO":[0,0,0,0,41,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} +BNeta IO-WIFI-GU10 380lm {"NAME":"BNeta","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +BriHome 6,5W 500lm {"NAME":"BRI E27 6,5W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20} +Brilliant HK17653S72 350lm {"NAME":"HK17653S72","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +BrilliantSmart 20698 9W 800lm {"NAME":"Brilliant20698","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":0,"BASE":18} +BrilliantSmart 20699 9W 800lm {"NAME":"Brilliant20699","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":0,"BASE":18} +BrilliantSmart 20741 9W 750lm {"NAME":"Brilliant RGB+","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +BrilliantSmart 20888 {"NAME":"BS-GU10-20888","GPIO":[0,0,0,0,140,38,0,0,37,142,141,0,0],"FLAG":0,"BASE":18} +BrizLabs A19 9W 806LM {"NAME":"TCP Smart RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +BrizLabs Candle 4,5W 350lm {"NAME":"BrizLabs RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Brizlabs EBE-LZW10 350Lm {"NAME":"Brizlabs E14","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":15,"BASE":18} +BrizLabs EBE-SHW03 380lm {"NAME":"BrizLabs","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} +BTZ1 {"NAME":"WifiBulb","GPIO":[0,0,0,0,0,37,0,0,39,40,38,0,0],"FLAG":0,"BASE":18} +Calex 429008 B35 5W 470lm {"NAME":"Calex E14 RGBW","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} +Cleverio 51398 370lm {"NAME":"CleverioGU10","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Cocoon DY180363-B 800lm {"NAME":"Cocoon RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Connex Connect A60 6W 470lm {"NAME":"Connex RGBW Bu","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +electriQ 600lm {"NAME":"ElectricQ B22","GPIO":[0,0,0,0,37,41,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} +EleLight 350lm {"NAME":"EleLight 7wA19","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} +Esicoo 810lm {"NAME":"Esicoo Bulb","GPIO":[0,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +Eurolux A70 10W {"NAME":"Eurolux A70 RG","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":1,"BASE":18} +Fcmila 10W {"NAME":"FCMILA LED E27","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +Fcmila 7W {"NAME":"FCMILA E27 0.1","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Fcmila Spotlight 460lm {"NAME":"Fcmila LED 6W","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +Feit Electric BR30 650lm {"NAME":"BR30/RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Feit Electric BR30 700lm {"NAME":"Feit BR30/RGBW","GPIO":[0,0,0,38,141,140,0,0,0,142,37,0,0],"FLAG":0,"BASE":18} +Fulighture A60 810lm {"NAME":"Fulighture A60","GPIO":[0,0,0,0,38,37,0,0,0,39,40,0,0],"FLAG":0,"BASE":18} +Geeni Prisma 1050lm {"NAME":"Geeni 1050 RGB","GPIO":[37,0,0,0,140,38,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} +Generic 10W {"NAME":"10W E27 RGBW","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} +Generic GU10 5W 450lm {"NAME":"RGBCW GU10","GPIO":[0,255,0,255,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":3} +Gosund WB3 8W 800lm {"NAME":"Gosund WB3","GPIO":[0,0,0,0,40,0,0,0,37,38,39,0,0],"FLAG":0,"BASE":18} +Hama 10W 1050lm {"NAME":"Hama Bulb RGBW","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} +Hama 10W 806lm {"NAME":"Hama Smart WiF","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Hama 4.5W {"NAME":"hama E14 RGB","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Hykker SL-0492 810lm {"NAME":"Hykker RBGW 9W","GPIO":[0,0,0,0,0,40,0,0,37,0,39,38,0],"FLAG":0,"BASE":18} +Kainsy 600lm {"NAME":"KAINSY","GPIO":[17,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +Koaanw 650lm {"NAME":"KOAANW Bulb","GPIO":[0,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +Kogan 10W Ambient 1050lm {"NAME":"Kogan RGB","GPIO":[0,0,0,0,140,37,0,0,0,0,141,0,0],"FLAG":0,"BASE":18} +Kogan Ambient Candle {"NAME":"Kogan_E14","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Kogan Ambient Spotlight 330lm {"NAME":"Kogan_GU10","GPIO":[0,0,0,0,39,40,0,0,37,0,38,0,0],"FLAG":0,"BASE":18} +Kuled 800lm {"NAME":"KULED 60W RGB","GPIO":[0,0,0,0,39,40,0,0,37,0,38,0,0],"FLAG":1,"BASE":18} +Laideyi 7W {"NAME":"7W-E14-RGBW-La","GPIO":[0,0,0,0,38,37,0,0,39,0,40,0,0],"FLAG":0,"BASE":18} +LE lampUX A19 9.5W 806lm {"NAME":"LE lampUX 9W Bulb","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +LeCardio 10W 980lm {"NAME":"LeCardio RGBW","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +Linganzh LWE3 7W 800lm {"NAME":"LINGANZH Smart ","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} +LiteMate A90 810lm {"NAME":"LiteMate 9W","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +LOFTer 7W 450lm {"NAME":"Lofter","GPIO":[0,255,255,255,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Lohas ZN001 810lm {"NAME":"Lohas RGBW","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":18} +Lohas ZN001-E12 810lm {"NAME":"Lohas","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":26} +Lohas ZN005 420lm {"NAME":"Lohas E14 R50","GPIO":[17,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Lohas ZN006 1380lm {"NAME":"Lohas100 RGBW","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":18} +Lohas ZN012 450lm {"NAME":"LH-5W-ZN01204","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Lonsonho 900lm {"NAME":"RGB+W+C Bulb","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +LSC Smart Connect C45 400lm {"NAME":"LSC RGBCW E14","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} +LSC Smart Connect MR16 380lm {"NAME":"LSC RGBCW GU10","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} +Lumiman LM530 7.5W 800lm {"NAME":"Lumiman LM530","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18} +Lumiman LM530 7.5W 800lm {"NAME":"Lumiman LM530","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18} +Lumiman LM530 7.5W 800lm {"NAME":"LM530","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18} +Luminea ZX-2832 {"NAME":"Luminea RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":1,"BASE":18} +Luminea ZX-2986 1400lm {"NAME":"Luminea RGBW","GPIO":[0,0,0,0,37,41,0,0,0,38,39,40,0],"FLAG":0,"BASE":18} +LWE3 600lm {"NAME":"Linganzh LWE3 ","GPIO":[0,0,0,0,0,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} +MagicLight 4.5W 350lm {"NAME":"4.5W RGBW Bulb","GPIO":[0,0,0,0,0,37,0,0,39,40,38,0,0],"FLAG":0,"BASE":18} +Manzoku XS-001 1050lm {"NAME":"Manzoku RGBW","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +Maxcio YX-L01P-E27-2P 9W {"NAME":"Maxcio YXL01P","GPIO":[17,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +Merkury 75W 1050lm {"NAME":"MIC-BW904-999W","GPIO":[38,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":0,"BASE":18} +Merkury A21 10W 1050lm {"NAME":"MI-BW210-999W","GPIO":[0,0,0,0,140,37,0,0,142,38,141,0,0],"FLAG":0,"BASE":48} +Merkury A21 10W 1050lm {"NAME":"MI-BW210-999W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Merkury MI-BW904-999W 1050lm {"NAME":"MI-BW904-999W","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} +Merkury MI-BW904-999W v2 1050lm {"NAME":"MI-BW210-999W","GPIO":[0,0,0,0,38,37,0,0,141,142,140,0,0],"FLAG":0,"BASE":48} +Merkury MI-BW904-999W v3 {"NAME":"MI-BW904-999W","GPIO":[0,0,0,0,37,38,0,0,141,142,140,0,0],"FLAG":0,"BASE":69} +Merkury MI-BW906-999W BR30 750lm {"NAME":"MI-BW906-999W","GPIO":[0,0,0,0,38,37,0,0,141,142,140,0,0],"FLAG":0,"BASE":18} +Mirabella Genio 9W 800lm {"NAME":"GenioBulbRGB","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":1,"BASE":18} +Mirabella Genio 9W 800lm {"NAME":"GenioBulbRGB","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":1,"BASE":18} +Mirabella Genio 9W 800lm {"NAME":"MiraBellaGenio","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} +Mixigoo 950lm {"NAME":"Mixigoo Bulb","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +MoKo GU10 {"NAME":"MoKo GU10","GPIO":[255,255,255,255,39,255,255,255,38,41,37,40,255],"FLAG":15,"BASE":18} +MoKo JL81 5W 400lm {"NAME":"MoKo E14","GPIO":[0,0,0,0,0,0,0,0,143,0,144,0,0],"FLAG":0,"BASE":27} +MOKO YX-L01C-E14 A60 810lm {"NAME":"MOKO","GPIO":[17,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +Nedis 4.5W 380lm {"NAME":"Nedis RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Nedis A60 800lm {"NAME":"Nedis RGBW","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":1,"BASE":18} +Nedis C10 350lm {"NAME":"Nedis WIFILC10","GPIO":[0,0,0,0,39,37,0,0,40,38,41,0,0],"FLAG":1,"BASE":18} +Nedis PAR16 330lm {"NAME":"Nedis GU10","GPIO":[0,0,0,0,39,37,0,0,40,38,41,0,0],"FLAG":0,"BASE":18} +NiteBird TT-WB4 800lm {"NAME":"NiteBird TT-WB","GPIO":[0,0,0,0,40,0,0,0,37,38,39,0,0],"FLAG":0,"BASE":18} +Novostella UT55506 10W 1050lm {"NAME":"Novostella 10W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Onforu 7W 700lm {"NAME":"Onforu RGBW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Orbecco 5W 400lm {"NAME":"Orbecco Bulb","GPIO":[0,0,0,0,0,0,0,0,143,0,144,0,0],"FLAG":0,"BASE":27} +Premier A19 10W {"NAME":"Premier Light","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":1,"BASE":18} +Riversong Juno 10W {"NAME":"Juno10","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18} +Rogoei EBE-QPZ04 6.5W 450lm {"NAME":"EBE-QPZ04","GPIO":[0,0,0,0,180,0,0,0,0,0,181,0,0],"FLAG":0,"BASE":18} +Smart 810lm {"NAME":"OOOLED 60W RGB","GPIO":[0,0,0,0,39,40,0,0,37,0,38,0,0],"FLAG":1,"BASE":18} +SmartLED 9W 400lm {"NAME":"SmartLED RGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Smartyfi 600lm {"NAME":"SMARTYFI 9W","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Solimo 12W {"NAME":"Solimo RGBWW12","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Solimo 810lm {"NAME":"Solimo RGBWW 9","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Swisstone SH 320 350lm {"NAME":"SH 320","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Swisstone SH 340 806lm {"NAME":"SH 340","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":15,"BASE":18} +Syska 720lm {"NAME":"SyskaSmartBulb","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +TCP Smart 806lm {"NAME":"TCP Smart RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Teckin SB50 800lm {"NAME":"Teckin SB50","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +Teckin SB50 v2 800lm {"NAME":"Teckin SB50","GPIO":[0,0,0,0,37,0,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} +Teckin SB51 800lm {"NAME":"Teckin SB51","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +TikLOk TL530 A19 7.5W 800lm {"NAME":"TikLOk WW-CW-L","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +TVLive 7.5W 800lm {"NAME":"TVLIVE RGBCW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Utorch LE7 600lm {"NAME":"Utorch LE7","GPIO":[0,0,0,0,0,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} +V-Tac A60 11W 1055lm {"NAME":"V-TAC LED A60 ","GPIO":[0,0,0,0,180,0,0,0,0,0,181,0,0],"FLAG":0,"BASE":18} +V-TAC A95 18W 1350lm {"NAME":"V-TAC VT-5021","GPIO":[0,0,0,0,37,41,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} +Wallfire WF-05 {"NAME":"Wallfire E27","GPIO":[17,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Woopower 460lm {"NAME":"Woopower E14","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +WOOX R4553 650lm {"NAME":"WOOX R4553","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +WOOX R5076 4W 350lm {"NAME":"WOOX R4553","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18} +Woox R5077 {"NAME":"WOOX R5077","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Zemismart 5W {"NAME":"Zemismart_GU10","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Zemismart A19 10W {"NAME":"Zemism_E27_A19","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Zilotek A19 800lm {"NAME":"Zilotek RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +``` + ## Relay ``` 1 Channel Inching/Self-Locking {"NAME":"1 Channel","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":12} BlitzWolf BW-SS1 {"NAME":"BW-SS1","GPIO":[255,255,255,255,157,21,0,0,255,17,255,255,0],"FLAG":0,"BASE":18} +BlitzWolf SS4 Two Gang {"NAME":"BlitzWolf SS4","GPIO":[0,0,0,0,56,21,0,0,22,17,0,0,0],"FLAG":0,"BASE":18} Canwing CW-001 {"NAME":"Canwing CW-001","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} Century Aoke Smart Switch {"NAME":"CenturyAoke","GPIO":[0,255,0,255,21,0,0,0,17,56,255,0,0],"FLAG":0,"BASE":18} Deta 6000HA Smart Inline Switch {"NAME":"DETA-6000HA","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} +dewenwils Outdoor Timer Box {"NAME":"Dewenwils50054","GPIO":[0,0,54,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} DoHome HomeKit DIY Switch {"NAME":"DoHome DIY","GPIO":[255,255,0,255,255,157,0,0,21,0,0,0,0],"FLAG":0,"BASE":1} Eachen ST-DC2 {"NAME":"Garage Control","GPIO":[11,0,0,0,23,22,18,0,21,52,12,24,0],"FLAG":1,"BASE":18} Eachen ST-UDC1 {"NAME":"ST-UDC1","GPIO":[9,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":1,"BASE":18} @@ -951,26 +1087,30 @@ EX Store 2 Kanal V5 {"NAME":"EXS Relay V5","GPIO":[255,255,255,255,255,255, Garage Door Controller {"NAME":"Garage Opener","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} Geekcreit 2 Channel AC 85V-250V {"NAME":"Geekcreit 2ch","GPIO":[17,0,0,0,0,22,18,0,21,52,0,0,0],"FLAG":1,"BASE":18} Geekcreit 5V DIY 4 Channel Jog Inching Self-Locking {"NAME":"Geekcreit-4ch","GPIO":[9,0,0,0,23,22,10,11,21,52,12,24,0],"FLAG":0,"BASE":18} -Geekcreit Module 220V 10A {"NAME":"WeMos Relay","GPIO":[17,255,52,255,21,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Geekcreit Module 220V 10A {"NAME":"DIY ESP8266 Re","GPIO":[0,0,157,0,21,17,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} Gocomma Wi-Fi Smart Switch {"NAME":"GoCommaSmartSw","GPIO":[17,255,255,255,21,0,0,0,255,56,0,0,0],"FLAG":0,"BASE":18} Hoch Circuit Breaker 1P {"NAME":"HOCH ZJSB9","GPIO":[17,255,255,255,255,255,0,0,21,56,0,0,0],"FLAG":0,"BASE":18} +HW-622 ESP8266 {"NAME":"HW-622","GPIO":[0,0,157,0,21,17,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} L-5A01 {"NAME":"L-5A01","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} LC Technology 5V 2 Channel {"NAME":"LC-ESP01-2R-5V","GPIO":[0,148,0,149,0,0,0,0,21,22,0,0,0],"FLAG":0,"BASE":18} LC Technology 5V 4 Channel {"NAME":"LC-Tech_4CH ","GPIO":[52,255,17,255,0,0,0,0,21,22,23,24,0],"FLAG":0,"BASE":18} LC Technology AC/DC 1 Channel ESP-12F Dev Board {"NAME":"LC-ESP12-1R-MV","GPIO":[255,255,157,255,255,21,255,255,255,255,255,255,57],"FLAG":15,"BASE":18} +LC Technology ESP8266 5V {"NAME":"ESP8266-01S","GPIO":[21,148,0,149,0,0,0,0,0,0,0,0,0],"FLAG":1,"BASE":18} LinkNode R4 {"NAME":"LinkNode R4","GPIO":[0,0,0,0,0,0,0,0,21,22,23,0,24],"FLAG":0,"BASE":18} LinkNode R8 {"NAME":"LinkNode R8","GPIO":[0,0,0,0,25,26,0,28,23,24,22,27,21],"FLAG":0,"BASE":18} +LoraTap 10A {"NAME":"LoraTap RR400W","GPIO":[0,0,0,0,157,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} LoraTap RR500W {"NAME":"LoraTap RR500W","GPIO":[157,255,255,255,9,255,255,255,255,21,255,255,56],"FLAG":15,"BASE":18} LoraTap SC500W Roller Shutter {"NAME":"SC500W","GPIO":[0,0,0,158,9,10,0,0,21,17,22,0,0],"FLAG":0,"BASE":18} Mhcozy 5V {"NAME":"Portail","GPIO":[9,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":1,"BASE":18} Moes MS-104B-1 {"NAME":"Moes MS-104B","GPIO":[0,0,17,0,160,0,0,0,43,42,21,22,0],"FLAG":0,"BASE":18} Nova Digital Basic 1 MS101 {"NAME":"NovaDigBasic1","GPIO":[0,255,0,255,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +OpenEnergyMonitor WiFi MQTT Thermostat {"NAME":"MQTT-RELAY","GPIO":[17,0,255,0,0,21,0,0,0,0,0,0,56],"FLAG":0,"BASE":18} Protium PS-1604 {"NAME":"Protium16A","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":1} -QS-WIFI-S03 {"NAME":"QS-WIFI-S03","GPIO":[17,255,255,255,255,0,0,0,82,21,0,0,0],"FLAG":0,"BASE":1} +QS-WIFI-S03 Module Switch {"NAME":"QS-WIFI-S03","GPIO":[17,255,255,255,255,0,0,0,82,21,0,0,0],"FLAG":0,"BASE":1} Shelly 1 {"NAME":"Shelly 1","GPIO":[0,0,0,0,21,82,0,0,0,0,0,0,0],"FLAG":0,"BASE":46} Shelly 1PM {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} Shelly 2 {"NAME":"Shelly 2","GPIO":[0,135,0,136,21,22,0,0,9,0,10,137,0],"FLAG":0,"BASE":47} -Shelly 2.5 {"NAME":"Shelly 2.5","GPIO":[56,255,17,255,21,83,0,0,6,82,5,22,156],"FLAG":2,"BASE":18} +Shelly 2.5 {"NAME":"Shelly 2.5","GPIO":[56,0,17,0,21,83,0,0,6,82,5,22,156],"FLAG":2,"BASE":18} Sinilink XY-WF36V DC6V-36V Switch Module {"NAME":"Sinilink XY-WF5V","GPIO":[0,0,0,0,21,255,0,0,17,52,0,0,255],"FLAG":0,"BASE":18} Sinilink XY-WFMS MOS Switch Module {"NAME":"Sinilink MOS","GPIO":[0,0,0,0,21,255,0,0,17,52,0,0,255],"FLAG":0,"BASE":18} Sinilink XY-WFUSB USB Switch {"NAME":"XY-WFUSB","GPIO":[255,255,0,255,17,21,0,0,0,0,56,0,157],"FLAG":0,"BASE":18} @@ -993,15 +1133,19 @@ SS311KWS RF Kinetic Switch and WiFi {"NAME":"SS311KWS","GPIO":[0,0,0,0,52,0,0,0 SW-R03 {"NAME":"SW-R03","GPIO":[0,0,0,0,0,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} WL-SW01_10 {"NAME":"WL-SW01_10","GPIO":[17,149,0,148,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} Zemismart ERC309 Kinetic Switch {"NAME":"Kinetic Switch","GPIO":[255,255,255,255,255,255,0,0,255,108,255,107,255],"FLAG":0,"BASE":54} +ZMAi-90 Digital Energy Meter {"NAME":"ZMAi-90","GPIO":[0,148,0,149,0,0,0,0,21,90,0,0,0],"FLAG":0,"BASE":18} ``` ## Sensor ``` +Digoo DG-ZXD21 Door Detector {"NAME":"Digoo ZXD21","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} DP-WP001 PIR {"NAME":"TUYA PIR","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} DS18B20 ESP01 Module Temperature {"NAME":"ESP01S ds18b20","GPIO":[255,255,4,255,255,255,0,0,255,255,255,255,255],"FLAG":15,"BASE":18} Earykong TYMC-1 Door Window {"NAME":"TYMC-1","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} IOT4SH01DS Temperature {"NAME":"IOT4SH01DS","GPIO":[255,255,255,255,255,255,0,0,255,4,255,255,255],"FLAG":15,"BASE":18} Mirabella Genio I002576 Motion {"NAME":"GenioPir","GPIO":[17,107,0,108,0,0,0,0,0,56,0,0,0],"FLAG":0,"BASE":54} +Nedis Smoke Detector {"NAME":"Nedis Smoke","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} +Shelly Temperature Sensor Add-on {"NAME":"Shelly 1 Temp ","GPIO":[192,0,0,4,21,82,0,0,0,0,0,0,0],"FLAG":0,"BASE":46} Sonoff SC {"NAME":"Sonoff SC","GPIO":[17,148,255,149,0,0,0,0,0,56,0,0,0],"FLAG":0,"BASE":21} Zemismart Door Window {"NAME":"Zemismart","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} ``` @@ -1010,11 +1154,15 @@ Zemismart Door Window {"NAME":"Zemismart","GPIO":[255,107,255,108,255,255,0,0 ``` 3A Smart Home HGZB-043 {"NAME":"3A Smart Home ","GPIO":[52,0,55,18,22,19,0,0,17,21,54,23,53],"FLAG":0,"BASE":18} Aoycocr SW1 {"NAME":"Aoycocr SW1","GPIO":[158,255,57,255,255,255,255,255,56,17,255,21,255],"FLAG":15,"BASE":18} +Avatto 2 Gang {"NAME":"Avatto Wifi - ","GPIO":[0,0,52,0,0,17,0,0,21,22,0,0,18],"FLAG":0,"BASE":18} Bardi Smart Wallswitch 2 {"NAME":"BARDI 2 Gang","GPIO":[56,0,157,18,22,0,0,0,52,21,57,0,17],"FLAG":0,"BASE":18} Bardi Smart Wallswitch 3 {"NAME":"BARDI 3 Gang","GPIO":[56,57,157,19,23,18,0,0,52,21,58,22,17],"FLAG":0,"BASE":18} +BAZZ SWTCHWFW1 {"NAME":"BAZZ KS-602S","GPIO":[17,0,0,0,0,0,21,52,29,56,0,0,0],"FLAG":0,"BASE":18} +Blitzwolf BW-SS3 1 Gang {"NAME":"BW-SS3-1G-EU","GPIO":[52,0,0,17,0,0,0,0,0,21,0,0,0],"FLAG":0,"BASE":18} BlitzWolf BW-SS3 2 Gang {"NAME":"BW-SS3-2G-EU","GPIO":[157,255,255,255,22,18,255,255,17,21,255,255,255],"FLAG":15,"BASE":18} BlitzWolf BW-SS3 3 Gang {"NAME":"BlitzWolf SS3","GPIO":[158,0,0,10,22,11,0,0,9,21,0,23,0],"FLAG":0,"BASE":18} CD303 3 Gang Touch {"NAME":"Touch Switch 3","GPIO":[54,57,255,19,23,18,255,255,17,21,255,22,52],"FLAG":15,"BASE":18} +Connect Smart 2 Gang Wall {"NAME":"CSH-SWTCH2","GPIO":[0,0,52,0,0,18,0,0,22,21,0,0,17],"FLAG":0,"BASE":18} Deta 6911HA {"NAME":"Deta 1G Switch","GPIO":[0,0,0,0,157,0,0,0,0,21,0,0,90],"FLAG":0,"BASE":18} Deta 6912HA {"NAME":"DETA 2G Switch","GPIO":[0,0,0,0,157,0,0,0,91,21,22,0,90],"FLAG":0,"BASE":18} Digoo DG-S811 3 Gang {"NAME":"DIGOO Switch","GPIO":[0,0,0,0,19,18,0,0,22,21,23,0,17],"FLAG":0,"BASE":18} @@ -1028,14 +1176,19 @@ Etekcity ESWL01 {"NAME":"EtekCityESWL01","GPIO":[0,255,0,255,52,53,0,0, Etekcity ESWL03 3-way {"NAME":"Etekcity 3Way","GPIO":[0,0,0,0,23,29,0,0,82,22,10,0,0],"FLAG":0,"BASE":18} Eva Logik WF30 3-Way {"NAME":"WF30 Switch","GPIO":[0,0,0,0,18,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Geeni TAP 3-Way {"NAME":"Geeni 3-Way","GPIO":[157,0,0,0,0,0,0,0,17,21,0,0,0],"FLAG":0,"BASE":18} +Girier EK01 RF433Mhz 1 Gang {"NAME":"Girier EK01","GPIO":[157,0,0,0,21,0,0,0,0,0,0,0,17],"FLAG":0,"BASE":18} +Girier EK02 RF433Mhz 2 Gang {"NAME":"Girier EK02","GPIO":[157,0,0,0,0,17,0,0,18,21,22,0,0],"FLAG":0,"BASE":18} +Girier EK03 RF433Mhz 3 Gang {"NAME":"EK03","GPIO":[157,0,0,0,22,17,0,0,19,21,23,0,18],"FLAG":0,"BASE":18} Girier JRSWR-SEU01 1 Gang {"NAME":"W601","GPIO":[0,0,0,0,0,17,0,0,0,0,0,21,157],"FLAG":15,"BASE":18} Girier JRSWR-SEU01 2 Gang {"NAME":"W602","GPIO":[0,0,0,0,22,0,0,0,17,21,18,0,157],"FLAG":15,"BASE":18} -Girier JRSWR-US01 No Neutral {"NAME":"Tuya 1 Channel","GPIO":[0,0,0,0,0,17,0,0,0,0,0,21,157],"FLAG":0,"BASE":18} +Girier JRSWR-SEU01 3 Gang {"NAME":"W603","GPIO":[0,0,0,0,23,18,0,0,17,21,19,22,157],"FLAG":15,"BASE":18} +Girier JRSWR-US01 No Neutral 1 Gang {"NAME":"Tuya 1 Channel","GPIO":[0,0,0,0,0,17,0,0,0,0,0,21,157],"FLAG":0,"BASE":18} +Girier JRSWR-US01 No Neutral 3 Gang {"NAME":"Girier JRSWR-U","GPIO":[0,0,0,0,21,18,0,0,19,23,17,22,157],"FLAG":0,"BASE":18} GoKlug Glass Touch 1 Gang {"NAME":"GoKlug 1x","GPIO":[56,57,0,0,0,9,0,0,0,0,0,21,0],"FLAG":0,"BASE":18} -Gosund KS-602S {"NAME":"Gosund KS-602S","GPIO":[17,56,0,0,0,0,0,0,0,0,21,0,158],"FLAG":0,"BASE":18} -Gosund SW1 {"NAME":"Gosund SW1","GPIO":[17,56,0,0,0,0,0,0,0,0,21,0,158],"FLAG":0,"BASE":18} -Gosund SW1 v2 {"NAME":"Gosund SW1","GPIO":[17,0,56,0,0,0,0,0,0,0,21,0,57],"FLAG":0,"BASE":18} -HBN Wall-Mounted Timer {"NAME":"HBN Time Switc","GPIO":[0,0,0,0,54,57,0,0,21,0,0,0,0],"FLAG":0,"BASE":18} +Gosund KS-602S {"NAME":"Gosund KS-602S","GPIO":[17,0,56,0,0,0,0,0,0,0,21,0,158],"FLAG":0,"BASE":18} +Gosund SW1 {"NAME":"Gosund SW1","GPIO":[17,0,56,0,0,0,0,0,0,0,21,0,57],"FLAG":0,"BASE":18} +Hama Flush-mounted {"NAME":"Hama WiFiTouch","GPIO":[157,0,0,0,0,18,0,0,17,22,0,21,0],"FLAG":0,"BASE":45} +HBN Wall-Mounted Timer {"NAME":"HBN Timer Switch","GPIO":[0,0,0,0,54,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Jinvoo SM-SW101-1 {"NAME":"SM-SW101-1","GPIO":[52,0,0,18,0,0,0,0,17,21,0,0,0],"FLAG":1,"BASE":18} Jinvoo SM-SW101-2 {"NAME":"SM-SW101-2","GPIO":[52,0,0,18,22,0,0,0,17,21,0,0,0],"FLAG":1,"BASE":18} Jinvoo SM-SW101-3 {"NAME":"Jinvoo Wall Sw","GPIO":[52,0,0,18,22,19,0,0,17,21,0,23,0],"FLAG":1,"BASE":18} @@ -1044,6 +1197,7 @@ Koaanw CD302-EU-1 {"NAME":"CD302-EU-1","GPIO":[0,0,0,0,157,56,0,0,21,17,0 Koaanw CD302-EU-2 {"NAME":"CD302-EU-2","GPIO":[157,0,0,0,0,18,0,0,17,21,57,22,56],"FLAG":0,"BASE":18} Koaanw CD302-EU-3 {"NAME":"CD302-EU-3","GPIO":[158,53,0,19,23,18,0,0,17,21,54,22,52],"FLAG":0,"BASE":18} KS-601 2-way {"NAME":"2way Switch","GPIO":[255,255,158,255,255,83,0,0,22,21,255,82,255],"FLAG":0,"BASE":18} +KS-602 {"NAME":"Gosund KS-602","GPIO":[17,0,0,0,0,0,0,0,21,157,0,0,0],"FLAG":0,"BASE":18} KS-605 {"NAME":"KS-605","GPIO":[17,0,0,0,0,0,0,0,21,158,0,0,0],"FLAG":0,"BASE":18} Kuled K36 {"NAME":"KULED-B","GPIO":[9,255,255,255,255,255,21,52,29,56,255,255,255],"FLAG":0,"BASE":18} Kuled KS602S {"NAME":"KULED","GPIO":[17,255,255,255,255,255,0,0,21,56,255,255,255],"FLAG":0,"BASE":18} @@ -1055,7 +1209,7 @@ LerLink X802A-L No Neutral {"NAME":"LerLink X802-L","GPIO":[0,0,0,18,17,0,0,0,2 Lightstory WT02S {"NAME":"WT02S","GPIO":[0,0,0,0,57,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":50} Lonsonho SK3-01 {"NAME":"Tuya 1 Channel","GPIO":[0,0,0,0,0,17,0,0,0,0,0,21,52],"FLAG":0,"BASE":18} Lonsonho SK3-02 {"NAME":"Tuya 2 Channel","GPIO":[0,0,0,0,22,0,0,0,17,21,18,0,52],"FLAG":0,"BASE":18} -Lonsonho SK3-03 {"NAME":"ID Components","GPIO":[0,0,0,0,21,18,0,0,19,23,17,22,157],"FLAG":0,"BASE":18} +Lonsonho SK3-03 {"NAME":"Tuya 3-ch v2","GPIO":[157,58,0,18,22,19,0,0,17,21,57,23,56],"FLAG":0,"BASE":18} LoraTap WH100W-US 20A {"NAME":"LoraTap Boiler","GPIO":[0,0,0,0,0,0,0,0,17,21,0,0,56],"FLAG":0,"BASE":18} Luminea LHC-101.on {"NAME":"LHC-101.on","GPIO":[157,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18} Luminea LHC-102.on {"NAME":"LHC-102.on","GPIO":[157,0,53,0,0,18,0,0,17,21,0,22,52],"FLAG":0,"BASE":18} @@ -1069,23 +1223,30 @@ Minitiger 1 Gang {"NAME":"minitiger 1 Gang","GPIO":[17,255,255,255,0,0,0 Minitiger 1 Gang v2 {"NAME":"MiniTiger1Band","GPIO":[0,56,0,0,0,17,0,0,21,0,0,0,0],"FLAG":0,"BASE":18} Minitiger 2 Gang {"NAME":"minitiger 2 Gang","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":28} Minitiger 2 Gang v2 {"NAME":"Minitiger2Band","GPIO":[0,0,0,17,18,0,0,0,0,21,22,0,0],"FLAG":0,"BASE":18} +Minitiger 3 Gang {"NAME":"Minitiger3gang","GPIO":[0,0,0,9,11,10,255,255,22,21,23,0,0],"FLAG":0,"BASE":18} Moes BS-US-W Boiler {"NAME":"BS-US-W","GPIO":[54,0,0,17,21,0,0,0,0,0,52,0,55],"FLAG":0,"BASE":18} Moes SS01-1 3-Way {"NAME":"Moes 3-Way","GPIO":[255,255,255,255,21,57,0,0,30,10,9,255,255],"FLAG":0,"BASE":18} Moes SS01S-1 {"NAME":"Moes Switch","GPIO":[255,255,255,255,56,0,0,0,21,17,255,255,255],"FLAG":0,"BASE":18} -Moes SS86-01 AI {"NAME":"SS86-AI 1 Gang","GPIO":[157,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18} +Moes WF-FL01 Light and Fan {"NAME":"Moes WF-FL01","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} Moes WS-EU1-LB 1 Gang No Neutral {"NAME":"Moes WS-EU1-LB","GPIO":[0,0,0,0,0,17,0,0,0,0,0,21,157],"FLAG":0,"BASE":18} Moes WS-EU2-LW 2 Gang No Neutral {"NAME":"Tuya 2 Channel","GPIO":[0,0,0,0,22,0,0,0,17,21,18,0,157],"FLAG":0,"BASE":18} Moes WS-EU3-LW 3 Gang No Neutral {"NAME":"Tuya 3 Channel","GPIO":[0,0,0,0,21,19,0,0,18,22,17,23,157],"FLAG":0,"BASE":18} -Moes WS-EUY3-W {"NAME":"WS-EUY3-W","GPIO":[157,0,58,18,22,19,0,0,17,21,57,23,56],"FLAG":0,"BASE":18} +Moes WS-EUY1 1 Gang {"NAME":"WS-EUY1-W","GPIO":[157,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18} +Moes WS-EUY2 2 Gang {"NAME":"WS-EUY2-W","GPIO":[157,0,53,0,0,18,0,0,17,21,0,22,52],"FLAG":0,"BASE":18} +Moes WS-EUY3 3 Gang {"NAME":"WS-EUY3-W","GPIO":[157,0,58,18,22,19,0,0,17,21,57,23,56],"FLAG":0,"BASE":18} Moes WS-US1-W 1 Gang {"NAME":"WS-US1-W","GPIO":[54,0,0,17,21,0,0,0,0,0,52,0,55],"FLAG":0,"BASE":18} Moes WS-US2-W 2 Gang {"NAME":"WS-US2-W ","GPIO":[52,0,53,0,0,18,0,0,17,21,0,22,54],"FLAG":0,"BASE":18} -Moes WS-US3-W 3 Gang {"NAME":"Tuya Moes 3 Ch","GPIO":[27,255,26,18,22,19,0,0,17,21,25,23,24],"FLAG":0,"BASE":18} +Moes WS-US3-W 3 Gang {"NAME":"Tuya Moes 3 Ch","GPIO":[157,0,54,18,22,19,0,0,17,21,53,23,52],"FLAG":0,"BASE":18} Moes WT02S {"NAME":"Moes WT02S","GPIO":[0,0,0,0,56,158,0,0,21,9,0,0,0],"FLAG":15,"BASE":18} +MoesHouse RF433 3 Gang {"NAME":"WS-EUB3-WR","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} +MoKo Scene Life {"NAME":"Moko Smart Swi","GPIO":[158,0,0,0,39,38,0,0,56,0,37,21,0],"FLAG":0,"BASE":18} MoKo Smart Life {"NAME":"Moko Switch","GPIO":[0,0,0,17,21,134,0,0,0,0,0,0,0],"FLAG":0,"BASE":59} NaamaSmart KS602 {"NAME":"KS-602","GPIO":[17,0,0,0,0,0,0,0,21,158,0,0,0],"FLAG":0,"BASE":18} +Nedis Dual {"NAME":"SM-SW102U-2","GPIO":[158,0,0,18,22,0,0,0,17,21,0,0,0],"FLAG":1,"BASE":18} Nexete DS-123 {"NAME":"DS-123","GPIO":[157,57,255,17,21,18,0,0,255,22,56,255,255],"FLAG":0,"BASE":18} Nexete DS-123 Single {"NAME":"DS-123","GPIO":[157,0,255,18,0,17,0,0,255,21,56,255,255],"FLAG":0,"BASE":18} Sainko 1-Way {"NAME":"SAINKO 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28} +Sainko 2 Way {"NAME":"Sainko 2-Way","GPIO":[0,255,56,0,0,17,0,0,21,22,0,0,18],"FLAG":0,"BASE":18} SANA SASW-03 {"NAME":"SANA SASW-03","GPIO":[54,0,0,19,23,18,0,0,17,21,0,22,0],"FLAG":0,"BASE":18} SANA SW02-02 {"NAME":"SW02-02","GPIO":[31,255,255,18,22,0,0,0,17,21,255,0,255],"FLAG":0,"BASE":18} SANA SW02-03 {"NAME":"SW02-03","GPIO":[56,255,255,19,23,18,0,0,17,21,255,22,255],"FLAG":0,"BASE":18} @@ -1096,6 +1257,7 @@ Sesoo WIFI-EU-SK3-02 {"NAME":"Sesoo SK3-02","GPIO":[0,0,57,0,22,0,0,0,17,21, Sesoo WIFI-US-SK3-04 {"NAME":"Tuya 4 Channel","GPIO":[52,255,255,19,23,17,0,0,20,24,22,21,18],"FLAG":0,"BASE":18} Smartlife Opard CD302 {"NAME":"CD302","GPIO":[0,0,0,0,52,57,0,0,29,17,0,0,0],"FLAG":0,"BASE":18} SmartPlex 3 Gang {"NAME":"Tuya 3 Channel","GPIO":[255,255,255,255,21,18,0,0,19,23,17,22,255],"FLAG":0,"BASE":18} +Sonoff IW101 {"NAME":"Sonoff IW100","GPIO":[17,145,0,146,0,0,0,0,21,157,0,0,0],"FLAG":0,"BASE":41} Sonoff T1 EU 1 Gang {"NAME":"Sonoff T1 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28} Sonoff T1 EU 2 Gang {"NAME":"Sonoff T1 2CH","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":29} Sonoff T1 UK 1 Gang {"NAME":"Sonoff T1 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28} @@ -1104,30 +1266,36 @@ Sonoff T1 UK 3 Gang {"NAME":"Sonoff T1 3CH","GPIO":[17,255,255,255,23,22,18 Sonoff T1 US 1 Gang {"NAME":"Sonoff T1 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28} Sonoff T1 US 2 Gang {"NAME":"Sonoff T1 2CH","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":29} Sonoff T1 US 3 Gang {"NAME":"Sonoff T1 3CH","GPIO":[17,255,255,255,23,22,18,19,21,56,0,0,0],"FLAG":0,"BASE":30} -Sonoff Touch EU {"NAME":"Sonoff Touch","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":10} -Sonoff Touch US {"NAME":"Sonoff Touch","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":10} +Sonoff Touch {"NAME":"Sonoff Touch","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":10} +Sonoff Touch {"NAME":"Sonoff Touch","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":10} Sonoff TX T3 3 Gang {"NAME":"TX T3EU3C","GPIO":[17,255,0,255,23,22,18,19,21,158,0,0,0],"FLAG":0,"BASE":30} +SRL 3-4WW 4 Gang {"NAME":"SRL 4WW Switch","GPIO":[0,0,0,19,23,18,0,0,17,21,24,22,20],"FLAG":0,"BASE":18} SS118-01K1 {"NAME":"SS118-01K1","GPIO":[255,255,255,17,21,255,0,0,255,255,56,255,255],"FLAG":0,"BASE":18} SS86-AI 3-Gang {"NAME":"SS86-AI 3 Gang","GPIO":[157,0,58,18,22,19,0,0,17,21,57,23,56],"FLAG":0,"BASE":18} SSMS118-01A1 Scene Light Smart {"NAME":"RGB Switch","GPIO":[30,0,32,10,39,38,0,0,31,9,37,21,0],"FLAG":0,"BASE":18} STITCH by Monoprice 35557 {"NAME":"Tuya WF15S ","GPIO":[255,255,0,0,255,255,0,0,255,108,255,107,0],"FLAG":0,"BASE":54} +Teckin SR43 {"NAME":"Teckin SR43","GPIO":[0,0,52,0,0,17,0,0,21,22,0,0,18],"FLAG":0,"BASE":18} Teekar 10 Way 1 Gang {"NAME":"Teekar 10way","GPIO":[9,10,11,20,13,14,0,0,15,16,0,0,0],"FLAG":0,"BASE":18} Teekar Wi-Fi Light 1 Gang {"NAME":"TeeKar Touch","GPIO":[0,0,255,0,255,21,0,0,0,255,17,0,255],"FLAG":0,"BASE":18} Teepao Smart-Rollladen-Schalter {"NAME":"Teepao","GPIO":[158,58,23,18,22,19,0,0,56,21,57,0,17],"FLAG":0,"BASE":18} Tonbux AMZ180648-2 {"NAME":"Tonbux","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":1} Touch 2 Gang {"NAME":"tuya_2_gang","GPIO":[52,0,0,0,18,0,0,0,17,21,255,22,29],"FLAG":0,"BASE":18} +Touch 3 Gang {"NAME":"Switch 3-Gang","GPIO":[0,0,0,0,23,18,0,0,17,21,19,22,157],"FLAG":0,"BASE":18} TreatLife SS01S {"NAME":"TL SS01S Swtch","GPIO":[0,0,0,0,52,158,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} TreatLife SS02 3-Way {"NAME":"TreatLife 3Way","GPIO":[0,0,0,0,53,29,0,0,30,18,9,0,0],"FLAG":0,"BASE":18} TreatLife SS02S {"NAME":"Treatlife SS02","GPIO":[0,0,0,0,53,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +TY-US-L1-W {"NAME":"TY-US-L1-W","GPIO":[0,0,0,0,0,17,0,0,0,21,0,0,158],"FLAG":0,"BASE":18} +TY-US-L3-W {"NAME":"TY-US-L3-W","GPIO":[0,0,0,0,21,18,0,0,19,23,17,22,158],"FLAG":15,"BASE":18} Vaticas 1 {"NAME":"Vaticas","GPIO":[0,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18} +vhome RF433 3 Gang {"NAME":"VH-TB-US-003","GPIO":[0,0,0,0,21,18,0,0,19,23,17,22,158],"FLAG":15,"BASE":18} WiFi Smart Switch 2 Gang {"NAME":"Kingart N2","GPIO":[17,255,0,255,0,22,18,0,21,0,0,0,0],"FLAG":15,"BASE":18} WiFi Smart Switch 3 Gang {"NAME":"KingArt-3CH","GPIO":[17,255,0,255,23,22,18,19,21,52,0,0,0],"FLAG":15,"BASE":18} WS-US-03 {"NAME":"WS-US-03","GPIO":[17,255,255,255,23,22,18,19,21,56,0,0,0],"FLAG":0,"BASE":30} Xenon SM-SW102U 2 Gang {"NAME":"SM-SW102U-2","GPIO":[52,0,0,18,22,0,0,0,17,21,0,0,0],"FLAG":1,"BASE":18} Xenon SM-SW202 {"NAME":"SM-SW202","GPIO":[0,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18} Yapmor 1-gang {"NAME":"YAPMOR 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28} -Youngzuth 3in1 {"NAME":"SW02 3W","GPIO":[52,0,255,19,23,18,0,0,17,21,255,22,255],"FLAG":0,"BASE":18} -Youngzuth SW02 2-way {"NAME":"SW02 2W","GPIO":[52,255,255,9,21,255,0,0,10,22,255,255,255],"FLAG":0,"BASE":18} +Youngzuth 2in1 {"NAME":"SW02 2W","GPIO":[52,0,0,9,21,0,0,0,10,22,0,0,0],"FLAG":0,"BASE":18} +Youngzuth 3in1 {"NAME":"SW02 3W","GPIO":[56,0,0,19,23,18,0,0,17,21,0,22,0],"FLAG":0,"BASE":18} Zemismart KS-611 3 Gang {"NAME":"Zemismart 3 Ga","GPIO":[0,0,56,0,19,18,0,0,22,21,23,0,17],"FLAG":0,"BASE":18} Zemismart KS-811 1 Gang {"NAME":"KS-811 Single","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":18} Zemismart KS-811 2 Gang {"NAME":"KS-811 Dual","GPIO":[0,0,52,0,0,18,0,0,22,21,0,0,17],"FLAG":0,"BASE":18} @@ -1137,6 +1305,8 @@ Zemismart WF-BS01 {"NAME":"WF-BS01","GPIO":[53,0,0,17,21,0,0,0,0,0,52,0,0 Zemismart ZM-L01E {"NAME":"ZSmart ZM-L01E","GPIO":[255,255,255,255,255,255,0,0,255,21,255,255,17],"FLAG":0,"BASE":18} Zemismart ZM-L02E {"NAME":"ZSmart ZM-L02E","GPIO":[255,255,255,255,255,17,0,0,18,21,22,255,255],"FLAG":0,"BASE":18} Zemismart ZM-L03E {"NAME":"ZSmart ZM-L03E","GPIO":[52,53,0,0,23,17,0,0,19,21,22,0,18],"FLAG":0,"BASE":18} +ZUCZUG 2 Gang {"NAME":"2ph105626a x2","GPIO":[0,52,0,17,18,0,0,0,0,21,22,0,0],"FLAG":0,"BASE":1} +ZUCZUG 3 Gang {"NAME":"2ph105626a x3","GPIO":[0,52,0,17,19,18,0,0,22,21,23,0,0],"FLAG":0,"BASE":1} ``` ## Valve @@ -1146,12 +1316,13 @@ Hoenyzy DN20 3/4 {"NAME":"DN20 Valve","GPIO":[0,0,0,0,0,0,0,0,17,21,0,0, Jinvoo SM-AW713 {"NAME":"Jinvoo Valve","GPIO":[0,0,0,0,0,52,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} Jinvoo SM-AW713 v2 {"NAME":"Jinvoo Valve v2","GPIO":[0,0,0,0,52,53,0,0,21,17,0,0,0],"FLAG":0,"BASE":1} Jinvoo SM-PW713 {"NAME":"Jinvoo Valve","GPIO":[0,0,0,0,21,52,0,0,17,53,0,0,0],"FLAG":1,"BASE":18} +Owfeel EN71 {"NAME":"SmartValve","GPIO":[21,0,0,0,0,0,0,0,17,52,0,0,0],"FLAG":0,"BASE":18} ``` ## Wall Outlet ``` -Bestten LO-2-W v2 {"NAME":"BESTTEN LO-2-W","GPIO":[0,0,0,0,158,17,0,0,21,0,0,0,0],"FLAG":0,"BASE":18} -Bestten LO-2-W2 {"NAME":"BESTTEN LO-2-W","GPIO":[17,0,0,0,57,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} +Aseer THWFS01 {"NAME":"ASEER-THWFS01","GPIO":[56,18,157,59,134,132,0,0,131,22,57,21,17],"FLAG":0,"BASE":18} +Bestten LO-2-W {"NAME":"BESTTEN LO-2-W","GPIO":[0,0,0,0,158,17,0,0,21,0,0,0,0],"FLAG":0,"BASE":18} BlitzWolf BW-SHP8 {"NAME":"SHP8","GPIO":[0,56,0,17,134,132,0,0,131,53,21,0,0],"FLAG":0,"BASE":64} BSEED Smart Socket / WIFI {"NAME":"BSEED Socket","GPIO":[0,0,0,0,157,52,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} CE Smart Home LA-2-W3 {"NAME":"CE Smart Wall","GPIO":[255,255,255,255,157,17,0,0,21,255,255,255,255],"FLAG":15,"BASE":18} @@ -1172,7 +1343,7 @@ PS-1607 {"NAME":"PS-1607","GPIO":[17,0,0,0,0,22,18,0,21,0,0,0,0 Smanergy KA10 {"NAME":"KA10","GPIO":[0,56,0,17,134,132,0,0,131,53,21,0,0],"FLAG":0,"BASE":64} Sonoff IW100 {"NAME":"Sonoff IW100","GPIO":[17,145,0,146,0,0,0,0,21,157,0,0,0],"FLAG":0,"BASE":41} Sonoff S55 {"NAME":"Sonoff S55","GPIO":[17,255,0,255,255,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} -Teckin RF-SR40-US {"NAME":"RF-SR40-US","GPIO":[0,0,0,0,57,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Teckin SR40 {"NAME":"RF-SR40-US","GPIO":[158,0,0,17,56,18,0,0,22,21,57,23,0],"FLAG":0,"BASE":18} TopGreener TGWF15RM {"NAME":"TGWF15RM","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":55} Vigica VGSPK00815 {"NAME":"VIGICA outlet","GPIO":[17,255,255,255,255,22,18,255,21,255,255,255,255],"FLAG":1,"BASE":18} -``` \ No newline at end of file +``` diff --git a/esp32_partition_app1984k_spiffs64k.csv b/esp32_partition_app1984k_spiffs64k.csv new file mode 100644 index 000000000..3b428f9a9 --- /dev/null +++ b/esp32_partition_app1984k_spiffs64k.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1F0000, +app1, app, ota_1, 0x200000, 0x1F0000, +spiffs, data, spiffs, 0x3F0000,0x10000, diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp index 9827f7584..ec7097983 100644 --- a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp @@ -916,15 +916,22 @@ uint8_t *Adafruit_SSD1306::getBuffer(void) { of graphics commands, as best needed by one's own application. */ void Adafruit_SSD1306::display(void) { + int16_t col_start = 0; + int16_t col_end = WIDTH - 1; + if ((64 == WIDTH) && (48 == HEIGHT)) { // for 64x48, we need to shift by 32 in both directions + col_start += 32; + col_end += 32; + } + TRANSACTION_START static const uint8_t PROGMEM dlist1[] = { SSD1306_PAGEADDR, 0, // Page start address 0xFF, // Page end (not really, but works here) - SSD1306_COLUMNADDR, - 0 }; // Column start address + SSD1306_COLUMNADDR }; ssd1306_commandList(dlist1, sizeof(dlist1)); - ssd1306_command1(WIDTH - 1); // Column end address + ssd1306_command1(col_start); // Column start address + ssd1306_command1(col_end); // Column end address #if defined(ESP8266) // ESP8266 needs a periodic yield() call to avoid watchdog reset. diff --git a/lib/ArduinoNTPd/NTPServer.cpp b/lib/ArduinoNTPd/NTPServer.cpp index 6ee82bd6c..860ccc79d 100644 --- a/lib/ArduinoNTPd/NTPServer.cpp +++ b/lib/ArduinoNTPd/NTPServer.cpp @@ -17,63 +17,81 @@ bool NtpServer::beginListening() { - if (timeServerPort_.begin(NTP_PORT)){ - return true; - } - return false; + if (timeServerPort_.begin(NTP_PORT)){ + return true; + } + return false; } bool NtpServer::processOneRequest(uint32_t utc, uint32_t millisecs) { - // We need the time we've received the packet in our response. - uint32_t recvSecs = utc + NTP_TIMESTAMP_DIFF; - double recvFractDouble = (double)millisecs/0.00023283064365386963; // millisec/((10^6)/(2^32)) - uint32_t recvFract = (double)recvFractDouble; //TODO: really handle this!!! - bool processed = false; - - int packetDataSize = timeServerPort_.parsePacket(); - if (packetDataSize && packetDataSize >= NtpPacket::PACKET_SIZE) - { - // Received what is probably an NTP packet. Read it in and verify - // that it's legit. - NtpPacket packet; - timeServerPort_.read((char*)&packet, NtpPacket::PACKET_SIZE); - // TODO: verify packet. + // millisecs is millis() at the time of the last iTOW reception, where iTOW%1000 == 0 + uint32_t refMillis = millis()-millisecs; + if (refMillis>999){ + utc++; + refMillis = refMillis%1000; + } - // Populate response. - packet.swapEndian(); - packet.leapIndicator(0); - packet.versionNumber(4); - packet.mode(4); - packet.stratum = 2; // I guess stratum 1 is too optimistic - packet.poll = 10; // 6-10 per RFC 5905. - packet.precision = -21; // ~0.5 microsecond precision. - packet.rootDelay = 0; //60 * (0xFFFF / 1000); // ~60 milliseconds, TBD - packet.rootDispersion = 0; //10 * (0xFFFF / 1000); // ~10 millisecond dispersion, TBD - packet.referenceId[0] = 'G'; - packet.referenceId[1] = 'P'; - packet.referenceId[2] = 'S'; - packet.referenceId[3] = 0; - packet.referenceTimestampSeconds = utc; - packet.referenceTimestampFraction = recvFract; - packet.originTimestampSeconds = packet.transmitTimestampSeconds; - packet.originTimestampFraction = packet.transmitTimestampFraction; - packet.receiveTimestampSeconds = recvSecs; - packet.receiveTimestampFraction = recvFract; - - // ...and the transmit time. - // timeSource_.now(&packet.transmitTimestampSeconds, &packet.transmitTimestampFraction); - - // Now transmit the response to the client. - packet.swapEndian(); - timeServerPort_.beginPacket(timeServerPort_.remoteIP(), timeServerPort_.remotePort()); - for (int count = 0; count < NtpPacket::PACKET_SIZE; count++) - { - timeServerPort_.write(packet.packet()[count]); - } - timeServerPort_.endPacket(); - processed = true; - } - - return processed; + bool processed = false; + + int packetDataSize = timeServerPort_.parsePacket(); + if (packetDataSize && packetDataSize >= NtpPacket::PACKET_SIZE) + { + // We need the time we've received the packet in our response. + uint32_t recvSecs = utc + NTP_TIMESTAMP_DIFF; + + uint64_t recvFract64 = refMillis; + recvFract64 <<= 32; + recvFract64 /= 1000; + uint32_t recvFract = recvFract64 & 0xffffffff; + // is equal to: + // uint32_t recvFract = (double)(refMillis)/0.00000023283064365386963; + + // Received what is probably an NTP packet. Read it in and verify + // that it's legit. + NtpPacket packet; + timeServerPort_.read((char*)&packet, NtpPacket::PACKET_SIZE); + // TODO: verify packet. + + // Populate response. + packet.swapEndian(); + packet.leapIndicator(0); + packet.versionNumber(4); + packet.mode(4); + packet.stratum = 1; // >1 will lead to misinterpretation of refId + packet.poll = 10; // 6-10 per RFC 5905. + packet.precision = -21; // ~0.5 microsecond precision. + packet.rootDelay = 100 * (0xFFFF / 1000); //~100 milliseconds + packet.rootDispersion = 50 * (0xFFFF / 1000);; //~50 millisecond dispersion + packet.referenceId[0] = 'G'; + packet.referenceId[1] = 'P'; + packet.referenceId[2] = 'S'; + packet.referenceId[3] = 0; + packet.referenceTimestampSeconds = recvSecs; + packet.referenceTimestampFraction = 0; // the "click" of the GPS + packet.originTimestampSeconds = packet.transmitTimestampSeconds; + packet.originTimestampFraction = packet.transmitTimestampFraction; + packet.receiveTimestampSeconds = recvSecs; + packet.receiveTimestampFraction = recvFract; + + // ...and the transmit time. + // the latency has been between 135 and 175 microseconds in internal testing, so we harcode 150 + uint32_t transFract = recvFract+(150*(10^3)/(2^32)); // microsec/((10^3)/(2^32)) + if (recvFract>transFract){ + recvSecs++; //overflow + } + packet.transmitTimestampSeconds = recvSecs; + packet.transmitTimestampFraction = transFract; + + // Now transmit the response to the client. + packet.swapEndian(); + + timeServerPort_.beginPacket(timeServerPort_.remoteIP(), timeServerPort_.remotePort()); + timeServerPort_.write((const uint8_t *)packet.packet(), NtpPacket::PACKET_SIZE); + timeServerPort_.endPacket(); + + processed = true; + } + + return processed; } \ No newline at end of file diff --git a/lib/FrogmoreScd30/FrogmoreScd30.cpp b/lib/FrogmoreScd30/FrogmoreScd30.cpp index 32bbee5ba..e610bfb92 100644 --- a/lib/FrogmoreScd30/FrogmoreScd30.cpp +++ b/lib/FrogmoreScd30/FrogmoreScd30.cpp @@ -58,7 +58,9 @@ void FrogmoreScd30::begin(TwoWire *pWire, uint8_t i2cAddress) } co2NewDataLocation = -1; // indicates there is no data, so the 1st data point needs to fill up the median filter +#ifdef ESP8266 this->pWire->setClockStretchLimit(200000); +#endif this->ambientPressure = 0; } @@ -106,7 +108,11 @@ int FrogmoreScd30::clearI2CBus(void) snprintf_P(scd30log_data, sizeof(scd30log_data), "clearI2CBus"); AddLog(LOG_LEVEL_DEBUG_MORE); #endif +#ifdef ESP8266 return (twi_status()); +#else + return 0; +#endif } #ifdef SCD30_DEBUG @@ -253,7 +259,7 @@ int FrogmoreScd30::get16BitRegCheckCRC(void* pInput, uint16_t *pData) } // gets 32 bits, (2) 16-bit chunks, and validates the CRCs -// +// int FrogmoreScd30::get32BitRegCheckCRC(void *pInput, float *pData) { uint16_t tempU16High; @@ -458,7 +464,7 @@ int FrogmoreScd30::setTemperatureOffset(float offset_degC) { return (ERROR_SCD30_INVALID_VALUE); } - + } int FrogmoreScd30::setTemperatureOffset(uint16_t offset_centiDegC) @@ -568,7 +574,7 @@ int FrogmoreScd30::readMeasurement( return (error); } - error = get32BitRegCheckCRC(&bytes[12], &tempHumidity); + error = get32BitRegCheckCRC(&bytes[12], &tempHumidity); if (error) { #ifdef SCD30_DEBUG @@ -650,4 +656,3 @@ int FrogmoreScd30::stopMeasuring(void) { return (sendCommand(COMMAND_SCD30_STOP_MEASUREMENT)); } - diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.7.4/examples/IRrecvDumpV2/platformio.ini deleted file mode 100644 index d3c660df9..000000000 --- a/lib/IRremoteESP8266-2.7.4/examples/IRrecvDumpV2/platformio.ini +++ /dev/null @@ -1,46 +0,0 @@ -[platformio] -src_dir = . - -[env] -; Default platform -platform = espressif8266 -; Default board -board = nodemcuv2 -framework = arduino -lib_extra_dirs = ../../ -lib_ldf_mode = deep+ -lib_ignore = examples -build_flags = ; -D_IR_LOCALE_=en-AU - -[env:nodemcuv2] -board = nodemcuv2 -; build_flags = -D_IR_LOCALE_=en-AU - -[env:esp32dev] -platform = espressif32 -board = esp32dev -; build_flags = -D_IR_LOCALE_=en-AU - -[env:de-CH] -build_flags = -D_IR_LOCALE_=de-CH - -[env:de-DE] -build_flags = -D_IR_LOCALE_=de-DE - -[env:en-AU] -build_flags = -D_IR_LOCALE_=en-AU - -[env:en-IE] -build_flags = -D_IR_LOCALE_=en-IE - -[env:en-UK] -build_flags = -D_IR_LOCALE_=en-UK - -[env:en-US] -build_flags = -D_IR_LOCALE_=en-US - -[env:es-ES] -build_flags = -D_IR_LOCALE_=es-ES - -[env:fr-FR] -build_flags = -D_IR_LOCALE_=fr-FR diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Hitachi.h b/lib/IRremoteESP8266-2.7.4/src/ir_Hitachi.h deleted file mode 100644 index 3e5ab4ae0..000000000 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Hitachi.h +++ /dev/null @@ -1,178 +0,0 @@ -// Hitachi A/C -// -// Copyright 2018-2019 David Conran - -// Supports: -// Brand: Hitachi, Model: RAS-35THA6 remote -// Brand: Hitachi, Model: LT0541-HTA remote -// Brand: Hitachi, Model: Series VI A/C (Circa 2007) -// Brand: Hitachi, Model: RAR-8P2 remote -// Brand: Hitachi, Model: RAS-AJ25H A/C - -#ifndef IR_HITACHI_H_ -#define IR_HITACHI_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifndef UNIT_TEST -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" -#ifdef UNIT_TEST -#include "IRsend_test.h" -#endif - -// Constants -const uint16_t kHitachiAcFreq = 38000; // Hz. -const uint8_t kHitachiAcAuto = 2; -const uint8_t kHitachiAcHeat = 3; -const uint8_t kHitachiAcCool = 4; -const uint8_t kHitachiAcDry = 5; -const uint8_t kHitachiAcFan = 0xC; -const uint8_t kHitachiAcFanAuto = 1; -const uint8_t kHitachiAcFanLow = 2; -const uint8_t kHitachiAcFanMed = 3; -const uint8_t kHitachiAcFanHigh = 5; -const uint8_t kHitachiAcMinTemp = 16; // 16C -const uint8_t kHitachiAcMaxTemp = 32; // 32C -const uint8_t kHitachiAcAutoTemp = 23; // 23C -const uint8_t kHitachiAcPowerOffset = 0; -const uint8_t kHitachiAcSwingOffset = 7; - -// HitachiAc424 -// Byte[11] -const uint8_t kHitachiAc424ButtonByte = 11; -const uint8_t kHitachiAc424ButtonPowerMode = 0x13; -const uint8_t kHitachiAc424ButtonFan = 0x42; -const uint8_t kHitachiAc424ButtonTempDown = 0x43; -const uint8_t kHitachiAc424ButtonTempUp = 0x44; -const uint8_t kHitachiAc424ButtonSwingV = 0x81; - -// Byte[13] -const uint8_t kHitachiAc424TempByte = 13; -const uint8_t kHitachiAc424TempOffset = 2; -const uint8_t kHitachiAc424TempSize = 6; -const uint8_t kHitachiAc424MinTemp = 16; // 16C -const uint8_t kHitachiAc424MaxTemp = 32; // 32C -const uint8_t kHitachiAc424FanTemp = 27; // 27C - -// Byte[25] -const uint8_t kHitachiAc424ModeByte = 25; -const uint8_t kHitachiAc424Fan = 1; -const uint8_t kHitachiAc424Cool = 3; -const uint8_t kHitachiAc424Dry = 5; -const uint8_t kHitachiAc424Heat = 6; -const uint8_t kHitachiAc424FanByte = kHitachiAc424ModeByte; -const uint8_t kHitachiAc424FanMin = 1; -const uint8_t kHitachiAc424FanLow = 2; -const uint8_t kHitachiAc424FanMedium = 3; -const uint8_t kHitachiAc424FanHigh = 4; -const uint8_t kHitachiAc424FanAuto = 5; -const uint8_t kHitachiAc424FanMax = 6; -const uint8_t kHitachiAc424FanMaxDry = 2; -// Byte[27] -const uint8_t kHitachiAc424PowerByte = 27; -const uint8_t kHitachiAc424PowerOn = 0xF1; -const uint8_t kHitachiAc424PowerOff = 0xE1; - -// Classes -class IRHitachiAc { - public: - explicit IRHitachiAc(const uint16_t pin, const bool inverted = false, - const bool use_modulation = true); - - void stateReset(void); -#if SEND_HITACHI_AC - void send(const uint16_t repeat = kHitachiAcDefaultRepeat); - uint8_t calibrate(void) { return _irsend.calibrate(); } -#endif // SEND_HITACHI_AC - void begin(void); - void on(void); - void off(void); - void setPower(const bool on); - bool getPower(void); - void setTemp(const uint8_t temp); - uint8_t getTemp(void); - void setFan(const uint8_t speed); - uint8_t getFan(void); - void setMode(const uint8_t mode); - uint8_t getMode(void); - void setSwingVertical(const bool on); - bool getSwingVertical(void); - void setSwingHorizontal(const bool on); - bool getSwingHorizontal(void); - uint8_t* getRaw(void); - void setRaw(const uint8_t new_code[], - const uint16_t length = kHitachiAcStateLength); - static bool validChecksum(const uint8_t state[], - const uint16_t length = kHitachiAcStateLength); - static uint8_t calcChecksum(const uint8_t state[], - const uint16_t length = kHitachiAcStateLength); - uint8_t convertMode(const stdAc::opmode_t mode); - uint8_t convertFan(const stdAc::fanspeed_t speed); - static stdAc::opmode_t toCommonMode(const uint8_t mode); - static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); - stdAc::state_t toCommon(void); - String toString(void); -#ifndef UNIT_TEST - - private: - IRsend _irsend; -#else - IRsendTest _irsend; -#endif - // The state of the IR remote in IR code form. - uint8_t remote_state[kHitachiAcStateLength]; - void checksum(const uint16_t length = kHitachiAcStateLength); - uint8_t _previoustemp; -}; - -class IRHitachiAc424 { - public: - explicit IRHitachiAc424(const uint16_t pin, const bool inverted = false, - const bool use_modulation = true); - - void stateReset(void); -#if SEND_HITACHI_AC424 - void send(const uint16_t repeat = kHitachiAcDefaultRepeat); - uint8_t calibrate(void) { return _irsend.calibrate(); } -#endif // SEND_HITACHI_AC424 - void begin(void); - void on(void); - void off(void); - void setPower(const bool on); - bool getPower(void); - void setTemp(const uint8_t temp, bool setPrevious = true); - uint8_t getTemp(void); - void setFan(const uint8_t speed); - uint8_t getFan(void); - uint8_t getButton(void); - void setButton(const uint8_t button); - void setSwingVToggle(const bool on); - bool getSwingVToggle(void); - void setMode(const uint8_t mode); - uint8_t getMode(void); - uint8_t* getRaw(void); - void setRaw(const uint8_t new_code[], - const uint16_t length = kHitachiAc424StateLength); - uint8_t convertMode(const stdAc::opmode_t mode); - uint8_t convertFan(const stdAc::fanspeed_t speed); - static stdAc::opmode_t toCommonMode(const uint8_t mode); - static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); - stdAc::state_t toCommon(void); - String toString(void); -#ifndef UNIT_TEST - - private: - IRsend _irsend; -#else - IRsendTest _irsend; -#endif - // The state of the IR remote in IR code form. - uint8_t remote_state[kHitachiAc424StateLength]; - void setInvertedStates(void); - uint8_t _previoustemp; -}; - -#endif // IR_HITACHI_H_ diff --git a/lib/IRremoteESP8266-2.7.4/CPPLINT.cfg b/lib/IRremoteESP8266-2.7.6/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.7.4/CPPLINT.cfg rename to lib/IRremoteESP8266-2.7.6/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.7.4/LICENSE.txt b/lib/IRremoteESP8266-2.7.6/LICENSE.txt similarity index 100% rename from lib/IRremoteESP8266-2.7.4/LICENSE.txt rename to lib/IRremoteESP8266-2.7.6/LICENSE.txt diff --git a/lib/IRremoteESP8266-2.7.4/README.md b/lib/IRremoteESP8266-2.7.6/README.md similarity index 98% rename from lib/IRremoteESP8266-2.7.4/README.md rename to lib/IRremoteESP8266-2.7.6/README.md index 093b15cc1..83859d6b6 100644 --- a/lib/IRremoteESP8266-2.7.4/README.md +++ b/lib/IRremoteESP8266-2.7.6/README.md @@ -9,8 +9,8 @@ This library enables you to **send _and_ receive** infra-red signals on an [ESP8266](https://github.com/esp8266/Arduino) or an [ESP32](https://github.com/espressif/arduino-esp32) using the [Arduino framework](https://www.arduino.cc/) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* demodulators etc. -## v2.7.4 Now Available -Version 2.7.4 of the library is now [available](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. +## v2.7.6 Now Available +Version 2.7.6 of the library is now [available](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. #### Upgrading from pre-v2.0 Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. diff --git a/lib/IRremoteESP8266-2.7.4/README_fr.md b/lib/IRremoteESP8266-2.7.6/README_fr.md similarity index 98% rename from lib/IRremoteESP8266-2.7.4/README_fr.md rename to lib/IRremoteESP8266-2.7.6/README_fr.md index f95bcc945..6b6c6e7d0 100644 --- a/lib/IRremoteESP8266-2.7.4/README_fr.md +++ b/lib/IRremoteESP8266-2.7.6/README_fr.md @@ -9,8 +9,8 @@ Cette librairie vous permetra de **recevoir et d'envoyer des signaux** infrarouge sur le protocole [ESP8266](https://github.com/esp8266/Arduino) ou sur le protocole [ESP32](https://github.com/espressif/arduino-esp32) en utilisant le [Arduino framework](https://www.arduino.cc/) qui utilise la norme 940nm IR LEDs et le module basique de reception d'onde IR. Exemple : TSOP{17,22,24,36,38,44,48}* modules etc. -## v2.7.4 disponible -Version 2.7.4 de la libraire est maintenant [disponible](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Vous pouvez voir le [Release Notes](ReleaseNotes.md) pour tous les changements importants. +## v2.7.6 disponible +Version 2.7.6 de la libraire est maintenant [disponible](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Vous pouvez voir le [Release Notes](ReleaseNotes.md) pour tous les changements importants. #### mise à jour depuis pre-v2.0 L'utilisation de la librairie à un peu changer depuis la version in v2.0. Si vous voulez l'utiliser vous devrez changer votre utilisation aussi. Vous pouvez vous renseigner sur les précondition d'utilisation ici : [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. diff --git a/lib/IRremoteESP8266-2.7.4/ReleaseNotes.md b/lib/IRremoteESP8266-2.7.6/ReleaseNotes.md similarity index 95% rename from lib/IRremoteESP8266-2.7.4/ReleaseNotes.md rename to lib/IRremoteESP8266-2.7.6/ReleaseNotes.md index 4c6b1a967..69cc753cd 100644 --- a/lib/IRremoteESP8266-2.7.4/ReleaseNotes.md +++ b/lib/IRremoteESP8266-2.7.6/ReleaseNotes.md @@ -1,5 +1,35 @@ # Release Notes +## _v2.7.6 (20200425)_ + +**[Features]** +- IRMQTTServer: Use more i18n text. (#1086) +- Convert Protocol names to shared text. Saves ~3k of flash. (#1078) +- Add Chinese translation (zh-CN) & add utf-8 support. (#1080, #1085) + +**[Misc]** +- IRMQTTServer: Ensure MQTT_MAX_PACKET_SIZE is correctly set. (#1084) +- Add Italian locale to IRrecvDumpV2 platformio file. + + +## _v2.7.5 (20200409)_ + +**[Features]** +- Detailed support for `HITACHI_AC1` protocol. (#1056, #1061, #1072) +- update sharp to match Sharp AH-A5SAY (#1074) +- Experimental support for AIRWELL protocol. (#1069, #1070) +- SamsungAC: Add Breeze (Aka WindFree) control (#1062, #1071) +- Support for Daikin FFN-C A/C (#1064, #1065) +- Add basic support for HITACHI_AC3 protocol. (#1060, #1063) +- Add support for `SYMPHONY` 11 bit protocol. (#1057, #1058) +- IRMQTTServer: Improve Home-Assistant discovery by sending a 'device' with the discovery packet (#1055) + +**[Misc]** +- Clean up support status of various protocols. +- Add `decodeToState()` unit tests to all supported protocols (#1067, #1068) +- Add Gree AC example code. (#1066) + + ## _v2.7.4 (20200226)_ **[Bug Fixes]** diff --git a/lib/IRremoteESP8266-2.7.4/SupportedProtocols.md b/lib/IRremoteESP8266-2.7.6/SupportedProtocols.md similarity index 92% rename from lib/IRremoteESP8266-2.7.4/SupportedProtocols.md rename to lib/IRremoteESP8266-2.7.6/SupportedProtocols.md index 04661c3bb..ed4699de2 100644 --- a/lib/IRremoteESP8266-2.7.4/SupportedProtocols.md +++ b/lib/IRremoteESP8266-2.7.6/SupportedProtocols.md @@ -1,18 +1,20 @@ + Last generated: Thu Apr 9 15:49:53 2020 ---> # IR Protocols supported by this library | Protocol | Brand | Model | A/C Model | Detailed A/C Support | | --- | --- | --- | --- | --- | +| [Airwell](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Airwell.cpp) | **Airwell** | RC08W remote | | - | | [Aiwa](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Aiwa.cpp) | **Aiwa** | RC-T501 RCU | | - | | [Amcor](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Amcor.cpp) | **[Amcor](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Amcor.h)** | ADR-853H A/C
ADR-853H A/C
TAC-444 remote
TAC-444 remote
TAC-495 remote
TAC-495 remote | | Yes | | [Argo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Argo.cpp) | **[Argo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Argo.h)** | Ulisse 13 DCI Mobile Split A/C | | Yes | | [Carrier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Carrier.cpp) | **Carrier/Surrey** | 42QG5A55970 remote
53NGK009/012 Inverter
619EGX0090E0 A/C
619EGX0120E0 A/C
619EGX0180E0 A/C
619EGX0220E0 A/C | | - | +| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Airwell](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | RC08B remote | | Yes | | [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Beko](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | BINR 070/071 split-type A/C
BINR 070/071 split-type A/C
RG57K7(B)/BGEF Remote
RG57K7(B)/BGEF Remote | | Yes | | [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | MS12FU-10HRDN1-QRD0GW(B) A/C
MS12FU-10HRDN1-QRD0GW(B) A/C
MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
RG52D/BGE Remote
RG52D/BGE Remote | | Yes | | [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Tokio](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | AATOEMF17-12CHR1SW split-type RG51\|50/BGE Remote
AATOEMF17-12CHR1SW split-type RG51\|50/BGE Remote | | Yes | -| [Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.cpp) | **[Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.h)** | 17 Series A/C (DAIKIN128)
ARC423A5 remote
ARC433** remote
ARC433B69 remote
ARC477A1 remote
ARC480A5 remote (DAIKIN152)
BRC4C153 remote
BRC52B63 remote (DAIKIN128)
FTE12HV2S A/C
FTXB09AXVJU A/C (DAIKIN128)
FTXB12AXVJU A/C (DAIKIN128)
FTXZ25NV1B A/C
FTXZ35NV1B A/C
FTXZ50NV1B A/C | | Yes | +| [Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.cpp) | **[Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.h)** | 17 Series A/C (DAIKIN128)
ARC423A5 remote
ARC433** remote
ARC433B69 remote
ARC477A1 remote
ARC480A5 remote (DAIKIN152)
BRC4C153 remote
BRC52B63 remote (DAIKIN128)
DGS01 remote (DAIKIN64)
FFN-C/FCN-F Series A/C (DAIKIN64)
FTE12HV2S A/C
FTXB09AXVJU A/C (DAIKIN128)
FTXB12AXVJU A/C (DAIKIN128)
FTXZ25NV1B A/C
FTXZ35NV1B A/C
FTXZ50NV1B A/C | | Yes | | [Denon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Denon.cpp) | **Unknown** | | | - | | [Dish](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Dish.cpp) | **DISH NETWORK** | echostar 301 | | - | | [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[AUX](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | KFR-35GW/BpNFW=3 A/C
YKR-T/011 remote | | Yes | @@ -28,7 +30,7 @@ | [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[RusClimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | EACS/I-09HAR_X/N3 A/C
YAW1F remote | YAW1F
YBOFB | Yes | | [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Ultimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | Heat Pump | YAW1F
YBOFB | Yes | | [Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.h)** | HSU-09HMC203 A/C
HSU07-HEA03 remote
YR-W02 remote | | Yes | -| [Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | LT0541-HTA remote
RAR-8P2 remote
RAS-35THA6 remote
RAS-AJ25H A/C
Series VI A/C (Circa 2007) | | Yes | +| [Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | KAZE-312KSDP A/C (HITACHI_AC1)
LT0541-HTA remote
PC-LH3B (HITACHI_AC3)
R-LT0541-HTA/Y.K.1.1-1 V2.3 remote (HITACHI_AC1)
RAR-8P2 remote
RAS-35THA6 remote
RAS-AJ25H A/C
Series VI A/C (Circa 2007) | | Yes | | [Inax](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Inax.cpp) | **Lixil** | Inax DT-BA283 Toilet | | - | | [JVC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **Unknown** | | | - | | [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Green](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | YAPOF3 remote | | Yes | @@ -56,11 +58,12 @@ | [Pronto](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Pronto.cpp) | **Unknown** | | | - | | [RC5_RC6](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_RC5_RC6.cpp) | **Unknown** | | | - | | [RCMM](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_RCMM.cpp) | **Microsoft** | XBOX 360 | | - | -| [Samsung](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Samsung.cpp) | **[Samsung](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Samsung.h)** | AR09FSSDAWKNFA A/C
AR12HSSDBWKNEU A/C
AR12KSFPEWQNET A/C
DB63-03556X003 remote
IEC-R03 remote
UA55H6300 TV | | Yes | +| [Samsung](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Samsung.cpp) | **[Samsung](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Samsung.h)** | AR09FSSDAWKNFA A/C
AR12HSSDBWKNEU A/C
AR12KSFPEWQNET A/C
AR12NXCXAWKXEU A/C
DB63-03556X003 remote
DB93-16761C remote
IEC-R03 remote
UA55H6300 TV | | Yes | | [Sanyo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sanyo.cpp) | **Unknown** | | | - | -| [Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.cpp) | **[Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.h)** | AY-ZP40KR A/C
LC-52D62U TV | | Yes | +| [Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.cpp) | **[Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.h)** | AH-AxSAY A/C
AY-ZP40KR A/C
LC-52D62U TV | | Yes | | [Sherwood](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sherwood.cpp) | **Sherwood** | RC-138 remote
RD6505(B) Receiver | | - | | [Sony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sony.cpp) | **Sony** | HT-CT380 Soundbar (Uses 38kHz & 3 repeats) | | - | +| [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **Symphony** | Air Cooler 3Di | | - | | [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Leberg](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | LBS-TOR07 A/C | | Yes | | [Teco](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.cpp) | **[Alaska](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.h)** | SAC9010QC A/C
SAC9010QC remote | | Yes | | [Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.cpp) | **[Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.h)** | Akita EVO II
RAS 18SKP-ES
RAS-B13N3KV2
RAS-B13N3KVP-E
WC-L03SE
WH-TA04NE | | Yes | @@ -81,6 +84,7 @@ ## Send & decodable protocols: +- AIRWELL - AIWA_RC_T501 - AMCOR - ARGO @@ -93,6 +97,7 @@ - DAIKIN176 - DAIKIN2 - DAIKIN216 +- DAIKIN64 - DENON - DISH - ELECTRA_AC @@ -106,6 +111,7 @@ - HITACHI_AC - HITACHI_AC1 - HITACHI_AC2 +- HITACHI_AC3 - HITACHI_AC424 - INAX - JVC @@ -144,6 +150,7 @@ - SHARP - SHARP_AC - SONY +- SYMPHONY - TCL112AC - TECO - TOSHIBA_AC diff --git a/lib/IRremoteESP8266-2.7.4/examples/CommonAcControl/CommonAcControl.ino b/lib/IRremoteESP8266-2.7.6/examples/CommonAcControl/CommonAcControl.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/CommonAcControl/CommonAcControl.ino rename to lib/IRremoteESP8266-2.7.6/examples/CommonAcControl/CommonAcControl.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/CommonAcControl/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/CommonAcControl/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/CommonAcControl/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/CommonAcControl/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/ControlSamsungAC/ControlSamsungAC.ino b/lib/IRremoteESP8266-2.7.6/examples/ControlSamsungAC/ControlSamsungAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/ControlSamsungAC/ControlSamsungAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/ControlSamsungAC/ControlSamsungAC.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/ControlSamsungAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/ControlSamsungAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/ControlSamsungAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/ControlSamsungAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/DumbIRRepeater/DumbIRRepeater.ino b/lib/IRremoteESP8266-2.7.6/examples/DumbIRRepeater/DumbIRRepeater.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/DumbIRRepeater/DumbIRRepeater.ino rename to lib/IRremoteESP8266-2.7.6/examples/DumbIRRepeater/DumbIRRepeater.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/DumbIRRepeater/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/DumbIRRepeater/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/DumbIRRepeater/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/DumbIRRepeater/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRGCSendDemo/IRGCSendDemo.ino b/lib/IRremoteESP8266-2.7.6/examples/IRGCSendDemo/IRGCSendDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRGCSendDemo/IRGCSendDemo.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRGCSendDemo/IRGCSendDemo.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRGCSendDemo/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRGCSendDemo/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRGCSendDemo/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRGCSendDemo/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRGCTCPServer/IRGCTCPServer.ino b/lib/IRremoteESP8266-2.7.6/examples/IRGCTCPServer/IRGCTCPServer.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRGCTCPServer/IRGCTCPServer.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRGCTCPServer/IRGCTCPServer.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRGCTCPServer/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRGCTCPServer/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRGCTCPServer/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRGCTCPServer/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRMQTTServer/IRMQTTServer.h b/lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/IRMQTTServer.h similarity index 99% rename from lib/IRremoteESP8266-2.7.4/examples/IRMQTTServer/IRMQTTServer.h rename to lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/IRMQTTServer.h index b363d98b9..c886b705b 100644 --- a/lib/IRremoteESP8266-2.7.4/examples/IRMQTTServer/IRMQTTServer.h +++ b/lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/IRMQTTServer.h @@ -239,7 +239,7 @@ const uint16_t kJsonAcStateMaxSize = 1024; // Bytes // ----------------- End of User Configuration Section ------------------------- // Constants -#define _MY_VERSION_ "v1.4.6" +#define _MY_VERSION_ "v1.4.9" const uint8_t kRebootTime = 15; // Seconds const uint8_t kQuickDisplayTime = 2; // Seconds diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/IRMQTTServer.ino similarity index 94% rename from lib/IRremoteESP8266-2.7.4/examples/IRMQTTServer/IRMQTTServer.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/IRMQTTServer.ino index efcf25431..c04c1e23b 100644 --- a/lib/IRremoteESP8266-2.7.4/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/IRMQTTServer.ino @@ -358,12 +358,17 @@ #include #include #if MQTT_ENABLE +#include // -------------------------------------------------------------------- // * * * IMPORTANT * * * // You must change to have the following value. // #define MQTT_MAX_PACKET_SIZE 768 // -------------------------------------------------------------------- -#include +// Check that the user has set MQTT_MAX_PACKET_SIZE to an appropriate size. +#if MQTT_MAX_PACKET_SIZE < 768 +#error "MQTT_MAX_PACKET_SIZE in is too small. "\ + "Increase the value per comments." +#endif // MQTT_MAX_PACKET_SIZE < 768 #endif // MQTT_ENABLE #include // NOLINT(build/include) #include @@ -449,6 +454,7 @@ String MqttClimate; // Sub-topic for the climate topics. String MqttClimateCmnd; // Sub-topic for the climate command topics. #if MQTT_DISCOVERY_ENABLE String MqttDiscovery; +String MqttUniqueId; #endif // MQTT_DISCOVERY_ENABLE String MqttHAName; String MqttClientId; @@ -621,7 +627,7 @@ bool loadConfigFile(void) { String timeElapsed(uint32_t const msec) { String result = msToString(msec); - if (result.equalsIgnoreCase("Now")) + if (result.equalsIgnoreCase(D_STR_NOW)) return result; else return result + F(" ago"); @@ -641,7 +647,7 @@ String timeSince(uint32_t const start) { String gpioToString(const int16_t gpio) { if (gpio == kGpioUnused) - return F("Unused"); + return F(D_STR_UNUSED); else return String(gpio); } @@ -714,13 +720,13 @@ void handleRoot(void) { html += F( "

Send a simple IR message

" "

" - "Type: "); + D_STR_PROTOCOL ": "); html += htmlSelectAcStateProtocol(KEY_TYPE, decode_type_t::NEC, true); html += F( - " Code: 0x" - " Bit size: " - " " + D_STR_BITS ": " + "" - " Repeats: " - " " + " " D_STR_REPEAT ": " + " " "
" "

" "

Send a complex (Air Conditioner) IR message

" "

" - "Type: "); + D_STR_PROTOCOL ": "); html += htmlSelectAcStateProtocol(KEY_TYPE, decode_type_t::KELVINATOR, false); html += F( - " State code: 0x" - "" - " " + " " "
" "

" "

Send an IRremote Raw IR message

" "

" - "" - "String: (freq,array data) " + "String: (freq,array data) GlobalCache" " IR message

" "" - "" - "String: 1:1,1," + "String: 1:1,1,Pronto code IR message

" "" - "" - "String (comma separated): " + "String (comma separated): " - " Repeats: " + " " D_STR_REPEAT ": " " " "

" "
"); @@ -824,7 +830,7 @@ String addJsReloadUrl(const String url, const uint16_t timeout_s, if (notify && timeout_s) { html += F(" document.write(\"You will be redirected to the main page in "); html += String(timeout_s); - html += F(" seconds.\");\n"); + html += F(" " D_STR_SECONDS ".\");\n"); } html += F(" setTimeout('Redirect()', "); html += String(timeout_s * 1000); // Convert to mSecs @@ -847,35 +853,44 @@ void handleExamples(void) { html += htmlMenu(); html += F( "

Hardcoded examples

" - "

" - "Sherwood Amp On (GlobalCache)

" - "

" - "Sherwood Amp Off (Raw)

" - "

" + "

Sherwood Amp " D_STR_ON " (GlobalCache)

" + "

Sherwood Amp " D_STR_OFF " (Raw)

" + "

" "Sherwood Amp Input TAPE (Pronto)

" - "

TV on (Samsung)

" - "

Power Off (Sony 12bit)

" - "

" - "Panasonic A/C LKE model, On, Auto mode, Min fan, 23C" + "

TV " D_STR_ON + " (Samsung)

" + "

" D_STR_POWER + " " D_STR_OFF " (Sony 12 " D_STR_BITS ")

" + "

" + "Panasonic A/C " D_STR_MODEL " LKE, " D_STR_ON ", " D_STR_AUTO " " + D_STR_MODE ", " D_STR_MIN " " D_STR_FAN ", 23C" " (via HTTP aircon interface)

" - "

" - "Change just the temp to 27C (via HTTP aircon interface)

" - "

" - "Turn OFF the current A/C (via HTTP aircon interface)

" + "

" + "Change just the " D_STR_TEMP " to 27C " + "(via HTTP aircon interface)

" + "

" + "Turn " D_STR_OFF " the current A/C (" + "via HTTP aircon interface)

" "

"); html += htmlEnd(); server.send(200, "text/html", html); @@ -995,7 +1010,9 @@ String htmlSelectSwingh(const String name, const stdAc::swingh_t def) { String htmlHeader(const String title, const String h1_text) { String html = F(""); html += title; - html += F("

"); + html += F("" + "

"); if (h1_text.length()) html += h1_text; else @@ -1041,20 +1058,20 @@ void handleAirCon(void) { "" + "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" " -const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s°%c{e}"; -const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s%%{e}"; -const char HTTP_SNS_DEW[] PROGMEM = "{s}%s " D_DEWPOINT "{m}%s°%c{e}"; -const char HTTP_SNS_PRESSURE[] PROGMEM = "{s}%s " D_PRESSURE "{m}%s %s{e}"; -const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "{s}%s " D_PRESSUREATSEALEVEL "{m}%s %s{e}"; -const char HTTP_SNS_ANALOG[] PROGMEM = "{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; -const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}"; -const char HTTP_SNS_CO2[] PROGMEM = "{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; -const char HTTP_SNS_CO2EAVG[] PROGMEM = "{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; -const char HTTP_SNS_GALLONS[] PROGMEM = "{s}%s " D_TOTAL_USAGE "{m}%s " D_UNIT_GALLONS " {e}"; -const char HTTP_SNS_GPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_GALLONS_PER_MIN" {e}"; -const char HTTP_SNS_MOISTURE[] PROGMEM = "{s}%s " D_MOISTURE "{m}%d %%{e}"; -const char HTTP_SNS_RANGE[] PROGMEM = "{s}%s " D_RANGE "{m}%d{e}"; +const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s " D_UNIT_DEGREE "%c{e}"; +const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s " D_UNIT_PERCENT "{e}"; +const char HTTP_SNS_DEW[] PROGMEM = "{s}%s " D_DEWPOINT "{m}%s " D_UNIT_DEGREE "%c{e}"; +const char HTTP_SNS_PRESSURE[] PROGMEM = "{s}%s " D_PRESSURE "{m}%s " "%s{e}"; +const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "{s}%s " D_PRESSUREATSEALEVEL "{m}%s " "%s{e}"; +const char HTTP_SNS_ANALOG[] PROGMEM = "{s}%s " D_ANALOG_INPUT "%d{m}%d" "{e}"; +const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}"; +const char HTTP_SNS_CO2[] PROGMEM = "{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; +const char HTTP_SNS_CO2EAVG[] PROGMEM = "{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; +const char HTTP_SNS_GALLONS[] PROGMEM = "{s}%s " D_TOTAL_USAGE "{m}%s " D_UNIT_GALLONS "{e}"; +const char HTTP_SNS_GPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_GALLONS_PER_MIN "{e}"; +const char HTTP_SNS_MOISTURE[] PROGMEM = "{s}%s " D_MOISTURE "{m}%d " D_UNIT_PERCENT "{e}"; +const char HTTP_SNS_RANGE[] PROGMEM = "{s}%s " D_RANGE "{m}%d" "{e}"; +const char HTTP_SNS_VOLTAGE[] PROGMEM = "{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}"; +const char HTTP_SNS_CURRENT[] PROGMEM = "{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}"; +const char HTTP_SNS_POWER[] PROGMEM = "{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; +const char HTTP_SNS_ENERGY_TOTAL[] PROGMEM = "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; -const char HTTP_SNS_VOLTAGE[] PROGMEM = "{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}"; -const char HTTP_SNS_CURRENT[] PROGMEM = "{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}"; -const char HTTP_SNS_POWER[] PROGMEM = "{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; -const char HTTP_SNS_ENERGY_TOTAL[] PROGMEM = "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; - -const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU; -const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION; -const char S_CONFIGURE_TEMPLATE[] PROGMEM = D_CONFIGURE_TEMPLATE; -const char S_CONFIGURE_MODULE[] PROGMEM = D_CONFIGURE_MODULE; -const char S_CONFIGURE_WIFI[] PROGMEM = D_CONFIGURE_WIFI; -const char S_NO_NETWORKS_FOUND[] PROGMEM = D_NO_NETWORKS_FOUND; -const char S_CONFIGURE_LOGGING[] PROGMEM = D_CONFIGURE_LOGGING; -const char S_CONFIGURE_OTHER[] PROGMEM = D_CONFIGURE_OTHER; -const char S_SAVE_CONFIGURATION[] PROGMEM = D_SAVE_CONFIGURATION; -const char S_RESET_CONFIGURATION[] PROGMEM = D_RESET_CONFIGURATION; -const char S_RESTORE_CONFIGURATION[] PROGMEM = D_RESTORE_CONFIGURATION; -const char S_FIRMWARE_UPGRADE[] PROGMEM = D_FIRMWARE_UPGRADE; -const char S_CONSOLE[] PROGMEM = D_CONSOLE; -const char S_INFORMATION[] PROGMEM = D_INFORMATION; -const char S_RESTART[] PROGMEM = D_RESTART; +const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU; +const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION; +const char S_CONFIGURE_TEMPLATE[] PROGMEM = D_CONFIGURE_TEMPLATE; +const char S_CONFIGURE_MODULE[] PROGMEM = D_CONFIGURE_MODULE; +const char S_CONFIGURE_WIFI[] PROGMEM = D_CONFIGURE_WIFI; +const char S_NO_NETWORKS_FOUND[] PROGMEM = D_NO_NETWORKS_FOUND; +const char S_CONFIGURE_LOGGING[] PROGMEM = D_CONFIGURE_LOGGING; +const char S_CONFIGURE_OTHER[] PROGMEM = D_CONFIGURE_OTHER; +const char S_SAVE_CONFIGURATION[] PROGMEM = D_SAVE_CONFIGURATION; +const char S_RESET_CONFIGURATION[] PROGMEM = D_RESET_CONFIGURATION; +const char S_RESTORE_CONFIGURATION[] PROGMEM = D_RESTORE_CONFIGURATION; +const char S_FIRMWARE_UPGRADE[] PROGMEM = D_FIRMWARE_UPGRADE; +const char S_CONSOLE[] PROGMEM = D_CONSOLE; +const char S_INFORMATION[] PROGMEM = D_INFORMATION; +const char S_RESTART[] PROGMEM = D_RESTART; #endif // USE_WEBSERVER const uint32_t MARKER_START = 0x5AA55AA5; diff --git a/tasmota/language/bg-BG.h b/tasmota/language/bg_BG.h similarity index 88% rename from tasmota/language/bg-BG.h rename to tasmota/language/bg_BG.h index f7760f302..03ae5bd6c 100644 --- a/tasmota/language/bg-BG.h +++ b/tasmota/language/bg_BG.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v7.1.2.4 + * Updated until v8.2.0.6 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -98,6 +98,8 @@ #define D_FILE "Файл" #define D_FLOW_RATE "Дебит" #define D_FREE_MEMORY "Свободна памет" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Честота" #define D_GAS "Газ" #define D_GATEWAY "Шлюз" @@ -115,7 +117,7 @@ #define D_LIGHT "Светлина" #define D_LWT "LWT" #define D_MODULE "Модул" -#define D_MOISTURE "Moisture" +#define D_MOISTURE "Влага" #define D_MQTT "MQTT" #define D_MULTI_PRESS "неколкократно натискане" #define D_NOISE "Шум" @@ -138,7 +140,7 @@ #define D_PROGRAM_SIZE "Размер на програмата" #define D_PROJECT "Проект" #define D_RAIN "Дъжд" -#define D_RANGE "Range" +#define D_RANGE "Обхват" #define D_RECEIVED "Получено" #define D_RESTART "Рестарт" #define D_RESTARTING "Рестартиране" @@ -210,7 +212,7 @@ #define D_IN_MODE "в режим" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Грешка при свързването, не е получен IP адрес" #define D_CONNECT_FAILED_AP_NOT_REACHED "Грешка при свързването, точката за достъп е недостижима" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Грешка при свързването, грешна парола към точката за достъп" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Грешка при свързването" #define D_CONNECT_FAILED_AP_TIMEOUT "Грешка при свързването, превишено време за изчакване" #define D_ATTEMPTING_CONNECTION "Опитва свързване..." #define D_CHECKING_CONNECTION "Проверка на свързването..." @@ -357,7 +359,7 @@ #define D_UPLOAD_ERR_11 "Грешка при изтриване на RF чипа" #define D_UPLOAD_ERR_12 "Грешка при записване в RF чипа" #define D_UPLOAD_ERR_13 "Грешка при декодиране на RF фърмуера" -#define D_UPLOAD_ERR_14 "Not compatible" +#define D_UPLOAD_ERR_14 "Несъвместим" #define D_UPLOAD_ERROR_CODE "Код на грешка при зареждането" #define D_ENTER_COMMAND "Въвеждане на команда" @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Частици" +// xsns_27_apds9960.ino +#define D_GESTURE "Жест" +#define D_COLOR_RED "Червен" +#define D_COLOR_GREEN "Зелен" +#define D_COLOR_BLUE "Син" +#define D_CCT "CCT" +#define D_PROXIMITY "Близост" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Ускорение - ос X" #define D_AY_AXIS "Ускорение - ос Y" @@ -506,7 +516,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Посока на вятъра" #define D_TX20_WIND_SPEED "Скорост на вятъра" -#define D_TX20_WIND_SPEED_MIN "Мини. скорост на вятъра" +#define D_TX20_WIND_SPEED_MIN "Мин. скорост на вятъра" #define D_TX20_WIND_SPEED_MAX "Макс. скорост на вятъра" #define D_TX20_NORTH "С" #define D_TX20_EAST "И" @@ -514,24 +524,24 @@ #define D_TX20_WEST "З" // xsns_53_sml.ino -#define D_TPWRIN "Energy Total-In" -#define D_TPWROUT "Energy Total-Out" -#define D_TPWRCURR "Active Power-In/Out" -#define D_TPWRCURR1 "Active Power-In p1" -#define D_TPWRCURR2 "Active Power-In p2" -#define D_TPWRCURR3 "Active Power-In p3" -#define D_Strom_L1 "Current L1" -#define D_Strom_L2 "Current L2" -#define D_Strom_L3 "Current L3" -#define D_Spannung_L1 "Voltage L1" -#define D_Spannung_L2 "Voltage L2" -#define D_Spannung_L3 "Voltage L3" -#define D_METERNR "Meter_number" -#define D_METERSID "Service ID" -#define D_GasIN "Counter" -#define D_H2oIN "Counter" -#define D_StL1L2L3 "Current L1+L2+L3" -#define D_SpL1L2L3 "Voltage L1+L2+L3/3" +#define D_TPWRIN "Общо енергия - IN" +#define D_TPWROUT "Общо енергия - OUT" +#define D_TPWRCURR "Активна мощност - In/Out" +#define D_TPWRCURR1 "Активна мощност - In p1" +#define D_TPWRCURR2 "Активна мощност - In p2" +#define D_TPWRCURR3 "Активна мощност - In p3" +#define D_Strom_L1 "Ток L1" +#define D_Strom_L2 "Ток L2" +#define D_Strom_L3 "Ток L3" +#define D_Spannung_L1 "Напрежение L1" +#define D_Spannung_L2 "Напрежение L2" +#define D_Spannung_L3 "Напрежение L3" +#define D_METERNR "Номер_електромер" +#define D_METERSID "ID на услугата" +#define D_GasIN "Брояч" +#define D_H2oIN "Брояч" +#define D_StL1L2L3 "Ток L1+L2+L3" +#define D_SpL1L2L3 "Напрежение L1+L2+L3/3" // tasmota_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Няма" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Подсветка" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -627,8 +638,8 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" -#define D_SENSOR_BUZZER "Buzzer" -#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_BUZZER "Зумер" +#define D_SENSOR_OLED_RESET "Нулиране OLED" #define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" #define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" @@ -664,16 +675,35 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "gal/min" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOMETER_PER_HOUR "km/h" #define D_UNIT_KILOOHM "kΩ" #define D_UNIT_KILOWATTHOUR "kWh" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "s" #define D_UNIT_SECTORS "сектори" @@ -708,7 +739,7 @@ #define D_TOTAL_REACTIVE "Общо реактивна мощност" #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "°" -#define D_TOTAL_ACTIVE "Total Active" +#define D_TOTAL_ACTIVE "Общо активна мощност" //SOLAXX1 #define D_PV1_VOLTAGE "Напрежение на PV1" @@ -735,16 +766,43 @@ #define D_SOLAX_ERROR_8 "Грешка - друго оборудване" //xdrv_10_scripter.ino -#define D_CONFIGURE_SCRIPT "Edit script" -#define D_SCRIPT "edit script" -#define D_SDCARD_UPLOAD "file upload" -#define D_SDCARD_DIR "sd card directory" -#define D_UPL_DONE "Done" -#define D_SCRIPT_CHARS_LEFT "chars left" -#define D_SCRIPT_CHARS_NO_MORE "no more chars" -#define D_SCRIPT_DOWNLOAD "Download" -#define D_SCRIPT_ENABLE "script enable" -#define D_SCRIPT_UPLOAD "Upload" -#define D_SCRIPT_UPLOAD_FILES "Upload files" +#define D_CONFIGURE_SCRIPT "Редакция на скрипт" +#define D_SCRIPT "редактирай скрипт" +#define D_SDCARD_UPLOAD "изпрати файл" +#define D_SDCARD_DIR "директория на SD картата" +#define D_UPL_DONE "Готово" +#define D_SCRIPT_CHARS_LEFT "оставащи символи" +#define D_SCRIPT_CHARS_NO_MORE "няма повече символи" +#define D_SCRIPT_DOWNLOAD "Изтегляне" +#define D_SCRIPT_ENABLE "активирай скрипт" +#define D_SCRIPT_UPLOAD "Изпращане" +#define D_SCRIPT_UPLOAD_FILES "Изпращане на файлове" + +//xsns_67_as3935.ino +#define D_AS3935_GAIN "усилване:" +#define D_AS3935_ENERGY "енергия:" +#define D_AS3935_DISTANCE "разстояние:" +#define D_AS3935_DISTURBER "смущение:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "прибл.:" +#define D_AS3935_AWAY "далече" +#define D_AS3935_LIGHT "осветление" +#define D_AS3935_OUT "осветление извън обхват" +#define D_AS3935_NOT "неопределено разстояние" +#define D_AS3935_ABOVE "околно осветление" +#define D_AS3935_NOISE "открит шум" +#define D_AS3935_DISTDET "открито смущение" +#define D_AS3935_INTNOEV "Прекъсване без Събитие!" +#define D_AS3935_NOMESS "слушане..." +#define D_AS3935_ON "Вкл." +#define D_AS3935_OFF "Изкл." +#define D_AS3935_INDOORS "На закрито" +#define D_AS3935_OUTDOORS "На открито" +#define D_AS3935_CAL_FAIL "калибрирането е неуспешно" +#define D_AS3935_CAL_OK "калибрирането е зададено на:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" #endif // _LANGUAGE_BG_BG_H_ diff --git a/tasmota/language/cs-CZ.h b/tasmota/language/cs_CZ.h similarity index 93% rename from tasmota/language/cs-CZ.h rename to tasmota/language/cs_CZ.h index 72bb6c275..71a907a81 100644 --- a/tasmota/language/cs-CZ.h +++ b/tasmota/language/cs_CZ.h @@ -98,6 +98,8 @@ #define D_FILE "Soubor" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Volná paměť" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Kmitočet" #define D_GAS "Plyn" #define D_GATEWAY "Výchozí brána" @@ -210,7 +212,7 @@ #define D_IN_MODE "v módu" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Chyba připojení, nebyla obdržena IP adresa" #define D_CONNECT_FAILED_AP_NOT_REACHED "Chyba připojení, nedostupný AP" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Chyba připojení, nesprávné heslo pro AP" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Chyba připojení" #define D_CONNECT_FAILED_AP_TIMEOUT "Chyba připojení, uplynul AP timeout" #define D_ATTEMPTING_CONNECTION "Připojování..." #define D_CHECKING_CONNECTION "Zkouška spojení..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "částic" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesto" +#define D_COLOR_RED "Červená" +#define D_COLOR_GREEN "Zelená" +#define D_COLOR_BLUE "Modrá" +#define D_CCT "CCT" +#define D_PROXIMITY "Blízkost" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. osa-X" #define D_AY_AXIS "Accel. osa-Y" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,16 +675,35 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "hod" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" #define D_UNIT_KILOWATTHOUR "kWh" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sec" #define D_UNIT_SECTORS "sektory" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_CS_CZ_H_ diff --git a/tasmota/language/de-DE.h b/tasmota/language/de_DE.h similarity index 93% rename from tasmota/language/de-DE.h rename to tasmota/language/de_DE.h index 12cb30769..fdd1ec18d 100644 --- a/tasmota/language/de-DE.h +++ b/tasmota/language/de_DE.h @@ -98,6 +98,8 @@ #define D_FILE "Datei" #define D_FLOW_RATE "Durchflussmenge" #define D_FREE_MEMORY "Freier Arbeitsspeicher" +#define D_PSR_MAX_MEMORY "PS-RAM Speicher" +#define D_PSR_FREE_MEMORY "PS-RAM freier Speicher" #define D_FREQUENCY "Frequenz" #define D_GAS "Gas" #define D_GATEWAY "Gateway" @@ -210,7 +212,7 @@ #define D_IN_MODE "in Modus" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Verbindung fehlgeschlagen, da keine IP-Adresse zugeteilt wurde" #define D_CONNECT_FAILED_AP_NOT_REACHED "Verbindung fehlgeschlagen, da AP nicht erreicht werden konnte" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Verbindung fehlgeschlagen, da das Passwort falsch ist" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Verbindung fehlgeschlagen" #define D_CONNECT_FAILED_AP_TIMEOUT "Verbindung fehlgeschlagen, da der AP nicht antwortet (timeout)" #define D_ATTEMPTING_CONNECTION "Verbindungsversuch..." #define D_CHECKING_CONNECTION "Prüfe Verbindung..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Partikel" +// xsns_27_apds9960.ino +#define D_GESTURE "Geste" +#define D_COLOR_RED "Rot" +#define D_COLOR_GREEN "Grün" +#define D_COLOR_BLUE "Blau" +#define D_CCT "CCT" +#define D_PROXIMITY "Nähe" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Beschl. X-Achse" #define D_AY_AXIS "Beschl. Y-Achse" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "s" #define D_UNIT_SECTORS "Sektoren" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload Dateien" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "Rauschpegel:" +#define D_AS3935_ENERGY "Energie:" +#define D_AS3935_DISTANCE "Entfernung:" +#define D_AS3935_DISTURBER "Störsingal:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "ca.:" +#define D_AS3935_AWAY "entfernt" +#define D_AS3935_LIGHT "Blitz" +#define D_AS3935_OUT "ausserhalb der Reichweite" +#define D_AS3935_NOT "Entfernung nicht ermittelbar" +#define D_AS3935_ABOVE "Blitz überhalb" +#define D_AS3935_NOISE "Rauschen entdeckt" +#define D_AS3935_DISTDET "Störer entdeckt" +#define D_AS3935_INTNOEV "Interrupt ohne Grund!" +#define D_AS3935_NOMESS "lausche..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "Kalibrierung fehlerhaft" +#define D_AS3935_CAL_OK "Cap gesetzt auf:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_DE_DE_H_ diff --git a/tasmota/language/el-GR.h b/tasmota/language/el_GR.h similarity index 93% rename from tasmota/language/el-GR.h rename to tasmota/language/el_GR.h index 8a8160956..ddd1d5b0b 100644 --- a/tasmota/language/el-GR.h +++ b/tasmota/language/el_GR.h @@ -98,6 +98,8 @@ #define D_FILE "Αρχείο" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Ελεύθερη μνήμη" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Συχνότητα" #define D_GAS "Αέριο" #define D_GATEWAY "Πύλη" @@ -210,7 +212,7 @@ #define D_IN_MODE "σε mode" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Αποτυχία σύνδεσης, δεν απονεμήθηκε διεύθυνση IP" #define D_CONNECT_FAILED_AP_NOT_REACHED "Αποτυχία σύνδεσης, δεν ανταποκρίνεται το AP" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Αποτυχία σύνδεσης, λάθος κωδικός για το AP" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Αποτυχία σύνδεσης" #define D_CONNECT_FAILED_AP_TIMEOUT "Αποτυχία σύνδεσης, λήξη ορίου απόκρισης από το AP" #define D_ATTEMPTING_CONNECTION "Προσπάθεια για σύνδεση..." #define D_CHECKING_CONNECTION "Έλεγχος σύνδεσης..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Particals" +// xsns_27_apds9960.ino +#define D_GESTURE "Χειρονομία" +#define D_COLOR_RED "Κόκκινο" +#define D_COLOR_GREEN "Πράσινο" +#define D_COLOR_BLUE "Μπλε" +#define D_CCT "CCT" +#define D_PROXIMITY "Εγγύτητα" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sec" #define D_UNIT_SECTORS "sectors" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_EL_GR_H_ diff --git a/tasmota/language/en-GB.h b/tasmota/language/en_GB.h similarity index 92% rename from tasmota/language/en-GB.h rename to tasmota/language/en_GB.h index 09b43c1be..0396ca3e6 100644 --- a/tasmota/language/en-GB.h +++ b/tasmota/language/en_GB.h @@ -98,6 +98,8 @@ #define D_FILE "File" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Free Memory" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frequency" #define D_GAS "Gas" #define D_GATEWAY "Gateway" @@ -210,7 +212,7 @@ #define D_IN_MODE "in mode" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Connect failed as no IP address received" #define D_CONNECT_FAILED_AP_NOT_REACHED "Connect failed as AP cannot be reached" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Connect failed with AP incorrect password" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Connect failed" #define D_CONNECT_FAILED_AP_TIMEOUT "Connect failed with AP timeout" #define D_ATTEMPTING_CONNECTION "Attempting connection..." #define D_CHECKING_CONNECTION "Checking connection..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Particles" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -651,9 +662,9 @@ #define D_SENSOR_SM2135_DAT "SM2135 Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" #define D_SENSOR_EXS_ENABLE "EXS Enable" -#define D_SENSOR_SLAVE_TX "Slave TX" -#define D_SENSOR_SLAVE_RX "Slave RX" -#define D_SENSOR_SLAVE_RESET "Slave RST" +#define D_SENSOR_SLAVE_TX "Slave TX" +#define D_SENSOR_SLAVE_RX "Slave RX" +#define D_SENSOR_SLAVE_RESET "Slave RST" #define D_SENSOR_GPS_RX "GPS RX" #define D_SENSOR_GPS_TX "GPS TX" #define D_SENSOR_HM10_RX "HM10 RX" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sec" #define D_UNIT_SECTORS "sectors" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_EN_GB_H_ diff --git a/tasmota/language/es-ES.h b/tasmota/language/es_ES.h similarity index 93% rename from tasmota/language/es-ES.h rename to tasmota/language/es_ES.h index f70940a38..0602c3634 100644 --- a/tasmota/language/es-ES.h +++ b/tasmota/language/es_ES.h @@ -98,6 +98,8 @@ #define D_FILE "Archivo" #define D_FLOW_RATE "Caudal" #define D_FREE_MEMORY "Memoria Libre" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frecuencia" #define D_GAS "Gas" #define D_GATEWAY "Gateway" @@ -210,7 +212,7 @@ #define D_IN_MODE "en modo" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Falló Conexión, Dirección IP no recibida" #define D_CONNECT_FAILED_AP_NOT_REACHED "Falló Conexión, AP no pudo ser contactado" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Falló Conexión, clave de AP incorrecta" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Falló Conexión" #define D_CONNECT_FAILED_AP_TIMEOUT "Falló Conexión, timeout de AP" #define D_ATTEMPTING_CONNECTION "Intentando conectar..." #define D_CHECKING_CONNECTION "Probando conexión..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Partículas" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesto" +#define D_COLOR_RED "Rojo" +#define D_COLOR_GREEN "Verde" +#define D_COLOR_BLUE "Azul" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximidad" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "seg" #define D_UNIT_SECTORS "sectores" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Cargar" #define D_SCRIPT_UPLOAD_FILES "Cargar Archivos" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_ES_ES_H_ diff --git a/tasmota/language/fr-FR.h b/tasmota/language/fr_FR.h similarity index 92% rename from tasmota/language/fr-FR.h rename to tasmota/language/fr_FR.h index 488386aea..c620572ca 100644 --- a/tasmota/language/fr-FR.h +++ b/tasmota/language/fr_FR.h @@ -79,7 +79,7 @@ #define D_DATA "Donnée" #define D_DARKLIGHT "Sombre" #define D_DEBUG "Debug" -#define D_DEWPOINT "Dew point" +#define D_DEWPOINT "Point de rosée" #define D_DISABLED "Désactivé" #define D_DISTANCE "Distance" #define D_DNS_SERVER "Serveur DNS" @@ -98,6 +98,8 @@ #define D_FILE "Fichier" #define D_FLOW_RATE "Débit" #define D_FREE_MEMORY "Mémoire libre" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Fréquence" #define D_GAS "Gaz" #define D_GATEWAY "Passerelle" @@ -138,7 +140,7 @@ #define D_PROGRAM_SIZE "Taille programme" #define D_PROJECT "Projet" #define D_RAIN "Pluie" -#define D_RANGE "Range" +#define D_RANGE "Intervalle" #define D_RECEIVED "Reçu" #define D_RESTART "Redémarrage" #define D_RESTARTING "Redémarre" @@ -189,8 +191,8 @@ // tasmota.ino #define D_WARNING_MINIMAL_VERSION "ATTENTION Cette version ne supporte pas les réglages persistants" -#define D_LEVEL_10 "level 1-0" -#define D_LEVEL_01 "level 0-1" +#define D_LEVEL_10 "niveau 1-0" +#define D_LEVEL_01 "niveau 0-1" #define D_SERIAL_LOGGING_DISABLED "Journalisation série désactivée" #define D_SYSLOG_LOGGING_REENABLED "Jounalisation SysLog réactivée" @@ -210,7 +212,7 @@ #define D_IN_MODE "en mode" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Échec de connexion car aucune adresse IP n'a été reçue" #define D_CONNECT_FAILED_AP_NOT_REACHED "Échec de connexion car l'AP ne peut-être contacté" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Échec de connexion car le mot de passe de l'AP est incorrect" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Échec de connexion" #define D_CONNECT_FAILED_AP_TIMEOUT "Échec de connexion avec l'AP, expiré" #define D_ATTEMPTING_CONNECTION "Tentative de connexion..." #define D_CHECKING_CONNECTION "Vérification connexion..." @@ -402,7 +404,7 @@ #define D_DOMOTICZ_TEMP_HUM "Temp,Hum" #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro" #define D_DOMOTICZ_POWER_ENERGY "Puissance,Énergie" - #define D_DOMOTICZ_ILLUMINANCE "Illuminance" + #define D_DOMOTICZ_ILLUMINANCE "Éclairement" #define D_DOMOTICZ_COUNT "Compteur/PM1" #define D_DOMOTICZ_VOLTAGE "Tension/PM2,5" #define D_DOMOTICZ_CURRENT "Courant/PM10" @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Particules" +// xsns_27_apds9960.ino +#define D_GESTURE "Geste" +#define D_COLOR_RED "Rouge" +#define D_COLOR_GREEN "Vert" +#define D_COLOR_BLUE "Bleu" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximité" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accél. Axe-X" #define D_AY_AXIS "Accél. Axe-Y" @@ -503,7 +513,7 @@ #define D_CALIBRATE "Étalonner" #define D_CALIBRATION "Étalonnage" -//xsns_35_TX20.ino +// xsns_35_TX20.ino #define D_TX20_WIND_DIRECTION "Direction du vent" #define D_TX20_WIND_SPEED "Vitesse du vent" #define D_TX20_WIND_SPEED_MIN "Vitesse Min" @@ -528,8 +538,8 @@ #define D_Spannung_L3 "Voltage L3" #define D_METERNR "Meter_number" #define D_METERSID "Service ID" -#define D_GasIN "Counter" -#define D_H2oIN "Counter" +#define D_GasIN "Compteur" +#define D_H2oIN "Compteur" #define D_StL1L2L3 "Current L1+L2+L3" #define D_SpL1L2L3 "Voltage L1+L2+L3/3" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "RétroÉcl" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 RX" #define D_SENSOR_SDS0X1_TX "SDS0X1 TX" #define D_SENSOR_HPMA_RX "HPMA RX" @@ -654,8 +665,8 @@ #define D_SENSOR_SLAVE_TX "Esclave TX" #define D_SENSOR_SLAVE_RX "Esclave RX" #define D_SENSOR_SLAVE_RESET "Esclave Rst" -#define D_SENSOR_GPS_TX "GPS TX" #define D_SENSOR_GPS_RX "GPS RX" +#define D_SENSOR_GPS_TX "GPS TX" #define D_SENSOR_HM10_RX "HM10 RX" #define D_SENSOR_HM10_TX "HM10 TX" #define D_SENSOR_LE01MR_RX "LE-01MR Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "gal/mn" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -685,10 +715,11 @@ #define D_UNIT_MILLIMETER "mm" #define D_UNIT_MILLIMETER_MERCURY "mmHg" #define D_UNIT_MILLISECOND "ms" -#define D_UNIT_MINUTE "mn" +#define D_UNIT_MINUTE "min" // https://fr.wikipedia.org/wiki/Minute_(temps)#Symbole%20et%20d%C3%A9finition #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "s" #define D_UNIT_SECTORS "secteurs" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_FR_FR_H_ diff --git a/tasmota/language/he-HE.h b/tasmota/language/he_HE.h similarity index 93% rename from tasmota/language/he-HE.h rename to tasmota/language/he_HE.h index 70d474e8f..4802b529e 100644 --- a/tasmota/language/he-HE.h +++ b/tasmota/language/he_HE.h @@ -98,6 +98,8 @@ #define D_FILE "קובץ" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "זכרון פנוי" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "תדר" #define D_GAS "גז" #define D_GATEWAY "שער" @@ -210,7 +212,7 @@ #define D_IN_MODE "במצב" #define D_CONNECT_FAILED_NO_IP_ADDRESS "IP החיבור נכשל מכיוון שלא התקבלה כתובת" #define D_CONNECT_FAILED_AP_NOT_REACHED "זמין AP החיבור נכשל כיוון שאין" -#define D_CONNECT_FAILED_WRONG_PASSWORD "סיסמא שגויה , AP חיבור נכשל ל" +#define D_CONNECT_FAILED_WRONG_PASSWORD "AP חיבור נכשל ל" #define D_CONNECT_FAILED_AP_TIMEOUT "פג זמן המתנה , AP חיבור נכשל ל" #define D_ATTEMPTING_CONNECTION "...מנסה להתחבר" #define D_CHECKING_CONNECTION "...בודק חיבור" @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "חלקיקים" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sec" #define D_UNIT_SECTORS "sectors" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_HE_HE_H_ diff --git a/tasmota/language/hu-HU.h b/tasmota/language/hu_HU.h similarity index 93% rename from tasmota/language/hu-HU.h rename to tasmota/language/hu_HU.h index cc47f6a5d..ee45ae083 100644 --- a/tasmota/language/hu-HU.h +++ b/tasmota/language/hu_HU.h @@ -98,6 +98,8 @@ #define D_FILE "Fájl" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Szabad memória" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frekvencia" #define D_GAS "Gáz" #define D_GATEWAY "Átjáró" @@ -210,7 +212,7 @@ #define D_IN_MODE "mód:" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Sikertelen csatlakozás, nincs kiosztott IP cím" #define D_CONNECT_FAILED_AP_NOT_REACHED "Sikertelen csatlakozás, AP nem elérhető" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Sikertelen csatlakozás, hibás AP jelszó" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Sikertelen csatlakozás" #define D_CONNECT_FAILED_AP_TIMEOUT "Sikertelen csatlakozás AP időtúllépés miatt" #define D_ATTEMPTING_CONNECTION "Csatlakozás..." #define D_CHECKING_CONNECTION "Kapcsolat ellenőrzése..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Részecskék" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesztus" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "közelség" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Gyorsulásm. X-tengely" #define D_AY_AXIS "Gyorsulásm. Y-tengely" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Háttérfény" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "s" #define D_UNIT_SECTORS "szektorok" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_HU_HU_H_ diff --git a/tasmota/language/it-IT.h b/tasmota/language/it-IT.h deleted file mode 100644 index d9a4b9a6b..000000000 --- a/tasmota/language/it-IT.h +++ /dev/null @@ -1,750 +0,0 @@ -/* - it-IT.h - localization for Italian - Italy for Tasmota - - Copyright (C) 2020 Gennaro Tortone - some mods by Antonio Fragola - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef _LANGUAGE_IT_IT_H_ -#define _LANGUAGE_IT_IT_H_ - -/*************************** ATTENTION *******************************\ - * - * Due to memory constraints only UTF-8 is supported. - * To save code space keep text as short as possible. - * Time and Date provided by SDK can not be localized (yet). - * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. - * Use online command Prefix to translate cmnd, stat and tele. - * - * Updated until v6.0.0a -\*********************************************************************/ - -#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) - -#define LANGUAGE_LCID 1040 -// HTML (ISO 639-1) Language Code -#define D_HTML_LANGUAGE "it" - -// "2017-03-07T11:08:02" - ISO8601:2004 -#define D_YEAR_MONTH_SEPARATOR "-" -#define D_MONTH_DAY_SEPARATOR "-" -#define D_DATE_TIME_SEPARATOR "T" -#define D_HOUR_MINUTE_SEPARATOR ":" -#define D_MINUTE_SECOND_SEPARATOR ":" - -#define D_DAY3LIST "DomLunMarMerGioVenSab" -#define D_MONTH3LIST "GenFebMarAprMagGiuLugAgoSetOttNovDic" - -// Non JSON decimal separator -#define D_DECIMAL_SEPARATOR "." - -// Common -#define D_ADMIN "Admin" -#define D_AIR_QUALITY "Qualità dell'aria" -#define D_AP "AP" // Access Point -#define D_AS "come" -#define D_AUTO "AUTO" -#define D_BLINK "Blink" -#define D_BLINKOFF "BlinkOff" -#define D_BOOT_COUNT "Numero di boot" -#define D_BRIGHTLIGHT "Luminoso" -#define D_BSSID "BSSId" -#define D_BUTTON "Pulsante" -#define D_BY "da" // Written by me -#define D_BYTES "Bytes" -#define D_CELSIUS "Celsius" -#define D_CHANNEL "Canale" -#define D_CO2 "CO2" -#define D_CODE "codice" // Button code -#define D_COLDLIGHT "Fredda" -#define D_COMMAND "Comando" -#define D_CONNECTED "Connesso" -#define D_CORS_DOMAIN "CORS Domain" -#define D_COUNT "Conteggio" -#define D_COUNTER "Contatore" -#define D_CT_POWER "CT Power" -#define D_CURRENT "Corrente" // As in Voltage and Current -#define D_DATA "Dati" -#define D_DARKLIGHT "Scuro" -#define D_DEBUG "Debug" -#define D_DEWPOINT "Dew point" -#define D_DISABLED "Disabilitato" -#define D_DISTANCE "Distanza" -#define D_DNS_SERVER "DNS Server" -#define D_DONE "Fatto" -#define D_DST_TIME "DST" -#define D_ECO2 "eCO₂" -#define D_EMULATION "Emulazione" -#define D_ENABLED "Abilitato" -#define D_ERASE "Cancellare" -#define D_ERROR "Errore" -#define D_FAHRENHEIT "Fahrenheit" -#define D_FAILED "Fallito" -#define D_FALLBACK "Riserva" -#define D_FALLBACK_TOPIC "Topic Riserva" -#define D_FALSE "Falso" -#define D_FILE "File" -#define D_FLOW_RATE "Flow rate" -#define D_FREE_MEMORY "Memoria Libera" -#define D_FREQUENCY "Frequenza" -#define D_GAS "Gas" -#define D_GATEWAY "Gateway" -#define D_GROUP "Gruppo" -#define D_HOST "Host" -#define D_HOSTNAME "Nome Host" -#define D_HUMIDITY "Umidità" -#define D_ILLUMINANCE "Illuminazione" -#define D_IMMEDIATE "immediato" // Button immediate -#define D_INDEX "Indice" -#define D_INFO "Info" -#define D_INFRARED "Infrarosso" -#define D_INITIALIZED "Inizializzato" -#define D_IP_ADDRESS "Indirizzo IP" -#define D_LIGHT "Luce" -#define D_LWT "LWT" -#define D_MODULE "Modulo" -#define D_MOISTURE "Umidità" -#define D_MQTT "MQTT" -#define D_MULTI_PRESS "multi-pressione" -#define D_NOISE "Rumore" -#define D_NONE "Nessuno" -#define D_OFF "Off" -#define D_OFFLINE "Offline" -#define D_OK "Ok" -#define D_ON "On" -#define D_ONLINE "Online" -#define D_PASSWORD "Password" -#define D_PORT "Porta" -#define D_POWER_FACTOR "Fattore di potenza" -#define D_POWERUSAGE "Potenza" -#define D_POWERUSAGE_ACTIVE "Potenza Attiva" -#define D_POWERUSAGE_APPARENT "Potenza Apparente" -#define D_POWERUSAGE_REACTIVE "Potenza Reattiva" -#define D_PRESSURE "Pressione" -#define D_PRESSUREATSEALEVEL "Pressione al livello del mare" -#define D_PROGRAM_FLASH_SIZE "Dimensione Flash Programma" -#define D_PROGRAM_SIZE "Dimensione Programma" -#define D_PROJECT "Progetto" -#define D_RAIN "Rain" -#define D_RANGE "Range" -#define D_RECEIVED "Ricevuto" -#define D_RESTART "Riavvio" -#define D_RESTARTING "Riavviando" -#define D_RESTART_REASON "Causa Riavvio" -#define D_RESTORE "ripristino" -#define D_RETAINED "salvato" -#define D_RULE "Rule" -#define D_SAVE "Salva" -#define D_SENSOR "Sensore" -#define D_SSID "SSId" -#define D_START "Start" -#define D_STD_TIME "STD" -#define D_STOP "Stop" -#define D_SUBNET_MASK "Maschera Subnet" -#define D_SUBSCRIBE_TO "Sottoscrivi a" -#define D_UNSUBSCRIBE_FROM "Cancella da" -#define D_SUCCESSFUL "Riuscito" -#define D_SUNRISE "Alba" -#define D_SUNSET "Tramonto" -#define D_TEMPERATURE "Temperatura" -#define D_TO "a" -#define D_TOGGLE "Toggle" -#define D_TOPIC "Topic" -#define D_TOTAL_USAGE "Total Usage" -#define D_TRANSMIT "Trasmesso" -#define D_TRUE "Vero" -#define D_TVOC "TVOC" -#define D_UPGRADE "Aggiornamento" -#define D_UPLOAD "Invio" -#define D_UPTIME "Uptime" -#define D_USER "Utente" -#define D_UTC_TIME "UTC" -#define D_UV_INDEX "Indice UV" -#define D_UV_INDEX_1 "Basso" -#define D_UV_INDEX_2 "Medio" -#define D_UV_INDEX_3 "Alto" -#define D_UV_INDEX_4 "Pericolo" -#define D_UV_INDEX_5 "BurnL1/2" -#define D_UV_INDEX_6 "BurnL3" -#define D_UV_INDEX_7 "OoR" -#define D_UV_LEVEL "Livello UV" -#define D_UV_POWER "Intensità UV" -#define D_VERSION "Versione" -#define D_VOLTAGE "Tensione" -#define D_WEIGHT "Peso" -#define D_WARMLIGHT "Calda" -#define D_WEB_SERVER "Web Server" - -// tasmota.ino -#define D_WARNING_MINIMAL_VERSION "ATTENZIONE Questa versione non supporta il salvataggio delle impostazioni" -#define D_LEVEL_10 "level 1-0" -#define D_LEVEL_01 "level 0-1" -#define D_SERIAL_LOGGING_DISABLED "Log Seriale disabilitato" -#define D_SYSLOG_LOGGING_REENABLED "Syslog ri-abilitato" - -#define D_SET_BAUDRATE_TO "Baudrate impostato a" -#define D_RECEIVED_TOPIC "Topic Ricevuto" -#define D_DATA_SIZE "Dimensione Dati" -#define D_ANALOG_INPUT "Ingresso Analogico" - -// support.ino -#define D_OSWATCH "osWatch" -#define D_BLOCKED_LOOP "Ciclo Bloccato" -#define D_WPS_FAILED_WITH_STATUS "WPSconfig Fallito con stato" -#define D_ACTIVE_FOR_3_MINUTES "Attivo per 3 minuti" -#define D_FAILED_TO_START "Partenza fallita" -#define D_PATCH_ISSUE_2186 "Patch issue 2186" -#define D_CONNECTING_TO_AP "Connessione ad AP" -#define D_IN_MODE "In modalità" -#define D_CONNECT_FAILED_NO_IP_ADDRESS "Connessione fallita, indirizzo IP non ricevuto" -#define D_CONNECT_FAILED_AP_NOT_REACHED "Connessione fallita, AP non raggiungibile" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Connessione fallita, password AP non corretta" -#define D_CONNECT_FAILED_AP_TIMEOUT "Connessione fallita, timeout AP" -#define D_ATTEMPTING_CONNECTION "Tentativo di connessione..." -#define D_CHECKING_CONNECTION "Controllo connessione..." -#define D_QUERY_DONE "Query eseguita. Servizio MQTT trovato" -#define D_MQTT_SERVICE_FOUND "Servizio MQTT trovato su" -#define D_FOUND_AT "trovato a" -#define D_SYSLOG_HOST_NOT_FOUND "Syslog Host non trovato" - -// settings.ino -#define D_SAVED_TO_FLASH_AT "Salvato nella flash in" -#define D_LOADED_FROM_FLASH_AT "Caricato dalla flash da" -#define D_USE_DEFAULTS "Utilizzo valori default" -#define D_ERASED_SECTOR "Settore cancellato" - -// xdrv_02_webserver.ino -#define D_NOSCRIPT "Abilitare JavaScript per utilizzare Tasmota" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
effettuare aggiornamento" -#define D_WEBSERVER_ACTIVE_ON "Web server attivo su" -#define D_WITH_IP_ADDRESS "con indirizzo IP" -#define D_WEBSERVER_STOPPED "Web server arrestato" -#define D_FILE_NOT_FOUND "File Non Trovato" -#define D_REDIRECTED "Redirezione al captive portal" -#define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Impostazione Wifimanager come AccessPoint e Station" -#define D_WIFIMANAGER_SET_ACCESSPOINT "Impostazione Wifimanager come AccessPoint" -#define D_TRYING_TO_CONNECT "Tentativo di connessione del dispositivo alla rete" - -#define D_RESTART_IN "Riavvio in" -#define D_SECONDS "secondi" -#define D_DEVICE_WILL_RESTART "Il dispositivo verrà riavviato tra pochi secondi" -#define D_BUTTON_TOGGLE "On/Off" -#define D_CONFIGURATION "Configurazione" -#define D_INFORMATION "Informazioni" -#define D_FIRMWARE_UPGRADE "Aggiornamento Firmware" -#define D_CONSOLE "Console" -#define D_CONFIRM_RESTART "Conferma Riavvio" - -#define D_CONFIGURE_MODULE "Configurazione Modulo" -#define D_CONFIGURE_WIFI "Configurazione WiFi" -#define D_CONFIGURE_MQTT "Configurazione MQTT" -#define D_CONFIGURE_DOMOTICZ "Configurazione Domoticz" -#define D_CONFIGURE_LOGGING "Configurazione Logging" -#define D_CONFIGURE_OTHER "Configurazione Extra" -#define D_CONFIRM_RESET_CONFIGURATION "Conferma Reset Configurazione" -#define D_RESET_CONFIGURATION "Reset Configurazione" -#define D_BACKUP_CONFIGURATION "Backup Configurazione" -#define D_RESTORE_CONFIGURATION "Ripristino Configurazione" -#define D_MAIN_MENU "Menu Principale" - -#define D_MODULE_PARAMETERS "Parametri del modulo" -#define D_MODULE_TYPE "Tipo modulo" -#define D_PULLUP_ENABLE "No Button/Switch pull-up" -#define D_ADC "ADC" -#define D_GPIO "GPIO" -#define D_SERIAL_IN "Seriale In" -#define D_SERIAL_OUT "Seriale Out" - -#define D_WIFI_PARAMETERS "Parametri Wifi" -#define D_SCAN_FOR_WIFI_NETWORKS "Scansione delle reti wifi" -#define D_SCAN_DONE "Scansione effettuata" -#define D_NO_NETWORKS_FOUND "Nessuna rete trovata" -#define D_REFRESH_TO_SCAN_AGAIN "Ricarica per nuova scansione" -#define D_DUPLICATE_ACCESSPOINT "AccessPoint duplicato" -#define D_SKIPPING_LOW_QUALITY "Ignorato a causa di bassa qualità" -#define D_RSSI "RSSI" -#define D_WEP "WEP" -#define D_WPA_PSK "WPA PSK" -#define D_WPA2_PSK "WPA2 PSK" -#define D_AP1_SSID "AP1 SSId" -#define D_AP1_PASSWORD "AP1 Password" -#define D_AP2_SSID "AP2 SSId" -#define D_AP2_PASSWORD "AP2 Password" - -#define D_MQTT_PARAMETERS "Parametri MQTT" -#define D_CLIENT "Client" -#define D_FULL_TOPIC "Full Topic" - -#define D_LOGGING_PARAMETERS "Parametri Logging" -#define D_SERIAL_LOG_LEVEL "Livello di log Seriale" -#define D_MQTT_LOG_LEVEL "Mqtt log level" -#define D_WEB_LOG_LEVEL "livello di log Web" -#define D_SYS_LOG_LEVEL "livello di log Sys" -#define D_MORE_DEBUG "Debug aggiuntivo" -#define D_SYSLOG_HOST "Syslog host" -#define D_SYSLOG_PORT "Syslog porta" -#define D_TELEMETRY_PERIOD "Periodo Telemetria" - -#define D_OTHER_PARAMETERS "Altri parametri" -#define D_TEMPLATE "Modello" -#define D_ACTIVATE "Attivare" -#define D_WEB_ADMIN_PASSWORD "Password Amministratore Web" -#define D_MQTT_ENABLE "Abilita MQTT" -#define D_FRIENDLY_NAME "Nome amichevole" -#define D_BELKIN_WEMO "Belkin WeMo" -#define D_HUE_BRIDGE "Hue Bridge" -#define D_SINGLE_DEVICE "dispositivo singolo" -#define D_MULTI_DEVICE "dispositivo multiplo" - -#define D_CONFIGURE_TEMPLATE "Configurare Modello" -#define D_TEMPLATE_PARAMETERS "Parametri Modello" -#define D_TEMPLATE_NAME "Nome" -#define D_BASE_TYPE "Basato nel" -#define D_TEMPLATE_FLAGS "Opzioni" - -#define D_SAVE_CONFIGURATION "Salva configurazione" -#define D_CONFIGURATION_SAVED "Configurazione salvata" -#define D_CONFIGURATION_RESET "Configurazione azzerata" - -#define D_PROGRAM_VERSION "Versione del programma" -#define D_BUILD_DATE_AND_TIME "Data e ora compilazione" -#define D_CORE_AND_SDK_VERSION "Versione Core/SDK" -#define D_FLASH_WRITE_COUNT "Contatore scrittura Flash" -#define D_MAC_ADDRESS "Indirizzo MAC" -#define D_MQTT_HOST "MQTT Host" -#define D_MQTT_PORT "MQTT Porta" -#define D_MQTT_CLIENT "MQTT Client" -#define D_MQTT_USER "MQTT Utente" -#define D_MQTT_TOPIC "MQTT Topic" -#define D_MQTT_GROUP_TOPIC "MQTT Group Topic" -#define D_MQTT_FULL_TOPIC "MQTT Full Topic" -#define D_MDNS_DISCOVERY "mDNS Discovery" -#define D_MDNS_ADVERTISE "mDNS Advertise" -#define D_ESP_CHIP_ID "ESP Chip Id" -#define D_FLASH_CHIP_ID "Flash Chip Id" -#define D_FLASH_CHIP_SIZE "Dimensione Flash" -#define D_FREE_PROGRAM_SPACE "Spazio Libero Memoria Programma" - -#define D_UPGRADE_BY_WEBSERVER "Aggiornamento da web server" -#define D_OTA_URL "OTA Url" -#define D_START_UPGRADE "Avvio aggiornamento" -#define D_UPGRADE_BY_FILE_UPLOAD "Aggiornamento tramite invio file" -#define D_UPLOAD_STARTED "Invio iniziato" -#define D_UPGRADE_STARTED "Aggiornamento avviato" -#define D_UPLOAD_DONE "Invio effettuato" -#define D_UPLOAD_ERR_1 "Nessun file selezionato" -#define D_UPLOAD_ERR_2 "Spazio insufficiente" -#define D_UPLOAD_ERR_3 "Magic byte non corrispondente a 0xE9" -#define D_UPLOAD_ERR_4 "Dimensione della memoria programma maggiore della dimensione reale della flash" -#define D_UPLOAD_ERR_5 "Errore di comparazione del buffer di invio" -#define D_UPLOAD_ERR_6 "Invio fallito. Abilitare logging 3" -#define D_UPLOAD_ERR_7 "Invio annullato" -#define D_UPLOAD_ERR_8 "File non valido" -#define D_UPLOAD_ERR_9 "File troppo grande" -#define D_UPLOAD_ERR_10 "Inizializzazione fallita del chip RF" -#define D_UPLOAD_ERR_11 "Cancellazione fallita del chip RF" -#define D_UPLOAD_ERR_12 "Scrittura fallita del chip RF" -#define D_UPLOAD_ERR_13 "Decodifica fallita del firmware RF" -#define D_UPLOAD_ERR_14 "Not compatible" -#define D_UPLOAD_ERROR_CODE "Codice errore invio" - -#define D_ENTER_COMMAND "Inserire comando" -#define D_ENABLE_WEBLOG_FOR_RESPONSE "Abilitare weblog 2 se è attesa una risposta" -#define D_NEED_USER_AND_PASSWORD "Richiesto user=&password=" - -// xdrv_01_mqtt.ino -#define D_FINGERPRINT "Verifica TLS fingerprint..." -#define D_TLS_CONNECT_FAILED_TO "Connessione TLS fallita a" -#define D_RETRY_IN "Nuovo tentativo in" -#define D_VERIFIED "Verificato Fingerprint" -#define D_INSECURE "Connessione insicura a causa di Fingerprint non valido" -#define D_CONNECT_FAILED_TO "Connessione Fallita a" - -// xplg_wemohue.ino -#define D_MULTICAST_DISABLED "Multicast disabilitato" -#define D_MULTICAST_REJOINED "Multicast (ri)associato" -#define D_MULTICAST_JOIN_FAILED "Associazione Multicast fallita" -#define D_FAILED_TO_SEND_RESPONSE "Invio risposta fallito" - -#define D_WEMO "WeMo" -#define D_WEMO_BASIC_EVENT "WeMo evento base" -#define D_WEMO_EVENT_SERVICE "WeMo servizio eventi" -#define D_WEMO_META_SERVICE "WeMo meta service" -#define D_WEMO_SETUP "Impostazione WeMo" -#define D_RESPONSE_SENT "Risposta inviata" - -#define D_HUE "Hue" -#define D_HUE_BRIDGE_SETUP "Impostazione Hue" -#define D_HUE_API_NOT_IMPLEMENTED "Hue API non implementata" -#define D_HUE_API "Hue API" -#define D_HUE_POST_ARGS "Hue POST argomenti" -#define D_3_RESPONSE_PACKETS_SENT "3 pacchetti di risposta inviati" - -// xdrv_07_domoticz.ino -#define D_DOMOTICZ_PARAMETERS "Parametri Domoticz" -#define D_DOMOTICZ_IDX "Idx" -#define D_DOMOTICZ_KEY_IDX "Key idx" -#define D_DOMOTICZ_SWITCH_IDX "Switch idx" -#define D_DOMOTICZ_SENSOR_IDX "Sensor idx" - #define D_DOMOTICZ_TEMP "Temp" - #define D_DOMOTICZ_TEMP_HUM "Temp,Hum" - #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro" - #define D_DOMOTICZ_POWER_ENERGY "Power,Energy" - #define D_DOMOTICZ_ILLUMINANCE "Illuminance" - #define D_DOMOTICZ_COUNT "Count/PM1" - #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" - #define D_DOMOTICZ_CURRENT "Current/PM10" - #define D_DOMOTICZ_AIRQUALITY "AirQuality" - #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" -#define D_DOMOTICZ_UPDATE_TIMER "Intervallo di aggiornamento" - -// xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Configura Timer" -#define D_TIMER_PARAMETERS "Parametri Timer" -#define D_TIMER_ENABLE "Abilita Timer" -#define D_TIMER_ARM "Attiva" -#define D_TIMER_TIME "Ora" -#define D_TIMER_DAYS "Giorni" -#define D_TIMER_REPEAT "Ripeti" -#define D_TIMER_OUTPUT "Output" -#define D_TIMER_ACTION "Azione" - -// xdrv_10_knx.ino -#define D_CONFIGURE_KNX "Configura KNX" -#define D_KNX_PARAMETERS "Parametri KNX" -#define D_KNX_GENERAL_CONFIG "Generale" -#define D_KNX_PHYSICAL_ADDRESS "Indirizzo Fisico" -#define D_KNX_PHYSICAL_ADDRESS_NOTE "( Deve essere univoco nella rete KNX )" -#define D_KNX_ENABLE "Abilita KNX" -#define D_KNX_GROUP_ADDRESS_TO_WRITE "Dati da Inviare al Gruppo di Indirizzi" -#define D_ADD "Aggiungi" -#define D_DELETE "Elimina" -#define D_REPLY "Rispondi" -#define D_KNX_GROUP_ADDRESS_TO_READ "Gruppo di Indirizzi da cui Ricevere Dati" -#define D_RECEIVED_FROM "Ricevuto Da" -#define D_KNX_COMMAND_WRITE "Scrivi" -#define D_KNX_COMMAND_READ "Leggi" -#define D_KNX_COMMAND_OTHER "Altro" -#define D_SENT_TO "invia a" -#define D_KNX_WARNING "L'indirizzo del gruppo ( 0 / 0 / 0 ) è riservato e non può essere usato." -#define D_KNX_ENHANCEMENT "Miglioramento Comunicazione" -#define D_KNX_TX_SLOT "KNX TX" -#define D_KNX_RX_SLOT "KNX RX" - -// xdrv_03_energy.ino -#define D_ENERGY_TODAY "Energia Oggi" -#define D_ENERGY_YESTERDAY "Energia Ieri" -#define D_ENERGY_TOTAL "Energia Totale" - -// xdrv_27_shutter.ino -#define D_OPEN "Aperta" -#define D_CLOSE "Chiusa" -#define D_DOMOTICZ_SHUTTER "Serranda" - -// xdrv_28_pcf8574.ino -#define D_CONFIGURE_PCF8574 "Configura PCF8574" -#define D_PCF8574_PARAMETERS "Parametri PCF8574" -#define D_INVERT_PORTS "Porte Invertite" -#define D_DEVICE "Dispositivo" -#define D_DEVICE_INPUT "Input" -#define D_DEVICE_OUTPUT "Output" - -// xsns_05_ds18b20.ino -#define D_SENSOR_BUSY "Sensore occupato" -#define D_SENSOR_CRC_ERROR "Sensore errore CRC" -#define D_SENSORS_FOUND "Sensori trovati" - -// xsns_06_dht.ino -#define D_TIMEOUT_WAITING_FOR "Attesa timeout per" -#define D_START_SIGNAL_LOW "inizio segnale basso" -#define D_START_SIGNAL_HIGH "inizio segnale alto" -#define D_PULSE "impulso" -#define D_CHECKSUM_FAILURE "Checksum fallito" - -// xsns_07_sht1x.ino -#define D_SENSOR_DID_NOT_ACK_COMMAND "Sensore non ha eseguito il comando ACK" -#define D_SHT1X_FOUND "SHT1X trovato" - -// xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter -#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter -#define D_PARTICALS_BEYOND "Particelle" - -// xsns_32_mpu6050.ino -#define D_AX_AXIS "Accel. Asse-X" -#define D_AY_AXIS "Accel. Asse-Y" -#define D_AZ_AXIS "Accel. Asse-Z" -#define D_GX_AXIS "Gyro Asse-X" -#define D_GY_AXIS "Gyro Asse-Y" -#define D_GZ_AXIS "Gyro Asse-Z" - -// xsns_34_hx711.ino -#define D_HX_CAL_REMOVE "Rimuovere peso" -#define D_HX_CAL_REFERENCE "Caricare peso di riferimento" -#define D_HX_CAL_DONE "Calibrato" -#define D_HX_CAL_FAIL "Calibrazione fallita" -#define D_RESET_HX711 "Reset Scala" -#define D_CONFIGURE_HX711 "Configura Scala" -#define D_HX711_PARAMETERS "Parametri Scala" -#define D_ITEM_WEIGHT "Peso oggetto" -#define D_REFERENCE_WEIGHT "Peso di riferimento" -#define D_CALIBRATE "Calibrato" -#define D_CALIBRATION "Calibrazione" - -//xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Direzione Vento" -#define D_TX20_WIND_SPEED "Velocità Vento" -#define D_TX20_WIND_SPEED_MIN "Velocità Minima Vento" -#define D_TX20_WIND_SPEED_MAX "Velocità Massima Vento" -#define D_TX20_NORTH "N" -#define D_TX20_EAST "E" -#define D_TX20_SOUTH "S" -#define D_TX20_WEST "O" - -// xsns_53_sml.ino -#define D_TPWRIN "Energy Total-In" -#define D_TPWROUT "Energy Total-Out" -#define D_TPWRCURR "Active Power-In/Out" -#define D_TPWRCURR1 "Active Power-In p1" -#define D_TPWRCURR2 "Active Power-In p2" -#define D_TPWRCURR3 "Active Power-In p3" -#define D_Strom_L1 "Current L1" -#define D_Strom_L2 "Current L2" -#define D_Strom_L3 "Current L3" -#define D_Spannung_L1 "Voltage L1" -#define D_Spannung_L2 "Voltage L2" -#define D_Spannung_L3 "Voltage L3" -#define D_METERNR "Meter_number" -#define D_METERSID "Service ID" -#define D_GasIN "Counter" -#define D_H2oIN "Counter" -#define D_StL1L2L3 "Current L1+L2+L3" -#define D_SpL1L2L3 "Voltage L1+L2+L3/3" - -// tasmota_template.h - keep them as short as possible to be able to fit them in GUI drop down box -#define D_SENSOR_NONE "Nessuno" -#define D_SENSOR_USER "User" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Switch" // Suffix "1" -#define D_SENSOR_BUTTON "Button" // Suffix "1" -#define D_SENSOR_RELAY "Relay" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Counter" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_SPI_MISO "SPI MISO" -#define D_SENSOR_SPI_MOSI "SPI MOSI" -#define D_SENSOR_SPI_CLK "SPI CLK" -#define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_HPMA_RX "HPMA Rx" -#define D_SENSOR_HPMA_TX "HPMA Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri/TX" -#define D_SENSOR_SR04_ECHO "SR04 Ech/RX" -#define D_SENSOR_SDM120_TX "SDMx20 Tx" -#define D_SENSOR_SDM120_RX "SDMx20 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX2X_TX "TX2x" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" -#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" -#define D_SENSOR_MAX31855_CS "MX31855 CS" -#define D_SENSOR_MAX31855_CLK "MX31855 CLK" -#define D_SENSOR_MAX31855_DO "MX31855 DO" -#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" -#define D_SENSOR_NRG_CF1 "HLWBL CF1" -#define D_SENSOR_HLW_CF "HLW8012 CF" -#define D_SENSOR_HJL_CF "BL0937 CF" -#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" -#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" -#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" -#define D_SENSOR_CSE7766_TX "CSE7766 Tx" -#define D_SENSOR_CSE7766_RX "CSE7766 Rx" -#define D_SENSOR_PN532_TX "PN532 Tx" -#define D_SENSOR_PN532_RX "PN532 Rx" -#define D_SENSOR_SM16716_CLK "SM16716 CLK" -#define D_SENSOR_SM16716_DAT "SM16716 DAT" -#define D_SENSOR_SM16716_POWER "SM16716 PWR" -#define D_SENSOR_MY92X1_DI "MY92x1 DI" -#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" -#define D_SENSOR_ARIRFRCV "ALux IrRcv" -#define D_SENSOR_ARIRFSEL "ALux IrSel" -#define D_SENSOR_TXD "Serial Tx" -#define D_SENSOR_RXD "Serial Rx" -#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" -#define D_SENSOR_HRE_CLOCK "HRE Clock" -#define D_SENSOR_HRE_DATA "HRE Data" -#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" -#define D_SENSOR_BUZZER "Buzzer" -#define D_SENSOR_OLED_RESET "OLED Reset" -#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" -#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" -#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" -#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" -#define D_SENSOR_IBEACON_TX "iBeacon TX" -#define D_SENSOR_IBEACON_RX "iBeacon RX" -#define D_SENSOR_RDM6300_RX "RDM6300 RX" -#define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" -#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" -#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" -#define D_SENSOR_DDSU666_TX "DDSU666 Tx" -#define D_SENSOR_DDSU666_RX "DDSU666 Rx" -#define D_SENSOR_SM2135_CLK "SM2135 Clk" -#define D_SENSOR_SM2135_DAT "SM2135 Dat" -#define D_SENSOR_DEEPSLEEP "DeepSleep" -#define D_SENSOR_EXS_ENABLE "EXS Enable" -#define D_SENSOR_SLAVE_TX "Slave TX" -#define D_SENSOR_SLAVE_RX "Slave RX" -#define D_SENSOR_SLAVE_RESET "Slave RST" -#define D_SENSOR_GPS_RX "GPS RX" -#define D_SENSOR_GPS_TX "GPS TX" -#define D_SENSOR_HM10_RX "HM10 RX" -#define D_SENSOR_HM10_TX "HM10 TX" -#define D_SENSOR_LE01MR_RX "LE-01MR Rx" -#define D_SENSOR_LE01MR_TX "LE-01MR Tx" -#define D_SENSOR_CC1101_GDO0 "CC1101 GDO0" -#define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" -#define D_SENSOR_HRXL_RX "HRXL Rx" -#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" - -// Units -#define D_UNIT_AMPERE "A" -#define D_UNIT_CENTIMETER "cm" -#define D_UNIT_HERTZ "Hz" -#define D_UNIT_HOUR "h" -#define D_UNIT_GALLONS "gal" -#define D_UNIT_GALLONS_PER_MIN "g/m" -#define D_UNIT_INCREMENTS "inc" -#define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" -#define D_UNIT_KILOOHM "kΩ" -#define D_UNIT_KILOWATTHOUR "kWh" -#define D_UNIT_LUX "lx" -#define D_UNIT_MICROGRAM_PER_CUBIC_METER "µg/m³" -#define D_UNIT_MICROMETER "µm" -#define D_UNIT_MICROSECOND "µs" -#define D_UNIT_MILLIAMPERE "mA" -#define D_UNIT_MILLIMETER "mm" -#define D_UNIT_MILLIMETER_MERCURY "mmHg" -#define D_UNIT_MILLISECOND "ms" -#define D_UNIT_MINUTE "Min" -#define D_UNIT_PARTS_PER_BILLION "ppb" -#define D_UNIT_PARTS_PER_DECILITER "ppd" -#define D_UNIT_PARTS_PER_MILLION "ppm" -#define D_UNIT_PRESSURE "hPa" -#define D_UNIT_SECOND "sec" -#define D_UNIT_SECTORS "settori" -#define D_UNIT_VA "VA" -#define D_UNIT_VAR "VAr" -#define D_UNIT_VOLT "V" -#define D_UNIT_WATT "W" -#define D_UNIT_WATTHOUR "Wh" -#define D_UNIT_WATT_METER_QUADRAT "W/m²" - -//SDM220, SDM120, LE01MR -#define D_PHASE_ANGLE "Angolo Fase" -#define D_IMPORT_ACTIVE "Potenza Attiva Importata" -#define D_EXPORT_ACTIVE "Potenza Attiva Esportata" -#define D_IMPORT_REACTIVE "Potenza Reattiva Importata" -#define D_EXPORT_REACTIVE "Potenza Reattiva Esportata" -#define D_TOTAL_REACTIVE "Potenza Reattiva Totale" -#define D_UNIT_KWARH "kVArh" -#define D_UNIT_ANGLE "°" -#define D_TOTAL_ACTIVE "Total Active" - -//SOLAXX1 -#define D_PV1_VOLTAGE "PV1 Voltaggio" -#define D_PV1_CURRENT "PV1 Corrente" -#define D_PV1_POWER "PV1 Eergia" -#define D_PV2_VOLTAGE "PV2 Voltaggio" -#define D_PV2_CURRENT "PV2 Corrente" -#define D_PV2_POWER "PV2 Energia" -#define D_SOLAR_POWER "Energia Solar" -#define D_INVERTER_POWER "Energia Inverter" -#define D_STATUS "Stato" -#define D_WAITING "In attesa" -#define D_CHECKING "Controllando" -#define D_WORKING "Lavorando" -#define D_FAILURE "Errore" -#define D_SOLAX_ERROR_0 "No Codice Errore" -#define D_SOLAX_ERROR_1 "Errore Grid Persa" -#define D_SOLAX_ERROR_2 "Errore Voltaggio Grid" -#define D_SOLAX_ERROR_3 "Errore Frequenza Grid" -#define D_SOLAX_ERROR_4 "Errore Voltaggio Pv" -#define D_SOLAX_ERROR_5 "Errore Isolamento" -#define D_SOLAX_ERROR_6 "Errore Temperatura Eccessiva" -#define D_SOLAX_ERROR_7 "Errore Ventilatore" -#define D_SOLAX_ERROR_8 "Altro Errore del Dispositivo" - -//xdrv_10_scripter.ino -#define D_CONFIGURE_SCRIPT "Edit script" -#define D_SCRIPT "edit script" -#define D_SDCARD_UPLOAD "file upload" -#define D_SDCARD_DIR "sd card directory" -#define D_UPL_DONE "Done" -#define D_SCRIPT_CHARS_LEFT "chars left" -#define D_SCRIPT_CHARS_NO_MORE "no more chars" -#define D_SCRIPT_DOWNLOAD "Download" -#define D_SCRIPT_ENABLE "script enable" -#define D_SCRIPT_UPLOAD "Upload" -#define D_SCRIPT_UPLOAD_FILES "Upload files" - -#endif // _LANGUAGE_IT_IT_H_ diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h new file mode 100644 index 000000000..88acefc92 --- /dev/null +++ b/tasmota/language/it_IT.h @@ -0,0 +1,808 @@ +/* + it-IT.h - localization for Italian - Italy for Tasmota + + Copyright (C) 2020 Gennaro Tortone - some mods by Antonio Fragola + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _LANGUAGE_IT_IT_H_ +#define _LANGUAGE_IT_IT_H_ + +/*************************** ATTENTION *******************************\ + * + * Due to memory constraints only UTF-8 is supported. + * To save code space keep text as short as possible. + * Time and Date provided by SDK can not be localized (yet). + * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. + * Use online command Prefix to translate cmnd, stat and tele. + * + * Updated until v6.0.0a +\*********************************************************************/ + +#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) + +#define LANGUAGE_LCID 1040 +// HTML (ISO 639-1) Language Code +#define D_HTML_LANGUAGE "it" + +// "2017-03-07T11:08:02" - ISO8601:2004 +#define D_YEAR_MONTH_SEPARATOR "-" +#define D_MONTH_DAY_SEPARATOR "-" +#define D_DATE_TIME_SEPARATOR "T" +#define D_HOUR_MINUTE_SEPARATOR ":" +#define D_MINUTE_SECOND_SEPARATOR ":" + +#define D_DAY3LIST "DomLunMarMerGioVenSab" +#define D_MONTH3LIST "GenFebMarAprMagGiuLugAgoSetOttNovDic" + +// Non JSON decimal separator +#define D_DECIMAL_SEPARATOR "." + +// Common +#define D_ADMIN "Admin" +#define D_AIR_QUALITY "Qualità dell'aria" +#define D_AP "AP" // Access Point +#define D_AS "come" +#define D_AUTO "AUTO" +#define D_BLINK "Lampeggia" +#define D_BLINKOFF "Lampeggia OFF" +#define D_BOOT_COUNT "Numero di boot" +#define D_BRIGHTLIGHT "Luminoso" +#define D_BSSID "BSSId" +#define D_BUTTON "Pulsante" +#define D_BY "di" // Written by me +#define D_BYTES "Byte" +#define D_CELSIUS "Celsius" +#define D_CHANNEL "Canale" +#define D_CO2 "CO2" +#define D_CODE "codice" // Button code +#define D_COLDLIGHT "Fredda" +#define D_COMMAND "Comando" +#define D_CONNECTED "Connesso" +#define D_CORS_DOMAIN "Dominio CORS" +#define D_COUNT "Conteggio" +#define D_COUNTER "Contatore" +#define D_CT_POWER "Alimentazione CT" +#define D_CURRENT "Corrente" // As in Voltage and Current +#define D_DATA "Dati" +#define D_DARKLIGHT "Scuro" +#define D_DEBUG "Debug" +#define D_DEWPOINT "Punto rugiada" // +#define D_DISABLED "Disabilitato" +#define D_DISTANCE "Distanza" +#define D_DNS_SERVER "Server DNS" +#define D_DONE "Completato" +#define D_DST_TIME "DST" +#define D_ECO2 "eCO₂" +#define D_EMULATION "Emulazione" +#define D_ENABLED "Abilitato" +#define D_ERASE "Cancella" +#define D_ERROR "Errore" +#define D_FAHRENHEIT "Fahrenheit" +#define D_FAILED "Fallito" +#define D_FALLBACK "Riserva" +#define D_FALLBACK_TOPIC "Topic Riserva" +#define D_FALSE "Falso" +#define D_FILE "File" +#define D_FLOW_RATE "Flusso dati" +#define D_FREE_MEMORY "Memoria Libera" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" +#define D_FREQUENCY "Frequenza" +#define D_GAS "Gas" +#define D_GATEWAY "Gateway" +#define D_GROUP "Gruppo" +#define D_HOST "Host" +#define D_HOSTNAME "Nome host" +#define D_HUMIDITY "Umidità" +#define D_ILLUMINANCE "Illuminazione" +#define D_IMMEDIATE "immediato" // Button immediate +#define D_INDEX "Indice" +#define D_INFO "Info" +#define D_INFRARED "Infrarosso" +#define D_INITIALIZED "Inizializzato" +#define D_IP_ADDRESS "Indirizzo IP" +#define D_LIGHT "Luce" +#define D_LWT "LWT" +#define D_MODULE "Modulo" +#define D_MOISTURE "Umidità" +#define D_MQTT "MQTT" +#define D_MULTI_PRESS "multi-pressione" +#define D_NOISE "Rumore" +#define D_NONE "Nessuno" +#define D_OFF "OFF" +#define D_OFFLINE "Offline" +#define D_OK "OK" +#define D_ON "ON" +#define D_ONLINE "Online" +#define D_PASSWORD "Password" +#define D_PORT "Porta" +#define D_POWER_FACTOR "Fattore di potenza" +#define D_POWERUSAGE "Potenza" +#define D_POWERUSAGE_ACTIVE "Potenza attiva" +#define D_POWERUSAGE_APPARENT "Potenza apparente" +#define D_POWERUSAGE_REACTIVE "Potenza reattiva" +#define D_PRESSURE "Pressione" +#define D_PRESSUREATSEALEVEL "Pressione al livello del mare" +#define D_PROGRAM_FLASH_SIZE "Dimensione flash programma" +#define D_PROGRAM_SIZE "Dimensione programma" +#define D_PROJECT "Progetto" +#define D_RAIN "Pioggia" +#define D_RANGE "Intervallo" +#define D_RECEIVED "Ricevuto" +#define D_RESTART "Riavvio" +#define D_RESTARTING "Riavvio" +#define D_RESTART_REASON "Causa riavvio" +#define D_RESTORE "ripristino" +#define D_RETAINED "salvato" +#define D_RULE "Regola" +#define D_SAVE "Salva" +#define D_SENSOR "Sensore" +#define D_SSID "SSID" +#define D_START "Avvia" +#define D_STD_TIME "STD" +#define D_STOP "Stop" +#define D_SUBNET_MASK "Maschera sottorete" +#define D_SUBSCRIBE_TO "Abbonati a" +#define D_UNSUBSCRIBE_FROM "Rimuovi abbonamento da" +#define D_SUCCESSFUL "Completato" +#define D_SUNRISE "Alba" +#define D_SUNSET "Tramonto" +#define D_TEMPERATURE "Temperatura" +#define D_TO "a" +#define D_TOGGLE "ON/OFF" +#define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Uso totale" +#define D_TRANSMIT "Trasmesso" +#define D_TRUE "Vero" +#define D_TVOC "TVOC" +#define D_UPGRADE "Aggiorna" +#define D_UPLOAD "Invio" +#define D_UPTIME "Tempo accensione" +#define D_USER "Utente" +#define D_UTC_TIME "UTC" +#define D_UV_INDEX "Indice UV" +#define D_UV_INDEX_1 "Basso" +#define D_UV_INDEX_2 "Medio" +#define D_UV_INDEX_3 "Alto" +#define D_UV_INDEX_4 "Pericolo" +#define D_UV_INDEX_5 "BurnL1/2" +#define D_UV_INDEX_6 "BurnL3" +#define D_UV_INDEX_7 "OoR" // Out of Range +#define D_UV_LEVEL "Livello UV" +#define D_UV_POWER "Intensità UV" +#define D_VERSION "Versione" +#define D_VOLTAGE "Tensione" +#define D_WEIGHT "Peso" +#define D_WARMLIGHT "Calda" +#define D_WEB_SERVER "Server web" + +// tasmota.ino +#define D_WARNING_MINIMAL_VERSION "ATTENZIONE Questa versione non supporta il salvataggio delle impostazioni" +#define D_LEVEL_10 "livello 1-0" +#define D_LEVEL_01 "livello 0-1" +#define D_SERIAL_LOGGING_DISABLED "Registro attività seriale disabilitato" +#define D_SYSLOG_LOGGING_REENABLED "Syslog ri-abilitato" + +#define D_SET_BAUDRATE_TO "Imposta baudrate" +#define D_RECEIVED_TOPIC "Topic Ricevuto" +#define D_DATA_SIZE "Dimensione dati" +#define D_ANALOG_INPUT "Ingresso analogico" + +// support.ino +#define D_OSWATCH "osWatch" +#define D_BLOCKED_LOOP "Ciclo bloccato" +#define D_WPS_FAILED_WITH_STATUS "WPSconfig fallito con stato" +#define D_ACTIVE_FOR_3_MINUTES "Attivo per 3 minuti" +#define D_FAILED_TO_START "Avvio fallito" +#define D_PATCH_ISSUE_2186 "Patch problema 2186" +#define D_CONNECTING_TO_AP "Connessione ad AP" +#define D_IN_MODE "In modalità" +#define D_CONNECT_FAILED_NO_IP_ADDRESS "Connessione fallita - indirizzo IP non ricevuto" +#define D_CONNECT_FAILED_AP_NOT_REACHED "Connessione fallita - AP non raggiungibile" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Connessione fallita" +#define D_CONNECT_FAILED_AP_TIMEOUT "Connessione fallita - timeout AP" +#define D_ATTEMPTING_CONNECTION "Tentativo di connessione..." +#define D_CHECKING_CONNECTION "Controllo connessione..." +#define D_QUERY_DONE "Query eseguita. Servizio MQTT trovato" +#define D_MQTT_SERVICE_FOUND "Servizio MQTT trovato in" +#define D_FOUND_AT "trovato in" +#define D_SYSLOG_HOST_NOT_FOUND "Host Syslog non trovato" + +// settings.ino +#define D_SAVED_TO_FLASH_AT "Salvato nella flash in" +#define D_LOADED_FROM_FLASH_AT "Caricato dalla flash da" +#define D_USE_DEFAULTS "Usa valori predefiniti" +#define D_ERASED_SECTOR "Settore cancellato" + +// xdrv_02_webserver.ino +#define D_NOSCRIPT "Per usare Tasmota abilita JavaScript" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "FFirmware MINIMALE
Effettua aggiornamento" +#define D_WEBSERVER_ACTIVE_ON "Server web attivo in" +#define D_WITH_IP_ADDRESS "con indirizzo IP" +#define D_WEBSERVER_STOPPED "Server web arrestato" +#define D_FILE_NOT_FOUND "File non trovato" +#define D_REDIRECTED "Redirezione al captive portal" +#define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Impostazione Wifimanager come AccessPoint e Station" +#define D_WIFIMANAGER_SET_ACCESSPOINT "Impostazione Wifimanager come AccessPoint" +#define D_TRYING_TO_CONNECT "Tentativo connessione dispositivo alla rete" + +#define D_RESTART_IN "Riavvio tra" +#define D_SECONDS "secondi" +#define D_DEVICE_WILL_RESTART "Il dispositivo verrà riavviato tra pochi secondi" +#define D_BUTTON_TOGGLE "ON/OFF" +#define D_CONFIGURATION "Configurazione" +#define D_INFORMATION "Informazioni" +#define D_FIRMWARE_UPGRADE "Aggiornamento firmware" +#define D_CONSOLE "Console" +#define D_CONFIRM_RESTART "Conferma riavvio" + +#define D_CONFIGURE_MODULE "Configura modulo" +#define D_CONFIGURE_WIFI "Configura WiFi" +#define D_CONFIGURE_MQTT "Configura MQTT" +#define D_CONFIGURE_DOMOTICZ "Configura Domoticz" +#define D_CONFIGURE_LOGGING "Configura registrazione" +#define D_CONFIGURE_OTHER "Configura extra" +#define D_CONFIRM_RESET_CONFIGURATION "Conferma ripristino configurazione" +#define D_RESET_CONFIGURATION "Ripristino configurazione" +#define D_BACKUP_CONFIGURATION "Backup configurazione" +#define D_RESTORE_CONFIGURATION "Ripristino configurazione" +#define D_MAIN_MENU "Menu principale" + +#define D_MODULE_PARAMETERS "Parametri modulo" +#define D_MODULE_TYPE "Tipo modulo" +#define D_PULLUP_ENABLE "Nessun pulsante/switch pull-up" +#define D_ADC "ADC" +#define D_GPIO "GPIO" +#define D_SERIAL_IN "Seriale - IN" +#define D_SERIAL_OUT "Seriale - OUT" + +#define D_WIFI_PARAMETERS "Parametri WiFi" +#define D_SCAN_FOR_WIFI_NETWORKS "Scansione reti WiFi" +#define D_SCAN_DONE "Scansione completata" +#define D_NO_NETWORKS_FOUND "Nessuna rete trovata" +#define D_REFRESH_TO_SCAN_AGAIN "Aggiorna per nuova scansione" +#define D_DUPLICATE_ACCESSPOINT "AccessPoint duplicato" +#define D_SKIPPING_LOW_QUALITY "Ignorato a causa di bassa qualità" +#define D_RSSI "RSSI" +#define D_WEP "WEP" +#define D_WPA_PSK "WPA PSK" +#define D_WPA2_PSK "WPA2 PSK" +#define D_AP1_SSID "AP1 - SSID" +#define D_AP1_PASSWORD "AP1 - Password" +#define D_AP2_SSID "AP2 - SSID" +#define D_AP2_PASSWORD "AP2 - Password" + +#define D_MQTT_PARAMETERS "Parametri MQTT" +#define D_CLIENT "Client" +#define D_FULL_TOPIC "Full Topic" + +#define D_LOGGING_PARAMETERS "Parametri registrazione" +#define D_SERIAL_LOG_LEVEL "Livello registrazione seriale" +#define D_MQTT_LOG_LEVEL "Livello registrazione MQTT" +#define D_WEB_LOG_LEVEL "Livello registrazione web" +#define D_SYS_LOG_LEVEL "Livello registrazione Sys" +#define D_MORE_DEBUG "Debug aggiuntivo" +#define D_SYSLOG_HOST "Host Syslog" +#define D_SYSLOG_PORT "Porta Syslog" +#define D_TELEMETRY_PERIOD "Periodo Telemetria" + +#define D_OTHER_PARAMETERS "Altri parametri" +#define D_TEMPLATE "Modello" +#define D_ACTIVATE "Attiva" +#define D_WEB_ADMIN_PASSWORD "Password amministratore web" +#define D_MQTT_ENABLE "Abilita MQTT" +#define D_FRIENDLY_NAME "Nome amichevole" +#define D_BELKIN_WEMO "Belkin WeMo" +#define D_HUE_BRIDGE "Bridge Hue" +#define D_SINGLE_DEVICE "dispositivo singolo" +#define D_MULTI_DEVICE "dispositivo multiplo" + +#define D_CONFIGURE_TEMPLATE "Configura modello" +#define D_TEMPLATE_PARAMETERS "Parametri modello" +#define D_TEMPLATE_NAME "Nome" +#define D_BASE_TYPE "Basato su" +#define D_TEMPLATE_FLAGS "Opzioni" + +#define D_SAVE_CONFIGURATION "Salva configurazione" +#define D_CONFIGURATION_SAVED "Configurazione salvata" +#define D_CONFIGURATION_RESET "Configurazione ripristinata" + +#define D_PROGRAM_VERSION "Versione programma" +#define D_BUILD_DATE_AND_TIME "Data e ora compilazione" +#define D_CORE_AND_SDK_VERSION "Versione core/SDK" +#define D_FLASH_WRITE_COUNT "Contatore scritture flash" +#define D_MAC_ADDRESS "Indirizzo MAC" +#define D_MQTT_HOST "Host MQTT" +#define D_MQTT_PORT "Porta MQTT" +#define D_MQTT_CLIENT "Client MQTT" +#define D_MQTT_USER "Utente MQTT" +#define D_MQTT_TOPIC "Topic MQTT" +#define D_MQTT_GROUP_TOPIC "Gruppo topic MQTT" +#define D_MQTT_FULL_TOPIC "Full topic MQTT" +#define D_MDNS_DISCOVERY "Ricerca mDNS" +#define D_MDNS_ADVERTISE "Notifica mDNS" +#define D_ESP_CHIP_ID "ID chip ESP" +#define D_FLASH_CHIP_ID "ID chip flash" +#define D_FLASH_CHIP_SIZE "Dimensione flash" +#define D_FREE_PROGRAM_SPACE "Spazio libero memoria programma" + +#define D_UPGRADE_BY_WEBSERVER "Aggiornamento via server web" +#define D_OTA_URL "OTA URL" +#define D_START_UPGRADE "Avvio aggiornamento" +#define D_UPGRADE_BY_FILE_UPLOAD "Aggiornamento tramite upload file" +#define D_UPLOAD_STARTED "Upload iniziato" +#define D_UPGRADE_STARTED "Aggiornamento avviato" +#define D_UPLOAD_DONE "Upload completato" +#define D_UPLOAD_ERR_1 "Nessun file selezionato" +#define D_UPLOAD_ERR_2 "Spazio insufficiente" +#define D_UPLOAD_ERR_3 "Magic byte non corrispondente a 0xE9" +#define D_UPLOAD_ERR_4 "Dimensione memoria programma maggiore della dimensione reale della flash" +#define D_UPLOAD_ERR_5 "Errore comparazione buffer upload" +#define D_UPLOAD_ERR_6 "Invio fallito. Abilita registrazione logging 3" +#define D_UPLOAD_ERR_7 "Upload annullato" +#define D_UPLOAD_ERR_8 "File non valido" +#define D_UPLOAD_ERR_9 "File troppo grande" +#define D_UPLOAD_ERR_10 "Inizializzazione chip RF fallita" +#define D_UPLOAD_ERR_11 "Cancellazione chip RF fallita" +#define D_UPLOAD_ERR_12 "Scrittura chip RF fallita" +#define D_UPLOAD_ERR_13 "Decodifica firmware RF fallita" +#define D_UPLOAD_ERR_14 "Non compatibile" +#define D_UPLOAD_ERROR_CODE "Codice errore upload" + +#define D_ENTER_COMMAND "Inserisci comando" +#define D_ENABLE_WEBLOG_FOR_RESPONSE "Abilita weblog 2 se è attesa una risposta" +#define D_NEED_USER_AND_PASSWORD "Richiesto utente=&password=" + +// xdrv_01_mqtt.ino +#define D_FINGERPRINT "Verifica chiave TLS..." +#define D_TLS_CONNECT_FAILED_TO "Connessione TLS fallita a" +#define D_RETRY_IN "Nuovo tentativo in" +#define D_VERIFIED "Verificato tramite chiave" +#define D_INSECURE "Connessione non sicura a causa di chiave non valida" +#define D_CONNECT_FAILED_TO "Connessione fallita a" + +// xplg_wemohue.ino +#define D_MULTICAST_DISABLED "Multicast disabilitato" +#define D_MULTICAST_REJOINED "Multicast (ri)associato" +#define D_MULTICAST_JOIN_FAILED "Associazione multicast fallita" +#define D_FAILED_TO_SEND_RESPONSE "Invio risposta fallito" + +#define D_WEMO "WeMo" +#define D_WEMO_BASIC_EVENT "Evento base WeMo" +#define D_WEMO_EVENT_SERVICE "Servizio eventi WeMo" +#define D_WEMO_META_SERVICE "Servizio meta WeMo" +#define D_WEMO_SETUP "Impostazione WeMo" +#define D_RESPONSE_SENT "Risposta inviata" + +#define D_HUE "Hue" +#define D_HUE_BRIDGE_SETUP "Impostazione Hue" +#define D_HUE_API_NOT_IMPLEMENTED "API Hue non implementata" +#define D_HUE_API "API Hue" +#define D_HUE_POST_ARGS "POST argomenti Hue" +#define D_3_RESPONSE_PACKETS_SENT "3 pacchetti di risposta inviati" + +// xdrv_07_domoticz.ino +#define D_DOMOTICZ_PARAMETERS "Parametri Domoticz" +#define D_DOMOTICZ_IDX "Idx" +#define D_DOMOTICZ_KEY_IDX "Idx chiave" +#define D_DOMOTICZ_SWITCH_IDX "Idx switch" +#define D_DOMOTICZ_SENSOR_IDX "Idx sensore" + #define D_DOMOTICZ_TEMP "Temp" + #define D_DOMOTICZ_TEMP_HUM "Temp,Umd" + #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Umd,Baro" + #define D_DOMOTICZ_POWER_ENERGY "Alim,Energia" + #define D_DOMOTICZ_ILLUMINANCE "Illuminazione" + #define D_DOMOTICZ_COUNT "Cont/PM1" + #define D_DOMOTICZ_VOLTAGE "Tensione/PM2.5" + #define D_DOMOTICZ_CURRENT "Corrente/PM10" + #define D_DOMOTICZ_AIRQUALITY "QualitàAria" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" +#define D_DOMOTICZ_UPDATE_TIMER "Intervallo aggiornamento" + +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configura timer" +#define D_TIMER_PARAMETERS "Parametri timer" +#define D_TIMER_ENABLE "Abilita timer" +#define D_TIMER_ARM "Attiva" +#define D_TIMER_TIME "Ora" +#define D_TIMER_DAYS "Giorni" +#define D_TIMER_REPEAT "Ripeti" +#define D_TIMER_OUTPUT "Output" +#define D_TIMER_ACTION "Azione" + +// xdrv_10_knx.ino +#define D_CONFIGURE_KNX "Configura KNX" +#define D_KNX_PARAMETERS "Parametri KNX" +#define D_KNX_GENERAL_CONFIG "Generale" +#define D_KNX_PHYSICAL_ADDRESS "Indirizzo fisico" +#define D_KNX_PHYSICAL_ADDRESS_NOTE "(deve essere univoco nella rete KNX )" +#define D_KNX_ENABLE "Abilita KNX" +#define D_KNX_GROUP_ADDRESS_TO_WRITE "Dati da inviare al gruppo di indirizzi" +#define D_ADD "Aggiungi" +#define D_DELETE "Elimina" +#define D_REPLY "Rispondi" +#define D_KNX_GROUP_ADDRESS_TO_READ "Gruppo di indirizzi da cui ricevere dati" +#define D_RECEIVED_FROM "Ricevuto da" +#define D_KNX_COMMAND_WRITE "Scrivi" +#define D_KNX_COMMAND_READ "Leggi" +#define D_KNX_COMMAND_OTHER "Altro" +#define D_SENT_TO "invia a" +#define D_KNX_WARNING "L'indirizzo del gruppo ( 0 / 0 / 0 ) è riservato e non può essere usato." +#define D_KNX_ENHANCEMENT "Miglioramento comunicazione" +#define D_KNX_TX_SLOT "KNX - TX" +#define D_KNX_RX_SLOT "KNX - RX" + +// xdrv_03_energy.ino +#define D_ENERGY_TODAY "Energia - oggi" +#define D_ENERGY_YESTERDAY "Energia - ieri" +#define D_ENERGY_TOTAL "Energia - totale" + +// xdrv_27_shutter.ino +#define D_OPEN "Apri" +#define D_CLOSE "Chiudi" +#define D_DOMOTICZ_SHUTTER "Serranda" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configura PCF8574" +#define D_PCF8574_PARAMETERS "Parametri PCF8574" +#define D_INVERT_PORTS "Inverti porte" +#define D_DEVICE "Dispositivo" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + +// xsns_05_ds18b20.ino +#define D_SENSOR_BUSY "Sensore occupato" +#define D_SENSOR_CRC_ERROR "Errore CRC sensore" +#define D_SENSORS_FOUND "Sensori trovati" + +// xsns_06_dht.ino +#define D_TIMEOUT_WAITING_FOR "Timeout attesa per" +#define D_START_SIGNAL_LOW "inizio segnale basso" +#define D_START_SIGNAL_HIGH "inizio segnale alto" +#define D_PULSE "impulso" +#define D_CHECKSUM_FAILURE "Checksum fallito" + +// xsns_07_sht1x.ino +#define D_SENSOR_DID_NOT_ACK_COMMAND "Il sensore non ha eseguito il comando ACK" +#define D_SHT1X_FOUND "Trovato SHT1X" + +// xsns_18_pms5003.ino +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "Particelle" + +// xsns_27_apds9960.ino +#define D_GESTURE "Gesto" +#define D_COLOR_RED "Rosso" +#define D_COLOR_GREEN "Verde" +#define D_COLOR_BLUE "Blu" +#define D_CCT "CCT" +#define D_PROXIMITY "Vicinanza" + +// xsns_32_mpu6050.ino +#define D_AX_AXIS "Accelerazione asse X" +#define D_AY_AXIS "Accelerazione asse Y" +#define D_AZ_AXIS "Accelerazione asse Z" +#define D_GX_AXIS "Giroscopio asse X" +#define D_GY_AXIS "Giroscopio asse Y" +#define D_GZ_AXIS "Giroscopio asse Z" + +// xsns_34_hx711.ino +#define D_HX_CAL_REMOVE "Rimuovi peso" +#define D_HX_CAL_REFERENCE "Carica riferimento peso" +#define D_HX_CAL_DONE "Calibrato" +#define D_HX_CAL_FAIL "Calibrazione fallita" +#define D_RESET_HX711 "Ripristino scala" +#define D_CONFIGURE_HX711 "Configura scala" +#define D_HX711_PARAMETERS "Parametri scala" +#define D_ITEM_WEIGHT "Peso oggetto" +#define D_REFERENCE_WEIGHT "Peso di riferimento" +#define D_CALIBRATE "Calibrato" +#define D_CALIBRATION "Calibrazione" + +//xsns_35_tx20.ino +#define D_TX20_WIND_DIRECTION "Direzione vento" +#define D_TX20_WIND_SPEED "Velocità vento" +#define D_TX20_WIND_SPEED_MIN "Velocità minima vento" +#define D_TX20_WIND_SPEED_MAX "Velocità massima vento" +#define D_TX20_NORTH "N" +#define D_TX20_EAST "E" +#define D_TX20_SOUTH "S" +#define D_TX20_WEST "O" + +// xsns_53_sml.ino +#define D_TPWRIN "Energia totale IN" +#define D_TPWROUT "Energia totale OUT" +#define D_TPWRCURR "Corrente IN/OUT" +#define D_TPWRCURR1 "Corrente IN p1" +#define D_TPWRCURR2 "Corrente IN p2" +#define D_TPWRCURR3 "Corrente IN p3" +#define D_Strom_L1 "Corrente L1" +#define D_Strom_L2 "Corrente L2" +#define D_Strom_L3 "Corrente L3" +#define D_Spannung_L1 "Tensione L1" +#define D_Spannung_L2 "Tensione L2" +#define D_Spannung_L3 "Tensione L3" +#define D_METERNR "Meter_number" +#define D_METERSID "ID servizio" +#define D_GasIN "Contatore" +#define D_H2oIN "Contatore" +#define D_StL1L2L3 "Corrente L1+L2+L3" +#define D_SpL1L2L3 "Tensione L1+L2+L3/3" + +// tasmota_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Nessuno" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "Riproduttore MP3" +#define D_SENSOR_IRSEND "IR - TX" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Pulsante" // Suffix "1" +#define D_SENSOR_RELAY "Relè" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "Led - Lampeggio" // Suffix "i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Contatore" // Suffix "1" +#define D_SENSOR_IRRECV "IR - RX" +#define D_SENSOR_MHZ_RX "MHZ - RX" +#define D_SENSOR_MHZ_TX "MHZ - TX" +#define D_SENSOR_PZEM004_RX "PZEM004 - RX" +#define D_SENSOR_PZEM016_RX "PZEM016 - RX" +#define D_SENSOR_PZEM017_RX "PZEM017 - RX" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX - TX" +#define D_SENSOR_SAIR_RX "SAir - RX" +#define D_SENSOR_SAIR_TX "SAir - TX" +#define D_SENSOR_SPI_CS "SPI - CS" +#define D_SENSOR_SPI_DC "SPI - DC" +#define D_SENSOR_SPI_MISO "SPI - MISO" +#define D_SENSOR_SPI_MOSI "SPI - MOSI" +#define D_SENSOR_SPI_CLK "SPI - CLK" +#define D_SENSOR_BACKLIGHT "Retroilluminazione" +#define D_SENSOR_PMS5003_TX "PMS5003 - TX" +#define D_SENSOR_PMS5003_RX "PMS5003 - RX" +#define D_SENSOR_SDS0X1_RX "SDS0X1 - RX" +#define D_SENSOR_SDS0X1_TX "SDS0X1 - TX" +#define D_SENSOR_HPMA_RX "HPMA - RX" +#define D_SENSOR_HPMA_TX "HPMA - TX" +#define D_SENSOR_SBR_RX "SerBr - RX" +#define D_SENSOR_SBR_TX "SerBr - TX" +#define D_SENSOR_SR04_TRIG "SR04 Tri - TX" +#define D_SENSOR_SR04_ECHO "SR04 Ech - RX" +#define D_SENSOR_SDM120_TX "SDMx20 - TX" +#define D_SENSOR_SDM120_RX "SDMx20 - RX" +#define D_SENSOR_SDM630_TX "SDM630 - TX" +#define D_SENSOR_SDM630_RX "SDM630 - RX" +#define D_SENSOR_TM1638_CLK "TM16 - CLK" +#define D_SENSOR_TM1638_DIO "TM16 - DIO" +#define D_SENSOR_TM1638_STB "TM16 - STB" +#define D_SENSOR_HX711_SCK "HX711 - SCK" +#define D_SENSOR_HX711_DAT "HX711 - DAT" +#define D_SENSOR_TX2X_TX "TX2x" +#define D_SENSOR_RFSEND "RF - TX" +#define D_SENSOR_RFRECV "RF - RX" +#define D_SENSOR_TUYA_TX "Tuya - TX" +#define D_SENSOR_TUYA_RX "Tuya - RX" +#define D_SENSOR_MGC3130_XFER "MGC3130 - XFR" +#define D_SENSOR_MGC3130_RESET "MGC3130 - RST" +#define D_SENSOR_SSPI_MISO "SSPI - MISO" +#define D_SENSOR_SSPI_MOSI "SSPI - MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI - SCLK" +#define D_SENSOR_SSPI_CS "SSPI - CS" +#define D_SENSOR_SSPI_DC "SSPI - DC" +#define D_SENSOR_RF_SENSOR "Sensore RF" +#define D_SENSOR_AZ_RX "AZ - RX" +#define D_SENSOR_AZ_TX "AZ - TX" +#define D_SENSOR_MAX31855_CS "MX31855 - CS" +#define D_SENSOR_MAX31855_CLK "MX31855 - CLK" +#define D_SENSOR_MAX31855_DO "MX31855 - DO" +#define D_SENSOR_NRG_SEL "HLWBL - SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL - CF1" +#define D_SENSOR_HLW_CF "HLW8012 - CF" +#define D_SENSOR_HJL_CF "BL0937 - CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 - TX" +#define D_SENSOR_MCP39F5_RX "MCP39F5 - RX" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 - TX" +#define D_SENSOR_CSE7766_RX "CSE7766 - RX" +#define D_SENSOR_PN532_TX "PN532 - TX" +#define D_SENSOR_PN532_RX "PN532 - RX" +#define D_SENSOR_SM16716_CLK "SM16716 - CLK" +#define D_SENSOR_SM16716_DAT "SM16716 - DAT" +#define D_SENSOR_SM16716_POWER "SM16716 - PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 - DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 - DCKI" +#define D_SENSOR_ARIRFRCV "IR ALux - RCV" +#define D_SENSOR_ARIRFSEL "IR ALux - SEL" +#define D_SENSOR_TXD "Seriale - TX" +#define D_SENSOR_RXD "Seriale - RX" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE - Clock" +#define D_SENSOR_HRE_DATA "HRE - Dati" +#define D_SENSOR_ADE7953_IRQ "ADE7953 - IRQ" +#define D_SENSOR_BUZZER "Cicalino" +#define D_SENSOR_OLED_RESET "Ripristino OLED" +#define D_SENSOR_ZIGBEE_TXD "Zigbee - TX" +#define D_SENSOR_ZIGBEE_RXD "Zigbee - RX" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 - TX" +#define D_SENSOR_SOLAXX1_RX "SolaxX1- RX" +#define D_SENSOR_IBEACON_TX "iBeacon - TX" +#define D_SENSOR_IBEACON_RX "iBeacon - RX" +#define D_SENSOR_RDM6300_RX "RDM6300 - RX" +#define D_SENSOR_CC1101_CS "CC1101 - CS" +#define D_SENSOR_A4988_DIR "A4988 - DIR" +#define D_SENSOR_A4988_STP "A4988 - STP" +#define D_SENSOR_A4988_ENA "A4988 - ENA" +#define D_SENSOR_A4988_MS1 "A4988 - MS1" +#define D_SENSOR_A4988_MS2 "A4988 - MS2" +#define D_SENSOR_A4988_MS3 "A4988 - MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 - TX" +#define D_SENSOR_DDS2382_RX "DDS238-2 - RX" +#define D_SENSOR_DDSU666_TX "DDSU666 - TX" +#define D_SENSOR_DDSU666_RX "DDSU666 - RX" +#define D_SENSOR_SM2135_CLK "SM2135 - CLK" +#define D_SENSOR_SM2135_DAT "SM2135 - DATI" +#define D_SENSOR_DEEPSLEEP "Deep sleep" +#define D_SENSOR_EXS_ENABLE "EXS - Abilita" +#define D_SENSOR_SLAVE_TX "Slave - TX" +#define D_SENSOR_SLAVE_RX "Slave - RX" +#define D_SENSOR_SLAVE_RESET "Slave - RST" +#define D_SENSOR_GPS_RX "GPS - RX" +#define D_SENSOR_GPS_TX "GPS - TX" +#define D_SENSOR_HM10_RX "HM10 - RX" +#define D_SENSOR_HM10_TX "HM10 - TX" +#define D_SENSOR_LE01MR_RX "LE-01MR - RX" +#define D_SENSOR_LE01MR_TX "LE-01MR - TX" +#define D_SENSOR_CC1101_GDO0 "CC1101 - GDO0" +#define D_SENSOR_CC1101_GDO2 "CC1101 - GDO2" +#define D_SENSOR_HRXL_RX "HRXL - RX" +#define D_SENSOR_ELECTRIQ_MOODL "MOODL - TX" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "Velocità vento" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" + +// Units +#define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" +#define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" +#define D_UNIT_HERTZ "Hz" +#define D_UNIT_HOUR "o" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" +#define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" +#define D_UNIT_KILOGRAM "kg" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" +#define D_UNIT_KILOOHM "kΩ" +#define D_UNIT_KILOWATTHOUR "kWh" +#define D_UNIT_LUX "lx" +#define D_UNIT_MICROGRAM_PER_CUBIC_METER "µg/m³" +#define D_UNIT_MICROMETER "µm" +#define D_UNIT_MICROSECOND "µs" +#define D_UNIT_MILLIAMPERE "mA" +#define D_UNIT_MILLIMETER "mm" +#define D_UNIT_MILLIMETER_MERCURY "mmHg" +#define D_UNIT_MILLISECOND "ms" +#define D_UNIT_MINUTE "Min" +#define D_UNIT_PARTS_PER_BILLION "ppb" +#define D_UNIT_PARTS_PER_DECILITER "ppd" +#define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" +#define D_UNIT_PRESSURE "hPa" +#define D_UNIT_SECOND "sec" +#define D_UNIT_SECTORS "settori" +#define D_UNIT_VA "VA" +#define D_UNIT_VAR "VAr" +#define D_UNIT_VOLT "V" +#define D_UNIT_WATT "W" +#define D_UNIT_WATTHOUR "Wh" +#define D_UNIT_WATT_METER_QUADRAT "W/m²" + +//SDM220, SDM120, LE01MR +#define D_PHASE_ANGLE "Angolo Fase" +#define D_IMPORT_ACTIVE "Potenza attiva importata" +#define D_EXPORT_ACTIVE "Potenza attiva esportata" +#define D_IMPORT_REACTIVE "Potenza reattiva importata" +#define D_EXPORT_REACTIVE "Potenza reattiva esportata" +#define D_TOTAL_REACTIVE "Potenza reattiva totale" +#define D_UNIT_KWARH "kVArh" +#define D_UNIT_ANGLE "°" +#define D_TOTAL_ACTIVE "Potenza attiva totale" + +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 - Voltaggio" +#define D_PV1_CURRENT "PV1 - Corrente" +#define D_PV1_POWER "PV1 - Energia" +#define D_PV2_VOLTAGE "PV2 - Voltaggio" +#define D_PV2_CURRENT "PV2 - Corrente" +#define D_PV2_POWER "PV2 - Energia" +#define D_SOLAR_POWER "Energia solare" +#define D_INVERTER_POWER "Energia inverter" +#define D_STATUS "Stato" +#define D_WAITING "In attesa" +#define D_CHECKING "Controllo" +#define D_WORKING "Attivo" +#define D_FAILURE "Errore" +#define D_SOLAX_ERROR_0 "Nessun codice errore" +#define D_SOLAX_ERROR_1 "Griglia errore persa" +#define D_SOLAX_ERROR_2 "Griglia errore tensione" +#define D_SOLAX_ERROR_3 "Griglia errore frequenza" +#define D_SOLAX_ERROR_4 "Errore tensione PV" +#define D_SOLAX_ERROR_5 "Errore isolamento" +#define D_SOLAX_ERROR_6 "Errore temperatura eccessiva" +#define D_SOLAX_ERROR_7 "Errore ventilatore" +#define D_SOLAX_ERROR_8 "Altro errore dispositivo" + +//xdrv_10_scripter.ino +#define D_CONFIGURE_SCRIPT "Modifica script" +#define D_SCRIPT "modifica script" +#define D_SDCARD_UPLOAD "upload file" +#define D_SDCARD_DIR "cartella scheda SD" +#define D_UPL_DONE "Completato" +#define D_SCRIPT_CHARS_LEFT "caratteri rimanenti" +#define D_SCRIPT_CHARS_NO_MORE "nessun altro carattere" +#define D_SCRIPT_DOWNLOAD "Download" +#define D_SCRIPT_ENABLE "abilita script" +#define D_SCRIPT_UPLOAD "Upload" +#define D_SCRIPT_UPLOAD_FILES "Upload file" + +//xsns_67_as3935.ino +#define D_AS3935_GAIN "guadagno:" +#define D_AS3935_ENERGY "energia:" +#define D_AS3935_DISTANCE "distanza:" +#define D_AS3935_DISTURBER "disturbatore:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "apross.:" +#define D_AS3935_AWAY "lontano" +#define D_AS3935_LIGHT "illuminazione" +#define D_AS3935_OUT "illuminazione fuori intervallo" +#define D_AS3935_NOT "distanza non determinata" +#define D_AS3935_ABOVE "illuminazione ambientale" +#define D_AS3935_NOISE "rilevato rumore" +#define D_AS3935_DISTDET "rilevato disturbatore" +#define D_AS3935_INTNOEV "Interrupt senza evento!" +#define D_AS3935_NOMESS "in ascolto..." +#define D_AS3935_ON "ON" +#define D_AS3935_OFF "OFF" +#define D_AS3935_INDOORS "Interno" +#define D_AS3935_OUTDOORS "Esterno" +#define D_AS3935_CAL_FAIL "calibrazione fallita" +#define D_AS3935_CAL_OK "calibrazione impostata a:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm - RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm - TX" + +#endif // _LANGUAGE_IT_IT_H_ diff --git a/tasmota/language/ko-KO.h b/tasmota/language/ko_KO.h similarity index 93% rename from tasmota/language/ko-KO.h rename to tasmota/language/ko_KO.h index 1767366db..3e3bd17e0 100644 --- a/tasmota/language/ko-KO.h +++ b/tasmota/language/ko_KO.h @@ -98,6 +98,8 @@ #define D_FILE "파일" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "남은 메모리" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frequency" #define D_GAS "가스" #define D_GATEWAY "게이트웨이" @@ -210,7 +212,7 @@ #define D_IN_MODE "in mode" #define D_CONNECT_FAILED_NO_IP_ADDRESS "IP 주소가 수신되지 않아 연결이 실패했습니다" #define D_CONNECT_FAILED_AP_NOT_REACHED "연결이 닿지 않아 AP에 연결할 수 없습니다" -#define D_CONNECT_FAILED_WRONG_PASSWORD "비밀번호가 틀려 AP에 연결할 수 없습니다" +#define D_CONNECT_FAILED_WRONG_PASSWORD "연결할 수 없습니다" #define D_CONNECT_FAILED_AP_TIMEOUT "시간초과로 AP에 연결할 수 없습니다" #define D_ATTEMPTING_CONNECTION "연결 시도 중..." #define D_CHECKING_CONNECTION "연결 체크 중..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "입자" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "시" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "초" #define D_UNIT_SECTORS "섹터" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_KO_KO_H_ diff --git a/tasmota/language/nl-NL.h b/tasmota/language/nl_NL.h similarity index 92% rename from tasmota/language/nl-NL.h rename to tasmota/language/nl_NL.h index 246066035..49efcb622 100644 --- a/tasmota/language/nl-NL.h +++ b/tasmota/language/nl_NL.h @@ -98,6 +98,8 @@ #define D_FILE "Bestand" #define D_FLOW_RATE "Debiet" #define D_FREE_MEMORY "Vrij geheugen" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frequentie" #define D_GAS "Gas" #define D_GATEWAY "Gateway" @@ -210,7 +212,7 @@ #define D_IN_MODE "in stand" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Verbinding mislukt omdat geen IP adres werd ontvangen" #define D_CONNECT_FAILED_AP_NOT_REACHED "Verbinding mislukt omdat AP onbereikbaar is" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Verbinding mislukt door fout wachtwoord" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Verbinding mislukt" #define D_CONNECT_FAILED_AP_TIMEOUT "Verbinding mislukt door AP time-out" #define D_ATTEMPTING_CONNECTION "Verbinden..." #define D_CHECKING_CONNECTION "Controleer verbinding..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Stofdeeltjes" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Versn. X-as" #define D_AY_AXIS "Versn. Y-as" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sec" #define D_UNIT_SECTORS "sectoren" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_NL_NL_H_ diff --git a/tasmota/language/pl-PL.h b/tasmota/language/pl_PL.h similarity index 93% rename from tasmota/language/pl-PL.h rename to tasmota/language/pl_PL.h index 6f945183a..d9d05fbeb 100644 --- a/tasmota/language/pl-PL.h +++ b/tasmota/language/pl_PL.h @@ -98,6 +98,8 @@ #define D_FILE "Plik" #define D_FLOW_RATE "Przepływ" #define D_FREE_MEMORY "Wolna pamięć" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Częstotliwość" #define D_GAS "Gas" #define D_GATEWAY "Brama" @@ -210,7 +212,7 @@ #define D_IN_MODE "w trybie" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Połączenie nie powiodło się, ponieważ nie otrzymano adresu IP" #define D_CONNECT_FAILED_AP_NOT_REACHED "Połączenie nie powiodło się, AP nie osiągalny" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Połączenie nie powiodło się, nieprawidlowe hasło" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Połączenie nie powiodło się" #define D_CONNECT_FAILED_AP_TIMEOUT "Nie udało się nawiązac połączenia, limit czasu przekroczony" #define D_ATTEMPTING_CONNECTION "Próba połączenia..." #define D_CHECKING_CONNECTION "Sprawdzanie połączenia..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Cząstki" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Podświetlanie" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Godz" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sec" #define D_UNIT_SECTORS "sektory" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_PL_PL_D_H_ diff --git a/tasmota/language/pt-BR.h b/tasmota/language/pt_BR.h similarity index 93% rename from tasmota/language/pt-BR.h rename to tasmota/language/pt_BR.h index 236652de2..4fb48c04d 100644 --- a/tasmota/language/pt-BR.h +++ b/tasmota/language/pt_BR.h @@ -98,6 +98,8 @@ #define D_FILE "Arquivo" #define D_FLOW_RATE "Quociente de vazão" #define D_FREE_MEMORY "Memória livre" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frequência" #define D_GAS "Gás" #define D_GATEWAY "Gateway" @@ -210,7 +212,7 @@ #define D_IN_MODE "em modo" #define D_CONNECT_FAILED_NO_IP_ADDRESS "A ligação falhou porque nenhum endereço IP foi recebido" #define D_CONNECT_FAILED_AP_NOT_REACHED "A ligação falhou porque o PA não pôde ser alcançado" -#define D_CONNECT_FAILED_WRONG_PASSWORD "A ligação falhou porque a senha está incorreta" +#define D_CONNECT_FAILED_WRONG_PASSWORD "A ligação falhou" #define D_CONNECT_FAILED_AP_TIMEOUT "A ligação falhou porque o tempo foi excedido" #define D_ATTEMPTING_CONNECTION "Ligando..." #define D_CHECKING_CONNECTION "Verificando ligação..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Partículas" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Luz de fundo" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "H" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "s" #define D_UNIT_SECTORS "setores" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_PT_BR_H_ diff --git a/tasmota/language/pt-PT.h b/tasmota/language/pt_PT.h similarity index 93% rename from tasmota/language/pt-PT.h rename to tasmota/language/pt_PT.h index 507e7fe8b..232b7494d 100644 --- a/tasmota/language/pt-PT.h +++ b/tasmota/language/pt_PT.h @@ -98,6 +98,8 @@ #define D_FILE "Ficheiro" #define D_FLOW_RATE "Taxa de Fluxo" #define D_FREE_MEMORY "Memoria Livre" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frequência" #define D_GAS "Gás" #define D_GATEWAY "Gateway" @@ -210,7 +212,7 @@ #define D_IN_MODE "em modo" #define D_CONNECT_FAILED_NO_IP_ADDRESS "A ligação falhou porque nenhum endereço IP foi recebido" #define D_CONNECT_FAILED_AP_NOT_REACHED "A ligação falhou porque o AP não pode ser alcançado" -#define D_CONNECT_FAILED_WRONG_PASSWORD "A ligação falhou porque a palavra chave está incorreta" +#define D_CONNECT_FAILED_WRONG_PASSWORD "A ligação falhou" #define D_CONNECT_FAILED_AP_TIMEOUT "A ligação falhou porque o tempo excedeu" #define D_ATTEMPTING_CONNECTION "A ligar..." #define D_CHECKING_CONNECTION "A verificar ligação..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Partículas" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Luz fundo" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sec" #define D_UNIT_SECTORS "setores" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_PT_PT_H_ diff --git a/tasmota/language/ro-RO.h b/tasmota/language/ro_RO.h similarity index 93% rename from tasmota/language/ro-RO.h rename to tasmota/language/ro_RO.h index c3efc5758..79c66d779 100644 --- a/tasmota/language/ro-RO.h +++ b/tasmota/language/ro_RO.h @@ -98,6 +98,8 @@ #define D_FILE "Fișier" #define D_FLOW_RATE "Debit" #define D_FREE_MEMORY "Memorie Liberă" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frecvență" #define D_GAS "Gaz" #define D_GATEWAY "Gateway" @@ -210,7 +212,7 @@ #define D_IN_MODE "in mode" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Conexiune eșuată, lipsă primire adresă IP" #define D_CONNECT_FAILED_AP_NOT_REACHED "Conexiune eșuată, nu s-a găsit AP" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Conexiune eșuată, parolă greșită AP" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Conexiune eșuată" #define D_CONNECT_FAILED_AP_TIMEOUT "Conexiune eșuată, expirare timp AP" #define D_ATTEMPTING_CONNECTION "Conectare in curs ..." #define D_CHECKING_CONNECTION "Verificare conexiune..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Particule" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel.Axa-X" #define D_AY_AXIS "Accel.Axa-Y" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sec" #define D_UNIT_SECTORS "sectors" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Încarcă" #define D_SCRIPT_UPLOAD_FILES "Încarcă fișiere" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_RO_RO_H_ diff --git a/tasmota/language/ru-RU.h b/tasmota/language/ru_RU.h similarity index 93% rename from tasmota/language/ru-RU.h rename to tasmota/language/ru_RU.h index a104dca55..b7d8adcb6 100644 --- a/tasmota/language/ru-RU.h +++ b/tasmota/language/ru_RU.h @@ -98,6 +98,8 @@ #define D_FILE "Файл" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Свободная память" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frequency" #define D_GAS "Газ" #define D_GATEWAY "Шлюз" @@ -210,7 +212,7 @@ #define D_IN_MODE "в режиме" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Ошибка подключения, IP-адрес не получен" #define D_CONNECT_FAILED_AP_NOT_REACHED "Ошибка соединения, AP не может быть достигнута" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Ошибка соединения, неверный пароль к AP" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Ошибка соединения" #define D_CONNECT_FAILED_AP_TIMEOUT "Ошибка соединения с AP по тайм-ауту" #define D_ATTEMPTING_CONNECTION "Попытка подключения..." #define D_CHECKING_CONNECTION "Проверка соединения..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Particals" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "А" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Ч" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "кОм" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "гПа" #define D_UNIT_SECOND "сек" #define D_UNIT_SECTORS "секторов" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_RU_RU_H_ diff --git a/tasmota/language/sk-SK.h b/tasmota/language/sk_SK.h similarity index 93% rename from tasmota/language/sk-SK.h rename to tasmota/language/sk_SK.h index 706432e5b..9db574bf8 100644 --- a/tasmota/language/sk-SK.h +++ b/tasmota/language/sk_SK.h @@ -98,6 +98,8 @@ #define D_FALSE "Nepravda" #define D_FILE "Súbor" #define D_FREE_MEMORY "Voľná pamäť" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frekvencia" #define D_GAS "Plyn" #define D_GATEWAY "Predvolená brána" @@ -210,7 +212,7 @@ #define D_IN_MODE "v režime" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Chyba pripojenia, nebola obdržaná IP adresa" #define D_CONNECT_FAILED_AP_NOT_REACHED "Chyba pripojenia, nedostupný AP" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Chyba pripojenia, nesprávne heslo pre AP" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Chyba pripojenia" #define D_CONNECT_FAILED_AP_TIMEOUT "Chyba pripojenia, uplynul AP timeout" #define D_ATTEMPTING_CONNECTION "Pripájanie..." #define D_CHECKING_CONNECTION "Skúška spojenia..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "častíc" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. os-X" #define D_AY_AXIS "Accel. os-Y" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,16 +675,35 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "hod" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" #define D_UNIT_KILOWATTHOUR "kWh" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sek" #define D_UNIT_SECTORS "sektory" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_SK_SK_H_ diff --git a/tasmota/language/sv-SE.h b/tasmota/language/sv_SE.h similarity index 93% rename from tasmota/language/sv-SE.h rename to tasmota/language/sv_SE.h index df64cf2e3..da8355215 100644 --- a/tasmota/language/sv-SE.h +++ b/tasmota/language/sv_SE.h @@ -98,6 +98,8 @@ #define D_FILE "Fil" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Ledigt minne" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frekvens" #define D_GAS "Gas" #define D_GATEWAY "Gateway" @@ -210,7 +212,7 @@ #define D_IN_MODE "i läge" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Anslutning misslyckades mottog ingen IP-adress" #define D_CONNECT_FAILED_AP_NOT_REACHED "Anslutning misslyckades, kunde inte nå AP" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Anslutning misslyckades, fel lösenord för AP" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Anslutning misslyckades" #define D_CONNECT_FAILED_AP_TIMEOUT "Anslutning misslyckadess med AP, timeout" #define D_ATTEMPTING_CONNECTION "Försöker ansluta..." #define D_CHECKING_CONNECTION "Kontrollerar anslutning..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Partiklar" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axel" #define D_AY_AXIS "Accel. Y-Axel" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Tim" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "ink" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sek" #define D_UNIT_SECTORS "sektorer" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_SV_SE_H_ diff --git a/tasmota/language/tr-TR.h b/tasmota/language/tr_TR.h similarity index 92% rename from tasmota/language/tr-TR.h rename to tasmota/language/tr_TR.h index 1366b9996..9b205954b 100644 --- a/tasmota/language/tr-TR.h +++ b/tasmota/language/tr_TR.h @@ -98,6 +98,8 @@ #define D_FILE "Dosya" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Boş Hafıza" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frekans" #define D_GAS "Gas" #define D_GATEWAY "Geçit" @@ -210,7 +212,7 @@ #define D_IN_MODE "modunda" #define D_CONNECT_FAILED_NO_IP_ADDRESS "IP adresi alınamadığı için bağlantı kurulamadı" #define D_CONNECT_FAILED_AP_NOT_REACHED "Ap'ye ulaşılamadı" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Girilen parolayı AP kabul etmedi" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Bağlantı sağlanamadı" #define D_CONNECT_FAILED_AP_TIMEOUT "AP'ye bağlanılırken süre aşımı oluştu" #define D_ATTEMPTING_CONNECTION "Yeniden bağlanılıyor..." #define D_CHECKING_CONNECTION "Bağlantı kontrol ediliyor..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Particals" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sec" #define D_UNIT_SECTORS "sectors" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_TR_TR_H_ diff --git a/tasmota/language/uk-UA.h b/tasmota/language/uk_UA.h similarity index 94% rename from tasmota/language/uk-UA.h rename to tasmota/language/uk_UA.h index 2e3c4c077..8c566eac3 100644 --- a/tasmota/language/uk-UA.h +++ b/tasmota/language/uk_UA.h @@ -98,6 +98,8 @@ #define D_FILE "Файл" #define D_FLOW_RATE "Потік" #define D_FREE_MEMORY "Вільна память" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Частота" #define D_GAS "Газ" #define D_GATEWAY "Шлюз" @@ -210,7 +212,7 @@ #define D_IN_MODE "в режимі" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Помилка підключення, IP-адреса не отримана" #define D_CONNECT_FAILED_AP_NOT_REACHED "Помилка з'єднання, AP не знайдено" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Помилка з'єднання, невірне гасло до AP" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Помилка з'єднання" #define D_CONNECT_FAILED_AP_TIMEOUT "Помилка з'єднання з AP по тайм-ауту" #define D_ATTEMPTING_CONNECTION "Спроба підключення..." #define D_CHECKING_CONNECTION "Перевірка з'єднання..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "Частинки понад" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Приск. Вісь-X" #define D_AY_AXIS "Приск. Вісь-Y" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "OLED Light" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "А" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cм" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Гц" #define D_UNIT_HOUR "г" #define D_UNIT_GALLONS "гал" #define D_UNIT_GALLONS_PER_MIN "гал/хв" #define D_UNIT_INCREMENTS "інк" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "кг" #define D_UNIT_KILOMETER_PER_HOUR "км/г" // or "km/h" #define D_UNIT_KILOOHM "㏀" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "млрд⁻¹" #define D_UNIT_PARTS_PER_DECILITER "децилітр⁻¹" #define D_UNIT_PARTS_PER_MILLION "млн⁻¹" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "гПа" #define D_UNIT_SECOND "сек" #define D_UNIT_SECTORS "секторів" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Завантажити" #define D_SCRIPT_UPLOAD_FILES "Завантажити файли" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_UK_UA_H_ diff --git a/tasmota/language/zh-CN.h b/tasmota/language/zh_CN.h similarity index 92% rename from tasmota/language/zh-CN.h rename to tasmota/language/zh_CN.h index c59cb7a57..143e49aec 100644 --- a/tasmota/language/zh-CN.h +++ b/tasmota/language/zh_CN.h @@ -98,6 +98,8 @@ #define D_FILE "文件:" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "空闲内存" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "频率" #define D_GAS "气体" #define D_GATEWAY "网关" @@ -210,7 +212,7 @@ #define D_IN_MODE "模式:" #define D_CONNECT_FAILED_NO_IP_ADDRESS "连接失败,因为没有获取到 IP 地址" #define D_CONNECT_FAILED_AP_NOT_REACHED "连接失败,无法连接 AP" -#define D_CONNECT_FAILED_WRONG_PASSWORD "连接失败,AP 密码不正确" +#define D_CONNECT_FAILED_WRONG_PASSWORD "连接失败" #define D_CONNECT_FAILED_AP_TIMEOUT "连接失败,AP 超时" #define D_ATTEMPTING_CONNECTION "尝试连接..." #define D_CHECKING_CONNECTION "检查连接..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "颗粒物直径大于" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "加速度计X轴分量" #define D_AY_AXIS "加速度计Y轴分量" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "安" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "厘米" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "赫兹" #define D_UNIT_HOUR "时" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "千克" #define D_UNIT_KILOMETER_PER_HOUR "公里/时" // or "km/h" #define D_UNIT_KILOOHM "千欧" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "每分升" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "百帕" #define D_UNIT_SECOND "秒" #define D_UNIT_SECTORS "扇区" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_ZH_CN_H_ diff --git a/tasmota/language/zh-TW.h b/tasmota/language/zh_TW.h similarity index 92% rename from tasmota/language/zh-TW.h rename to tasmota/language/zh_TW.h index 6ef104bec..5c96ec688 100644 --- a/tasmota/language/zh-TW.h +++ b/tasmota/language/zh_TW.h @@ -98,6 +98,8 @@ #define D_FILE "文件:" #define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "可用記憶體" +#define D_PSR_MAX_MEMORY "PS-RAM Memory" +#define D_PSR_FREE_MEMORY "PS-RAM free Memory" #define D_FREQUENCY "Frequency" #define D_GAS "氣體" #define D_GATEWAY "網關" @@ -210,7 +212,7 @@ #define D_IN_MODE "模式:" #define D_CONNECT_FAILED_NO_IP_ADDRESS "連接失敗,因為沒有獲取到IP地址" #define D_CONNECT_FAILED_AP_NOT_REACHED "連接失敗,無法連接AP" -#define D_CONNECT_FAILED_WRONG_PASSWORD "連接失敗,AP密碼不正確" +#define D_CONNECT_FAILED_WRONG_PASSWORD "連接失敗" #define D_CONNECT_FAILED_AP_TIMEOUT "連接失敗,AP超時" #define D_ATTEMPTING_CONNECTION "嘗試連接..." #define D_CHECKING_CONNECTION "檢查連接..." @@ -482,6 +484,14 @@ #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "顆粒物直徑大於" +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" #define D_AY_AXIS "Accel. Y-Axis" @@ -567,7 +577,8 @@ #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Backlight" -#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" #define D_SENSOR_HPMA_RX "HPMA Rx" @@ -664,15 +675,34 @@ #define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" // Units #define D_UNIT_AMPERE "安" +#define D_UNIT_CELSIUS "C" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "時" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "千歐" @@ -689,6 +719,7 @@ #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "每分升" #define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PERCENT "%%" #define D_UNIT_PRESSURE "百帕" #define D_UNIT_SECOND "秒" #define D_UNIT_SECTORS "扇區" @@ -747,4 +778,31 @@ #define D_SCRIPT_UPLOAD "Upload" #define D_SCRIPT_UPLOAD_FILES "Upload files" +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + #endif // _LANGUAGE_ZH_TW_H_ diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index ab80c4cf6..150a200e0 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -70,6 +70,7 @@ #define WIFI_CONFIG_TOOL WIFI_RETRY // [WifiConfig] Default tool if wifi fails to connect (default option: 4 - WIFI_RETRY) // (WIFI_RESTART, WIFI_MANAGER, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY) // The configuration can be changed after first setup using WifiConfig 0, 2, 4, 5, 6 and 7. +#define WIFI_ARP_INTERVAL 0 // [SetOption41] Send gratuitous ARP interval #define WIFI_SCAN_AT_RESTART false // [SetOption56] Scan wifi network at restart for configured AP's #define WIFI_SCAN_REGULARLY false // [SetOption57] Scan wifi network every 44 minutes for configured AP's @@ -282,6 +283,7 @@ #define LIGHT_CHANNEL_MODE false // [SetOption68] Enable multi-channels PWM instead of Color PWM #define LIGHT_SLIDER_POWER false // [SetOption77] Do not power off if slider moved to far left #define LIGHT_ALEXA_CT_RANGE false // [SetOption82] Reduced CT range for Alexa +#define LIGHT_PWM_CT_MODE false // [SetOption92] Set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) a.k.a. module 48 mode // -- Energy -------------------------------------- #define ENERGY_VOLTAGE_ALWAYS false // [SetOption21] Enable show voltage even if powered off @@ -311,29 +313,29 @@ // -- Localization -------------------------------- // If non selected the default en-GB will be used -//#define MY_LANGUAGE bg-BG // Bulgarian in Bulgaria -//#define MY_LANGUAGE cs-CZ // Czech in Czech -//#define MY_LANGUAGE de-DE // German in Germany -//#define MY_LANGUAGE el-GR // Greek in Greece -//#define MY_LANGUAGE en-GB // English in Great Britain. Enabled by Default -//#define MY_LANGUAGE es-ES // Spanish in Spain -//#define MY_LANGUAGE fr-FR // French in France -//#define MY_LANGUAGE he-HE // Hebrew in Israel -//#define MY_LANGUAGE hu-HU // Hungarian in Hungary -//#define MY_LANGUAGE it-IT // Italian in Italy -//#define MY_LANGUAGE ko-KO // Korean in Korea -//#define MY_LANGUAGE nl-NL // Dutch in the Netherlands -//#define MY_LANGUAGE pl-PL // Polish in Poland -//#define MY_LANGUAGE pt-BR // Portuguese in Brazil -//#define MY_LANGUAGE pt-PT // Portuguese in Portugal -//#define MY_LANGUAGE ro-RO // Romanian in Romania -//#define MY_LANGUAGE ru-RU // Russian in Russia -//#define MY_LANGUAGE sk-SK // Slovak in Slovakia -//#define MY_LANGUAGE sv-SE // Swedish in Sweden -//#define MY_LANGUAGE tr-TR // Turkish in Turkey -//#define MY_LANGUAGE uk-UA // Ukrainian in Ukraine -//#define MY_LANGUAGE zh-CN // Chinese (Simplified) in China -//#define MY_LANGUAGE zh-TW // Chinese (Traditional) in Taiwan +//#define MY_LANGUAGE bg_BG // Bulgarian in Bulgaria +//#define MY_LANGUAGE cs_CZ // Czech in Czech +//#define MY_LANGUAGE de_DE // German in Germany +//#define MY_LANGUAGE el_GR // Greek in Greece +//#define MY_LANGUAGE en_GB // English in Great Britain. Enabled by Default +//#define MY_LANGUAGE es_ES // Spanish in Spain +//#define MY_LANGUAGE fr_FR // French in France +//#define MY_LANGUAGE he_HE // Hebrew in Israel +//#define MY_LANGUAGE hu_HU // Hungarian in Hungary +//#define MY_LANGUAGE it_IT // Italian in Italy +//#define MY_LANGUAGE ko_KO // Korean in Korea +//#define MY_LANGUAGE nl_NL // Dutch in the Netherlands +//#define MY_LANGUAGE pl_PL // Polish in Poland +//#define MY_LANGUAGE pt_BR // Portuguese in Brazil +//#define MY_LANGUAGE pt_PT // Portuguese in Portugal +//#define MY_LANGUAGE ro_RO // Romanian in Romania +//#define MY_LANGUAGE ru_RU // Russian in Russia +//#define MY_LANGUAGE sk_SK // Slovak in Slovakia +//#define MY_LANGUAGE sv_SE // Swedish in Sweden +//#define MY_LANGUAGE tr_TR // Turkish in Turkey +//#define MY_LANGUAGE uk_UA // Ukrainian in Ukraine +//#define MY_LANGUAGE zh_CN // Chinese (Simplified) in China +//#define MY_LANGUAGE zh_TW // Chinese (Traditional) in Taiwan // -- Wifi Config tools --------------------------- #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI @@ -389,9 +391,13 @@ #define USE_SUNRISE // Add support for Sunrise and sunset tools (+16k) #define SUNRISE_DAWN_ANGLE DAWN_NORMAL // Select desired Dawn Angle from (DAWN_NORMAL, DAWN_CIVIL, DAWN_NAUTIC, DAWN_ASTRONOMIC) +// -- Ping ---------------------------------------- +// #define USE_PING // Enable Ping command (+2k code) + // -- Rules or Script ---------------------------- -// Select none or only one of the below defines +// Select none or only one of the below defines USE_RULES or USE_SCRIPT #define USE_RULES // Add support for rules (+8k code) + #define USE_RULES_COMPRESSION // Compresses rules in Flash at about ~50% (+3.8k code) //#define USE_SCRIPT // Add support for script (+17k code) //#define USE_SCRIPT_FATFS 4 // Script: Add FAT FileSystem Support @@ -406,6 +412,7 @@ #define USE_SONOFF_SC // Add support for Sonoff Sc (+1k1 code) #define USE_TUYA_MCU // Add support for Tuya Serial MCU #define TUYA_DIMMER_ID 0 // Default dimmer Id +// #define USE_TUYA_TIME // Add support for Set Time in Tuya MCU #define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) #define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer (+2k code) #define USE_SONOFF_IFAN // Add support for Sonoff iFan02 and iFan03 (+2k code) @@ -416,9 +423,12 @@ #define USE_EXS_DIMMER // Add support for ES-Store WiFi Dimmer (+1k5 code) // #define EXS_MCU_CMNDS // Add command to send MCU commands (+0k8 code) //#define USE_HOTPLUG // Add support for sensor HotPlug -#define USE_DEVICE_GROUPS // Add support for device groups (+5k6 code) -#define USE_PWM_DIMMER // Add support for MJ-SD01/acenx/NTONPOWER PWM dimmers (+2k5 code) - #define USE_PWM_DIMMER_REMOTE // Add support for remote switches to PWM Dimmer, also adds device groups support (+1k1 code plus device groups size) +#define USE_DEVICE_GROUPS // Add support for device groups (+5k5 code) + #define DEVICE_GROUPS_ADDRESS 239,255,250,250 // Device groups multicast address + #define DEVICE_GROUPS_PORT 4447 // Device groups multicast port + #define USE_DEVICE_GROUPS_SEND // Add support for the DevGroupSend command (+0k6 code) +#define USE_PWM_DIMMER // Add support for MJ-SD01/acenx/NTONPOWER PWM dimmers (+2k2 code, DGR=0k4) + #define USE_PWM_DIMMER_REMOTE // Add support for remote switches to PWM Dimmer (requires USE_DEVICE_GROUPS) (+0k9 code) //#define USE_KEELOQ // Add support for Jarolift rollers by Keeloq algorithm (+4k5 code) #define USE_SONOFF_D1 // Add support for Sonoff D1 Dimmer (+0k7 code) @@ -432,6 +442,8 @@ #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller (+0k3 code) +#define USE_LIGHT_PALETTE // Add support for color palette (+0k7 code) +#define USE_DGR_LIGHT_SEQUENCE // Add support for device group light sequencing (requires USE_DEVICE_GROUPS) (+0k2 code) // -- Counter input ------------------------------- #define USE_COUNTER // Enable inputs as counter (+0k8 code) @@ -467,6 +479,10 @@ // #define USE_SI1145 // [I2cDriver19] Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code) // #define USE_LM75AD // [I2cDriver20] Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code) // #define USE_APDS9960 // [I2cDriver21] Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code) + #define USE_APDS9960_GESTURE // Enable APDS9960 Gesture feature (+2k code) + #define USE_APDS9960_PROXIMITY // Enable APDS9960 Proximity feature (>50 code) + #define USE_APDS9960_COLOR // Enable APDS9960 Color feature (+0.8k code) + #define USE_APDS9960_STARTMODE 0 // Default to enable Gesture mode // #define USE_MCP230xx // [I2cDriver22] Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code) // #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x26 - set according to your wired setup) // #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code) @@ -498,6 +514,8 @@ // #define WEMOS_MOTOR_V1_ADDR 0x30 // Default I2C address 0x30 // #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency // #define USE_HDC1080 // [I2cDriver45] Enable HDC1080 temperature/humidity sensor (I2C address 0x40) (+1k5 code) +// #define USE_IAQ // [I2cDriver46] Enable iAQ-core air quality sensor (I2C address 0x5a) (+0k6 code) +// #define USE_AS3935 // [I2cDriver48] Enable AS3935 Franklin Lightning Sensor (I2C address 0x03) (+5k4 code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 @@ -512,6 +530,8 @@ #define MTX_ADDRESS6 0x76 // [DisplayAddress6] I2C address of sixth 8x8 matrix module #define MTX_ADDRESS7 0x00 // [DisplayAddress7] I2C address of seventh 8x8 matrix module #define MTX_ADDRESS8 0x00 // [DisplayAddress8] I2C address of eigth 8x8 matrix module + #define USE_DISPLAY_SEVENSEG // [DisplayModel 11] [I2cDriver47] Enable sevenseg display (I2C 0x70-0x77) (<+11k code) + #define SEVENSEG_ADDRESS1 0x70 // [DisplayAddress1] I2C address of first sevenseg matrix module // #define USE_DISPLAY_SH1106 // [DisplayModel 7] [I2cDriver6] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D) #endif // USE_I2C @@ -556,8 +576,13 @@ //#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) //#define USE_GPS // Add support for GPS and NTP Server for becoming Stratus 1 Time Source (+3k1 code, +132 bytes RAM) // #define USE_FLOG // Add support for GPS logging in OTA's Flash (Experimental) (+2k9 code, +8 bytes RAM) -//#define USE_HM10 // Add support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code) +//#define USE_HM10 // (ESP8266 only) Add support for HM-10 as a BLE-bridge (+9k3 code) +//#define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) //#define USE_HRXL // Add support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7) +//#define USE_TASMOTA_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k6 code, 64 mem) + #define USE_TASMOTA_SLAVE_FLASH_SPEED 57600 // Usually 57600 for 3.3V variants and 115200 for 5V variants + #define USE_TASMOTA_SLAVE_SERIAL_SPEED 57600 // Depends on the sketch that is running on the Uno/Pro Mini +//#define USE_OPENTHERM // Add support for OpenTherm (+15k code) // -- Power monitoring sensors -------------------- #define USE_ENERGY_MARGIN_DETECTION // Add support for Energy Margin detection (+1k6 code) @@ -615,11 +640,13 @@ // -- Zigbee interface ---------------------------- //#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP (+49k code, +3k mem) #define USE_ZIGBEE_PANID 0x1A63 // arbitrary PAN ID for Zigbee network, must be unique in the home + // if PANID == 0xFFFF, then the device will act as a Zigbee router, the parameters below are ignored + // if PANID == 0xFFFE, then the device will act as a Zigbee end-device (non-router), the parameters below are ignored #define USE_ZIGBEE_EXTPANID 0xCCCCCCCCCCCCCCCCL // arbitrary extended PAN ID #define USE_ZIGBEE_CHANNEL 11 // Zigbee Channel (11-26) #define USE_ZIGBEE_PRECFGKEY_L 0x0F0D0B0907050301L // note: changing requires to re-pair all devices #define USE_ZIGBEE_PRECFGKEY_H 0x0D0C0A0806040200L // note: changing requires to re-pair all devices - #define USE_ZIGBEE_PERMIT_JOIN false // don't allow joining by default + #define USE_ZIGBEE_COALESCE_ATTR_TIMER 350 // timer to coalesce attribute values (in ms) // -- Other sensors/drivers ----------------------- @@ -634,6 +661,8 @@ //#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k6/0k8 code) //#define USE_TX23_WIND_SENSOR // Add support for La Crosse TX23 anemometer (+2k7/1k code) +//#define USE_WINDMETER // Add support for analog anemometer (+2k2 code) + //#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) //#define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) @@ -643,9 +672,33 @@ //#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) //#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) -//#define USE_TASMOTA_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k6 code, 64 mem) - #define USE_TASMOTA_SLAVE_FLASH_SPEED 57600 // Usually 57600 for 3.3V variants and 115200 for 5V variants - #define USE_TASMOTA_SLAVE_SERIAL_SPEED 57600 // Depends on the sketch that is running on the Uno/Pro Mini +// -- Thermostat control ---------------------------- +//#define USE_THERMOSTAT // Add support for Thermostat + #define THERMOSTAT_CONTROLLER_OUTPUTS 1 // Number of outputs to be controlled independently + #define THERMOSTAT_SENSOR_NAME "DS18B20" // Name of the local sensor to be used + #define THERMOSTAT_RELAY_NUMBER 1 // Default output relay number for the first controller (+i for following ones) + #define THERMOSTAT_SWITCH_NUMBER 1 // Default input switch number for the first controller (+i for following ones) + #define THERMOSTAT_TIME_ALLOW_RAMPUP 300 // Default time after last target update to allow ramp-up controller phase in minutes + #define THERMOSTAT_TIME_RAMPUP_MAX 960 // Default time maximum ramp-up controller duration in minutes + #define THERMOSTAT_TIME_RAMPUP_CYCLE 30 // Default time ramp-up cycle in minutes + #define THERMOSTAT_TIME_SENS_LOST 30 // Maximum time w/o sensor update to set it as lost in minutes + #define THERMOSTAT_TEMP_SENS_NUMBER 1 // Default temperature sensor number + #define THERMOSTAT_TIME_MANUAL_TO_AUTO 60 // Default time without input switch active to change from manual to automatic in minutes + #define THERMOSTAT_TIME_RESET 12000 // Default reset time of the PI controller in seconds + #define THERMOSTAT_TIME_PI_CYCLE 30 // Default cycle time for the thermostat controller in minutes + #define THERMOSTAT_TIME_MAX_ACTION 20 // Default maximum thermostat time per cycle in minutes + #define THERMOSTAT_TIME_MIN_ACTION 4 // Default minimum thermostat time per cycle in minutes + #define THERMOSTAT_TIME_MIN_TURNOFF_ACTION 3 // Default minimum turnoff time in minutes, below it the thermostat will be held on + #define THERMOSTAT_PROP_BAND 4 // Default proportional band of the PI controller in degrees celsius + #define THERMOSTAT_TEMP_RESET_ANTI_WINDUP 8 // Default range where reset antiwindup is disabled, in tenths of degrees celsius + #define THERMOSTAT_TEMP_HYSTERESIS 1 // Default range hysteresis for temperature PI controller, in tenths of degrees celsius + #define THERMOSTAT_TEMP_FROST_PROTECT 40 // Default minimum temperature for frost protection, in tenths of degrees celsius + #define THERMOSTAT_TEMP_RAMPUP_DELTA_IN 4 // Default minimum delta temperature to target to get into rampup mode, in tenths of degrees celsius + #define THERMOSTAT_TEMP_RAMPUP_DELTA_OUT 2 // Default minimum delta temperature to target to get out of the rampup mode, in tenths of degrees celsius + #define THERMOSTAT_TEMP_PI_RAMPUP_ACC_E 200 // Default accumulated error when switching from ramp-up controller to PI in hundreths of degrees celsius + #define THERMOSTAT_TIME_OUTPUT_DELAY 180 // Default output delay between state change and real actuation event (f.i. valve open/closed) + #define THERMOSTAT_TEMP_INIT 180 // Default init target temperature for the thermostat controller + #define THERMOSTAT_TIME_MAX_OUTPUT_INCONSIST 3 // Default maximum time where the input and the outpus shall differ (for diagnostic) in minutes // -- End of general directives ------------------- @@ -660,7 +713,7 @@ /*********************************************************************************************\ * Optional firmware configurations - * Select none or just one for optional features and sensors as configured in tasmota_post.h + * Select none or just one for optional features and sensors as configured in tasmota_configurations.h * See RELEASENOTES.md for selected features \*********************************************************************************************/ @@ -684,4 +737,8 @@ #error "Select either USE_DISCOVERY or USE_MQTT_AWS_IOT, mDNS takes too much code space and is not needed for AWS IoT" #endif +#if defined(USE_RULES) && defined(USE_SCRIPT) + #error "Select either USE_RULES or USE_SCRIPT. They can't both be used at the same time" +#endif + #endif // _MY_USER_CONFIG_H_ diff --git a/tasmota/sendemail.h b/tasmota/sendemail.h index 2422ad91f..d95ba9311 100644 --- a/tasmota/sendemail.h +++ b/tasmota/sendemail.h @@ -8,7 +8,11 @@ #include //#include +#ifdef ESP8266 #include "WiFiClientSecureLightBearSSL.h" +#else +#include +#endif class SendEmail { @@ -20,18 +24,24 @@ class SendEmail const int timeout; const bool ssl; const int auth_used; +#ifdef ESP8266 #if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) WiFiClient* client; #else // use bear ssl BearSSL::WiFiClientSecure_light *client; #endif +#else + WiFiClient *client; +#endif + String readClient(); void a3_to_a4(unsigned char * a4, unsigned char * a3); int base64_encode(char *output, const char *input, int inputLen); public: SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used); bool send(const String& from, const String& to, const String& subject, const char *msg); + void send_message_txt(char *msg); ~SendEmail() {client->stop(); delete client;} }; diff --git a/tasmota/sendemail.ino b/tasmota/sendemail.ino index 70571b5d2..0a4c4de0e 100644 --- a/tasmota/sendemail.ino +++ b/tasmota/sendemail.ino @@ -1,5 +1,7 @@ #ifdef USE_SENDMAIL +#ifndef USE_ESP32MAIL + #include "sendemail.h" // enable serial debugging @@ -26,6 +28,8 @@ #define SEND_MAIL_MINRAM 12*1024 #endif +void script_send_email_body(void(*func)(char *)); + #define xPSTR(a) a uint16_t SendMail(char *buffer) { @@ -174,12 +178,19 @@ exit: return status; } -void script_send_email_body(BearSSL::WiFiClientSecure_light *client); +#ifdef ESP8266 +WiFiClient *g_client; SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used) : host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), auth_used(auth_used), client(new BearSSL::WiFiClientSecure_light(1024,1024)) { } +#else +WiFiClient *g_client; +SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used) : + host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), auth_used(auth_used), client(new WiFiClientSecure()) { +} +#endif String SendEmail::readClient() { delay(0); @@ -341,7 +352,8 @@ String buffer; #ifdef USE_SCRIPT if (*msg=='*' && *(msg+1)==0) { - script_send_email_body(client); + g_client=client; + script_send_email_body(xsend_message_txt); } else { client->println(msg); } @@ -365,5 +377,289 @@ exit: return status; } +void xsend_message_txt(char *msg) { + g_client->println(msg); +} +#else + +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +//To receive Email for Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + +#include "ESP32_MailClient.h" +#include "SD.h" + +//For demo only +//#include "image.h" + +#ifndef SEND_MAIL32_MINRAM +#define SEND_MAIL32_MINRAM 30*1024 +#endif + +void script_send_email_body(void(*func)(char *)); + +#define xPSTR(a) a +//The Email Sending data object contains config and data to send +SMTPData smtpData; + +//Callback function to get the Email sending status +//void sendCallback(SendStatus info); +//#define DEBUG_EMAIL_PORT + +uint16_t SendMail(char *buffer) { + char *params,*oparams; + const char *mserv; + uint16_t port; + const char *user; + const char *pstr; + const char *passwd; + const char *from; + const char *to; + const char *subject; + const char *cmd; + uint16_t status=1; + uint16_t blen; + char *endcmd; + + // return if not enough memory + uint32_t mem=ESP.getFreeHeap(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("heap: %d"),mem); + if (mem"); + + //Set the storage types to read the attach files (SD is default) + //smtpData.setFileStorageType(MailClientStorageType::SPIFFS); +#if defined (USE_SCRIPT) && defined(USE_SCRIPT_FATFS) + smtpData.setFileStorageType(MailClientStorageType::SD); +#endif + //smtpData.setSendCallback(sendCallback); + + //Start sending Email, can be set callback function to track the status + if (!MailClient.sendMail(smtpData)) { + //Serial.println("Error sending Email, " + MailClient.smtpErrorReason()); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Error sending Email, %s"), MailClient.smtpErrorReason()); + + } else { + status=0; + } + //Clear all data from Email object to free memory + smtpData.empty(); + + exit: + if (oparams) free(oparams); + return status; +} + + +void send_message_txt(char *txt) { + if (*txt=='&') { + txt++; + smtpData.addAttachFile(txt); + } else if (*txt=='$') { + txt++; +#if defined(ESP32) && defined(USE_WEBCAM) + uint32_t cnt; + uint8_t *buff; + uint32_t len,picmax; + picmax=WcGetPicstore(-1,0); + cnt=*txt&7; + if (cnt<1 || cnt>picmax) cnt=1; + len=WcGetPicstore(cnt-1,&buff); + if (len) { + char str[12]; + sprintf(str,"img_%1d.jpg",cnt+1); + smtpData.addAttachData(str, "image/jpg",buff,len); + } +#endif + } else { + smtpData.addMessage(txt); + } +} + +/* +//Callback function to get the Email sending status +void sendCallback(SendStatus msg) +{ + //Print the current status + Serial.println(msg.info()); + + //Do something when complete + if (msg.success()) + { + Serial.println("----------------"); + } +} +*/ +#endif + #endif // USE_SENDMAIL diff --git a/tasmota/settings.h b/tasmota/settings.h index 737ce8a82..edd3de523 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -86,7 +86,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t energy_weekend : 1; // bit 20 (v6.6.0.8) - CMND_TARIFF uint32_t dds2382_model : 1; // bit 21 (v6.6.0.14) - SetOption71 - Select different Modbus registers for Active Energy (#6531) uint32_t hardware_energy_total : 1; // bit 22 (v6.6.0.15) - SetOption72 - Enable hardware energy total counter as reference (#6561) - uint32_t ex_cors_enabled : 1; // bit 23 (v7.0.0.1) - SetOption73 - Enable HTTP CORS + uint32_t mqtt_buttons : 1; // bit 23 (v8.2.0.3) - SetOption73 - Detach buttons from relays and enable MQTT action state for multipress uint32_t ds18x20_internal_pullup : 1; // bit 24 (v7.0.0.1) - SetOption74 - Enable internal pullup for single DS18x20 sensor uint32_t grouptopic_mode : 1; // bit 25 (v7.0.0.1) - SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1) uint32_t bootcount_update : 1; // bit 26 (v7.0.0.4) - SetOption76 - Enable incrementing bootcount when deepsleep is enabled @@ -109,10 +109,10 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t powered_off_led : 1; // bit 5 (v8.1.0.9) - SetOption87 - PWM Dimmer Turn red LED on when powered off uint32_t remote_device_mode : 1; // bit 6 (v8.1.0.9) - SetOption88 - PWM Dimmer Buttons control remote devices uint32_t zigbee_distinct_topics : 1; // bit 7 (v8.1.0.10) - SetOption89 - Distinct MQTT topics per device for Zigbee (#7835) - uint32_t spare08 : 1; - uint32_t spare09 : 1; - uint32_t spare10 : 1; - uint32_t spare11 : 1; + uint32_t only_json_message : 1; // bit 8 (v8.2.0.3) - SetOption90 - Disable non-json MQTT response + uint32_t fade_at_startup : 1; // bit 9 (v8.2.0.3) - SetOption91 - Enable light fading at start/power on + uint32_t pwm_ct_mode : 1; // bit 10 (v8.2.0.4) - SetOption92 - Set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) + uint32_t compress_rules_cpu : 1; // bit 11 (v8.2.0.6) - SetOption93 - Keep uncompressed rules in memory to avoid CPU load of uncompressing at each tick uint32_t spare12 : 1; uint32_t spare13 : 1; uint32_t spare14 : 1; @@ -182,6 +182,35 @@ typedef union { }; } Timer; +typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... + uint32_t data; + struct { + uint32_t stream : 1; + uint32_t mirror : 1; + uint32_t flip : 1; + uint32_t spare3 : 1; + uint32_t spare4 : 1; + uint32_t spare5 : 1; + uint32_t spare6 : 1; + uint32_t spare7 : 1; + uint32_t spare8 : 1; + uint32_t spare9 : 1; + uint32_t spare10 : 1; + uint32_t spare11 : 1; + uint32_t spare12 : 1; + uint32_t spare13 : 1; + uint32_t spare14 : 1; + uint32_t spare15 : 1; + uint32_t spare16 : 1; + uint32_t spare17 : 1; + uint32_t spare18 : 1; + uint32_t contrast : 3; + uint32_t brightness : 3; + uint32_t saturation : 3; + uint32_t resolution : 4; + }; +} WebCamCfg; + typedef union { uint16_t data; struct { @@ -205,13 +234,36 @@ typedef union { uint8_t spare1 : 1; uint8_t spare2 : 1; uint8_t spare3 : 1; - uint8_t spare4 : 1; - uint8_t spare5 : 1; + uint8_t bh1750_resolution : 2; // Sensor10 1,2,3 uint8_t hx711_json_weight_change : 1; // Sensor34 8,x - Enable JSON message on weight change uint8_t mhz19b_abc_disable : 1; // Disable ABC (Automatic Baseline Correction for MHZ19(B) (0 = Enabled (default), 1 = Disabled with Sensor15 command) }; } SensorCfg1; +typedef union { + uint8_t data; + struct { + uint8_t nf_autotune : 1; // Autotune the NF Noise Level + uint8_t dist_autotune : 1; // Autotune Disturber on/off + uint8_t nf_autotune_both : 1; // Autotune over both Areas: INDOORS/OUDOORS + uint8_t mqtt_only_Light_Event : 1; // mqtt only if lightning Irq + uint8_t spare4 : 1; + uint8_t spare5 : 1; + uint8_t spare6 : 1; + uint8_t spare7 : 1; + }; +} As3935IntCfg; + +typedef union { + uint16_t data; + struct { + uint16_t nf_autotune_time : 4; // NF Noise Autotune Time + uint16_t dist_autotune_time : 4; // Disturber Autotune Time + uint16_t nf_autotune_min : 4; // Min Stages + uint16_t spare3 : 4; + }; +} As3935Param; + typedef struct { uint32_t usage1_kWhtotal; uint32_t usage2_kWhtotal; @@ -230,7 +282,7 @@ typedef struct { const uint32_t settings_text_size = 699; // Settings.text_pool[size] = Settings.display_model (2D2) - Settings.text_pool (017) const uint8_t MAX_TUYA_FUNCTIONS = 16; -struct SYSCFG { +struct { uint16_t cfg_holder; // 000 v6 header uint16_t cfg_size; // 002 unsigned long save_flag; // 004 @@ -241,7 +293,7 @@ struct SYSCFG { int16_t save_data; // 014 int8_t timezone; // 016 - // Start of char array storing all parameter strings + // Start of char array storing all parameter strings ******** char text_pool[101]; // 017 - was ota_url[101] - size is settings_text_size @@ -277,7 +329,7 @@ struct SYSCFG { char ex_button_topic[33]; // 290 char ex_mqtt_grptopic[33]; // 2B1 - // End of single char array of 698 chars max + // End of single char array of 698 chars max **************** uint8_t display_model; // 2D2 uint8_t display_mode; // 2D3 @@ -335,8 +387,19 @@ struct SYSCFG { SysBitfield3 flag3; // 3A0 uint8_t switchmode[MAX_SWITCHES]; // 3A4 (6.0.0b - moved from 0x4CA) +#ifdef ESP8266 char ex_friendlyname[4][33]; // 3AC char ex_switch_topic[33]; // 430 +#else // ESP32 + myio my_gp; // 3AC - 2 x 40 bytes (ESP32) + mytmplt user_template; // 3FC - 2 x 37 bytes (ESP32) + + uint8_t free_esp32_446[6]; // 446 + + WebCamCfg webcam_config; // 44C + + uint8_t free_esp32_450[1]; // 450 +#endif // ESP8266 - ESP32 char serial_delimiter; // 451 uint8_t seriallog_level; // 452 @@ -346,7 +409,13 @@ struct SYSCFG { uint8_t module; // 474 uint8_t ws_color[4][3]; // 475 uint8_t ws_width[3]; // 481 - myio my_gp; // 484 + +#ifdef ESP8266 + myio my_gp; // 484 - 17 bytes (ESP8266) +#else // ESP32 + uint8_t free_esp32_484[17]; // 484 +#endif // ESP8266 - ESP32 + uint8_t my_adc0; // 495 uint16_t light_pixels; // 496 uint8_t light_color[5]; // 498 @@ -397,7 +466,15 @@ struct SYSCFG { uint16_t mcp230xx_int_timer; // 718 uint8_t rgbwwTable[5]; // 71A uint8_t user_template_base; // 71F - mytmplt user_template; // 720 29 bytes + + char user_template_name[15]; // 720 15 bytes - Backward compatibility since v8.2.0.3 + +#ifdef ESP8266 + mytmplt user_template; // 72F 14 bytes (ESP8266) +#else // ESP32 + uint8_t free_esp32_72f[14]; // 72F +#endif // ESP8266 - ESP32 + uint8_t novasds_startingoffset; // 73D uint8_t web_color[18][3]; // 73E uint16_t display_width; // 774 @@ -435,6 +512,12 @@ struct SYSCFG { uint8_t shutter_position[MAX_SHUTTERS]; // E80 uint8_t shutter_startrelay[MAX_SHUTTERS]; // E84 uint8_t pcf8574_config[MAX_PCF8574]; // E88 + uint8_t ot_hot_water_setpoint; // E8C + uint8_t ot_boiler_setpoint; // E8D + uint8_t ot_flags; // E8E + + uint8_t free_e8f[1]; // E8F + uint16_t dimmer_hw_min; // E90 uint16_t dimmer_hw_max; // E92 uint32_t deepsleep; // E94 @@ -468,9 +551,30 @@ struct SYSCFG { uint8_t bri_preset_low; // F06 uint8_t bri_preset_high; // F07 int8_t hum_comp; // F08 + uint8_t wifi_channel; // F09 + uint8_t wifi_bssid[6]; // F0A + uint8_t as3935_sensor_cfg[5]; // F10 + As3935IntCfg as3935_functions; // F15 + As3935Param as3935_parameter; // F16 + uint64_t zb_ext_panid; // F18 + uint64_t zb_precfgkey_l; // F20 + uint64_t zb_precfgkey_h; // F28 + uint16_t zb_pan_id; // F30 + uint8_t zb_channel; // F32 + uint8_t zb_free_byte; // F33 + uint16_t pms_wake_interval; // F34 + uint8_t config_version; // F36 + uint8_t windmeter_pulses_x_rot; // F37 + uint16_t windmeter_radius; // F38 + uint16_t windmeter_pulse_debounce; // F3A + int16_t windmeter_speed_factor; // F3C + uint8_t windmeter_tele_pchange; // F3E - uint8_t free_f09[179]; // F09 + uint8_t free_f3f[121]; // F3F - Decrement if adding new Setting variables just above and below + // Only 32 bit boundary variables below + uint16_t pulse_counter_debounce_low; // FB8 + uint16_t pulse_counter_debounce_high; // FBA uint32_t keeloq_master_msb; // FBC uint32_t keeloq_master_lsb; // FC0 uint32_t keeloq_serial; // FC4 @@ -485,13 +589,17 @@ struct SYSCFG { uint32_t cfg_crc32; // FFC } Settings; -struct RTCRBT { +typedef struct { uint16_t valid; // 280 (RTC memory offset 100 - sizeof(RTCRBT)) uint8_t fast_reboot_count; // 282 uint8_t free_003[1]; // 283 -} RtcReboot; +} TRtcReboot; +TRtcReboot RtcReboot; +#ifdef ESP32 +RTC_NOINIT_ATTR TRtcReboot RtcDataReboot; +#endif -struct RTCMEM { +typedef struct { uint16_t valid; // 290 (RTC memory offset 100) uint8_t oswatch_blocked_loop; // 292 uint8_t ota_loader; // 293 @@ -507,7 +615,11 @@ struct RTCMEM { uint8_t free_022[22]; // 2D6 // 2EC - 2FF free locations -} RtcSettings; +} TRtcSettings; +TRtcSettings RtcSettings; +#ifdef ESP32 +RTC_NOINIT_ATTR TRtcSettings RtcDataSettings; +#endif struct TIME_T { uint8_t second; diff --git a/tasmota/settings.ino b/tasmota/settings.ino index 5ebf970d4..de890ee41 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -17,166 +17,6 @@ along with this program. If not, see . */ -#ifndef DOMOTICZ_UPDATE_TIMER -#define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds) (Optional) -#endif - -#ifndef EMULATION -#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE) -#endif - -#ifndef MTX_ADDRESS1 // Add Display Support for up to eigth Matrices -#define MTX_ADDRESS1 0 -#endif -#ifndef MTX_ADDRESS2 -#define MTX_ADDRESS2 0 -#endif -#ifndef MTX_ADDRESS3 -#define MTX_ADDRESS3 0 -#endif -#ifndef MTX_ADDRESS4 -#define MTX_ADDRESS4 0 -#endif -#ifndef MTX_ADDRESS5 -#define MTX_ADDRESS5 0 -#endif -#ifndef MTX_ADDRESS6 -#define MTX_ADDRESS6 0 -#endif -#ifndef MTX_ADDRESS7 -#define MTX_ADDRESS7 0 -#endif -#ifndef MTX_ADDRESS8 -#define MTX_ADDRESS8 0 -#endif - -#ifndef HOME_ASSISTANT_DISCOVERY_ENABLE -#define HOME_ASSISTANT_DISCOVERY_ENABLE 0 -#endif - -#ifndef LATITUDE -#define LATITUDE 48.858360 // [Latitude] Your location to be used with sunrise and sunset -#endif -#ifndef LONGITUDE -#define LONGITUDE 2.294442 // [Longitude] Your location to be used with sunrise and sunset -#endif - -#ifndef WORKING_PERIOD -#define WORKING_PERIOD 5 // Working period of the SDS Sensor, Takes a reading every X Minutes -#endif - -#ifndef COLOR_TEXT -#define COLOR_TEXT "#000" // Global text color - Black -#endif -#ifndef COLOR_BACKGROUND -#define COLOR_BACKGROUND "#fff" // Global background color - White -#endif -#ifndef COLOR_FORM -#define COLOR_FORM "#f2f2f2" // Form background color - Greyish -#endif -#ifndef COLOR_INPUT_TEXT -#define COLOR_INPUT_TEXT "#000" // Input text color - Black -#endif -#ifndef COLOR_INPUT -#define COLOR_INPUT "#fff" // Input background color - White -#endif -#ifndef COLOR_CONSOLE_TEXT -#define COLOR_CONSOLE_TEXT "#000" // Console text color - Black -#endif -#ifndef COLOR_CONSOLE -#define COLOR_CONSOLE "#fff" // Console background color - White -#endif -#ifndef COLOR_TEXT_WARNING -#define COLOR_TEXT_WARNING "#f00" // Warning text color - Red -#endif -#ifndef COLOR_TEXT_SUCCESS -#define COLOR_TEXT_SUCCESS "#008000" // Success text color - Green -#endif -#ifndef COLOR_BUTTON_TEXT -#define COLOR_BUTTON_TEXT "#fff" // Button text color - White -#endif -#ifndef COLOR_BUTTON -#define COLOR_BUTTON "#1fa3ec" // Button color - Blueish -#endif -#ifndef COLOR_BUTTON_HOVER -#define COLOR_BUTTON_HOVER "#0e70a4" // Button color when hovered over - Darker blueish -#endif -#ifndef COLOR_BUTTON_RESET -#define COLOR_BUTTON_RESET "#d43535" // Restart/Reset/Delete button color - Redish -#endif -#ifndef COLOR_BUTTON_RESET_HOVER -#define COLOR_BUTTON_RESET_HOVER "#931f1f" // Restart/Reset/Delete button color when hovered over - Darker redish -#endif -#ifndef COLOR_BUTTON_SAVE -#define COLOR_BUTTON_SAVE "#47c266" // Save button color - Greenish -#endif -#ifndef COLOR_BUTTON_SAVE_HOVER -#define COLOR_BUTTON_SAVE_HOVER "#5aaf6f" // Save button color when hovered over - Darker greenish -#endif -#ifndef COLOR_TIMER_TAB_TEXT -#define COLOR_TIMER_TAB_TEXT "#fff" // Config timer tab text color - White -#endif -#ifndef COLOR_TIMER_TAB_BACKGROUND -#define COLOR_TIMER_TAB_BACKGROUND "#999" // Config timer tab background color - Light grey -#endif -#ifndef COLOR_TITLE_TEXT -#define COLOR_TITLE_TEXT COLOR_TEXT // Title text color defaults to global text color either dark or light -#endif -#ifndef IR_RCV_MIN_UNKNOWN_SIZE -#define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6, max 255) -#endif -#ifndef ENERGY_OVERTEMP -#define ENERGY_OVERTEMP 90 // Overtemp in Celsius -#endif -#ifndef DEFAULT_DIMMER_MAX -#define DEFAULT_DIMMER_MAX 100 -#endif -#ifndef DEFAULT_DIMMER_MIN -#define DEFAULT_DIMMER_MIN 0 -#endif -#ifndef DEFAULT_LIGHT_DIMMER -#define DEFAULT_LIGHT_DIMMER 10 -#endif -#ifndef DEFAULT_LIGHT_COMPONENT -#define DEFAULT_LIGHT_COMPONENT 255 -#endif -#ifndef CORS_ENABLED_ALL -#define CORS_ENABLED_ALL "*" -#endif - - -enum WebColors { - COL_TEXT, COL_BACKGROUND, COL_FORM, - COL_INPUT_TEXT, COL_INPUT, COL_CONSOLE_TEXT, COL_CONSOLE, - COL_TEXT_WARNING, COL_TEXT_SUCCESS, - COL_BUTTON_TEXT, COL_BUTTON, COL_BUTTON_HOVER, COL_BUTTON_RESET, COL_BUTTON_RESET_HOVER, COL_BUTTON_SAVE, COL_BUTTON_SAVE_HOVER, - COL_TIMER_TAB_TEXT, COL_TIMER_TAB_BACKGROUND, COL_TITLE, - COL_LAST }; - -const char kWebColors[] PROGMEM = - COLOR_TEXT "|" COLOR_BACKGROUND "|" COLOR_FORM "|" - COLOR_INPUT_TEXT "|" COLOR_INPUT "|" COLOR_CONSOLE_TEXT "|" COLOR_CONSOLE "|" - COLOR_TEXT_WARNING "|" COLOR_TEXT_SUCCESS "|" - COLOR_BUTTON_TEXT "|" COLOR_BUTTON "|" COLOR_BUTTON_HOVER "|" COLOR_BUTTON_RESET "|" COLOR_BUTTON_RESET_HOVER "|" COLOR_BUTTON_SAVE "|" COLOR_BUTTON_SAVE_HOVER "|" - COLOR_TIMER_TAB_TEXT "|" COLOR_TIMER_TAB_BACKGROUND "|" COLOR_TITLE_TEXT; - -enum TasmotaSerialConfig { - TS_SERIAL_5N1, TS_SERIAL_6N1, TS_SERIAL_7N1, TS_SERIAL_8N1, - TS_SERIAL_5N2, TS_SERIAL_6N2, TS_SERIAL_7N2, TS_SERIAL_8N2, - TS_SERIAL_5E1, TS_SERIAL_6E1, TS_SERIAL_7E1, TS_SERIAL_8E1, - TS_SERIAL_5E2, TS_SERIAL_6E2, TS_SERIAL_7E2, TS_SERIAL_8E2, - TS_SERIAL_5O1, TS_SERIAL_6O1, TS_SERIAL_7O1, TS_SERIAL_8O1, - TS_SERIAL_5O2, TS_SERIAL_6O2, TS_SERIAL_7O2, TS_SERIAL_8O2 }; - -const uint8_t kTasmotaSerialConfig[] PROGMEM = { - SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1, - SERIAL_5N2, SERIAL_6N2, SERIAL_7N2, SERIAL_8N2, - SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_8E1, - SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, - SERIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1, - SERIAL_5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2 -}; - /*********************************************************************************************\ * RTC memory \*********************************************************************************************/ @@ -190,7 +30,7 @@ uint32_t GetRtcSettingsCrc(void) uint32_t crc = 0; uint8_t *bytes = (uint8_t*)&RtcSettings; - for (uint32_t i = 0; i < sizeof(RTCMEM); i++) { + for (uint32_t i = 0; i < sizeof(RtcSettings); i++) { crc += bytes[i]*(i+1); } return crc; @@ -200,16 +40,24 @@ void RtcSettingsSave(void) { if (GetRtcSettingsCrc() != rtc_settings_crc) { RtcSettings.valid = RTC_MEM_VALID; - ESP.rtcUserMemoryWrite(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); +#ifdef ESP8266 + ESP.rtcUserMemoryWrite(100, (uint32_t*)&RtcSettings, sizeof(RtcSettings)); +#else + RtcDataSettings = RtcSettings; +#endif rtc_settings_crc = GetRtcSettingsCrc(); } } void RtcSettingsLoad(void) { - ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); // 0x290 +#ifdef ESP8266 + ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RtcSettings)); // 0x290 +#else + RtcSettings = RtcDataSettings; +#endif if (RtcSettings.valid != RTC_MEM_VALID) { - memset(&RtcSettings, 0, sizeof(RTCMEM)); + memset(&RtcSettings, 0, sizeof(RtcSettings)); RtcSettings.valid = RTC_MEM_VALID; RtcSettings.energy_kWhtoday = Settings.energy_kWhtoday; RtcSettings.energy_kWhtotal = Settings.energy_kWhtotal; @@ -237,7 +85,7 @@ uint32_t GetRtcRebootCrc(void) uint32_t crc = 0; uint8_t *bytes = (uint8_t*)&RtcReboot; - for (uint32_t i = 0; i < sizeof(RTCRBT); i++) { + for (uint32_t i = 0; i < sizeof(RtcReboot); i++) { crc += bytes[i]*(i+1); } return crc; @@ -247,7 +95,11 @@ void RtcRebootSave(void) { if (GetRtcRebootCrc() != rtc_reboot_crc) { RtcReboot.valid = RTC_MEM_VALID; - ESP.rtcUserMemoryWrite(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); +#ifdef ESP8266 + ESP.rtcUserMemoryWrite(100 - sizeof(RtcReboot), (uint32_t*)&RtcReboot, sizeof(RtcReboot)); +#else + RtcDataReboot = RtcReboot; +#endif rtc_reboot_crc = GetRtcRebootCrc(); } } @@ -260,9 +112,13 @@ void RtcRebootReset(void) void RtcRebootLoad(void) { - ESP.rtcUserMemoryRead(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); // 0x280 +#ifdef ESP8266 + ESP.rtcUserMemoryRead(100 - sizeof(RtcReboot), (uint32_t*)&RtcReboot, sizeof(RtcReboot)); // 0x280 +#else + RtcReboot = RtcDataReboot; +#endif if (RtcReboot.valid != RTC_MEM_VALID) { - memset(&RtcReboot, 0, sizeof(RTCRBT)); + memset(&RtcReboot, 0, sizeof(RtcReboot)); RtcReboot.valid = RTC_MEM_VALID; // RtcReboot.fast_reboot_count = 0; // Explicit by memset RtcRebootSave(); @@ -301,6 +157,8 @@ extern "C" { } #include "eboot_command.h" +#ifdef ESP8266 + #if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) extern "C" uint32_t _SPIFFS_end; @@ -328,6 +186,9 @@ const uint32_t SPIFFS_END = ((uint32_t)&_FS_end - 0x40200000) / SPI_FLASH_SEC_SI // Version 4.2 config = eeprom area const uint32_t SETTINGS_LOCATION = SPIFFS_END; // No need for SPIFFS as it uses EEPROM area + +#endif // ESP8266 + // Version 5.2 allow for more flash space const uint8_t CFG_ROTATES = 8; // Number of flash sectors used (handles uploads) @@ -341,6 +202,7 @@ uint8_t *settings_buffer = nullptr; */ void SetFlashModeDout(void) { +#ifdef ESP8266 uint8_t *_buffer; uint32_t address; @@ -358,10 +220,13 @@ void SetFlashModeDout(void) } } delete[] _buffer; +#endif // ESP8266 } bool VersionCompatible(void) { +#ifdef ESP8266 + if (Settings.flag3.compatibility_check) { return true; } @@ -404,6 +269,8 @@ bool VersionCompatible(void) return false; } +#endif // ESP8266 + return true; } @@ -438,7 +305,7 @@ uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size) uint16_t GetSettingsCrc(void) { // Fix miscalculation if previous Settings was 3584 and current Settings is 4096 between 0x06060007 and 0x0606000A - uint32_t size = ((Settings.version < 0x06060007) || (Settings.version > 0x0606000A)) ? 3584 : sizeof(SYSCFG); + uint32_t size = ((Settings.version < 0x06060007) || (Settings.version > 0x0606000A)) ? 3584 : sizeof(Settings); return GetCfgCrc16((uint8_t*)&Settings, size); } @@ -458,7 +325,7 @@ uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size) uint32_t GetSettingsCrc32(void) { - return GetCfgCrc32((uint8_t*)&Settings, sizeof(SYSCFG) -4); // Skip crc32 + return GetCfgCrc32((uint8_t*)&Settings, sizeof(Settings) -4); // Skip crc32 } void SettingsSaveAll(void) @@ -479,12 +346,17 @@ void SettingsSaveAll(void) void UpdateQuickPowerCycle(bool update) { - if (Settings.flag3.fast_power_cycle_disable) { return; } +#ifndef FIRMWARE_MINIMAL + if (Settings.flag3.fast_power_cycle_disable) { return; } // SetOption65 - Disable fast power cycle detection for device reset uint32_t pc_register; uint32_t pc_location = SETTINGS_LOCATION - CFG_ROTATES; +#ifdef ESP8266 ESP.flashRead(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); +#else // ESP32 + QPCRead(&pc_register, sizeof(pc_register)); +#endif // ESP8266 - ESP32 if (update && ((pc_register & 0xFFFFFFF0) == 0xFFA55AB0)) { uint32_t counter = ((pc_register & 0xF) << 1) & 0xF; if (0 == counter) { // 4 power cycles in a row @@ -492,18 +364,27 @@ void UpdateQuickPowerCycle(bool update) EspRestart(); // And restart } else { pc_register = 0xFFA55AB0 | counter; +#ifdef ESP8266 ESP.flashWrite(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); +#else // ESP32 + QPCWrite(&pc_register, sizeof(pc_register)); +#endif // ESP8266 - ESP32 AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Flag %02X"), counter); } } else if (pc_register != 0xFFA55ABF) { pc_register = 0xFFA55ABF; +#ifdef ESP8266 // Assume flash is default all ones and setting a bit to zero does not need an erase if (ESP.flashEraseSector(pc_location)) { ESP.flashWrite(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); } +#else // ESP32 + QPCWrite(&pc_register, sizeof(pc_register)); +#endif // ESP8266 - ESP32 AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Reset")); } +#endif // FIRMWARE_MINIMAL } /*********************************************************************************************\ @@ -526,9 +407,9 @@ bool SettingsUpdateText(uint32_t index, const char* replace_me) } // Make a copy first in case we use source from Settings.text - uint32_t replace_len = strlen(replace_me); + uint32_t replace_len = strlen_P(replace_me); char replace[replace_len +1]; - memcpy(replace, replace_me, sizeof(replace)); + memcpy_P(replace, replace_me, sizeof(replace)); uint32_t start_pos = 0; uint32_t end_pos = 0; @@ -589,6 +470,12 @@ char* SettingsText(uint32_t index) * Config Save - Save parameters to Flash ONLY if any parameter has changed \*********************************************************************************************/ +void UpdateBackwardCompatibility(void) +{ + // Perform updates for backward compatibility + strlcpy(Settings.user_template_name, SettingsText(SET_TEMPLATE_NAME), sizeof(Settings.user_template_name)); +} + uint32_t GetSettingsAddress(void) { return settings_location * SPI_FLASH_SEC_SIZE; @@ -605,6 +492,7 @@ void SettingsSave(uint8_t rotate) * stop_flash_rotate 1 = Allow only eeprom flash slot use (SetOption12 1) */ #ifndef FIRMWARE_MINIMAL + UpdateBackwardCompatibility(); if ((GetSettingsCrc32() != settings_crc32) || rotate) { if (1 == rotate) { // Use eeprom flash slot only and disable flash rotate from now on (upgrade) stop_flash_rotate = 1; @@ -627,12 +515,13 @@ void SettingsSave(uint8_t rotate) } else { Settings.cfg_timestamp++; } - Settings.cfg_size = sizeof(SYSCFG); + Settings.cfg_size = sizeof(Settings); Settings.cfg_crc = GetSettingsCrc(); // Keep for backward compatibility in case of fall-back just after upgrade Settings.cfg_crc32 = GetSettingsCrc32(); +#ifdef ESP8266 if (ESP.flashEraseSector(settings_location)) { - ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(Settings)); } if (!stop_flash_rotate && rotate) { @@ -641,8 +530,11 @@ void SettingsSave(uint8_t rotate) delay(1); } } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), settings_location, Settings.save_flag, sizeof(SYSCFG)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), settings_location, Settings.save_flag, sizeof(Settings)); +#else // ESP32 + SettingsWrite(&Settings, sizeof(Settings)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "Saved, " D_COUNT " %d, " D_BYTES " %d"), Settings.save_flag, sizeof(Settings)); +#endif // ESP8266 settings_crc32 = Settings.cfg_crc32; } @@ -652,8 +544,9 @@ void SettingsSave(uint8_t rotate) void SettingsLoad(void) { +#ifdef ESP8266 // Load configuration from eeprom or one of 7 slots below if first valid load does not stop_flash_rotate - struct SYSCFGH { + struct { uint16_t cfg_holder; // 000 uint16_t cfg_size; // 002 unsigned long save_flag; // 004 @@ -665,8 +558,7 @@ void SettingsLoad(void) uint16_t cfg_holder = 0; for (uint32_t i = 0; i < CFG_ROTATES; i++) { flash_location--; - ESP.flashRead(flash_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); - + ESP.flashRead(flash_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(Settings)); bool valid = false; if (Settings.version > 0x06000000) { bool almost_valid = (Settings.cfg_crc32 == GetSettingsCrc32()); @@ -677,7 +569,7 @@ void SettingsLoad(void) if (almost_valid && (0 == cfg_holder)) { cfg_holder = Settings.cfg_holder; } // At FB always active cfg_holder valid = (cfg_holder == Settings.cfg_holder); } else { - ESP.flashRead((flash_location -1) * SPI_FLASH_SEC_SIZE, (uint32*)&_SettingsH, sizeof(SYSCFGH)); + ESP.flashRead((flash_location -1) * SPI_FLASH_SEC_SIZE, (uint32*)&_SettingsH, sizeof(_SettingsH)); valid = (Settings.cfg_holder == _SettingsH.cfg_holder); } if (valid) { @@ -689,13 +581,16 @@ void SettingsLoad(void) } } } - delay(1); } if (settings_location > 0) { - ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(Settings)); AddLog_P2(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %lu"), settings_location, Settings.save_flag); } +#else // ESP32 + SettingsRead(&Settings, sizeof(Settings)); + AddLog_P2(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG "Loaded, " D_COUNT " %lu"), Settings.save_flag); +#endif // ESP8266 - ESP32 #ifndef FIRMWARE_MINIMAL if (!settings_location || (Settings.cfg_holder != (uint16_t)CFG_HOLDER)) { // Init defaults if cfg_holder differs from user settings in my_user_config.h @@ -730,6 +625,7 @@ void EspErase(uint32_t start_sector, uint32_t end_sector) } } +#ifdef ESP8266 void SettingsErase(uint8_t type) { /* @@ -784,6 +680,7 @@ void SettingsErase(uint8_t type) EsptoolErase(_sectorStart, _sectorEnd); // Esptool - erases flash completely #endif // FIRMWARE_MINIMAL } +#endif // ESP8266 void SettingsSdkErase(void) { @@ -804,10 +701,10 @@ void SettingsDefault(void) void SettingsDefaultSet1(void) { - memset(&Settings, 0x00, sizeof(SYSCFG)); + memset(&Settings, 0x00, sizeof(Settings)); Settings.cfg_holder = (uint16_t)CFG_HOLDER; - Settings.cfg_size = sizeof(SYSCFG); + Settings.cfg_size = sizeof(Settings); // Settings.save_flag = 0; Settings.version = VERSION; // Settings.bootcount = 0; @@ -816,15 +713,28 @@ void SettingsDefaultSet1(void) void SettingsDefaultSet2(void) { - memset((char*)&Settings +16, 0x00, sizeof(SYSCFG) -16); + memset((char*)&Settings +16, 0x00, sizeof(Settings) -16); - Settings.flag.stop_flash_rotate = APP_FLASH_CYCLE; - Settings.flag.global_state = APP_ENABLE_LEDLINK; - Settings.flag3.sleep_normal = APP_NORMAL_SLEEP; - Settings.flag3.no_power_feedback = APP_NO_RELAY_SCAN; - Settings.flag3.fast_power_cycle_disable = APP_DISABLE_POWERCYCLE; - Settings.flag3.bootcount_update = DEEPSLEEP_BOOTCOUNT; - Settings.flag3.compatibility_check = OTA_COMPATIBILITY; + // this little trick allows GCC to optimize the assignment by grouping values and doing only ORs + SysBitfield flag = { 0 }; + SysBitfield2 flag2 = { 0 }; + SysBitfield3 flag3 = { 0 }; + SysBitfield4 flag4 = { 0 }; + +#ifdef ESP8266 +// Settings.config_version = 0; // ESP8266 (Has been 0 for long time) +#endif // ESP8266 +#ifdef ESP32 + Settings.config_version = 1; // ESP32 +#endif // ESP32 + + flag.stop_flash_rotate |= APP_FLASH_CYCLE; + flag.global_state |= APP_ENABLE_LEDLINK; + flag3.sleep_normal |= APP_NORMAL_SLEEP; + flag3.no_power_feedback |= APP_NO_RELAY_SCAN; + flag3.fast_power_cycle_disable |= APP_DISABLE_POWERCYCLE; + flag3.bootcount_update |= DEEPSLEEP_BOOTCOUNT; + flag3.compatibility_check |= OTA_COMPATIBILITY; Settings.save_data = SAVE_DATA; Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY; Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; // SetOption36 @@ -835,19 +745,19 @@ void SettingsDefaultSet2(void) } // Module -// Settings.flag.interlock = 0; +// flag.interlock |= 0; Settings.interlock[0] = 0xFF; // Legacy support using all relays in one interlock group Settings.module = MODULE; ModuleDefault(WEMOS); -// for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { Settings.my_gp.io[i] = GPIO_NONE; } - SettingsUpdateText(SET_FRIENDLYNAME1, FRIENDLY_NAME); - SettingsUpdateText(SET_FRIENDLYNAME2, FRIENDLY_NAME"2"); - SettingsUpdateText(SET_FRIENDLYNAME3, FRIENDLY_NAME"3"); - SettingsUpdateText(SET_FRIENDLYNAME4, FRIENDLY_NAME"4"); - SettingsUpdateText(SET_OTAURL, OTA_URL); +// for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { Settings.my_gp.io[i] = GPIO_NONE; } + SettingsUpdateText(SET_FRIENDLYNAME1, PSTR(FRIENDLY_NAME)); + SettingsUpdateText(SET_FRIENDLYNAME2, PSTR(FRIENDLY_NAME"2")); + SettingsUpdateText(SET_FRIENDLYNAME3, PSTR(FRIENDLY_NAME"3")); + SettingsUpdateText(SET_FRIENDLYNAME4, PSTR(FRIENDLY_NAME"4")); + SettingsUpdateText(SET_OTAURL, PSTR(OTA_URL)); // Power - Settings.flag.save_state = SAVE_STATE; + flag.save_state |= SAVE_STATE; Settings.power = APP_POWER; Settings.poweronstate = APP_POWERON_STATE; Settings.blinktime = APP_BLINKTIME; @@ -865,59 +775,60 @@ void SettingsDefaultSet2(void) Settings.seriallog_level = SERIAL_LOG_LEVEL; // Wifi - Settings.flag3.use_wifi_scan = WIFI_SCAN_AT_RESTART; - Settings.flag3.use_wifi_rescan = WIFI_SCAN_REGULARLY; + flag3.use_wifi_scan |= WIFI_SCAN_AT_RESTART; + flag3.use_wifi_rescan |= WIFI_SCAN_REGULARLY; Settings.wifi_output_power = 170; + Settings.param[P_ARP_GRATUITOUS] = WIFI_ARP_INTERVAL; ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS); ParseIp(&Settings.ip_address[1], WIFI_GATEWAY); ParseIp(&Settings.ip_address[2], WIFI_SUBNETMASK); ParseIp(&Settings.ip_address[3], WIFI_DNS); Settings.sta_config = WIFI_CONFIG_TOOL; // Settings.sta_active = 0; - SettingsUpdateText(SET_STASSID1, STA_SSID1); - SettingsUpdateText(SET_STASSID2, STA_SSID2); - SettingsUpdateText(SET_STAPWD1, STA_PASS1); - SettingsUpdateText(SET_STAPWD2, STA_PASS2); + SettingsUpdateText(SET_STASSID1, PSTR(STA_SSID1)); + SettingsUpdateText(SET_STASSID2, PSTR(STA_SSID2)); + SettingsUpdateText(SET_STAPWD1, PSTR(STA_PASS1)); + SettingsUpdateText(SET_STAPWD2, PSTR(STA_PASS2)); SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); // Syslog - SettingsUpdateText(SET_SYSLOG_HOST, SYS_LOG_HOST); + SettingsUpdateText(SET_SYSLOG_HOST, PSTR(SYS_LOG_HOST)); Settings.syslog_port = SYS_LOG_PORT; Settings.syslog_level = SYS_LOG_LEVEL; // Webserver - Settings.flag2.emulation = EMULATION; - Settings.flag3.gui_hostname_ip = GUI_SHOW_HOSTNAME; - Settings.flag3.mdns_enabled = MDNS_ENABLED; + flag2.emulation |= EMULATION; + flag3.gui_hostname_ip |= GUI_SHOW_HOSTNAME; + flag3.mdns_enabled |= MDNS_ENABLED; Settings.webserver = WEB_SERVER; Settings.weblog_level = WEB_LOG_LEVEL; - SettingsUpdateText(SET_WEBPWD, WEB_PASSWORD); - SettingsUpdateText(SET_CORS, CORS_DOMAIN); + SettingsUpdateText(SET_WEBPWD, PSTR(WEB_PASSWORD)); + SettingsUpdateText(SET_CORS, PSTR(CORS_DOMAIN)); // Button - Settings.flag.button_restrict = KEY_DISABLE_MULTIPRESS; - Settings.flag.button_swap = KEY_SWAP_DOUBLE_PRESS; - Settings.flag.button_single = KEY_ONLY_SINGLE_PRESS; + flag.button_restrict |= KEY_DISABLE_MULTIPRESS; + flag.button_swap |= KEY_SWAP_DOUBLE_PRESS; + flag.button_single |= KEY_ONLY_SINGLE_PRESS; Settings.param[P_HOLD_TIME] = KEY_HOLD_TIME; // Default 4 seconds hold time // Switch for (uint32_t i = 0; i < MAX_SWITCHES; i++) { Settings.switchmode[i] = SWITCH_MODE; } // MQTT - Settings.flag.mqtt_enabled = MQTT_USE; - Settings.flag.mqtt_response = MQTT_RESULT_COMMAND; - Settings.flag.mqtt_offline = MQTT_LWT_MESSAGE; - Settings.flag.mqtt_power_retain = MQTT_POWER_RETAIN; - Settings.flag.mqtt_button_retain = MQTT_BUTTON_RETAIN; - Settings.flag.mqtt_switch_retain = MQTT_SWITCH_RETAIN; - Settings.flag.mqtt_sensor_retain = MQTT_SENSOR_RETAIN; -// Settings.flag.mqtt_serial = 0; - Settings.flag.device_index_enable = MQTT_POWER_FORMAT; - Settings.flag3.time_append_timezone = MQTT_APPEND_TIMEZONE; - Settings.flag3.button_switch_force_local = MQTT_BUTTON_SWITCH_FORCE_LOCAL; - Settings.flag3.no_hold_retain = MQTT_NO_HOLD_RETAIN; - Settings.flag3.use_underscore = MQTT_INDEX_SEPARATOR; - Settings.flag3.grouptopic_mode = MQTT_GROUPTOPIC_FORMAT; + flag.mqtt_enabled |= MQTT_USE; + flag.mqtt_response |= MQTT_RESULT_COMMAND; + flag.mqtt_offline |= MQTT_LWT_MESSAGE; + flag.mqtt_power_retain |= MQTT_POWER_RETAIN; + flag.mqtt_button_retain |= MQTT_BUTTON_RETAIN; + flag.mqtt_switch_retain |= MQTT_SWITCH_RETAIN; + flag.mqtt_sensor_retain |= MQTT_SENSOR_RETAIN; +// flag.mqtt_serial |= 0; + flag.device_index_enable |= MQTT_POWER_FORMAT; + flag3.time_append_timezone |= MQTT_APPEND_TIMEZONE; + flag3.button_switch_force_local |= MQTT_BUTTON_SWITCH_FORCE_LOCAL; + flag3.no_hold_retain |= MQTT_NO_HOLD_RETAIN; + flag3.use_underscore |= MQTT_INDEX_SEPARATOR; + flag3.grouptopic_mode |= MQTT_GROUPTOPIC_FORMAT; SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST); Settings.mqtt_port = MQTT_PORT; SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID); @@ -936,13 +847,13 @@ void SettingsDefaultSet2(void) SettingsUpdateText(SET_STATE_TXT2, MQTT_STATUS_ON); SettingsUpdateText(SET_STATE_TXT3, MQTT_CMND_TOGGLE); SettingsUpdateText(SET_STATE_TXT4, MQTT_CMND_HOLD); - char fingerprint[60]; - strlcpy(fingerprint, MQTT_FINGERPRINT1, sizeof(fingerprint)); + char fingerprint[64]; + strncpy_P(fingerprint, PSTR(MQTT_FINGERPRINT1), sizeof(fingerprint)); char *p = fingerprint; for (uint32_t i = 0; i < 20; i++) { Settings.mqtt_fingerprint[0][i] = strtol(p, &p, 16); } - strlcpy(fingerprint, MQTT_FINGERPRINT2, sizeof(fingerprint)); + strncpy_P(fingerprint, PSTR(MQTT_FINGERPRINT2), sizeof(fingerprint)); p = fingerprint; for (uint32_t i = 0; i < 20; i++) { Settings.mqtt_fingerprint[1][i] = strtol(p, &p, 16); @@ -951,13 +862,13 @@ void SettingsDefaultSet2(void) Settings.mqttlog_level = MQTT_LOG_LEVEL; // Energy - Settings.flag.no_power_on_check = ENERGY_VOLTAGE_ALWAYS; - Settings.flag2.current_resolution = 3; -// Settings.flag2.voltage_resolution = 0; -// Settings.flag2.wattage_resolution = 0; - Settings.flag2.energy_resolution = ENERGY_RESOLUTION; - Settings.flag3.dds2382_model = ENERGY_DDS2382_MODE; - Settings.flag3.hardware_energy_total = ENERGY_HARDWARE_TOTALS; + flag.no_power_on_check |= ENERGY_VOLTAGE_ALWAYS; + flag2.current_resolution |= 3; +// flag2.voltage_resolution |= 0; +// flag2.wattage_resolution |= 0; + flag2.energy_resolution |= ENERGY_RESOLUTION; + flag3.dds2382_model |= ENERGY_DDS2382_MODE; + flag3.hardware_energy_total |= ENERGY_HARDWARE_TOTALS; Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY; // Settings.energy_power_delta = 0; Settings.energy_power_calibration = HLW_PREF_PULSE; @@ -987,12 +898,12 @@ void SettingsDefaultSet2(void) Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; // IRRemote - Settings.flag.ir_receive_decimal = IR_DATA_RADIX; - Settings.flag3.receive_raw = IR_ADD_RAW_DATA; + flag.ir_receive_decimal |= IR_DATA_RADIX; + flag3.receive_raw |= IR_ADD_RAW_DATA; Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; // RF Bridge - Settings.flag.rf_receive_decimal = RF_DATA_RADIX; + flag.rf_receive_decimal |= RF_DATA_RADIX; // for (uint32_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; } memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9); @@ -1008,42 +919,43 @@ void SettingsDefaultSet2(void) // } // Sensor - Settings.flag.temperature_conversion = TEMP_CONVERSION; - Settings.flag.pressure_conversion = PRESSURE_CONVERSION; - Settings.flag2.pressure_resolution = PRESSURE_RESOLUTION; - Settings.flag2.humidity_resolution = HUMIDITY_RESOLUTION; - Settings.flag2.temperature_resolution = TEMP_RESOLUTION; - Settings.flag3.ds18x20_internal_pullup = DS18X20_PULL_UP; - Settings.flag3.counter_reset_on_tele = COUNTER_RESET; + flag.temperature_conversion |= TEMP_CONVERSION; + flag.pressure_conversion |= PRESSURE_CONVERSION; + flag2.pressure_resolution |= PRESSURE_RESOLUTION; + flag2.humidity_resolution |= HUMIDITY_RESOLUTION; + flag2.temperature_resolution |= TEMP_RESOLUTION; + flag3.ds18x20_internal_pullup |= DS18X20_PULL_UP; + flag3.counter_reset_on_tele |= COUNTER_RESET; // Settings.altitude = 0; // Rules // Settings.rule_enabled = 0; // Settings.rule_once = 0; // for (uint32_t i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; } - Settings.flag2.calc_resolution = CALC_RESOLUTION; + flag2.calc_resolution |= CALC_RESOLUTION; // Timer - Settings.flag3.timers_enable = TIMERS_ENABLED; + flag3.timers_enable |= TIMERS_ENABLED; // Home Assistant - Settings.flag.hass_light = HASS_AS_LIGHT; - Settings.flag.hass_discovery = HOME_ASSISTANT_DISCOVERY_ENABLE; - Settings.flag3.hass_tele_on_power = TELE_ON_POWER; + flag.hass_light |= HASS_AS_LIGHT; + flag.hass_discovery |= HOME_ASSISTANT_DISCOVERY_ENABLE; + flag3.hass_tele_on_power |= TELE_ON_POWER; // Knx - Settings.flag.knx_enabled = KNX_ENABLED; - Settings.flag.knx_enable_enhancement = KNX_ENHANCED; + flag.knx_enabled |= KNX_ENABLED; + flag.knx_enable_enhancement |= KNX_ENHANCED; // Light - Settings.flag.pwm_control = LIGHT_MODE; - Settings.flag.ws_clock_reverse = LIGHT_CLOCK_DIRECTION; - Settings.flag.light_signal = LIGHT_PAIRS_CO2; - Settings.flag.not_power_linked = LIGHT_POWER_CONTROL; - Settings.flag.decimal_text = LIGHT_COLOR_RADIX; - Settings.flag3.pwm_multi_channels = LIGHT_CHANNEL_MODE; - Settings.flag3.slider_dimmer_stay_on = LIGHT_SLIDER_POWER; - Settings.flag4.alexa_ct_range = LIGHT_ALEXA_CT_RANGE; + flag.pwm_control |= LIGHT_MODE; + flag.ws_clock_reverse |= LIGHT_CLOCK_DIRECTION; + flag.light_signal |= LIGHT_PAIRS_CO2; + flag.not_power_linked |= LIGHT_POWER_CONTROL; + flag.decimal_text |= LIGHT_COLOR_RADIX; + flag3.pwm_multi_channels |= LIGHT_CHANNEL_MODE; + flag3.slider_dimmer_stay_on |= LIGHT_SLIDER_POWER; + flag4.alexa_ct_range |= LIGHT_ALEXA_CT_RANGE; + flag4.pwm_ct_mode |= LIGHT_PWM_CT_MODE; Settings.pwm_frequency = PWM_FREQ; Settings.pwm_range = PWM_RANGE; @@ -1104,9 +1016,9 @@ void SettingsDefaultSet2(void) Settings.timezone = APP_TIMEZONE / 60; Settings.timezone_minutes = abs(APP_TIMEZONE % 60); } - SettingsUpdateText(SET_NTPSERVER1, NTP_SERVER1); - SettingsUpdateText(SET_NTPSERVER2, NTP_SERVER2); - SettingsUpdateText(SET_NTPSERVER3, NTP_SERVER3); + SettingsUpdateText(SET_NTPSERVER1, PSTR(NTP_SERVER1)); + SettingsUpdateText(SET_NTPSERVER2, PSTR(NTP_SERVER2)); + SettingsUpdateText(SET_NTPSERVER3, PSTR(NTP_SERVER3)); for (uint32_t i = 0; i < MAX_NTP_SERVERS; i++) { SettingsUpdateText(SET_NTPSERVER1 +i, ReplaceCommaWithDot(SettingsText(SET_NTPSERVER1 +i))); } @@ -1130,13 +1042,18 @@ void SettingsDefaultSet2(void) SettingsEnableAllI2cDrivers(); // Tuya - Settings.flag3.tuya_apply_o20 = TUYA_SETOPTION_20; - Settings.flag3.tuya_serial_mqtt_publish = MQTT_TUYA_RECEIVED; + flag3.tuya_apply_o20 |= TUYA_SETOPTION_20; + flag3.tuya_serial_mqtt_publish |= MQTT_TUYA_RECEIVED; - Settings.flag3.buzzer_enable = BUZZER_ENABLE; - Settings.flag3.shutter_mode = SHUTTER_SUPPORT; - Settings.flag3.pcf8574_ports_inverted = PCF8574_INVERT_PORTS; - Settings.flag4.zigbee_use_names = ZIGBEE_FRIENDLY_NAMES; + flag3.buzzer_enable |= BUZZER_ENABLE; + flag3.shutter_mode |= SHUTTER_SUPPORT; + flag3.pcf8574_ports_inverted |= PCF8574_INVERT_PORTS; + flag4.zigbee_use_names |= ZIGBEE_FRIENDLY_NAMES; + + Settings.flag = flag; + Settings.flag2 = flag2; + Settings.flag3 = flag3; + Settings.flag4 = flag4; } /********************************************************************************************/ @@ -1181,8 +1098,10 @@ void SettingsEnableAllI2cDrivers(void) void SettingsDelta(void) { if (Settings.version != VERSION) { // Fix version dependent changes + +#ifdef ESP8266 if (Settings.version < 0x06000000) { - Settings.cfg_size = sizeof(SYSCFG); + Settings.cfg_size = sizeof(Settings); Settings.cfg_crc = GetSettingsCrc(); } if (Settings.version < 0x06000002) { @@ -1193,7 +1112,7 @@ void SettingsDelta(void) Settings.switchmode[i] = SWITCH_MODE; } } - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { if (Settings.my_gp.io[i] >= GPIO_SWT5) { // Move up from GPIO_SWT5 to GPIO_KEY1 Settings.my_gp.io[i] += 4; } @@ -1266,7 +1185,7 @@ void SettingsDelta(void) Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; } if (Settings.version < 0x06060007) { - memset((char*)&Settings +0xE00, 0x00, sizeof(SYSCFG) -0xE00); + memset((char*)&Settings +0xE00, 0x00, sizeof(Settings) -0xE00); } if (Settings.version < 0x06060008) { // Move current tuya dimmer range to the new param. @@ -1291,8 +1210,8 @@ void SettingsDelta(void) Settings.tuya_fnid_map[tuyaindex].dpid = 1; tuyaindex++; } - if (Settings.param[P_ex_TUYA_RELAYS] > 0) { - for (uint8_t i = 0 ; i < Settings.param[P_ex_TUYA_RELAYS]; i++) { // ex SetOption41 + if (Settings.param[P_ARP_GRATUITOUS] > 0) { // Was P_ex_TUYA_RELAYS + for (uint8_t i = 0 ; i < Settings.param[P_ARP_GRATUITOUS]; i++) { // ex SetOption41 Settings.tuya_fnid_map[tuyaindex].fnid = 12 + i; // TUYA_MCU_FUNC_REL2 - Create FnID for Switches Settings.tuya_fnid_map[tuyaindex].dpid = i + 2; tuyaindex++; @@ -1377,7 +1296,7 @@ void SettingsDelta(void) Settings.ex_serial_config = TS_SERIAL_8N1; } if (Settings.version < 0x07010204) { - if (Settings.flag3.ex_cors_enabled == 1) { + if (Settings.flag3.mqtt_buttons == 1) { strlcpy(Settings.ex_cors_domain, CORS_ENABLED_ALL, sizeof(Settings.ex_cors_domain)); } else { Settings.ex_cors_domain[0] = 0; @@ -1394,7 +1313,6 @@ void SettingsDelta(void) Settings.mqtt_port = Settings.ex_mqtt_port; // 20A -> EFC memcpy((char*)&Settings.serial_config, (char*)&Settings.ex_serial_config, 5); // 1E4 -> EFE } - if (Settings.version < 0x08000000) { char temp[strlen(Settings.text_pool) +1]; strncpy(temp, Settings.text_pool, sizeof(temp)); // Was ota_url char temp21[strlen(Settings.ex_mqtt_prefix[0]) +1]; strncpy(temp21, Settings.ex_mqtt_prefix[0], sizeof(temp21)); @@ -1466,6 +1384,31 @@ void SettingsDelta(void) SettingsUpdateText(SET_FRIENDLYNAME3, Settings.ex_friendlyname[2]); SettingsUpdateText(SET_FRIENDLYNAME4, Settings.ex_friendlyname[3]); } + if (Settings.version < 0x08020003) { + SettingsUpdateText(SET_TEMPLATE_NAME, Settings.user_template_name); + Settings.zb_channel = 0; // set channel to zero to force reinit of zigbee parameters + } +#endif // ESP8266 + + if (Settings.version < 0x08020004) { +#ifdef ESP8266 + Settings.config_version = 0; // ESP8266 (Has been 0 for long time) +#endif // ESP8266 +#ifdef ESP32 + Settings.config_version = 1; // ESP32 +#endif // ESP32 + } + + if (Settings.version < 0x08020006) { +#ifdef ESP32 + Settings.module = WEMOS; + ModuleDefault(WEMOS); +#endif // ESP32 + // make sure the empty rules have two consecutive NULLs, to be compatible with compressed rules + if (Settings.rules[0][0] == 0) { Settings.rules[0][1] = 0; } + if (Settings.rules[1][0] == 0) { Settings.rules[1][1] = 0; } + if (Settings.rules[2][0] == 0) { Settings.rules[2][1] = 0; } + } Settings.version = VERSION; SettingsSave(1); diff --git a/tasmota/support.ino b/tasmota/support.ino index 490918276..db4c9c26e 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -52,7 +52,7 @@ void OsWatchTicker(void) #ifdef DEBUG_THEO int32_t rssi = WiFi.RSSI(); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(rssi), rssi, last_run); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP_getFreeHeap(), WifiGetRssiAsQuality(rssi), rssi, last_run); #endif // DEBUG_THEO if (last_run >= (OSWATCH_RESET_TIME * 1000)) { // AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_OSWATCH " " D_BLOCKED_LOOP ". " D_RESTARTING)); // Save iram space @@ -99,7 +99,7 @@ uint32_t ResetReason(void) REASON_DEEP_SLEEP_AWAKE = 5, // "Deep-Sleep Wake" wake up from deep-sleep REASON_EXT_SYS_RST = 6 // "External System" external system reset */ - return resetInfo.reason; + return ESP_ResetInfoReason(); } String GetResetReason(void) @@ -109,7 +109,7 @@ String GetResetReason(void) strncpy_P(buff, PSTR(D_JSON_BLOCKED_LOOP), sizeof(buff)); return String(buff); } else { - return ESP.getResetReason(); + return ESP_getResetReason(); } } @@ -213,7 +213,7 @@ char* ulltoa(unsigned long long value, char *str, int radix) } // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c -// char* ToHex_P(unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); in tasmota_post.h +// char* ToHex_P(unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); in tasmota_globals.h char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween) { // ToHex_P(in, insz, out, outz) -> "12345667" @@ -562,6 +562,7 @@ char* GetPowerDevice(char* dest, uint32_t idx, size_t size) void GetEspHardwareType(void) { +#ifdef ESP8266 // esptool.py get_efuses uint32_t efuse1 = *(uint32_t*)(0x3FF00050); uint32_t efuse2 = *(uint32_t*)(0x3FF00054); @@ -572,16 +573,23 @@ void GetEspHardwareType(void) if (is_8285 && (ESP.getFlashChipRealSize() > 1048576)) { is_8285 = false; // ESP8285 can only have 1M flash } +#else + is_8285 = false; // ESP8285 can only have 1M flash +#endif } String GetDeviceHardware(void) { char buff[10]; +#ifdef ESP8266 if (is_8285) { strcpy_P(buff, PSTR("ESP8285")); } else { strcpy_P(buff, PSTR("ESP8266EX")); } +#else + strcpy_P(buff, PSTR("ESP32")); +#endif return String(buff); } @@ -612,7 +620,8 @@ float ConvertTempToCelsius(float c) char TempUnit(void) { - return (Settings.flag.temperature_conversion) ? 'F' : 'C'; // SetOption8 - Switch between Celsius or Fahrenheit + // SetOption8 - Switch between Celsius or Fahrenheit + return (Settings.flag.temperature_conversion) ? D_UNIT_FAHRENHEIT[0] : D_UNIT_CELSIUS[0]; } float ConvertHumidity(float h) @@ -629,7 +638,7 @@ float ConvertHumidity(float h) float CalcTempHumToDew(float t, float h) { - if (isnan(h) || isnan(t)) { return 0.0; } + if (isnan(h) || isnan(t)) { return NAN; } if (Settings.flag.temperature_conversion) { // SetOption8 - Switch between Celsius or Fahrenheit t = (t - 32) / 1.8; // Celsius @@ -1065,10 +1074,38 @@ int ResponseJsonEndEnd(void) * GPIO Module and Template management \*********************************************************************************************/ -void DigitalWrite(uint32_t gpio_pin, uint32_t state) +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception +uint32_t Pin(uint32_t gpio, uint32_t index) ICACHE_RAM_ATTR; +#endif + +uint32_t Pin(uint32_t gpio, uint32_t index = 0); +uint32_t Pin(uint32_t gpio, uint32_t index) { +#ifdef ESP8266 + uint16_t real_gpio = gpio + index; +#else // ESP32 + uint16_t real_gpio = (gpio << 5) + index; +#endif // ESP8266 - ESP32 + for (uint32_t i = 0; i < ARRAY_SIZE(gpio_pin); i++) { + if (gpio_pin[i] == real_gpio) { + return i; // Pin number configured for gpio + } + } + return 99; // No pin used for gpio +} + +boolean PinUsed(uint32_t gpio, uint32_t index = 0); +boolean PinUsed(uint32_t gpio, uint32_t index) { + return (Pin(gpio, index) < 99); +} + +void SetPin(uint32_t lpin, uint32_t gpio) { + gpio_pin[lpin] = gpio; +} + +void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state) { - if (pin[gpio_pin] < 99) { - digitalWrite(pin[gpio_pin], state &1); + if (PinUsed(gpio_pin, index)) { + digitalWrite(Pin(gpio_pin, index), state &1); } } @@ -1098,9 +1135,10 @@ bool ValidModule(uint32_t index) String AnyModuleName(uint32_t index) { if (USER_MODULE == index) { - return String(Settings.user_template.name); + return String(SettingsText(SET_TEMPLATE_NAME)); } else { - return FPSTR(kModules[index].name); + char name[TOPSZ]; + return String(GetTextIndexed(name, sizeof(name), index, kModuleNames)); } } @@ -1111,21 +1149,30 @@ String ModuleName(void) void ModuleGpios(myio *gp) { +#ifdef ESP8266 uint8_t *dest = (uint8_t *)gp; - memset(dest, GPIO_NONE, sizeof(myio)); + uint8_t src[ARRAY_SIZE(Settings.user_template.gp.io)]; +#else // ESP32 + uint16_t *dest = (uint16_t *)gp; + uint16_t src[ARRAY_SIZE(Settings.user_template.gp.io)]; +#endif // ESP8266 - ESP32 - uint8_t src[sizeof(mycfgio)]; + memset(dest, GPIO_NONE, sizeof(myio)); if (USER_MODULE == Settings.module) { memcpy(&src, &Settings.user_template.gp, sizeof(mycfgio)); } else { +#ifdef ESP8266 memcpy_P(&src, &kModules[Settings.module].gp, sizeof(mycfgio)); +#else // ESP32 + memcpy_P(&src, &kModules.gp, sizeof(mycfgio)); +#endif // ESP8266 - ESP32 } // 11 85 00 85 85 00 00 00 15 38 85 00 00 81 // AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)&src, sizeof(mycfgio)); uint32_t j = 0; - for (uint32_t i = 0; i < sizeof(mycfgio); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { if (6 == i) { j = 9; } if (8 == i) { j = 12; } dest[j] = src[i]; @@ -1143,7 +1190,11 @@ gpio_flag ModuleFlag(void) if (USER_MODULE == Settings.module) { flag = Settings.user_template.flag; } else { +#ifdef ESP8266 memcpy_P(&flag, &kModules[Settings.module].flag, sizeof(gpio_flag)); +#else // ESP32 + memcpy_P(&flag, &kModules.flag, sizeof(gpio_flag)); +#endif // ESP8266 - ESP32 } return flag; @@ -1153,7 +1204,13 @@ void ModuleDefault(uint32_t module) { if (USER_MODULE == module) { module = WEMOS; } // Generic Settings.user_template_base = module; + char name[TOPSZ]; + SettingsUpdateText(SET_TEMPLATE_NAME, GetTextIndexed(name, sizeof(name), module, kModuleNames)); +#ifdef ESP8266 memcpy_P(&Settings.user_template, &kModules[module], sizeof(mytmplt)); +#else // ESP32 + memcpy_P(&Settings.user_template, &kModules, sizeof(mytmplt)); +#endif // ESP8266 - ESP32 } void SetModuleType(void) @@ -1166,7 +1223,7 @@ bool FlashPin(uint32_t pin) return (((pin > 5) && (pin < 9)) || (11 == pin)); } -uint8_t ValidPin(uint32_t pin, uint32_t gpio) +uint32_t ValidPin(uint32_t pin, uint32_t gpio) { if (FlashPin(pin)) { return GPIO_NONE; // Disable flash pins GPIO6, GPIO7, GPIO8 and GPIO11 @@ -1184,17 +1241,27 @@ uint8_t ValidPin(uint32_t pin, uint32_t gpio) bool ValidGPIO(uint32_t pin, uint32_t gpio) { +#ifdef ESP8266 return (GPIO_USER == ValidPin(pin, gpio)); // Only allow GPIO_USER pins +#else // ESP32 + return (GPIO_USER == ValidPin(pin, gpio >> 5)); // Only allow GPIO_USER pins +#endif // ESP8266 - ESP32 } +#ifdef ESP8266 bool ValidAdc(void) { gpio_flag flag = ModuleFlag(); uint32_t template_adc0 = flag.data &15; return (ADC0_USER == template_adc0); } +#endif // ESP8266 +#ifdef ESP8266 bool GetUsedInModule(uint32_t val, uint8_t *arr) +#else // ESP32 +bool GetUsedInModule(uint32_t val, uint16_t *arr) +#endif // ESP8266 - ESP32 { int offset = 0; @@ -1261,26 +1328,30 @@ bool JsonTemplate(const char* dataBuf) if (strlen(dataBuf) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks - StaticJsonBuffer<350> jb; // 331 from https://arduinojson.org/v5/assistant/ +#ifdef ESP8266 + StaticJsonBuffer<400> jb; // 331 from https://arduinojson.org/v5/assistant/ +#else + StaticJsonBuffer<999> jb; // 654 from https://arduinojson.org/v5/assistant/ +#endif JsonObject& obj = jb.parseObject(dataBuf); if (!obj.success()) { return false; } // All parameters are optional allowing for partial changes const char* name = obj[D_JSON_NAME]; if (name != nullptr) { - strlcpy(Settings.user_template.name, name, sizeof(Settings.user_template.name)); + SettingsUpdateText(SET_TEMPLATE_NAME, name); } if (obj[D_JSON_GPIO].success()) { - for (uint32_t i = 0; i < sizeof(mycfgio); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { Settings.user_template.gp.io[i] = obj[D_JSON_GPIO][i] | 0; } } if (obj[D_JSON_FLAG].success()) { - uint8_t flag = obj[D_JSON_FLAG] | 0; + uint32_t flag = obj[D_JSON_FLAG] | 0; memcpy(&Settings.user_template.flag, &flag, sizeof(gpio_flag)); } if (obj[D_JSON_BASE].success()) { - uint8_t base = obj[D_JSON_BASE]; + uint32_t base = obj[D_JSON_BASE]; if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; } Settings.user_template_base = base -1; // Default WEMOS } @@ -1289,8 +1360,8 @@ bool JsonTemplate(const char* dataBuf) void TemplateJson(void) { - Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template.name); - for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { + Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), SettingsText(SET_TEMPLATE_NAME)); + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", Settings.user_template.gp.io[i]); } ResponseAppend_P(PSTR("],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), Settings.user_template.flag, Settings.user_template_base +1); @@ -1657,7 +1728,7 @@ void Syslog(void) memmove(log_data + strlen(syslog_preamble), log_data, sizeof(log_data) - strlen(syslog_preamble)); log_data[sizeof(log_data) -1] = '\0'; memcpy(log_data, syslog_preamble, strlen(syslog_preamble)); - PortUdp.write(log_data, strlen(log_data)); + PortUdp_write(log_data, strlen(log_data)); PortUdp.endPacket(); delay(1); // Add time for UDP handling (#5512) } else { @@ -1775,7 +1846,79 @@ void AddLogSerial(uint32_t loglevel) AddLogBuffer(loglevel, (uint8_t*)serial_in_buffer, serial_in_byte_counter); } -void AddLogMissed(char *sensor, uint32_t misses) +void AddLogMissed(const char *sensor, uint32_t misses) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses); } + +void AddLogBufferSize(uint32_t loglevel, uint8_t *buffer, uint32_t count, uint32_t size) { + snprintf_P(log_data, sizeof(log_data), PSTR("DMP:")); + for (uint32_t i = 0; i < count; i++) { + if (1 == size) { // uint8_t + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer)); + } else { // uint16_t + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X%02X"), log_data, *(buffer +1), *(buffer)); + } + buffer += size; + } + AddLog(loglevel); +} + +/*********************************************************************************************\ + * JSON parsing +\*********************************************************************************************/ + +// does the character needs to be escaped, and if so with which character +char escapeJSONChar(char c) { + if ((c == '\"') || (c == '\\')) { + return c; + } + if (c == '\n') { return 'n'; } + if (c == '\t') { return 't'; } + if (c == '\r') { return 'r'; } + if (c == '\f') { return 'f'; } + if (c == '\b') { return 'b'; } + return 0; +} + +String escapeJSONString(const char *str) { + String r(""); + if (nullptr == str) { return r; } + + bool needs_escape = false; + size_t len_out = 1; + const char * c = str; + + while (*c) { + if (escapeJSONChar(*c)) { + len_out++; + needs_escape = true; + } + c++; + len_out++; + } + + if (needs_escape) { + // we need to escape some chars + // allocate target buffer + r.reserve(len_out); + c = str; + char *d = r.begin(); + while (*c) { + char c2 = escapeJSONChar(*c); + if (c2) { + c++; + *d++ = '\\'; + *d++ = c2; + } else { + *d++ = *c++; + } + } + *d = 0; // add NULL terminator + r = (char*) r.begin(); // assign the buffer to the string + } else { + r = str; + } + + return r; +} \ No newline at end of file diff --git a/tasmota/support_button.ino b/tasmota/support_button.ino index f30f3a287..659a02348 100644 --- a/tasmota/support_button.ino +++ b/tasmota/support_button.ino @@ -17,13 +17,14 @@ along with this program. If not, see . */ -#define BUTTON_V1 +//#define BUTTON_V1 #ifdef BUTTON_V1 /*********************************************************************************************\ * Button support \*********************************************************************************************/ #define MAX_BUTTON_COMMANDS 5 // Max number of button commands supported + const char kCommands[] PROGMEM = D_CMND_WIFICONFIG " 2|" D_CMND_WIFICONFIG " 2|" D_CMND_WIFICONFIG " 2|" D_CMND_RESTART " 1|" D_CMND_UPGRADE " 1"; @@ -59,9 +60,9 @@ void ButtonInit(void) { Button.present = 0; for (uint32_t i = 0; i < MAX_KEYS; i++) { - if (pin[GPIO_KEY1 +i] < 99) { + if (PinUsed(GPIO_KEY1, i)) { Button.present++; - pinMode(pin[GPIO_KEY1 +i], bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); + pinMode(Pin(GPIO_KEY1, i), bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_KEY1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); } #ifndef USE_ADC_VCC else if ((99 == Button.adc) && ((ADC0_BUTTON == my_adc0) || (ADC0_BUTTON_INV == my_adc0))) { @@ -119,6 +120,7 @@ void ButtonHandler(void) uint8_t button = NOT_PRESSED; uint8_t button_present = 0; +#ifdef ESP8266 if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) { button_present = 1; if (Button.dual_code) { @@ -131,9 +133,11 @@ void ButtonHandler(void) Button.dual_code = 0; } } - else if (pin[GPIO_KEY1 +button_index] < 99) { + else +#endif // ESP8266 + if (PinUsed(GPIO_KEY1, button_index)) { button_present = 1; - button = (digitalRead(pin[GPIO_KEY1 +button_index]) != bitRead(Button.inverted_mask, button_index)); + button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index)); } #ifndef USE_ADC_VCC if (Button.adc == button_index) { @@ -153,6 +157,7 @@ void ButtonHandler(void) if (XdrvCall(FUNC_BUTTON_PRESSED)) { // Serviced } +#ifdef ESP8266 else if (SONOFF_4CHPRO == my_module_type) { if (Button.hold_timer[button_index]) { Button.hold_timer[button_index]--; } @@ -172,6 +177,7 @@ void ButtonHandler(void) } } } +#endif // ESP8266 else { if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action @@ -227,9 +233,12 @@ void ButtonHandler(void) if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < MAX_BUTTON_COMMANDS +3)) { bool single_press = false; if (Button.press_counter[button_index] < 3) { // Single or Double press +#ifdef ESP8266 if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { single_press = true; - } else { + } else +#endif // ESP8266 + { single_press = (Settings.flag.button_swap +1 == Button.press_counter[button_index]); // SetOption11 (0) if ((1 == Button.present) && (2 == devices_present)) { // Single Button with two devices only if (Settings.flag.button_swap) { // SetOption11 (0) diff --git a/tasmota/support_button_v2.ino b/tasmota/support_button_v2.ino new file mode 100644 index 000000000..4dc94cf13 --- /dev/null +++ b/tasmota/support_button_v2.ino @@ -0,0 +1,330 @@ +/* + support_button_v2.ino - button support for Tasmota + + Copyright (C) 2020 Federico Leoni and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define BUTTON_V2 +#ifdef BUTTON_V2 +/*********************************************************************************************\ + * Button support +\*********************************************************************************************/ + +#define MAX_RELAY_BUTTON1 5 // Max number of relay controlled by BUTTON1 + +const char kMultiPress[] PROGMEM = + "|SINGLE|DOUBLE|TRIPLE|QUAD|PENTA|"; + +struct BUTTON { + unsigned long debounce = 0; // Button debounce timer + uint16_t hold_timer[MAX_KEYS] = { 0 }; // Timer for button hold + uint16_t dual_code = 0; // Sonoff dual received code + + uint8_t last_state[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; // Last button states + uint8_t window_timer[MAX_KEYS] = { 0 }; // Max time between button presses to record press count + uint8_t press_counter[MAX_KEYS] = { 0 }; // Number of button presses within Button.window_timer + + uint8_t dual_receive_count = 0; // Sonoff dual input flag + uint8_t no_pullup_mask = 0; // key no pullup flag (1 = no pullup) + uint8_t inverted_mask = 0; // Key inverted flag (1 = inverted) + uint8_t present = 0; // Number of buttons found flag + uint8_t adc = 99; // ADC0 button number +} Button; + +/********************************************************************************************/ + +void ButtonPullupFlag(uint8 button_bit) +{ + bitSet(Button.no_pullup_mask, button_bit); +} + +void ButtonInvertFlag(uint8 button_bit) +{ + bitSet(Button.inverted_mask, button_bit); +} + +void ButtonInit(void) +{ + Button.present = 0; + for (uint32_t i = 0; i < MAX_KEYS; i++) { + if (PinUsed(GPIO_KEY1, i)) { + Button.present++; + pinMode(Pin(GPIO_KEY1, i), bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_KEY1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); + } +#ifndef USE_ADC_VCC + else if ((99 == Button.adc) && ((ADC0_BUTTON == my_adc0) || (ADC0_BUTTON_INV == my_adc0))) { + Button.present++; + Button.adc = i; + } +#endif // USE_ADC_VCC + } +} + +uint8_t ButtonSerial(uint8_t serial_in_byte) +{ + if (Button.dual_receive_count) { + Button.dual_receive_count--; + if (Button.dual_receive_count) { + Button.dual_code = (Button.dual_code << 8) | serial_in_byte; + serial_in_byte = 0; + } else { + if (serial_in_byte != 0xA1) { + Button.dual_code = 0; // 0xA1 - End of Sonoff dual button code + } + } + } + if (0xA0 == serial_in_byte) { // 0xA0 - Start of Sonoff dual button code + serial_in_byte = 0; + Button.dual_code = 0; + Button.dual_receive_count = 3; + } + + return serial_in_byte; +} + +/*********************************************************************************************\ + * Button handler with single press only or multi-press and hold on all buttons + * + * ButtonDebounce (50) - Debounce time in mSec + * SetOption1 (0) - If set do not execute commands WifiConfig and Reset + * SetOption11 (0) - If set perform single press action on double press and reverse (on two relay devices only) + * SetOption13 (0) - If set act on single press only + * SetOption73 (0) - Decouple button from relay and send just mqtt topic +\*********************************************************************************************/ + +void ButtonHandler(void) +{ + if (uptime < 4) { return; } // Block GPIO for 4 seconds after poweron to workaround Wemos D1 / Obi RTS circuit + + uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; // Extent hold time factor in case of iminnent Reset command + uint16_t loops_per_second = 1000 / Settings.button_debounce; // ButtonDebounce (50) + char scmnd[20]; + + for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { + uint8_t button = NOT_PRESSED; + uint8_t button_present = 0; + +#ifdef ESP8266 + if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) { + button_present = 1; + if (Button.dual_code) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), Button.dual_code); + button = PRESSED; + if (0xF500 == Button.dual_code) { // Button hold + Button.hold_timer[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; // SetOption32 (40) + hold_time_extent = 1; + } + Button.dual_code = 0; + } + } + else +#endif // ESP8266 + if (PinUsed(GPIO_KEY1, button_index)) { + button_present = 1; + button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index)); + } +#ifndef USE_ADC_VCC + if (Button.adc == button_index) { + button_present = 1; + if (ADC0_BUTTON_INV == my_adc0) { + button = (AdcRead(1) < 128); + } + else if (ADC0_BUTTON == my_adc0) { + button = (AdcRead(1) > 128); + } + } +#endif // USE_ADC_VCC + + if (button_present) { + XdrvMailbox.index = button_index; + XdrvMailbox.payload = button; + if (XdrvCall(FUNC_BUTTON_PRESSED)) { + // Serviced + } +#ifdef ESP8266 + else if (SONOFF_4CHPRO == my_module_type) { + if (Button.hold_timer[button_index]) { Button.hold_timer[button_index]--; } + + bool button_pressed = false; + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1); + Button.hold_timer[button_index] = loops_per_second; + button_pressed = true; + } + if ((NOT_PRESSED == button) && (PRESSED == Button.last_state[button_index])) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); + if (!Button.hold_timer[button_index]) { button_pressed = true; } // Do not allow within 1 second + } + if (button_pressed) { + if (!Settings.flag3.mqtt_buttons) { + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally + } + } else { + MqttButtonTopic(button_index +1, 1, 0); // SetOption73 (0) - Decouple button from relay and send just mqtt topic + } + } + } +#endif // ESP8266 + else { + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + + if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action, SetOption73 (0) - Decouple button from relay and send just mqtt topic + if (!Settings.flag3.mqtt_buttons) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally + } + } else { + MqttButtonTopic(button_index +1, 1, 0); // SetOption73 (0) - Decouple button from relay and send just mqtt topic + } + } else { + Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, Button.press_counter[button_index]); + Button.window_timer[button_index] = loops_per_second / 2; // 0.5 second multi press window + } + blinks = 201; + } + + if (NOT_PRESSED == button) { + Button.hold_timer[button_index] = 0; + } else { + Button.hold_timer[button_index]++; + if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action + if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only + ExecuteCommand(scmnd, SRC_BUTTON); + } + } else { + if (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button hold + Button.press_counter[button_index] = 0; + if (Settings.flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic + MqttButtonTopic(button_index +1, 3, 1); + } else { + SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); // Execute Hold command via MQTT if ButtonTopic is set + } + } else { + if (!Settings.flag.button_restrict) { + if ((Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10)) { // SetOption32 (40) - Button held for factor times longer + Button.press_counter[button_index] = 0; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + } + } + } + + if (!Settings.flag.button_single) { // SetOption13 (0) - Allow multi-press + if (Button.window_timer[button_index]) { + Button.window_timer[button_index]--; + } else { + if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < 7)) { + + bool single_press = false; + if (Button.press_counter[button_index] < 3) { // Single or Double press +#ifdef ESP8266 + if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + single_press = true; + } else +#endif // ESP8266 + { + single_press = (Settings.flag.button_swap +1 == Button.press_counter[button_index]); // SetOption11 (0) + if ((1 == Button.present) && (2 == devices_present)) { // Single Button with two devices only + if (Settings.flag.button_swap) { // SetOption11 (0) + Button.press_counter[button_index] = (single_press) ? 1 : 2; + } + } + } + } +#if defined(USE_LIGHT) && defined(ROTARY_V1) + if (!((0 == button_index) && RotaryButtonPressed())) { +#endif + if (!Settings.flag3.mqtt_buttons && single_press && SendKey(KEY_BUTTON, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + // Success + } else { + if (Button.press_counter[button_index] < 6) { // Single to Penta press + if (WifiState() > WIFI_RESTART) { // Wifimanager active + restart_flag = 1; + } + if (!Settings.flag3.mqtt_buttons) { + if (Button.press_counter[button_index] == 1) { // By default first press always send a TOGGLE (2) + ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); + } else { + SendKey(KEY_BUTTON, button_index +1, Button.press_counter[button_index] +9); // 2,3,4 and 5 press send just the key value (11,12,13 and 14) for rules + if (0 == button_index) { // BUTTON1 can toggle up to 5 relays if present. If a relay is not present will send out the key value (2,11,12,13 and 14) for rules + if ((Button.press_counter[button_index] > 1 && PinUsed(GPIO_REL1, Button.press_counter[button_index]-1)) && Button.press_counter[button_index] <= MAX_RELAY_BUTTON1) { + ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: Relay%d found on GPIO%d"), Button.press_counter[button_index], Pin(GPIO_REL1, Button.press_counter[button_index]-1)); + } + } + } + } + + } else { // 6 press start wificonfig 2 + if (!Settings.flag.button_restrict) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + if (Settings.flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic + if (Button.press_counter[button_index] >= 1 && Button.press_counter[button_index] <= 5) { + MqttButtonTopic(button_index +1, Button.press_counter[button_index], 0); + } + } + } +#if defined(USE_LIGHT) && defined(ROTARY_V1) + } +#endif + Button.press_counter[button_index] = 0; + } + } + } + + } + } + Button.last_state[button_index] = button; + } +} + +void MqttButtonTopic(uint8_t button_id, uint8_t action, uint8_t hold) +{ + char scommand[CMDSZ]; + char stopic[TOPSZ]; + char mqttstate[7]; + + SendKey(KEY_BUTTON, button_id, (hold) ? 3 : action +9); + + if (!Settings.flag.hass_discovery) { + GetTextIndexed(mqttstate, sizeof(mqttstate), action, kMultiPress); + snprintf_P(scommand, sizeof(scommand), PSTR("BUTTON%d"), button_id); + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P(S_JSON_COMMAND_SVALUE, "ACTION", (hold) ? SettingsText(SET_STATE_TXT4) : mqttstate); + MqttPublish(stopic); + } +} + +void ButtonLoop(void) +{ + if (Button.present) { + if (TimeReached(Button.debounce)) { + SetNextTimeInterval(Button.debounce, Settings.button_debounce); // ButtonDebounce (50) + ButtonHandler(); + } + } +} + +#endif // BUTTON_V2 \ No newline at end of file diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index e1cc9e812..cee21783e 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -20,38 +20,46 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_RESTART "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SAVEDATA "|" - D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" + D_CMND_SO "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|" D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" D_CMND_SERIALDELIMITER "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" - D_CMND_SPEEDUNIT "|" + D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM "|" #ifdef USE_I2C D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|" #endif #ifdef USE_DEVICE_GROUPS - D_CMND_DEVGROUP_SHARE "|" + D_CMND_DEVGROUP_NAME "|" +#ifdef USE_DEVICE_GROUPS_SEND + D_CMND_DEVGROUP_SEND "|" +#endif // USE_DEVICE_GROUPS_SEND + D_CMND_DEVGROUP_SHARE "|" D_CMND_DEVGROUPSTATUS "|" #endif // USE_DEVICE_GROUPS D_CMND_SENSOR "|" D_CMND_DRIVER; void (* const TasmotaCommand[])(void) PROGMEM = { &CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, &CmndSeriallog, &CmndRestart, &CmndPowerOnState, &CmndPulsetime, &CmndBlinktime, &CmndBlinkcount, &CmndSavedata, - &CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution, + &CmndSetoption, &CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution, &CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution, &CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange, &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, &CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, &CmndHumOffset, - &CmndSpeedUnit, + &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, #ifdef USE_I2C &CmndI2cScan, CmndI2cDriver, #endif #ifdef USE_DEVICE_GROUPS - &CmndDevGroupShare, + &CmndDevGroupName, +#ifdef USE_DEVICE_GROUPS_SEND + &CmndDevGroupSend, +#endif // USE_DEVICE_GROUPS_SEND + &CmndDevGroupShare, &CmndDevGroupStatus, #endif // USE_DEVICE_GROUPS &CmndSensor, &CmndDriver }; @@ -79,10 +87,7 @@ void ResponseCmndIdxNumber(int value) void ResponseCmndChar_P(const char* value) { - size_t buf_size = strlen_P(value); - char buf[buf_size + 1]; - strcpy_P(buf, value); - Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, buf); + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, value); } void ResponseCmndChar(const char* value) @@ -107,9 +112,11 @@ void ResponseCmndIdxChar(const char* value) void ResponseCmndAll(uint32_t text_index, uint32_t count) { + uint32_t real_index = text_index; mqtt_data[0] = '\0'; for (uint32_t i = 0; i < count; i++) { - ResponseAppend_P(PSTR("%c\"%s%d\":\"%s\""), (i) ? ',' : '{', XdrvMailbox.command, i +1, SettingsText(text_index +i)); + if ((SET_MQTT_GRP_TOPIC == text_index) && (1 == i)) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + ResponseAppend_P(PSTR("%c\"%s%d\":\"%s\""), (i) ? ',' : '{', XdrvMailbox.command, i +1, SettingsText(real_index +i)); } ResponseJsonEnd(); } @@ -172,7 +179,16 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) data_len--; } - bool grpflg = (strstr(topicBuf, SettingsText(SET_MQTT_GRP_TOPIC)) != nullptr); + bool grpflg = false; + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + char *group_topic = SettingsText(real_index +i); + if (*group_topic && strstr(topicBuf, group_topic) != nullptr) { + grpflg = true; + break; + } + } char stemp1[TOPSZ]; GetFallbackTopic_P(stemp1, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/ @@ -185,14 +201,19 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) if (type != nullptr) { type++; uint32_t i; - for (i = 0; i < strlen(type); i++) { - type[i] = toupper(type[i]); + int nLen; // strlen(type) + char *s = type; + for (nLen = 0; *s; s++, nLen++) { + *s=toupper(*s); } - while (isdigit(type[i-1])) { - i--; + i = nLen; + if (i > 0) { // may be 0 + while (isdigit(type[i-1])) { + i--; + } } - if (i < strlen(type)) { - index = atoi(type +i); + if (i < nLen) { + index = atoi(type + i); user_index = true; } type[i] = '\0'; @@ -394,20 +415,34 @@ void CmndStatus(void) if ((0 == payload) || (1 == payload)) { Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_SERIALCONFIG "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" - D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"BCResetTime\":\"%s\",\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), + D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"BCResetTime\":\"%s\",\"" D_JSON_SAVECOUNT "\":%d" +#ifdef ESP8266 + ",\"" D_JSON_SAVEADDRESS "\":\"%X\"" +#endif + "}}"), Settings.baudrate * 300, GetSerialConfig().c_str(), SettingsText(SET_MQTT_GRP_TOPIC), SettingsText(SET_OTAURL), GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, - Settings.cfg_holder, Settings.bootcount, GetDateAndTime(DT_BOOTCOUNT).c_str(), Settings.save_flag, GetSettingsAddress()); + Settings.cfg_holder, Settings.bootcount, GetDateAndTime(DT_BOOTCOUNT).c_str(), Settings.save_flag +#ifdef ESP8266 + , GetSettingsAddress() +#endif + ); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1")); } if ((0 == payload) || (2 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" - D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\"" +#ifdef ESP8266 + ",\"" D_JSON_BOOTVERSION "\":%d" +#endif + ",\"" D_JSON_COREVERSION "\":\"" ARDUINO_CORE_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," "\"Hardware\":\"%s\"" "%s}}"), - my_version, my_image, GetBuildDateAndTime().c_str(), - ESP.getBootVersion(), ESP.getSdkVersion(), + my_version, my_image, GetBuildDateAndTime().c_str() +#ifdef ESP8266 + , ESP.getBootVersion() +#endif + , ESP.getSdkVersion(), GetDeviceHardware().c_str(), GetStatistics().c_str()); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2")); @@ -426,10 +461,24 @@ void CmndStatus(void) if ((0 == payload) || (4 == payload)) { Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" - D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\"" +#ifdef ESP32 + D_JSON_PSRMAXMEMORY "\":%d,\"" D_JSON_PSRFREEMEMORY "\":%d," +#endif + D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d" +#ifdef ESP8266 + ",\"" D_JSON_FLASHCHIPID "\":\"%06X\"" +#endif + ",\"" D_JSON_FLASHMODE "\":%d,\"" D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"), - ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, - ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipId(), ESP.getFlashChipMode(), + ESP_getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP_getFreeHeap()/1024, +#ifdef ESP32 + ESP.getPsramSize()/1024, ESP.getFreePsram()/1024, +#endif + ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024 +#ifdef ESP8266 + , ESP.getFlashChipId() +#endif + , ESP.getFlashChipMode(), LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2, feature5, feature6); XsnsDriverState(); ResponseAppend_P(PSTR(",\"Sensors\":")); @@ -518,6 +567,11 @@ void CmndStatus(void) #ifdef USE_SCRIPT_STATUS if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data); #endif + + if (payload) { + XdrvRulesProcess(); // Allow rule processing on single Status command only + } + mqtt_data[0] = '\0'; } @@ -557,14 +611,41 @@ void CmndHumOffset(void) ResponseCmndFloat((float)(Settings.hum_comp) / 10, 1); } +void CmndGlobalTemp(void) +{ + if (XdrvMailbox.data_len > 0) { + float temperature = CharToFloat(XdrvMailbox.data); + if (!isnan(temperature) && Settings.flag.temperature_conversion) { // SetOption8 - Switch between Celsius or Fahrenheit + temperature = (temperature - 32) / 1.8; // Celsius + } + if ((temperature >= -50.0) && (temperature <= 100.0)) { + ConvertTemp(temperature); + global_update = 1; // Keep global values just entered valid + } + } + ResponseCmndFloat(global_temperature, 1); +} + +void CmndGlobalHum(void) +{ + if (XdrvMailbox.data_len > 0) { + float humidity = CharToFloat(XdrvMailbox.data); + if ((humidity >= 0.0) && (humidity <= 100.0)) { + ConvertHumidity(humidity); + global_update = 1; // Keep global values just entered valid + } + } + ResponseCmndFloat(global_humidity, 1); +} + void CmndSleep(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) { Settings.sleep = XdrvMailbox.payload; - sleep = XdrvMailbox.payload; + ssleep = XdrvMailbox.payload; WiFiSetSleepMode(); } - Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.sleep, sleep); + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.sleep, ssleep); } @@ -586,7 +667,7 @@ void CmndUpgrade(void) void CmndOtaUrl(void) { if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_OTAURL, (SC_DEFAULT == Shortcut()) ? OTA_URL : XdrvMailbox.data); + SettingsUpdateText(SET_OTAURL, (SC_DEFAULT == Shortcut()) ? PSTR(OTA_URL) : XdrvMailbox.data); } ResponseCmndChar(SettingsText(SET_OTAURL)); } @@ -627,7 +708,10 @@ void CmndRestart(void) void CmndPowerOnState(void) { - if (my_module_type != MOTOR) { +#ifdef ESP8266 + if (my_module_type != MOTOR) +#endif // ESP8266 + { /* 0 = Keep relays off after power on * 1 = Turn relays on after power on, if PulseTime set wait for PulseTime seconds, and turn relays off * 2 = Toggle relays after power on @@ -705,6 +789,8 @@ void CmndSavedata(void) void CmndSetoption(void) { + snprintf_P(XdrvMailbox.command, CMDSZ, PSTR(D_CMND_SETOPTION)); // Rename result shortcut command SO to SetOption + if (XdrvMailbox.index < 114) { uint32_t ptype; uint32_t pindex; @@ -803,6 +889,7 @@ void CmndSetoption(void) else if (4 == ptype) { // SetOption82 .. 113 bitWrite(Settings.flag4.data, pindex, XdrvMailbox.payload); switch (pindex) { + case 3: // SetOption85 - Enable Device Groups case 6: // SetOption88 - PWM Dimmer Buttons control remote devices restart_flag = 2; break; @@ -927,7 +1014,7 @@ void CmndModule(void) Settings.module = XdrvMailbox.payload; SetModuleType(); if (Settings.last_module != XdrvMailbox.payload) { - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { Settings.my_gp.io[i] = GPIO_NONE; } } @@ -963,17 +1050,28 @@ void CmndModules(void) void CmndGpio(void) { - if (XdrvMailbox.index < sizeof(Settings.my_gp)) { + if (XdrvMailbox.index < ARRAY_SIZE(Settings.my_gp.io)) { myio cmodule; ModuleGpios(&cmodule); - if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < GPIO_SENSOR_END)) { + if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < AGPIO(GPIO_SENSOR_END))) { bool present = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { +#ifdef ESP8266 uint32_t midx = pgm_read_byte(kGpioNiceList + i); - if (midx == XdrvMailbox.payload) { present = true; } + if (midx == XdrvMailbox.payload) { + present = true; + break; + } +#else // ESP32 + uint32_t midx = pgm_read_word(kGpioNiceList + i); + if ((XdrvMailbox.payload >= (midx & 0xFFE0)) && (XdrvMailbox.payload < midx)) { + present = true; + break; + } +#endif // ESP8266 - ESP32 } if (present) { - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { if (ValidGPIO(i, cmodule.io[i]) && (Settings.my_gp.io[i] == XdrvMailbox.payload)) { Settings.my_gp.io[i] = GPIO_NONE; } @@ -984,26 +1082,39 @@ void CmndGpio(void) } Response_P(PSTR("{")); bool jsflg = false; - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (ValidGPIO(i, cmodule.io[i]) || ((GPIO_USER == XdrvMailbox.payload) && !FlashPin(i))) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { + if (ValidGPIO(i, cmodule.io[i]) || ((AGPIO(GPIO_USER) == XdrvMailbox.payload) && !FlashPin(i))) { if (jsflg) { ResponseAppend_P(PSTR(",")); } jsflg = true; - uint8_t sensor_type = Settings.my_gp.io[i]; + uint32_t sensor_type = Settings.my_gp.io[i]; if (!ValidGPIO(i, cmodule.io[i])) { sensor_type = cmodule.io[i]; - if (GPIO_USER == sensor_type) { // A user GPIO equals a not connected (=GPIO_NONE) GPIO here + if (AGPIO(GPIO_USER) == sensor_type) { // A user GPIO equals a not connected (=GPIO_NONE) GPIO here sensor_type = GPIO_NONE; } } - uint8_t sensor_name_idx = sensor_type; + char sindex[4] = { 0 }; +#ifdef ESP8266 + uint32_t sensor_name_idx = sensor_type; +#else // ESP32 + uint32_t sensor_name_idx = sensor_type >> 5; + uint32_t nice_list_search = sensor_type & 0xFFE0; + for (uint32_t j = 0; j < ARRAY_SIZE(kGpioNiceList); j++) { + uint32_t nls_idx = pgm_read_word(kGpioNiceList + j); + if (((nls_idx & 0xFFE0) == nice_list_search) && ((nls_idx & 0x001F) > 0)) { + snprintf_P(sindex, sizeof(sindex), PSTR("%d"), (sensor_type & 0x001F) +1); + break; + } + } +#endif // ESP8266 - ESP32 const char *sensor_names = kSensorNames; - if (sensor_type > GPIO_FIX_START) { - sensor_name_idx = sensor_type - GPIO_FIX_START -1; + if (sensor_name_idx > GPIO_FIX_START) { + sensor_name_idx = sensor_name_idx - GPIO_FIX_START -1; sensor_names = kSensorNamesFixed; } char stemp1[TOPSZ]; - ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s\"}"), - i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names)); + ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s%s\"}"), + i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names), sindex); } } if (jsflg) { @@ -1020,8 +1131,14 @@ void CmndGpios(void) ModuleGpios(&cmodule); uint32_t lines = 1; bool jsflg = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { +#ifdef ESP8266 uint32_t midx = pgm_read_byte(kGpioNiceList + i); + uint32_t ridx = midx; +#else // ESP32 + uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0; + uint32_t midx = ridx >> 5; +#endif // ESP8266 - ESP32 if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } if (!jsflg) { Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":{"), lines); @@ -1030,7 +1147,7 @@ void CmndGpios(void) } jsflg = true; char stemp1[TOPSZ]; - if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), ridx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == ARRAY_SIZE(kGpioNiceList) -1)) { ResponseJsonEndEnd(); MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); jsflg = false; @@ -1062,9 +1179,9 @@ void CmndTemplate(void) if (Settings.module != USER_MODULE) { ModuleDefault(Settings.module); } - snprintf_P(Settings.user_template.name, sizeof(Settings.user_template.name), PSTR("Merged")); + SettingsUpdateText(SET_TEMPLATE_NAME, "Merged"); uint32_t j = 0; - for (uint32_t i = 0; i < sizeof(mycfgio); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { if (6 == i) { j = 9; } if (8 == i) { j = 12; } if (my_module.io[j] > GPIO_NONE) { @@ -1088,9 +1205,9 @@ void CmndTemplate(void) void CmndPwm(void) { if (pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && (pin[GPIO_PWM1 + XdrvMailbox.index -1] < 99)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && PinUsed(GPIO_PWM1, XdrvMailbox.index -1)) { Settings.pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload; - analogWrite(pin[GPIO_PWM1 + XdrvMailbox.index -1], bitRead(pwm_inverted, XdrvMailbox.index -1) ? Settings.pwm_range - XdrvMailbox.payload : XdrvMailbox.payload); + analogWrite(Pin(GPIO_PWM1, XdrvMailbox.index -1), bitRead(pwm_inverted, XdrvMailbox.index -1) ? Settings.pwm_range - XdrvMailbox.payload : XdrvMailbox.payload); } Response_P(PSTR("{")); MqttShowPWMState(); // Render the PWM status to MQTT @@ -1102,7 +1219,11 @@ void CmndPwmfrequency(void) { if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) { Settings.pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload; +#ifdef ESP8266 analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) +#else + analogWriteFreqRange(0,Settings.pwm_frequency,Settings.pwm_range); +#endif } ResponseCmndNumber(Settings.pwm_frequency); } @@ -1116,7 +1237,11 @@ void CmndPwmrange(void) Settings.pwm_value[i] = Settings.pwm_range; } } +#ifdef ESP8266 analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) +#else + analogWriteFreqRange(0,Settings.pwm_frequency,Settings.pwm_range); +#endif } ResponseCmndNumber(Settings.pwm_range); } @@ -1277,7 +1402,7 @@ void CmndNtpServer(void) uint32_t ntp_server = SET_NTPSERVER1 + XdrvMailbox.index -1; if (XdrvMailbox.data_len > 0) { SettingsUpdateText(ntp_server, - (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? NTP_SERVER1 : (2 == XdrvMailbox.index) ? NTP_SERVER2 : NTP_SERVER3 : XdrvMailbox.data); + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? PSTR(NTP_SERVER1) : (2 == XdrvMailbox.index) ? PSTR(NTP_SERVER2) : PSTR(NTP_SERVER3) : XdrvMailbox.data); SettingsUpdateText(ntp_server, ReplaceCommaWithDot(SettingsText(ntp_server))); // restart_flag = 2; // Issue #3890 ntp_force_sync = true; @@ -1485,7 +1610,7 @@ void CmndReset(void) switch (XdrvMailbox.payload) { case 1: restart_flag = 211; - ResponseCmndChar(D_JSON_RESET_AND_RESTARTING); + ResponseCmndChar(PSTR(D_JSON_RESET_AND_RESTARTING)); break; case 2 ... 6: restart_flag = 210 + XdrvMailbox.payload; @@ -1497,7 +1622,7 @@ void CmndReset(void) ResponseCmndDone(); break; default: - ResponseCmndChar(D_JSON_ONE_TO_RESET); + ResponseCmndChar(PSTR(D_JSON_ONE_TO_RESET)); } } @@ -1615,7 +1740,7 @@ void CmndAltitude(void) void CmndLedPower(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) { - if (99 == pin[GPIO_LEDLNK]) { XdrvMailbox.index = 1; } + if (!PinUsed(GPIO_LEDLNK)) { XdrvMailbox.index = 1; } if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { Settings.ledstate &= 8; // Disable power control uint32_t mask = 1 << (XdrvMailbox.index -1); // Led to control @@ -1634,14 +1759,14 @@ void CmndLedPower(void) break; } blinks = 0; - if (99 == pin[GPIO_LEDLNK]) { + if (!PinUsed(GPIO_LEDLNK)) { SetLedPower(Settings.ledstate &8); } else { SetLedPowerIdx(XdrvMailbox.index -1, (led_power & mask)); } } bool state = bitRead(led_power, XdrvMailbox.index -1); - if (99 == pin[GPIO_LEDLNK]) { + if (!PinUsed(GPIO_LEDLNK)) { state = bitRead(Settings.ledstate, 3); } ResponseCmndIdxChar(GetStateText(state)); @@ -1705,6 +1830,33 @@ void CmndI2cDriver(void) #endif // USE_I2C #ifdef USE_DEVICE_GROUPS +void CmndDevGroupName(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DEV_GROUP_NAMES)) { + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.data_len > TOPSZ) + XdrvMailbox.data[TOPSZ - 1] = 0; + else if (1 == XdrvMailbox.data_len && ('"' == XdrvMailbox.data[0] || '0' == XdrvMailbox.data[0])) + XdrvMailbox.data[0] = 0; + SettingsUpdateText(SET_DEV_GROUP_NAME1 + XdrvMailbox.index - 1, XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndAll(SET_DEV_GROUP_NAME1, MAX_DEV_GROUP_NAMES); + } +} + +#ifdef USE_DEVICE_GROUPS_SEND +void CmndDevGroupSend(void) +{ + uint8_t device_group_index = (XdrvMailbox.usridx ? XdrvMailbox.index - 1 : 0); + if (device_group_index < device_group_count) { + if (!_SendDeviceGroupMessage(device_group_index, DGR_MSGTYPE_UPDATE_COMMAND)) { + ResponseCmndChar(XdrvMailbox.data); + } + } +} +#endif // USE_DEVICE_GROUPS_SEND + void CmndDevGroupShare(void) { uint32_t parm[2] = { Settings.device_group_share_in, Settings.device_group_share_out }; @@ -1713,6 +1865,11 @@ void CmndDevGroupShare(void) Settings.device_group_share_out = parm[1]; Response_P(PSTR("{\"" D_CMND_DEVGROUP_SHARE "\":{\"In\":\"%X\",\"Out\":\"%X\"}}"), Settings.device_group_share_in, Settings.device_group_share_out); } + +void CmndDevGroupStatus(void) +{ + DeviceGroupStatus((XdrvMailbox.usridx ? XdrvMailbox.index - 1 : 0)); +} #endif // USE_DEVICE_GROUPS void CmndSensor(void) diff --git a/tasmota/support_crash_recorder.ino b/tasmota/support_crash_recorder.ino index cc9721cb4..dd0834550 100644 --- a/tasmota/support_crash_recorder.ino +++ b/tasmota/support_crash_recorder.ino @@ -17,6 +17,8 @@ along with this program. If not, see . */ +#ifdef ESP8266 + const uint32_t crash_magic = 0x53415400; // Stack trace magic number (TASx) const uint32_t crash_rtc_offset = 32; // Offset in RTC memory skipping OTA used block const uint32_t crash_dump_max_len = 31; // Dump only 31 call addresses to satisfy max JSON length of about 600 characters @@ -109,3 +111,5 @@ void CrashDump(void) ResponseJsonEnd(); } + +#endif // ESP8266 \ No newline at end of file diff --git a/tasmota/support_device_groups.ino b/tasmota/support_device_groups.ino index f88bc5c93..30b5bacc0 100644 --- a/tasmota/support_device_groups.ino +++ b/tasmota/support_device_groups.ino @@ -20,25 +20,29 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ + #ifdef USE_DEVICE_GROUPS //#define DEVICE_GROUPS_DEBUG #define DGR_MEMBER_TIMEOUT 45000 #define DGR_ANNOUNCEMENT_INTERVAL 60000 +#define DEVICE_GROUP_MESSAGE "TASMOTA_DGR" -extern bool udp_connected; +const char kDeviceGroupMessage[] PROGMEM = DEVICE_GROUP_MESSAGE; struct device_group_member { struct device_group_member * flink; IPAddress ip_address; uint16_t received_sequence; uint16_t acked_sequence; + uint32_t unicast_count; }; struct device_group { uint32_t next_announcement_time; uint32_t next_ack_check_time; uint32_t member_timeout_time; + uint16_t outgoing_sequence; uint16_t last_full_status_sequence; uint16_t message_length; uint16_t ack_check_interval; @@ -46,34 +50,92 @@ struct device_group { uint8_t initial_status_requests_remaining; bool local; char group_name[TOPSZ]; - char message[128]; - uint8_t group_member_count; + uint8_t message[128]; struct device_group_member * device_group_members; +#ifdef USE_DEVICE_GROUPS_SEND + uint8_t values_8bit[DGR_ITEM_LAST_8BIT]; + uint16_t values_16bit[DGR_ITEM_LAST_16BIT - DGR_ITEM_MAX_8BIT - 1]; + uint32_t values_32bit[DGR_ITEM_LAST_32BIT - DGR_ITEM_MAX_16BIT - 1]; +#endif // USE_DEVICE_GROUPS_SEND }; +WiFiUDP device_groups_udp; struct device_group * device_groups; -uint32_t next_check_time = 0; -uint16_t outgoing_sequence = 0; +uint32_t next_check_time; bool device_groups_initialized = false; -bool device_groups_initialization_failed = false; +bool device_groups_up = false; bool building_status_message = false; -bool processing_remote_device_message = false; -bool udp_was_connected = false; +bool ignore_dgr_sends = false; + +char * IPAddressToString(const IPAddress& ip_address) +{ + static char buffer[16]; + sprintf_P(buffer, PSTR("%u.%u.%u.%u"), ip_address[0], ip_address[1], ip_address[2], ip_address[3]); + return buffer; +} + +uint8_t * BeginDeviceGroupMessage(struct device_group * device_group, uint16_t flags, bool hold_sequence = false) +{ + uint8_t * message_ptr = &device_group->message[device_group->message_header_length]; + if (!hold_sequence && !++device_group->outgoing_sequence) device_group->outgoing_sequence = 1; + *message_ptr++ = device_group->outgoing_sequence & 0xff; + *message_ptr++ = device_group->outgoing_sequence >> 8; + *message_ptr++ = flags & 0xff; + *message_ptr++ = flags >> 8; + return message_ptr; +} + +// Return true if we're configured to share the specified item. +bool DeviceGroupItemShared(bool incoming, uint8_t item) +{ + uint32_t mask; + if (item == DGR_ITEM_LIGHT_BRI || item == DGR_ITEM_BRI_POWER_ON) + mask = DGR_SHARE_LIGHT_BRI; + else if (item == DGR_ITEM_POWER) + mask = DGR_SHARE_POWER; + else if (item == DGR_ITEM_LIGHT_SCHEME) + mask = DGR_SHARE_LIGHT_SCHEME; + else if (item == DGR_ITEM_LIGHT_FIXED_COLOR || DGR_ITEM_LIGHT_CHANNELS) + mask = DGR_SHARE_LIGHT_COLOR; + else if (item == DGR_ITEM_LIGHT_FADE || item == DGR_ITEM_LIGHT_SPEED) + mask = DGR_SHARE_LIGHT_FADE; + else if (item == DGR_ITEM_BRI_PRESET_LOW || item == DGR_ITEM_BRI_PRESET_HIGH) + mask = DGR_SHARE_DIMMER_SETTINGS; + else if (item == DGR_ITEM_EVENT) + mask = DGR_SHARE_EVENT; + else + return true; + return mask & (incoming ? Settings.device_group_share_in : Settings.device_group_share_out); +} void DeviceGroupsInit(void) { - // Initialize the device information for each device group. The group name is the MQTT group topic. + // If there are more device group names set than the number of device groups needed by the + // mdoule, use the device group name count as the device group count. + for (; device_group_count < MAX_DEV_GROUP_NAMES; device_group_count++) { + if (!*SettingsText(SET_DEV_GROUP_NAME1 + device_group_count)) break; + } + + // Initialize the device information for each device group. device_groups = (struct device_group *)calloc(device_group_count, sizeof(struct device_group)); - if (device_groups == nullptr) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: error allocating %u-element device group array"), device_group_count); - device_groups_initialization_failed = true; + if (!device_groups) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating %u-element array"), device_group_count); return; } - for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++) { - struct device_group * device_group = &device_groups[device_group_index]; - strcpy(device_group->group_name, SettingsText((device_group_index == 0 ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + device_group_index - 1))); - device_group->message_header_length = sprintf_P(device_group->message, PSTR("%s%s HTTP/1.1\n\n"), kDeviceGroupMessage, device_group->group_name); + struct device_group * device_group = device_groups; + for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++, device_group++) { + strcpy(device_group->group_name, SettingsText(SET_DEV_GROUP_NAME1 + device_group_index)); + + // If the device group name is not set, use the MQTT group topic (with the device group index + + // 1 appended for device group indices > 0). + if (!device_group->group_name[0]) { + strcpy(device_group->group_name, SettingsText(SET_MQTT_GRP_TOPIC)); + if (device_group_index) { + snprintf_P(device_group->group_name, sizeof(device_group->group_name), PSTR("%s%u"), device_group->group_name, device_group_index + 1); + } + } + device_group->message_header_length = sprintf_P((char *)device_group->message, PSTR("%s%s"), kDeviceGroupMessage, device_group->group_name) + 1; device_group->last_full_status_sequence = -1; } @@ -87,450 +149,147 @@ void DeviceGroupsInit(void) device_groups_initialized = true; } -char * IPAddressToString(const IPAddress& ip_address) +void DeviceGroupsStart() { - static char buffer[16]; - sprintf_P(buffer, PSTR("%u.%u.%u.%u"), ip_address[0], ip_address[1], ip_address[2], ip_address[3]); - return buffer; -} + if (Settings.flag4.device_groups_enabled && !device_groups_up && !restart_flag) { -char * BeginDeviceGroupMessage(struct device_group * device_group, uint16_t flags, bool hold_sequence = false) -{ - char * message_ptr = &device_group->message[device_group->message_header_length]; - if (!hold_sequence && !++outgoing_sequence) outgoing_sequence = 1; - *message_ptr++ = outgoing_sequence & 0xff; - *message_ptr++ = outgoing_sequence >> 8; - *message_ptr++ = flags & 0xff; - *message_ptr++ = flags >> 8; - return message_ptr; -} - -// Return true if we're configured to share the specified item. -bool DeviceGroupItemShared(bool incoming, uint8_t item) -{ - uint8_t mask = 0; - switch (item) { - case DGR_ITEM_LIGHT_BRI: - case DGR_ITEM_BRI_POWER_ON: - mask = DGR_SHARE_LIGHT_BRI; - break; - case DGR_ITEM_POWER: - mask = DGR_SHARE_POWER; - break; - case DGR_ITEM_LIGHT_SCHEME: - mask = DGR_SHARE_LIGHT_SCHEME; - break; - case DGR_ITEM_LIGHT_FIXED_COLOR: - case DGR_ITEM_LIGHT_CHANNELS: - mask = DGR_SHARE_LIGHT_COLOR; - break; - case DGR_ITEM_LIGHT_FADE: - case DGR_ITEM_LIGHT_SPEED: - mask = DGR_SHARE_LIGHT_FADE; - break; - case DGR_ITEM_BRI_PRESET_LOW: - case DGR_ITEM_BRI_PRESET_HIGH: - mask = DGR_SHARE_DIMMER_SETTINGS; - break; - } - return (!mask || ((incoming ? Settings.device_group_share_in : Settings.device_group_share_out) & mask)); -} - -void SendDeviceGroupPacket(IPAddress ip, char * packet, int len, const char * label) -{ - if (!ip) ip = IPAddress(239,255,255,250); - for (int attempt = 1; attempt <= 5; attempt++) { - if (PortUdp.beginPacket(ip, 1900)) { - PortUdp.write(packet, len); - if (PortUdp.endPacket()) return; + // If we haven't successfuly initialized device groups yet, attempt to do it now. + if (!device_groups_initialized) { + DeviceGroupsInit(); + if (!device_groups_initialized) return; } - delay(10); - } - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: error sending %s packet"), label); -} -void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType message_type, ...) -{ - // If device groups are not enabled, ignore this request. - if (!Settings.flag4.device_groups_enabled) return; - - // If UDP is not set up, ignore this request. - if (!udp_connected) return; - - // If we're currently processing a remote device message, ignore this request. - if (processing_remote_device_message) return; - - // Get a pointer to the device information for this device. - device_group * device_group = &device_groups[device_group_index]; - - // If we're still sending initial status requests, ignore this request. - if (device_group->initial_status_requests_remaining) return; - - // A full status request is a request from a remote device for the status of every item we - // control. As long as we're building it, we may as well multicast the status update to all - // device group members. - char * message_ptr = &device_group->message[device_group->message_header_length]; - if (message_type == DGR_MSGTYP_FULL_STATUS) { - - // Set the flag indicating we're currently building a status message. SendDeviceGroupMessage - // will build but not send messages while this flag is set. - building_status_message = true; - - // Call the drivers to build the status update. - if (!++outgoing_sequence) outgoing_sequence = 1; -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Building device group %s full status packet"), device_group->group_name); -#endif // DEVICE_GROUPS_DEBUG - device_group->message_length = 0; - SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_POWER, power); - XdrvMailbox.command_code = DGR_ITEM_STATUS; - XdrvCall(FUNC_DEVICE_GROUP_ITEM); - building_status_message = false; - - // If we have nothing to share with the other members, restore the message sequence and return. - if (!device_group->message_length) { - if (!--outgoing_sequence) outgoing_sequence = -1; + // Subscribe to device groups multicasts. + if (!device_groups_udp.beginMulticast(WiFi.localIP(), IPAddress(DEVICE_GROUPS_ADDRESS), DEVICE_GROUPS_PORT)) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error subscribing")); return; } - device_group->last_full_status_sequence = outgoing_sequence; + device_groups_up = true; - // Set the status update flag in the outgoing message. - *(message_ptr + 2) |= DGR_FLAG_FULL_STATUS; - } - - else { - bool shared; - uint8_t item; - uint32_t value; - uint8_t * value_ptr; - va_list ap; - -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Building device group %s packet"), device_group->group_name); -#endif // DEVICE_GROUPS_DEBUG - - value = 0; - if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) - value |= DGR_FLAG_MORE_TO_COME; - else if (message_type == DGR_MSGTYP_UPDATE_DIRECT) - value |= DGR_FLAG_DIRECT; -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">sequence=%u, flags=%u"), outgoing_sequence, value); -#endif // DEVICE_GROUPS_DEBUG - message_ptr = BeginDeviceGroupMessage(device_group, value, building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE); - - // If we're still building this update or all group members haven't acknowledged the previous - // update yet, update the message to include these new updates. First we need to rebuild the - // previous update message to remove any items and their values that are included in this new - // update. - if (device_group->message_length) { - uint8_t item_array[32]; - int item_index = 0; - int kept_item_count = 0; - - // Build an array of all the items in this new update. - va_start(ap, message_type); - while ((item = va_arg(ap, int))) { - item_array[item_index++] = item; - if (item <= DGR_ITEM_MAX_32BIT) - va_arg(ap, int); - else if (item <= DGR_ITEM_MAX_STRING) - va_arg(ap, char *); - else { - switch (item) { - case DGR_ITEM_LIGHT_CHANNELS: - va_arg(ap, uint8_t *) ; - break; - } - } - } - va_end(ap); - item_array[item_index] = 0; - - // Rebuild the previous update message, removing any items their values that are included in - // this new update. - char * previous_message_ptr = message_ptr; - while (item = *previous_message_ptr++) { - - // Search for this item in the new update. - for (item_index = 0; item_array[item_index]; item_index++) { - if (item_array[item_index] == item) break; - } - - // If this item was found in the new update skip over it and it's value. - if (item_array[item_index]) { - if (item <= DGR_ITEM_MAX_32BIT) { - previous_message_ptr++; - if (item > DGR_ITEM_MAX_8BIT) { - previous_message_ptr++; - if (item > DGR_ITEM_MAX_16BIT) { - previous_message_ptr++; - previous_message_ptr++; - } - } - } - else if (item <= DGR_ITEM_MAX_STRING) - previous_message_ptr += *previous_message_ptr++; - else { - switch (item) { - case DGR_ITEM_LIGHT_CHANNELS: - previous_message_ptr += 5; - break; - } - } - } - - // If this item was not found in the new udpate, copy it to the new update message. - else { - *message_ptr++ = item; - if (item <= DGR_ITEM_MAX_32BIT) { - *message_ptr++ = *previous_message_ptr++; - if (item > DGR_ITEM_MAX_8BIT) { - *message_ptr++ = *previous_message_ptr++; - if (item > DGR_ITEM_MAX_16BIT) { - *message_ptr++ = *previous_message_ptr++; - *message_ptr++ = *previous_message_ptr++; - } - } - } - else if (item <= DGR_ITEM_MAX_STRING) { - *message_ptr++ = value = *previous_message_ptr++; - memmove(message_ptr, previous_message_ptr, value); - previous_message_ptr += value; - message_ptr += value; - } - else { - switch (item) { - case DGR_ITEM_LIGHT_CHANNELS: - memmove(message_ptr, previous_message_ptr, 5); - previous_message_ptr += 5; - message_ptr += 5; - break; - } - } - kept_item_count++; - } - } -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%u items carried over from previous update"), kept_item_count); -#endif // DEVICE_GROUPS_DEBUG + // The WiFi was down but now it's up and device groups is initialized. (Re-)discover devices in + // our device group(s). Load the status request message for all device groups. This message will + // be multicast 10 times at 200ms intervals. + next_check_time = millis() + 2000; + struct device_group * device_group = device_groups; + for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++, device_group++) { + device_group->next_announcement_time = -1; + device_group->message_length = BeginDeviceGroupMessage(device_group, DGR_FLAG_RESET | DGR_FLAG_STATUS_REQUEST) - device_group->message; + device_group->initial_status_requests_remaining = 10; + device_group->next_ack_check_time = next_check_time; } - - // Itertate through the passed items adding them and their values to the message. - va_start(ap, message_type); - while ((item = va_arg(ap, int))) { - shared = DeviceGroupItemShared(false, item); - if (shared) *message_ptr++ = item; - if (item <= DGR_ITEM_MAX_32BIT) { - value = va_arg(ap, int); - if (shared) { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">item=%u, value=%u"), item, value); -#endif // DEVICE_GROUPS_DEBUG - *message_ptr++ = value & 0xff; - if (item > DGR_ITEM_MAX_8BIT) { - value >>= 8; - *message_ptr++ = value & 0xff; - } - if (item > DGR_ITEM_MAX_16BIT) { - value >>= 8; - *message_ptr++ = value & 0xff; - *message_ptr++ = (item == DGR_ITEM_POWER ? devices_present : value >> 8); - } - } - } - else if (item <= DGR_ITEM_MAX_STRING) { - value_ptr = va_arg(ap, uint8_t *); - if (shared) { - value = strlen((const char *)value_ptr); -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">item=%u, value=%s"), item, value_ptr); -#endif // DEVICE_GROUPS_DEBUG - *message_ptr++ = value; - memcpy(message_ptr, value_ptr, value); - message_ptr += value; - } - } - else { - switch (item) { - case DGR_ITEM_LIGHT_CHANNELS: - value_ptr = va_arg(ap, uint8_t *); - if (shared) { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">item=%u, value=%u,%u,%u,%u,%u"), item, *value_ptr, *(value_ptr + 1), *(value_ptr + 2), *(value_ptr + 3), *(value_ptr + 4)); -#endif // DEVICE_GROUPS_DEBUG - memmove(message_ptr, value_ptr, 5); - message_ptr += 5; - } - break; - } - } - } - va_end(ap); - - // Add the EOL item code and calculate the message length. - *message_ptr++ = 0; - device_group->message_length = message_ptr - device_group->message; - - // If there's going to be more items added to this message, return. - if (building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE) return; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: (Re)discovering members")); } - - // Multicast the packet. -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: sending %u-byte device group %s packet via multicast, sequence=%u"), device_group->message_length, device_group->group_name, device_group->message[device_group->message_header_length] | device_group->message[device_group->message_header_length + 1] << 8); -#endif // DEVICE_GROUPS_DEBUG - SendDeviceGroupPacket(0, device_group->message, device_group->message_length, PSTR("Multicast")); - - uint32_t now = millis(); - if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) { - device_group->next_ack_check_time = 0; -// for (struct device_group_member * device_group_member = device_group->device_group_members; device_group_member != nullptr; device_group_member = device_group_member->flink) { -// device_group_member->acked_sequence = outgoing_sequence; -// } - } - else { - device_group->ack_check_interval = 100; - device_group->next_ack_check_time = now + device_group->ack_check_interval; - if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time; - device_group->member_timeout_time = now + DGR_MEMBER_TIMEOUT; - } - - device_group->next_announcement_time = now + DGR_ANNOUNCEMENT_INTERVAL; - if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; } -void ProcessDeviceGroupMessage(char * packet, int packet_length) +void DeviceGroupsStop() { - // Make the group name a null-terminated string. - char * message_group_name = packet + sizeof(DEVICE_GROUP_MESSAGE) - 1; - char * message_ptr = strchr(message_group_name, ' '); - if (message_ptr == nullptr) return; - *message_ptr = 0; + device_groups_udp.flush(); + device_groups_up = false; +} - // Search for a device group with the target group name. If one isn't found, return. - struct device_group * device_group; - uint32_t device_group_index = 0; - for (;;) { - device_group = &device_groups[device_group_index]; - if (!strcmp(message_group_name, device_group->group_name)) break; - if (++device_group_index >= device_group_count) return; - } - *message_ptr++ = ' '; - - // Find the group member. If this is a new group member, add it. - IPAddress remote_ip = PortUdp.remoteIP(); - struct device_group_member * * flink = &device_group->device_group_members; - struct device_group_member * device_group_member; - for (;;) { - device_group_member = *flink; - if (!device_group_member) { - device_group_member = (struct device_group_member *)calloc(1, sizeof(struct device_group_member)); - if (device_group_member == nullptr) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: error allocating device group member block")); - return; - } -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: adding member %s (%p)"), IPAddressToString(remote_ip), device_group_member); -#endif // DEVICE_GROUPS_DEBUG - device_group_member->ip_address = remote_ip; - *flink = device_group_member; - break; - } - else if (device_group_member->ip_address == remote_ip) { - break; - } - flink = &device_group_member->flink; - } - - // Find the start of the actual message (after the http header). - message_ptr = strstr_P(message_ptr, PSTR("\n\n")); - if (message_ptr == nullptr) return; - message_ptr += 2; - - bool light_fade; - uint16_t flags; - uint16_t item; +void SendReceiveDeviceGroupMessage(struct device_group * device_group, struct device_group_member * device_group_member, uint8_t * message, int message_length, bool received) +{ + char log_buffer[512]; + bool item_processed = false; uint16_t message_sequence; - int32_t value; + uint16_t flags; + int device_group_index = device_group - device_groups; + int log_length; + int log_remaining; + char * log_ptr; + + // Find the end and start of the actual message (after the header). + uint8_t * message_end_ptr = message + message_length; + uint8_t * message_ptr = message + strlen((char *)message) + 1; // Get the message sequence and flags. - if (packet_length - (message_ptr - packet) < 4) goto badmsg; // Malformed message - must be at least 16-bit sequence, 16-bit flags left + if (message_ptr + 4 > message_end_ptr) goto badmsg; // Malformed message - must be at least 16-bit sequence, 16-bit flags left message_sequence = *message_ptr++; message_sequence |= *message_ptr++ << 8; flags = *message_ptr++; flags |= *message_ptr++ << 8; -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Received device group %s packet from %s: sequence=%u, flags=%u"), device_group->group_name, IPAddressToString(remote_ip), message_sequence, flags); -#endif // DEVICE_GROUPS_DEBUG - // If this is an announcement, simply return. - if (flags == DGR_FLAG_ANNOUNCEMENT) return; + // Initialize the log buffer. + log_length = sprintf(log_buffer, PSTR("DGR: %s %s message %s %s: seq=%u, flags=%u"), (received ? PSTR("Received") : PSTR("Sending")), device_group->group_name, (received ? PSTR("from") : PSTR("to")), (device_group_member ? IPAddressToString(device_group_member->ip_address) : received ? PSTR("local") : PSTR("network")), message_sequence, flags); + log_ptr = log_buffer + log_length; + log_remaining = sizeof(log_buffer) - log_length; - // If this is an ack message, save the message sequence if it's newwer than the last ack we + // If this is an announcement, just log it. + if (flags == DGR_FLAG_ANNOUNCEMENT) goto write_log; + + // If this is a received ack message, save the message sequence if it's newer than the last ack we // received from this member. if (flags == DGR_FLAG_ACK) { - if (message_sequence > device_group_member->acked_sequence || device_group_member->acked_sequence - message_sequence < 64536) { + if (received && device_group_member && (message_sequence > device_group_member->acked_sequence || device_group_member->acked_sequence - message_sequence < 64536)) { device_group_member->acked_sequence = message_sequence; } -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("acked_sequence != device_group->last_full_status_sequence) { - _SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_FULL_STATUS); + // If this is a received message, send an ack message to the sender. + if (device_group_member) { + if (received) { + if (!(flags & DGR_FLAG_MORE_TO_COME)) { + *(message_ptr - 2) = DGR_FLAG_ACK; + *(message_ptr - 1) = 0; + SendReceiveDeviceGroupMessage(device_group, device_group_member, message, message_ptr - message, false); + } } -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("received_sequence) { - if (message_sequence == device_group_member->received_sequence || device_group_member->received_sequence - message_sequence > 64536) { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("acked_sequence); + log_ptr += log_length; + log_remaining -= log_length; + goto write_log; } } - device_group_member->received_sequence = message_sequence; - // Set the flag indicating we're currently processing a remote device message. - // SendDeviceGroupMessage will not send messages while this flag is set. - processing_remote_device_message = true; + // If this is a status request message, skip item processing. + if ((flags & DGR_FLAG_STATUS_REQUEST)) goto write_log; - /* - XdrvMailbox - bool grpflg - bool usridx - uint16_t command_code Item code - uint32_t index 0:15 Flags, 16:23 Device group index - uint32_t data_len String item value length - int32_t payload Integer item value - char *topic - char *data Pointer to non-integer item value - char *command nullptr - */ - XdrvMailbox.command = nullptr; // Indicates the source is a device group update - XdrvMailbox.index = flags | device_group_index << 16; - if (flags & (DGR_FLAG_MORE_TO_COME | DGR_FLAG_DIRECT)) skip_light_fade = true; + // If this is a received message, ... + if (received) { + // If we already processed this or a later message from this group member, ignore this message. + if (device_group_member) { + if (message_sequence <= device_group_member->received_sequence) { + if (message_sequence == device_group_member->received_sequence || device_group_member->received_sequence - message_sequence > 64536) { + log_length = snprintf(log_ptr, log_remaining, PSTR(" (old)")); + log_ptr += log_length; + log_remaining -= log_length; + goto write_log; + } + } + device_group_member->received_sequence = message_sequence; + } + + /* + XdrvMailbox + bool grpflg + bool usridx + uint16_t command_code Item code + uint32_t index 0:15 Flags, 16:31 Message sequence + uint32_t data_len String item value length + int32_t payload Integer item value + char *topic Pointer to device group index + char *data Pointer to non-integer item value + char *command nullptr + */ + XdrvMailbox.command = nullptr; // Indicates the source is a device group update + XdrvMailbox.index = flags | message_sequence << 16; + XdrvMailbox.topic = (char *)&device_group_index; + if (flags & (DGR_FLAG_MORE_TO_COME | DGR_FLAG_DIRECT)) skip_light_fade = true; + + // Set the flag to ignore device group send message request so callbacks from the drivers do not + // send updates. + ignore_dgr_sends = true; + } + + uint8_t item; + int32_t value; for (;;) { - if (packet_length - (message_ptr - packet) < 1) goto badmsg; // Malformed message + if (message_ptr >= message_end_ptr) goto badmsg; // Malformed message item = *message_ptr++; if (!item) break; // Done @@ -545,14 +304,18 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length) case DGR_ITEM_BRI_PRESET_HIGH: case DGR_ITEM_BRI_POWER_ON: case DGR_ITEM_POWER: + case DGR_ITEM_EVENT: case DGR_ITEM_LIGHT_CHANNELS: break; default: - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: ********** invalid item=%u received from device group %s member %s"), item, device_group->group_name, IPAddressToString(remote_ip)); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: *** Invalid item=%u"), item); } #endif // DEVICE_GROUPS_DEBUG - XdrvMailbox.command_code = item; + log_length = snprintf(log_ptr, log_remaining, PSTR(", %u="), item); + log_ptr += log_length; + log_remaining -= log_length; + log_length = 0; if (item <= DGR_ITEM_LAST_32BIT) { value = *message_ptr++; if (item > DGR_ITEM_MAX_8BIT) { @@ -560,214 +323,575 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length) if (item > DGR_ITEM_MAX_16BIT) { value |= *message_ptr++ << 16; value |= *message_ptr++ << 24; +#ifdef USE_DEVICE_GROUPS_SEND + device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1] = (item == DGR_ITEM_POWER ? value & 0xffffff : value); +#endif // USE_DEVICE_GROUPS_SEND } +#ifdef USE_DEVICE_GROUPS_SEND + else { + device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] = value; + } +#endif // USE_DEVICE_GROUPS_SEND } -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("= packet_length - (message_ptr - packet)) goto badmsg; // Malformed message -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("values_8bit[item] = value; + } +#endif // USE_DEVICE_GROUPS_SEND + log_length = snprintf(log_ptr, log_remaining, PSTR("%u"), value); } else { - switch (item) { - case DGR_ITEM_LIGHT_CHANNELS: -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("local) { - uint8_t mask_devices = value >> 24; - if (mask_devices > devices_present) mask_devices = devices_present; - for (uint32_t i = 0; i < devices_present; i++) { - uint32_t mask = 1 << i; - bool on = (value & mask); - if (on != (power & mask)) ExecuteCommandPower(i + 1, (on ? POWER_ON : POWER_OFF), SRC_REMOTE); - } - } + value = *message_ptr++; + if (received) XdrvMailbox.data = (char *)message_ptr; + if (message_ptr + value >= message_end_ptr) goto badmsg; // Malformed message + if (item <= DGR_ITEM_MAX_STRING) { + log_length = snprintf(log_ptr, log_remaining, PSTR("'%s'"), message_ptr); } else { - XdrvCall(FUNC_DEVICE_GROUP_ITEM); + switch (item) { + case DGR_ITEM_LIGHT_CHANNELS: + log_length = snprintf(log_ptr, log_remaining, PSTR("%u,%u,%u,%u,%u,%u"), *message_ptr, *(message_ptr + 1), *(message_ptr + 2), *(message_ptr + 3), *(message_ptr + 4), *(message_ptr + 5)); + break; + } + } + message_ptr += value; + } + log_ptr += log_length; + log_remaining -= log_length; + + if (received && DeviceGroupItemShared(true, item)) { + item_processed = true; + XdrvMailbox.command_code = item; + XdrvMailbox.payload = value; + XdrvMailbox.data_len = value; + *log_ptr++ = '*'; + log_remaining--; + switch (item) { + case DGR_ITEM_POWER: + if (device_group->local) { + uint8_t mask_devices = value >> 24; + if (mask_devices > devices_present) mask_devices = devices_present; + for (uint32_t i = 0; i < devices_present; i++) { + uint32_t mask = 1 << i; + bool on = (value & mask); + if (on != (power & mask)) ExecuteCommandPower(i + 1, (on ? POWER_ON : POWER_OFF), SRC_REMOTE); + } + } + break; +#ifdef USE_RULES + case DGR_ITEM_EVENT: + CmndEvent(); + break; +#endif + case DGR_ITEM_COMMAND: + ExecuteCommand(XdrvMailbox.data, SRC_REMOTE); + break; + } + XdrvCall(FUNC_DEVICE_GROUP_ITEM); + } + } + + if (received) { + if (item_processed) { + XdrvMailbox.command_code = DGR_ITEM_EOL; + XdrvCall(FUNC_DEVICE_GROUP_ITEM); + } + } + +write_log: + *log_ptr++ = 0; + AddLog_P(LOG_LEVEL_DEBUG_MORE, log_buffer); + + // If this is a received status request message, then if the requestor didn't just ack our + // previous full status update, send a full status update. + if (received) { + if ((flags & DGR_FLAG_STATUS_REQUEST)) { + if ((flags & DGR_FLAG_RESET) || device_group_member->acked_sequence != device_group->last_full_status_sequence) { + _SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_FULL_STATUS); } } } - XdrvMailbox.command_code = DGR_ITEM_EOL; - XdrvCall(FUNC_DEVICE_GROUP_ITEM); - skip_light_fade = false; - - processing_remote_device_message = false; -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ip_address : IPAddress(DEVICE_GROUPS_ADDRESS)); + for (attempt = 1; attempt <= 5; attempt++) { + if (device_groups_udp.beginPacket(ip_address, DEVICE_GROUPS_PORT)) { + device_groups_udp.write(message, message_length); + if (device_groups_udp.endPacket()) break; + } + delay(10); + } + if (attempt > 5) AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error sending message")); + } + goto cleanup; badmsg: - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: malformed message received from %s"), IPAddressToString(remote_ip)); + AddLog_P(LOG_LEVEL_ERROR, PSTR("%s ** incorrect length"), log_buffer); + +cleanup: + if (received) { + skip_light_fade = false; + ignore_dgr_sends = false; + } +} + +bool _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType message_type, ...) +{ + // If device groups is not up, ignore this request. + if (!device_groups_up) return 1; + + // If we're currently processing a remote device message, ignore this request. + if (ignore_dgr_sends && message_type != DGR_MSGTYPE_UPDATE_COMMAND) return 0; + + // Get a pointer to the device information for this device. + struct device_group * device_group = &device_groups[device_group_index]; + + // If we're still sending initial status requests, ignore this request. + if (device_group->initial_status_requests_remaining) return 1; + + // Load the message header, sequence and flags. #ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("packet_length=%u, offset=%u"), packet_length, message_ptr - packet); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Building %s %spacket"), device_group->group_name, (message_type == DGR_MSGTYP_FULL_STATUS ? PSTR("full status ") : PSTR(""))); #endif // DEVICE_GROUPS_DEBUG - processing_remote_device_message = false; + uint16_t original_sequence = device_group->outgoing_sequence; + uint16_t flags = 0; + if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) + flags = DGR_FLAG_MORE_TO_COME; + else if (message_type == DGR_MSGTYP_UPDATE_DIRECT) + flags = DGR_FLAG_DIRECT; + uint8_t * message_ptr = BeginDeviceGroupMessage(device_group, flags, building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE); + + // A full status request is a request from a remote device for the status of every item we + // control. As long as we're building it, we may as well multicast the status update to all + // device group members. + if (message_type == DGR_MSGTYP_FULL_STATUS) { + device_group->last_full_status_sequence = device_group->outgoing_sequence; + device_group->message_length = 0; + + // Set the flag indicating we're currently building a status message. SendDeviceGroupMessage + // will build but not send messages while this flag is set. + building_status_message = true; + + // Call the drivers to build the status update. + if (device_group->local) SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_POWER, power); + XdrvMailbox.index = device_group_index << 16; + XdrvMailbox.command_code = DGR_ITEM_STATUS; + XdrvMailbox.topic = (char *)&device_group_index; + XdrvCall(FUNC_DEVICE_GROUP_ITEM); + building_status_message = false; + + // Set the status update flag in the outgoing message. + *(message_ptr - 2) |= DGR_FLAG_FULL_STATUS; + + // If we have nothing to share with the other members, just send the EOL item. + if (!device_group->message_length) { + *message_ptr++ = 0; + device_group->message_length = message_ptr - device_group->message; + } + } + + else { +#ifdef USE_DEVICE_GROUPS_SEND + uint8_t out_buffer[128]; + bool escaped; + char chr; + char oper; + uint32_t old_value; + uint8_t * out_ptr = out_buffer; +#endif // USE_DEVICE_GROUPS_SEND + struct item { + uint8_t item; + uint32_t value; + void * value_ptr; + } item_array[32]; + bool shared; + uint8_t item; + uint32_t value; + uint8_t * value_ptr; + uint8_t * first_item_ptr = message_ptr; + struct item * item_ptr; + va_list ap; + + // Build an array of all the items and values in this update. + item_ptr = item_array; +#ifdef USE_DEVICE_GROUPS_SEND + if (message_type == DGR_MSGTYPE_UPDATE_COMMAND) { + value_ptr = (uint8_t *)XdrvMailbox.data; + while ((item = strtoul((char *)value_ptr, (char **)&value_ptr, 0))) { + item_ptr->item = item; + if (*value_ptr != '=') return 1; + value_ptr++; + if (item <= DGR_ITEM_MAX_32BIT) { + oper = 0; + if (*value_ptr == '@') { + oper = value_ptr[1]; + value_ptr += 2; + } + value = (isdigit(*value_ptr) ? strtoul((char *)value_ptr, (char **)&value_ptr, 0) : 1); + if (oper) { + old_value = (item <= DGR_ITEM_MAX_8BIT ? device_group->values_8bit[item] : (item <= DGR_ITEM_MAX_16BIT ? device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] : device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1])); + value = (oper == '+' ? old_value + value : (oper == '-' ? old_value - value : (oper == '^' ? old_value ^ (value ? value : 0xffffffff) : old_value))); + } + item_ptr->value = value; + } + else { + item_ptr->value_ptr = out_ptr; + if (item <= DGR_ITEM_MAX_STRING) { + escaped = false; + while ((chr = *value_ptr++)) { + if (chr == ' ' && !escaped) break; + if (!(escaped = (chr == '\\' && !escaped))) *out_ptr++ = chr; + } + *out_ptr = 0; + } + else { + switch (item) { + case DGR_ITEM_LIGHT_CHANNELS: + for (int i = 0; i < 6; i++) { + *out_ptr++ = strtoul((char *)value_ptr, (char **)&value_ptr, 0); + if (*value_ptr == ',') value_ptr++; + } + break; + } + } + } + item_ptr++; + } + } + else { +#endif // USE_DEVICE_GROUPS_SEND + va_start(ap, message_type); + while ((item = va_arg(ap, int))) { + item_ptr->item = item; + if (item <= DGR_ITEM_MAX_32BIT) + item_ptr->value = va_arg(ap, int); + else if (item <= DGR_ITEM_MAX_STRING) + item_ptr->value_ptr = va_arg(ap, char *); + else { + item_ptr->value_ptr = va_arg(ap, uint8_t *); + } + item_ptr++; + } + va_end(ap); +#ifdef USE_DEVICE_GROUPS_SEND + } +#endif // USE_DEVICE_GROUPS_SEND + item_ptr->item = 0; + + // If we're still building this update or all group members haven't acknowledged the previous + // update yet, update the message to include these new updates. First we need to rebuild the + // previous update message to remove any items and their values that are included in this new + // update. + if (device_group->message_length) { + int kept_item_count = 0; + + // Rebuild the previous update message, removing any items whose values are included in this + // new update. + uint8_t * previous_message_ptr = message_ptr; + while (item = *previous_message_ptr++) { + + // Determine the length of this item's value. + if (item <= DGR_ITEM_MAX_32BIT) { + value = 1; + if (item > DGR_ITEM_MAX_8BIT) { + value = 2; + if (item > DGR_ITEM_MAX_16BIT) { + value = 4; + } + } + } + else { + value = *previous_message_ptr + 1; + } + + // Search for this item in the new update. + for (item_ptr = item_array; item_ptr->item; item_ptr++) { + if (item_ptr->item == item) break; + } + + // If this item was not found in the new update, copy it to the new update message. + if (!item_ptr->item) { + kept_item_count++; + *message_ptr++ = item; + memmove(message_ptr, previous_message_ptr, value); + message_ptr += value; + } + + // Advance past the item value. + previous_message_ptr += value; + } +#ifdef DEVICE_GROUPS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: %u items carried over"), kept_item_count); +#endif // DEVICE_GROUPS_DEBUG + } + + // Itertate through the passed items adding them and their values to the message. + for (item_ptr = item_array; (item = item_ptr->item); item_ptr++) { + + // If this item is shared with the group add it to the message. + shared = true; + if (!device_group_index && message_type != DGR_MSGTYPE_UPDATE_COMMAND) shared = DeviceGroupItemShared(false, item); + if (shared) { + *message_ptr++ = item; + + // For integer items, add the value to the message. + if (item <= DGR_ITEM_MAX_32BIT) { + value = item_ptr->value; + *message_ptr++ = value & 0xff; + if (item > DGR_ITEM_MAX_8BIT) { + value >>= 8; + *message_ptr++ = value & 0xff; + if (item > DGR_ITEM_MAX_16BIT) { + value >>= 8; + *message_ptr++ = value & 0xff; + value >>= 8; + // For the power item, the device count is overlayed onto the highest 8 bits. + if (item == DGR_ITEM_POWER) { + if (!value) + value = (device_group_index == 0 ? devices_present : 1); + } + *message_ptr++ = value; + } + } + } + + // For string items and special items, get the value length. + else { + if (item <= DGR_ITEM_MAX_STRING) { + value = strlen((const char *)item_ptr->value_ptr) + 1; + } + else { + switch (item) { + case DGR_ITEM_LIGHT_CHANNELS: + value = 6; + break; + } + } + + // Load the length and copy the value. + *message_ptr++ = value; + memcpy(message_ptr, item_ptr->value_ptr, value); + message_ptr += value; + } + } + } + + // If we added any items, add the EOL item code and calculate the message length. + if (message_ptr != first_item_ptr) { + *message_ptr++ = 0; + device_group->message_length = message_ptr - device_group->message; + } + + // If there's going to be more items added to this message, return. + if (building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE) return 0; + } + + // If there is no message, restore the sequence number and return. + if (!device_group->message_length) { + device_group->outgoing_sequence = original_sequence; + return 0; + } + + // Multicast the packet. + SendReceiveDeviceGroupMessage(device_group, nullptr, device_group->message, device_group->message_length, false); + +#ifdef USE_DEVICE_GROUPS_SEND + // If this is the DevGroupSend command, also handle the update locally. + if (message_type == DGR_MSGTYPE_UPDATE_COMMAND) { + struct XDRVMAILBOX save_XdrvMailbox = XdrvMailbox; + SendReceiveDeviceGroupMessage(device_group, nullptr, device_group->message, device_group->message_length, true); + XdrvMailbox = save_XdrvMailbox; + } +#endif // USE_DEVICE_GROUPS_SEND + + uint32_t now = millis(); + if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) { + device_group->message_length = 0; + device_group->next_ack_check_time = 0; + } + else { + device_group->ack_check_interval = 200; + device_group->next_ack_check_time = now + device_group->ack_check_interval; + if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time; + device_group->member_timeout_time = now + DGR_MEMBER_TIMEOUT; + } + + device_group->next_announcement_time = now + DGR_ANNOUNCEMENT_INTERVAL; + if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; + return 0; +} + +void ProcessDeviceGroupMessage(uint8_t * message, int message_length) +{ + // Search for a device group with the target group name. If one isn't found, return. + uint8_t device_group_index = 0; + struct device_group * device_group = device_groups; + char * message_group_name = (char *)message + sizeof(DEVICE_GROUP_MESSAGE) - 1; + for (;;) { + if (!strcmp(message_group_name, device_group->group_name)) break; + if (++device_group_index >= device_group_count) return; + device_group++; + } + + // Find the group member. If this is a new group member, add it. + struct device_group_member * device_group_member; + IPAddress remote_ip = device_groups_udp.remoteIP(); + struct device_group_member * * flink = &device_group->device_group_members; + for (;;) { + device_group_member = *flink; + if (!device_group_member) { + device_group_member = (struct device_group_member *)calloc(1, sizeof(struct device_group_member)); + if (device_group_member == nullptr) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating member block")); + return; + } + device_group_member->ip_address = remote_ip; + *flink = device_group_member; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Member %s added"), IPAddressToString(remote_ip)); + break; + } + else if (device_group_member->ip_address == remote_ip) { + break; + } + flink = &device_group_member->flink; + } + + SendReceiveDeviceGroupMessage(device_group, device_group_member, message, message_length, true); +} + +void DeviceGroupStatus(uint8_t device_group_index) +{ + if (Settings.flag4.device_groups_enabled && device_group_index < device_group_count) { + char buffer[1024]; + int member_count = 0; + struct device_group * device_group = &device_groups[device_group_index]; + buffer[0] = buffer[1] = 0; + for (struct device_group_member * device_group_member = device_group->device_group_members; device_group_member; device_group_member = device_group_member->flink) { + snprintf_P(buffer, sizeof(buffer), PSTR("%s,{\"IPAddress\":\"%s\",\"ResendCount\":%u,\"LastRcvdSeq\":%u,\"LastAckedSeq\":%u}"), buffer, IPAddressToString(device_group_member->ip_address), device_group_member->unicast_count, device_group_member->received_sequence, device_group_member->acked_sequence); + member_count++; + } + Response_P(PSTR("{\"" D_CMND_DEVGROUPSTATUS "\":{\"Index\":%u,\"GroupName\":\"%s\",\"MessageSeq\":%u,\"MemberCount\":%d,\"Members\":[%s]}"), device_group_index, device_group->group_name, device_group->outgoing_sequence, member_count, &buffer[1]); + } } void DeviceGroupsLoop(void) { - if (!Settings.flag4.device_groups_enabled) return; - if (udp_connected) { - uint32_t now = millis(); + if (!device_groups_up || restart_flag) return; - // If UDP was not connected before, (re)initialize. - if (!udp_was_connected) { - udp_was_connected = true; - - if (!device_groups_initialized) DeviceGroupsInit(); - if (device_groups_initialization_failed) return; - - // Load the status request message for all device groups. This message will be multicast 5 - // times. - next_check_time = now + 1000; - for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++) { - device_group * device_group = &device_groups[device_group_index]; - device_group->message_length = BeginDeviceGroupMessage(device_group, DGR_FLAG_RESET | DGR_FLAG_STATUS_REQUEST) - device_group->message; - device_group->initial_status_requests_remaining = 5; - device_group->next_ack_check_time = next_check_time; - } - - } - - if (device_groups_initialization_failed) return; - - // If it's time to check on things, iterate through the device groups. - if (next_check_time <= now) { -#ifdef DEVICE_GROUPS_DEBUG -AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Ckecking next_check_time=%u, now=%u"), next_check_time, now); -#endif // DEVICE_GROUPS_DEBUG - next_check_time = now + DGR_ANNOUNCEMENT_INTERVAL * 2; - - for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++) { - device_group * device_group = &device_groups[device_group_index]; - - // If we're still waiting for acks to the last update from this device group, ... - if (device_group->next_ack_check_time) { - - // If it's time to check for acks, ... - if (device_group->next_ack_check_time <= now) { - - // If we're still sending the initial status request message, send it. - if (device_group->initial_status_requests_remaining) { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: sending initial status request for group %s"), device_group->group_name); -#endif // DEVICE_GROUPS_DEBUG - if (--device_group->initial_status_requests_remaining) { - SendDeviceGroupPacket(0, device_group->message, device_group->message_length, PSTR("Initial")); - device_group->message[device_group->message_header_length + 2] = DGR_FLAG_STATUS_REQUEST; // The reset flag is on only for the first packet - turn it off now - device_group->next_ack_check_time = now + 200; - } - - // If we've sent the initial status request message 5 times, send our status to all - // the members. - else { - device_group->next_ack_check_time = 0; - _SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_FULL_STATUS); - } - } - - // If we're done initializing, iterate through the group memebers, ... - else { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: checking for ack's")); -#endif // DEVICE_GROUPS_DEBUG - bool acked = true; - struct device_group_member ** flink = &device_group->device_group_members; - struct device_group_member * device_group_member; - while ((device_group_member = *flink)) { - - // If we have not received an ack to our last message from this member, ... - if (device_group_member->acked_sequence != outgoing_sequence) { - - // If we haven't receive an ack from this member in DGR_MEMBER_TIMEOUT ms, assume - // they're offline and remove them from the group. - if (device_group->member_timeout_time < now) { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: removing member %s (%p)"), IPAddressToString(device_group_member->ip_address), device_group_member); -#endif // DEVICE_GROUPS_DEBUG - *flink = device_group_member->flink; - free(device_group_member); - } - - // Otherwise, unicast the last message directly to this member. - else { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: sending %u-byte device group %s packet via unicast to %s, sequence %u, last message acked=%u"), device_group->message_length, device_group->group_name, IPAddressToString(device_group_member->ip_address), outgoing_sequence, device_group_member->acked_sequence); -#endif // DEVICE_GROUPS_DEBUG - SendDeviceGroupPacket(device_group_member->ip_address, device_group->message, device_group->message_length, PSTR("Unicast")); - acked = false; - flink = &device_group_member->flink; - } - } - else { - flink = &device_group_member->flink; - } - } - - // If we've received an ack to the last message from all members, clear the ack check - // time and zero-out the message length. - if (acked) { - device_group->next_ack_check_time = 0; - device_group->message_length = 0; // Let _SendDeviceGroupMessage know we're done with this update - } - - // If there are still members we haven't received an ack from, set the next ack check - // time. We start at 200ms and double the interval each pass with a maximum interval of - // 5 seconds. - else { - device_group->ack_check_interval *= 2; - if (device_group->ack_check_interval > 5000) device_group->ack_check_interval = 5000; - device_group->next_ack_check_time = now + device_group->ack_check_interval; - } - } - } - - if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time; - } - - // If it's time to send multicast announcement for this group, send it. This is to - // announcement ourself to any members that have somehow not heard about us. We send it at - // the announcement interval plus a random number of milliseconds so that even if all the - // devices booted at the same time, they don't all multicast their announcements at the same - // time. -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: next_announcement_time=%u, now=%u"), device_group->next_announcement_time, now); -#endif // DEVICE_GROUPS_DEBUG - if (device_group->next_announcement_time <= now) { - device_group->message_length = BeginDeviceGroupMessage(device_group, DGR_FLAG_ANNOUNCEMENT) - device_group->message; -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: sending %u-byte device group %s announcement"), device_group->message_length, device_group->group_name); -#endif // DEVICE_GROUPS_DEBUG - SendDeviceGroupPacket(0, device_group->message, device_group->message_length, PSTR("Announcement")); - device_group->next_announcement_time = now + DGR_ANNOUNCEMENT_INTERVAL + random(10000); - } - if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; + while (device_groups_udp.parsePacket()) { + uint8_t packet_buffer[512]; + int length = device_groups_udp.read(packet_buffer, sizeof(packet_buffer) - 1); + if (length > 0) { + packet_buffer[length] = 0; + if (!strncmp_P((char *)packet_buffer, kDeviceGroupMessage, sizeof(DEVICE_GROUP_MESSAGE) - 1)) { + ProcessDeviceGroupMessage(packet_buffer, length); } } } - else { - udp_was_connected = false; + + uint32_t now = millis(); + + // If it's time to check on things, iterate through the device groups. + if ((long)(now - next_check_time) >= 0) { +#ifdef DEVICE_GROUPS_DEBUG +AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Checking next_check_time=%u, now=%u"), next_check_time, now); +#endif // DEVICE_GROUPS_DEBUG + next_check_time = now + DGR_ANNOUNCEMENT_INTERVAL * 2; + + struct device_group * device_group = device_groups; + for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++, device_group++) { + + // If we're still waiting for acks to the last update from this device group, ... + if (device_group->next_ack_check_time) { + + // If it's time to check for acks, ... + if ((long)(now - device_group->next_ack_check_time) >= 0) { + + // If we're still sending the initial status request message, send it. + if (device_group->initial_status_requests_remaining) { + if (--device_group->initial_status_requests_remaining) { +#ifdef DEVICE_GROUPS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending initial status request for group %s"), device_group->group_name); +#endif // DEVICE_GROUPS_DEBUG + SendReceiveDeviceGroupMessage(device_group, nullptr, device_group->message, device_group->message_length, false); + device_group->message[device_group->message_header_length + 2] = DGR_FLAG_STATUS_REQUEST; // The reset flag is on only for the first packet - turn it off now + next_check_time = device_group->next_ack_check_time = now + 200; + continue; + } + + // If we've sent the initial status request message the set number of times, send our + // status to all the members. + else { + _SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_FULL_STATUS); + } + } + + // If we're done initializing, iterate through the group memebers, ... + else { +#ifdef DEVICE_GROUPS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Checking for ack's")); +#endif // DEVICE_GROUPS_DEBUG + bool acked = true; + struct device_group_member ** flink = &device_group->device_group_members; + struct device_group_member * device_group_member; + while ((device_group_member = *flink)) { + + // If we have not received an ack to our last message from this member, ... + if (device_group_member->acked_sequence != device_group->outgoing_sequence) { + + // If we haven't receive an ack from this member in DGR_MEMBER_TIMEOUT ms, assume + // they're offline and remove them from the group. + if ((long)(now - device_group->member_timeout_time) >= 0) { + *flink = device_group_member->flink; + free(device_group_member); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Member %s removed"), IPAddressToString(device_group_member->ip_address)); + continue; + } + + // Otherwise, unicast the last message directly to this member. + SendReceiveDeviceGroupMessage(device_group, device_group_member, device_group->message, device_group->message_length, false); + device_group_member->unicast_count++; + acked = false; + } + flink = &device_group_member->flink; + } + + // If we've received an ack to the last message from all members, clear the ack check + // time and zero-out the message length. + if (acked) { + device_group->next_ack_check_time = 0; + device_group->message_length = 0; // Let _SendDeviceGroupMessage know we're done with this update + } + + // If there are still members we haven't received an ack from, set the next ack check + // time. We start at 200ms and double the interval each pass with a maximum interval of + // 5 seconds. + else { + device_group->ack_check_interval *= 2; + if (device_group->ack_check_interval > 5000) device_group->ack_check_interval = 5000; + device_group->next_ack_check_time = now + device_group->ack_check_interval; + } + } + } + + if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time; + } + + // If it's time to send a multicast announcement for this group, send it. This is to + // announcement ourself to any members that have somehow not heard about us. We send it at the + // announcement interval plus a random number of milliseconds so that even if all the devices + // booted at the same time, they don't all multicast their announcements at the same time. +#ifdef DEVICE_GROUPS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: next_announcement_time=%u, now=%u"), device_group->next_announcement_time, now); +#endif // DEVICE_GROUPS_DEBUG + if ((long)(now - device_group->next_announcement_time) >= 0) { + SendReceiveDeviceGroupMessage(device_group, nullptr, device_group->message, BeginDeviceGroupMessage(device_group, DGR_FLAG_ANNOUNCEMENT, true) - device_group->message, false); + device_group->next_announcement_time = now + DGR_ANNOUNCEMENT_INTERVAL + random(10000); + } + if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; + } } } diff --git a/tasmota/support_esp32.ino b/tasmota/support_esp32.ino new file mode 100644 index 000000000..fbd99ad67 --- /dev/null +++ b/tasmota/support_esp32.ino @@ -0,0 +1,261 @@ +/* + support_esp32.ino - ESP32 specific code for Tasmota + + Copyright (C) 2020 Theo Arends / Jörg Schüler-Maroldt + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*********************************************************************************************\ + * ESP8266 Support +\*********************************************************************************************/ + +#ifdef ESP8266 + +extern "C" { +extern struct rst_info resetInfo; +} + +uint32_t ESP_ResetInfoReason(void) { + return resetInfo.reason; +} + +String ESP_getResetReason(void) { + return ESP.getResetReason(); +} + +uint32_t ESP_getChipId(void) { + return ESP.getChipId(); +} + +uint32_t ESP_getSketchSize(void) { + return ESP.getSketchSize(); +} + +uint32_t ESP_getFreeHeap(void) { + return ESP.getFreeHeap(); +} + +void ESP_Restart(void) { +// ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 + ESP.reset(); +} + +#endif + +/*********************************************************************************************\ + * ESP32 Support +\*********************************************************************************************/ + +#ifdef ESP32 + +#include +#include + +void NvmLoad(const char *sNvsName, const char *sName, void *pSettings, unsigned nSettingsLen) { + nvs_handle handle; + noInterrupts(); + nvs_open(sNvsName, NVS_READONLY, &handle); + size_t size = nSettingsLen; + nvs_get_blob(handle, sName, pSettings, &size); + nvs_close(handle); + interrupts(); +} + +void NvmSave(const char *sNvsName, const char *sName, const void *pSettings, unsigned nSettingsLen) { + nvs_handle handle; + noInterrupts(); + nvs_open(sNvsName, NVS_READWRITE, &handle); + nvs_set_blob(handle, sName, pSettings, nSettingsLen); + nvs_commit(handle); + nvs_close(handle); + interrupts(); +} + +void NvmErase(const char *sNvsName) { + nvs_handle handle; + noInterrupts(); + nvs_open(sNvsName, NVS_READWRITE, &handle); + nvs_erase_all(handle); + nvs_commit(handle); + nvs_close(handle); + interrupts(); +} + +void SettingsErase(uint8_t type) { + if (1 == type) { // SDK parameter area + } else if (2 == type) { // Tasmota parameter area (0x0F3xxx - 0x0FBFFF) + } else if (3 == type) { // Tasmota and SDK parameter area (0x0F3xxx - 0x0FFFFF) + } + + NvmErase("main"); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " t=%d"), type); +} + +void SettingsRead(void *data, size_t size) { + NvmLoad("main", "Settings", data, size); +} + +void SettingsWrite(const void *pSettings, unsigned nSettingsLen) { + NvmSave("main", "Settings", pSettings, nSettingsLen); +} + +void QPCRead(void *pSettings, unsigned nSettingsLen) { + NvmLoad("qpc", "pcreg", pSettings, nSettingsLen); +} + +void QPCWrite(const void *pSettings, unsigned nSettingsLen) { + NvmSave("qpc", "pcreg", pSettings, nSettingsLen); +} + +// +// sntp emulation +// +static bool bNetIsTimeSync = false; +// +void SntpInit() { + bNetIsTimeSync = true; +} + +uint32_t SntpGetCurrentTimestamp(void) { + time_t now = 0; + if (bNetIsTimeSync || ntp_force_sync) + { + //Serial_DebugX(("timesync configTime %d\n", ntp_force_sync, bNetIsTimeSync)); + // init to UTC Time + configTime(0, 0, SettingsText(SET_NTPSERVER1), SettingsText(SET_NTPSERVER2), SettingsText(SET_NTPSERVER3)); + bNetIsTimeSync = false; + ntp_force_sync = false; + } + time(&now); + return now; +} + +// +// Crash stuff +// + +void CrashDump(void) { +} + +bool CrashFlag(void) { + return false; +} + +void CrashDumpClear(void) { +} + +void CmndCrash(void) { + /* + volatile uint32_t dummy; + dummy = *((uint32_t*) 0x00000000); +*/ +} + +// Do an infinite loop to trigger WDT watchdog +void CmndWDT(void) { + /* + volatile uint32_t dummy = 0; + while (1) { + dummy++; + } +*/ +} +// This will trigger the os watch after OSWATCH_RESET_TIME (=120) seconds +void CmndBlockedLoop(void) { + /* + while (1) { + delay(1000); + } +*/ +} + +// +// ESP32 specific +// + +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" + +void DisableBrownout(void) { + // https://github.com/espressif/arduino-esp32/issues/863#issuecomment-347179737 + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable brownout detector +} + +// +// ESP32 Alternatives +// + +String ESP32GetResetReason(uint32_t cpu_no) { + // tools\sdk\include\esp32\rom\rtc.h + switch (rtc_get_reset_reason( (RESET_REASON) cpu_no)) { + case POWERON_RESET : return F("Vbat power on reset"); // 1 + case SW_RESET : return F("Software reset digital core"); // 3 + case OWDT_RESET : return F("Legacy watch dog reset digital core"); // 4 + case DEEPSLEEP_RESET : return F("Deep Sleep reset digital core"); // 5 + case SDIO_RESET : return F("Reset by SLC module, reset digital core"); // 6 + case TG0WDT_SYS_RESET : return F("Timer Group0 Watch dog reset digital core"); // 7 + case TG1WDT_SYS_RESET : return F("Timer Group1 Watch dog reset digital core"); // 8 + case RTCWDT_SYS_RESET : return F("RTC Watch dog Reset digital core"); // 9 + case INTRUSION_RESET : return F("Instrusion tested to reset CPU"); // 10 + case TGWDT_CPU_RESET : return F("Time Group reset CPU"); // 11 + case SW_CPU_RESET : return F("Software reset CPU"); // 12 + case RTCWDT_CPU_RESET : return F("RTC Watch dog Reset CPU"); // 13 + case EXT_CPU_RESET : return F("or APP CPU, reseted by PRO CPU"); // 14 + case RTCWDT_BROWN_OUT_RESET : return F("Reset when the vdd voltage is not stable"); // 15 + case RTCWDT_RTC_RESET : return F("RTC Watch dog reset digital core and rtc module"); // 16 + default : return F("NO_MEAN"); // 0 + } +} + +String ESP_getResetReason(void) { + return ESP32GetResetReason(0); // CPU 0 +} + +uint32_t ESP_ResetInfoReason(void) { + RESET_REASON reason = rtc_get_reset_reason(0); + if (POWERON_RESET == reason) { return REASON_DEFAULT_RST; } + if (SW_CPU_RESET == reason) { return REASON_SOFT_RESTART; } + if (DEEPSLEEP_RESET == reason) { return REASON_DEEP_SLEEP_AWAKE; } + if (SW_RESET == reason) { return REASON_EXT_SYS_RST; } +} + +uint32_t ESP_getChipId(void) { + uint32_t id = 0; + for (uint32_t i = 0; i < 17; i = i +8) { + id |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; + } + return id; +} + +uint32_t ESP_getSketchSize(void) { + static uint32_t sketchsize = 0; + + if (!sketchsize) { + sketchsize = ESP.getSketchSize(); // This takes almost 2 seconds on an ESP32 + } + return sketchsize; +} + +uint32_t ESP_getFreeHeap(void) { +// return ESP.getFreeHeap(); + return ESP.getMaxAllocHeap(); +} + +void ESP_Restart(void) { + ESP.restart(); +} + +#endif // ESP32 diff --git a/tasmota/support_esptool.ino b/tasmota/support_esptool.ino index 5bb82f999..efde513fc 100644 --- a/tasmota/support_esptool.ino +++ b/tasmota/support_esptool.ino @@ -17,7 +17,10 @@ along with this program. If not, see . */ +#ifdef ESP8266 #define USE_ESPTOOL +#endif // ESP8266 + #ifdef USE_ESPTOOL /*********************************************************************************************\ * EspTool Erase function based on Version 2.8 diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 201711e48..02a04a962 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -55,15 +55,15 @@ void GetFeatures(void) #ifdef USE_EMULATION_HUE feature_drv1 |= 0x00000200; // xdrv_20_hue.ino #endif -#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT) +//#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT) feature_drv1 |= 0x00000400; // xdrv_02_mqtt.ino -#endif -#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) +//#endif +//#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) // feature_drv1 |= 0x00000800; // xdrv_02_mqtt.ino -#endif -#if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) // Obsolete since 6.2.1.11 +//#endif +//#if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) // Obsolete since 6.2.1.11 // feature_drv1 |= 0x00001000; // xdrv_02_mqtt.ino -#endif +//#endif #ifdef MQTT_HOST_DISCOVERY feature_drv1 |= 0x00002000; // xdrv_02_mqtt.ino #endif @@ -477,7 +477,7 @@ void GetFeatures(void) #ifdef USE_EXS_DIMMER feature5 |= 0x00008000; // xdrv_30_exs_dimmer.ino #endif -#ifdef USE_ARDUINO_SLAVE +#ifdef USE_TASMOTA_SLAVE feature5 |= 0x00010000; // xdrv_31_arduino_slave.ino #endif #ifdef USE_HIH6 @@ -542,15 +542,28 @@ void GetFeatures(void) #ifdef USE_HDC1080 feature6 |= 0x00000008; // xsns_65_hdc1080.ino #endif +#ifdef USE_IAQ + feature6 |= 0x00000010; // xsns_66_iAQ.ino +#endif +#ifdef USE_DISPLAY_SEVENSEG + feature6 |= 0x00000020; // xdsp_11_sevenseg.ino +#endif +#ifdef USE_AS3935 + feature6 |= 0x00000040; // xsns_67_as3935.ino +#endif +#ifdef USE_PING + feature6 |= 0x00000080; // xdrv_38_ping.ino +#endif +#ifdef USE_WINDMETER + feature6 |= 0x00000100; // xsns_68_windmeter.ino +#endif +#ifdef USE_OPENTHERM + feature6 |= 0x00000200; // xsns_69_opentherm.ino +#endif +#ifdef USE_THERMOSTAT + feature6 |= 0x00000400; // xdrv_39_heating.ino +#endif -// feature6 |= 0x00000010; -// feature6 |= 0x00000020; -// feature6 |= 0x00000040; -// feature6 |= 0x00000080; - -// feature6 |= 0x00000100; -// feature6 |= 0x00000200; -// feature6 |= 0x00000400; // feature6 |= 0x00000800; // feature6 |= 0x00001000; @@ -576,6 +589,7 @@ void GetFeatures(void) // feature6 |= 0x10000000; // feature6 |= 0x20000000; // feature6 |= 0x40000000; -// feature6 |= 0x80000000; - +#ifdef USE_WEBCAM + feature6 |= 0x80000000; +#endif } diff --git a/tasmota/support_flash_log.ino b/tasmota/support_flash_log.ino index 162ea1f0b..5f73c4078 100644 --- a/tasmota/support_flash_log.ino +++ b/tasmota/support_flash_log.ino @@ -37,6 +37,7 @@ \*********************************************************************************************/ #ifdef USE_FLOG +#ifdef ESP8266 class FLOG @@ -370,13 +371,13 @@ void FLOG::stopRecording(void){ * * @param size: size of the data entry/record in bytes, i.e. sizeof(myStruct) * @param sendHeader: should implement at least something like: - * @example WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); // This is very likely unknown!! - * WebServer->sendHeader(F("Content-Disposition"), F("attachment; filename=myfile.txt")); + * @example Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); // This is very likely unknown!! + * Webserver->sendHeader(F("Content-Disposition"), F("attachment; filename=myfile.txt")); * @param sendRecord: will receive the memory address as "uint8_t* addr" and should consume the current entry/record * @example myStruct_t *entry = (myStruct_t*)addr; - * Then make useful Strings and send it, i.e.: WebServer->sendContent_P(myString); + * Then make useful Strings and send it, i.e.: Webserver->sendContent_P(myString); * @param sendFooter: finish the download, should implement at least: - * @example WebServer->sendContent(""); + * @example Webserver->sendContent(""); */ void FLOG::startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter){ @@ -400,7 +401,7 @@ void FLOG::stopRecording(void){ if(k%128 == 0){ // give control to the system every x iteration, TODO: This will fail, when record/entry-size is not 8 // DEBUG_SENSOR_LOG(PSTR("FLOG: now loop(), %u bytes left"), Flog->bytes_left); OsWatchLoop(); - delay(sleep); + delay(ssleep); } k+=size; if(bytes_left>7){ @@ -419,7 +420,7 @@ void FLOG::stopRecording(void){ _readSector(next_sector); bytes_left = sector.header.buf_pointer - sizeof(sector.header); OsWatchLoop(); - delay(sleep); + delay(ssleep); } running_download = false; // Callback 3: create a footer or simply finish the download with an empty payload @@ -429,4 +430,5 @@ void FLOG::stopRecording(void){ _initBuffer(); } + #endif // ESP8266 #endif // USE_FLOG \ No newline at end of file diff --git a/tasmota/support_float.ino b/tasmota/support_float.ino index 29613453a..98553d183 100644 --- a/tasmota/support_float.ino +++ b/tasmota/support_float.ino @@ -422,3 +422,26 @@ uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, } return (uint32_t) (result > to_max ? to_max : (result < to_min ? to_min : result)); } + +// Force a float value between two ranges, and adds or substract the range until we fit +float ModulusRangef(float f, float a, float b) { + if (b <= a) { return a; } // inconsistent, do what we can + float range = b - a; + float x = f - a; // now range of x should be 0..range + x = fmodf(x, range); // actual range is now -range..range + if (x < 0.0f) { x += range; } // actual range is now 0..range + return x + a; // returns range a..b +} + +// Compute a n-degree polynomial for value x and an array of coefficient (by increasing order) +// Ex: +// For factors = { f0, f1, f2, f3 } +// Returns : f0 + f1 x + f2 x^2, + f3 x^3 +// Internally computed as : f0 + x (f1 + x (f2 + x f3)) +float Polynomialf(const float *factors, uint32_t degree, float x) { + float r = 0.0f; + for (uint32_t i = degree - 1; i >= 0; i--) { + r = r * x + factors[i]; + } + return r; +} diff --git a/tasmota/support_legacy_cores.ino b/tasmota/support_legacy_cores.ino index f8c5b3060..46546ff1d 100644 --- a/tasmota/support_legacy_cores.ino +++ b/tasmota/support_legacy_cores.ino @@ -155,3 +155,24 @@ void* memmove_P(void *dest, const void *src, size_t n) } #endif // ARDUINO_ESP8266_RELEASE < 2_6_0 + + + +/*********************************************************************************************\ + * Core overrides +\*********************************************************************************************/ + +// Add below line to tasmota_globals.h +// extern "C" void resetPins(); +void resetPins() +{ +/* + for (int i = 0; i <= 5; ++i) { + pinMode(i, INPUT); + } + // pins 6-11 are used for the SPI flash interface + for (int i = 12; i <= 16; ++i) { + pinMode(i, INPUT); + } +*/ +} diff --git a/tasmota/support_rotary.ino b/tasmota/support_rotary.ino index 071b15b92..25c064a18 100644 --- a/tasmota/support_rotary.ino +++ b/tasmota/support_rotary.ino @@ -49,8 +49,8 @@ void update_rotary(void) */ uint8_t s = Rotary.state & 3; - if (digitalRead(pin[GPIO_ROT1A])) { s |= 4; } - if (digitalRead(pin[GPIO_ROT1B])) { s |= 8; } + if (digitalRead(Pin(GPIO_ROT1A))) { s |= 4; } + if (digitalRead(Pin(GPIO_ROT1B))) { s |= 8; } switch (s) { case 0: case 5: case 10: case 15: break; @@ -80,20 +80,20 @@ bool RotaryButtonPressed(void) void RotaryInit(void) { Rotary.present = 0; - if ((pin[GPIO_ROT1A] < 99) && (pin[GPIO_ROT1B] < 99)) { + if (PinUsed(GPIO_ROT1A) && PinUsed(GPIO_ROT1B)) { Rotary.present++; - pinMode(pin[GPIO_ROT1A], INPUT_PULLUP); - pinMode(pin[GPIO_ROT1B], INPUT_PULLUP); + pinMode(Pin(GPIO_ROT1A), INPUT_PULLUP); + pinMode(Pin(GPIO_ROT1B), INPUT_PULLUP); // GPIO6-GPIO11 are typically used to interface with the flash memory IC on // most esp8266 modules, so we should avoid adding interrupts to these pins. - if ((pin[GPIO_ROT1A] < 6) || (pin[GPIO_ROT1A] > 11)) { - attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1A]), update_rotary, CHANGE); + if ((Pin(GPIO_ROT1A) < 6) || (Pin(GPIO_ROT1A) > 11)) { + attachInterrupt(digitalPinToInterrupt(Pin(GPIO_ROT1A)), update_rotary, CHANGE); Rotary.interrupts_in_use_count++; } - if ((pin[GPIO_ROT1B] < 6) || (pin[GPIO_ROT1B] > 11)) { - attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1B]), update_rotary, CHANGE); + if ((Pin(GPIO_ROT1B) < 6) || (Pin(GPIO_ROT1B) > 11)) { + attachInterrupt(digitalPinToInterrupt(Pin(GPIO_ROT1B)), update_rotary, CHANGE); Rotary.interrupts_in_use_count++; } } diff --git a/tasmota/support_rtc.ino b/tasmota/support_rtc.ino index 3b7da8e37..0b2249ec2 100644 --- a/tasmota/support_rtc.ino +++ b/tasmota/support_rtc.ino @@ -367,7 +367,7 @@ void RtcSecond(void) if ((Rtc.ntp_sync_minute > 59) && (uptime_minute > 2)) { Rtc.ntp_sync_minute = 1; // If sync prepare for a new cycle } - uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP.getChipId() & 0xF) * 3) + 3) ; // First try ASAP to sync. If fails try once every 60 seconds based on chip id + uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP_getChipId() & 0xF) * 3) + 3) ; // First try ASAP to sync. If fails try once every 60 seconds based on chip id if ( (((offset == RtcTime.second) && ( (RtcTime.year < 2016) || // Never synced (Rtc.ntp_sync_minute == uptime_minute))) || // Re-sync every hour ntp_force_sync ) ) { // Forced sync diff --git a/tasmota/support_static_buffer.ino b/tasmota/support_static_buffer.ino index bec831cc9..ea21b2805 100644 --- a/tasmota/support_static_buffer.ino +++ b/tasmota/support_static_buffer.ino @@ -112,7 +112,7 @@ public: } size_t addBuffer(const uint8_t *buf2, size_t len2) { - if (len() + len2 <= size()) { + if ((buf2) && (len() + len2 <= size())) { for (uint32_t i = 0; i < len2; i++) { _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); } @@ -121,7 +121,7 @@ public: } size_t addBuffer(const char *buf2, size_t len2) { - if (len() + len2 <= size()) { + if ((buf2) && (len() + len2 <= size())) { for (uint32_t i = 0; i < len2; i++) { _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); } diff --git a/tasmota/support_switch.ino b/tasmota/support_switch.ino index f1d5c7ea2..64837c74c 100644 --- a/tasmota/support_switch.ino +++ b/tasmota/support_switch.ino @@ -86,9 +86,9 @@ void SwitchProbe(void) uint8_t force_low = (Settings.switch_debounce % 50) &2; // 52, 102, 152 etc for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if (pin[GPIO_SWT1 +i] < 99) { + if (PinUsed(GPIO_SWT1, i)) { // Olimex user_switch2.c code to fix 50Hz induced pulses - if (1 == digitalRead(pin[GPIO_SWT1 +i])) { + if (1 == digitalRead(Pin(GPIO_SWT1, i))) { if (force_high) { // Enabled with SwitchDebounce x1 if (1 == Switch.virtual_state[i]) { @@ -127,10 +127,10 @@ void SwitchInit(void) Switch.present = 0; for (uint32_t i = 0; i < MAX_SWITCHES; i++) { Switch.last_state[i] = 1; // Init global to virtual switch state; - if (pin[GPIO_SWT1 +i] < 99) { + if (PinUsed(GPIO_SWT1, i)) { Switch.present++; - pinMode(pin[GPIO_SWT1 +i], bitRead(Switch.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_SWT1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); - Switch.last_state[i] = digitalRead(pin[GPIO_SWT1 +i]); // Set global now so doesn't change the saved power state on first switch check + pinMode(Pin(GPIO_SWT1, i), bitRead(Switch.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_SWT1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); + Switch.last_state[i] = digitalRead(Pin(GPIO_SWT1, i)); // Set global now so doesn't change the saved power state on first switch check } Switch.virtual_state[i] = Switch.last_state[i]; } @@ -148,7 +148,7 @@ void SwitchHandler(uint8_t mode) uint16_t loops_per_second = 1000 / Settings.switch_debounce; for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if ((pin[GPIO_SWT1 +i] < 99) || (mode)) { + if (PinUsed(GPIO_SWT1, i) || (mode)) { uint8_t button = Switch.virtual_state[i]; uint8_t switchflag = POWER_TOGGLE +1; diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index d8ca2668c..b1cc56a68 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -39,14 +39,14 @@ char* Format(char* output, const char* input, int size) char tmp[size]; if (strchr(token, 'd')) { snprintf_P(tmp, size, PSTR("%s%c0%dd"), output, '%', digits); - snprintf_P(output, size, tmp, ESP.getChipId() & 0x1fff); // %04d - short chip ID in dec, like in hostname + snprintf_P(output, size, tmp, ESP_getChipId() & 0x1fff); // %04d - short chip ID in dec, like in hostname } else { snprintf_P(tmp, size, PSTR("%s%c0%dX"), output, '%', digits); - snprintf_P(output, size, tmp, ESP.getChipId()); // %06X - full chip ID in hex + snprintf_P(output, size, tmp, ESP_getChipId()); // %06X - full chip ID in hex } } else { if (strchr(token, 'd')) { - snprintf_P(output, size, PSTR("%s%d"), output, ESP.getChipId()); // %d - full chip ID in dec + snprintf_P(output, size, PSTR("%s%d"), output, ESP_getChipId()); // %d - full chip ID in dec digits = 8; } } @@ -61,10 +61,10 @@ char* Format(char* output, const char* input, int size) char* GetOtaUrl(char *otaurl, size_t otaurl_size) { if (strstr(SettingsText(SET_OTAURL), "%04d") != nullptr) { // OTA url contains placeholder for chip ID - snprintf(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP.getChipId() & 0x1fff); + snprintf(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP_getChipId() & 0x1fff); } else if (strstr(SettingsText(SET_OTAURL), "%d") != nullptr) { // OTA url contains placeholder for chip ID - snprintf_P(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP.getChipId()); + snprintf_P(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP_getChipId()); } else { strlcpy(otaurl, SettingsText(SET_OTAURL), otaurl_size); @@ -130,11 +130,11 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi return stopic; } -char* GetGroupTopic_P(char *stopic, const char* subtopic) +char* GetGroupTopic_P(char *stopic, const char* subtopic, uint32_t itopic) { // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# // SetOption75 1: cmnd/ - return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, SettingsText(SET_MQTT_GRP_TOPIC), subtopic); // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1) + return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, SettingsText(itopic), subtopic); // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1) } char* GetFallbackTopic_P(char *stopic, const char* subtopic) @@ -166,7 +166,7 @@ void SetLatchingRelay(power_t lpower, uint32_t state) for (uint32_t i = 0; i < devices_present; i++) { uint32_t port = (i << 1) + ((latching_power >> i) &1); - DigitalWrite(GPIO_REL1 +port, bitRead(rel_inverted, port) ? !state : state); + DigitalWrite(GPIO_REL1, port, bitRead(rel_inverted, port) ? !state : state); } } @@ -210,6 +210,7 @@ void SetDevicePower(power_t rpower, uint32_t source) if (XdrvCall(FUNC_SET_DEVICE_POWER)) { // Set power state and stop if serviced // Serviced } +#ifdef ESP8266 else if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { Serial.write(0xA0); Serial.write(0x04); @@ -221,11 +222,13 @@ void SetDevicePower(power_t rpower, uint32_t source) else if (EXS_RELAY == my_module_type) { SetLatchingRelay(rpower, 1); } - else { + else +#endif // ESP8266 + { for (uint32_t i = 0; i < devices_present; i++) { power_t state = rpower &1; if (i < MAX_RELAYS) { - DigitalWrite(GPIO_REL1 +i, bitRead(rel_inverted, i) ? !state : state); + DigitalWrite(GPIO_REL1, i, bitRead(rel_inverted, i) ? !state : state); } rpower >>= 1; } @@ -279,9 +282,11 @@ void SetAllPower(uint32_t state, uint32_t source) void SetPowerOnState(void) { +#ifdef ESP8266 if (MOTOR == my_module_type) { Settings.poweronstate = POWER_ALL_ON; // Needs always on else in limbo! } +#endif // ESP8266 if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { SetDevicePower(1, SRC_RESTART); } else { @@ -320,8 +325,8 @@ void SetPowerOnState(void) // Issue #526 and #909 for (uint32_t i = 0; i < devices_present; i++) { if (!Settings.flag3.no_power_feedback) { // SetOption63 - Don't scan relay power state at restart - #5594 and #5663 - if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { - bitWrite(power, i, digitalRead(pin[GPIO_REL1 +i]) ^ bitRead(rel_inverted, i)); + if ((i < MAX_RELAYS) && PinUsed(GPIO_REL1, i)) { + bitWrite(power, i, digitalRead(Pin(GPIO_REL1, i)) ^ bitRead(rel_inverted, i)); } } if ((i < MAX_PULSETIMERS) && (bitRead(power, i) || (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate))) { @@ -333,12 +338,12 @@ void SetPowerOnState(void) void SetLedPowerIdx(uint32_t led, uint32_t state) { - if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present - if (pin[GPIO_LED2] < 99) { + if (!PinUsed(GPIO_LEDLNK) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present + if (PinUsed(GPIO_LED1, 1)) { led = 1; } } - if (pin[GPIO_LED1 + led] < 99) { + if (PinUsed(GPIO_LED1, led)) { uint32_t mask = 1 << led; if (state) { state = 1; @@ -346,7 +351,7 @@ void SetLedPowerIdx(uint32_t led, uint32_t state) } else { led_power &= (0xFF ^ mask); } - DigitalWrite(GPIO_LED1 + led, bitRead(led_inverted, led) ? !state : state); + DigitalWrite(GPIO_LED1, led, bitRead(led_inverted, led) ? !state : state); } #ifdef USE_BUZZER if (led == 0) { @@ -357,7 +362,7 @@ void SetLedPowerIdx(uint32_t led, uint32_t state) void SetLedPower(uint32_t state) { - if (99 == pin[GPIO_LEDLNK]) { // Legacy - Only use LED1 and/or LED2 + if (!PinUsed(GPIO_LEDLNK)) { // Legacy - Only use LED1 and/or LED2 SetLedPowerIdx(0, state); } else { power_t mask = 1; @@ -378,10 +383,10 @@ void SetLedPowerAll(uint32_t state) void SetLedLink(uint32_t state) { - uint32_t led_pin = pin[GPIO_LEDLNK]; + uint32_t led_pin = Pin(GPIO_LEDLNK); uint32_t led_inv = ledlnk_inverted; if (99 == led_pin) { // Legacy - LED1 is status - led_pin = pin[GPIO_LED1]; + led_pin = Pin(GPIO_LED1); led_inv = bitRead(led_inverted, 0); } if (led_pin < 99) { @@ -603,7 +608,7 @@ void MqttShowPWMState(void) ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); bool first = true; for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 + i] < 99) { + if (PinUsed(GPIO_PWM1, i)) { ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]); first = false; } @@ -624,8 +629,8 @@ void MqttShowState(void) #endif ResponseAppend_P(PSTR(",\"" D_JSON_HEAPSIZE "\":%d,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u,\"MqttCount\":%u"), - ESP.getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), // SetOption60 - Enable normal sleep instead of dynamic sleep - sleep, loop_load_avg, MqttConnectCount()); + ESP_getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), // SetOption60 - Enable normal sleep instead of dynamic sleep + ssleep, loop_load_avg, MqttConnectCount()); for (uint32_t i = 1; i <= devices_present; i++) { #ifdef USE_LIGHT @@ -698,9 +703,9 @@ bool MqttShowSensor(void) int json_data_start = strlen(mqtt_data); for (uint32_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 - if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { + if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB))) { #else - if (pin[GPIO_SWT1 +i] < 99) { + if (PinUsed(GPIO_SWT1, i)) { #endif // USE_TM1638 ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(SwitchState(i))); } @@ -814,6 +819,18 @@ void PerformEverySecond(void) } } } + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 + // Wifi keep alive to send Gratuitous ARP + wifiKeepAlive(); +#endif // ARDUINO_ESP8266_RELEASE_2_3_0 + + +#ifdef ESP32 + if (11 == uptime) { // Perform one-time ESP32 houskeeping + ESP_getSketchSize(); // Init sketchsize as it can take up to 2 seconds + } +#endif } /*-------------------------------------------------------------------------------------------*\ @@ -896,11 +913,13 @@ void Every250mSeconds(void) if (200 == blinks) blinks = 0; // Disable blink } } - if (Settings.ledstate &1 && (pin[GPIO_LEDLNK] < 99 || !(blinks || restart_flag || ota_state_flag)) ) { + if (Settings.ledstate &1 && (PinUsed(GPIO_LEDLNK) || !(blinks || restart_flag || ota_state_flag)) ) { bool tstate = power & Settings.ledmask; +#ifdef ESP8266 if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { tstate = (!power) ? 1 : 0; // As requested invert signal for Touch devices to find them in the dark } +#endif // ESP8266 SetLedPower(tstate); } @@ -1208,13 +1227,25 @@ void SerialInput(void) delay(0); serial_in_byte = Serial.read(); + if (0 == serial_in_byte_counter) { + serial_buffer_overrun = false; + } + else if ((serial_in_byte_counter == INPUT_BUFFER_SIZE) +#ifdef ESP8266 + || Serial.hasOverrun() // Default ESP8266 Serial buffer size is 256. Tasmota increases to INPUT_BUFFER_SIZE +#endif + ) { + serial_buffer_overrun = true; + } + +#ifdef ESP8266 /*-------------------------------------------------------------------------------------------*\ * Sonoff dual and ch4 19200 baud serial interface \*-------------------------------------------------------------------------------------------*/ if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { serial_in_byte = ButtonSerial(serial_in_byte); } - +#endif // ESP8266 /*-------------------------------------------------------------------------------------------*/ if (XdrvCall(FUNC_SERIAL)) { @@ -1235,7 +1266,7 @@ void SerialInput(void) if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // Add char to string if it still fits serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; } else { - serial_in_byte_counter = 0; + serial_buffer_overrun = true; // Signal overrun but continue reading input to flush until '\n' (EOL) } } } else { @@ -1272,8 +1303,12 @@ void SerialInput(void) if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { // CMND_SERIALSEND and CMND_SERIALLOG serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); - ExecuteCommand(serial_in_buffer, SRC_SERIAL); + if (serial_buffer_overrun) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "Serial buffer overrun")); + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); + ExecuteCommand(serial_in_buffer, SRC_SERIAL); + } serial_in_byte_counter = 0; serial_polling_window = 0; Serial.flush(); @@ -1286,9 +1321,9 @@ void SerialInput(void) char hex_char[(serial_in_byte_counter * 2) + 2]; bool assume_json = (!Settings.flag.mqtt_serial_raw && (serial_in_buffer[0] == '{')); Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":%s%s%s}"), - (assume_json) ? "" : """", + (assume_json) ? "" : "\"", (Settings.flag.mqtt_serial_raw) ? ToHex_P((unsigned char*)serial_in_buffer, serial_in_byte_counter, hex_char, sizeof(hex_char)) : serial_in_buffer, - (assume_json) ? "" : """"); + (assume_json) ? "" : "\""); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); XdrvRulesProcess(); serial_in_byte_counter = 0; @@ -1297,13 +1332,31 @@ void SerialInput(void) /********************************************************************************************/ +void ResetPwm(void) +{ + for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only + if (PinUsed(GPIO_PWM1, i)) { + analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); +// analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); + } + } +} + +/********************************************************************************************/ + void GpioInit(void) { - uint32_t mpin; - if (!ValidModule(Settings.module)) { uint32_t module = MODULE; - if (!ValidModule(MODULE)) { module = SONOFF_BASIC; } + if (!ValidModule(MODULE)) { +#ifdef ESP8266 + module = SONOFF_BASIC; +#endif // ESP8266 +#ifdef ESP32 + module = WEMOS; +#endif // ESP32 + } + Settings.module = module; Settings.last_module = module; } @@ -1314,25 +1367,26 @@ void GpioInit(void) Settings.serial_config = TS_SERIAL_8N1; } - for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { - if ((Settings.user_template.gp.io[i] >= GPIO_SENSOR_END) && (Settings.user_template.gp.io[i] < GPIO_USER)) { - Settings.user_template.gp.io[i] = GPIO_USER; // Fix not supported sensor ids in template + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { + if ((Settings.user_template.gp.io[i] >= AGPIO(GPIO_SENSOR_END)) && (Settings.user_template.gp.io[i] < AGPIO(GPIO_USER))) { + Settings.user_template.gp.io[i] = AGPIO(GPIO_USER); // Fix not supported sensor ids in template } } myio def_gp; ModuleGpios(&def_gp); - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if ((Settings.my_gp.io[i] >= GPIO_SENSOR_END) && (Settings.my_gp.io[i] < GPIO_USER)) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { + if ((Settings.my_gp.io[i] >= AGPIO(GPIO_SENSOR_END)) && (Settings.my_gp.io[i] < AGPIO(GPIO_USER))) { Settings.my_gp.io[i] = GPIO_NONE; // Fix not supported sensor ids in module } else if (Settings.my_gp.io[i] > GPIO_NONE) { my_module.io[i] = Settings.my_gp.io[i]; // Set User selected Module sensors } - if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < GPIO_USER)) { + if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < AGPIO(GPIO_USER))) { my_module.io[i] = def_gp.io[i]; // Force Template override } } +#ifdef ESP8266 if ((Settings.my_adc0 >= ADC0_END) && (Settings.my_adc0 < ADC0_USER)) { Settings.my_adc0 = ADC0_NONE; // Fix not supported sensor ids in module } @@ -1344,51 +1398,49 @@ void GpioInit(void) if ((template_adc0 > ADC0_NONE) && (template_adc0 < ADC0_USER)) { my_adc0 = template_adc0; // Force Template override } +#endif - for (uint32_t i = 0; i < GPIO_MAX; i++) { - pin[i] = 99; - } - for (uint32_t i = 0; i < sizeof(my_module.io); i++) { - mpin = ValidPin(i, my_module.io[i]); + for (uint32_t i = 0; i < ARRAY_SIZE(my_module.io); i++) { + uint32_t mpin = ValidPin(i, my_module.io[i]); DEBUG_CORE_LOG(PSTR("INI: gpio pin %d, mpin %d"), i, mpin); - if (mpin) { + if (mpin) { // Above GPIO_NONE XdrvMailbox.index = mpin; XdrvMailbox.payload = i; - if ((mpin >= GPIO_SWT1_NP) && (mpin < (GPIO_SWT1_NP + MAX_SWITCHES))) { - SwitchPullupFlag(mpin - GPIO_SWT1_NP); - mpin -= (GPIO_SWT1_NP - GPIO_SWT1); + if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES))) { + SwitchPullupFlag(mpin - AGPIO(GPIO_SWT1_NP)); + mpin -= (AGPIO(GPIO_SWT1_NP) - AGPIO(GPIO_SWT1)); } - else if ((mpin >= GPIO_KEY1_NP) && (mpin < (GPIO_KEY1_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_NP); // 0 .. 3 - mpin -= (GPIO_KEY1_NP - GPIO_KEY1); + else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS))) { + ButtonPullupFlag(mpin - AGPIO(GPIO_KEY1_NP)); // 0 .. 3 + mpin -= (AGPIO(GPIO_KEY1_NP) - AGPIO(GPIO_KEY1)); } - else if ((mpin >= GPIO_KEY1_INV) && (mpin < (GPIO_KEY1_INV + MAX_KEYS))) { - ButtonInvertFlag(mpin - GPIO_KEY1_INV); // 0 .. 3 - mpin -= (GPIO_KEY1_INV - GPIO_KEY1); + else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS))) { + ButtonInvertFlag(mpin - AGPIO(GPIO_KEY1_INV)); // 0 .. 3 + mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1)); } - else if ((mpin >= GPIO_KEY1_INV_NP) && (mpin < (GPIO_KEY1_INV_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 - ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 - mpin -= (GPIO_KEY1_INV_NP - GPIO_KEY1); + else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS))) { + ButtonPullupFlag(mpin - AGPIO(GPIO_KEY1_INV_NP)); // 0 .. 3 + ButtonInvertFlag(mpin - AGPIO(GPIO_KEY1_INV_NP)); // 0 .. 3 + mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1)); } - else if ((mpin >= GPIO_REL1_INV) && (mpin < (GPIO_REL1_INV + MAX_RELAYS))) { - bitSet(rel_inverted, mpin - GPIO_REL1_INV); - mpin -= (GPIO_REL1_INV - GPIO_REL1); + else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS))) { + bitSet(rel_inverted, mpin - AGPIO(GPIO_REL1_INV)); + mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); } - else if ((mpin >= GPIO_LED1_INV) && (mpin < (GPIO_LED1_INV + MAX_LEDS))) { - bitSet(led_inverted, mpin - GPIO_LED1_INV); - mpin -= (GPIO_LED1_INV - GPIO_LED1); + else if ((mpin >= AGPIO(GPIO_LED1_INV)) && (mpin < (AGPIO(GPIO_LED1_INV) + MAX_LEDS))) { + bitSet(led_inverted, mpin - AGPIO(GPIO_LED1_INV)); + mpin -= (AGPIO(GPIO_LED1_INV) - AGPIO(GPIO_LED1)); } - else if (mpin == GPIO_LEDLNK_INV) { + else if (mpin == AGPIO(GPIO_LEDLNK_INV)) { ledlnk_inverted = 1; - mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK); + mpin -= (AGPIO(GPIO_LEDLNK_INV) - AGPIO(GPIO_LEDLNK)); } - else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) { - bitSet(pwm_inverted, mpin - GPIO_PWM1_INV); - mpin -= (GPIO_PWM1_INV - GPIO_PWM1); + else if ((mpin >= AGPIO(GPIO_PWM1_INV)) && (mpin < (AGPIO(GPIO_PWM1_INV) + MAX_PWMS))) { + bitSet(pwm_inverted, mpin - AGPIO(GPIO_PWM1_INV)); + mpin -= (AGPIO(GPIO_PWM1_INV) - AGPIO(GPIO_PWM1)); } else if (XdrvCall(FUNC_PIN_STATE)) { mpin = XdrvMailbox.index; @@ -1397,34 +1449,54 @@ void GpioInit(void) mpin = XdrvMailbox.index; }; } - if (mpin) pin[mpin] = i; + if (mpin) { SetPin(i, mpin); } // Anything above GPIO_NONE and below GPIO_SENSOR_END } - if ((2 == pin[GPIO_TXD]) || (H801 == my_module_type)) { Serial.set_tx(2); } +// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)gpio_pin, ARRAY_SIZE(gpio_pin), sizeof(gpio_pin[0])); + +#ifdef ESP8266 + if ((2 == Pin(GPIO_TXD)) || (H801 == my_module_type)) { Serial.set_tx(2); } analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) #ifdef USE_SPI - spi_flg = ((((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CS] > 14)) || (pin[GPIO_SPI_CS] < 12)) || (((pin[GPIO_SPI_DC] < 99) && (pin[GPIO_SPI_DC] > 14)) || (pin[GPIO_SPI_DC] < 12))); + spi_flg = (((PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_CS) > 14)) || (Pin(GPIO_SPI_CS) < 12)) || ((PinUsed(GPIO_SPI_DC) && (Pin(GPIO_SPI_DC) > 14)) || (Pin(GPIO_SPI_DC) < 12))); if (spi_flg) { - for (uint32_t i = 0; i < GPIO_MAX; i++) { - if ((pin[i] >= 12) && (pin[i] <=14)) pin[i] = 99; - } my_module.io[12] = GPIO_SPI_MISO; - pin[GPIO_SPI_MISO] = 12; + SetPin(12, GPIO_SPI_MISO); my_module.io[13] = GPIO_SPI_MOSI; - pin[GPIO_SPI_MOSI] = 13; + SetPin(13, GPIO_SPI_MOSI); my_module.io[14] = GPIO_SPI_CLK; - pin[GPIO_SPI_CLK] = 14; + SetPin(14, GPIO_SPI_CLK); } - soft_spi_flg = ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && ((pin[GPIO_SSPI_MOSI] < 99) || (pin[GPIO_SSPI_MOSI] < 99))); + soft_spi_flg = (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MISO))); #endif // USE_SPI +#else // ESP32 + analogWriteFreqRange(0, Settings.pwm_frequency, Settings.pwm_range); + +#ifdef USE_SPI + spi_flg = (PinUsed(GPIO_SPI_CLK) && (PinUsed(GPIO_SPI_MOSI) || PinUsed(GPIO_SPI_MISO))); + soft_spi_flg = (PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MISO))); +#endif // USE_SPI +#endif // ESP8266 - ESP32 + + // Set any non-used GPIO to INPUT - Related to resetPins() in support_legacy_cores.ino + // Doing it here solves relay toggles at restart. + for (uint32_t i = 0; i < ARRAY_SIZE(my_module.io); i++) { + uint32_t mpin = ValidPin(i, my_module.io[i]); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("INI: gpio pin %d, mpin %d"), i, mpin); + if (((i < 6) || (i > 11)) && (0 == mpin)) { // Skip SPI flash interface + if (!((1 == i) || (3 == i))) { // Skip serial + pinMode(i, INPUT); + } + } + } #ifdef USE_I2C - i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99)); + i2c_flg = (PinUsed(GPIO_I2C_SCL) && PinUsed(GPIO_I2C_SDA)); if (i2c_flg) { - Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]); + Wire.begin(Pin(GPIO_I2C_SDA), Pin(GPIO_I2C_SCL)); } #endif // USE_I2C @@ -1433,6 +1505,7 @@ void GpioInit(void) if (XdrvCall(FUNC_MODULE_INIT)) { // Serviced } +#ifdef ESP8266 else if (YTF_IR_BRIDGE == my_module_type) { ClaimSerial(); // Stop serial loopback mode // devices_present = 1; @@ -1450,54 +1523,61 @@ void GpioInit(void) SetSerial(19200, TS_SERIAL_8N1); } #endif // USE_SONOFF_SC +#endif // ESP8266 for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only - if (pin[GPIO_PWM1 +i] < 99) { - pinMode(pin[GPIO_PWM1 +i], OUTPUT); + if (PinUsed(GPIO_PWM1, i)) { + pinMode(Pin(GPIO_PWM1, i), OUTPUT); +#ifdef ESP32 + analogAttach(Pin(GPIO_PWM1, i),i); + analogWriteFreqRange(i,Settings.pwm_frequency,Settings.pwm_range); +#endif + if (light_type) { // force PWM GPIOs to low or high mode, see #7165 - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); + analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); } else { pwm_present = true; - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); + analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); } } } for (uint32_t i = 0; i < MAX_RELAYS; i++) { - if (pin[GPIO_REL1 +i] < 99) { - pinMode(pin[GPIO_REL1 +i], OUTPUT); + if (PinUsed(GPIO_REL1, i)) { + pinMode(Pin(GPIO_REL1, i), OUTPUT); devices_present++; +#ifdef ESP8266 if (EXS_RELAY == my_module_type) { - digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0); + digitalWrite(Pin(GPIO_REL1, i), bitRead(rel_inverted, i) ? 1 : 0); if (i &1) { devices_present--; } } +#endif // ESP8266 } } for (uint32_t i = 0; i < MAX_LEDS; i++) { - if (pin[GPIO_LED1 +i] < 99) { + if (PinUsed(GPIO_LED1, i)) { #ifdef USE_ARILUX_RF - if ((3 == i) && (leds_present < 2) && (99 == pin[GPIO_ARIRFSEL])) { - pin[GPIO_ARIRFSEL] = pin[GPIO_LED4]; // Legacy support where LED4 was Arilux RF enable - pin[GPIO_LED4] = 99; + if ((3 == i) && (leds_present < 2) && !PinUsed(GPIO_ARIRFSEL)) { + SetPin(Pin(GPIO_LED1, i), GPIO_ARIRFSEL); // Legacy support where LED4 was Arilux RF enable } else { #endif - pinMode(pin[GPIO_LED1 +i], OUTPUT); + pinMode(Pin(GPIO_LED1, i), OUTPUT); leds_present++; - digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i)); + digitalWrite(Pin(GPIO_LED1, i), bitRead(led_inverted, i)); #ifdef USE_ARILUX_RF } #endif } } - if (pin[GPIO_LEDLNK] < 99) { - pinMode(pin[GPIO_LEDLNK], OUTPUT); - digitalWrite(pin[GPIO_LEDLNK], ledlnk_inverted); + if (PinUsed(GPIO_LEDLNK)) { + pinMode(Pin(GPIO_LEDLNK), OUTPUT); + digitalWrite(Pin(GPIO_LEDLNK), ledlnk_inverted); } #ifdef USE_PWM_DIMMER - if (PWM_DIMMER == my_module_type && pin[GPIO_REL1] < 99) devices_present--; + if (PWM_DIMMER == my_module_type && PinUsed(GPIO_REL1)) { devices_present--; } #endif // USE_PWM_DIMMER ButtonInit(); diff --git a/tasmota/support_udp.ino b/tasmota/support_udp.ino index 701abee66..451135e6a 100644 --- a/tasmota/support_udp.ino +++ b/tasmota/support_udp.ino @@ -49,7 +49,11 @@ bool UdpDisconnect(void) { if (udp_connected) { PortUdp.flush(); +#ifdef USE_DEVICE_GROUPS + PortUdp.stop(); +#else // USE_DEVICE_GROUPS WiFiUDP::stopAll(); +#endif // !USE_DEVICE_GROUPS AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED)); udp_connected = false; } @@ -135,12 +139,6 @@ void PollUdp(void) continue; } } - -#ifdef USE_DEVICE_GROUPS - if (Settings.flag4.device_groups_enabled && !strncmp_P(packet_buffer, kDeviceGroupMessage, sizeof(DEVICE_GROUP_MESSAGE) - 1)) { - ProcessDeviceGroupMessage(packet_buffer, len); - } -#endif // USE_DEVICE_GROUPS } optimistic_yield(100); } diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index f4e88a49d..c42e1d49a 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -156,10 +156,10 @@ void WiFiSetSleepMode(void) // Sleep explanation: https://github.com/esp8266/Arduino/blob/3f0c601cfe81439ce17e9bd5d28994a7ed144482/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp#L255 #if defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) #else // Enabled in 2.3.0, 2.4.0 and stage - if (sleep && Settings.flag3.sleep_normal) { // SetOption60 - Enable normal sleep instead of dynamic sleep - WiFi.setSleepMode(WIFI_LIGHT_SLEEP); // Allow light sleep during idle times + if (ssleep && Settings.flag3.sleep_normal) { // SetOption60 - Enable normal sleep instead of dynamic sleep + WiFi.setSleepMode(WIFI_LIGHT_SLEEP); // Allow light sleep during idle times } else { - WiFi.setSleepMode(WIFI_MODEM_SLEEP); // Disable sleep (Esp8288/Arduino core and sdk default) + WiFi.setSleepMode(WIFI_MODEM_SLEEP); // Disable sleep (Esp8288/Arduino core and sdk default) } #endif WifiSetOutputPower(); @@ -401,6 +401,11 @@ void WifiCheckIp(void) Settings.ip_address[1] = (uint32_t)WiFi.gatewayIP(); Settings.ip_address[2] = (uint32_t)WiFi.subnetMask(); Settings.ip_address[3] = (uint32_t)WiFi.dnsIP(); + + // Save current AP parameters for quick reconnect + Settings.wifi_channel = WiFi.channel(); + uint8_t *bssid = WiFi.BSSID(); + memcpy((void*) &Settings.wifi_bssid, (void*) bssid, sizeof(Settings.wifi_bssid)); } Wifi.status = WL_CONNECTED; #ifdef USE_DISCOVERY @@ -423,6 +428,7 @@ void WifiCheckIp(void) break; case WL_NO_SSID_AVAIL: AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_NOT_REACHED)); + Settings.wifi_channel = 0; // Disable stored AP if (WIFI_WAIT == Settings.sta_config) { Wifi.retry = Wifi.retry_init; } else { @@ -436,6 +442,7 @@ void WifiCheckIp(void) break; case WL_CONNECT_FAILED: AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_WRONG_PASSWORD)); + Settings.wifi_channel = 0; // Disable stored AP if (Wifi.retry > (Wifi.retry_init / 2)) { Wifi.retry = Wifi.retry_init / 2; } @@ -446,8 +453,10 @@ void WifiCheckIp(void) default: // WL_IDLE_STATUS and WL_DISCONNECTED if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) { AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT)); + Settings.wifi_channel = 0; // Disable stored AP } else { if (!strlen(SettingsText(SET_STASSID1)) && !strlen(SettingsText(SET_STASSID2))) { + Settings.wifi_channel = 0; // Disable stored AP wifi_config_tool = WIFI_MANAGER; // Skip empty SSIDs and start Wifi config tool Wifi.retry = 0; } else { @@ -462,7 +471,7 @@ void WifiCheckIp(void) } } else { if (Wifi.retry_init == Wifi.retry) { - WifiBegin(3, 0); // Select default SSID + WifiBegin(3, Settings.wifi_channel); // Select default SSID } if ((Settings.sta_config != WIFI_WAIT) && ((Wifi.retry_init / 2) == Wifi.retry)) { WifiBegin(2, 0); // Select alternate SSID @@ -564,14 +573,12 @@ void WifiCheck(uint8_t param) StopWebserver(); } #ifdef USE_EMULATION -#ifdef USE_DEVICE_GROUPS - if (Settings.flag2.emulation || Settings.flag4.device_groups_enabled) { UdpConnect(); } -#else // USE_DEVICE_GROUPS if (Settings.flag2.emulation) { UdpConnect(); } -#endif // USE_DEVICE_GROUPS #endif // USE_EMULATION #endif // USE_WEBSERVER - +#ifdef USE_DEVICE_GROUPS + DeviceGroupsStart(); +#endif // USE_DEVICE_GROUPS #ifdef USE_KNX if (!knx_started && Settings.flag.knx_enabled) { // CMND_KNX_ENABLED KNXStart(); @@ -584,6 +591,9 @@ void WifiCheck(uint8_t param) #ifdef USE_EMULATION UdpDisconnect(); #endif // USE_EMULATION +#ifdef USE_DEVICE_GROUPS + DeviceGroupsStop(); +#endif // USE_DEVICE_GROUPS Wifi.mdns_begun = 0; #ifdef USE_KNX knx_started = false; @@ -646,10 +656,12 @@ void WifiConnect(void) WifiSetOutputPower(); WiFi.persistent(false); // Solve possible wifi init errors Wifi.status = 0; - Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + (ESP.getChipId() & 0xF); // Add extra delay to stop overrun by simultanous re-connects + Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + (ESP_getChipId() & 0xF); // Add extra delay to stop overrun by simultanous re-connects Wifi.retry = Wifi.retry_init; Wifi.counter = 1; + memcpy((void*) &Wifi.bssid, (void*) Settings.wifi_bssid, sizeof(Wifi.bssid)); + #ifdef WIFI_RF_PRE_INIT if (rf_pre_init_flag) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Pre-init done")); @@ -694,8 +706,58 @@ void WifiShutdown(bool option = false) void EspRestart(void) { + ResetPwm(); WifiShutdown(true); CrashDumpClear(); // Clear the stack dump in RTC -// ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 - ESP.reset(); + ESP_Restart(); } + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 +// +// Gratuitous ARP, backported from https://github.com/esp8266/Arduino/pull/6889 +// +extern "C" { +#if LWIP_VERSION_MAJOR == 1 +#include "netif/wlan_lwip_if.h" // eagle_lwip_getif() +#include "netif/etharp.h" // gratuitous arp +#else +#include "lwip/etharp.h" // gratuitous arp +#endif +} + +unsigned long wifiTimer = 0; + +void stationKeepAliveNow(void) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_WIFI "Sending Gratuitous ARP")); + for (netif* interface = netif_list; interface != nullptr; interface = interface->next) + if ( + (interface->flags & NETIF_FLAG_LINK_UP) + && (interface->flags & NETIF_FLAG_UP) +#if LWIP_VERSION_MAJOR == 1 + && interface == eagle_lwip_getif(STATION_IF) /* lwip1 does not set if->num properly */ + && (!ip_addr_isany(&interface->ip_addr)) +#else + && interface->num == STATION_IF + && (!ip4_addr_isany_val(*netif_ip4_addr(interface))) +#endif + ) + { + etharp_gratuitous(interface); + break; + } +} + +void wifiKeepAlive(void) { + uint32_t wifiTimerSec = Settings.param[P_ARP_GRATUITOUS]; // 8-bits number of seconds, or minutes if > 100 + + if ((WL_CONNECTED != Wifi.status) || (0 == wifiTimerSec)) { return; } // quick exit if wifi not connected or feature disabled + + if (TimeReached(wifiTimer)) { + stationKeepAliveNow(); + if (wifiTimerSec > 100) { + wifiTimerSec = (wifiTimerSec - 100) * 60; // convert >100 as minutes, ex: 105 = 5 minutes, 110 = 10 minutes + } + SetNextTimeInterval(wifiTimer, wifiTimerSec * 1000); + } +} +#endif // ARDUINO_ESP8266_RELEASE_2_3_0 \ No newline at end of file diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index a1c7d1082..3cb309910 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -30,7 +30,6 @@ * Default sensor states \*********************************************************************************************/ -#define CODE_IMAGE 0 #define CODE_IMAGE_STR "tasmota" #define USE_LIGHT // Enable light control @@ -69,7 +68,7 @@ const uint8_t MAX_XDRV_DRIVERS = 96; // Max number of allowed driver driv const uint8_t MAX_XSNS_DRIVERS = 96; // Max number of allowed sensor drivers const uint8_t MAX_I2C_DRIVERS = 96; // Max number of allowed i2c drivers const uint8_t MAX_SHUTTERS = 4; // Max number of shutters -const uint8_t MAX_PCF8574 = 8; // Max number of PCF8574 devices +const uint8_t MAX_PCF8574 = 4; // Max number of PCF8574 devices const uint8_t MAX_RULE_SETS = 3; // Max number of rule sets of size 512 characters const uint16_t MAX_RULE_SIZE = 512; // Max number of characters in rules @@ -81,6 +80,8 @@ const uint8_t MAX_NTP_SERVERS = 3; // Max number of NTP servers const uint8_t MAX_RULE_MEMS = 16; // Max number of saved vars const uint8_t MAX_FRIENDLYNAMES = 8; // Max number of Friendly names const uint8_t MAX_BUTTON_TEXT = 16; // Max number of GUI button labels +const uint8_t MAX_GROUP_TOPICS = 4; // Max number of Group Topics +const uint8_t MAX_DEV_GROUP_NAMES = 4; // Max number of Device Group names const uint8_t MAX_HUE_DEVICES = 15; // Max number of Philips Hue device per emulation @@ -103,9 +104,9 @@ const uint16_t WS2812_MAX_LEDS = 512; // Max number of LEDs const uint32_t PWM_RANGE = 1023; // 255..1023 needs to be devisible by 256 //const uint16_t PWM_FREQ = 1000; // 100..1000 Hz led refresh //const uint16_t PWM_FREQ = 910; // 100..1000 Hz led refresh (iTead value) -const uint16_t PWM_FREQ = 880; // 100..1000 Hz led refresh (BN-SZ01 value) +const uint16_t PWM_FREQ = 977; // 100..4000 Hz led refresh const uint16_t PWM_MAX = 4000; // [PWM_MAX] Maximum frequency - Default: 4000 -const uint16_t PWM_MIN = 100; // [PWM_MIN] Minimum frequency - Default: 100 +const uint16_t PWM_MIN = 40; // [PWM_MIN] Minimum frequency - Default: 40 // For Dimmers use double of your mains AC frequecy (100 for 50Hz and 120 for 60Hz) // For Controlling Servos use 50 and also set PWM_FREQ as 50 (DO NOT USE THESE VALUES FOR DIMMERS) @@ -123,7 +124,7 @@ const uint16_t SYSLOG_TIMER = 600; // Seconds to restore syslog_level const uint16_t SERIALLOG_TIMER = 600; // Seconds to disable SerialLog const uint8_t OTA_ATTEMPTS = 5; // Number of times to try fetching the new firmware -const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in (serial and http) command buffer +const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in serial command buffer const uint16_t FLOATSZ = 16; // Max number of characters in float result from dtostrfd (max 32) const uint16_t CMDSZ = 24; // Max number of characters in command const uint16_t TOPSZ = 151; // Max number of characters in topic string @@ -153,19 +154,6 @@ const uint32_t LOOP_SLEEP_DELAY = 50; // Lowest number of milliseconds to #define MAX_RULE_TIMERS 8 // Max number of rule timers (4 bytes / timer) #define MAX_RULE_VARS 16 // Max number of rule variables (33 bytes / variable) -/* -// Removed from esp8266 core since 20171105 -#define min(a,b) ((a)<(b)?(a):(b)) -#define max(a,b) ((a)>(b)?(a):(b)) -*/ -#define tmin(a,b) ((a)<(b)?(a):(b)) -#define tmax(a,b) ((a)>(b)?(a):(b)) - -#define STR_HELPER(x) #x -#ifndef STR -#define STR(x) STR_HELPER(x) -#endif - //enum ws2812NeopixelbusFeature { NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_3LED, NEO_RGBW, NEO_GRBW }; // Doesn't work #define NEO_RGB 0 // Neopixel RGB leds #define NEO_GRB 1 // Neopixel GRB leds @@ -246,6 +234,7 @@ enum ExecuteCommandPowerOptions { POWER_OFF, POWER_ON, POWER_TOGGLE, POWER_BLINK POWER_SHOW_STATE = 16 }; enum SendKeyPowerOptions { POWER_HOLD = 3, POWER_INCREMENT = 4, POWER_INV = 5, POWER_CLEAR = 6, CLEAR_RETAIN = 9 }; enum SendKeyOptions { KEY_BUTTON, KEY_SWITCH }; +enum SendKeyMultiClick { SINGLE = 10, DOUBLE = 11, TRIPLE = 12, QUAD = 13, PENTA = 14}; enum PowerOnStateOptions { POWER_ALL_OFF, POWER_ALL_ON, POWER_ALL_SAVED_TOGGLE, POWER_ALL_SAVED, POWER_ALL_ALWAYS_ON, POWER_ALL_OFF_PULSETIME_ON }; @@ -254,7 +243,7 @@ enum ButtonStates { PRESSED, NOT_PRESSED }; enum Shortcuts { SC_CLEAR, SC_DEFAULT, SC_USER }; enum SettingsParamIndex { P_HOLD_TIME, P_MAX_POWER_RETRY, P_BACKLOG_DELAY, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, // SetOption32 .. SetOption38 - P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_ex_TUYA_RELAYS, P_OVER_TEMP, // SetOption39 .. SetOption42 + P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_ARP_GRATUITOUS, P_OVER_TEMP, // SetOption39 .. SetOption42 P_ex_DIMMER_MAX, P_ex_TUYA_VOLTAGE_ID, P_ex_TUYA_CURRENT_ID, P_ex_TUYA_POWER_ID, // SetOption43 .. SetOption46 P_ex_ENERGY_TARIFF1, P_ex_ENERGY_TARIFF2, // SetOption47 .. SetOption48 P_MAX_PARAM8 }; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 @@ -301,36 +290,56 @@ enum SettingsTextIndex { SET_OTAURL, SET_BUTTON1, SET_BUTTON2, SET_BUTTON3, SET_BUTTON4, SET_BUTTON5, SET_BUTTON6, SET_BUTTON7, SET_BUTTON8, SET_BUTTON9, SET_BUTTON10, SET_BUTTON11, SET_BUTTON12, SET_BUTTON13, SET_BUTTON14, SET_BUTTON15, SET_BUTTON16, SET_MQTT_GRP_TOPIC2, SET_MQTT_GRP_TOPIC3, SET_MQTT_GRP_TOPIC4, + SET_TEMPLATE_NAME, + SET_DEV_GROUP_NAME1, SET_DEV_GROUP_NAME2, SET_DEV_GROUP_NAME3, SET_DEV_GROUP_NAME4, SET_MAX }; -enum DeviceGroupMessageType { DGR_MSGTYP_FULL_STATUS, DGR_MSGTYP_PARTIAL_UPDATE, DGR_MSGTYP_UPDATE, DGR_MSGTYP_UPDATE_MORE_TO_COME, DGR_MSGTYP_UPDATE_DIRECT, DGR_MSGTYP_REUPDATE }; +enum DevGroupMessageType { DGR_MSGTYP_FULL_STATUS, DGR_MSGTYP_PARTIAL_UPDATE, DGR_MSGTYP_UPDATE, DGR_MSGTYP_UPDATE_MORE_TO_COME, DGR_MSGTYP_UPDATE_DIRECT, DGR_MSGTYPE_UPDATE_COMMAND }; -enum DeviceGroupMessageFlag { DGR_FLAG_RESET = 1, DGR_FLAG_STATUS_REQUEST = 2, DGR_FLAG_FULL_STATUS = 4, DGR_FLAG_ACK = 8, DGR_FLAG_MORE_TO_COME = 16, DGR_FLAG_DIRECT = 32, DGR_FLAG_ANNOUNCEMENT = 64 }; +enum DevGroupMessageFlag { DGR_FLAG_RESET = 1, DGR_FLAG_STATUS_REQUEST = 2, DGR_FLAG_FULL_STATUS = 4, DGR_FLAG_ACK = 8, DGR_FLAG_MORE_TO_COME = 16, DGR_FLAG_DIRECT = 32, DGR_FLAG_ANNOUNCEMENT = 64 }; -enum DeviceGroupItem { DGR_ITEM_EOL, DGR_ITEM_STATUS, - DGR_ITEM_LIGHT_FADE, DGR_ITEM_LIGHT_SPEED, DGR_ITEM_LIGHT_BRI, DGR_ITEM_LIGHT_SCHEME, DGR_ITEM_LIGHT_FIXED_COLOR, - DGR_ITEM_BRI_PRESET_LOW, DGR_ITEM_BRI_PRESET_HIGH, DGR_ITEM_BRI_POWER_ON, - // Add new 8-bit items before this line - DGR_ITEM_LAST_8BIT, DGR_ITEM_MAX_8BIT = 63, - DGR_ITEM_ANALOG1, DGR_ITEM_ANALOG2, DGR_ITEM_ANALOG3, DGR_ITEM_ANALOG4, DGR_ITEM_ANALOG5, - // Add new 16-bit items before this line - DGR_ITEM_LAST_16BIT, DGR_ITEM_MAX_16BIT = 127, - DGR_ITEM_POWER, DGR_ITEM_DIMMER_RANGE, - // Add new 32-bit items before this line - DGR_ITEM_LAST_32BIT, DGR_ITEM_MAX_32BIT = 191, - // Add new string items before this line - DGR_ITEM_LAST_STRING, DGR_ITEM_MAX_STRING = 223, - DGR_ITEM_LIGHT_CHANNELS }; +enum DevGroupItem { DGR_ITEM_EOL, DGR_ITEM_STATUS, DGR_ITEM_FLAGS, + DGR_ITEM_LIGHT_FADE, DGR_ITEM_LIGHT_SPEED, DGR_ITEM_LIGHT_BRI, DGR_ITEM_LIGHT_SCHEME, DGR_ITEM_LIGHT_FIXED_COLOR, + DGR_ITEM_BRI_PRESET_LOW, DGR_ITEM_BRI_PRESET_HIGH, DGR_ITEM_BRI_POWER_ON, + // Add new 8-bit items before this line + DGR_ITEM_LAST_8BIT, DGR_ITEM_MAX_8BIT = 63, + //DGR_ITEM_ANALOG1, DGR_ITEM_ANALOG2, DGR_ITEM_ANALOG3, DGR_ITEM_ANALOG4, DGR_ITEM_ANALOG5, + // Add new 16-bit items before this line + DGR_ITEM_LAST_16BIT, DGR_ITEM_MAX_16BIT = 127, + DGR_ITEM_POWER, + // Add new 32-bit items before this line + DGR_ITEM_LAST_32BIT, DGR_ITEM_MAX_32BIT = 191, + DGR_ITEM_EVENT, DGR_ITEM_COMMAND, + // Add new string items before this line + DGR_ITEM_LAST_STRING, DGR_ITEM_MAX_STRING = 223, + DGR_ITEM_LIGHT_CHANNELS }; -enum DeviceGroupShareItem { DGR_SHARE_POWER = 1, DGR_SHARE_LIGHT_BRI = 2, DGR_SHARE_LIGHT_FADE = 4, DGR_SHARE_LIGHT_SCHEME = 8, - DGR_SHARE_LIGHT_COLOR = 16, DGR_SHARE_DIMMER_SETTINGS = 32 }; +enum DevGroupShareItem { DGR_SHARE_POWER = 1, DGR_SHARE_LIGHT_BRI = 2, DGR_SHARE_LIGHT_FADE = 4, DGR_SHARE_LIGHT_SCHEME = 8, + DGR_SHARE_LIGHT_COLOR = 16, DGR_SHARE_DIMMER_SETTINGS = 32, DGR_SHARE_EVENT = 64 }; enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_SHUTTER, - SRC_MAX }; + SRC_THERMOSTAT, SRC_MAX }; const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|" - "Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote|Shutter"; + "Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote|Shutter|Thermostat"; const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; +enum TasmotaSerialConfig { + TS_SERIAL_5N1, TS_SERIAL_6N1, TS_SERIAL_7N1, TS_SERIAL_8N1, + TS_SERIAL_5N2, TS_SERIAL_6N2, TS_SERIAL_7N2, TS_SERIAL_8N2, + TS_SERIAL_5E1, TS_SERIAL_6E1, TS_SERIAL_7E1, TS_SERIAL_8E1, + TS_SERIAL_5E2, TS_SERIAL_6E2, TS_SERIAL_7E2, TS_SERIAL_8E2, + TS_SERIAL_5O1, TS_SERIAL_6O1, TS_SERIAL_7O1, TS_SERIAL_8O1, + TS_SERIAL_5O2, TS_SERIAL_6O2, TS_SERIAL_7O2, TS_SERIAL_8O2 }; + +const SerConfu8 kTasmotaSerialConfig[] PROGMEM = { + SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1, + SERIAL_5N2, SERIAL_6N2, SERIAL_7N2, SERIAL_8N2, + SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_8E1, + SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, + SERIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1, + SERIAL_5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2 +}; + #endif // _TASMOTA_H_ diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 6572be777..091b6eed3 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -32,13 +32,14 @@ // Location specific includes #include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) +#include "tasmota_compat.h" #include "tasmota_version.h" // Tasmota version information #include "tasmota.h" // Enumeration used in my_user_config.h #include "my_user_config.h" // Fixed user configurable options #ifdef USE_MQTT_TLS - #include // we need to include before "tasmota_post.h" to take precedence over the BearSSL version in Arduino + #include // We need to include before "tasmota_globals.h" to take precedence over the BearSSL version in Arduino #endif // USE_MQTT_TLS -#include "tasmota_post.h" // Configuration overrides for all previous includes +#include "tasmota_globals.h" // Function prototypes and global configuration #include "i18n.h" // Language support configured by my_user_config.h #include "tasmota_template.h" // Hardware configuration @@ -117,15 +118,24 @@ uint16_t tele_period = 9999; // Tele period timer uint16_t blink_counter = 0; // Number of blink cycles uint16_t seriallog_timer = 0; // Timer to disable Seriallog uint16_t syslog_timer = 0; // Timer to re-enable syslog_level + +#ifdef ESP32 +uint16_t gpio_pin[MAX_GPIO_PIN] = { 0 }; // GPIO functions indexed by pin number +#endif // ESP32 + int16_t save_data_counter; // Counter and flag for config save to Flash RulesBitfield rules_flag; // Rule state flags (16 bits) uint8_t mqtt_cmnd_blocked = 0; // Ignore flag for publish command uint8_t mqtt_cmnd_blocked_reset = 0; // Count down to reset if needed uint8_t state_250mS = 0; // State 250msecond per second flag uint8_t latching_relay_pulse = 0; // Latching relay pulse timer -uint8_t sleep; // Current copy of Settings.sleep +uint8_t ssleep; // Current copy of Settings.sleep uint8_t blinkspeed = 1; // LED blink rate -uint8_t pin[GPIO_MAX]; // Possible pin configurations + +#ifdef ESP8266 +uint8_t gpio_pin[MAX_GPIO_PIN] = { 0 }; // GPIO functions indexed by pin number +#endif // ESP8266 - ESP32 + uint8_t active_device = 1; // Active device in ExecuteCommandPower uint8_t leds_present = 0; // Max number of LED supported uint8_t led_inverted = 0; // LED inverted flag (1 = (0 = On, 1 = Off)) @@ -141,12 +151,13 @@ uint8_t devices_present = 0; // Max number of devices supported uint8_t seriallog_level; // Current copy of Settings.seriallog_level uint8_t syslog_level; // Current copy of Settings.syslog_level uint8_t my_module_type; // Current copy of Settings.module or user template type -uint8_t my_adc0; // Active copy of Module ADC0 +uint8_t my_adc0 = 0; // Active copy of Module ADC0 uint8_t last_source = 0; // Last command source uint8_t shutters_present = 0; // Number of actual define shutters uint8_t prepped_loglevel = 0; // Delayed log level message //uint8_t mdns_delayed_start = 0; // mDNS delayed start -bool serial_local = false; // Handle serial locally; +bool serial_local = false; // Handle serial locally +bool serial_buffer_overrun = false; // Serial buffer overrun bool fallback_topic_flag = false; // Use Topic or FallbackTopic bool backlog_mutex = false; // Command backlog pending bool interlock_mutex = false; // Interlock power command pending @@ -187,8 +198,13 @@ char web_log[WEB_LOG_SIZE] = {'\0'}; // Web log buffer * Main \*********************************************************************************************/ -void setup(void) -{ +void setup(void) { +#ifdef ESP32 +#ifdef DISABLE_ESP32_BROWNOUT + DisableBrownout(); // Workaround possible weak LDO resulting in brownout detection during Wifi connection +#endif +#endif + global_state.data = 3; // Init global state (wifi_down, mqtt_down) to solve possible network issues RtcRebootLoad(); @@ -199,6 +215,7 @@ void setup(void) RtcRebootSave(); Serial.begin(APP_BAUDRATE); +// Serial.setRxBufferSize(INPUT_BUFFER_SIZE); // Default is 256 chars seriallog_level = LOG_LEVEL_INFO; // Allow specific serial messages until config loaded snprintf_P(my_version, sizeof(my_version), PSTR("%d.%d.%d"), VERSION >> 24 & 0xff, VERSION >> 16 & 0xff, VERSION >> 8 & 0xff); // Release version 6.3.0 @@ -226,7 +243,7 @@ void setup(void) syslog_level = Settings.syslog_level; stop_flash_rotate = Settings.flag.stop_flash_rotate; // SetOption12 - Switch between dynamic or fixed slot flash save location save_data_counter = Settings.save_data; - sleep = Settings.sleep; + ssleep = Settings.sleep; #ifndef USE_EMULATION Settings.flag2.emulation = 0; #else @@ -253,14 +270,20 @@ void setup(void) Settings.rule_enabled = 0; // Disable all rules } if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +3) { // Restarted 5 times - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { Settings.my_gp.io[i] = GPIO_NONE; // Reset user defined GPIO disabling sensors } +#ifdef ESP8266 Settings.my_adc0 = ADC0_NONE; // Reset user defined ADC0 disabling sensors +#endif } if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +4) { // Restarted 6 times +#ifdef ESP8266 Settings.module = SONOFF_BASIC; // Reset module to Sonoff Basic // Settings.last_module = SONOFF_BASIC; +#else // ESP32 + Settings.module = WEMOS; // Reset module to Wemos +#endif // ESP8266 - ESP32 } AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_LOG_SOME_SETTINGS_RESET " (%d)"), RtcReboot.fast_reboot_count); } @@ -270,7 +293,7 @@ void setup(void) Format(mqtt_topic, SettingsText(SET_MQTT_TOPIC), sizeof(mqtt_topic)); if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); - snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME), mqtt_topic, ESP.getChipId() & 0x1FFF); + snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME), mqtt_topic, ESP_getChipId() & 0x1FFF); } else { snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME)); } @@ -284,7 +307,7 @@ void setup(void) SetPowerOnState(); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), PROJECT, SettingsText(SET_FRIENDLYNAME1), my_version, my_image); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_CORE_RELEASE), PROJECT, SettingsText(SET_FRIENDLYNAME1), my_version, my_image); #ifdef FIRMWARE_MINIMAL AddLog_P2(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION)); #endif // FIRMWARE_MINIMAL @@ -301,8 +324,7 @@ void setup(void) XsnsCall(FUNC_INIT); } -void BacklogLoop(void) -{ +void BacklogLoop(void) { if (TimeReached(backlog_delay)) { if (!BACKLOG_EMPTY && !backlog_mutex) { #ifdef SUPPORT_IF_STATEMENT @@ -321,8 +343,18 @@ void BacklogLoop(void) } } -void loop(void) -{ +void SleepDelay(uint32_t mseconds) { + if (mseconds) { + for (uint32_t wait = 0; wait < mseconds; wait++) { + delay(1); + if (Serial.available()) { break; } // We need to service serial buffer ASAP as otherwise we get uart buffer overrun + } + } else { + delay(0); + } +} + +void loop(void) { uint32_t my_sleep = millis(); XdrvCall(FUNC_LOOP); @@ -372,23 +404,23 @@ void loop(void) uint32_t my_activity = millis() - my_sleep; - if (Settings.flag3.sleep_normal) { // SetOption60 - Enable normal sleep instead of dynamic sleep - // yield(); // yield == delay(0), delay contains yield, auto yield in loop - delay(sleep); // https://github.com/esp8266/Arduino/issues/2021 + if (Settings.flag3.sleep_normal) { // SetOption60 - Enable normal sleep instead of dynamic sleep + // yield(); // yield == delay(0), delay contains yield, auto yield in loop + SleepDelay(ssleep); // https://github.com/esp8266/Arduino/issues/2021 } else { - if (my_activity < (uint32_t)sleep) { - delay((uint32_t)sleep - my_activity); // Provide time for background tasks like wifi + if (my_activity < (uint32_t)ssleep) { + SleepDelay((uint32_t)ssleep - my_activity); // Provide time for background tasks like wifi } else { if (global_state.wifi_down) { - delay(my_activity /2); // If wifi down and my_activity > setoption36 then force loop delay to 1/3 of my_activity period + SleepDelay(my_activity /2); // If wifi down and my_activity > setoption36 then force loop delay to 1/3 of my_activity period } } } - if (!my_activity) { my_activity++; } // We cannot divide by 0 - uint32_t loop_delay = sleep; - if (!loop_delay) { loop_delay++; } // We cannot divide by 0 - uint32_t loops_per_second = 1000 / loop_delay; // We need to keep track of this many loops per second + if (!my_activity) { my_activity++; } // We cannot divide by 0 + uint32_t loop_delay = ssleep; + if (!loop_delay) { loop_delay++; } // We cannot divide by 0 + uint32_t loops_per_second = 1000 / loop_delay; // We need to keep track of this many loops per second uint32_t this_cycle_ratio = 100 * my_activity / loop_delay; loop_load_avg = loop_load_avg - (loop_load_avg / loops_per_second) + (this_cycle_ratio / loops_per_second); // Take away one loop average away and add the new one } diff --git a/tasmota/tasmota_compat.h b/tasmota/tasmota_compat.h new file mode 100644 index 000000000..598ca5619 --- /dev/null +++ b/tasmota/tasmota_compat.h @@ -0,0 +1,18 @@ +#pragma once + +#ifdef ESP32 +#include +// Modul +#undef MODULE +#define MODULE WEMOS // [Module] Select default model +#endif // ESP32 + +#ifdef ESP8266 +// ESP8266 +// +// UDP +#define PortUdp_write(p,n) PortUdp.write(p, n) +// +// Serial minimal type to hold the config +#define SerConfu8 uint8_t +#endif // ESP8266 diff --git a/tasmota/tasmota_post.h b/tasmota/tasmota_configurations.h similarity index 88% rename from tasmota/tasmota_post.h rename to tasmota/tasmota_configurations.h index b9bc6a833..37ac20237 100644 --- a/tasmota/tasmota_post.h +++ b/tasmota/tasmota_configurations.h @@ -1,5 +1,5 @@ /* - tasmota_post.h - Post header file for Tasmota + tasmota_configurations.h - Configurations for Tasmota Copyright (C) 2020 Theo Arends @@ -17,72 +17,8 @@ along with this program. If not, see . */ -#ifndef _TASMOTA_POST_H_ -#define _TASMOTA_POST_H_ - -/*********************************************************************************************\ - * Function prototypes -\*********************************************************************************************/ - -// Needed for core 2.3.0 compilation (#6721) -#ifdef __cplusplus -extern "C" { -#endif -#include "user_interface.h" -#ifdef __cplusplus -} -#endif - -//#ifdef USE_KNX // Enabling this will fail compilation. It has no impact if not used. (20180417) -#include -void KNX_CB_Action(message_t const &msg, void *arg); -//#endif // USE_KNX - -void DomoticzTempHumPressureSensor(float temp, float hum, float baro = -1); -char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); -extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end); - -/*********************************************************************************************\ - * Default global defines -\*********************************************************************************************/ - -#ifndef ENERGY_OVERTEMP -#define ENERGY_OVERTEMP 90 // Overtemp in Celsius -#endif - -#ifdef USE_EMULATION_HUE -#define USE_EMULATION -#endif -#ifdef USE_EMULATION_WEMO -#define USE_EMULATION -#endif -#ifdef USE_DEVICE_GROUPS -#define USE_EMULATION -#endif - -#ifdef USE_MQTT_TLS - const uint16_t WEB_LOG_SIZE = 2000; // Max number of characters in weblog -#else - const uint16_t WEB_LOG_SIZE = 4000; // Max number of characters in weblog -#endif - -#if defined(USE_MQTT_TLS) && defined(ARDUINO_ESP8266_RELEASE_2_3_0) - #error "TLS is no more supported on Core 2.3.0, use 2.4.2 or higher." -#endif - -#ifndef MODULE -#define MODULE SONOFF_BASIC // [Module] Select default model -#endif - -#ifdef USE_PWM_DIMMER_REMOTE -#ifdef USE_PWM_DIMMER -#ifndef USE_DEVICE_GROUPS -#define USE_DEVICE_GROUPS -#endif // USE_DEVICE_GROUPS -#else // USE_PWM_DIMMER -#undef USE_PWM_DIMMER_REMOTE -#endif // USE_PWM_DIMMER -#endif // USE_PWM_DIMMER_REMOTE +#ifndef _TASMOTA_CONFIGURATIONS_H_ +#define _TASMOTA_CONFIGURATIONS_H_ /*********************************************************************************************\ * [tasmota-sensors.bin] @@ -91,8 +27,6 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #ifdef FIRMWARE_SENSORS -#undef CODE_IMAGE -#define CODE_IMAGE 2 #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "sensors" @@ -116,7 +50,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #define USE_DEEPSLEEP // Add support for deepsleep (+1k code) #undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer //#define USE_HOTPLUG // Add support for sensor HotPlug -#undef USE_DEVICE_GROUPS // Disable support for device groups (+5k6 code) +//#undef USE_DEVICE_GROUPS // Disable support for device groups (+5k6 code) #undef USE_PWM_DIMMER // Disable support for MJ-SD01/acenx/NTONPOWER PWM dimmers (+4k5 code) #undef USE_KEELOQ // Disable support for Jarolift rollers by Keeloq algorithm (+4k5 code) #undef USE_SONOFF_D1 // DIsable support for Sonoff D1 Dimmer (+0k7 code) @@ -136,6 +70,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller +#define USE_LIGHT_PALETTE // Add support for color palette (+0k9 code) #define USE_COUNTER // Enable counters #undef USE_ADC_VCC // Add Analog input on selected devices @@ -188,6 +123,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #define WEMOS_MOTOR_V1_ADDR 0x30 // Default I2C address 0x30 #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency //#define USE_HDC1080 // Enable HDC1080 temperature/humidity sensor +#define USE_IAQ // [I2cDriver46] Enable iAQ-core air quality sensor (I2C address 0x5a) (+0k6 code) +#define USE_AS3935 // [I2cDriver48] Enable AS3935 Franklin Lightning Sensor (I2C address 0x03) (+5k4 code) #define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code) #define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) @@ -206,11 +143,15 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max) //#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger #define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP #define USE_RDM6300 // Add support for RDM6300 125kHz RFID Reader (+0k8) #define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) //#define USE_GPS // Add support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM) -#define USE_HM10 // Add support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code) +#define USE_HM10 // (ESP8266 only) Add support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code) +//#define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) #define USE_HRXL // Add support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7) +//#define USE_TASMOTA_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +//#define USE_OPENTHERM // Add support for OpenTherm (+15k code) #define USE_ENERGY_SENSOR // Add energy sensors (-14k code) #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) @@ -230,21 +171,20 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram) -//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP - #define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) #define USE_TM1638 // Add support for TM1638 switches copying Switch1 .. Switch8 (+1k code) #define USE_HX711 // Add support for HX711 load cell (+1k5 code) //#define USE_HX711_GUI // Add optional web GUI to HX711 as scale (+1k8 code) //#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k6/0k8 code) //#define USE_TX23_WIND_SENSOR // Add support for La Crosse TX23 anemometer (+2k7/1k code) +//#define USE_WINDMETER // Add support for analog anemometer (+2k2 code) #define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) #define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) // #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 using 868MHz RF sensor receiver (+1k7 code) #define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) //#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) -//#define USE_ARDUINO_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +//#define USE_THERMOSTAT // Add support for Thermostat #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_SENSORS @@ -256,8 +196,6 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #ifdef FIRMWARE_KNX_NO_EMULATION -#undef CODE_IMAGE -#define CODE_IMAGE 3 #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "knx" @@ -281,8 +219,6 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #ifdef FIRMWARE_DISPLAYS -#undef CODE_IMAGE -#define CODE_IMAGE 5 #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "display" @@ -331,6 +267,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #define USE_DISPLAY_LCD // [DisplayModel 1] Enable Lcd display (I2C addresses 0x27 and 0x3F) (+6k code) #define USE_DISPLAY_SSD1306 // [DisplayModel 2] Enable SSD1306 Oled 128x64 display (I2C addresses 0x3C and 0x3D) (+16k code) #define USE_DISPLAY_MATRIX // [DisplayModel 3] Enable 8x8 Matrix display (I2C adresseses see below) (+11k code) + #define USE_DISPLAY_SEVENSEG // [DisplayModel 11] [I2cDriver47] Enable sevenseg display (I2C addresses 0x70 - 0x77) (<+11k code) #define USE_DISPLAY_SH1106 // [DisplayModel 7] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D) #define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC) @@ -354,8 +291,6 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #ifdef FIRMWARE_IR -#undef CODE_IMAGE -#define CODE_IMAGE 6 #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "ir" @@ -401,7 +336,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control -#undef USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller +#undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller +#undef USE_LIGHT_PALETTE // Disable support for color palette (+0k9 code) #undef USE_ENERGY_SENSOR // Disable energy sensors (-14k code) #undef USE_PZEM004T // Disable PZEM004T energy sensor @@ -431,11 +367,15 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +#undef USE_ZIGBEE // Disable serial communication with Zigbee CC2530 flashed with ZNP #undef USE_RDM6300 // Disable support for RDM6300 125kHz RFID Reader (+0k8) #undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) #undef USE_GPS // Disable support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM) -#undef USE_HM10 // Disable support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code) +#undef USE_HM10 // (ESP8266 only) Disable support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code) +#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) #undef USE_HRXL // Disable support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7) +#undef USE_TASMOTA_SLAVE // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +#undef USE_OPENTHERM // Disable support for OpenTherm (+15k code) //#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI @@ -445,11 +385,12 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #undef USE_HX711 // Disable support for HX711 load cell #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_TX23_WIND_SENSOR // Disable support for La Crosse TX23 anemometer +#undef USE_WINDMETER // Disable support for analog anemometer (+2k2 code) #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) #undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) #undef USE_A4988_STEPPER // Disable support for A4988_Stepper -#undef USE_ARDUINO_SLAVE // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +#undef USE_THERMOSTAT // Disable support for Thermostat #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_IR @@ -461,8 +402,6 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #ifdef FIRMWARE_LITE -#undef CODE_IMAGE -#define CODE_IMAGE 4 #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "lite" @@ -515,7 +454,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control -#undef USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller +#undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller +#undef USE_LIGHT_PALETTE // Disable support for color palette (+0k9 code) #undef USE_COUNTER // Disable counters #define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices @@ -532,11 +472,15 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +#undef USE_ZIGBEE // Disable serial communication with Zigbee CC2530 flashed with ZNP #undef USE_RDM6300 // Disable support for RDM6300 125kHz RFID Reader (+0k8) #undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) #undef USE_GPS // Disable support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM) -#undef USE_HM10 // Disable support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code) +#undef USE_HM10 // (ESP8266 only) Disable support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code) +#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) #undef USE_HRXL // Disable support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7) +#undef USE_TASMOTA_SLAVE // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +#undef USE_OPENTHERM // Disable support for OpenTherm (+15k code) //#undef USE_ENERGY_SENSOR // Disable energy sensors #undef USE_PZEM004T // Disable PZEM004T energy sensor @@ -555,21 +499,20 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #undef USE_MAX31865 // Disable support for MAX31865 RTD sensors using softSPI #undef USE_IR_REMOTE // Disable IR driver -#undef USE_ZIGBEE // Disable serial communication with Zigbee CC2530 flashed with ZNP - #undef USE_SR04 // Disable support for for HC-SR04 ultrasonic devices #undef USE_TM1638 // Disable support for TM1638 switches copying Switch1 .. Switch8 #undef USE_HX711 // Disable support for HX711 load cell #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_TX23_WIND_SENSOR // Disable support for La Crosse TX23 anemometer +#undef USE_WINDMETER // Disable support for analog anemometer (+2k2 code) #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) #undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) #undef USE_A4988_STEPPER // Disable support for A4988_Stepper -#undef USE_ARDUINO_SLAVE // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +#undef USE_THERMOSTAT // Disable support for Thermostat #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code -#endif // FIRMWARE_BASIC +#endif // FIRMWARE_LITE /*********************************************************************************************\ * [tasmota-minimal.bin] @@ -578,8 +521,6 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #ifdef FIRMWARE_MINIMAL -#undef CODE_IMAGE -#define CODE_IMAGE 1 #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "minimal" @@ -636,7 +577,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control -#undef USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller +#undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller +#undef USE_LIGHT_PALETTE // Disable support for color palette (+0k9 code) #undef USE_COUNTER // Disable counters #define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices @@ -655,11 +597,15 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +#undef USE_ZIGBEE // Disable serial communication with Zigbee CC2530 flashed with ZNP #undef USE_RDM6300 // Disable support for RDM6300 125kHz RFID Reader (+0k8) #undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) #undef USE_GPS // Disable support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM) -#undef USE_HM10 // Disable support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code) +#undef USE_HM10 // (ESP8266 only) Disable support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code) +#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) #undef USE_HRXL // Disable support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7) +#undef USE_TASMOTA_SLAVE // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +#undef USE_OPENTHERM // Disable support for OpenTherm (+15k code) #undef USE_ENERGY_SENSOR // Disable energy sensors #undef USE_PZEM004T // Disable PZEM004T energy sensor @@ -682,104 +628,18 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #undef USE_HX711 // Disable support for HX711 load cell #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_TX23_WIND_SENSOR // Disable support for La Crosse TX23 anemometer +#undef USE_WINDMETER // Disable support for analog anemometer (+2k2 code) #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) #undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) #undef USE_A4988_STEPPER // Disable support for A4988_Stepper -#undef USE_ARDUINO_SLAVE // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +#undef USE_THERMOSTAT // Disable support for Thermostat #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_MINIMAL -/*********************************************************************************************\ - * Mandatory defines satisfying possible disabled defines -\*********************************************************************************************/ +#ifdef ESP32 +#include "tasmota_configurations_ESP32.h" +#endif // ESP32 - // See https://github.com/esp8266/Arduino/pull/4889 -#undef NO_EXTRA_4K_HEAP // Allocate 4k heap for WPS in ESP8166/Arduino core v2.4.2 (was always allocated in previous versions) - -#ifndef USE_SONOFF_RF -#undef USE_RF_FLASH // Disable RF firmware flash when SOnoff Rf is disabled -#endif - -#ifndef SWITCH_MODE -#define SWITCH_MODE TOGGLE // TOGGLE, FOLLOW or FOLLOW_INV (the wall switch state) -#endif - -#ifndef STARTING_OFFSET // NOVA SDS parameter used in settings -#define STARTING_OFFSET 30 -#endif - -#ifndef MQTT_FINGERPRINT1 -// Set an all-zeros default fingerprint to activate auto-learning on first connection (AWS IoT) -#define MQTT_FINGERPRINT1 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" -#endif - -#ifndef MQTT_FINGERPRINT2 -#define MQTT_FINGERPRINT2 "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" -#endif - -#ifndef WS2812_LEDS -#define WS2812_LEDS 30 // [Pixels] Number of LEDs -#endif - -#ifndef MQTT_MAX_PACKET_SIZE -#define MQTT_MAX_PACKET_SIZE 1200 // Bytes -#endif -#ifndef MQTT_KEEPALIVE -#define MQTT_KEEPALIVE 30 // Seconds -#endif -#ifndef MQTT_TIMEOUT -#define MQTT_TIMEOUT 10000 // milli seconds -#endif -#ifndef MQTT_CLEAN_SESSION -#define MQTT_CLEAN_SESSION 1 // 0 = No clean session, 1 = Clean session (default) -#endif - -#ifndef MESSZ -//#define MESSZ 1040 // Max number of characters in JSON message string (Hass discovery and nice MQTT_MAX_PACKET_SIZE = 1200) -#define MESSZ (MQTT_MAX_PACKET_SIZE -TOPSZ -7) // Max number of characters in JSON message string -#endif - -//#include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) -#ifndef ARDUINO_ESP8266_RELEASE -#define ARDUINO_ESP8266_RELEASE "STAGE" -#endif - -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 // Disable not supported features in core 2.3.0 -#undef USE_MQTT_TLS_CA_CERT -#endif - -#ifdef USE_DEVICE_GROUPS -#define SendDeviceGroupMessage(DEVICE_INDEX, REQUEST_TYPE, ...) _SendDeviceGroupMessage(DEVICE_INDEX, REQUEST_TYPE, __VA_ARGS__, 0) -#define SendLocalDeviceGroupMessage(REQUEST_TYPE, ...) _SendDeviceGroupMessage(0, REQUEST_TYPE, __VA_ARGS__, 0) -#define DEVICE_GROUP_MESSAGE "M-TASMOTA_DGR/" -const char kDeviceGroupMessage[] PROGMEM = DEVICE_GROUP_MESSAGE; -uint8_t device_group_count = 1; -#endif // USE_DEVICE_GROUPS - -#ifdef DEBUG_TASMOTA_CORE -#define DEBUG_CORE_LOG(...) AddLog_Debug(__VA_ARGS__) -#else -#define DEBUG_CORE_LOG(...) -#endif - -#ifdef DEBUG_TASMOTA_DRIVER -#define DEBUG_DRIVER_LOG(...) AddLog_Debug(__VA_ARGS__) -#else -#define DEBUG_DRIVER_LOG(...) -#endif - -#ifdef DEBUG_TASMOTA_SENSOR -#define DEBUG_SENSOR_LOG(...) AddLog_Debug(__VA_ARGS__) -#else -#define DEBUG_SENSOR_LOG(...) -#endif - -#ifdef DEBUG_TASMOTA_TRACE -#define DEBUG_TRACE_LOG(...) AddLog_Debug(__VA_ARGS__) -#else -#define DEBUG_TRACE_LOG(...) -#endif - -#endif // _TASMOTA_POST_H_ +#endif // _TASMOTA_CONFIGURATIONS_H_ diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h new file mode 100644 index 000000000..b551c78ef --- /dev/null +++ b/tasmota/tasmota_configurations_ESP32.h @@ -0,0 +1,40 @@ +/* + tasmota_configurations_ESP32.h - ESP32 only Configurations for Tasmota + + Copyright (C) 2020 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _TASMOTA_CONFIGURATIONS_ESP32_H_ +#define _TASMOTA_CONFIGURATIONS_ESP32_H_ + +#ifdef ESP32 + +/*********************************************************************************************\ + * [tasmota32-webcam.bin] + * Provide an image with useful supported sensors enabled +\*********************************************************************************************/ + +#ifdef FIRMWARE_WEBCAM + +#undef CODE_IMAGE_STR +#define CODE_IMAGE_STR "webcam" + +#define USE_WEBCAM +#endif // FIRMWARE_WEBCAM + +#endif // ESP32 + +#endif // _TASMOTA_CONFIGURATIONS_ESP32_H_ diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h new file mode 100644 index 000000000..27a730ec5 --- /dev/null +++ b/tasmota/tasmota_globals.h @@ -0,0 +1,364 @@ +/* + tasmota_globals.h - Function prototypes and global configurations for Tasmota + + Copyright (C) 2020 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _TASMOTA_GLOBALS_H_ +#define _TASMOTA_GLOBALS_H_ + +/*********************************************************************************************\ + * Function prototypes +\*********************************************************************************************/ + +// Needed for core 2.3.0 compilation (#6721) +#ifdef __cplusplus +extern "C" { +#endif +#include "user_interface.h" +#ifdef __cplusplus +} +#endif + +//#ifdef USE_KNX // Enabling this will fail compilation. It has no impact if not used. (20180417) +#include +void KNX_CB_Action(message_t const &msg, void *arg); +//#endif // USE_KNX + +void DomoticzTempHumPressureSensor(float temp, float hum, float baro = -1); +char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); +extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end); +extern "C" void resetPins(); + +/*********************************************************************************************\ + * Preconfigured configurations +\*********************************************************************************************/ + +#include "tasmota_configurations.h" // Preconfigured configurations + +/*********************************************************************************************\ + * Mandatory defines satisfying disabled defines +\*********************************************************************************************/ + +#ifdef USE_EMULATION_HUE +#define USE_EMULATION +#endif +#ifdef USE_EMULATION_WEMO +#define USE_EMULATION +#endif + // See https://github.com/esp8266/Arduino/pull/4889 +#undef NO_EXTRA_4K_HEAP // Allocate 4k heap for WPS in ESP8166/Arduino core v2.4.2 (was always allocated in previous versions) + +#ifndef USE_SONOFF_RF +#undef USE_RF_FLASH // Disable RF firmware flash when Sonoff Rf is disabled +#endif + +#ifndef SWITCH_MODE +#define SWITCH_MODE TOGGLE // TOGGLE, FOLLOW or FOLLOW_INV (the wall switch state) +#endif + +#ifndef MQTT_FINGERPRINT1 +// Set an all-zeros default fingerprint to activate auto-learning on first connection (AWS IoT) +#define MQTT_FINGERPRINT1 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" +#endif +#ifndef MQTT_FINGERPRINT2 +#define MQTT_FINGERPRINT2 "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" +#endif + +#ifndef WS2812_LEDS +#define WS2812_LEDS 30 // [Pixels] Number of LEDs +#endif + +#ifdef USE_MQTT_TLS + const uint16_t WEB_LOG_SIZE = 2000; // Max number of characters in weblog +#else + const uint16_t WEB_LOG_SIZE = 4000; // Max number of characters in weblog +#endif + +#if defined(USE_MQTT_TLS) && defined(ARDUINO_ESP8266_RELEASE_2_3_0) + #error "TLS is no more supported on Core 2.3.0, use 2.4.2 or higher." +#endif + +#ifndef MQTT_MAX_PACKET_SIZE +#define MQTT_MAX_PACKET_SIZE 1200 // Bytes +#endif +#ifndef MQTT_KEEPALIVE +#define MQTT_KEEPALIVE 30 // Seconds +#endif +#ifndef MQTT_TIMEOUT +#define MQTT_TIMEOUT 10000 // milli seconds +#endif +#ifndef MQTT_CLEAN_SESSION +#define MQTT_CLEAN_SESSION 1 // 0 = No clean session, 1 = Clean session (default) +#endif + +#ifndef MESSZ +//#define MESSZ 1040 // Max number of characters in JSON message string (Hass discovery and nice MQTT_MAX_PACKET_SIZE = 1200) +#define MESSZ (MQTT_MAX_PACKET_SIZE -TOPSZ -7) // Max number of characters in JSON message string +#endif + +#ifndef USE_DEVICE_GROUPS +#undef USE_PWM_DIMMER_REMOTE +#undef USE_DGR_LIGHT_SEQUENCE +#endif // USE_DEVICE_GROUPS + +#ifndef DOMOTICZ_UPDATE_TIMER +#define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds) (Optional) +#endif + +#ifndef EMULATION +#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE) +#endif + +#ifndef MTX_ADDRESS1 // Add Display Support for up to eigth Matrices +#define MTX_ADDRESS1 0 +#endif +#ifndef MTX_ADDRESS2 +#define MTX_ADDRESS2 0 +#endif +#ifndef MTX_ADDRESS3 +#define MTX_ADDRESS3 0 +#endif +#ifndef MTX_ADDRESS4 +#define MTX_ADDRESS4 0 +#endif +#ifndef MTX_ADDRESS5 +#define MTX_ADDRESS5 0 +#endif +#ifndef MTX_ADDRESS6 +#define MTX_ADDRESS6 0 +#endif +#ifndef MTX_ADDRESS7 +#define MTX_ADDRESS7 0 +#endif +#ifndef MTX_ADDRESS8 +#define MTX_ADDRESS8 0 +#endif + +#ifndef HOME_ASSISTANT_DISCOVERY_ENABLE +#define HOME_ASSISTANT_DISCOVERY_ENABLE 0 +#endif + +#ifndef LATITUDE +#define LATITUDE 48.858360 // [Latitude] Your location to be used with sunrise and sunset +#endif +#ifndef LONGITUDE +#define LONGITUDE 2.294442 // [Longitude] Your location to be used with sunrise and sunset +#endif + +#ifndef IR_RCV_MIN_UNKNOWN_SIZE +#define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6, max 255) +#endif + +#ifndef ENERGY_OVERTEMP +#define ENERGY_OVERTEMP 90 // Overtemp in Celsius +#endif + +#ifndef DEFAULT_DIMMER_MAX +#define DEFAULT_DIMMER_MAX 100 +#endif +#ifndef DEFAULT_DIMMER_MIN +#define DEFAULT_DIMMER_MIN 0 +#endif +#ifndef DEFAULT_LIGHT_DIMMER +#define DEFAULT_LIGHT_DIMMER 10 +#endif +#ifndef DEFAULT_LIGHT_COMPONENT +#define DEFAULT_LIGHT_COMPONENT 255 +#endif + +#ifndef CORS_ENABLED_ALL +#define CORS_ENABLED_ALL "*" +#endif + +#ifndef WORKING_PERIOD +#define WORKING_PERIOD 5 // Working period of the SDS Sensor, Takes a reading every X Minutes +#endif +#ifndef STARTING_OFFSET +#define STARTING_OFFSET 30 // NOVA SDS parameter used in settings +#endif + +/*********************************************************************************************\ + * UserConfig related parameters +\*********************************************************************************************/ + +#ifndef COLOR_TEXT +#define COLOR_TEXT "#000" // Global text color - Black +#endif +#ifndef COLOR_BACKGROUND +#define COLOR_BACKGROUND "#fff" // Global background color - White +#endif +#ifndef COLOR_FORM +#define COLOR_FORM "#f2f2f2" // Form background color - Greyish +#endif +#ifndef COLOR_INPUT_TEXT +#define COLOR_INPUT_TEXT "#000" // Input text color - Black +#endif +#ifndef COLOR_INPUT +#define COLOR_INPUT "#fff" // Input background color - White +#endif +#ifndef COLOR_CONSOLE_TEXT +#define COLOR_CONSOLE_TEXT "#000" // Console text color - Black +#endif +#ifndef COLOR_CONSOLE +#define COLOR_CONSOLE "#fff" // Console background color - White +#endif +#ifndef COLOR_TEXT_WARNING +#define COLOR_TEXT_WARNING "#f00" // Warning text color - Red +#endif +#ifndef COLOR_TEXT_SUCCESS +#define COLOR_TEXT_SUCCESS "#008000" // Success text color - Green +#endif +#ifndef COLOR_BUTTON_TEXT +#define COLOR_BUTTON_TEXT "#fff" // Button text color - White +#endif +#ifndef COLOR_BUTTON +#define COLOR_BUTTON "#1fa3ec" // Button color - Blueish +#endif +#ifndef COLOR_BUTTON_HOVER +#define COLOR_BUTTON_HOVER "#0e70a4" // Button color when hovered over - Darker blueish +#endif +#ifndef COLOR_BUTTON_RESET +#define COLOR_BUTTON_RESET "#d43535" // Restart/Reset/Delete button color - Redish +#endif +#ifndef COLOR_BUTTON_RESET_HOVER +#define COLOR_BUTTON_RESET_HOVER "#931f1f" // Restart/Reset/Delete button color when hovered over - Darker redish +#endif +#ifndef COLOR_BUTTON_SAVE +#define COLOR_BUTTON_SAVE "#47c266" // Save button color - Greenish +#endif +#ifndef COLOR_BUTTON_SAVE_HOVER +#define COLOR_BUTTON_SAVE_HOVER "#5aaf6f" // Save button color when hovered over - Darker greenish +#endif +#ifndef COLOR_TIMER_TAB_TEXT +#define COLOR_TIMER_TAB_TEXT "#fff" // Config timer tab text color - White +#endif +#ifndef COLOR_TIMER_TAB_BACKGROUND +#define COLOR_TIMER_TAB_BACKGROUND "#999" // Config timer tab background color - Light grey +#endif +#ifndef COLOR_TITLE_TEXT +#define COLOR_TITLE_TEXT COLOR_TEXT // Title text color defaults to global text color either dark or light +#endif + +enum WebColors { + COL_TEXT, COL_BACKGROUND, COL_FORM, + COL_INPUT_TEXT, COL_INPUT, COL_CONSOLE_TEXT, COL_CONSOLE, + COL_TEXT_WARNING, COL_TEXT_SUCCESS, + COL_BUTTON_TEXT, COL_BUTTON, COL_BUTTON_HOVER, COL_BUTTON_RESET, COL_BUTTON_RESET_HOVER, COL_BUTTON_SAVE, COL_BUTTON_SAVE_HOVER, + COL_TIMER_TAB_TEXT, COL_TIMER_TAB_BACKGROUND, COL_TITLE, + COL_LAST }; + +const char kWebColors[] PROGMEM = + COLOR_TEXT "|" COLOR_BACKGROUND "|" COLOR_FORM "|" + COLOR_INPUT_TEXT "|" COLOR_INPUT "|" COLOR_CONSOLE_TEXT "|" COLOR_CONSOLE "|" + COLOR_TEXT_WARNING "|" COLOR_TEXT_SUCCESS "|" + COLOR_BUTTON_TEXT "|" COLOR_BUTTON "|" COLOR_BUTTON_HOVER "|" COLOR_BUTTON_RESET "|" COLOR_BUTTON_RESET_HOVER "|" COLOR_BUTTON_SAVE "|" COLOR_BUTTON_SAVE_HOVER "|" + COLOR_TIMER_TAB_TEXT "|" COLOR_TIMER_TAB_BACKGROUND "|" COLOR_TITLE_TEXT; + +/*********************************************************************************************\ + * ESP8266 vs ESP32 related parameters +\*********************************************************************************************/ + +#ifdef ESP8266 + +#ifndef MODULE +#define MODULE SONOFF_BASIC // [Module] Select default model +#endif + +#ifndef ARDUINO_ESP8266_RELEASE +#define ARDUINO_CORE_RELEASE "STAGE" +#else +#define ARDUINO_CORE_RELEASE ARDUINO_ESP8266_RELEASE +#endif // ARDUINO_ESP8266_RELEASE + +#endif // ESP8266 + +#ifdef ESP32 + +#ifndef MODULE +#define MODULE WEMOS // [Module] Select default model +#endif + +#ifndef ARDUINO_ESP32_RELEASE +#define ARDUINO_CORE_RELEASE "STAGE" +#else +#define ARDUINO_CORE_RELEASE ARDUINO_ESP32_RELEASE +#endif // ARDUINO_ESP32_RELEASE + +#undef USE_HM10 // Disable support for HM-10 as a BLE-bridge as an alternative is using the internal ESP32 BLE +#undef USE_KEELOQ // Disable support for Jarolift rollers by Keeloq algorithm as it's library cc1101 is not compatible with ESP32 +#undef USE_DISPLAY_ILI9488 // Disable as it's library JaretBurkett_ILI9488-gemu-1.0 is not compatible with ESP32 +#undef USE_DISPLAY_SSD1351 // Disable as it's library Adafruit_SSD1351_gemu-1.0 is not compatible with ESP32 + +#endif // ESP32 + +/*********************************************************************************************\ + * Macros +\*********************************************************************************************/ + +/* +// Removed from esp8266 core since 20171105 +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) +*/ +#define tmin(a,b) ((a)<(b)?(a):(b)) +#define tmax(a,b) ((a)>(b)?(a):(b)) + +#define STR_HELPER(x) #x +#ifndef STR +#define STR(x) STR_HELPER(x) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#ifdef ESP8266 +#define AGPIO(x) (x) +#else // ESP32 +#define AGPIO(x) (x<<5) +#endif // ESP8266 - ESP32 + +#ifdef USE_DEVICE_GROUPS +#define SendDeviceGroupMessage(DEVICE_INDEX, REQUEST_TYPE, ...) _SendDeviceGroupMessage(DEVICE_INDEX, REQUEST_TYPE, __VA_ARGS__, 0) +#define SendLocalDeviceGroupMessage(REQUEST_TYPE, ...) _SendDeviceGroupMessage(0, REQUEST_TYPE, __VA_ARGS__, 0) +uint8_t device_group_count = 1; +#endif // USE_DEVICE_GROUPS + +#ifdef DEBUG_TASMOTA_CORE +#define DEBUG_CORE_LOG(...) AddLog_Debug(__VA_ARGS__) +#else +#define DEBUG_CORE_LOG(...) +#endif +#ifdef DEBUG_TASMOTA_DRIVER +#define DEBUG_DRIVER_LOG(...) AddLog_Debug(__VA_ARGS__) +#else +#define DEBUG_DRIVER_LOG(...) +#endif +#ifdef DEBUG_TASMOTA_SENSOR +#define DEBUG_SENSOR_LOG(...) AddLog_Debug(__VA_ARGS__) +#else +#define DEBUG_SENSOR_LOG(...) +#endif +#ifdef DEBUG_TASMOTA_TRACE +#define DEBUG_TRACE_LOG(...) AddLog_Debug(__VA_ARGS__) +#else +#define DEBUG_TRACE_LOG(...) +#endif + +/*********************************************************************************************/ + +#endif // _TASMOTA_GLOBALS_H_ diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index 4715f682f..b3d3b48ba 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -20,6 +20,8 @@ #ifndef _TASMOTA_TEMPLATE_H_ #define _TASMOTA_TEMPLATE_H_ +#ifdef ESP8266 + // User selectable GPIO functionality // ATTENTION: Only add at the end of this list just before GPIO_SENSOR_END // Then add the same name(s) in a nice location in array kGpioNiceList @@ -93,7 +95,7 @@ enum UserSelectablePins { GPIO_SPI_CS, // SPI Chip Select GPIO_SPI_DC, // SPI Data Direction GPIO_BACKLIGHT, // Display backlight control - GPIO_PMS5003, // Plantower PMS5003 Serial interface + GPIO_PMS5003_RX, // Plantower PMS5003 Serial interface GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface GPIO_SBR_TX, // Serial Bridge Serial interface GPIO_SBR_RX, // Serial Bridge Serial interface @@ -226,6 +228,11 @@ enum UserSelectablePins { GPIO_CC1101_GDO2, // CC1101 pin for RX GPIO_HRXL_RX, // Data from MaxBotix HRXL sonar range sensor GPIO_ELECTRIQ_MOODL_TX, // ElectriQ iQ-wifiMOODL Serial TX + GPIO_AS3935, + GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface + GPIO_BOILER_OT_RX, // OpenTherm Boiler RX pin + GPIO_BOILER_OT_TX, // OpenTherm Boiler TX pin + GPIO_WINDMETER_SPEED, // WindMeter speed counter pin GPIO_SENSOR_END }; // Programmer selectable GPIO functionality @@ -259,7 +266,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_PZEM0XX_TX "|" D_SENSOR_PZEM004_RX "|" D_SENSOR_SAIR_TX "|" D_SENSOR_SAIR_RX "|" D_SENSOR_SPI_CS "|" D_SENSOR_SPI_DC "|" D_SENSOR_BACKLIGHT "|" - D_SENSOR_PMS5003 "|" D_SENSOR_SDS0X1_RX "|" + D_SENSOR_PMS5003_RX "|" D_SENSOR_SDS0X1_RX "|" D_SENSOR_SBR_TX "|" D_SENSOR_SBR_RX "|" D_SENSOR_SR04_TRIG "|" D_SENSOR_SR04_ECHO "|" D_SENSOR_SDM120_TX "|" D_SENSOR_SDM120_RX "|" @@ -312,164 +319,16 @@ const char kSensorNames[] PROGMEM = D_SENSOR_LE01MR_RX "|" D_SENSOR_LE01MR_TX "|" D_SENSOR_CC1101_GDO0 "|" D_SENSOR_CC1101_GDO2 "|" D_SENSOR_HRXL_RX "|" - D_SENSOR_ELECTRIQ_MOODL + D_SENSOR_ELECTRIQ_MOODL "|" + D_SENSOR_AS3935 "|" D_SENSOR_PMS5003_TX "|" + D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX "|" + D_SENSOR_WINDMETER_SPEED ; const char kSensorNamesFixed[] PROGMEM = D_SENSOR_SPI_MISO "|" D_SENSOR_SPI_MOSI "|" D_SENSOR_SPI_CLK "|" D_SENSOR_USER; -// User selectable ADC0 functionality -enum UserSelectableAdc0 { - ADC0_NONE, // Not used - ADC0_INPUT, // Analog input - ADC0_TEMP, // Thermistor - ADC0_LIGHT, // Light sensor - ADC0_BUTTON, // Button - ADC0_BUTTON_INV, - ADC0_RANGE, // Range - ADC0_CT_POWER, // Current -// ADC0_SWITCH, // Switch -// ADC0_SWITCH_INV, - ADC0_END }; - -// Programmer selectable ADC0 functionality -enum ProgramSelectableAdc0 { - ADC0_FIX_START = 14, - ADC0_USER, // User configurable needs to be 15 - ADC0_MAX }; - -// Text in webpage Module Parameters and commands ADC -const char kAdc0Names[] PROGMEM = - D_SENSOR_NONE "|" D_ANALOG_INPUT "|" - D_TEMPERATURE "|" D_LIGHT "|" - D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|" - D_RANGE "|" - D_CT_POWER "|" -// D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "i|" - ; - -/********************************************************************************************/ - -// Supported hardware modules -enum SupportedModules { - SONOFF_BASIC, - SONOFF_RF, - SONOFF_SV, - SONOFF_TH, - SONOFF_DUAL, - SONOFF_POW, - SONOFF_4CH, - SONOFF_S2X, - SLAMPHER, - SONOFF_TOUCH, - SONOFF_LED, - CH1, - CH4, - MOTOR, - ELECTRODRAGON, - EXS_RELAY, - WION, - WEMOS, - SONOFF_DEV, - H801, - SONOFF_SC, - SONOFF_BN, - SONOFF_4CHPRO, - HUAFAN_SS, - SONOFF_BRIDGE, - SONOFF_B1, - AILIGHT, - SONOFF_T11, - SONOFF_T12, - SONOFF_T13, - SUPLA1, - WITTY, - YUNSHAN, - MAGICHOME, - LUANIHVIO, - KMC_70011, - ARILUX_LC01, - ARILUX_LC11, - SONOFF_DUAL_R2, - ARILUX_LC06, - SONOFF_S31, - ZENGGE_ZF_WF017, - SONOFF_POW_R2, - SONOFF_IFAN02, - BLITZWOLF_BWSHP, - SHELLY1, - SHELLY2, - PHILIPS, - NEO_COOLCAM, - ESP_SWITCH, - OBI, - TECKIN, - APLIC_WDP303075, - TUYA_DIMMER, - GOSUND, - ARMTRONIX_DIMMERS, - SK03_TUYA, - PS_16_DZ, - TECKIN_US, - MANZOKU_EU_4, - OBI2, - YTF_IR_BRIDGE, - DIGOO, - KA10, - ZX2820, - MI_DESK_LAMP, - SP10, - WAGA, - SYF05, - SONOFF_L1, - SONOFF_IFAN03, - EXS_DIMMER, - PWM_DIMMER, - SONOFF_D1, - MAXMODULE}; - -#define USER_MODULE 255 - -/********************************************************************************************/ - -#define MAX_GPIO_PIN 17 // Number of supported GPIO -#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) - -const char PINS_WEMOS[] PROGMEM = "D3TXD4RXD2D1flashcFLFLolD6D7D5D8D0A0"; - -typedef struct MYIO { - uint8_t io[MAX_GPIO_PIN]; -} myio; - -typedef struct MYCFGIO { - uint8_t io[MAX_GPIO_PIN - MIN_FLASH_PINS]; -} mycfgio; - -#define GPIO_FLAG_USED 0 // Currently two flags used - -#define GPIO_FLAG_SPARE04 16 -#define GPIO_FLAG_SPARE05 32 -#define GPIO_FLAG_SPARE06 64 -#define GPIO_FLAG_SPARE07 128 - -typedef union { - uint8_t data; - struct { - uint8_t adc0 : 4; // Allow ADC0 when define USE_ADC_VCC is disabled - uint8_t spare04 : 1; - uint8_t spare05 : 1; - uint8_t spare06 : 1; - uint8_t spare07 : 1; - }; -} gpio_flag; - -typedef struct MYTMPLT { - char name[15]; - mycfgio gp; - gpio_flag flag; -} mytmplt; - const uint8_t kGpioNiceList[] PROGMEM = { GPIO_NONE, // Not used GPIO_KEY1, // Buttons @@ -727,15 +586,19 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface #endif #ifdef USE_HPMA - GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface - GPIO_HPMA_RX, // Honeywell HPMA115S0 Serial interface + GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface + GPIO_HPMA_RX, // Honeywell HPMA115S0 Serial interface #endif #ifdef USE_PMS5003 - GPIO_PMS5003, // Plantower PMS5003 Serial interface + GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface + GPIO_PMS5003_RX, // Plantower PMS5003 Serial interface #endif #if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin #endif +#ifdef USE_WINDMETER + GPIO_WINDMETER_SPEED, +#endif #ifdef USE_MP3_PLAYER GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface #endif @@ -757,16 +620,20 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_RDM6300_RX, #endif #ifdef USE_IBEACON - GPIO_IBEACON_RX, GPIO_IBEACON_TX, + GPIO_IBEACON_RX, #endif #ifdef USE_GPS - GPIO_GPS_RX, // GPS serial interface GPIO_GPS_TX, // GPS serial interface + GPIO_GPS_RX, // GPS serial interface #endif #ifdef USE_HM10 - GPIO_HM10_RX, // GPS serial interface GPIO_HM10_TX, // GPS serial interface + GPIO_HM10_RX, // GPS serial interface +#endif +#ifdef USE_OPENTHERM + GPIO_BOILER_OT_TX, + GPIO_BOILER_OT_RX, #endif #ifdef USE_MGC3130 @@ -807,8 +674,113 @@ const uint8_t kGpioNiceList[] PROGMEM = { #ifdef USE_HRXL GPIO_HRXL_RX, #endif +#ifdef USE_AS3935 + GPIO_AS3935, +#endif }; +/********************************************************************************************/ + +// User selectable ADC0 functionality +enum UserSelectableAdc0 { + ADC0_NONE, // Not used + ADC0_INPUT, // Analog input + ADC0_TEMP, // Thermistor + ADC0_LIGHT, // Light sensor + ADC0_BUTTON, // Button + ADC0_BUTTON_INV, + ADC0_RANGE, // Range + ADC0_CT_POWER, // Current +// ADC0_SWITCH, // Switch +// ADC0_SWITCH_INV, + ADC0_END }; + +// Programmer selectable ADC0 functionality +enum ProgramSelectableAdc0 { + ADC0_FIX_START = 14, + ADC0_USER, // User configurable needs to be 15 + ADC0_MAX }; + +// Text in webpage Module Parameters and commands ADC +const char kAdc0Names[] PROGMEM = + D_SENSOR_NONE "|" D_ANALOG_INPUT "|" + D_TEMPERATURE "|" D_LIGHT "|" + D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|" + D_RANGE "|" + D_CT_POWER "|" +// D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "i|" + ; + +/********************************************************************************************/ + +#define MAX_GPIO_PIN 17 // Number of supported GPIO +#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) +#define MAX_USER_PINS 13 // MAX_GPIO_PIN - MIN_FLASH_PINS +#define ADC0_PIN 17 // Pin number of ADC0 +#define WEMOS_MODULE 17 // Wemos module + +const char PINS_WEMOS[] PROGMEM = "D3TXD4RXD2D1flashcFLFLolD6D7D5D8D0A0"; + +/********************************************************************************************/ + +typedef struct MYIO { + uint8_t io[MAX_GPIO_PIN]; +} myio; + +typedef struct MYCFGIO { + uint8_t io[MAX_GPIO_PIN - MIN_FLASH_PINS]; +} mycfgio; + +#define GPIO_FLAG_USED 0 // Currently two flags used + +#define GPIO_FLAG_SPARE04 16 +#define GPIO_FLAG_SPARE05 32 +#define GPIO_FLAG_SPARE06 64 +#define GPIO_FLAG_SPARE07 128 + +typedef union { + uint8_t data; + struct { + uint8_t adc0 : 4; // Allow ADC0 when define USE_ADC_VCC is disabled + uint8_t spare04 : 1; + uint8_t spare05 : 1; + uint8_t spare06 : 1; + uint8_t spare07 : 1; + }; +} gpio_flag; + +typedef struct MYTMPLT { + mycfgio gp; + gpio_flag flag; +} mytmplt; + +/********************************************************************************************/ +// Supported hardware modules + +enum SupportedModules { + SONOFF_BASIC, SONOFF_RF, SONOFF_SV, SONOFF_TH, SONOFF_DUAL, SONOFF_POW, SONOFF_4CH, SONOFF_S2X, SLAMPHER, SONOFF_TOUCH, + SONOFF_LED, CH1, CH4, MOTOR, ELECTRODRAGON, EXS_RELAY, WION, WEMOS, SONOFF_DEV, H801, + SONOFF_SC, SONOFF_BN, SONOFF_4CHPRO, HUAFAN_SS, SONOFF_BRIDGE, SONOFF_B1, AILIGHT, SONOFF_T11, SONOFF_T12, SONOFF_T13, + SUPLA1, WITTY, YUNSHAN, MAGICHOME, LUANIHVIO, KMC_70011, ARILUX_LC01, ARILUX_LC11, SONOFF_DUAL_R2, ARILUX_LC06, + SONOFF_S31, ZENGGE_ZF_WF017, SONOFF_POW_R2, SONOFF_IFAN02, BLITZWOLF_BWSHP, SHELLY1, SHELLY2, PHILIPS, NEO_COOLCAM, ESP_SWITCH, + OBI, TECKIN, APLIC_WDP303075, TUYA_DIMMER, GOSUND, ARMTRONIX_DIMMERS, SK03_TUYA, PS_16_DZ, TECKIN_US, MANZOKU_EU_4, + OBI2, YTF_IR_BRIDGE, DIGOO, KA10, ZX2820, MI_DESK_LAMP, SP10, WAGA, SYF05, SONOFF_L1, + SONOFF_IFAN03, EXS_DIMMER, PWM_DIMMER, SONOFF_D1, + MAXMODULE}; + +#define USER_MODULE 255 + +const char kModuleNames[] PROGMEM = + "Sonoff Basic|Sonoff RF|Sonoff SV|Sonoff TH|Sonoff Dual|Sonoff Pow|Sonoff 4CH|Sonoff S2X|Slampher|Sonoff Touch|" + "Sonoff LED|1 Channel|4 Channel|Motor C/AC|ElectroDragon|EXS Relay(s)|WiOn|Generic|Sonoff Dev|H801|" + "Sonoff SC|Sonoff BN-SZ|Sonoff 4CH Pro|Huafan SS|Sonoff Bridge|Sonoff B1|AiLight|Sonoff T1 1CH|Sonoff T1 2CH|Sonoff T1 3CH|" + "Supla Espablo|Witty Cloud|Yunshan Relay|MagicHome|Luani HVIO|KMC 70011|Arilux LC01|Arilux LC11|Sonoff Dual R2|Arilux LC06|" + "Sonoff S31|Zengge WF017|Sonoff Pow R2|Sonoff iFan02|BlitzWolf SHP|Shelly 1|Shelly 2|Xiaomi Philips|Neo Coolcam|ESP Switch|" + "OBI Socket|Teckin|AplicWDP303075|Tuya MCU|Gosund SP1 v23|ARMTR Dimmer|SK03 Outdoor|PS-16-DZ|Teckin US|Manzoku strip|" + "OBI Socket 2|YTF IR Bridge|Digoo DG-SP202|KA10|Luminea ZX2820|Mi Desk Lamp|SP10|WAGA CHCZ02MB|SYF05|Sonoff L1|" + "Sonoff iFan03|EXS Dimmer|PWM Dimmer|Sonoff D1" + ; + const uint8_t kModuleNiceList[] PROGMEM = { SONOFF_BASIC, // Sonoff Relay Devices SONOFF_RF, @@ -910,1369 +882,1375 @@ const uint8_t kModuleNiceList[] PROGMEM = { // Default module settings const mytmplt kModules[MAXMODULE] PROGMEM = { - { "Sonoff Basic", // SONOFF_BASIC - Sonoff Basic (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Only available on newer Sonoff Basic R2 V1 - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - 0, // GPIO05 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, // GPIO15 - 0, // GPIO16 - 0 // ADC0 Analog input + { // SONOFF_BASIC - Sonoff Basic (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Only available on newer Sonoff Basic R2 V1 + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, // GPIO05 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, // GPIO15 + 0, // GPIO16 + 0 // ADC0 Analog input }, - { "Sonoff RF", // SONOFF_RF - Sonoff RF (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional sensor - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, 0, 0 + { // SONOFF_RF - Sonoff RF (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional sensor + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, 0 }, - { "Sonoff SV", // SONOFF_SV - Sonoff SV (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - GPIO_USER, // GPIO05 Optional sensor - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, 0, - ADC0_USER // ADC0 Analog input + { // SONOFF_SV - Sonoff SV (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + GPIO_USER, // GPIO05 Optional sensor + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, + ADC0_USER // ADC0 Analog input }, - { "Sonoff TH", // SONOFF_TH - Sonoff TH10/16 (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, 0, 0 + { // SONOFF_TH - Sonoff TH10/16 (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, 0 }, - { "Sonoff Dual", // SONOFF_DUAL - Sonoff Dual (ESP8266) - 0, - GPIO_TXD, // GPIO01 Relay control - 0, - GPIO_RXD, // GPIO03 Relay control - GPIO_USER, // GPIO04 Optional sensor - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, 0, 0 + { // SONOFF_DUAL - Sonoff Dual (ESP8266) + 0, + GPIO_TXD, // GPIO01 Relay control + 0, + GPIO_RXD, // GPIO03 Relay control + GPIO_USER, // GPIO04 Optional sensor + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, 0 }, - { "Sonoff Pow", // SONOFF_POW - Sonoff Pow (ESP8266 - HLW8012) - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, 0, - GPIO_NRG_SEL, // GPIO05 HLW8012 Sel output (1 = Voltage) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_NRG_CF1, // GPIO13 HLW8012 CF1 voltage / current - GPIO_HLW_CF, // GPIO14 HLW8012 CF power - GPIO_LED1, // GPIO15 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0 + { // SONOFF_POW - Sonoff Pow (ESP8266 - HLW8012) + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, + GPIO_NRG_SEL, // GPIO05 HLW8012 Sel output (1 = Voltage) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_NRG_CF1, // GPIO13 HLW8012 CF1 voltage / current + GPIO_HLW_CF, // GPIO14 HLW8012 CF power + GPIO_LED1, // GPIO15 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0 }, - { "Sonoff 4CH", // SONOFF_4CH - Sonoff 4CH (ESP8285) - GPIO_KEY1, // GPIO00 Button 1 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional sensor - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On) - GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_KEY2, // GPIO09 Button 2 - GPIO_KEY3, // GPIO10 Button 3 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) - Link and Power status - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - GPIO_KEY4, // GPIO14 Button 4 - GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) - 0, 0 + { // SONOFF_4CH - Sonoff 4CH (ESP8285) + GPIO_KEY1, // GPIO00 Button 1 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional sensor + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On) + GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_KEY2, // GPIO09 Button 2 + GPIO_KEY3, // GPIO10 Button 3 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) - Link and Power status + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_KEY4, // GPIO14 Button 4 + GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) + 0, 0 }, - { "Sonoff S2X", // SONOFF_S2X - Sonoff S20, S22 and S26 Smart Socket (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional sensor - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - Link and Power status - GPIO_LED1_INV, // GPIO13 Green/Blue Led (0 = On, 1 = Off) - 0, 0, 0, 0 + { // SONOFF_S2X - Sonoff S20, S22 and S26 Smart Socket (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional sensor + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - Link and Power status + GPIO_LED1_INV, // GPIO13 Green/Blue Led (0 = On, 1 = Off) + 0, 0, 0, 0 }, - { "Slampher", // SLAMPHER - Slampher (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SLAMPHER - Slampher (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff Touch", // SONOFF_TOUCH - Sonoff Touch (ESP8285) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - Link and Power status - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - 0, 0, 0, 0 + { // SONOFF_TOUCH - Sonoff Touch (ESP8285) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - Link and Power status + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + 0, 0, 0, 0 }, - { "Sonoff LED", // SONOFF_LED - Sonoff LED (ESP8266) - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, - GPIO_USER, // GPIO04 Optional sensor (PWM3 Green) - GPIO_USER, // GPIO05 Optional sensor (PWM2 Red) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM1, // GPIO12 Cold light (PWM0 Cold) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - GPIO_PWM2, // GPIO14 Warm light (PWM1 Warm) - GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue) - 0, 0 + { // SONOFF_LED - Sonoff LED (ESP8266) + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, + GPIO_USER, // GPIO04 Optional sensor (PWM3 Green) + GPIO_USER, // GPIO05 Optional sensor (PWM2 Red) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM1, // GPIO12 Cold light (PWM0 Cold) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + GPIO_PWM2, // GPIO14 Warm light (PWM1 Warm) + GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue) + 0, 0 }, - { "1 Channel", // CH1 - 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285) - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // CH1 - 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285) + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "4 Channel", // CH4 - 4 Channel Inching/Latching Relays (ESP8266) - 0, - GPIO_TXD, // GPIO01 Relay control - 0, - GPIO_RXD, // GPIO03 Relay control - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // CH4 - 4 Channel Inching/Latching Relays (ESP8266) + 0, + GPIO_TXD, // GPIO01 Relay control + 0, + GPIO_RXD, // GPIO03 Relay control + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Motor C/AC", // MOTOR - Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266) - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // MOTOR - Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266) + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "ElectroDragon", // ELECTRODRAGON - ElectroDragon IoT Relay Board (ESP8266) - GPIO_KEY2, // GPIO00 Button 2 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_KEY1, // GPIO02 Button 1 - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - GPIO_USER, // GPIO05 Optional sensor - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL2, // GPIO12 Red Led and Relay 2 (0 = Off, 1 = On) - GPIO_REL1, // GPIO13 Red Led and Relay 1 (0 = Off, 1 = On) - GPIO_USER, // GPIO14 Optional sensor - GPIO_USER, // GPIO15 Optional sensor - GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status - ADC0_USER // ADC0 A0 Analog input + { // ELECTRODRAGON - ElectroDragon IoT Relay Board (ESP8266) + GPIO_KEY2, // GPIO00 Button 2 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_KEY1, // GPIO02 Button 1 + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + GPIO_USER, // GPIO05 Optional sensor + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL2, // GPIO12 Red Led and Relay 2 (0 = Off, 1 = On) + GPIO_REL1, // GPIO13 Red Led and Relay 1 (0 = Off, 1 = On) + GPIO_USER, // GPIO14 Optional sensor + GPIO_USER, // GPIO15 Optional sensor + GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status + ADC0_USER // ADC0 A0 Analog input }, - { "EXS Relay(s)", // EXS_RELAY - ES-Store Latching relay(s) (ESP8266) - // https://ex-store.de/ESP8266-WiFi-Relay-V31 - // V3.1 Module Pin 1 VCC 3V3, Module Pin 6 GND - // https://ex-store.de/2-Kanal-WiFi-WLan-Relay-V5-Blackline-fuer-Unterputzmontage - GPIO_USER, // GPIO00 V3.1 Module Pin 8 - V5.0 Module Pin 4 - GPIO_USER, // GPIO01 UART0_TXD V3.1 Module Pin 2 - V5.0 Module Pin 3 - GPIO_USER, // GPIO02 V3.1 Module Pin 7 - GPIO_USER, // GPIO03 UART0_RXD V3.1 Module Pin 3 - GPIO_USER, // GPIO04 V3.1 Module Pin 10 - V5.0 Module Pin 2 - GPIO_USER, // GPIO05 V3.1 Module Pin 9 - V5.0 Module Pin 1 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Relay1 ( 1 = Off) - GPIO_REL2, // GPIO13 Relay1 ( 1 = On) - GPIO_USER, // GPIO14 V3.1 Module Pin 5 - V5.0 GPIO_REL3_INV Relay2 ( 1 = Off) - GPIO_LED1, // GPIO15 V5.0 LED1 - Link and Power status - GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On) - 0 + { // EXS_RELAY - ES-Store Latching relay(s) (ESP8266) + // https://ex-store.de/ESP8266-WiFi-Relay-V31 + // V3.1 Module Pin 1 VCC 3V3, Module Pin 6 GND + // https://ex-store.de/2-Kanal-WiFi-WLan-Relay-V5-Blackline-fuer-Unterputzmontage + GPIO_USER, // GPIO00 V3.1 Module Pin 8 - V5.0 Module Pin 4 + GPIO_USER, // GPIO01 UART0_TXD V3.1 Module Pin 2 - V5.0 Module Pin 3 + GPIO_USER, // GPIO02 V3.1 Module Pin 7 + GPIO_USER, // GPIO03 UART0_RXD V3.1 Module Pin 3 + GPIO_USER, // GPIO04 V3.1 Module Pin 10 - V5.0 Module Pin 2 + GPIO_USER, // GPIO05 V3.1 Module Pin 9 - V5.0 Module Pin 1 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Relay1 ( 1 = Off) + GPIO_REL2, // GPIO13 Relay1 ( 1 = On) + GPIO_USER, // GPIO14 V3.1 Module Pin 5 - V5.0 GPIO_REL3_INV Relay2 ( 1 = Off) + GPIO_LED1, // GPIO15 V5.0 LED1 - Link and Power status + GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On) + 0 }, - { "WiOn", // WION - Indoor Tap (ESP8266) - // https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w - GPIO_USER, // GPIO00 Optional sensor (pm clock) - 0, - GPIO_LED1, // GPIO02 Green Led (1 = On, 0 = Off) - Link and Power status - 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 Optional sensor (pm data) - GPIO_KEY1, // GPIO13 Button - 0, - GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) - 0, 0 + { // WION - Indoor Tap (ESP8266) + // https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w + GPIO_USER, // GPIO00 Optional sensor (pm clock) + 0, + GPIO_LED1, // GPIO02 Green Led (1 = On, 0 = Off) - Link and Power status + 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 Optional sensor (pm data) + GPIO_KEY1, // GPIO13 Button + 0, + GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) + 0, 0 }, - { "Generic", // WEMOS - Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266) - GPIO_USER, // GPIO00 D3 Wemos Button Shield - GPIO_USER, // GPIO01 TX Serial RXD - GPIO_USER, // GPIO02 D4 Wemos DHT Shield - GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor - GPIO_USER, // GPIO04 D2 Wemos I2C SDA - GPIO_USER, // GPIO05 D1 Wemos I2C SCL / Wemos Relay Shield (0 = Off, 1 = On) / Wemos WS2812B RGB led Shield - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_USER, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - GPIO_USER, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 D6 - GPIO_USER, // GPIO13 D7 - GPIO_USER, // GPIO14 D5 - GPIO_USER, // GPIO15 D8 - GPIO_USER, // GPIO16 D0 Wemos Wake - ADC0_USER // ADC0 A0 Analog input + { // WEMOS - Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266) + GPIO_USER, // GPIO00 D3 Wemos Button Shield + GPIO_USER, // GPIO01 TX Serial RXD + GPIO_USER, // GPIO02 D4 Wemos DHT Shield + GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor + GPIO_USER, // GPIO04 D2 Wemos I2C SDA + GPIO_USER, // GPIO05 D1 Wemos I2C SCL / Wemos Relay Shield (0 = Off, 1 = On) / Wemos WS2812B RGB led Shield + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_USER, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + GPIO_USER, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 D6 + GPIO_USER, // GPIO13 D7 + GPIO_USER, // GPIO14 D5 + GPIO_USER, // GPIO15 D8 + GPIO_USER, // GPIO16 D0 Wemos Wake + ADC0_USER // ADC0 A0 Analog input }, - { "Sonoff Dev", // SONOFF_DEV - Sonoff Dev (ESP8266) - GPIO_KEY1, // GPIO00 E-FW Button - GPIO_USER, // GPIO01 TX Serial RXD and Optional sensor - 0, // GPIO02 - GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - GPIO_USER, // GPIO05 Optional sensor - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 - GPIO_USER, // GPIO13 BLUE LED - GPIO_USER, // GPIO14 Optional sensor - 0, // GPIO15 - 0, // GPIO16 - ADC0_USER // ADC0 A0 Analog input + { // SONOFF_DEV - Sonoff Dev (ESP8266) + GPIO_KEY1, // GPIO00 E-FW Button + GPIO_USER, // GPIO01 TX Serial RXD and Optional sensor + 0, // GPIO02 + GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + GPIO_USER, // GPIO05 Optional sensor + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 + GPIO_USER, // GPIO13 BLUE LED + GPIO_USER, // GPIO14 Optional sensor + 0, // GPIO15 + 0, // GPIO16 + ADC0_USER // ADC0 A0 Analog input }, - { "H801", // H801 - Lixada H801 Wifi (ESP8266) - GPIO_USER, // GPIO00 E-FW Button - GPIO_LED1, // GPIO01 Green LED - Link and Power status - GPIO_USER, // GPIO02 TX and Optional sensor - Pin next to TX on the PCB - GPIO_USER, // GPIO03 RX and Optional sensor - Pin next to GND on the PCB - GPIO_PWM5, // GPIO04 W2 - PWM5 - GPIO_LED2_INV, // GPIO05 Red LED - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM3, // GPIO12 Blue - GPIO_PWM2, // GPIO13 Green - GPIO_PWM4, // GPIO14 W1 - PWM4 - GPIO_PWM1, // GPIO15 Red - 0, 0 + { // H801 - Lixada H801 Wifi (ESP8266) + GPIO_USER, // GPIO00 E-FW Button + GPIO_LED1, // GPIO01 Green LED - Link and Power status + GPIO_USER, // GPIO02 TX and Optional sensor - Pin next to TX on the PCB + GPIO_USER, // GPIO03 RX and Optional sensor - Pin next to GND on the PCB + GPIO_PWM5, // GPIO04 W2 - PWM5 + GPIO_LED2_INV, // GPIO05 Red LED + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM3, // GPIO12 Blue + GPIO_PWM2, // GPIO13 Green + GPIO_PWM4, // GPIO14 W1 - PWM4 + GPIO_PWM1, // GPIO15 Red + 0, 0 }, - { "Sonoff SC", // SONOFF_SC - onoff SC (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_TXD, // GPIO01 RXD to ATMEGA328P - GPIO_USER, // GPIO02 Optional sensor - GPIO_RXD, // GPIO03 TXD to ATMEGA328P - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_SC - onoff SC (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_TXD, // GPIO01 RXD to ATMEGA328P + GPIO_USER, // GPIO02 Optional sensor + GPIO_RXD, // GPIO03 TXD to ATMEGA328P + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff BN-SZ", // SONOFF_BN - Sonoff BN-SZ01 Ceiling led (ESP8285) - 0, 0, 0, 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_PWM1, // GPIO12 Light - GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_BN - Sonoff BN-SZ01 Ceiling led (ESP8285) + 0, 0, 0, 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_PWM1, // GPIO12 Light + GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff 4CH Pro", // SONOFF_4CHPRO - Sonoff 4CH Pro (ESP8285) - GPIO_KEY1, // GPIO00 Button 1 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional sensor - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On) - GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_KEY2, // GPIO09 Button 2 - GPIO_KEY3, // GPIO10 Button 3 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - GPIO_KEY4, // GPIO14 Button 4 - GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) - 0, 0 + { // SONOFF_4CHPRO - Sonoff 4CH Pro (ESP8285) + GPIO_KEY1, // GPIO00 Button 1 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional sensor + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On) + GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_KEY2, // GPIO09 Button 2 + GPIO_KEY3, // GPIO10 Button 3 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + GPIO_KEY4, // GPIO14 Button 4 + GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) + 0, 0 }, - { "Huafan SS", // HUAFAN_SS - Hua Fan Smart Socket (ESP8266) - like Sonoff Pow - GPIO_LEDLNK_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status - 0, 0, - GPIO_LED1_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status - GPIO_KEY1, // GPIO04 Button - GPIO_REL1_INV, // GPIO05 Relay (0 = On, 1 = Off) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_CF1, // GPIO12 HLW8012 CF1 voltage / current - GPIO_NRG_SEL, // GPIO13 HLW8012 Sel output (1 = Voltage) - GPIO_HLW_CF, // GPIO14 HLW8012 CF power - 0, 0, 0 + { // HUAFAN_SS - Hua Fan Smart Socket (ESP8266) - like Sonoff Pow + GPIO_LEDLNK_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status + 0, 0, + GPIO_LED1_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status + GPIO_KEY1, // GPIO04 Button + GPIO_REL1_INV, // GPIO05 Relay (0 = On, 1 = Off) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_CF1, // GPIO12 HLW8012 CF1 voltage / current + GPIO_NRG_SEL, // GPIO13 HLW8012 Sel output (1 = Voltage) + GPIO_HLW_CF, // GPIO14 HLW8012 CF power + 0, 0, 0 }, - { "Sonoff Bridge", // SONOFF_BRIDGE - Sonoff RF Bridge 433 (ESP8285) - GPIO_KEY1, // GPIO00 Button - GPIO_TXD, // GPIO01 RF bridge control - GPIO_USER, // GPIO02 Optional sensor - GPIO_RXD, // GPIO03 RF bridge control - GPIO_USER, // GPIO04 Optional sensor - GPIO_USER, // GPIO05 Optional sensor - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 Optional sensor - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, 0, 0 + { // SONOFF_BRIDGE - Sonoff RF Bridge 433 (ESP8285) + GPIO_KEY1, // GPIO00 Button + GPIO_TXD, // GPIO01 RF bridge control + GPIO_USER, // GPIO02 Optional sensor + GPIO_RXD, // GPIO03 RF bridge control + GPIO_USER, // GPIO04 Optional sensor + GPIO_USER, // GPIO05 Optional sensor + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 Optional sensor + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, 0 }, - { "Sonoff B1", // SONOFF_B1 - Sonoff B1 (ESP8285 - my9231) - GPIO_KEY1, // GPIO00 Pad - GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad - GPIO_USER, // GPIO02 Optional sensor SDA pad - GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_DI, // GPIO12 my9231 DI - 0, - GPIO_DCKI, // GPIO14 my9231 DCKI - 0, 0, 0 + { // SONOFF_B1 - Sonoff B1 (ESP8285 - my9231) + GPIO_KEY1, // GPIO00 Pad + GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad + GPIO_USER, // GPIO02 Optional sensor SDA pad + GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_DI, // GPIO12 my9231 DI + 0, + GPIO_DCKI, // GPIO14 my9231 DCKI + 0, 0, 0 }, - { "AiLight", // AILIGHT - Ai-Thinker RGBW led (ESP8266 - my9291) - GPIO_KEY1, // GPIO00 Pad - GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad - GPIO_USER, // GPIO02 Optional sensor SDA pad - GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_DI, // GPIO13 my9291 DI - 0, - GPIO_DCKI, // GPIO15 my9291 DCKI - 0, 0 + { // AILIGHT - Ai-Thinker RGBW led (ESP8266 - my9291) + GPIO_KEY1, // GPIO00 Pad + GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad + GPIO_USER, // GPIO02 Optional sensor SDA pad + GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, + GPIO_DI, // GPIO13 my9291 DI + 0, + GPIO_DCKI, // GPIO15 my9291 DCKI + 0, 0 }, - { "Sonoff T1 1CH", // SONOFF_T11 - Sonoff T1 1CH (ESP8285) - GPIO_KEY1, // GPIO00 Button 1 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_T11 - Sonoff T1 1CH (ESP8285) + GPIO_KEY1, // GPIO00 Button 1 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff T1 2CH", // SONOFF_T12 - Sonoff T1 2CH (ESP8285) - GPIO_KEY1, // GPIO00 Button 1 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, - GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_KEY2, // GPIO09 Button 2 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_T12 - Sonoff T1 2CH (ESP8285) + GPIO_KEY1, // GPIO00 Button 1 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, + GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_KEY2, // GPIO09 Button 2 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff T1 3CH", // SONOFF_T13 - Sonoff T1 3CH (ESP8285) - GPIO_KEY1, // GPIO00 Button 1 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_REL3, // GPIO04 Blue Led and Relay 3 (0 = Off, 1 = On) - GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_KEY2, // GPIO09 Button 2 - GPIO_KEY3, // GPIO10 Button 3 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_T13 - Sonoff T1 3CH (ESP8285) + GPIO_KEY1, // GPIO00 Button 1 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_REL3, // GPIO04 Blue Led and Relay 3 (0 = Off, 1 = On) + GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_KEY2, // GPIO09 Button 2 + GPIO_KEY3, // GPIO10 Button 3 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Supla Espablo", // SUPLA1 - Supla Espablo (ESP8266) - // http://www.wykop.pl/ramka/3325399/diy-supla-do-puszki-instalacyjnej-podtynkowej-supla-org/ - 0, // GPIO00 Flash jumper - GPIO_USER, // GPIO01 Serial RXD and Optional sensor + { // SUPLA1 - Supla Espablo (ESP8266) + // http://www.wykop.pl/ramka/3325399/diy-supla-do-puszki-instalacyjnej-podtynkowej-supla-org/ + 0, // GPIO00 Flash jumper + GPIO_USER, // GPIO01 Serial RXD and Optional sensor #ifdef USE_DS18x20 - GPIO_DSB, // GPIO02 DS18B20 sensor + GPIO_DSB, // GPIO02 DS18B20 sensor #else - GPIO_USER, // GPIO02 Optional sensor + GPIO_USER, // GPIO02 Optional sensor #endif - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_KEY1, // GPIO04 Button 1 - GPIO_REL1, // GPIO05 Relay 1 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 Optional sensor - GPIO_REL2, // GPIO13 Relay 2 (0 = Off, 1 = On) - GPIO_USER, // GPIO14 Optional sensor - 0, - GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - Link and Power status - ADC0_USER // ADC0 A0 Analog input + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_KEY1, // GPIO04 Button 1 + GPIO_REL1, // GPIO05 Relay 1 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 Optional sensor + GPIO_REL2, // GPIO13 Relay 2 (0 = Off, 1 = On) + GPIO_USER, // GPIO14 Optional sensor + 0, + GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - Link and Power status + ADC0_USER // ADC0 A0 Analog input }, - { "Witty Cloud", // WITTY - Witty Cloud Dev Board (ESP8266) - // https://www.aliexpress.com/item/ESP8266-serial-WIFI-Witty-cloud-Development-Board-ESP-12F-module-MINI-nodemcu/32643464555.html - GPIO_USER, // GPIO00 D3 flash push button on interface board - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 D4 Blue Led (0 = On, 1 = Off) on ESP-12F - Link and Power status - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_KEY1, // GPIO04 D2 push button on ESP-12F board - GPIO_USER, // GPIO05 D1 optional sensor - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 D6 RGB LED Green - GPIO_PWM3, // GPIO13 D7 RGB LED Blue - GPIO_USER, // GPIO14 D5 optional sensor - GPIO_PWM1, // GPIO15 D8 RGB LED Red - GPIO_USER, // GPIO16 D0 optional sensor - ADC0_USER // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled + { // WITTY - Witty Cloud Dev Board (ESP8266) + // https://www.aliexpress.com/item/ESP8266-serial-WIFI-Witty-cloud-Development-Board-ESP-12F-module-MINI-nodemcu/32643464555.html + GPIO_USER, // GPIO00 D3 flash push button on interface board + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_LED1_INV, // GPIO02 D4 Blue Led (0 = On, 1 = Off) on ESP-12F - Link and Power status + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_KEY1, // GPIO04 D2 push button on ESP-12F board + GPIO_USER, // GPIO05 D1 optional sensor + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 D6 RGB LED Green + GPIO_PWM3, // GPIO13 D7 RGB LED Blue + GPIO_USER, // GPIO14 D5 optional sensor + GPIO_PWM1, // GPIO15 D8 RGB LED Red + GPIO_USER, // GPIO16 D0 optional sensor + ADC0_USER // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled }, - { "Yunshan Relay", // YUNSHAN - Yunshan Wifi Relay (ESP8266) - // https://www.ebay.com/p/Esp8266-220v-10a-Network-Relay-WiFi-Module/1369583381 - // Schematics and Info https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/ - 0, // GPIO00 Flash jumper - Module Pin 8 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - Module Pin 2 - GPIO_LED1_INV, // GPIO02 Blue Led (0 = On, 1 = Off) on ESP-12F - Module Pin 7 - Link and Power status - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - Module Pin 3 - GPIO_REL1, // GPIO04 Red Led and Relay (0 = Off, 1 = On) - Module Pin 10 - GPIO_KEY1, // GPIO05 Blue Led and OptoCoupler input - Module Pin 9 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, 0, 0, 0, 0, 0 + { // YUNSHAN - Yunshan Wifi Relay (ESP8266) + // https://www.ebay.com/p/Esp8266-220v-10a-Network-Relay-WiFi-Module/1369583381 + // Schematics and Info https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/ + 0, // GPIO00 Flash jumper - Module Pin 8 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor - Module Pin 2 + GPIO_LED1_INV, // GPIO02 Blue Led (0 = On, 1 = Off) on ESP-12F - Module Pin 7 - Link and Power status + GPIO_USER, // GPIO03 Serial TXD and Optional sensor - Module Pin 3 + GPIO_REL1, // GPIO04 Red Led and Relay (0 = Off, 1 = On) - Module Pin 10 + GPIO_KEY1, // GPIO05 Blue Led and OptoCoupler input - Module Pin 9 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, 0, 0, 0, 0, 0 }, - { "MagicHome", // MAGICHOME - Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) - // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html - 0, - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) (Arilux LC10) - GPIO_PWM2, // GPIO05 RGB LED Green - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM3, // GPIO12 RGB LED Blue - GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10) - GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_ARIRFSEL, // GPIO15 RF receiver control (Arilux LC10) - 0, 0 + { // MAGICHOME - Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) + // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html + 0, + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) (Arilux LC10) + GPIO_PWM2, // GPIO05 RGB LED Green + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM3, // GPIO12 RGB LED Blue + GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10) + GPIO_PWM1, // GPIO14 RGB LED Red + GPIO_ARIRFSEL, // GPIO15 RF receiver control (Arilux LC10) + 0, 0 }, - { "Luani HVIO", // LUANIHVIO - ESP8266_HVIO - // https://luani.de/projekte/esp8266-hvio/ - 0, // GPIO00 Flash jumper - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional sensor / I2C SDA pad - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_REL1, // GPIO04 Relay 1 (0 = Off, 1 = On) - GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_SWT1, // GPIO12 External input 1 (0 = On, 1 = Off) - GPIO_SWT2, // GPIO13 External input 2 (0 = On, 1 = Off) - GPIO_USER, // GPIO14 Optional sensor / I2C SCL pad - GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status - 0, - ADC0_USER // ADC0 A0 Analog input + { // LUANIHVIO - ESP8266_HVIO + // https://luani.de/projekte/esp8266-hvio/ + 0, // GPIO00 Flash jumper + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional sensor / I2C SDA pad + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_REL1, // GPIO04 Relay 1 (0 = Off, 1 = On) + GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_SWT1, // GPIO12 External input 1 (0 = On, 1 = Off) + GPIO_SWT2, // GPIO13 External input 2 (0 = On, 1 = Off) + GPIO_USER, // GPIO14 Optional sensor / I2C SCL pad + GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status + 0, + ADC0_USER // ADC0 A0 Analog input }, - { "KMC 70011", // KMC_70011 - KMC 70011 - // https://www.amazon.com/KMC-Timing-Monitoring-Network-125V-240V/dp/B06XRX2GTQ - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, - GPIO_HLW_CF, // GPIO04 HLW8012 CF power - GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL, // GPIO12 HLW8012 SEL (1 = Voltage) - GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status - GPIO_REL1, // GPIO14 Relay - 0, 0, 0 + { // KMC_70011 - KMC 70011 + // https://www.amazon.com/KMC-Timing-Monitoring-Network-125V-240V/dp/B06XRX2GTQ + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, + GPIO_HLW_CF, // GPIO04 HLW8012 CF power + GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL, // GPIO12 HLW8012 SEL (1 = Voltage) + GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status + GPIO_REL1, // GPIO14 Relay + 0, 0, 0 }, - { "Arilux LC01", // ARILUX_LC01 - Arilux AL-LC01 (ESP8285) - // https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html - // (PwmFrequency 1111Hz) - GPIO_KEY1, // GPIO00 Optional Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_ARIRFSEL, // GPIO02 RF receiver control - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) - GPIO_PWM1, // GPIO05 RGB LED Red - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 RGB LED Green - GPIO_PWM3, // GPIO13 RGB LED Blue - GPIO_USER, // GPIO14 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) - 0, 0, 0 + { // ARILUX_LC01 - Arilux AL-LC01 (ESP8285) + // https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html + // (PwmFrequency 1111Hz) + GPIO_KEY1, // GPIO00 Optional Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_ARIRFSEL, // GPIO02 RF receiver control + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) + GPIO_PWM1, // GPIO05 RGB LED Red + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 RGB LED Green + GPIO_PWM3, // GPIO13 RGB LED Blue + GPIO_USER, // GPIO14 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) + 0, 0, 0 }, - { "Arilux LC11", // ARILUX_LC11 - Arilux AL-LC11 (ESP8266) - // https://www.banggood.com/nl/ARILUX-AL-LC11-Super-Mini-LED-WIFI-APP-Controller-RF-Remote-Control-For-RGBWW-LED-Strip-DC9-28V-p-1085112.html - // (PwmFrequency 540Hz) - GPIO_KEY1, // GPIO00 Optional Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_ARIRFSEL, // GPIO02 RF receiver control - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_PWM2, // GPIO04 RGB LED Green - GPIO_PWM1, // GPIO05 RGB LED Red - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM5, // GPIO12 RGBCW LED Warm - GPIO_PWM4, // GPIO13 RGBW LED Cold - GPIO_PWM3, // GPIO14 RGB LED Blue - GPIO_ARIRFRCV, // GPIO15 RF receiver input - 0, 0 + { // ARILUX_LC11 - Arilux AL-LC11 (ESP8266) + // https://www.banggood.com/nl/ARILUX-AL-LC11-Super-Mini-LED-WIFI-APP-Controller-RF-Remote-Control-For-RGBWW-LED-Strip-DC9-28V-p-1085112.html + // (PwmFrequency 540Hz) + GPIO_KEY1, // GPIO00 Optional Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_ARIRFSEL, // GPIO02 RF receiver control + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_PWM2, // GPIO04 RGB LED Green + GPIO_PWM1, // GPIO05 RGB LED Red + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM5, // GPIO12 RGBCW LED Warm + GPIO_PWM4, // GPIO13 RGBW LED Cold + GPIO_PWM3, // GPIO14 RGB LED Blue + GPIO_ARIRFRCV, // GPIO15 RF receiver input + 0, 0 }, - { "Sonoff Dual R2", // SONOFF_DUAL_R2 - Sonoff Dual R2 (ESP8285) - GPIO_USER, // GPIO00 Button 0 on header (0 = On, 1 = Off) - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, - GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_USER, // GPIO09 Button 1 on header (0 = On, 1 = Off) - GPIO_KEY1, // GPIO10 Button on casing - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_DUAL_R2 - Sonoff Dual R2 (ESP8285) + GPIO_USER, // GPIO00 Button 0 on header (0 = On, 1 = Off) + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, + GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_USER, // GPIO09 Button 1 on header (0 = On, 1 = Off) + GPIO_KEY1, // GPIO10 Button on casing + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Arilux LC06", // ARILUX_LC06 - Arilux AL-LC06 (ESP8285) - // https://www.banggood.com/ARILUX-AL-LC06-LED-WIFI-Smartphone-Controller-Romote-5-Channels-DC12-24V-For-RGBWW-Strip-light-p-1061476.html - GPIO_KEY1, // GPIO00 Optional Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Empty pad - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 W2 - PWM5 - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 RGB LED Green - GPIO_PWM3, // GPIO13 RGB LED Blue - GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_USER, // GPIO15 RGBW LED White - 0, 0 + { // ARILUX_LC06 - Arilux AL-LC06 (ESP8285) + // https://www.banggood.com/ARILUX-AL-LC06-LED-WIFI-Smartphone-Controller-Romote-5-Channels-DC12-24V-For-RGBWW-Strip-light-p-1061476.html + GPIO_KEY1, // GPIO00 Optional Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Empty pad + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 W2 - PWM5 + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 RGB LED Green + GPIO_PWM3, // GPIO13 RGB LED Blue + GPIO_PWM1, // GPIO14 RGB LED Red + GPIO_USER, // GPIO15 RGBW LED White + 0, 0 }, - { "Sonoff S31", // SONOFF_S31 - Sonoff S31 (ESP8266 - CSE7766) - GPIO_KEY1, // GPIO00 Button - GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor - 0, - GPIO_CSE7766_RX, // GPIO03 Serial TXD - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_S31 - Sonoff S31 (ESP8266 - CSE7766) + GPIO_KEY1, // GPIO00 Button + GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor + 0, + GPIO_CSE7766_RX, // GPIO03 Serial TXD + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Zengge WF017", // ZENGGE_ZF_WF017 - Zenggee ZJ-WF017-A (ESP12S)) - // https://www.ebay.com/p/Smartphone-Android-IOS-WiFi-Music-Controller-for-RGB-5050-3528-LED-Strip-Light/534446632?_trksid=p2047675.l2644 - GPIO_KEY1, // GPIO00 Optional Button - 0, - GPIO_USER, // GPIO02 Empty pad - 0, - GPIO_USER, // GPIO04 W2 - PWM5 - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 RGB LED Green - GPIO_PWM1, // GPIO13 RGB LED Red - GPIO_PWM3, // GPIO14 RGB LED Blue - 0, 0, 0 + { // ZENGGE_ZF_WF017 - Zenggee ZJ-WF017-A (ESP12S)) + // https://www.ebay.com/p/Smartphone-Android-IOS-WiFi-Music-Controller-for-RGB-5050-3528-LED-Strip-Light/534446632?_trksid=p2047675.l2644 + GPIO_KEY1, // GPIO00 Optional Button + 0, + GPIO_USER, // GPIO02 Empty pad + 0, + GPIO_USER, // GPIO04 W2 - PWM5 + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 RGB LED Green + GPIO_PWM1, // GPIO13 RGB LED Red + GPIO_PWM3, // GPIO14 RGB LED Blue + 0, 0, 0 }, - { "Sonoff Pow R2", // SONOFF_POW_R2 - Sonoff Pow R2 (ESP8285 - CSE7766) - GPIO_KEY1, // GPIO00 Button - GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor - 0, - GPIO_CSE7766_RX, // GPIO03 Serial TXD - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_POW_R2 - Sonoff Pow R2 (ESP8285 - CSE7766) + GPIO_KEY1, // GPIO00 Button + GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor + 0, + GPIO_CSE7766_RX, // GPIO03 Serial TXD + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff iFan02", // SONOFF_IFAN02 - Sonoff iFan02 (ESP8285) - GPIO_KEY1, // GPIO00 WIFI_KEY0 Virtual button 1 as feedback from RC - GPIO_USER, // GPIO01 ESP_TXD Serial RXD and Optional sensor - 0, // GPIO02 ESP_LOG - GPIO_USER, // GPIO03 ESP_RXD Serial TXD and Optional sensor - GPIO_REL3, // GPIO04 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan - GPIO_REL2, // GPIO05 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_KEY2, // GPIO09 WIFI_KEY1 Virtual button 2 as feedback from RC - GPIO_KEY3, // GPIO10 WIFI_KEY2 Virtual button 3 as feedback from RC - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light - GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status - GPIO_KEY4, // GPIO14 WIFI_KEY3 Virtual button 4 as feedback from RC - GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan - 0, 0 + { // SONOFF_IFAN02 - Sonoff iFan02 (ESP8285) + GPIO_KEY1, // GPIO00 WIFI_KEY0 Virtual button 1 as feedback from RC + GPIO_USER, // GPIO01 ESP_TXD Serial RXD and Optional sensor + 0, // GPIO02 ESP_LOG + GPIO_USER, // GPIO03 ESP_RXD Serial TXD and Optional sensor + GPIO_REL3, // GPIO04 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan + GPIO_REL2, // GPIO05 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_KEY2, // GPIO09 WIFI_KEY1 Virtual button 2 as feedback from RC + GPIO_KEY3, // GPIO10 WIFI_KEY2 Virtual button 3 as feedback from RC + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light + GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status + GPIO_KEY4, // GPIO14 WIFI_KEY3 Virtual button 4 as feedback from RC + GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan + 0, 0 }, - { "BlitzWolf SHP", // BLITZWOLF_BWSHP - BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring) - // https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html - // https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1 - // https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1 - // https://www.aliexpress.com/store/product/BlitzWolf-BW-SHP6-EU-Plug-Metering-Version-WIFI-Smart-Socket-220V-240V-10A-Work-with-Amazon/1965360_32945504669.html - GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, - GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_KEY1, // GPIO13 Button - GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage - GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) - 0, 0 + { // BLITZWOLF_BWSHP - BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring) + // https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html + // https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1 + // https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1 + // https://www.aliexpress.com/store/product/BlitzWolf-BW-SHP6-EU-Plug-Metering-Version-WIFI-Smart-Socket-220V-240V-10A-Work-with-Amazon/1965360_32945504669.html + GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, + GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_KEY1, // GPIO13 Button + GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage + GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) + 0, 0 }, - { "Shelly 1", // SHELLY1 - Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ - GPIO_USER, // GPIO00 - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC - GPIO_USER, // GPIO01 Serial RXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC - 0, - GPIO_USER, // GPIO03 Serial TXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC - GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) - GPIO_SWT1_NP, // GPIO05 SW pin - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, 0, 0, 0, 0, 0 + { // SHELLY1 - Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ + GPIO_USER, // GPIO00 - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC + GPIO_USER, // GPIO01 Serial RXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC + 0, + GPIO_USER, // GPIO03 Serial TXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC + GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) + GPIO_SWT1_NP, // GPIO05 SW pin + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, 0, 0, 0, 0, 0 }, - { "Shelly 2", // SHELLY2 - Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/ - 0, - GPIO_MCP39F5_TX, // GPIO01 MCP39F501 Serial input - 0, - GPIO_MCP39F5_RX, // GPIO03 MCP39F501 Serial output - GPIO_REL1, // GPIO04 - GPIO_REL2, // GPIO05 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_SWT1, // GPIO12 - 0, - GPIO_SWT2, // GPIO14 - GPIO_MCP39F5_RST, // GPIO15 MCP39F501 Reset - 0, - 0 + { // SHELLY2 - Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/ + 0, + GPIO_MCP39F5_TX, // GPIO01 MCP39F501 Serial input + 0, + GPIO_MCP39F5_RX, // GPIO03 MCP39F501 Serial output + GPIO_REL1, // GPIO04 + GPIO_REL2, // GPIO05 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_SWT1, // GPIO12 + 0, + GPIO_SWT2, // GPIO14 + GPIO_MCP39F5_RST, // GPIO15 MCP39F501 Reset + 0, + 0 }, - { "Xiaomi Philips", // PHILIPS - Xiaomi Philips bulb (ESP8266) - 0, 0, 0, 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 cold/warm light - 0, 0, - GPIO_PWM1, // GPIO15 light intensity - 0, 0 + { // PHILIPS - Xiaomi Philips bulb (ESP8266) + 0, 0, 0, 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 cold/warm light + 0, 0, + GPIO_PWM1, // GPIO15 light intensity + 0, 0 }, - { "Neo Coolcam", // NEO_COOLCAM - Neo Coolcam (ESP8266) - // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN - 0, 0, 0, 0, - GPIO_LED1_INV, // GPIO04 Red Led (0 = On, 1 = Off) - Link and Power status - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_KEY1, // GPIO13 Button - 0, 0, 0, 0 + { // NEO_COOLCAM - Neo Coolcam (ESP8266) + // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN + 0, 0, 0, 0, + GPIO_LED1_INV, // GPIO04 Red Led (0 = On, 1 = Off) - Link and Power status + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_KEY1, // GPIO13 Button + 0, 0, 0, 0 }, - { "ESP Switch", // ESP_SWITCH - Michael Haustein 4 channel wall switch (ESP07 = ESP8266) - // Use rules for further actions like - rule on power1#state do publish cmnd/other_device/power %value% endon - GPIO_KEY2, // GPIO00 Button 2 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_REL3_INV, // GPIO02 Yellow Led 3 (0 = On, 1 = Off) - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_KEY1, // GPIO04 Button 1 - GPIO_REL2_INV, // GPIO05 Red Led 2 (0 = On, 1 = Off) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL4_INV, // GPIO12 Blue Led 4 (0 = On, 1 = Off) - GPIO_KEY4, // GPIO13 Button 4 - GPIO_KEY3, // GPIO14 Button 3 - GPIO_LED1, // GPIO15 Optional sensor - GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off) - 0 + { // ESP_SWITCH - Michael Haustein 4 channel wall switch (ESP07 = ESP8266) + // Use rules for further actions like - rule on power1#state do publish cmnd/other_device/power %value% endon + GPIO_KEY2, // GPIO00 Button 2 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_REL3_INV, // GPIO02 Yellow Led 3 (0 = On, 1 = Off) + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_KEY1, // GPIO04 Button 1 + GPIO_REL2_INV, // GPIO05 Red Led 2 (0 = On, 1 = Off) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL4_INV, // GPIO12 Blue Led 4 (0 = On, 1 = Off) + GPIO_KEY4, // GPIO13 Button 4 + GPIO_KEY3, // GPIO14 Button 3 + GPIO_LED1, // GPIO15 Optional sensor + GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off) + 0 }, - { "OBI Socket", // OBI - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 - GPIO_USER, // GPIO00 - GPIO_USER, // GPIO01 Serial RXD - 0, - GPIO_USER, // GPIO03 Serial TXD - GPIO_LED1, // GPIO04 Blue LED - Link and Power status - GPIO_REL1, // GPIO05 (Relay OFF, but used as Relay Switch) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_LED3, // GPIO12 (Relay ON, but set to LOW, so we can switch with GPIO05) - GPIO_USER, // GPIO13 - GPIO_KEY1, // GPIO14 Button - 0, - GPIO_USER, // GPIO16 - ADC0_USER // ADC0 A0 Analog input + { // OBI - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 + GPIO_USER, // GPIO00 + GPIO_USER, // GPIO01 Serial RXD + 0, + GPIO_USER, // GPIO03 Serial TXD + GPIO_LED1, // GPIO04 Blue LED - Link and Power status + GPIO_REL1, // GPIO05 (Relay OFF, but used as Relay Switch) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_LED3, // GPIO12 (Relay ON, but set to LOW, so we can switch with GPIO05) + GPIO_USER, // GPIO13 + GPIO_KEY1, // GPIO14 Button + 0, + GPIO_USER, // GPIO16 + ADC0_USER // ADC0 A0 Analog input }, - { "Teckin", // TECKIN - https://www.amazon.de/gp/product/B07D5V139R - 0, - GPIO_KEY1, // GPIO01 Serial TXD and Button - 0, - GPIO_LED1_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status - GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power - GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_LEDLNK_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status - GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) - 0, 0, 0 + { // TECKIN - https://www.amazon.de/gp/product/B07D5V139R + 0, + GPIO_KEY1, // GPIO01 Serial TXD and Button + 0, + GPIO_LED1_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status + GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_LEDLNK_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status + GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) + 0, 0, 0 }, - { "AplicWDP303075", // APLIC_WDP303075 - Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring) - // https://www.amazon.de/dp/B07CNWVNJ2 - 0, 0, 0, - GPIO_KEY1, // GPIO03 Button - GPIO_HLW_CF, // GPIO04 HLW8012 CF power - GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) - GPIO_LED1_INV, // GPIO13 LED (0 = On, 1 = Off) - Link and Power status - GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On ) - 0, 0, 0 + { // APLIC_WDP303075 - Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring) + // https://www.amazon.de/dp/B07CNWVNJ2 + 0, 0, 0, + GPIO_KEY1, // GPIO03 Button + GPIO_HLW_CF, // GPIO04 HLW8012 CF power + GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) + GPIO_LED1_INV, // GPIO13 LED (0 = On, 1 = Off) - Link and Power status + GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On ) + 0, 0, 0 }, - { "Tuya MCU", // TUYA_DIMMER - Tuya MCU device (ESP8266 w/ separate MCU) - // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1 - GPIO_USER, // Virtual Button (controlled by MCU) - GPIO_USER, // GPIO01 MCU serial control - GPIO_USER, - GPIO_USER, // GPIO03 MCU serial control - GPIO_USER, - GPIO_USER, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, - GPIO_USER, - GPIO_USER, // GPIO14 Green Led - GPIO_USER, - GPIO_USER, - 0 + { // TUYA_DIMMER - Tuya MCU device (ESP8266 w/ separate MCU) + // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1 + GPIO_USER, // Virtual Button (controlled by MCU) + GPIO_USER, // GPIO01 MCU serial control + GPIO_USER, + GPIO_USER, // GPIO03 MCU serial control + GPIO_USER, + GPIO_USER, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, + GPIO_USER, + GPIO_USER, // GPIO14 Green Led + GPIO_USER, + GPIO_USER, + 0 }, - { "Gosund SP1 v23", // GOSUND - https://www.amazon.de/gp/product/B0777BWS1P - 0, - GPIO_LEDLNK_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status - 0, - GPIO_KEY1, // GPIO03 Serial TXD and Button - GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power - GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_LED1_INV, // GPIO13 LED2 (red) inv - Power status - GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) - 0, 0, 0 + { // GOSUND - https://www.amazon.de/gp/product/B0777BWS1P + 0, + GPIO_LEDLNK_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status + 0, + GPIO_KEY1, // GPIO03 Serial TXD and Button + GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_LED1_INV, // GPIO13 LED2 (red) inv - Power status + GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) + 0, 0, 0 }, - { "ARMTR Dimmer", // ARMTRONIX_DIMMERS - ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) - // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/ - // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/ - GPIO_USER, - GPIO_TXD, // GPIO01 MCU serial control - GPIO_USER, - GPIO_RXD, // GPIO03 MCU serial control - GPIO_USER, - GPIO_USER, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, - GPIO_USER, - GPIO_USER, - GPIO_USER, - GPIO_USER, - 0 + { // ARMTRONIX_DIMMERS - ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) + // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/ + // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/ + GPIO_USER, + GPIO_TXD, // GPIO01 MCU serial control + GPIO_USER, + GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, + GPIO_USER, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, + GPIO_USER, + GPIO_USER, + GPIO_USER, + GPIO_USER, + 0 }, - { "SK03 Outdoor", // SK03_TUYA - Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, - GPIO_HLW_CF, // GPIO04 HLW8012 CF power - GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) - GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Power status - GPIO_LEDLNK_INV, // GPIO14 Blue Led (0 = On, 1 = Off) - Link status - GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) - 0, 0 + { // SK03_TUYA - Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, + GPIO_HLW_CF, // GPIO04 HLW8012 CF power + GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) + GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Power status + GPIO_LEDLNK_INV, // GPIO14 Blue Led (0 = On, 1 = Off) - Link status + GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) + 0, 0 }, - { "PS-16-DZ", // PS_16_DZ - PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer) - // https://www.aliexpress.com/item/SM-Smart-WIFI-Wall-Dimmer-Light-Switch-US-Ewelink-APP-Remote-Control-Wi-Fi-Wirele-Work/32871151902.html - GPIO_USER, - GPIO_TXD, // GPIO01 MCU serial control - GPIO_USER, - GPIO_RXD, // GPIO03 MCU serial control - GPIO_USER, - GPIO_USER, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, - GPIO_LED1, // GPIO13 WiFi LED - Link and Power status - GPIO_USER, - GPIO_USER, - GPIO_USER, - 0 + { // PS_16_DZ - PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer) + // https://www.aliexpress.com/item/SM-Smart-WIFI-Wall-Dimmer-Light-Switch-US-Ewelink-APP-Remote-Control-Wi-Fi-Wirele-Work/32871151902.html + GPIO_USER, + GPIO_TXD, // GPIO01 MCU serial control + GPIO_USER, + GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, + GPIO_USER, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, + GPIO_LED1, // GPIO13 WiFi LED - Link and Power status + GPIO_USER, + GPIO_USER, + GPIO_USER, + 0 }, - { "Teckin US", // TECKIN_US - Teckin SP20 US with Energy Monitoring - // https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B - // https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN - GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status - 0, - GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status - 0, - GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) - GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_KEY1, // GPIO13 Button - GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage - 0, 0, 0 + { // TECKIN_US - Teckin SP20 US with Energy Monitoring + // https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B + // https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN + GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status + 0, + GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status + 0, + GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) + GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_KEY1, // GPIO13 Button + GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage + 0, 0, 0 }, - { "Manzoku strip", // MANZOKU_EU_4 - "MANZOKU" labeled power strip, EU version - // https://www.amazon.de/Steckdosenleiste-AOFO-Mehrfachsteckdose-Überspannungsschutz-Sprachsteuerung/dp/B07GBSD11P/ - // https://www.amazon.de/Steckdosenleiste-Geekbes-USB-Anschluss-Kompatibel-gesteuert/dp/B078W23BW9/ - 0, // GPIO00 - 0, // GPIO01 Serial RXD - 0, - GPIO_KEY1, // GPIO03 Serial TXD + Button - GPIO_REL2, // GPIO04 Relay 2 - GPIO_REL1, // GPIO05 Relay 1 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL3, // GPIO12 Relay 3 - GPIO_REL4, // GPIO13 Relay 4 - GPIO_USER, // GPIO14 - 0, - GPIO_USER, // GPIO16 - 0 + { // MANZOKU_EU_4 - "MANZOKU" labeled power strip, EU version + // https://www.amazon.de/Steckdosenleiste-AOFO-Mehrfachsteckdose-Überspannungsschutz-Sprachsteuerung/dp/B07GBSD11P/ + // https://www.amazon.de/Steckdosenleiste-Geekbes-USB-Anschluss-Kompatibel-gesteuert/dp/B078W23BW9/ + 0, // GPIO00 + 0, // GPIO01 Serial RXD + 0, + GPIO_KEY1, // GPIO03 Serial TXD + Button + GPIO_REL2, // GPIO04 Relay 2 + GPIO_REL1, // GPIO05 Relay 1 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL3, // GPIO12 Relay 3 + GPIO_REL4, // GPIO13 Relay 4 + GPIO_USER, // GPIO14 + 0, + GPIO_USER, // GPIO16 + 0 }, - { "OBI Socket 2", // OBI2 - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673 - 0, // GPIO00 - 0, // GPIO01 Serial RXD - 0, - 0, // GPIO03 Serial TXD - GPIO_REL1, // GPIO04 Relay 1 - GPIO_KEY1, // GPIO05 Button - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_LEDLNK_INV, // GPIO12 Green LED - Link status - GPIO_LED1, // GPIO13 Red LED - Power status - 0, 0, 0, 0 + { // OBI2 - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673 + 0, // GPIO00 + 0, // GPIO01 Serial RXD + 0, + 0, // GPIO03 Serial TXD + GPIO_REL1, // GPIO04 Relay 1 + GPIO_KEY1, // GPIO05 Button + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_LEDLNK_INV, // GPIO12 Green LED - Link status + GPIO_LED1, // GPIO13 Red LED - Power status + 0, 0, 0, 0 }, - { "YTF IR Bridge", // YTF_IR_BRIDGE - https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html - GPIO_USER, // GPIO00 - GPIO_USER, // GPIO01 Serial RXD - GPIO_USER, // GPIO02 - GPIO_USER, // GPIO03 Serial TXD - GPIO_LED1_INV, // GPIO04 Blue Led - Link status - GPIO_IRRECV, // GPIO05 IR Receiver - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, // GPIO12 - GPIO_KEY1, // GPIO13 Button - GPIO_IRSEND, // GPIO14 IR Transmitter - 0, 0, 0 + { // YTF_IR_BRIDGE - https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html + GPIO_USER, // GPIO00 + GPIO_USER, // GPIO01 Serial RXD + GPIO_USER, // GPIO02 + GPIO_USER, // GPIO03 Serial TXD + GPIO_LED1_INV, // GPIO04 Blue Led - Link status + GPIO_IRRECV, // GPIO05 IR Receiver + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, // GPIO12 + GPIO_KEY1, // GPIO13 Button + GPIO_IRSEND, // GPIO14 IR Transmitter + 0, 0, 0 }, - { "Digoo DG-SP202", // DIGOO - Digoo DG-SP202 - // https://www.banggood.com/DIGOO-DG-SP202-Dual-EU-Plug-Smart-WIFI-Socket-Individual-Controllable-Energy-Monitor-Remote-Control-Timing-Smart-Home-Outlet-let-p-1375323.html - GPIO_KEY1, // GPIO00 Button1 - 0, // GPIO01 Serial RXD - 0, // GPIO02 - 0, // GPIO03 Serial TXD - GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power - GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_LED1, // GPIO13 Blue Leds - Link Status - GPIO_REL2, // GPIO14 Relay2 (0 = Off, 1 = On) and Red Led - GPIO_REL1, // GPIO15 Relay1 (0 = Off, 1 = On) and Red Led - GPIO_KEY2_NP, // GPIO16 Button2, externally pulled up - 0 + { // DIGOO - Digoo DG-SP202 + // https://www.banggood.com/DIGOO-DG-SP202-Dual-EU-Plug-Smart-WIFI-Socket-Individual-Controllable-Energy-Monitor-Remote-Control-Timing-Smart-Home-Outlet-let-p-1375323.html + GPIO_KEY1, // GPIO00 Button1 + 0, // GPIO01 Serial RXD + 0, // GPIO02 + 0, // GPIO03 Serial TXD + GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_LED1, // GPIO13 Blue Leds - Link Status + GPIO_REL2, // GPIO14 Relay2 (0 = Off, 1 = On) and Red Led + GPIO_REL1, // GPIO15 Relay1 (0 = Off, 1 = On) and Red Led + GPIO_KEY2_NP, // GPIO16 Button2, externally pulled up + 0 }, - { "KA10", // KA10 - SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y - 0, // GPIO00 - GPIO_LEDLNK_INV, // GPIO01 Blue LED - Link status - 0, // GPIO02 - GPIO_KEY1, // GPIO03 Button - GPIO_HJL_CF, // GPIO04 BL0937 CF power - GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) - GPIO_LED1, // GPIO13 Red LED - Power status - GPIO_REL1, // GPIO14 Relay 1 - 0, 0, 0 + { // KA10 - SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y + 0, // GPIO00 + GPIO_LEDLNK_INV, // GPIO01 Blue LED - Link status + 0, // GPIO02 + GPIO_KEY1, // GPIO03 Button + GPIO_HJL_CF, // GPIO04 BL0937 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) + GPIO_LED1, // GPIO13 Red LED - Power status + GPIO_REL1, // GPIO14 Relay 1 + 0, 0, 0 }, - { "Luminea ZX2820", // ZX2820 - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, - GPIO_HLW_CF, // GPIO04 HLW8012 CF power - GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 HLW8012 SEL (0 = Voltage) - GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status - GPIO_REL1, // GPIO14 Relay - 0, 0, 0 + { // ZX2820 + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, + GPIO_HLW_CF, // GPIO04 HLW8012 CF power + GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 HLW8012 SEL (0 = Voltage) + GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status + GPIO_REL1, // GPIO14 Relay + 0, 0, 0 }, - { "Mi Desk Lamp", // MI_DESK_LAMP - Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/ - 0, 0, - GPIO_KEY1, // GPIO02 Button - 0, - GPIO_PWM1, // GPIO04 Cold White - GPIO_PWM2, // GPIO05 Warm White - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_ROT1A, // GPIO12 Rotary switch A pin - GPIO_ROT1B, // GPIO13 Rotary switch B pin - 0, 0, 0, 0 + { // MI_DESK_LAMP - Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/ + 0, 0, + GPIO_KEY1, // GPIO02 Button + 0, + GPIO_PWM1, // GPIO04 Cold White + GPIO_PWM2, // GPIO05 Warm White + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_ROT1A, // GPIO12 Rotary switch A pin + GPIO_ROT1B, // GPIO13 Rotary switch B pin + 0, 0, 0, 0 }, - { "SP10", // SP10 - Tuya SP10 (BL0937 Energy Monitoring) - // https://www.aliexpress.com/item/Smart-Mini-WiFi-Plug-Outlet-Switch-Work-With-ForEcho-Alexa-Google-Home-Remote-EU-Smart-Socket/32963670423.html - 0, // GPIO00 - GPIO_PWM1, // GPIO01 Nightlight - 0, // GPIO02 - GPIO_KEY1, // GPIO03 Button - GPIO_HJL_CF, // GPIO04 BL0937 CF power - GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) - GPIO_LED1, // GPIO13 Blue LED - Link status - GPIO_REL1, // GPIO14 Relay and red LED - 0, 0, 0 + { // SP10 - Tuya SP10 (BL0937 Energy Monitoring) + // https://www.aliexpress.com/item/Smart-Mini-WiFi-Plug-Outlet-Switch-Work-With-ForEcho-Alexa-Google-Home-Remote-EU-Smart-Socket/32963670423.html + 0, // GPIO00 + GPIO_PWM1, // GPIO01 Nightlight + 0, // GPIO02 + GPIO_KEY1, // GPIO03 Button + GPIO_HJL_CF, // GPIO04 BL0937 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) + GPIO_LED1, // GPIO13 Blue LED - Link status + GPIO_REL1, // GPIO14 Relay and red LED + 0, 0, 0 }, - { "WAGA CHCZ02MB", // WAGA - WAGA life CHCZ02MB (HJL-01 Energy Monitoring) - // https://www.ebay.com/itm/332595697006 - GPIO_LED1_INV, // GPIO00 Red LED - 0, // GPIO01 Serial RXD - 0, // GPIO02 - GPIO_NRG_SEL_INV, // GPIO03 HJL-01 Sel output (1 = Voltage) - 0, // GPIO04 - GPIO_HJL_CF, // GPIO05 HJL-01 CF power - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Relay - GPIO_KEY1, // GPIO13 Button - GPIO_NRG_CF1, // GPIO14 HJL-01 CF1 voltage / current - GPIO_LEDLNK_INV, // GPIO15 Blue LED - Link status - 0, 0 + { // WAGA - WAGA life CHCZ02MB (HJL-01 Energy Monitoring) + // https://www.ebay.com/itm/332595697006 + GPIO_LED1_INV, // GPIO00 Red LED + 0, // GPIO01 Serial RXD + 0, // GPIO02 + GPIO_NRG_SEL_INV, // GPIO03 HJL-01 Sel output (1 = Voltage) + 0, // GPIO04 + GPIO_HJL_CF, // GPIO05 HJL-01 CF power + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Relay + GPIO_KEY1, // GPIO13 Button + GPIO_NRG_CF1, // GPIO14 HJL-01 CF1 voltage / current + GPIO_LEDLNK_INV, // GPIO15 Blue LED - Link status + 0, 0 }, - { "SYF05", // SYF05 - Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726 - // Also works with Merkury 904 RGBW Bulbs with 13 set to GPIO_SM16716_SEL - // https://www.flipkart.com/fc-mila-bxav-xs-ad-smart-bulb/p/itmf85zgs45fzr7n - // https://docs.tuya.com/en/hardware/WiFi-module/wifi-e3s-module.html - // http://www.datasheet-pdf.com/PDF/SM16716-Datasheet-Sunmoon-932771 - GPIO_USER, // GPIO00 N.C. - 0, // GPIO01 Serial RXD - GPIO_USER, // GPIO02 N.C. - 0, // GPIO03 Serial TXD - GPIO_SM16716_CLK, // GPIO04 SM16716 Clock - GPIO_PWM1, // GPIO05 White - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 Alt. White on some devices - GPIO_USER, // GPIO13 SM16716 Enable on some devices - GPIO_SM16716_DAT, // GPIO14 SM16716 Data - 0, // GPIO15 wired to GND - GPIO_USER, // GPIO16 N.C. - ADC0_USER // ADC0 A0 Analog input + { // SYF05 - Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726 + // Also works with Merkury 904 RGBW Bulbs with 13 set to GPIO_SM16716_SEL + // https://www.flipkart.com/fc-mila-bxav-xs-ad-smart-bulb/p/itmf85zgs45fzr7n + // https://docs.tuya.com/en/hardware/WiFi-module/wifi-e3s-module.html + // http://www.datasheet-pdf.com/PDF/SM16716-Datasheet-Sunmoon-932771 + GPIO_USER, // GPIO00 N.C. + 0, // GPIO01 Serial RXD + GPIO_USER, // GPIO02 N.C. + 0, // GPIO03 Serial TXD + GPIO_SM16716_CLK, // GPIO04 SM16716 Clock + GPIO_PWM1, // GPIO05 White + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 Alt. White on some devices + GPIO_USER, // GPIO13 SM16716 Enable on some devices + GPIO_SM16716_DAT, // GPIO14 SM16716 Data + 0, // GPIO15 wired to GND + GPIO_USER, // GPIO16 N.C. + ADC0_USER // ADC0 A0 Analog input }, - { "Sonoff L1", // SONOFF_L1 - Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU) - 0, - GPIO_TXD, // GPIO01 MCU serial control - 0, - GPIO_RXD, // GPIO03 MCU serial control - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status - 0, 0, 0, 0 + { // SONOFF_L1 - Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU) + 0, + GPIO_TXD, // GPIO01 MCU serial control + 0, + GPIO_RXD, // GPIO03 MCU serial control + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, + GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff iFan03", // SONOFF_IFAN03 - Sonoff iFan03 (ESP8285) - GPIO_KEY1, // GPIO00 WIFI_KEY0 Button 1 - GPIO_TXD, // GPIO01 ESP_TXD Serial RXD connection to P0.5 of RF microcontroller - 0, // GPIO02 ESP_LOG - GPIO_RXD, // GPIO03 ESP_RXD Serial TXD connection to P0.4 of RF microcontroller - 0, // GPIO04 DEBUG_RX - 0, // GPIO05 DEBUG_TX - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_REL1_INV, // GPIO09 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light - GPIO_BUZZER_INV, // GPIO10 WIFI_O4 Buzzer (0 = Off, 1 = On) - // GPIO11 (SD_CMD Flash) - GPIO_REL3, // GPIO12 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan - GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status - GPIO_REL2, // GPIO14 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan - GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan - 0, 0 + { // SONOFF_IFAN03 - Sonoff iFan03 (ESP8285) + GPIO_KEY1, // GPIO00 WIFI_KEY0 Button 1 + GPIO_TXD, // GPIO01 ESP_TXD Serial RXD connection to P0.5 of RF microcontroller + 0, // GPIO02 ESP_LOG + GPIO_RXD, // GPIO03 ESP_RXD Serial TXD connection to P0.4 of RF microcontroller + 0, // GPIO04 DEBUG_RX + 0, // GPIO05 DEBUG_TX + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_REL1_INV, // GPIO09 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light + GPIO_BUZZER_INV, // GPIO10 WIFI_O4 Buzzer (0 = Off, 1 = On) + // GPIO11 (SD_CMD Flash) + GPIO_REL3, // GPIO12 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan + GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status + GPIO_REL2, // GPIO14 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan + GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan + 0, 0 }, - { "EXS Dimmer", // EXS_DIMMER - EX-Store WiFi Dimmer v4, two channel (ESP8266 w/ separate MCU dimmer) - // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A - // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A-ESP8266-V12-Stift-und-Buchsenleisten - 0, - GPIO_TXD, // GPIO01 MCU serial control - GPIO_LEDLNK, // GPIO02 LED Link - GPIO_RXD, // GPIO03 MCU serial control - GPIO_USER, // GPIO04 - GPIO_USER, // GPIO05 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 - GPIO_EXS_ENABLE, // GPIO13 EXS MCU Enable - GPIO_USER, // GPIO14 - 0, // GPIO15 - 0, 0 + { // EXS_DIMMER - EX-Store WiFi Dimmer v4, two channel (ESP8266 w/ separate MCU dimmer) + // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A + // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A-ESP8266-V12-Stift-und-Buchsenleisten + 0, + GPIO_TXD, // GPIO01 MCU serial control + GPIO_LEDLNK, // GPIO02 LED Link + GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, // GPIO04 + GPIO_USER, // GPIO05 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 + GPIO_EXS_ENABLE, // GPIO13 EXS MCU Enable + GPIO_USER, // GPIO14 + 0, // GPIO15 + 0, 0 }, - { "PWM Dimmer", // PWM_DIMMER - Support for Martin Jerry/acenx/Tessan/NTONPOWER SD0x PWM - // dimmer switches. The brightness of the load for these dimmers is - // controlled by a PWM GPIO pin. There are typically power, up & down - // buttons and 4 LED's. Examples are: - // https://www.amazon.com/dp/B07FXYSVR1 - // https://www.amazon.com/dp/B07V26Q3VD - // https://www.amazon.com/dp/B07K67D43J - // https://www.amazon.com/dp/B07TTGFWFM - GPIO_KEY3, // GPIO00 Up button - GPIO_KEY2, // GPIO01 Down button - 0, // GPIO02 - GPIO_LED4_INV, // GPIO03 Level 5 LED - GPIO_LEDLNK_INV, // GPIO04 LED Link - GPIO_LED3_INV, // GPIO05 Level 4 LED - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_LED2_INV, // GPIO12 Level 3 LED - GPIO_PWM1, // GPIO13 Dimmer PWM - GPIO_LED1_INV, // GPIO12 Level 2 LED - GPIO_KEY1_INV, // GPIO15 Power button - GPIO_REL1_INV, // GPIO16 Power relay/Level 1 LED - 0 + { // PWM_DIMMER - Support for Martin Jerry/acenx/Tessan/NTONPOWER SD0x PWM + // dimmer switches. The brightness of the load for these dimmers is + // controlled by a PWM GPIO pin. There are typically power, up & down + // buttons and 4 LED's. Examples are: + // https://www.amazon.com/dp/B07FXYSVR1 + // https://www.amazon.com/dp/B07V26Q3VD + // https://www.amazon.com/dp/B07K67D43J + // https://www.amazon.com/dp/B07TTGFWFM + GPIO_KEY3, // GPIO00 Up button + GPIO_KEY2, // GPIO01 Down button + 0, // GPIO02 + GPIO_LED4_INV, // GPIO03 Level 5 LED + GPIO_LEDLNK_INV, // GPIO04 LED Link + GPIO_LED3_INV, // GPIO05 Level 4 LED + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_LED2_INV, // GPIO12 Level 3 LED + GPIO_PWM1, // GPIO13 Dimmer PWM + GPIO_LED1_INV, // GPIO12 Level 2 LED + GPIO_KEY1_INV, // GPIO15 Power button + GPIO_REL1_INV, // GPIO16 Power relay/Level 1 LED + 0 }, - { "Sonoff D1", // SONOFF_D1 - Sonoff D1 RF Dimmer 433 (ESP8285) - GPIO_USER, // GPIO00 Pad - GPIO_TXD, // GPIO01 D1 control - 0, // GPIO02 - GPIO_RXD, // GPIO03 D1 control - 0, // GPIO04 DEBUG_RX - 0, // GPIO05 DEBUG_TX - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - 0, // GPIO12 - GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status - 0, 0, 0, 0 + { // SONOFF_D1 - Sonoff D1 RF Dimmer 433 (ESP8285) + GPIO_USER, // GPIO00 Pad + GPIO_TXD, // GPIO01 D1 control + 0, // GPIO02 + GPIO_RXD, // GPIO03 D1 control + 0, // GPIO04 DEBUG_RX + 0, // GPIO05 DEBUG_TX + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + 0, // GPIO12 + GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status + 0, 0, 0, 0 } }; +#endif // ESP8266 + +#ifdef ESP32 +#include "tasmota_template_ESP32.h" +#endif // ESP32 + #endif // _TASMOTA_TEMPLATE_H_ diff --git a/tasmota/tasmota_template_ESP32.h b/tasmota/tasmota_template_ESP32.h new file mode 100644 index 000000000..5fac8c85d --- /dev/null +++ b/tasmota/tasmota_template_ESP32.h @@ -0,0 +1,660 @@ +/* + tasmota_template_ESP32.h - ESP32 template settings for Tasmota + + Copyright (C) 2020 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _TASMOTA_TEMPLATE_ESP32_H_ +#define _TASMOTA_TEMPLATE_ESP32_H_ + +#ifdef ESP32 + +// Hardware has no ESP32 +#undef USE_TUYA_DIMMER +#undef USE_PWM_DIMMER +#undef USE_EXS_DIMMER +#undef USE_ARMTRONIX_DIMMERS +#undef USE_SONOFF_RF +#undef USE_SONOFF_SC +#undef USE_SONOFF_IFAN +#undef USE_SONOFF_L1 +#undef USE_SONOFF_D1 +#undef USE_RF_FLASH + +// Not ported (yet) +#undef USE_DISCOVERY +#undef USE_ADC_VCC // Needs to be ported +#undef USE_DEEPSLEEP +#undef USE_MY92X1 +#undef USE_TUYA_MCU +#undef USE_PS_16_DZ + +enum UserSelectablePins { + GPIO_NONE, // Not used + GPIO_KEY1, GPIO_KEY1_NP, GPIO_KEY1_INV, GPIO_KEY1_INV_NP, // 4 x Button + GPIO_SWT1, GPIO_SWT1_NP, // 8 x User connected external switches + GPIO_REL1, GPIO_REL1_INV, // 8 x Relays + GPIO_LED1, GPIO_LED1_INV, // 4 x Leds + GPIO_CNTR1, GPIO_CNTR1_NP, // 4 x Counter + GPIO_PWM1, GPIO_PWM1_INV, // 5 x PWM + GPIO_BUZZER, GPIO_BUZZER_INV, // Buzzer + GPIO_LEDLNK, GPIO_LEDLNK_INV, // Link led + GPIO_I2C_SCL, GPIO_I2C_SDA, // Software I2C + GPIO_SPI_MISO, GPIO_SPI_MOSI, GPIO_SPI_CLK, GPIO_SPI_CS, GPIO_SPI_DC, // Hardware SPI + GPIO_SSPI_MISO, GPIO_SSPI_MOSI, GPIO_SSPI_SCLK, GPIO_SSPI_CS, GPIO_SSPI_DC, // Software SPI + GPIO_BACKLIGHT, // Display backlight control + GPIO_OLED_RESET, // OLED Display Reset + GPIO_IRSEND, GPIO_IRRECV, // IR interface + GPIO_RFSEND, GPIO_RFRECV, // RF interface + GPIO_DHT11, GPIO_DHT22, GPIO_SI7021, GPIO_DHT11_OUT, // DHT11, DHT21, DHT22, AM2301, AM2302, AM2321 + GPIO_DSB, GPIO_DSB_OUT, // DS18B20 or DS18S20 + GPIO_WS2812, // WS2812 Led string + GPIO_MHZ_TXD, GPIO_MHZ_RXD, // MH-Z19 Serial interface + GPIO_PZEM0XX_TX, GPIO_PZEM004_RX, GPIO_PZEM016_RX, GPIO_PZEM017_RX, // PZEM Serial Modbus interface + GPIO_SAIR_TX, GPIO_SAIR_RX, // SenseAir Serial interface + GPIO_PMS5003_TX, GPIO_PMS5003_RX, // Plantower PMS5003 Serial interface + GPIO_SDS0X1_TX, GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface + GPIO_SBR_TX, GPIO_SBR_RX, // Serial Bridge Serial interface + GPIO_SR04_TRIG, GPIO_SR04_ECHO, // SR04 interface + GPIO_SDM120_TX, GPIO_SDM120_RX, // SDM120 Serial interface + GPIO_SDM630_TX, GPIO_SDM630_RX, // SDM630 Serial interface + GPIO_TM16CLK, GPIO_TM16DIO, GPIO_TM16STB, // TM1638 interface + GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player + GPIO_HX711_SCK, GPIO_HX711_DAT, // HX711 Load Cell interface + GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin + GPIO_TUYA_TX, GPIO_TUYA_RX, // Tuya Serial interface + GPIO_MGC3130_XFER, GPIO_MGC3130_RESET, // MGC3130 interface + GPIO_RF_SENSOR, // Rf receiver with sensor decoding + GPIO_AZ_TXD, GPIO_AZ_RXD, // AZ-Instrument 7798 Serial interface + GPIO_MAX31855CS, GPIO_MAX31855CLK, GPIO_MAX31855DO, // MAX31855 Serial interface + GPIO_NRG_SEL, GPIO_NRG_SEL_INV, GPIO_NRG_CF1, GPIO_HLW_CF, GPIO_HJL_CF, // HLW8012/HJL-01/BL0937 energy monitoring + GPIO_MCP39F5_TX, GPIO_MCP39F5_RX, GPIO_MCP39F5_RST, // MCP39F501 Energy monitoring (Shelly2) + GPIO_PN532_TXD, GPIO_PN532_RXD, // PN532 NFC Serial interface + GPIO_SM16716_CLK, GPIO_SM16716_DAT, GPIO_SM16716_SEL, // SM16716 SELECT + GPIO_DI, GPIO_DCKI, // my92x1 PWM controller + GPIO_CSE7766_TX, GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) + GPIO_ARIRFRCV, GPIO_ARIRFSEL, // Arilux RF Receive input + GPIO_TXD, GPIO_RXD, // Serial interface + GPIO_ROT1A, GPIO_ROT1B, GPIO_ROT2A, GPIO_ROT2B, // Rotary switch + GPIO_HRE_CLOCK, GPIO_HRE_DATA, // HR-E Water Meter + GPIO_ADE7953_IRQ, // ADE7953 IRQ + GPIO_SOLAXX1_TX, GPIO_SOLAXX1_RX, // Solax Inverter Serial interface + GPIO_ZIGBEE_TX, GPIO_ZIGBEE_RX, // Zigbee Serial interface + GPIO_RDM6300_RX, // RDM6300 RX + GPIO_IBEACON_TX, GPIO_IBEACON_RX, // HM17 IBEACON Serial interface + GPIO_A4988_DIR, GPIO_A4988_STP, GPIO_A4988_ENA, // A4988 interface + GPIO_A4988_MS1, GPIO_A4988_MS2, GPIO_A4988_MS3, // A4988 microstep + GPIO_DDS2382_TX, GPIO_DDS2382_RX, // DDS2382 Serial interface + GPIO_DDSU666_TX, GPIO_DDSU666_RX, // DDSU666 Serial interface + GPIO_SM2135_CLK, GPIO_SM2135_DAT, // SM2135 PWM controller + GPIO_DEEPSLEEP, // Kill switch for deepsleep + GPIO_EXS_ENABLE, // EXS MCU Enable + GPIO_TASMOTASLAVE_TXD, GPIO_TASMOTASLAVE_RXD, // Slave Serial interface + GPIO_TASMOTASLAVE_RST, GPIO_TASMOTASLAVE_RST_INV, // Slave Reset + GPIO_HPMA_RX, GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface + GPIO_GPS_RX, GPIO_GPS_TX, // GPS Serial interface + GPIO_HM10_RX, GPIO_HM10_TX, // HM10-BLE-Mijia-bridge Serial interface + GPIO_LE01MR_RX, GPIO_LE01MR_TX, // F&F LE-01MR energy meter + GPIO_CC1101_GDO0, GPIO_CC1101_GDO2, // CC1101 Serial interface + GPIO_HRXL_RX, // Data from MaxBotix HRXL sonar range sensor + GPIO_ELECTRIQ_MOODL_TX, // ElectriQ iQ-wifiMOODL Serial TX + GPIO_AS3935, + ADC0_INPUT, // Analog input + ADC0_TEMP, // Analog Thermistor + ADC0_LIGHT, // Analog Light sensor + ADC0_BUTTON, ADC0_BUTTON_INV, // Analog Button + ADC0_RANGE, // Analog Range + ADC0_CT_POWER, // ANalog Current + GPIO_WEBCAM_PWDN, GPIO_WEBCAM_RESET, GPIO_WEBCAM_XCLK, // Webcam + GPIO_WEBCAM_SIOD, GPIO_WEBCAM_SIOC, // Webcam I2C + GPIO_WEBCAM_DATA, + GPIO_WEBCAM_VSYNC, GPIO_WEBCAM_HREF, GPIO_WEBCAM_PCLK, + GPIO_WEBCAM_PSCLK, + GPIO_WEBCAM_HSD, + GPIO_WEBCAM_PSRCS, + GPIO_BOILER_OT_RX, GPIO_BOILER_OT_TX, // OpenTherm Boiler TX pin + GPIO_WINDMETER_SPEED, // WindMeter speed counter pin + GPIO_SENSOR_END }; + +enum ProgramSelectablePins { +// GPIO_FIX_START = 254, + GPIO_FIX_START = 2046, + GPIO_USER, // User configurable needs to be 2047 + GPIO_MAX }; + +// Text in webpage Module Parameters and commands GPIOS and GPIO +const char kSensorNames[] PROGMEM = + D_SENSOR_NONE "|" + D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "_n|" D_SENSOR_BUTTON "_i|" D_SENSOR_BUTTON "_in|" + D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "_n|" + D_SENSOR_RELAY "|" D_SENSOR_RELAY "_i|" + D_SENSOR_LED "|" D_SENSOR_LED "_i|" + D_SENSOR_COUNTER "|" D_SENSOR_COUNTER "_n|" + D_SENSOR_PWM "|" D_SENSOR_PWM "_i|" + D_SENSOR_BUZZER "|" D_SENSOR_BUZZER "_i|" + D_SENSOR_LED_LINK "|" D_SENSOR_LED_LINK "_i|" + D_SENSOR_I2C_SCL "|" D_SENSOR_I2C_SDA "|" + D_SENSOR_SPI_MISO "|" D_SENSOR_SPI_MOSI "|" D_SENSOR_SPI_CLK "|" D_SENSOR_SPI_CS "|" D_SENSOR_SPI_DC "|" + D_SENSOR_SSPI_MISO "|" D_SENSOR_SSPI_MOSI "|" D_SENSOR_SSPI_SCLK "|" D_SENSOR_SSPI_CS "|" D_SENSOR_SSPI_DC "|" + D_SENSOR_BACKLIGHT "|" D_SENSOR_OLED_RESET "|" + D_SENSOR_IRSEND "|" D_SENSOR_IRRECV "|" + D_SENSOR_RFSEND "|" D_SENSOR_RFRECV "|" + D_SENSOR_DHT11 "|" D_SENSOR_AM2301 "|" D_SENSOR_SI7021 "|" D_SENSOR_DHT11 "_o|" + D_SENSOR_DS18X20 "|" D_SENSOR_DS18X20 "_o|" + D_SENSOR_WS2812 "|" + D_SENSOR_MHZ_TX "|" D_SENSOR_MHZ_RX "|" + D_SENSOR_PZEM0XX_TX "|" D_SENSOR_PZEM004_RX "|" D_SENSOR_PZEM016_RX "|" D_SENSOR_PZEM017_RX "|" + D_SENSOR_SAIR_TX "|" D_SENSOR_SAIR_RX "|" + D_SENSOR_PMS5003_TX "|" D_SENSOR_PMS5003_RX "|" + D_SENSOR_SDS0X1_TX "|" D_SENSOR_SDS0X1_RX "|" + D_SENSOR_SBR_TX "|" D_SENSOR_SBR_RX "|" + D_SENSOR_SR04_TRIG "|" D_SENSOR_SR04_ECHO "|" + D_SENSOR_SDM120_TX "|" D_SENSOR_SDM120_RX "|" + D_SENSOR_SDM630_TX "|" D_SENSOR_SDM630_RX "|" + D_SENSOR_TM1638_CLK "|" D_SENSOR_TM1638_DIO "|" D_SENSOR_TM1638_STB "|" + D_SENSOR_DFR562 "|" + D_SENSOR_HX711_SCK "|" D_SENSOR_HX711_DAT "|" + D_SENSOR_TX2X_TX "|" + D_SENSOR_TUYA_TX "|" D_SENSOR_TUYA_RX "|" + D_SENSOR_MGC3130_XFER "|" D_SENSOR_MGC3130_RESET "|" + D_SENSOR_RF_SENSOR "|" + D_SENSOR_AZ_TX "|" D_SENSOR_AZ_RX "|" + D_SENSOR_MAX31855_CS "|" D_SENSOR_MAX31855_CLK "|" D_SENSOR_MAX31855_DO "|" + D_SENSOR_NRG_SEL "|" D_SENSOR_NRG_SEL "_i|" D_SENSOR_NRG_CF1 "|" D_SENSOR_HLW_CF "|" D_SENSOR_HJL_CF "|" + D_SENSOR_MCP39F5_TX "|" D_SENSOR_MCP39F5_RX "|" D_SENSOR_MCP39F5_RST "|" + D_SENSOR_PN532_TX "|" D_SENSOR_PN532_RX "|" + D_SENSOR_SM16716_CLK "|" D_SENSOR_SM16716_DAT "|" D_SENSOR_SM16716_POWER "|" + D_SENSOR_MY92X1_DI "|" D_SENSOR_MY92X1_DCKI "|" + D_SENSOR_CSE7766_TX "|" D_SENSOR_CSE7766_RX "|" + D_SENSOR_ARIRFRCV "|" D_SENSOR_ARIRFSEL "|" + D_SENSOR_TXD "|" D_SENSOR_RXD "|" + D_SENSOR_ROTARY "_1a|" D_SENSOR_ROTARY "_1b|" D_SENSOR_ROTARY "_2a|" D_SENSOR_ROTARY "_2b|" + D_SENSOR_HRE_CLOCK "|" D_SENSOR_HRE_DATA "|" + D_SENSOR_ADE7953_IRQ "|" + D_SENSOR_SOLAXX1_TX "|" D_SENSOR_SOLAXX1_RX "|" + D_SENSOR_ZIGBEE_TXD "|" D_SENSOR_ZIGBEE_RXD "|" + D_SENSOR_RDM6300_RX "|" + D_SENSOR_IBEACON_TX "|" D_SENSOR_IBEACON_RX "|" + D_SENSOR_A4988_DIR "|" D_SENSOR_A4988_STP "|" D_SENSOR_A4988_ENA "|" D_SENSOR_A4988_MS1 "|" D_SENSOR_A4988_MS2 "|" D_SENSOR_A4988_MS3 "|" + D_SENSOR_DDS2382_TX "|" D_SENSOR_DDS2382_RX "|" + D_SENSOR_DDSU666_TX "|" D_SENSOR_DDSU666_RX "|" + D_SENSOR_SM2135_CLK "|" D_SENSOR_SM2135_DAT "|" + D_SENSOR_DEEPSLEEP "|" D_SENSOR_EXS_ENABLE "|" + D_SENSOR_SLAVE_TX "|" D_SENSOR_SLAVE_RX "|" D_SENSOR_SLAVE_RESET "|" D_SENSOR_SLAVE_RESET "_i|" + D_SENSOR_HPMA_RX "|" D_SENSOR_HPMA_TX "|" + D_SENSOR_GPS_RX "|" D_SENSOR_GPS_TX "|" + D_SENSOR_HM10_RX "|" D_SENSOR_HM10_TX "|" + D_SENSOR_LE01MR_RX "|" D_SENSOR_LE01MR_TX "|" + D_SENSOR_CC1101_GDO0 "|" D_SENSOR_CC1101_GDO2 "|" + D_SENSOR_HRXL_RX "|" + D_SENSOR_ELECTRIQ_MOODL "|" + D_SENSOR_AS3935 "|" + D_ANALOG_INPUT "|" + D_TEMPERATURE "|" D_LIGHT "|" + D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "_i|" + D_RANGE "|" + D_CT_POWER "|" + D_GPIO_WEBCAM_PWDN "|" D_GPIO_WEBCAM_RESET "|" D_GPIO_WEBCAM_XCLK "|" + D_GPIO_WEBCAM_SIOD "|" D_GPIO_WEBCAM_SIOC "|" + D_GPIO_WEBCAM_DATA "|" + D_GPIO_WEBCAM_VSYNC "|" D_GPIO_WEBCAM_HREF "|" D_GPIO_WEBCAM_PCLK "|" + D_GPIO_WEBCAM_PSCLK "|" + D_GPIO_WEBCAM_HSD "|" + D_GPIO_WEBCAM_PSRCS "|" + D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX "|" + D_SENSOR_WINDMETER_SPEED + ; + +const char kSensorNamesFixed[] PROGMEM = + D_SENSOR_USER; + +#define MAX_WEBCAM_DATA 8 +#define MAX_WEBCAM_HSD 3 + +const uint16_t kGpioNiceList[] PROGMEM = { + GPIO_NONE, // Not used + AGPIO(GPIO_KEY1) + MAX_KEYS, // Buttons + AGPIO(GPIO_KEY1_NP) + MAX_KEYS, + AGPIO(GPIO_KEY1_INV) + MAX_KEYS, + AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS, + AGPIO(GPIO_SWT1) + MAX_SWITCHES, // User connected external switches + AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES, + AGPIO(GPIO_REL1) + MAX_RELAYS, // Relays + AGPIO(GPIO_REL1_INV) + MAX_RELAYS, + AGPIO(GPIO_LED1) + MAX_LEDS, // Leds + AGPIO(GPIO_LED1_INV) + MAX_LEDS, +#ifdef USE_COUNTER + AGPIO(GPIO_CNTR1) + MAX_COUNTERS, // Counters + AGPIO(GPIO_CNTR1_NP) + MAX_COUNTERS, +#endif + AGPIO(GPIO_PWM1) + MAX_PWMS, // RGB Red or C Cold White + AGPIO(GPIO_PWM1_INV) + MAX_PWMS, +#ifdef USE_BUZZER + AGPIO(GPIO_BUZZER), // Buzzer + AGPIO(GPIO_BUZZER_INV), // Inverted buzzer +#endif + AGPIO(GPIO_LEDLNK), // Link led + AGPIO(GPIO_LEDLNK_INV), // Inverted link led +#ifdef USE_I2C + AGPIO(GPIO_I2C_SCL), // I2C SCL + AGPIO(GPIO_I2C_SDA), // I2C SDA +#endif +#ifdef USE_SPI + AGPIO(GPIO_SPI_MISO), // SPI MISO + AGPIO(GPIO_SPI_MOSI), // SPI MOSI + AGPIO(GPIO_SPI_CLK), // SPI Clk + AGPIO(GPIO_SPI_CS), // SPI Chip Select + AGPIO(GPIO_SPI_DC), // SPI Data Direction + AGPIO(GPIO_SSPI_MISO), // Software SPI Master Input Slave Output + AGPIO(GPIO_SSPI_MOSI), // Software SPI Master Output Slave Input + AGPIO(GPIO_SSPI_SCLK), // Software SPI Serial Clock + AGPIO(GPIO_SSPI_CS), // Software SPI Chip Select + AGPIO(GPIO_SSPI_DC), // Software SPI Data or Command +#endif +#ifdef USE_DISPLAY + AGPIO(GPIO_BACKLIGHT), // Display backlight control + AGPIO(GPIO_OLED_RESET), // OLED Display Reset +#endif + + AGPIO(GPIO_TXD), // Serial interface + AGPIO(GPIO_RXD), // Serial interface + +#ifdef USE_DHT + AGPIO(GPIO_DHT11), // DHT11 + AGPIO(GPIO_DHT22), // DHT21, DHT22, AM2301, AM2302, AM2321 + AGPIO(GPIO_SI7021), // iTead SI7021 + AGPIO(GPIO_DHT11_OUT), // Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321 +#endif +#ifdef USE_DS18x20 + AGPIO(GPIO_DSB), // Single wire DS18B20 or DS18S20 + AGPIO(GPIO_DSB_OUT), // Pseudo Single wire DS18B20 or DS18S20 +#endif + +// Light +#ifdef USE_LIGHT +#ifdef USE_WS2812 + AGPIO(GPIO_WS2812), // WS2812 Led string +#endif +#ifdef USE_ARILUX_RF + AGPIO(GPIO_ARIRFRCV), // AriLux RF Receive input + AGPIO(GPIO_ARIRFSEL), // Arilux RF Receive input selected +#endif +#ifdef USE_MY92X1 + AGPIO(GPIO_DI), // my92x1 PWM input + AGPIO(GPIO_DCKI), // my92x1 CLK input +#endif // USE_MY92X1 +#ifdef USE_SM16716 + AGPIO(GPIO_SM16716_CLK), // SM16716 CLOCK + AGPIO(GPIO_SM16716_DAT), // SM16716 DATA + AGPIO(GPIO_SM16716_SEL), // SM16716 SELECT +#endif // USE_SM16716 +#ifdef USE_SM2135 + AGPIO(GPIO_SM2135_CLK), // SM2135 CLOCK + AGPIO(GPIO_SM2135_DAT), // SM2135 DATA +#endif // USE_SM2135 +#ifdef USE_TUYA_MCU + AGPIO(GPIO_TUYA_TX), // Tuya Serial interface + AGPIO(GPIO_TUYA_RX), // Tuya Serial interface +#endif +#ifdef USE_EXS_DIMMER + AGPIO(GPIO_EXS_ENABLE), // EXS MCU Enable +#endif +#ifdef USE_ELECTRIQ_MOODL + AGPIO(GPIO_ELECTRIQ_MOODL_TX), +#endif +#endif // USE_LIGHT + +#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) + AGPIO(GPIO_IRSEND), // IR remote +#if defined(USE_IR_RECEIVE) || defined(USE_IR_REMOTE_FULL) + AGPIO(GPIO_IRRECV), // IR receiver +#endif +#endif + +#ifdef USE_RC_SWITCH + AGPIO(GPIO_RFSEND), // RF transmitter + AGPIO(GPIO_RFRECV), // RF receiver +#endif +#ifdef USE_RF_SENSOR + AGPIO(GPIO_RF_SENSOR), // Rf receiver with sensor decoding +#endif +#ifdef USE_SR04 + AGPIO(GPIO_SR04_TRIG), // SR04 Tri/TXgger pin + AGPIO(GPIO_SR04_ECHO), // SR04 Ech/RXo pin +#endif +#ifdef USE_TM1638 + AGPIO(GPIO_TM16CLK), // TM1638 Clock + AGPIO(GPIO_TM16DIO), // TM1638 Data I/O + AGPIO(GPIO_TM16STB), // TM1638 Strobe +#endif +#ifdef USE_HX711 + AGPIO(GPIO_HX711_SCK), // HX711 Load Cell clock + AGPIO(GPIO_HX711_DAT), // HX711 Load Cell data +#endif + +// Energy sensors +#ifdef USE_ENERGY_SENSOR +#ifdef USE_HLW8012 + AGPIO(GPIO_NRG_SEL), // HLW8012/HLJ-01 Sel output (1 = Voltage) + AGPIO(GPIO_NRG_SEL_INV), // HLW8012/HLJ-01 Sel output (0 = Voltage) + AGPIO(GPIO_NRG_CF1), // HLW8012/HLJ-01 CF1 voltage / current + AGPIO(GPIO_HLW_CF), // HLW8012 CF power + AGPIO(GPIO_HJL_CF), // HJL-01/BL0937 CF power +#endif +#if defined(USE_I2C) && defined(USE_ADE7953) + AGPIO(GPIO_ADE7953_IRQ), // ADE7953 IRQ +#endif +#ifdef USE_CSE7766 + AGPIO(GPIO_CSE7766_TX), // CSE7766 Serial interface (S31 and Pow R2) + AGPIO(GPIO_CSE7766_RX), // CSE7766 Serial interface (S31 and Pow R2) +#endif +#ifdef USE_MCP39F501 + AGPIO(GPIO_MCP39F5_TX), // MCP39F501 Serial interface (Shelly2) + AGPIO(GPIO_MCP39F5_RX), // MCP39F501 Serial interface (Shelly2) + AGPIO(GPIO_MCP39F5_RST), // MCP39F501 Reset (Shelly2) +#endif +#if defined(USE_PZEM004T) || defined(USE_PZEM_AC) || defined(USE_PZEM_DC) + AGPIO(GPIO_PZEM0XX_TX), // PZEM0XX Serial interface +#endif +#ifdef USE_PZEM004T + AGPIO(GPIO_PZEM004_RX), // PZEM004T Serial interface +#endif +#ifdef USE_PZEM_AC + AGPIO(GPIO_PZEM016_RX), // PZEM-014,016 Serial Modbus interface +#endif +#ifdef USE_PZEM_DC + AGPIO(GPIO_PZEM017_RX), // PZEM-003,017 Serial Modbus interface +#endif +#ifdef USE_SDM120 + AGPIO(GPIO_SDM120_TX), // SDM120 Serial interface + AGPIO(GPIO_SDM120_RX), // SDM120 Serial interface +#endif +#ifdef USE_SDM630 + AGPIO(GPIO_SDM630_TX), // SDM630 Serial interface + AGPIO(GPIO_SDM630_RX), // SDM630 Serial interface +#endif +#ifdef USE_DDS2382 + AGPIO(GPIO_DDS2382_TX), // DDS2382 Serial interface + AGPIO(GPIO_DDS2382_RX), // DDS2382 Serial interface +#endif +#ifdef USE_DDSU666 + AGPIO(GPIO_DDSU666_TX), // DDSU666 Serial interface + AGPIO(GPIO_DDSU666_RX), // DDSU666 Serial interface +#endif // USE_DDSU666 +#ifdef USE_SOLAX_X1 + AGPIO(GPIO_SOLAXX1_TX), // Solax Inverter tx pin + AGPIO(GPIO_SOLAXX1_RX), // Solax Inverter rx pin +#endif // USE_SOLAX_X1 +#ifdef USE_LE01MR + AGPIO(GPIO_LE01MR_TX), // F7F LE-01MR energy meter tx pin + AGPIO(GPIO_LE01MR_RX), // F7F LE-01MR energy meter rx pin +#endif // IFDEF:USE_LE01MR +#endif // USE_ENERGY_SENSOR + +// Serial +#ifdef USE_SERIAL_BRIDGE + AGPIO(GPIO_SBR_TX), // Serial Bridge Serial interface + AGPIO(GPIO_SBR_RX), // Serial Bridge Serial interface +#endif +#ifdef USE_ZIGBEE + AGPIO(GPIO_ZIGBEE_TX), // Zigbee Serial interface + AGPIO(GPIO_ZIGBEE_RX), // Zigbee Serial interface +#endif +#ifdef USE_MHZ19 + AGPIO(GPIO_MHZ_TXD), // MH-Z19 Serial interface + AGPIO(GPIO_MHZ_RXD), // MH-Z19 Serial interface +#endif +#ifdef USE_SENSEAIR + AGPIO(GPIO_SAIR_TX), // SenseAir Serial interface + AGPIO(GPIO_SAIR_RX), // SenseAir Serial interface +#endif +#ifdef USE_NOVA_SDS + AGPIO(GPIO_SDS0X1_TX), // Nova Fitness SDS011 Serial interface + AGPIO(GPIO_SDS0X1_RX), // Nova Fitness SDS011 Serial interface +#endif +#ifdef USE_HPMA + AGPIO(GPIO_HPMA_TX), // Honeywell HPMA115S0 Serial interface + AGPIO(GPIO_HPMA_RX), // Honeywell HPMA115S0 Serial interface +#endif +#ifdef USE_PMS5003 + AGPIO(GPIO_PMS5003_TX), // Plantower PMS5003 Serial interface + AGPIO(GPIO_PMS5003_RX), // Plantower PMS5003 Serial interface +#endif +#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) + AGPIO(GPIO_TX2X_TXD_BLACK), // TX20/TX23 Transmission Pin +#endif +#ifdef USE_WINDMETER + GPIO_WINDMETER_SPEED, +#endif +#ifdef USE_MP3_PLAYER + AGPIO(GPIO_MP3_DFR562), // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface +#endif +#ifdef USE_AZ7798 + AGPIO(GPIO_AZ_TXD), // AZ-Instrument 7798 CO2 datalogger Serial interface + AGPIO(GPIO_AZ_RXD), // AZ-Instrument 7798 CO2 datalogger Serial interface +#endif +#ifdef USE_PN532_HSU + AGPIO(GPIO_PN532_TXD), // PN532 HSU Tx + AGPIO(GPIO_PN532_RXD), // PN532 HSU Rx +#endif +#ifdef USE_TASMOTA_SLAVE + AGPIO(GPIO_TASMOTASLAVE_TXD), // Tasmota Slave TX + AGPIO(GPIO_TASMOTASLAVE_RXD), // Tasmota Slave RX + AGPIO(GPIO_TASMOTASLAVE_RST), // Tasmota Slave Reset + AGPIO(GPIO_TASMOTASLAVE_RST_INV), // Tasmota Slave Reset Inverted +#endif +#ifdef USE_RDM6300 + AGPIO(GPIO_RDM6300_RX), +#endif +#ifdef USE_IBEACON + AGPIO(GPIO_IBEACON_TX), + AGPIO(GPIO_IBEACON_RX), +#endif +#ifdef USE_GPS + AGPIO(GPIO_GPS_TX), // GPS serial interface + AGPIO(GPIO_GPS_RX), // GPS serial interface +#endif +#ifdef USE_HM10 + AGPIO(GPIO_HM10_TX), // GPS serial interface + AGPIO(GPIO_HM10_RX), // GPS serial interface +#endif +#ifdef USE_OPENTHERM + GPIO_BOILER_OT_TX, + GPIO_BOILER_OT_RX, +#endif + +#ifdef USE_MGC3130 + AGPIO(GPIO_MGC3130_XFER), + AGPIO(GPIO_MGC3130_RESET), +#endif +#ifdef USE_MAX31855 + AGPIO(GPIO_MAX31855CS), // MAX31855 Serial interface + AGPIO(GPIO_MAX31855CLK), // MAX31855 Serial interface + AGPIO(GPIO_MAX31855DO), // MAX31855 Serial interface +#endif +#ifdef ROTARY_V1 + AGPIO(GPIO_ROT1A), // Rotary switch1 A Pin + AGPIO(GPIO_ROT1B), // Rotary switch1 B Pin + AGPIO(GPIO_ROT2A), // Rotary switch2 A Pin + AGPIO(GPIO_ROT2B), // Rotary switch2 B Pin +#endif +#ifdef USE_HRE + AGPIO(GPIO_HRE_CLOCK), + AGPIO(GPIO_HRE_DATA), +#endif +#ifdef USE_A4988_STEPPER + AGPIO(GPIO_A4988_DIR), // A4988 direction pin + AGPIO(GPIO_A4988_STP), // A4988 step pin + // folowing are not mandatory + AGPIO(GPIO_A4988_ENA), // A4988 enabled pin + AGPIO(GPIO_A4988_MS1), // A4988 microstep pin1 + AGPIO(GPIO_A4988_MS2), // A4988 microstep pin2 + AGPIO(GPIO_A4988_MS3), // A4988 microstep pin3 +#endif +#ifdef USE_DEEPSLEEP + AGPIO(GPIO_DEEPSLEEP), +#endif +#ifdef USE_KEELOQ + AGPIO(GPIO_CC1101_GDO0), // CC1101 pin for RX + AGPIO(GPIO_CC1101_GDO2), // CC1101 pin for RX +#endif +#ifdef USE_HRXL + AGPIO(GPIO_HRXL_RX), +#endif +#ifdef USE_AS3935 + AGPIO(GPIO_AS3935), +#endif +/* +#ifndef USE_ADC_VCC + AGPIO(ADC0_INPUT), // Analog input + AGPIO(ADC0_TEMP), // Thermistor + AGPIO(ADC0_LIGHT), // Light sensor + AGPIO(ADC0_BUTTON), // Button + AGPIO(ADC0_BUTTON_INV), + AGPIO(ADC0_RANGE), // Range + AGPIO(ADC0_CT_POWER), // Current +#endif +*/ +#ifdef USE_WEBCAM + AGPIO(GPIO_WEBCAM_PWDN), + AGPIO(GPIO_WEBCAM_RESET), + AGPIO(GPIO_WEBCAM_XCLK), + AGPIO(GPIO_WEBCAM_SIOD), + AGPIO(GPIO_WEBCAM_SIOC), + AGPIO(GPIO_WEBCAM_DATA) + MAX_WEBCAM_DATA, + AGPIO(GPIO_WEBCAM_VSYNC), + AGPIO(GPIO_WEBCAM_HREF), + AGPIO(GPIO_WEBCAM_PCLK), + AGPIO(GPIO_WEBCAM_PSCLK), + AGPIO(GPIO_WEBCAM_HSD) + MAX_WEBCAM_HSD, + AGPIO(GPIO_WEBCAM_PSRCS), +#endif +}; + +//******************************************************************************************** + +#define MAX_GPIO_PIN 40 // Number of supported GPIO +#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) +#define MAX_USER_PINS 36 // MAX_GPIO_PIN - MIN_FLASH_PINS +#define WEMOS_MODULE 0 // Wemos module + +// 0 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839 +const char PINS_WEMOS[] PROGMEM = "IOTXIORXIOIOflashcFLFLolIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOA6A7A0IoIoA3"; + +//******************************************************************************************** + +typedef struct MYIO { + uint16_t io[MAX_GPIO_PIN]; +} myio; // 40 * 2 = 80 bytes + +typedef struct MYCFGIO { + uint16_t io[MAX_USER_PINS]; +} mycfgio; // 36 * 2 = 72 bytes + +#define GPIO_FLAG_USED 0 // Currently no flags used + +typedef union { + uint16_t data; + struct { + uint16_t spare00 : 1; + uint16_t spare01 : 1; + uint16_t spare02 : 1; + uint16_t spare03 : 1; + uint16_t spare04 : 1; + uint16_t spare05 : 1; + uint16_t spare06 : 1; + uint16_t spare07 : 1; + uint16_t spare08 : 1; + uint16_t spare09 : 1; + uint16_t spare10 : 1; + uint16_t spare11 : 1; + uint16_t spare12 : 1; + uint16_t spare13 : 1; + uint16_t spare14 : 1; + uint16_t spare15 : 1; + }; +} gpio_flag; // 2 bytes + +typedef struct MYTMPLT { + mycfgio gp; // 72 bytes + gpio_flag flag; // 2 bytes +} mytmplt; // 74 bytes + +/********************************************************************************************/ +// Supported hardware modules +enum SupportedModules { + WEMOS, ESP32_CAM_AITHINKER, + MAXMODULE}; + +#define USER_MODULE 255 + +const char kModuleNames[] PROGMEM = + "ESP32-DevKit|ESP32 Cam AiThinker"; + +// Default module settings +const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { + WEMOS, + ESP32_CAM_AITHINKER +}; + +const mytmplt kModules PROGMEM = +{ // WEMOS - Espressif ESP32-DevKitC - Any ESP32 device like WeMos and NodeMCU hardware (ESP32) + AGPIO(GPIO_USER), // 0 (I)O GPIO0, ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1, EMAC_TX_CLK + AGPIO(GPIO_USER), // 1 IO TXD0 GPIO1, U0TXD, CLK_OUT3, EMAC_RXD2 + AGPIO(GPIO_USER), // 2 IO GPIO2, ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, HS2_DATA0, SD_DATA0 + AGPIO(GPIO_USER), // 3 IO RXD0 GPIO3, U0RXD, CLK_OUT2 + AGPIO(GPIO_USER), // 4 IO GPIO4, ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, HS2_DATA1, SD_DATA1, EMAC_TX_ER + AGPIO(GPIO_USER), // 5 IO GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK + // 6 IO GPIO6, Flash CLK + // 7 IO GPIO7, Flash D0 + // 8 IO GPIO8, Flash D1 + AGPIO(GPIO_USER), // 9 IO GPIO9, Flash D2, U1RXD + AGPIO(GPIO_USER), // 10 IO GPIO10, Flash D3, U1TXD + // 11 IO GPIO11, Flash CMD + AGPIO(GPIO_USER), // 12 (I)O GPIO12, ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI, HSPIQ, HS2_DATA2, SD_DATA2, EMAC_TXD3 (If driven High, flash voltage (VDD_SDIO) is 1.8V not default 3.3V. Has internal pull-down, so unconnected = Low = 3.3V. May prevent flashing and/or booting if 3.3V flash is connected and pulled high. See ESP32 datasheet for more details.) + AGPIO(GPIO_USER), // 13 IO GPIO13, ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID, HS2_DATA3, SD_DATA3, EMAC_RX_ER + AGPIO(GPIO_USER), // 14 IO GPIO14, ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, HS2_CLK, SD_CLK, EMAC_TXD2 + AGPIO(GPIO_USER), // 15 (I)O GPIO15, ADC2_CH3, TOUCH3, MTDO, HSPICS0, RTC_GPIO13, HS2_CMD, SD_CMD, EMAC_RXD3 (If driven Low, silences boot messages from normal boot. Has internal pull-up, so unconnected = High = normal output.) + AGPIO(GPIO_USER), // 16 IO GPIO16, HS1_DATA4, U2RXD, EMAC_CLK_OUT + AGPIO(GPIO_USER), // 17 IO GPIO17, HS1_DATA5, U2TXD, EMAC_CLK_OUT_180 + AGPIO(GPIO_USER), // 18 IO GPIO18, VSPICLK, HS1_DATA7 + AGPIO(GPIO_USER), // 19 IO GPIO19, VSPIQ, U0CTS, EMAC_TXD0 + 0, // 20 + AGPIO(GPIO_USER), // 21 IO GPIO21, VSPIHD, EMAC_TX_EN + AGPIO(GPIO_USER), // 22 IO LED GPIO22, VSPIWP, U0RTS, EMAC_TXD1 + AGPIO(GPIO_USER), // 23 IO GPIO23, VSPID, HS1_STROBE + 0, // 24 + AGPIO(GPIO_USER), // 25 IO GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0 + AGPIO(GPIO_USER), // 26 IO GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1 + AGPIO(GPIO_USER), // 27 IO GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17, EMAC_RX_DV + 0, // 28 + 0, // 29 + 0, // 30 + 0, // 31 + AGPIO(GPIO_USER), // 32 IO GPIO32, XTAL_32K_P (32.768 kHz crystal oscillator input), ADC1_CH4, TOUCH9, RTC_GPIO9 + AGPIO(GPIO_USER), // 33 IO GPIO33, XTAL_32K_N (32.768 kHz crystal oscillator output), ADC1_CH5, TOUCH8, RTC_GPIO8 + AGPIO(GPIO_USER), // 34 I NO PULLUP GPIO34, ADC1_CH6, RTC_GPIO4 + AGPIO(GPIO_USER), // 35 I NO PULLUP GPIO35, ADC1_CH7, RTC_GPIO5 + AGPIO(GPIO_USER), // 36 I NO PULLUP GPIO36, SENSOR_VP, ADC_H, ADC1_CH0, RTC_GPIO0 + 0, // 37 NO PULLUP + 0, // 38 NO PULLUP + AGPIO(GPIO_USER), // 39 I NO PULLUP GPIO39, SENSOR_VN, ADC1_CH3, ADC_H, RTC_GPIO3 + 0 // Flag +}; + +#endif // ESP32 + +#endif // _TASMOTA_TEMPLATE_ESP32_H_ diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 0a3b9aa5e..8bf2e98a9 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,7 +20,7 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x08020000; +const uint32_t VERSION = 0x08030000; // Lowest compatible version const uint32_t VERSION_COMPATIBLE = 0x07010006; diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 7f55a01fd..75f309569 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -31,7 +31,7 @@ #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by WifiManager web GUI #endif -const uint16_t CHUNKED_BUFFER_SIZE = 400; // Chunk buffer size (should be smaller than half mqtt_date size = MESSZ) +const uint16_t CHUNKED_BUFFER_SIZE = (MESSZ / 2) - 100; // Chunk buffer size (should be smaller than half mqtt_data size = MESSZ) const uint16_t HTTP_REFRESH_TIME = 2345; // milliseconds #define HTTP_RESTART_RECONNECT_TIME 9000 // milliseconds @@ -48,7 +48,7 @@ enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_TASMOTASLAVE }; static const char * HEADER_KEYS[] = { "User-Agent", }; -const char HTTP_HEADER[] PROGMEM = +const char HTTP_HEADER1[] PROGMEM = "" "" "" @@ -109,7 +109,7 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = "}" "};" "if (rfsh) {" - "x.open('GET','.?m=1'+a,true);" // ?m related to WebServer->hasArg("m") + "x.open('GET','.?m=1'+a,true);" // ?m related to Webserver->hasArg("m") "x.send();" "lt=setTimeout(la,%d);" // Settings.web_refresh "}" @@ -146,7 +146,7 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = "eb('l1').innerHTML=s;" "}" "};" - "x.open('GET','.?m=1'+a,true);" // ?m related to WebServer->hasArg("m") + "x.open('GET','.?m=1'+a,true);" // ?m related to Webserver->hasArg("m") "x.send();" "lt=setTimeout(la,%d);" // Settings.web_refresh "}"; @@ -184,7 +184,7 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM = "clearTimeout(lt);" "t=eb('t1');" "if(p==1){" - "c=eb('c1');" + "c=eb('c1');" // Console command id "o='&c1='+encodeURIComponent(c.value);" "c.value='';" "t.scrollTop=99999;" @@ -205,24 +205,71 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM = "sn=t.scrollTop;" "}" "};" - "x.open('GET','cs?c2='+id+o,true);" // Related to WebServer->hasArg("c2") and WebGetArg("c2", stmp, sizeof(stmp)) + "x.open('GET','cs?c2='+id+o,true);" // Related to Webserver->hasArg("c2") and WebGetArg("c2", stmp, sizeof(stmp)) "x.send();" "}" "lt=setTimeout(l,%d);" "return false;" "}" - "wl(l);"; + "wl(l);" // Load initial console text + + // Console command history + "var hc=[],cn=0;" // hc = History commands, cn = Number of history being shown + "function h(){" +// "if(!(navigator.maxTouchPoints||'ontouchstart'in document.documentElement)){eb('c1').autocomplete='off';}" // No touch so stop browser autocomplete + "eb('c1').addEventListener('keydown',function(e){" + "var b=eb('c1'),c=e.keyCode;" // c1 = Console command id + "if(38==c||40==c){b.autocomplete='off';}" // ArrowUp or ArrowDown must be a keyboard so stop browser autocomplete + "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // ArrowUp + "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // ArrowDown + "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history + "});" + "}" + "wl(h);"; // Add console command key eventlistener after name has been synced with id (= wl(jd)) const char HTTP_MODULE_TEMPLATE_REPLACE[] PROGMEM = "}2%d'>%s (%d}3"; // }2 and }3 are used in below os.replace +const char HTTP_MODULE_TEMPLATE_REPLACE_INDEX[] PROGMEM = + "}2%d'>%s (%d)}3"; // }2 and }3 are used in below os.replace +const char HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX[] PROGMEM = + "}2%d'>%s}3"; // }2 and }3 are used in below os.replace + const char HTTP_SCRIPT_MODULE_TEMPLATE[] PROGMEM = +#ifdef ESP8266 "var os;" "function sk(s,g){" // s = value, g = id and name "var o=os.replace(/}2/g,\"
Protocol" + + "
" D_STR_PROTOCOL "" + htmlSelectClimateProtocol(KEY_PROTOCOL, climate[chan]->next.protocol) + "
Model" + + "
" D_STR_MODEL "" + htmlSelectModel(KEY_MODEL, climate[chan]->next.model) + "
Power" + + "
" D_STR_POWER "" + htmlSelectBool(KEY_POWER, climate[chan]->next.power) + "
Mode" + + "
" D_STR_MODE "" + htmlSelectMode(KEY_MODE, climate[chan]->next.mode) + "
Temp" + "
" D_STR_TEMP "" "" "
Fan Speed" + + "
" D_STR_FAN "" + htmlSelectFanspeed(KEY_FANSPEED, climate[chan]->next.fanspeed) + "
Swing (V)" + + "
" D_STR_SWINGV "" + htmlSelectSwingv(KEY_SWINGV, climate[chan]->next.swingv) + "
Swing (H)" + + "
" D_STR_SWINGH "" + htmlSelectSwingh(KEY_SWINGH, climate[chan]->next.swingh) + "
Quiet" + + "
" D_STR_QUIET "" + htmlSelectBool(KEY_QUIET, climate[chan]->next.quiet) + "
Turbo" + + "
" D_STR_TURBO "" + htmlSelectBool(KEY_TURBO, climate[chan]->next.turbo) + "
Econo" + + "
" D_STR_ECONO "" + htmlSelectBool(KEY_ECONO, climate[chan]->next.econo) + "
Light" + + "
" D_STR_LIGHT "" + htmlSelectBool(KEY_LIGHT, climate[chan]->next.light) + "
Filter" + + "
" D_STR_FILTER "" + htmlSelectBool(KEY_FILTER, climate[chan]->next.filter) + "
Clean" + + "
" D_STR_CLEAN "" + htmlSelectBool(KEY_CLEAN, climate[chan]->next.clean) + "
Beep" + + "
" D_STR_BEEP "" + htmlSelectBool(KEY_BEEP, climate[chan]->next.beep) + "
Force resend" + @@ -1228,6 +1245,7 @@ void handleInfo(void) { "

General

" "

Hostname: " + String(Hostname) + "
" "IP address: " + WiFi.localIP().toString() + "
" + "MAC address: " + WiFi.macAddress() + "
" "Booted: " + timeSince(1) + "
" + "Version: " _MY_VERSION_ "
" "Built: " __DATE__ @@ -1258,9 +1276,9 @@ void handleInfo(void) { "Last IR Received: " + lastIrReceived + " (" + timeSince(lastIrReceivedTime) + ")
" #endif // IR_RX - "Duplicate Wifi networks: " + + "Duplicate " D_STR_WIFI " networks: " + String(HIDE_DUPLICATE_NETWORKS ? "Hide" : "Show") + "
" - "Min Wifi signal required: " + "Min " D_STR_WIFI " signal required: " #ifdef MIN_SIGNAL_STRENGTH + String(static_cast(MIN_SIGNAL_STRENGTH)) + #else // MIN_SIGNAL_STRENGTH @@ -1269,9 +1287,9 @@ void handleInfo(void) { "%
" "Serial debugging: " #if DEBUG - + String(isSerialGpioUsedByIr() ? "Off" : "On") + + + String(isSerialGpioUsedByIr() ? D_STR_OFF : D_STR_ON) + #else // DEBUG - "Off" + D_STR_OFF #endif // DEBUG "
" #if REPORT_VCC @@ -1287,6 +1305,7 @@ void handleInfo(void) { : "Disconnected " + timeSince(lastConnectedTime)) + ")
" "Disconnections: " + String(mqttDisconnectCounter - 1) + "
" + "Max Packet Size: " + MQTT_MAX_PACKET_SIZE + " bytes
" "Client id: " + MqttClientId + "
" "Command topic(s): " + listOfCommandTopics() + "
" "Acknowledgements topic: " + MqttAck + "
" @@ -1328,6 +1347,7 @@ void handleInfo(void) { timeElapsed(lastDiscovery.elapsed()) : String("Never"))) + "
" + "Discovery topic: " + MqttDiscovery + "
" + #endif // MQTT_DISCOVERY_ENABLE "Command topics: " + MqttClimate + channel_re + '/' + MQTT_CLIMATE_CMND + '/' + kClimateTopics + @@ -1337,7 +1357,7 @@ void handleInfo(void) { "

" // Page footer "

" - "(Note: Page will refresh every 60 seconds.)" + "(Note: Page will refresh every 60 " D_STR_SECONDS ".)" "

"; html += addJsReloadUrl(kUrlInfo, 60, false); html += htmlEnd(); @@ -1399,7 +1419,7 @@ void handleClearMqtt(void) { htmlHeader(F("Clearing saved info from MQTT"), F("Removing all saved settings for this device from " "MQTT.")) + - "

Device restarting. Try connecting in a few seconds.

" + + "

Device restarting. Try connecting in a few " D_STR_SECONDS ".

" + addJsReloadUrl(kUrlRoot, 10, true) + htmlEnd()); // Do the clearing. @@ -1421,7 +1441,7 @@ void handleReset(void) { server.send(200, "text/html", htmlHeader(F("Reset WiFi Config"), F("Resetting the WiFiManager config back to defaults.")) + - "

Device restarting. Try connecting in a few seconds.

" + + "

Device restarting. Try connecting in a few " D_STR_SECONDS ".

" + addJsReloadUrl(kUrlRoot, 10, true) + htmlEnd()); // Do the reset. @@ -1454,7 +1474,7 @@ void handleReboot() { #endif server.send(200, "text/html", htmlHeader(F("Device restarting.")) + - "

Try connecting in a few seconds.

" + + "

Try connecting in a few " D_STR_SECONDS ".

" + addJsReloadUrl(kUrlRoot, kRebootTime, true) + htmlEnd()); doRestart("Reboot requested"); @@ -1516,6 +1536,23 @@ bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType, // Lastly, it should never exceed the maximum "normal" size. stateSize = std::min(stateSize, kFujitsuAcStateLength); break; + case HITACHI_AC3: + // HitachiAc3 has two distinct & different size states, so make a best + // guess which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, + (uint16_t) (kHitachiAc3MinStateLength)); + // If we think it isn't a "short" message. + if (stateSize > kHitachiAc3MinStateLength) + // Then it probably the "normal" size. + stateSize = std::max(stateSize, + (uint16_t) (kHitachiAc3StateLength)); + // Lastly, it should never exceed the maximum "normal" size. + stateSize = std::min(stateSize, kHitachiAc3StateLength); + break; case MWM: // MWM has variable size states, so make a best guess // which one we are being presented with based on the number of @@ -1993,7 +2030,8 @@ void setup_wifi(void) { if (!wifiManager.autoConnect()) // Reboot. A.k.a. "Have you tried turning it Off and On again?" - doRestart("Wifi failed to connect and hit timeout. Rebooting...", true); + doRestart(D_STR_WIFI " failed to connect and hit timeout. Rebooting...", + true); #if MQTT_ENABLE strncpy(MqttServer, custom_mqtt_server.getValue(), kHostnameLength); @@ -2033,6 +2071,8 @@ void init_vars(void) { // Sub-topic for the climate stat topics. #if MQTT_DISCOVERY_ENABLE MqttDiscovery = "homeassistant/climate/" + String(Hostname) + "/config"; + MqttUniqueId = WiFi.macAddress(); + MqttUniqueId.replace(":", ""); #endif // MQTT_DISCOVERY_ENABLE MqttHAName = String(Hostname) + "_aircon"; // Create a unique MQTT client id. @@ -2163,7 +2203,8 @@ void setup(void) { server.send(200, "text/html", htmlHeader(F("Updating firmware")) + "
" - "

Warning! Don't power off the device for 60 seconds!

" + "

Warning! Don't " D_STR_POWER " " D_STR_OFF " the device for " + "60 " D_STR_SECONDS "!

" "

The firmware is uploading and will try to flash itself. " "It is important to not interrupt the process.

" "

The firmware upload seems to have " + @@ -2531,7 +2572,16 @@ void sendMQTTDiscovery(const char *topic) { "\"swing_mode_stat_t\":\"~/" MQTT_CLIMATE_STAT "/" KEY_SWINGV "\"," "\"swing_modes\":[\"" D_STR_OFF "\",\"" D_STR_AUTO "\",\"" D_STR_HIGHEST "\",\"" D_STR_HIGH "\",\"" D_STR_MIDDLE "\",\"" - D_STR_LOW "\",\"" D_STR_LOWEST "\"]" + D_STR_LOW "\",\"" D_STR_LOWEST "\"]," + "\"uniq_id\":\"" + MqttUniqueId + "\"," + "\"device\":{" + "\"identifiers\":[\"" + MqttUniqueId + "\"]," + "\"connections\":[[\"mac\",\"" + WiFi.macAddress() + "\"]]," + "\"manufacturer\":\"IRremoteESP8266\"," + "\"model\":\"IRMQTTServer\"," + "\"name\":\"" + Hostname + "\"," + "\"sw_version\":\"" _MY_VERSION_ "\"" + "}" "}").c_str(), true)) { mqttLog("MQTT climate discovery successful sent."); hasDiscoveryBeenSent = true; @@ -2738,12 +2788,12 @@ bool sendIRCode(IRsend *irsend, decode_type_t const ir_type, } else { debug("Failed to send IR Message:"); } - debug("Type:"); + debug(D_STR_PROTOCOL ": "); debug(String(ir_type).c_str()); // For "long" codes we basically repeat what we got. if (hasACState(ir_type) || ir_type == PRONTO || ir_type == RAW || ir_type == GLOBALCACHE) { - debug("Code: "); + debug(D_STR_CODE ": "); debug(code_str); // Confirm what we were asked to send was sent. #if MQTT_ENABLE @@ -2762,9 +2812,9 @@ bool sendIRCode(IRsend *irsend, decode_type_t const ir_type, } #endif // MQTT_ENABLE } else { // For "short" codes, we break it down a bit more before we report. - debug(("Code: 0x" + uint64ToString(code, 16)).c_str()); - debug(("Bits: " + String(bits)).c_str()); - debug(("Repeats: " + String(repeat)).c_str()); + debug((D_STR_CODE ": 0x" + uint64ToString(code, 16)).c_str()); + debug((D_STR_BITS ": " + String(bits)).c_str()); + debug((D_STR_REPEAT ": " + String(repeat)).c_str()); #if MQTT_ENABLE if (success) { mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRMQTTServer/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRServer/IRServer.ino b/lib/IRremoteESP8266-2.7.6/examples/IRServer/IRServer.ino similarity index 95% rename from lib/IRremoteESP8266-2.7.4/examples/IRServer/IRServer.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRServer/IRServer.ino index 96fad95d2..92bcd0302 100644 --- a/lib/IRremoteESP8266-2.7.4/examples/IRServer/IRServer.ino +++ b/lib/IRremoteESP8266-2.7.6/examples/IRServer/IRServer.ino @@ -65,7 +65,10 @@ IRsend irsend(kIrLed); // Set the GPIO to be used to sending the message. void handleRoot() { server.send(200, "text/html", "" \ - "" HOSTNAME " Demo" \ + "" HOSTNAME " Demo " \ + "" \ + "" \ "" \ "

Hello from " HOSTNAME ", you can send NEC encoded IR" \ "signals from here!

" \ diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRServer/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRServer/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRServer/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRServer/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRrecvDemo/IRrecvDemo.ino b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDemo/IRrecvDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRrecvDemo/IRrecvDemo.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRrecvDemo/IRrecvDemo.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDemo/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRrecvDemo/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRrecvDemo/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRrecvDump/IRrecvDump.ino b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDump/IRrecvDump.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRrecvDump/IRrecvDump.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRrecvDump/IRrecvDump.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDump/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRrecvDump/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRrecvDump/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDumpV2/IRrecvDumpV2.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRrecvDumpV2/IRrecvDumpV2.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRrecvDumpV2/IRrecvDumpV2.ino diff --git a/lib/IRremoteESP8266-2.7.6/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDumpV2/platformio.ini new file mode 100644 index 000000000..62fa06d3a --- /dev/null +++ b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDumpV2/platformio.ini @@ -0,0 +1,52 @@ +[platformio] +src_dir = . + +[env] +; Default platform +platform = espressif8266 +; Default board +board = nodemcuv2 +framework = arduino +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = ; -D_IR_LOCALE_=en-AU + +[env:nodemcuv2] +board = nodemcuv2 +; build_flags = -D_IR_LOCALE_=en-AU + +[env:esp32dev] +platform = espressif32 +board = esp32dev +; build_flags = -D_IR_LOCALE_=en-AU + +[env:de-CH] +build_flags = -D_IR_LOCALE_=de-CH ; German (Swiss) + +[env:de-DE] +build_flags = -D_IR_LOCALE_=de-DE ; German + +[env:en-AU] +build_flags = -D_IR_LOCALE_=en-AU ; English (Australian) (Default) + +[env:en-IE] +build_flags = -D_IR_LOCALE_=en-IE ; English (Irish) + +[env:en-UK] +build_flags = -D_IR_LOCALE_=en-UK ; English (UK) + +[env:en-US] +build_flags = -D_IR_LOCALE_=en-US ; English (Simplified) (USA) + +[env:es-ES] +build_flags = -D_IR_LOCALE_=es-ES ; Spanish + +[env:fr-FR] +build_flags = -D_IR_LOCALE_=fr-FR ; French + +[env:it-IT] +build_flags = -D_IR_LOCALE_=it-IT ; Italian + +[env:zh-CN] +build_flags = -D_IR_LOCALE_=zh-CN ; Chinese (Simplified) diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRsendDemo/IRsendDemo.ino b/lib/IRremoteESP8266-2.7.6/examples/IRsendDemo/IRsendDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRsendDemo/IRsendDemo.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRsendDemo/IRsendDemo.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRsendDemo/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRsendDemo/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRsendDemo/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRsendProntoDemo/IRsendProntoDemo.ino b/lib/IRremoteESP8266-2.7.6/examples/IRsendProntoDemo/IRsendProntoDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRsendProntoDemo/IRsendProntoDemo.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRsendProntoDemo/IRsendProntoDemo.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRsendProntoDemo/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/IRsendProntoDemo/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRsendProntoDemo/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino b/lib/IRremoteESP8266-2.7.6/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino rename to lib/IRremoteESP8266-2.7.6/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/JVCPanasonicSendDemo/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/JVCPanasonicSendDemo/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/JVCPanasonicSendDemo/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/LGACSend/LGACSend.ino b/lib/IRremoteESP8266-2.7.6/examples/LGACSend/LGACSend.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/LGACSend/LGACSend.ino rename to lib/IRremoteESP8266-2.7.6/examples/LGACSend/LGACSend.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/LGACSend/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/LGACSend/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/LGACSend/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/SmartIRRepeater/SmartIRRepeater.ino b/lib/IRremoteESP8266-2.7.6/examples/SmartIRRepeater/SmartIRRepeater.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/SmartIRRepeater/SmartIRRepeater.ino rename to lib/IRremoteESP8266-2.7.6/examples/SmartIRRepeater/SmartIRRepeater.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/SmartIRRepeater/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/SmartIRRepeater/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/SmartIRRepeater/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/SmartIRRepeater/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnArgoAC/TurnOnArgoAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnArgoAC/TurnOnArgoAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnArgoAC/TurnOnArgoAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnArgoAC/TurnOnArgoAC.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnArgoAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnArgoAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnArgoAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnDaikinAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnDaikinAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnDaikinAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnFujitsuAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnFujitsuAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnFujitsuAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.6/examples/TurnOnGreeAC/TurnOnGreeAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnGreeAC/TurnOnGreeAC.ino new file mode 100644 index 000000000..64c857f3d --- /dev/null +++ b/lib/IRremoteESP8266-2.7.6/examples/TurnOnGreeAC/TurnOnGreeAC.ino @@ -0,0 +1,77 @@ +/* Copyright 2016, 2018 David Conran +* Copyright 2020 Sadid Rafsun Tulon +* +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by kIrLed below. +* +* TL;DR: The IR LED needs to be driven by a transistor for a good result. +* +* Suggested circuit: +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending +* +* Common mistakes & tips: +* * Don't just connect the IR LED directly to the pin, it won't +* have enough current to drive the IR LED effectively. +* * Make sure you have the IR LED polarity correct. +* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity +* * Typical digital camera/phones can be used to see if the IR LED is flashed. +* Replace the IR LED with a normal LED if you don't have a digital camera +* when debugging. +* * Avoid using the following pins unless you really know what you are doing: +* * Pin 0/D3: Can interfere with the boot/program mode & support circuits. +* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. +* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. +* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs +* for your first time. e.g. ESP-12 etc. +*/ +#include +#include +#include +#include + +const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRGreeAC ac(kIrLed); // Set the GPIO to be used for sending messages. + +void printState() { + // Display the settings. + Serial.println("GREE A/C remote is in the following state:"); + Serial.printf(" %s\n", ac.toString().c_str()); + // Display the encoded IR sequence. + unsigned char* ir_code = ac.getRaw(); + Serial.print("IR Code: 0x"); + for (uint8_t i = 0; i < kGreeStateLength; i++) + Serial.printf("%02X", ir_code[i]); + Serial.println(); +} + +void setup() { + ac.begin(); + Serial.begin(115200); + delay(200); + + // Set up what we want to send. See ir_Gree.cpp for all the options. + // Most things default to off. + Serial.println("Default state of the remote."); + printState(); + Serial.println("Setting desired state for A/C."); + ac.on(); + ac.setFan(1); + // kGreeAuto, kGreeDry, kGreeCool, kGreeFan, kGreeHeat + ac.setMode(kGreeCool); + ac.setTemp(20); // 16-30C + ac.setSwingVertical(true, kGreeSwingAuto); + ac.setXFan(false); + ac.setLight(false); + ac.setSleep(false); + ac.setTurbo(false); +} + +void loop() { + // Now send the IR signal. +#if SEND_GREE + Serial.println("Sending IR command to A/C ..."); + ac.send(); +#endif // SEND_GREE + printState(); + delay(5000); +} diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnGreeAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnKelvinatorAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnGreeAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnKelvinatorAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnMitsubishiAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnKelvinatorAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnMitsubishiHeavyAc/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnMitsubishiHeavyAc/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnPanasonicAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiHeavyAc/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnPanasonicAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiHeavyAc/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnPanasonicAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnToshibaAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnPanasonicAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnToshibaAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnTrotecAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnToshibaAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino diff --git a/lib/IRremoteESP8266-2.7.6/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnTrotecAC/platformio.ini new file mode 100644 index 000000000..e6f6320da --- /dev/null +++ b/lib/IRremoteESP8266-2.7.6/examples/TurnOnTrotecAC/platformio.ini @@ -0,0 +1,17 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +framework = arduino +build_flags = ; -D_IR_LOCALE_=en-AU + +[env:nodemcuv2] +platform = espressif8266 +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +board = esp32dev diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/README.md b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/README.md similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/README.md rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/README.md diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/Web-AC-control.ino b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/Web-AC-control.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/Web-AC-control.ino rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/Web-AC-control.ino diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/printscreen.png b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/printscreen.png similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/printscreen.png rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/printscreen.png diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/favicon.ico b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/favicon.ico similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/favicon.ico rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/favicon.ico diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_1_off.svg b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_1_off.svg similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_1_off.svg rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_1_off.svg diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_1_on.svg b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_1_on.svg similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_1_on.svg rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_1_on.svg diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_2_off.svg b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_2_off.svg similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_2_off.svg rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_2_off.svg diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_2_on.svg b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_2_on.svg similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_2_on.svg rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_2_on.svg diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_3_off.svg b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_3_off.svg similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_3_off.svg rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_3_off.svg diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_3_on.svg b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_3_on.svg similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_3_on.svg rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_3_on.svg diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_4_off.svg b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_4_off.svg similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_4_off.svg rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_4_off.svg diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_4_on.svg b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_4_on.svg similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/level_4_on.svg rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/level_4_on.svg diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/ui.html b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/ui.html similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/ui.html rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/ui.html diff --git a/lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/ui.js b/lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/ui.js similarity index 100% rename from lib/IRremoteESP8266-2.7.4/examples/Web-AC-control/upload/ui.js rename to lib/IRremoteESP8266-2.7.6/examples/Web-AC-control/upload/ui.js diff --git a/lib/IRremoteESP8266-2.7.4/keywords.txt b/lib/IRremoteESP8266-2.7.6/keywords.txt similarity index 94% rename from lib/IRremoteESP8266-2.7.4/keywords.txt rename to lib/IRremoteESP8266-2.7.6/keywords.txt index 077c0bc74..7b9e356af 100644 --- a/lib/IRremoteESP8266-2.7.4/keywords.txt +++ b/lib/IRremoteESP8266-2.7.6/keywords.txt @@ -29,6 +29,7 @@ IRDaikin160 KEYWORD1 IRDaikin176 KEYWORD1 IRDaikin2 KEYWORD1 IRDaikin216 KEYWORD1 +IRDaikin64 KEYWORD1 IRDaikinESP KEYWORD1 IRElectraAc KEYWORD1 IRFujitsuAC KEYWORD1 @@ -37,6 +38,8 @@ IRGreeAC KEYWORD1 IRHaierAC KEYWORD1 IRHaierACYRW02 KEYWORD1 IRHitachiAc KEYWORD1 +IRHitachiAc1 KEYWORD1 +IRHitachiAc3 KEYWORD1 IRHitachiAc424 KEYWORD1 IRKelvinatorAC KEYWORD1 IRLgAc KEYWORD1 @@ -66,6 +69,7 @@ decode_type_t KEYWORD1 fanspeed_t KEYWORD1 fujitsu_ac_remote_model_t KEYWORD1 gree_ac_remote_model_t KEYWORD1 +hitachi_ac1_remote_model_t KEYWORD1 irparams_t KEYWORD1 lg_ac_remote_model_t KEYWORD1 match_result_t KEYWORD1 @@ -141,7 +145,9 @@ daikin160 KEYWORD2 daikin176 KEYWORD2 daikin2 KEYWORD2 daikin216 KEYWORD2 +daikin64 KEYWORD2 decode KEYWORD2 +decodeAirwell KEYWORD2 decodeAiwaRCT501 KEYWORD2 decodeAmcor KEYWORD2 decodeArgo KEYWORD2 @@ -155,6 +161,7 @@ decodeDaikin160 KEYWORD2 decodeDaikin176 KEYWORD2 decodeDaikin2 KEYWORD2 decodeDaikin216 KEYWORD2 +decodeDaikin64 KEYWORD2 decodeDenon KEYWORD2 decodeElectraAC KEYWORD2 decodeEpson KEYWORD2 @@ -166,6 +173,7 @@ decodeHaierAC KEYWORD2 decodeHaierACYRW02 KEYWORD2 decodeHash KEYWORD2 decodeHitachiAC KEYWORD2 +decodeHitachiAc3 KEYWORD2 decodeHitachiAc424 KEYWORD2 decodeInax KEYWORD2 decodeJVC KEYWORD2 @@ -200,6 +208,7 @@ decodeSanyoLC7461 KEYWORD2 decodeSharp KEYWORD2 decodeSharpAc KEYWORD2 decodeSony KEYWORD2 +decodeSymphony KEYWORD2 decodeTeco KEYWORD2 decodeToState KEYWORD2 decodeToshibaAC KEYWORD2 @@ -243,6 +252,7 @@ get3D KEYWORD2 get8CHeat KEYWORD2 getBeep KEYWORD2 getBit KEYWORD2 +getBreeze KEYWORD2 getBufSize KEYWORD2 getButton KEYWORD2 getClean KEYWORD2 @@ -280,15 +290,18 @@ getMode KEYWORD2 getMold KEYWORD2 getNight KEYWORD2 getOffTime KEYWORD2 +getOffTimeEnabled KEYWORD2 getOffTimer KEYWORD2 getOffTimerEnabled KEYWORD2 getOnTime KEYWORD2 +getOnTimeEnabled KEYWORD2 getOnTimer KEYWORD2 getOnTimerEnabled KEYWORD2 getOutsideQuiet KEYWORD2 getPower KEYWORD2 getPowerToggle KEYWORD2 getPowerful KEYWORD2 +getPreviousPower KEYWORD2 getPurify KEYWORD2 getQuiet KEYWORD2 getRClevel KEYWORD2 @@ -309,6 +322,7 @@ getSuper KEYWORD2 getSwing KEYWORD2 getSwingH KEYWORD2 getSwingHorizontal KEYWORD2 +getSwingToggle KEYWORD2 getSwingV KEYWORD2 getSwingVToggle KEYWORD2 getSwingVertical KEYWORD2 @@ -336,8 +350,10 @@ haier KEYWORD2 haierYrwo2 KEYWORD2 handleSpecialState KEYWORD2 hasACState KEYWORD2 +hasInvertedStates KEYWORD2 hasStateChanged KEYWORD2 hitachi KEYWORD2 +hitachi1 KEYWORD2 hitachi424 KEYWORD2 htmlEscape KEYWORD2 initState KEYWORD2 @@ -364,6 +380,7 @@ matchAtLeast KEYWORD2 matchBytes KEYWORD2 matchData KEYWORD2 matchGeneric KEYWORD2 +matchManchester KEYWORD2 matchMark KEYWORD2 matchSpace KEYWORD2 midea KEYWORD2 @@ -394,6 +411,7 @@ reverseBits KEYWORD2 samsung KEYWORD2 send KEYWORD2 sendAc KEYWORD2 +sendAirwell KEYWORD2 sendAiwaRCT501 KEYWORD2 sendAmcor KEYWORD2 sendArgo KEYWORD2 @@ -407,6 +425,7 @@ sendDaikin160 KEYWORD2 sendDaikin176 KEYWORD2 sendDaikin2 KEYWORD2 sendDaikin216 KEYWORD2 +sendDaikin64 KEYWORD2 sendData KEYWORD2 sendDenon KEYWORD2 sendElectraAC KEYWORD2 @@ -423,6 +442,7 @@ sendHaierACYRW02 KEYWORD2 sendHitachiAC KEYWORD2 sendHitachiAC1 KEYWORD2 sendHitachiAC2 KEYWORD2 +sendHitachiAc3 KEYWORD2 sendHitachiAc424 KEYWORD2 sendInax KEYWORD2 sendJVC KEYWORD2 @@ -434,6 +454,8 @@ sendLegoPf KEYWORD2 sendLutron KEYWORD2 sendMWM KEYWORD2 sendMagiQuest KEYWORD2 +sendManchester KEYWORD2 +sendManchesterData KEYWORD2 sendMidea KEYWORD2 sendMitsubishi KEYWORD2 sendMitsubishi112 KEYWORD2 @@ -466,6 +488,7 @@ sendSharpRaw KEYWORD2 sendSherwood KEYWORD2 sendSony KEYWORD2 sendSony38 KEYWORD2 +sendSymphony KEYWORD2 sendTcl112Ac KEYWORD2 sendTeco KEYWORD2 sendToshibaAC KEYWORD2 @@ -480,6 +503,7 @@ setAuto KEYWORD2 setBeep KEYWORD2 setBit KEYWORD2 setBits KEYWORD2 +setBreeze KEYWORD2 setButton KEYWORD2 setClean KEYWORD2 setClock KEYWORD2 @@ -516,9 +540,13 @@ setMode KEYWORD2 setModel KEYWORD2 setMold KEYWORD2 setNight KEYWORD2 +setOffTime KEYWORD2 +setOffTimeEnabled KEYWORD2 setOffTimer KEYWORD2 setOffTimerActive KEYWORD2 setOffTimerEnabled KEYWORD2 +setOnTime KEYWORD2 +setOnTimeEnabled KEYWORD2 setOnTimer KEYWORD2 setOnTimerActive KEYWORD2 setOnTimerEnabled KEYWORD2 @@ -526,6 +554,7 @@ setOutsideQuiet KEYWORD2 setPower KEYWORD2 setPowerToggle KEYWORD2 setPowerful KEYWORD2 +setPreviousPower KEYWORD2 setPurify KEYWORD2 setQuiet KEYWORD2 setRaw KEYWORD2 @@ -543,6 +572,7 @@ setSuper KEYWORD2 setSwing KEYWORD2 setSwingH KEYWORD2 setSwingHorizontal KEYWORD2 +setSwingToggle KEYWORD2 setSwingV KEYWORD2 setSwingVToggle KEYWORD2 setSwingVertical KEYWORD2 @@ -599,7 +629,9 @@ xorBytes KEYWORD2 # Constants (LITERAL1) ####################################### +*kAllProtocolNamesStr LITERAL1 // LITERAL1 +AIRWELL LITERAL1 AIWA_RC_T501 LITERAL1 AIWA_RC_T501_BITS LITERAL1 AKB75215403 LITERAL1 @@ -644,6 +676,7 @@ DAIKIN160 LITERAL1 DAIKIN176 LITERAL1 DAIKIN2 LITERAL1 DAIKIN216 LITERAL1 +DAIKIN64 LITERAL1 DAIKIN_AUTO LITERAL1 DAIKIN_COMMAND_LENGTH LITERAL1 DAIKIN_COOL LITERAL1 @@ -657,6 +690,7 @@ DAIKIN_HEAT LITERAL1 DAIKIN_MAX_TEMP LITERAL1 DAIKIN_MIN_TEMP LITERAL1 DECODE_AC LITERAL1 +DECODE_AIRWELL LITERAL1 DECODE_AIWA_RC_T501 LITERAL1 DECODE_AMCOR LITERAL1 DECODE_ARGO LITERAL1 @@ -669,6 +703,7 @@ DECODE_DAIKIN160 LITERAL1 DECODE_DAIKIN176 LITERAL1 DECODE_DAIKIN2 LITERAL1 DECODE_DAIKIN216 LITERAL1 +DECODE_DAIKIN64 LITERAL1 DECODE_DENON LITERAL1 DECODE_DISH LITERAL1 DECODE_ELECTRA_AC LITERAL1 @@ -684,6 +719,7 @@ DECODE_HASH LITERAL1 DECODE_HITACHI_AC LITERAL1 DECODE_HITACHI_AC1 LITERAL1 DECODE_HITACHI_AC2 LITERAL1 +DECODE_HITACHI_AC3 LITERAL1 DECODE_HITACHI_AC424 LITERAL1 DECODE_INAX LITERAL1 DECODE_JVC LITERAL1 @@ -719,6 +755,7 @@ DECODE_SHARP LITERAL1 DECODE_SHARP_AC LITERAL1 DECODE_SHERWOOD LITERAL1 DECODE_SONY LITERAL1 +DECODE_SYMPHONY LITERAL1 DECODE_TCL112AC LITERAL1 DECODE_TECO LITERAL1 DECODE_TOSHIBA_AC LITERAL1 @@ -855,6 +892,7 @@ HITACHI_AC1_STATE_LENGTH LITERAL1 HITACHI_AC2 LITERAL1 HITACHI_AC2_BITS LITERAL1 HITACHI_AC2_STATE_LENGTH LITERAL1 +HITACHI_AC3 LITERAL1 HITACHI_AC424 LITERAL1 HITACHI_AC_BITS LITERAL1 HITACHI_AC_STATE_LENGTH LITERAL1 @@ -950,6 +988,8 @@ RC6_36_BITS LITERAL1 RC6_MODE0_BITS LITERAL1 RCMM LITERAL1 RCMM_BITS LITERAL1 +R_LT0541_HTA_A LITERAL1 +R_LT0541_HTA_B LITERAL1 SAMSUNG LITERAL1 SAMSUNG36 LITERAL1 SAMSUNG_AC LITERAL1 @@ -958,6 +998,7 @@ SANYO LITERAL1 SANYO_LC7461 LITERAL1 SANYO_LC7461_BITS LITERAL1 SANYO_SA8650B_BITS LITERAL1 +SEND_AIRWELL LITERAL1 SEND_AIWA_RC_T501 LITERAL1 SEND_AMCOR LITERAL1 SEND_ARGO LITERAL1 @@ -970,6 +1011,7 @@ SEND_DAIKIN160 LITERAL1 SEND_DAIKIN176 LITERAL1 SEND_DAIKIN2 LITERAL1 SEND_DAIKIN216 LITERAL1 +SEND_DAIKIN64 LITERAL1 SEND_DENON LITERAL1 SEND_DISH LITERAL1 SEND_ELECTRA_AC LITERAL1 @@ -984,6 +1026,7 @@ SEND_HAIER_AC_YRW02 LITERAL1 SEND_HITACHI_AC LITERAL1 SEND_HITACHI_AC1 LITERAL1 SEND_HITACHI_AC2 LITERAL1 +SEND_HITACHI_AC3 LITERAL1 SEND_HITACHI_AC424 LITERAL1 SEND_INAX LITERAL1 SEND_JVC LITERAL1 @@ -1020,6 +1063,7 @@ SEND_SHARP LITERAL1 SEND_SHARP_AC LITERAL1 SEND_SHERWOOD LITERAL1 SEND_SONY LITERAL1 +SEND_SYMPHONY LITERAL1 SEND_TCL112AC LITERAL1 SEND_TECO LITERAL1 SEND_TOSHIBA_AC LITERAL1 @@ -1037,6 +1081,7 @@ SONY_12_BITS LITERAL1 SONY_15_BITS LITERAL1 SONY_20_BITS LITERAL1 SONY_38K LITERAL1 +SYMPHONY LITERAL1 TCL112AC LITERAL1 TECO LITERAL1 TIMEOUT_MS LITERAL1 @@ -1076,6 +1121,13 @@ k3DStr LITERAL1 k6thSenseStr LITERAL1 k8CHeatStr LITERAL1 kAirFlowStr LITERAL1 +kAirwellBits LITERAL1 +kAirwellFooterMark LITERAL1 +kAirwellHalfClockPeriod LITERAL1 +kAirwellHdrMark LITERAL1 +kAirwellHdrSpace LITERAL1 +kAirwellMinRepeats LITERAL1 +kAirwellOverhead LITERAL1 kAiwaRcT501Bits LITERAL1 kAiwaRcT501MinRepeats LITERAL1 kAiwaRcT501PostBits LITERAL1 @@ -1498,6 +1550,53 @@ kDaikin2SwingVLow LITERAL1 kDaikin2SwingVSwing LITERAL1 kDaikin2Tolerance LITERAL1 kDaikin2ZeroSpace LITERAL1 +kDaikin64BitMark LITERAL1 +kDaikin64Bits LITERAL1 +kDaikin64ChecksumOffset LITERAL1 +kDaikin64ChecksumSize LITERAL1 +kDaikin64ClockHoursSize LITERAL1 +kDaikin64ClockMinsSize LITERAL1 +kDaikin64ClockOffset LITERAL1 +kDaikin64ClockSize LITERAL1 +kDaikin64Cool LITERAL1 +kDaikin64DefaultRepeat LITERAL1 +kDaikin64Dry LITERAL1 +kDaikin64Fan LITERAL1 +kDaikin64FanAuto LITERAL1 +kDaikin64FanHigh LITERAL1 +kDaikin64FanLow LITERAL1 +kDaikin64FanMed LITERAL1 +kDaikin64FanOffset LITERAL1 +kDaikin64FanQuiet LITERAL1 +kDaikin64FanSize LITERAL1 +kDaikin64FanTurbo LITERAL1 +kDaikin64Freq LITERAL1 +kDaikin64Gap LITERAL1 +kDaikin64HdrMark LITERAL1 +kDaikin64HdrSpace LITERAL1 +kDaikin64KnownGoodState LITERAL1 +kDaikin64LdrMark LITERAL1 +kDaikin64LdrSpace LITERAL1 +kDaikin64MaxTemp LITERAL1 +kDaikin64MinTemp LITERAL1 +kDaikin64ModeOffset LITERAL1 +kDaikin64ModeSize LITERAL1 +kDaikin64OffTimeEnableBit LITERAL1 +kDaikin64OffTimeHalfHourBit LITERAL1 +kDaikin64OffTimeOffset LITERAL1 +kDaikin64OffTimeSize LITERAL1 +kDaikin64OnTimeEnableBit LITERAL1 +kDaikin64OnTimeHalfHourBit LITERAL1 +kDaikin64OnTimeOffset LITERAL1 +kDaikin64OnTimeSize LITERAL1 +kDaikin64OneSpace LITERAL1 +kDaikin64Overhead LITERAL1 +kDaikin64PowerToggleBit LITERAL1 +kDaikin64SleepBit LITERAL1 +kDaikin64SwingVBit LITERAL1 +kDaikin64TempOffset LITERAL1 +kDaikin64TempSize LITERAL1 +kDaikin64ZeroSpace LITERAL1 kDaikinAuto LITERAL1 kDaikinBeepLoud LITERAL1 kDaikinBeepOff LITERAL1 @@ -1955,12 +2054,67 @@ kHighNibble LITERAL1 kHighStr LITERAL1 kHighest LITERAL1 kHighestStr LITERAL1 +kHitachiAc1Auto LITERAL1 kHitachiAc1Bits LITERAL1 +kHitachiAc1ChecksumStartByte LITERAL1 +kHitachiAc1Cool LITERAL1 +kHitachiAc1Dry LITERAL1 +kHitachiAc1Fan LITERAL1 +kHitachiAc1FanAuto LITERAL1 +kHitachiAc1FanByte LITERAL1 +kHitachiAc1FanHigh LITERAL1 +kHitachiAc1FanLow LITERAL1 +kHitachiAc1FanMed LITERAL1 +kHitachiAc1FanOffset LITERAL1 +kHitachiAc1FanSize LITERAL1 kHitachiAc1HdrMark LITERAL1 kHitachiAc1HdrSpace LITERAL1 +kHitachiAc1Heat LITERAL1 +kHitachiAc1ModeByte LITERAL1 +kHitachiAc1ModeOffset LITERAL1 +kHitachiAc1ModeSize LITERAL1 +kHitachiAc1ModelByte LITERAL1 +kHitachiAc1ModelOffset LITERAL1 +kHitachiAc1ModelSize LITERAL1 +kHitachiAc1Model_A LITERAL1 +kHitachiAc1Model_B LITERAL1 +kHitachiAc1OffTimerHighByte LITERAL1 +kHitachiAc1OffTimerLowByte LITERAL1 +kHitachiAc1OnTimerHighByte LITERAL1 +kHitachiAc1OnTimerLowByte LITERAL1 +kHitachiAc1PowerByte LITERAL1 +kHitachiAc1PowerOffset LITERAL1 +kHitachiAc1PowerToggleOffset LITERAL1 +kHitachiAc1Sleep1 LITERAL1 +kHitachiAc1Sleep2 LITERAL1 +kHitachiAc1Sleep3 LITERAL1 +kHitachiAc1Sleep4 LITERAL1 +kHitachiAc1SleepByte LITERAL1 +kHitachiAc1SleepOff LITERAL1 +kHitachiAc1SleepOffset LITERAL1 +kHitachiAc1SleepSize LITERAL1 kHitachiAc1StateLength LITERAL1 +kHitachiAc1SwingByte LITERAL1 +kHitachiAc1SwingHOffset LITERAL1 +kHitachiAc1SwingToggleOffset LITERAL1 +kHitachiAc1SwingVOffset LITERAL1 +kHitachiAc1TempAuto LITERAL1 +kHitachiAc1TempByte LITERAL1 +kHitachiAc1TempDelta LITERAL1 +kHitachiAc1TempOffset LITERAL1 +kHitachiAc1TempSize LITERAL1 +kHitachiAc1TimerSize LITERAL1 kHitachiAc2Bits LITERAL1 kHitachiAc2StateLength LITERAL1 +kHitachiAc3BitMark LITERAL1 +kHitachiAc3Bits LITERAL1 +kHitachiAc3HdrMark LITERAL1 +kHitachiAc3HdrSpace LITERAL1 +kHitachiAc3MinBits LITERAL1 +kHitachiAc3MinStateLength LITERAL1 +kHitachiAc3OneSpace LITERAL1 +kHitachiAc3StateLength LITERAL1 +kHitachiAc3ZeroSpace LITERAL1 kHitachiAc424BitMark LITERAL1 kHitachiAc424Bits LITERAL1 kHitachiAc424ButtonByte LITERAL1 @@ -2709,6 +2863,7 @@ kPioneerZeroSpaceTicks LITERAL1 kPowerStr LITERAL1 kPowerToggleStr LITERAL1 kPowerfulStr LITERAL1 +kPreviousPowerStr LITERAL1 kProntoDataOffset LITERAL1 kProntoFreqFactor LITERAL1 kProntoFreqOffset LITERAL1 @@ -2770,12 +2925,14 @@ kRightMaxStr LITERAL1 kRightStr LITERAL1 kRoomStr LITERAL1 kSamsung36Bits LITERAL1 -kSamsungACSectionLength LITERAL1 kSamsungAcAuto LITERAL1 kSamsungAcAutoTemp LITERAL1 kSamsungAcBeepOffset LITERAL1 kSamsungAcBitMark LITERAL1 kSamsungAcBits LITERAL1 +kSamsungAcBreezeOffset LITERAL1 +kSamsungAcBreezeOn LITERAL1 +kSamsungAcBreezeSize LITERAL1 kSamsungAcClean10Offset LITERAL1 kSamsungAcClean11Offset LITERAL1 kSamsungAcCool LITERAL1 @@ -2806,11 +2963,13 @@ kSamsungAcPower6Offset LITERAL1 kSamsungAcPower6Size LITERAL1 kSamsungAcPowerSection LITERAL1 kSamsungAcPowerful10Offset LITERAL1 +kSamsungAcPowerful10On LITERAL1 kSamsungAcPowerful10Size LITERAL1 kSamsungAcPowerfulMask8 LITERAL1 kSamsungAcQuiet1Offset LITERAL1 kSamsungAcQuiet5Offset LITERAL1 kSamsungAcSectionGap LITERAL1 +kSamsungAcSectionLength LITERAL1 kSamsungAcSectionMark LITERAL1 kSamsungAcSectionSpace LITERAL1 kSamsungAcSections LITERAL1 @@ -2864,14 +3023,17 @@ kSensorStr LITERAL1 kSensorTempStr LITERAL1 kSetStr LITERAL1 kSharpAcAuto LITERAL1 -kSharpAcBitFanManualOffset LITERAL1 kSharpAcBitMark LITERAL1 -kSharpAcBitModeNonAutoOffset LITERAL1 kSharpAcBitPowerOffset LITERAL1 -kSharpAcBitTempManualOffset LITERAL1 +kSharpAcBitPreviousPowerOffset LITERAL1 kSharpAcBits LITERAL1 +kSharpAcButtonFan LITERAL1 +kSharpAcButtonOffset LITERAL1 +kSharpAcButtonPowerMode LITERAL1 +kSharpAcButtonSize LITERAL1 +kSharpAcButtonTemp LITERAL1 +kSharpAcByteButton LITERAL1 kSharpAcByteFan LITERAL1 -kSharpAcByteManual LITERAL1 kSharpAcByteMode LITERAL1 kSharpAcBytePower LITERAL1 kSharpAcByteTemp LITERAL1 @@ -2952,6 +3114,14 @@ kSwingStr LITERAL1 kSwingVModeStr LITERAL1 kSwingVStr LITERAL1 kSwingVToggleStr LITERAL1 +kSymphonyBits LITERAL1 +kSymphonyDefaultRepeat LITERAL1 +kSymphonyFooterGap LITERAL1 +kSymphonyFooterMark LITERAL1 +kSymphonyOneMark LITERAL1 +kSymphonyOneSpace LITERAL1 +kSymphonyZeroMark LITERAL1 +kSymphonyZeroSpace LITERAL1 kTcl112AcAuto LITERAL1 kTcl112AcBitEconoOffset LITERAL1 kTcl112AcBitHealthOffset LITERAL1 diff --git a/lib/IRremoteESP8266-2.7.4/library.json b/lib/IRremoteESP8266-2.7.6/library.json similarity index 97% rename from lib/IRremoteESP8266-2.7.4/library.json rename to lib/IRremoteESP8266-2.7.6/library.json index f135c19e7..c2780ac6c 100644 --- a/lib/IRremoteESP8266-2.7.4/library.json +++ b/lib/IRremoteESP8266-2.7.6/library.json @@ -1,6 +1,6 @@ { "name": "IRremoteESP8266", - "version": "2.7.4", + "version": "2.7.6", "keywords": "infrared, ir, remote, esp8266, esp32", "description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)", "repository": diff --git a/lib/IRremoteESP8266-2.7.4/library.properties b/lib/IRremoteESP8266-2.7.6/library.properties similarity index 97% rename from lib/IRremoteESP8266-2.7.4/library.properties rename to lib/IRremoteESP8266-2.7.6/library.properties index e98cd3f3b..b67edbce0 100644 --- a/lib/IRremoteESP8266-2.7.4/library.properties +++ b/lib/IRremoteESP8266-2.7.6/library.properties @@ -1,5 +1,5 @@ name=IRremoteESP8266 -version=2.7.4 +version=2.7.6 author=David Conran, Sebastien Warin, Mark Szabo, Ken Shirriff maintainer=David Conran, Mark Szabo, Sebastien Warin, Roi Dayan, Massimiliano Pinto sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32) diff --git a/lib/IRremoteESP8266-2.7.6/platformio.ini b/lib/IRremoteESP8266-2.7.6/platformio.ini new file mode 100644 index 000000000..67dbeb606 --- /dev/null +++ b/lib/IRremoteESP8266-2.7.6/platformio.ini @@ -0,0 +1,21 @@ +[platformio] +# Default to building IRrecvDumpV2 if not in a specific example directory. +src_dir = examples/IRrecvDumpV2 + +[env] +lib_extra_dirs = . +lib_ldf_mode = deep+ +lib_ignore = examples +framework = arduino +platform = espressif8266 +build_flags = ; -D_IR_LOCALE_=en-AU + +[env:nodemcuv2] +board = nodemcuv2 + +[env:d1_mini] +board = d1_mini + +[env:esp32dev] +platform = espressif32 +board = esp32dev diff --git a/lib/IRremoteESP8266-2.7.4/pylintrc b/lib/IRremoteESP8266-2.7.6/pylintrc similarity index 100% rename from lib/IRremoteESP8266-2.7.4/pylintrc rename to lib/IRremoteESP8266-2.7.6/pylintrc diff --git a/lib/IRremoteESP8266-2.7.4/src/CPPLINT.cfg b/lib/IRremoteESP8266-2.7.6/src/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/CPPLINT.cfg rename to lib/IRremoteESP8266-2.7.6/src/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.7.4/src/IRac.cpp b/lib/IRremoteESP8266-2.7.6/src/IRac.cpp similarity index 94% rename from lib/IRremoteESP8266-2.7.4/src/IRac.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRac.cpp index d3be30b80..abf450df9 100644 --- a/lib/IRremoteESP8266-2.7.4/src/IRac.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/IRac.cpp @@ -123,6 +123,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #if SEND_DAIKIN216 case decode_type_t::DAIKIN216: #endif +#if SEND_DAIKIN64 + case decode_type_t::DAIKIN64: +#endif #if SEND_ELECTRA_AC case decode_type_t::ELECTRA_AC: #endif @@ -144,6 +147,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #if SEND_HITACHI_AC case decode_type_t::HITACHI_AC: #endif +#if SEND_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: +#endif #if SEND_HITACHI_AC424 case decode_type_t::HITACHI_AC424: #endif @@ -461,6 +467,27 @@ void IRac::daikin216(IRDaikin216 *ac, } #endif // SEND_DAIKIN216 +#if SEND_DAIKIN64 +void IRac::daikin64(IRDaikin64 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, + const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setTurbo(turbo); + ac->setQuiet(quiet); + ac->setSleep(sleep >= 0); + ac->setClock(clock); + ac->send(); +} +#endif // SEND_DAIKIN64 + #if SEND_ELECTRA_AC void IRac::electra(IRElectraAc *ac, const bool on, const stdAc::opmode_t mode, @@ -674,6 +701,37 @@ void IRac::hitachi(IRHitachiAc *ac, } #endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 +void IRac::hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, + const bool on, const bool power_toggle, + const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool swing_toggle, const int16_t sleep) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setPowerToggle(power_toggle); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + ac->setSwingToggle(swing_toggle); + ac->setSleep((sleep >= 0) ? kHitachiAc1Sleep2 : kHitachiAc1SleepOff); + // No Sleep setting available. + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC1 + #if SEND_HITACHI_AC424 void IRac::hitachi424(IRHitachiAc424 *ac, const bool on, const stdAc::opmode_t mode, @@ -999,10 +1057,11 @@ void IRac::samsung(IRSamsungAc *ac, #if SEND_SHARP_AC void IRac::sharp(IRSharpAc *ac, - const bool on, const stdAc::opmode_t mode, + const bool on, const bool prev_power, + const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan) { ac->begin(); - ac->setPower(on); + ac->setPower(on, prev_power); ac->setMode(ac->convertMode(mode)); ac->setTemp(degrees); ac->setFan(ac->convertFan(fan)); @@ -1233,6 +1292,7 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, else result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. break; + case decode_type_t::DAIKIN64: case decode_type_t::WHIRLPOOL_AC: result.power = desired.power ^ prev->power; break; @@ -1390,6 +1450,15 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { break; } #endif // SEND_DAIKIN216 +#if SEND_DAIKIN64 + case DAIKIN64: + { + IRDaikin64 ac(_pin, _inverted, _modulation); + daikin64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.sleep, send.clock); + break; + } +#endif // SEND_DAIKIN64 #if SEND_ELECTRA_AC case ELECTRA_AC: { @@ -1457,6 +1526,23 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { break; } #endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + { + IRHitachiAc1 ac(_pin, _inverted, _modulation); + bool power_toggle = false; + bool swing_toggle = false; + if (prev != NULL) { + power_toggle = (send.power != prev->power); + swing_toggle = (send.swingv != prev->swingv) || + (send.swingh != prev->swingh); + } + hitachi1(&ac, (hitachi_ac1_remote_model_t)send.model, send.power, + power_toggle, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, swing_toggle, send.sleep); + break; + } +#endif // SEND_HITACHI_AC1 #if SEND_HITACHI_AC424 case HITACHI_AC424: { @@ -1572,7 +1658,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { case SHARP_AC: { IRSharpAc ac(_pin, _inverted, _modulation); - sharp(&ac, send.power, send.mode, degC, send.fanspeed); + bool prev_power = !send.power; + if (prev != NULL) prev_power = prev->power; + sharp(&ac, send.power, prev_power, send.mode, degC, send.fanspeed); break; } #endif // SEND_SHARP_AC @@ -1796,6 +1884,11 @@ int16_t IRac::strToModel(const char *str, const int16_t def) { return gree_ac_remote_model_t::YAW1F; } else if (!strcasecmp(str, "YBOFB")) { return gree_ac_remote_model_t::YBOFB; + // HitachiAc1 models + } else if (!strcasecmp(str, "R-LT0541-HTA-A")) { + return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; + } else if (!strcasecmp(str, "R-LT0541-HTA-B")) { + return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; // Fujitsu A/C models } else if (!strcasecmp(str, "ARRAH2E")) { return fujitsu_ac_remote_model_t::ARRAH2E; @@ -2008,6 +2101,13 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN64 + case decode_type_t::DAIKIN64: { + IRDaikin64 ac(kGpioUnused); + ac.setRaw(result->value); // Daikin64 uses value instead of state. + return ac.toString(); + } +#endif // DECODE_DAIKIN216 #if DECODE_ELECTRA_AC case decode_type_t::ELECTRA_AC: { IRElectraAc ac(0); @@ -2157,6 +2257,13 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_HITACHI_AC +#if DECODE_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: { + IRHitachiAc1 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC1 #if DECODE_HITACHI_AC424 case decode_type_t::HITACHI_AC424: { IRHitachiAc424 ac(0); @@ -2305,6 +2412,14 @@ namespace IRAcUtils { break; } #endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN64 + case decode_type_t::DAIKIN64: { + IRDaikin64 ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_DAIKIN64 #if DECODE_ELECTRA_AC case decode_type_t::ELECTRA_AC: { IRElectraAc ac(kGpioUnused); @@ -2361,6 +2476,14 @@ namespace IRAcUtils { break; } #endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) +#if DECODE_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: { + IRHitachiAc1 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC1 #if DECODE_HITACHI_AC424 case decode_type_t::HITACHI_AC424: { IRHitachiAc424 ac(kGpioUnused); diff --git a/lib/IRremoteESP8266-2.7.4/src/IRac.h b/lib/IRremoteESP8266-2.7.6/src/IRac.h similarity index 94% rename from lib/IRremoteESP8266-2.7.4/src/IRac.h rename to lib/IRremoteESP8266-2.7.6/src/IRac.h index f38865e50..02faba36a 100644 --- a/lib/IRremoteESP8266-2.7.4/src/IRac.h +++ b/lib/IRremoteESP8266-2.7.6/src/IRac.h @@ -166,6 +166,14 @@ void daikin216(IRDaikin216 *ac, const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, const bool quiet, const bool turbo); #endif // SEND_DAIKIN216 +#if SEND_DAIKIN64 + void daikin64(IRDaikin64 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, + const int16_t sleep = -1, const int16_t clock = -1); +#endif // SEND_DAIKIN64 #if SEND_ELECTRA_AC void electra(IRElectraAc *ac, const bool on, const stdAc::opmode_t mode, @@ -219,6 +227,14 @@ void electra(IRElectraAc *ac, const float degrees, const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); #endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + void hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, + const bool on, const bool power_toggle, + const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool swing_toggle, const int16_t sleep = -1); +#endif // SEND_HITACHI_AC1 #if SEND_HITACHI_AC424 void hitachi424(IRHitachiAc424 *ac, const bool on, const stdAc::opmode_t mode, @@ -308,7 +324,7 @@ void electra(IRElectraAc *ac, #endif // SEND_SAMSUNG_AC #if SEND_SHARP_AC void sharp(IRSharpAc *ac, - const bool on, const stdAc::opmode_t mode, + const bool on, const bool prev_power, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan); #endif // SEND_SHARP_AC #if SEND_TCL112AC diff --git a/lib/IRremoteESP8266-2.7.4/src/IRrecv.cpp b/lib/IRremoteESP8266-2.7.6/src/IRrecv.cpp similarity index 86% rename from lib/IRremoteESP8266-2.7.4/src/IRrecv.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRrecv.cpp index e629fc218..f59c1248c 100644 --- a/lib/IRremoteESP8266-2.7.4/src/IRrecv.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/IRrecv.cpp @@ -641,10 +641,28 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, if (decodeHaierACYRW02(results, offset)) return true; #endif #if DECODE_HITACHI_AC424 - // HitachiAc424 should be checked before HitachiAC & HitachiAC2 + // HitachiAc424 should be checked before HitachiAC, HitachiAC2, + // & HitachiAC184 DPRINTLN("Attempting Hitachi AC 424 decode"); if (decodeHitachiAc424(results, offset, kHitachiAc424Bits)) return true; -#endif // DECODE_HITACHI_AC2 +#endif // DECODE_HITACHI_AC424 +#if DECODE_MITSUBISHI136 + // Needs to happen before HitachiAc3 decode. + DPRINTLN("Attempting Mitsubishi136 decode"); + if (decodeMitsubishi136(results, offset)) return true; +#endif // DECODE_MITSUBISHI136 +#if DECODE_HITACHI_AC3 + // HitachiAc3 should be checked before HitachiAC & HitachiAC2 + // Attempt normal before the short version. + DPRINTLN("Attempting Hitachi AC3 decode"); + // Order these in decreasing bit size, as it is more optimal. + if (decodeHitachiAc3(results, offset, kHitachiAc3Bits) || + decodeHitachiAc3(results, offset, kHitachiAc3Bits - 4 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3Bits - 6 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3MinBits + 2 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3MinBits)) + return true; +#endif // DECODE_HITACHI_AC3 #if DECODE_HITACHI_AC2 // HitachiAC2 should be checked before HitachiAC DPRINTLN("Attempting Hitachi AC2 decode"); @@ -759,12 +777,20 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting Daikin152 decode"); if (decodeDaikin152(results, offset)) return true; #endif // DECODE_DAIKIN152 -#if DECODE_MITSUBISHI136 - DPRINTLN("Attempting Mitsubishi136 decode"); - if (decodeMitsubishi136(results, offset)) return true; -#endif // DECODE_MITSUBISHI136 - } +#if DECODE_SYMPHONY + DPRINTLN("Attempting Symphony decode"); + if (decodeSymphony(results, offset)) return true; +#endif // DECODE_SYMPHONY +#if DECODE_DAIKIN64 + DPRINTLN("Attempting Daikin64 decode"); + if (decodeDaikin64(results, offset)) return true; +#endif // DECODE_DAIKIN64 +#if DECODE_AIRWELL + DPRINTLN("Attempting Airwell decode"); + if (decodeAirwell(results, offset)) return true; +#endif // DECODE_AIRWELL // Typically new protocols are added above this line. + } #if DECODE_HASH // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. @@ -1270,4 +1296,166 @@ uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, zeromark, zerospace, footermark, footerspace, atleast, tolerance, excess, MSBfirst); } + +// Match & decode a Manchester Code <= 64bit IR message. +// The data is stored at result_ptr. +// Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean skip +// that requirement. +// +// Args: +// data_ptr: A pointer to where we are at in the capture buffer. +// NOTE: It is assumed to be pointing to a "Mark", not a "Space". +// result_ptr: A pointer to where to start storing the bits we decoded. +// remaining: The size of the capture buffer are remaining. +// nbits: Nr. of data bits we expect. +// hdrmark: Nr. of uSeconds for the expected header mark signal. +// hdrspace: Nr. of uSeconds for the expected header space signal. +// half_period: Nr. of uSeconds for half the clock's period. (1/2 wavelength) +// footermark: Nr. of uSeconds for the expected footer mark signal. +// footerspace: Nr. of uSeconds for the expected footer space/gap signal. +// atleast: Is the match on the footerspace a matchAtLeast or matchSpace? +// tolerance: Percentage error margin to allow. (Def: kUseDefTol) +// excess: Nr. of useconds. (Def: kMarkExcess) +// MSBfirst: Bit order to save the data in. (Def: true) +// GEThomas: Use G.E. Thomas (true/default) or IEEE 802.3 (false) convention? +// Returns: +// A uint16_t: If successful, how many buffer entries were used. Otherwise 0. +// +// Ref: +// https://en.wikipedia.org/wiki/Manchester_code +// http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf +uint16_t IRrecv::matchManchester(volatile const uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t half_period, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst, + const bool GEThomas) { + uint16_t offset = 0; + uint64_t data = 0; + uint16_t nr_of_half_periods = GEThomas; + // 2 per bit, and 4 extra for the timing sync. + uint16_t expected_half_periods = 2 * nbits + 4; + bool currentBit = false; + + // Calculate how much remaining buffer is required. + // Shortest case. Longest case is 2 * nbits. + uint16_t min_remaining = nbits + 2; + + if (hdrmark) min_remaining++; + if (hdrspace) min_remaining++; + if (footermark) min_remaining++; + // Don't need to extend for footerspace because it could be the end of message + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) return 0; // Nope, so abort. + + // Header + if (hdrmark && !matchMark(*(data_ptr + offset++), hdrmark, tolerance, excess)) + return 0; + // Manchester Code always has a guaranteed 2x half_period (T2) at the start + // of the data section. e.g. a sync header. If it is a GEThomas-style, then + // it is space(T);mark(2xT);space(T), thus we need to check for that space + // plus any requested "header" space. + if ((hdrspace || GEThomas) && + !matchSpace(*(data_ptr + offset++), + hdrspace + ((GEThomas) ? half_period : 0), tolerance, excess)) + return 0; + + // Data + // Loop until we find a 'long' pulse. This is the timing sync per protocol. + while ((offset < remaining) && (nr_of_half_periods < expected_half_periods) && + !match(*(data_ptr + offset), half_period * 2, tolerance, excess)) { + // Was it not a short pulse? + if (!match(*(data_ptr + offset), half_period, tolerance, excess)) + return 0; + nr_of_half_periods++; + offset++; + } + + // Data (cont.) + + // We are now pointing to the first 'long' pulse. + // Loop through the buffer till we run out of buffer, or nr of half periods. + while (offset < remaining && nr_of_half_periods < expected_half_periods) { + // Only if there is enough half_periods left for a long pulse & + // Is it a 'long' pulse? + if (nr_of_half_periods < expected_half_periods - 1 && + match(*(data_ptr + offset), half_period * 2, tolerance, excess)) { + // Yes, so invert the value we will append. + currentBit = !currentBit; + nr_of_half_periods += 2; // A 'long' pulse is two half periods. + offset++; + // Append the bit value. + data <<= 1; + data |= currentBit; + } else if (match(*(data_ptr + offset), half_period, tolerance, excess)) { + // or is it part of a 'short' pulse pair? + nr_of_half_periods++; + offset++; + // Look for the second half of the 'short' pulse pair. + // Do we have enough buffer or nr of half periods? + if (offset < remaining && nr_of_half_periods < expected_half_periods) { + // We do, so look for it. + if (match(*(data_ptr + offset), half_period, tolerance, excess)) { + // Found it! + nr_of_half_periods++; + // No change of the polarity of the bit we will append. + // Append the bit value. + data <<= 1; + data |= currentBit; + offset++; + } else { + // It's not what we expected. + return 0; + } + } + } else if (nr_of_half_periods == expected_half_periods - 1 && + matchAtLeast(*(data_ptr + offset), half_period, tolerance, + excess)) { + // Special case when we are at the end of the expected nr of periods. + // i.e. The pulse could be merged with the footer. + nr_of_half_periods++; + break; + } else { + // It's neither, so abort. + return 0; + } + } + // Did we collect the expected amount of data? + if (nr_of_half_periods < expected_half_periods) return 0; + + // Footer + if (footermark && + !(matchMark(*(data_ptr + offset), footermark + half_period, + tolerance, excess) || + matchMark(*(data_ptr + offset), footermark, + tolerance, excess))) + return 0; + offset++; + // If we have something still to match & haven't reached the end of the buffer + if (footerspace && offset < remaining) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } else { + if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } + offset++; + } + + // Clean up and process the data. + if (!MSBfirst) data = reverseBits(data, nbits); + // Trim the data to size to remove timing sync. + *result_ptr = GETBITS64(data, 0, nbits); + return offset; +} // End of IRrecv class ------------------- diff --git a/lib/IRremoteESP8266-2.7.4/src/IRrecv.h b/lib/IRremoteESP8266-2.7.6/src/IRrecv.h similarity index 93% rename from lib/IRremoteESP8266-2.7.4/src/IRrecv.h rename to lib/IRremoteESP8266-2.7.6/src/IRrecv.h index 60559981c..aeea5b32d 100644 --- a/lib/IRremoteESP8266-2.7.4/src/IRrecv.h +++ b/lib/IRremoteESP8266-2.7.6/src/IRrecv.h @@ -221,6 +221,20 @@ class IRrecv { const uint8_t tolerance = kUseDefTol, const int16_t excess = kMarkExcess, const bool MSBfirst = true); + uint16_t matchManchester(volatile const uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t clock_period, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true, + const bool GEThomas = true); void crudeNoiseFilter(decode_results *results, const uint16_t floor = 0); bool decodeHash(decode_results *results); #if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || SEND_SANYO) @@ -393,6 +407,11 @@ class IRrecv { const uint16_t nbits = kDaikinBits, const bool strict = true); #endif +#if DECODE_DAIKIN64 + bool decodeDaikin64(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin64Bits, + const bool strict = true); +#endif // DECODE_DAIKIN64 #if DECODE_DAIKIN128 bool decodeDaikin128(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kDaikin128Bits, @@ -485,6 +504,12 @@ class IRrecv { const uint16_t nbits = kHitachiAc1Bits, const bool strict = true); #endif +#if DECODE_HITACHI_AC3 + bool decodeHitachiAc3(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc3Bits, + const bool strict = true); +#endif // DECODE_HITACHI_AC3 #if DECODE_HITACHI_AC424 bool decodeHitachiAc424(decode_results *results, uint16_t offset = kStartOffset, @@ -558,6 +583,16 @@ class IRrecv { const uint16_t nbits = kEpsonBits, const bool strict = true); #endif // DECODE_EPSON +#if DECODE_SYMPHONY + bool decodeSymphony(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSymphonyBits, + const bool strict = true); +#endif // DECODE_SYMPHONY +#if DECODE_AIRWELL + bool decodeAirwell(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAirwellBits, + const bool strict = true); +#endif // DECODE_AIRWELL }; #endif // IRRECV_H_ diff --git a/lib/IRremoteESP8266-2.7.4/src/IRremoteESP8266.h b/lib/IRremoteESP8266-2.7.6/src/IRremoteESP8266.h similarity index 95% rename from lib/IRremoteESP8266-2.7.4/src/IRremoteESP8266.h rename to lib/IRremoteESP8266-2.7.6/src/IRremoteESP8266.h index a048e23fc..2bf906697 100644 --- a/lib/IRremoteESP8266-2.7.4/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266-2.7.6/src/IRremoteESP8266.h @@ -52,7 +52,7 @@ #endif // UNIT_TEST // Library Version -#define _IRREMOTEESP8266_VERSION_ "2.7.4" +#define _IRREMOTEESP8266_VERSION_ "2.7.6" // Set the language & locale for the library. See the `locale` dir for options. #ifndef _IR_LOCALE_ @@ -411,6 +411,20 @@ #define SEND_HITACHI_AC2 _IR_ENABLE_DEFAULT_ #endif // SEND_HITACHI_AC2 +#ifndef DECODE_HITACHI_AC3 +#define DECODE_HITACHI_AC3 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC3 +#ifndef SEND_HITACHI_AC3 +#define SEND_HITACHI_AC3 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC3 + +#ifndef DECODE_HITACHI_AC424 +#define DECODE_HITACHI_AC424 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC424 +#ifndef SEND_HITACHI_AC424 +#define SEND_HITACHI_AC424 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC424 + #ifndef DECODE_GICABLE #define DECODE_GICABLE _IR_ENABLE_DEFAULT_ #endif // DECODE_GICABLE @@ -558,13 +572,6 @@ #define SEND_DAIKIN152 _IR_ENABLE_DEFAULT_ #endif // SEND_DAIKIN152 -#ifndef DECODE_HITACHI_AC424 -#define DECODE_HITACHI_AC424 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC424 -#ifndef SEND_HITACHI_AC424 -#define SEND_HITACHI_AC424 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC424 - #ifndef DECODE_EPSON #define DECODE_EPSON _IR_ENABLE_DEFAULT_ #endif // DECODE_EPSON @@ -572,6 +579,27 @@ #define SEND_EPSON _IR_ENABLE_DEFAULT_ #endif // SEND_EPSON +#ifndef DECODE_SYMPHONY +#define DECODE_SYMPHONY _IR_ENABLE_DEFAULT_ +#endif // DECODE_SYMPHONY +#ifndef SEND_SYMPHONY +#define SEND_SYMPHONY _IR_ENABLE_DEFAULT_ +#endif // SEND_SYMPHONY + +#ifndef DECODE_DAIKIN64 +#define DECODE_DAIKIN64 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN64 +#ifndef SEND_DAIKIN64 +#define SEND_DAIKIN64 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN64 + +#ifndef DECODE_AIRWELL +#define DECODE_AIRWELL _IR_ENABLE_DEFAULT_ +#endif // DECODE_AIRWELL +#ifndef SEND_AIRWELL +#define SEND_AIRWELL _IR_ENABLE_DEFAULT_ +#endif // SEND_AIRWELL + #if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ @@ -582,7 +610,7 @@ DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \ DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \ DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \ - DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424) + DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC3) #define DECODE_AC true // We need some common infrastructure for decoding A/Cs. #else #define DECODE_AC false // We don't need that infrastructure. @@ -695,14 +723,20 @@ enum decode_type_t { HITACHI_AC424, SONY_38K, EPSON, // 75 + SYMPHONY, + HITACHI_AC3, + DAIKIN64, + AIRWELL, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = EPSON, + kLastDecodeType = AIRWELL, }; // Message lengths & required repeat values const uint16_t kNoRepeat = 0; const uint16_t kSingleRepeat = 1; +const uint16_t kAirwellBits = 32; +const uint16_t kAirwellMinRepeats = 2; const uint16_t kAiwaRcT501Bits = 15; const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat; const uint16_t kAlokaBits = 32; @@ -724,6 +758,8 @@ const uint16_t kDaikinDefaultRepeat = kNoRepeat; const uint16_t kDaikin2StateLength = 39; const uint16_t kDaikin2Bits = kDaikin2StateLength * 8; const uint16_t kDaikin2DefaultRepeat = kNoRepeat; +const uint16_t kDaikin64Bits = 64; +const uint16_t kDaikin64DefaultRepeat = kNoRepeat; const uint16_t kDaikin160StateLength = 20; const uint16_t kDaikin160Bits = kDaikin160StateLength * 8; const uint16_t kDaikin160DefaultRepeat = kNoRepeat; @@ -774,6 +810,10 @@ const uint16_t kHitachiAc1StateLength = 13; const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8; const uint16_t kHitachiAc2StateLength = 53; const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8; +const uint16_t kHitachiAc3StateLength = 27; +const uint16_t kHitachiAc3Bits = kHitachiAc3StateLength * 8; +const uint16_t kHitachiAc3MinStateLength = 15; +const uint16_t kHitachiAc3MinBits = kHitachiAc3MinStateLength * 8; const uint16_t kHitachiAc424StateLength = 53; const uint16_t kHitachiAc424Bits = kHitachiAc424StateLength * 8; const uint16_t kInaxBits = 24; @@ -857,6 +897,8 @@ const uint16_t kSony15Bits = 15; const uint16_t kSony20Bits = 20; const uint16_t kSonyMinBits = 12; const uint16_t kSonyMinRepeat = 2; +const uint16_t kSymphonyBits = 11; +const uint16_t kSymphonyDefaultRepeat = kSingleRepeat; const uint16_t kTcl112AcStateLength = 14; const uint16_t kTcl112AcBits = kTcl112AcStateLength * 8; const uint16_t kTcl112AcDefaultRepeat = kNoRepeat; diff --git a/lib/IRremoteESP8266-2.7.4/src/IRsend.cpp b/lib/IRremoteESP8266-2.7.6/src/IRsend.cpp similarity index 86% rename from lib/IRremoteESP8266-2.7.4/src/IRsend.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRsend.cpp index 8bc12949b..bb5f55407 100644 --- a/lib/IRremoteESP8266-2.7.4/src/IRsend.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/IRsend.cpp @@ -462,6 +462,105 @@ void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, } } +// Generic method for sending Manchester code data. +// Will send leading or trailing 0's if the nbits is larger than the number +// of bits in data. +// +// Args: +// half_period: Nr. of uSeconds for half the clock's period. (1/2 wavelength) +// data: The data to be transmitted. +// nbits: Nr. of bits of data to be sent. +// MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order. +// GEThomas: Use G.E. Thomas (true/default) or IEEE 802.3 (false). +void IRsend::sendManchesterData(const uint16_t half_period, + const uint64_t data, + const uint16_t nbits, const bool MSBfirst, + const bool GEThomas) { + if (nbits == 0) return; // Nothing to send. + uint16_t bits = nbits; + uint64_t copy = (GEThomas) ? data : ~data; + + if (MSBfirst) { // Send the MSB first. + // Send 0's until we get down to a bit size we can actually manage. + if (bits > (sizeof(data) * 8)) { + sendManchesterData(half_period, 0ULL, bits - sizeof(data) * 8, MSBfirst, + GEThomas); + bits = sizeof(data) * 8; + } + // Send the supplied data. + for (uint64_t mask = 1ULL << (bits - 1); mask; mask >>= 1) + if (copy & mask) { + mark(half_period); + space(half_period); + } else { + space(half_period); + mark(half_period); + } + } else { // Send the Least Significant Bit (LSB) first / MSB last. + for (bits = 0; bits < nbits; bits++, copy >>= 1) + if (copy & 1) { + mark(half_period); + space(half_period); + } else { + space(half_period); + mark(half_period); + } + } +} + +// Generic method for sending Manchester code messages. +// Will send leading or trailing 0's if the nbits is larger than the number +// of bits in data. +// +// Args: +// headermark: Nr. of usecs for the led to be pulsed for the header mark. +// A value of 0 means no header mark. +// headerspace: Nr. of usecs for the led to be off after the header mark. +// A value of 0 means no header space. +// half_period: Nr. of uSeconds for half the clock's period. (1/2 wavelength) +// footermark: Nr. of usecs for the led to be pulsed for the footer mark. +// A value of 0 means no footer mark. +// gap: Min. nr. of usecs for the led to be off after the footer mark. +// This is effectively the absolute minimum gap between messages. +// data: The data to be transmitted. +// nbits: Nr. of bits of data to be sent. +// frequency: The frequency we want to modulate at. +// Assumes < 1000 means kHz otherwise it is in Hz. +// Most common value is 38000 or 38, for 38kHz. +// MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order. +// repeat: Nr. of extra times the message will be sent. +// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +// dutycycle: Percentage duty cycle of the LED. +// e.g. 25 = 25% = 1/4 on, 3/4 off. +// If you are not sure, try 50 percent. +// GEThomas: Use G.E. Thomas (true/default) or IEEE 802.3 (false). +void IRsend::sendManchester(const uint16_t headermark, + const uint32_t headerspace, + const uint16_t half_period, + const uint16_t footermark, const uint32_t gap, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle, + const bool GEThomas) { + // Setup + enableIROut(frequency, dutycycle); + + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + // Data Marker/sync + // This guarantees a double width half_period. i.e. a Period or T2. + sendManchesterData(half_period, 0b01, 2, true, GEThomas); + // Data + sendManchesterData(half_period, data, nbits, MSBfirst, GEThomas); + // Footer + if (footermark) mark(footermark); + if (gap) space(gap); + } +} + #if SEND_RAW // Send a raw IRremote message. // @@ -509,17 +608,20 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) { case MITSUBISHI2: case MITSUBISHI_AC: case SHERWOOD: + case SYMPHONY: case TOSHIBA_AC: return kSingleRepeat; // Special + case AIRWELL: + return kAirwellMinRepeats; case DISH: return kDishMinRepeat; + case EPSON: + return kEpsonMinRepeat; case SONY: return kSonyMinRepeat; case SONY_38K: return kSonyMinRepeat + 1; - case EPSON: - return kEpsonMinRepeat; default: return kNoRepeat; } @@ -532,6 +634,8 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) { // int16_t: The number of bits. uint16_t IRsend::defaultBits(const decode_type_t protocol) { switch (protocol) { + case SYMPHONY: + return 11; case RC5: return 12; case LASERTAG: @@ -560,6 +664,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { case LG: case LG2: return 28; + case AIRWELL: case CARRIER_AC: case EPSON: case NEC: @@ -601,6 +706,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kDaikin2Bits; case DAIKIN216: return kDaikin216Bits; + case DAIKIN64: + return kDaikin64Bits; case ELECTRA_AC: return kElectraAcBits; case GREE: @@ -615,6 +722,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kHitachiAc1Bits; case HITACHI_AC2: return kHitachiAc2Bits; + case HITACHI_AC3: + return kHitachiAc3Bits; case HITACHI_AC424: return kHitachiAc424Bits; case KELVINATOR: @@ -666,6 +775,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, const uint16_t nbits, const uint16_t repeat) { uint16_t min_repeat = std::max(IRsend::minRepeats(type), repeat); switch (type) { +#if SEND_AIRWELL + case AIRWELL: + sendAirwell(data, nbits, min_repeat); + break; +#endif #if SEND_AIWA_RC_T501 case AIWA_RC_T501: sendAiwaRCT501(data, nbits, min_repeat); @@ -681,6 +795,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, sendCOOLIX(data, nbits, min_repeat); break; #endif +#if SEND_DAIKIN64 + case DAIKIN64: + sendDaikin64(data, nbits, min_repeat); + break; +#endif #if SEND_DENON case DENON: sendDenon(data, nbits, min_repeat); @@ -834,6 +953,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, sendSony38(data, nbits, min_repeat); break; #endif +#if SEND_SYMPHONY + case SYMPHONY: + sendSymphony(data, nbits, min_repeat); + break; +#endif #if SEND_TECO case TECO: sendTeco(data, nbits, min_repeat); @@ -951,6 +1075,11 @@ bool IRsend::send(const decode_type_t type, const unsigned char *state, sendHitachiAC2(state, nbytes); break; #endif // SEND_HITACHI_AC2 +#if SEND_HITACHI_AC3 + case HITACHI_AC3: + sendHitachiAc3(state, nbytes); + break; +#endif // SEND_HITACHI_AC3 #if SEND_HITACHI_AC424 case HITACHI_AC424: sendHitachiAc424(state, nbytes); diff --git a/lib/IRremoteESP8266-2.7.4/src/IRsend.h b/lib/IRremoteESP8266-2.7.6/src/IRsend.h similarity index 92% rename from lib/IRremoteESP8266-2.7.4/src/IRsend.h rename to lib/IRremoteESP8266-2.7.6/src/IRsend.h index 902f22010..8d5e6a862 100644 --- a/lib/IRremoteESP8266-2.7.4/src/IRsend.h +++ b/lib/IRremoteESP8266-2.7.6/src/IRsend.h @@ -126,6 +126,11 @@ enum gree_ac_remote_model_t { YBOFB, // (2) Green, YBOFB2, YAPOF3 }; +enum hitachi_ac1_remote_model_t { + R_LT0541_HTA_A = 1, // (1) R-LT0541-HTA Remote in "A" setting. (Default) + R_LT0541_HTA_B, // (2) R-LT0541-HTA Remote in "B" setting. +}; + enum panasonic_ac_remote_model_t { kPanasonicUnknown = 0, kPanasonicLke = 1, @@ -162,6 +167,17 @@ class IRsend { void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, uint32_t zerospace, uint64_t data, uint16_t nbits, bool MSBfirst = true); + void sendManchesterData(const uint16_t half_period, const uint64_t data, + const uint16_t nbits, const bool MSBfirst = true, + const bool GEThomas = true); + void sendManchester(const uint16_t headermark, const uint32_t headerspace, + const uint16_t half_period, const uint16_t footermark, + const uint32_t gap, const uint64_t data, + const uint16_t nbits, const uint16_t frequency = 38, + const bool MSBfirst = true, + const uint16_t repeat = kNoRepeat, + const uint8_t dutycycle = kDutyDefault, + const bool GEThomas = true); void sendGeneric(const uint16_t headermark, const uint32_t headerspace, const uint16_t onemark, const uint32_t onespace, const uint16_t zeromark, const uint32_t zerospace, @@ -362,6 +378,10 @@ class IRsend { const uint16_t nbytes = kDaikinStateLength, const uint16_t repeat = kDaikinDefaultRepeat); #endif +#if SEND_DAIKIN64 + void sendDaikin64(const uint64_t data, const uint16_t nbits = kDaikin64Bits, + const uint16_t repeat = kDaikin64DefaultRepeat); +#endif // SEND_DAIKIN64 #if SEND_DAIKIN128 void sendDaikin128(const unsigned char data[], const uint16_t nbytes = kDaikin128StateLength, @@ -471,6 +491,12 @@ class IRsend { const uint16_t nbytes = kHitachiAc2StateLength, const uint16_t repeat = kHitachiAcDefaultRepeat); #endif +#if SEND_HITACHI_AC3 + void sendHitachiAc3(const unsigned char data[], + const uint16_t nbytes, // No default as there as so many + // different sizes + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC3 #if SEND_HITACHI_AC424 void sendHitachiAc424(const unsigned char data[], const uint16_t nbytes = kHitachiAc424StateLength, @@ -539,6 +565,14 @@ class IRsend { void sendEpson(uint64_t data, uint16_t nbits = kEpsonBits, uint16_t repeat = kEpsonMinRepeat); #endif +#if SEND_SYMPHONY + void sendSymphony(uint64_t data, uint16_t nbits = kSymphonyBits, + uint16_t repeat = kSymphonyDefaultRepeat); +#endif +#if SEND_AIRWELL + void sendAirwell(uint64_t data, uint16_t nbits = kAirwellBits, + uint16_t repeat = kAirwellMinRepeats); +#endif protected: #ifdef UNIT_TEST diff --git a/lib/IRremoteESP8266-2.7.4/src/IRtext.cpp b/lib/IRremoteESP8266-2.7.6/src/IRtext.cpp similarity index 75% rename from lib/IRremoteESP8266-2.7.4/src/IRtext.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRtext.cpp index 82f728191..cb9ccd722 100644 --- a/lib/IRremoteESP8266-2.7.4/src/IRtext.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/IRtext.cpp @@ -130,6 +130,7 @@ const PROGMEM char* kEyeAutoStr = D_STR_EYEAUTO; const PROGMEM char* kLightToggleStr = D_STR_LIGHTTOGGLE; const PROGMEM char* kOutsideQuietStr = D_STR_OUTSIDEQUIET; const PROGMEM char* kPowerToggleStr = D_STR_POWERTOGGLE; +const PROGMEM char* kPreviousPowerStr = D_STR_PREVIOUSPOWER; const PROGMEM char* kSensorTempStr = D_STR_SENSORTEMP; const PROGMEM char* kSleepTimerStr = D_STR_SLEEP_TIMER; const PROGMEM char* kSwingVModeStr = D_STR_SWINGVMODE; @@ -162,3 +163,88 @@ const PROGMEM char* kFalseStr = D_STR_FALSE; const PROGMEM char* kRepeatStr = D_STR_REPEAT; const PROGMEM char* kCodeStr = D_STR_CODE; const PROGMEM char* kBitsStr = D_STR_BITS; + +// Protocol Names +// Needs to be in decode_type_t order. +const PROGMEM char *kAllProtocolNamesStr = + D_STR_UNUSED "\x0" + D_STR_RC5 "\x0" + D_STR_RC6 "\x0" + D_STR_NEC "\x0" + D_STR_SONY "\x0" + D_STR_PANASONIC "\x0" + D_STR_JVC "\x0" + D_STR_SAMSUNG "\x0" + D_STR_WHYNTER "\x0" + D_STR_AIWA_RC_T501 "\x0" + D_STR_LG "\x0" + D_STR_SANYO "\x0" + D_STR_MITSUBISHI "\x0" + D_STR_DISH "\x0" + D_STR_SHARP "\x0" + D_STR_COOLIX "\x0" + D_STR_DAIKIN "\x0" + D_STR_DENON "\x0" + D_STR_KELVINATOR "\x0" + D_STR_SHERWOOD "\x0" + D_STR_MITSUBISHI_AC "\x0" + D_STR_RCMM "\x0" + D_STR_SANYO_LC7461 "\x0" + D_STR_RC5X "\x0" + D_STR_GREE "\x0" + D_STR_PRONTO "\x0" + D_STR_NEC_LIKE "\x0" + D_STR_ARGO "\x0" + D_STR_TROTEC "\x0" + D_STR_NIKAI "\x0" + D_STR_RAW "\x0" + D_STR_GLOBALCACHE "\x0" + D_STR_TOSHIBA_AC "\x0" + D_STR_FUJITSU_AC "\x0" + D_STR_MIDEA "\x0" + D_STR_MAGIQUEST "\x0" + D_STR_LASERTAG "\x0" + D_STR_CARRIER_AC "\x0" + D_STR_HAIER_AC "\x0" + D_STR_MITSUBISHI2 "\x0" + D_STR_HITACHI_AC "\x0" + D_STR_HITACHI_AC1 "\x0" + D_STR_HITACHI_AC2 "\x0" + D_STR_GICABLE "\x0" + D_STR_HAIER_AC_YRW02 "\x0" + D_STR_WHIRLPOOL_AC "\x0" + D_STR_SAMSUNG_AC "\x0" + D_STR_LUTRON "\x0" + D_STR_ELECTRA_AC "\x0" + D_STR_PANASONIC_AC "\x0" + D_STR_PIONEER "\x0" + D_STR_LG2 "\x0" + D_STR_MWM "\x0" + D_STR_DAIKIN2 "\x0" + D_STR_VESTEL_AC "\x0" + D_STR_TECO "\x0" + D_STR_SAMSUNG36 "\x0" + D_STR_TCL112AC "\x0" + D_STR_LEGOPF "\x0" + D_STR_MITSUBISHI_HEAVY_88 "\x0" + D_STR_MITSUBISHI_HEAVY_152 "\x0" + D_STR_DAIKIN216 "\x0" + D_STR_SHARP_AC "\x0" + D_STR_GOODWEATHER "\x0" + D_STR_INAX "\x0" + D_STR_DAIKIN160 "\x0" + D_STR_NEOCLIMA "\x0" + D_STR_DAIKIN176 "\x0" + D_STR_DAIKIN128 "\x0" + D_STR_AMCOR "\x0" + D_STR_DAIKIN152 "\x0" + D_STR_MITSUBISHI136 "\x0" + D_STR_MITSUBISHI112 "\x0" + D_STR_HITACHI_AC424 "\x0" + D_STR_SONY_38K "\x0" + D_STR_EPSON "\x0" + D_STR_SYMPHONY "\x0" + D_STR_HITACHI_AC3 "\x0" + D_STR_DAIKIN64 "\x0" + D_STR_AIRWELL "\x0" + "\x0"; // This string requires double null termination. diff --git a/lib/IRremoteESP8266-2.7.4/src/IRtext.h b/lib/IRremoteESP8266-2.7.6/src/IRtext.h similarity index 98% rename from lib/IRremoteESP8266-2.7.4/src/IRtext.h rename to lib/IRremoteESP8266-2.7.6/src/IRtext.h index 522b528bc..cbce59747 100644 --- a/lib/IRremoteESP8266-2.7.4/src/IRtext.h +++ b/lib/IRremoteESP8266-2.7.6/src/IRtext.h @@ -17,6 +17,7 @@ extern const char* k3DStr; extern const char* k6thSenseStr; extern const char* k8CHeatStr; extern const char* kAirFlowStr; +extern const char *kAllProtocolNamesStr; extern const char* kAutomaticStr; extern const char* kAutoStr; extern const char* kBeepStr; @@ -104,6 +105,7 @@ extern const char* kOutsideStr; extern const char* kPowerfulStr; extern const char* kPowerStr; extern const char* kPowerToggleStr; +extern const char* kPreviousPowerStr; extern const char* kProtocolStr; extern const char* kPurifyStr; extern const char* kQuietStr; diff --git a/lib/IRremoteESP8266-2.7.4/src/IRtimer.cpp b/lib/IRremoteESP8266-2.7.6/src/IRtimer.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/IRtimer.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRtimer.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/IRtimer.h b/lib/IRremoteESP8266-2.7.6/src/IRtimer.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/IRtimer.h rename to lib/IRremoteESP8266-2.7.6/src/IRtimer.h diff --git a/lib/IRremoteESP8266-2.7.4/src/IRutils.cpp b/lib/IRremoteESP8266-2.7.6/src/IRutils.cpp similarity index 72% rename from lib/IRremoteESP8266-2.7.4/src/IRutils.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRutils.cpp index 230b24809..39d9973ed 100644 --- a/lib/IRremoteESP8266-2.7.4/src/IRutils.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/IRutils.cpp @@ -91,161 +91,14 @@ void serialPrintUint64(uint64_t input, uint8_t base) { // Returns: // A decode_type_t enum. decode_type_t strToDecodeType(const char * const str) { - if (!strcasecmp(str, kUnknownStr)) - return decode_type_t::UNKNOWN; - else if (!strcasecmp(str, "UNUSED")) - return decode_type_t::UNUSED; - else if (!strcasecmp(str, "AIWA_RC_T501")) - return decode_type_t::AIWA_RC_T501; - else if (!strcasecmp(str, "AMCOR")) - return decode_type_t::AMCOR; - else if (!strcasecmp(str, "ARGO")) - return decode_type_t::ARGO; - else if (!strcasecmp(str, "CARRIER_AC")) - return decode_type_t::CARRIER_AC; - else if (!strcasecmp(str, "COOLIX")) - return decode_type_t::COOLIX; - else if (!strcasecmp(str, "DAIKIN")) - return decode_type_t::DAIKIN; - else if (!strcasecmp(str, "DAIKIN128")) - return decode_type_t::DAIKIN128; - else if (!strcasecmp(str, "DAIKIN152")) - return decode_type_t::DAIKIN152; - else if (!strcasecmp(str, "DAIKIN160")) - return decode_type_t::DAIKIN160; - else if (!strcasecmp(str, "DAIKIN176")) - return decode_type_t::DAIKIN176; - else if (!strcasecmp(str, "DAIKIN2")) - return decode_type_t::DAIKIN2; - else if (!strcasecmp(str, "DAIKIN216")) - return decode_type_t::DAIKIN216; - else if (!strcasecmp(str, "DENON")) - return decode_type_t::DENON; - else if (!strcasecmp(str, "DISH")) - return decode_type_t::DISH; - else if (!strcasecmp(str, "ELECTRA_AC")) - return decode_type_t::ELECTRA_AC; - else if (!strcasecmp(str, "EPSON")) - return decode_type_t::EPSON; - else if (!strcasecmp(str, "FUJITSU_AC")) - return decode_type_t::FUJITSU_AC; - else if (!strcasecmp(str, "GICABLE")) - return decode_type_t::GICABLE; - else if (!strcasecmp(str, "GLOBALCACHE")) - return decode_type_t::GLOBALCACHE; - else if (!strcasecmp(str, "GOODWEATHER")) - return decode_type_t::GOODWEATHER; - else if (!strcasecmp(str, "GREE")) - return decode_type_t::GREE; - else if (!strcasecmp(str, "HAIER_AC")) - return decode_type_t::HAIER_AC; - else if (!strcasecmp(str, "HAIER_AC_YRW02")) - return decode_type_t::HAIER_AC_YRW02; - else if (!strcasecmp(str, "HITACHI_AC")) - return decode_type_t::HITACHI_AC; - else if (!strcasecmp(str, "HITACHI_AC1")) - return decode_type_t::HITACHI_AC1; - else if (!strcasecmp(str, "HITACHI_AC2")) - return decode_type_t::HITACHI_AC2; - else if (!strcasecmp(str, "HITACHI_AC424")) - return decode_type_t::HITACHI_AC424; - else if (!strcasecmp(str, "INAX")) - return decode_type_t::INAX; - else if (!strcasecmp(str, "JVC")) - return decode_type_t::JVC; - else if (!strcasecmp(str, "KELVINATOR")) - return decode_type_t::KELVINATOR; - else if (!strcasecmp(str, "LEGOPF")) - return decode_type_t::LEGOPF; - else if (!strcasecmp(str, "LG")) - return decode_type_t::LG; - else if (!strcasecmp(str, "LG2")) - return decode_type_t::LG2; - else if (!strcasecmp(str, "LASERTAG")) - return decode_type_t::LASERTAG; - else if (!strcasecmp(str, "LUTRON")) - return decode_type_t::LUTRON; - else if (!strcasecmp(str, "MAGIQUEST")) - return decode_type_t::MAGIQUEST; - else if (!strcasecmp(str, "MIDEA")) - return decode_type_t::MIDEA; - else if (!strcasecmp(str, "MITSUBISHI")) - return decode_type_t::MITSUBISHI; - else if (!strcasecmp(str, "MITSUBISHI2")) - return decode_type_t::MITSUBISHI2; - else if (!strcasecmp(str, "MITSUBISHI_AC")) - return decode_type_t::MITSUBISHI_AC; - else if (!strcasecmp(str, "MITSUBISHI136")) - return decode_type_t::MITSUBISHI136; - else if (!strcasecmp(str, "MITSUBISHI112")) - return decode_type_t::MITSUBISHI112; - else if (!strcasecmp(str, "MITSUBISHI_HEAVY_88")) - return decode_type_t::MITSUBISHI_HEAVY_88; - else if (!strcasecmp(str, "MITSUBISHI_HEAVY_152")) - return decode_type_t::MITSUBISHI_HEAVY_152; - else if (!strcasecmp(str, "MWM")) - return decode_type_t::MWM; - else if (!strcasecmp(str, "NEOCLIMA")) - return decode_type_t::NEOCLIMA; - else if (!strcasecmp(str, "NEC")) - return decode_type_t::NEC; - else if (!strcasecmp(str, "NEC_LIKE") || - !strcasecmp(str, "NEC (NON-STRICT)")) - return decode_type_t::NEC_LIKE; - else if (!strcasecmp(str, "NIKAI")) - return decode_type_t::NIKAI; - else if (!strcasecmp(str, "PANASONIC")) - return decode_type_t::PANASONIC; - else if (!strcasecmp(str, "PANASONIC_AC")) - return decode_type_t::PANASONIC_AC; - else if (!strcasecmp(str, "PIONEER")) - return decode_type_t::PIONEER; - else if (!strcasecmp(str, "PRONTO")) - return decode_type_t::PRONTO; - else if (!strcasecmp(str, "RAW")) - return decode_type_t::RAW; - else if (!strcasecmp(str, "RC5")) - return decode_type_t::RC5; - else if (!strcasecmp(str, "RC5X")) - return decode_type_t::RC5X; - else if (!strcasecmp(str, "RC6")) - return decode_type_t::RC6; - else if (!strcasecmp(str, "RCMM")) - return decode_type_t::RCMM; - else if (!strcasecmp(str, "SAMSUNG")) - return decode_type_t::SAMSUNG; - else if (!strcasecmp(str, "SAMSUNG36")) - return decode_type_t::SAMSUNG36; - else if (!strcasecmp(str, "SAMSUNG_AC")) - return decode_type_t::SAMSUNG_AC; - else if (!strcasecmp(str, "SANYO")) - return decode_type_t::SANYO; - else if (!strcasecmp(str, "SANYO_LC7461")) - return decode_type_t::SANYO_LC7461; - else if (!strcasecmp(str, "SHARP")) - return decode_type_t::SHARP; - else if (!strcasecmp(str, "SHARP_AC")) - return decode_type_t::SHARP_AC; - else if (!strcasecmp(str, "SHERWOOD")) - return decode_type_t::SHERWOOD; - else if (!strcasecmp(str, "SONY")) - return decode_type_t::SONY; - else if (!strcasecmp(str, "SONY_38K")) - return decode_type_t::SONY_38K; - else if (!strcasecmp(str, "TCL112AC")) - return decode_type_t::TCL112AC; - else if (!strcasecmp(str, "TECO")) - return decode_type_t::TECO; - else if (!strcasecmp(str, "TOSHIBA_AC")) - return decode_type_t::TOSHIBA_AC; - else if (!strcasecmp(str, "TROTEC")) - return decode_type_t::TROTEC; - else if (!strcasecmp(str, "VESTEL_AC")) - return decode_type_t::VESTEL_AC; - else if (!strcasecmp(str, "WHIRLPOOL_AC")) - return decode_type_t::WHIRLPOOL_AC; - else if (!strcasecmp(str, "WHYNTER")) - return decode_type_t::WHYNTER; + const char *ptr = kAllProtocolNamesStr; + uint16_t length = strlen(ptr); + for (uint16_t i = 0; length; i++) { + if (!strcasecmp(str, ptr)) return (decode_type_t)i; + ptr += length + 1; + length = strlen(ptr); + } + // Handle integer values of the type by converting to a string and back again. decode_type_t result = strToDecodeType( typeToString((decode_type_t)atoi(str)).c_str()); @@ -263,239 +116,17 @@ decode_type_t strToDecodeType(const char * const str) { // A string containing the protocol name. String typeToString(const decode_type_t protocol, const bool isRepeat) { String result = ""; - switch (protocol) { - case UNUSED: - result = F("UNUSED"); - break; - case AIWA_RC_T501: - result = F("AIWA_RC_T501"); - break; - case AMCOR: - result = F("AMCOR"); - break; - case ARGO: - result = F("ARGO"); - break; - case CARRIER_AC: - result = F("CARRIER_AC"); - break; - case COOLIX: - result = F("COOLIX"); - break; - case DAIKIN: - result = F("DAIKIN"); - break; - case DAIKIN128: - result = F("DAIKIN128"); - break; - case DAIKIN152: - result = F("DAIKIN152"); - break; - case DAIKIN160: - result = F("DAIKIN160"); - break; - case DAIKIN176: - result = F("DAIKIN176"); - break; - case DAIKIN2: - result = F("DAIKIN2"); - break; - case DAIKIN216: - result = F("DAIKIN216"); - break; - case DENON: - result = F("DENON"); - break; - case DISH: - result = F("DISH"); - break; - case ELECTRA_AC: - result = F("ELECTRA_AC"); - break; - case EPSON: - result = F("EPSON"); - break; - case FUJITSU_AC: - result = F("FUJITSU_AC"); - break; - case GICABLE: - result = F("GICABLE"); - break; - case GLOBALCACHE: - result = F("GLOBALCACHE"); - break; - case GOODWEATHER: - result = F("GOODWEATHER"); - break; - case GREE: - result = F("GREE"); - break; - case HAIER_AC: - result = F("HAIER_AC"); - break; - case HAIER_AC_YRW02: - result = F("HAIER_AC_YRW02"); - break; - case HITACHI_AC: - result = F("HITACHI_AC"); - break; - case HITACHI_AC1: - result = F("HITACHI_AC1"); - break; - case HITACHI_AC2: - result = F("HITACHI_AC2"); - break; - case HITACHI_AC424: - result = F("HITACHI_AC424"); - break; - case INAX: - result = F("INAX"); - break; - case JVC: - result = F("JVC"); - break; - case KELVINATOR: - result = F("KELVINATOR"); - break; - case LEGOPF: - result = F("LEGOPF"); - break; - case LG: - result = F("LG"); - break; - case LG2: - result = F("LG2"); - break; - case LASERTAG: - result = F("LASERTAG"); - break; - case LUTRON: - result = F("LUTRON"); - break; - case MAGIQUEST: - result = F("MAGIQUEST"); - break; - case MIDEA: - result = F("MIDEA"); - break; - case MITSUBISHI: - result = F("MITSUBISHI"); - break; - case MITSUBISHI2: - result = F("MITSUBISHI2"); - break; - case MITSUBISHI_AC: - result = F("MITSUBISHI_AC"); - break; - case MITSUBISHI136: - result = F("MITSUBISHI136"); - break; - case MITSUBISHI112: - result = F("MITSUBISHI112"); - break; - case MITSUBISHI_HEAVY_88: - result = F("MITSUBISHI_HEAVY_88"); - break; - case MITSUBISHI_HEAVY_152: - result = F("MITSUBISHI_HEAVY_152"); - break; - case MWM: - result = F("MWM"); - break; - case NEOCLIMA: - result = F("NEOCLIMA"); - break; - case NEC: - result = F("NEC"); - break; - case NEC_LIKE: - result = F("NEC (non-strict)"); - break; - case NIKAI: - result = F("NIKAI"); - break; - case PANASONIC: - result = F("PANASONIC"); - break; - case PANASONIC_AC: - result = F("PANASONIC_AC"); - break; - case PIONEER: - result = F("PIONEER"); - break; - case PRONTO: - result = F("PRONTO"); - break; - case RAW: - result = F("RAW"); - break; - case RC5: - result = F("RC5"); - break; - case RC5X: - result = F("RC5X"); - break; - case RC6: - result = F("RC6"); - break; - case RCMM: - result = F("RCMM"); - break; - case SAMSUNG: - result = F("SAMSUNG"); - break; - case SAMSUNG36: - result = F("SAMSUNG36"); - break; - case SAMSUNG_AC: - result = F("SAMSUNG_AC"); - break; - case SANYO: - result = F("SANYO"); - break; - case SANYO_LC7461: - result = F("SANYO_LC7461"); - break; - case SHARP: - result = F("SHARP"); - break; - case SHARP_AC: - result = F("SHARP_AC"); - break; - case SHERWOOD: - result = F("SHERWOOD"); - break; - case SONY: - result = F("SONY"); - break; - case SONY_38K: - result = F("SONY_38K"); - break; - case TCL112AC: - result = F("TCL112AC"); - break; - case TECO: - result = F("TECO"); - break; - case TOSHIBA_AC: - result = F("TOSHIBA_AC"); - break; - case TROTEC: - result = F("TROTEC"); - break; - case VESTEL_AC: - result = F("VESTEL_AC"); - break; - case WHIRLPOOL_AC: - result = F("WHIRLPOOL_AC"); - break; - case WHYNTER: - result = F("WHYNTER"); - break; - case UNKNOWN: - default: - result = kUnknownStr; - break; + const char *ptr = kAllProtocolNamesStr; + if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) { + result = kUnknownStr; + } else { + for (uint16_t i = 0; i <= protocol && strlen(ptr); i++) { + if (i == protocol) { + result = ptr; + break; + } + ptr += strlen(ptr) + 1; + } } if (isRepeat) { result += kSpaceLBraceStr; @@ -525,6 +156,7 @@ bool hasACState(const decode_type_t protocol) { case HITACHI_AC: case HITACHI_AC1: case HITACHI_AC2: + case HITACHI_AC3: case HITACHI_AC424: case KELVINATOR: case MITSUBISHI136: @@ -851,6 +483,15 @@ namespace irutils { default: return kUnknownStr; } break; + case decode_type_t::HITACHI_AC1: + switch (model) { + case hitachi_ac1_remote_model_t::R_LT0541_HTA_A: + return F("R-LT0541-HTA-A"); + case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: + return F("R-LT0541-HTA-B"); + default: return kUnknownStr; + } + break; case decode_type_t::LG: case decode_type_t::LG2: switch (model) { diff --git a/lib/IRremoteESP8266-2.7.4/src/IRutils.h b/lib/IRremoteESP8266-2.7.6/src/IRutils.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/IRutils.h rename to lib/IRremoteESP8266-2.7.6/src/IRutils.h diff --git a/lib/IRremoteESP8266-2.7.4/src/i18n.h b/lib/IRremoteESP8266-2.7.6/src/i18n.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/i18n.h rename to lib/IRremoteESP8266-2.7.6/src/i18n.h diff --git a/lib/IRremoteESP8266-2.7.6/src/ir_Airwell.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Airwell.cpp new file mode 100644 index 000000000..5eb103b84 --- /dev/null +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Airwell.cpp @@ -0,0 +1,83 @@ +// Copyright 2020 David Conran + +#include "IRrecv.h" +#include "IRsend.h" + +// Airwell "Manchester code" based protocol. +// Some other Airwell products use the COOLIX protocol. +// +// Supports: +// Brand: Airwell, Model: RC08W remote + +const uint8_t kAirwellOverhead = 4; +const uint16_t kAirwellHalfClockPeriod = 950; // uSeconds +const uint16_t kAirwellHdrMark = 3 * kAirwellHalfClockPeriod; // uSeconds +const uint16_t kAirwellHdrSpace = 4 * kAirwellHalfClockPeriod; // uSeconds +const uint16_t kAirwellFooterMark = 5 * kAirwellHalfClockPeriod; // uSeconds + +#if SEND_AIRWELL +// Send an Airwell Manchester Code formatted message. +// +// Args: +// data: The message to be sent. +// nbits: The number of bits of the message to be sent. +// Typically kAirwellBits. +// repeat: The number of times the command is to be repeated. +// +// Status: BETA / Appears to be working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1069 +void IRsend::sendAirwell(uint64_t data, uint16_t nbits, uint16_t repeat) { + // Header + Data + sendManchester(kAirwellHdrMark, kAirwellHdrMark, kAirwellHalfClockPeriod, + 0, 0, data, nbits, 38000, true, repeat); + // Footer + mark(kAirwellHdrMark + kAirwellHalfClockPeriod); + space(kDefaultMessageGap); // A guess. +} +#endif + +#if DECODE_AIRWELL +// Decode the supplied Airwell "Manchester code" message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// offset: The starting index to use when attempting to decode the raw data. +// Typically/Defaults to kStartOffset. +// nbits: The number of data bits to expect. Typically kAirwellBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Appears to be working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1069 +bool IRrecv::decodeAirwell(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < nbits + kAirwellOverhead - offset) + return false; // Too short a message to match. + + // Compliance + if (strict && nbits != kAirwellBits) + return false; // Doesn't match our protocol defn. + + // Header #1 + Data #1 + Footer #1 (There are total of 3 sections) + uint16_t used = matchManchester(results->rawbuf + offset, &results->value, + results->rawlen - offset, nbits, + kAirwellHdrMark, kAirwellHdrMark, + kAirwellHalfClockPeriod, + kAirwellHdrMark, kAirwellHdrSpace, + true); + if (used == 0) return false; + offset += used; + + // Success + results->decode_type = decode_type_t::AIRWELL; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Aiwa.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Aiwa.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Aiwa.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Aiwa.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Amcor.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Amcor.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Amcor.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Amcor.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Amcor.h b/lib/IRremoteESP8266-2.7.6/src/ir_Amcor.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Amcor.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Amcor.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Argo.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Argo.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Argo.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Argo.cpp index 11e8336e3..0c4656cb5 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Argo.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Argo.cpp @@ -38,7 +38,7 @@ using irutils::setBits; // Args: // data: An array of kArgoStateLength bytes containing the IR command. // -// Status: ALPHA / Untested. +// Status: BETA / Probably works. void IRsend::sendArgo(const unsigned char data[], const uint16_t nbytes, const uint16_t repeat) { @@ -385,7 +385,7 @@ String IRArgoAC::toString(void) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: ALPHA / Probably doesn't work. +// Status: BETA / Probably works. // // Note: // This decoder is based soley off sendArgo(). We have no actual captures diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Argo.h b/lib/IRremoteESP8266-2.7.6/src/ir_Argo.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Argo.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Argo.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Carrier.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Carrier.cpp similarity index 97% rename from lib/IRremoteESP8266-2.7.4/src/ir_Carrier.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Carrier.cpp index 7b2b00d58..293711a03 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Carrier.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Carrier.cpp @@ -30,7 +30,7 @@ const uint16_t kCarrierAcGap = 20000; // nbits: The bit size of the message being sent. typically kCarrierAcBits. // repeat: The number of times the message is to be repeated. // -// Status: BETA / Appears to work on real devices. +// Status: STABLE / Work on real devices. // void IRsend::sendCarrierAC(uint64_t data, uint16_t nbits, uint16_t repeat) { for (uint16_t r = 0; r <= repeat; r++) { @@ -61,7 +61,7 @@ void IRsend::sendCarrierAC(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: ALPHA / Untested. +// Status: BETA / Probably works. // bool IRrecv::decodeCarrierAC(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Coolix.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Coolix.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Coolix.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Coolix.cpp index a8d98301c..65535442a 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Coolix.cpp @@ -586,7 +586,7 @@ String IRCoolixAC::toString(void) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Probably working. +// Status: STABLE / Known Working. bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { // The protocol sends the data normal + inverted, alternating on diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Coolix.h b/lib/IRremoteESP8266-2.7.6/src/ir_Coolix.h similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Coolix.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Coolix.h index c2fb2034d..6f7778416 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Coolix.h +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Coolix.h @@ -23,6 +23,7 @@ // Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C // Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016) // Brand: Tokio, Model: AATOEMF17-12CHR1SW split-type RG51|50/BGE Remote +// Brand: Airwell, Model: RC08B remote // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/484 // Kudos: diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Daikin.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Daikin.cpp similarity index 88% rename from lib/IRremoteESP8266-2.7.4/src/ir_Daikin.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Daikin.cpp index 4941fe977..c18e77569 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Daikin.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Daikin.cpp @@ -593,7 +593,7 @@ bool IRrecv::decodeDaikin(decode_results *results, uint16_t offset, // Args: // data: An array of kDaikin2StateLength bytes containing the IR command. // -// Status: BETA/Appears to work. +// Status: STABLE / Expected to work. // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/582 @@ -1219,7 +1219,7 @@ String IRDaikin2::toString(void) { // - Daikin FTXZ25NV1B, FTXZ35NV1B, FTXZ50NV1B Aircon // - Daikin ARC477A1 remote // -// Status: BETA / Work as expected. +// Status: STABLE / Works as expected. // // Ref: // https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote @@ -1565,7 +1565,7 @@ String IRDaikin216::toString(void) { // Supported devices: // - Daikin ARC433B69 remote. // -// Status: BETA / Should be working. +// Status: STABLE / Should be working. // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/689 @@ -2283,7 +2283,7 @@ String IRDaikin176::toString(void) { // Supported devices: // - Daikin BRC4C153 remote. // -// Status: BETA / Probably works. +// Status: STABLE / Expected to work. // bool IRrecv::decodeDaikin176(decode_results *results, uint16_t offset, @@ -3187,3 +3187,420 @@ String IRDaikin152::toString(void) { result += addBoolToString(getComfort(), kComfortStr); return result; } + +#if SEND_DAIKIN64 +// Send a Daikin 64 bit A/C message. +// +// Args: +// data: A uint64_t containing the IR command/code. +// +// Supported devices: +// - Daikin FFN-C. +// +// Status: Beta / Probably Working. +// +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 +void IRsend::sendDaikin64(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + enableIROut(kDaikin64Freq); + for (uint16_t r = 0; r <= repeat; r++) { + for (uint8_t i = 0; i < 2; i++) { + // Leader + mark(kDaikin64LdrMark); + space(kDaikin64LdrSpace); + } + // Header + Data + Footer #1 + sendGeneric(kDaikin64HdrMark, kDaikin64HdrSpace, + kDaikin64BitMark, kDaikin64OneSpace, + kDaikin64BitMark, kDaikin64ZeroSpace, + kDaikin64BitMark, kDaikin64Gap, + data, nbits, kDaikin64Freq, false, 0, 50); + // Footer #2 + mark(kDaikin64HdrMark); + space(kDefaultMessageGap); // A guess of the gap between messages. + } +} +#endif // SEND_DAIKIN64 + +#if DECODE_DAIKIN64 +// Decode the supplied Daikin 64 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// offset: The starting index to use when attempting to decode the raw data. +// Typically/Defaults to kStartOffset. +// nbits: Nr. of bits to expect in the data portion. (kDaikin64Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin FFN-C. +// +// Status: Beta / Probably Working. +// +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 +bool IRrecv::decodeDaikin64(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kDaikin64Overhead - offset) + return false; // Too short a message to match. + // Compliance + if (strict && nbits != kDaikin64Bits) + return false; + + // Leader + for (uint8_t i = 0; i < 2; i++) { + if (!matchMark(results->rawbuf[offset++], kDaikin64LdrMark)) + return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin64LdrSpace)) + return false; + } + // Header + Data + Footer #1 + uint16_t used = matchGeneric(results->rawbuf + offset, &results->value, + results->rawlen - offset, nbits, + kDaikin64HdrMark, kDaikin64HdrSpace, + kDaikin64BitMark, kDaikin64OneSpace, + kDaikin64BitMark, kDaikin64ZeroSpace, + kDaikin64BitMark, kDaikin64Gap, + false, _tolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + // Footer #2 + if (!matchMark(results->rawbuf[offset++], kDaikin64HdrMark)) + return false; + + // Compliance + if (strict && !IRDaikin64::validChecksum(results->value)) return false; + // Success + results->decode_type = decode_type_t::DAIKIN64; + results->bits = nbits; + results->command = 0; + results->address = 0; + return true; +} +#endif // DAIKIN64 + +// Class for handling Daikin 64 bit / 19 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin ARC480A5 remote +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +// https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.cpp +// https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.h +IRDaikin64::IRDaikin64(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRDaikin64::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN64 +void IRDaikin64::send(const uint16_t repeat) { + _irsend.sendDaikin64(getRaw(), kDaikin64Bits, repeat); +} +#endif // SEND_DAIKIN64 + +// Calc the checksum for a given state. +// Args: +// state: The value to calc the checksum of. +// Returns: +// A 4-bit checksum stored in a uint_8. +uint8_t IRDaikin64::calcChecksum(const uint64_t state) { + uint64_t data = GETBITS64(state, 0, kDaikin64ChecksumOffset); + uint8_t result = 0; + for (; data; data >>= 4) // Add each nibble together. + result += GETBITS64(data, 0, 4); + return result & 0xF; +} + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin64::validChecksum(const uint64_t state) { + // Validate the checksum of the given state. + return (GETBITS64(state, kDaikin64ChecksumOffset, + kDaikin64ChecksumSize) == calcChecksum(state)); +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin64::checksum(void) { + setBits(&remote_state, kDaikin64ChecksumOffset, kDaikin64ChecksumSize, + calcChecksum(remote_state)); +} + +void IRDaikin64::stateReset(void) { + remote_state = kDaikin64KnownGoodState; +} + +uint64_t IRDaikin64::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin64::setRaw(const uint64_t new_state) { remote_state = new_state; } + +void IRDaikin64::setPowerToggle(const bool on) { + setBit(&remote_state, kDaikin64PowerToggleBit, on); +} + +bool IRDaikin64::getPowerToggle(void) { + return GETBIT64(remote_state, kDaikin64PowerToggleBit); +} + +// Set the temp in deg C +void IRDaikin64::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikin64MinTemp); + degrees = std::min(degrees, kDaikin64MaxTemp); + setBits(&remote_state, kDaikin64TempOffset, + kDaikin64TempSize, uint8ToBcd(degrees)); +} + +uint8_t IRDaikin64::getTemp(void) { + return bcdToUint8(GETBITS64(remote_state, kDaikin64TempOffset, + kDaikin64TempSize)); +} + +uint8_t IRDaikin64::getMode(void) { + return GETBITS64(remote_state, kDaikin64ModeOffset, kDaikin64ModeSize); +} + +void IRDaikin64::setMode(const uint8_t mode) { + switch (mode) { + case kDaikin64Fan: + case kDaikin64Dry: + case kDaikin64Cool: + break; + default: + this->setMode(kDaikin64Cool); + return; + } + setBits(&remote_state, kDaikin64ModeOffset, kDaikin64ModeSize, mode); +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin64::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kDry: return kDaikin64Dry; + case stdAc::opmode_t::kFan: return kDaikin64Fan; + default: return kDaikinCool; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRDaikin64::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikin64Cool: return stdAc::opmode_t::kCool; + case kDaikin64Dry: return stdAc::opmode_t::kDry; + case kDaikin64Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +uint8_t IRDaikin64::getFan(void) { + return GETBITS64(remote_state, kDaikin64FanOffset, kDaikin64FanSize); +} + +void IRDaikin64::setFan(const uint8_t speed) { + switch (speed) { + case kDaikin64FanQuiet: + case kDaikin64FanTurbo: + case kDaikin64FanAuto: + case kDaikin64FanHigh: + case kDaikin64FanMed: + case kDaikin64FanLow: + setBits(&remote_state, kDaikin64FanOffset, kDaikin64FanSize, speed); + break; + default: + this->setFan(kDaikin64FanAuto); + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin64::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikin64FanQuiet; + case stdAc::fanspeed_t::kLow: return kDaikin64FanLow; + case stdAc::fanspeed_t::kMedium: return kDaikin64FanMed; + case stdAc::fanspeed_t::kHigh: return kDaikin64FanHigh; + case stdAc::fanspeed_t::kMax: return kDaikin64FanTurbo; + default: return kDaikin64FanAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRDaikin64::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kDaikin64FanTurbo: return stdAc::fanspeed_t::kMax; + case kDaikin64FanHigh: return stdAc::fanspeed_t::kHigh; + case kDaikin64FanMed: return stdAc::fanspeed_t::kMedium; + case kDaikin64FanLow: return stdAc::fanspeed_t::kLow; + case kDaikinFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +bool IRDaikin64::getTurbo(void) { + return getFan() == kDaikin64FanTurbo; +} + +void IRDaikin64::setTurbo(const bool on) { + if (on) { + setFan(kDaikin64FanTurbo); + } else { + if (getFan() == kDaikin64FanTurbo) setFan(kDaikin64FanAuto); + } +} + +bool IRDaikin64::getQuiet(void) { + return getFan() == kDaikin64FanQuiet; +} + +void IRDaikin64::setQuiet(const bool on) { + if (on) { + setFan(kDaikin64FanQuiet); + } else { + if (getFan() == kDaikin64FanQuiet) setFan(kDaikin64FanAuto); + } +} + +void IRDaikin64::setSwingVertical(const bool on) { + setBit(&remote_state, kDaikin64SwingVBit, on); +} + +bool IRDaikin64::getSwingVertical(void) { + return GETBIT64(remote_state, kDaikin64SwingVBit); +} + +void IRDaikin64::setSleep(const bool on) { + setBit(&remote_state, kDaikin64SleepBit, on); +} + +bool IRDaikin64::getSleep(void) { + return GETBIT64(remote_state, kDaikin64SleepBit); +} + +void IRDaikin64::setClock(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. + setBits(&remote_state, kDaikin64ClockOffset, kDaikin64ClockMinsSize, + uint8ToBcd(mins % 60)); // Mins + setBits(&remote_state, kDaikin64ClockOffset + kDaikin64ClockMinsSize, + kDaikin64ClockHoursSize, uint8ToBcd(mins / 60)); // Hours +} + +uint16_t IRDaikin64::getClock(void) { + return bcdToUint8(GETBITS64(remote_state, + kDaikin64ClockOffset + kDaikin64ClockMinsSize, + kDaikin64ClockHoursSize)) * 60 + + bcdToUint8(GETBITS64(remote_state, kDaikin64ClockOffset, + kDaikin64ClockMinsSize)); +} + +void IRDaikin64::setOnTimeEnabled(const bool on) { + setBit(&remote_state, kDaikin64OnTimeEnableBit, on); +} + +bool IRDaikin64::getOnTimeEnabled(void) { + return GETBIT64(remote_state, kDaikin64OnTimeEnableBit); +} + +uint16_t IRDaikin64::getOnTime(void) { + return bcdToUint8(GETBITS64(remote_state, kDaikin64OnTimeOffset, + kDaikin64OnTimeSize)) * 60 + + (GETBIT64(remote_state, kDaikin64OnTimeHalfHourBit) ? 30 : 0); +} + +void IRDaikin64::setOnTime(const uint16_t mins_since_midnight) { + uint16_t halfhours = mins_since_midnight / 30; + if (mins_since_midnight >= 24 * 60) halfhours = 0; // Bounds check. + setBits(&remote_state, kDaikin64OnTimeOffset, kDaikin64OnTimeSize, + uint8ToBcd(halfhours / 2)); // Hours + // Half Hour + setBit(&remote_state, kDaikin64OnTimeHalfHourBit, halfhours % 2); +} + +void IRDaikin64::setOffTimeEnabled(const bool on) { + setBit(&remote_state, kDaikin64OffTimeEnableBit, on); +} + +bool IRDaikin64::getOffTimeEnabled(void) { + return GETBIT64(remote_state, kDaikin64OffTimeEnableBit); +} + +uint16_t IRDaikin64::getOffTime(void) { + return bcdToUint8(GETBITS64(remote_state, kDaikin64OffTimeOffset, + kDaikin64OffTimeSize)) * 60 + + (GETBIT64(remote_state, kDaikin64OffTimeHalfHourBit) ? 30 : 0); +} + +void IRDaikin64::setOffTime(const uint16_t mins_since_midnight) { + uint16_t halfhours = mins_since_midnight / 30; + if (mins_since_midnight >= 24 * 60) halfhours = 0; // Bounds check. + setBits(&remote_state, kDaikin64OffTimeOffset, kDaikin64OffTimeSize, + uint8ToBcd(halfhours / 2)); // Hours + // Half Hour + setBit(&remote_state, kDaikin64OffTimeHalfHourBit, halfhours % 2); +} + +// Convert the internal state into a human readable string. +String IRDaikin64::toString(void) { + String result = ""; + result.reserve(120); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPowerToggle(), kPowerToggleStr, false); + result += addModeToString(getMode(), 0xFF, kDaikin64Cool, + 0xFF, kDaikin64Dry, kDaikin64Fan); + result += addTempToString(getTemp()); + if (!getTurbo()) { + result += addFanToString(getFan(), kDaikin64FanHigh, kDaikin64FanLow, + kDaikin64FanAuto, kDaikin64FanQuiet, + kDaikin64FanMed); + } else { + result += addIntToString(getFan(), kFanStr); + result += kSpaceLBraceStr; + result += kTurboStr; + result += ')'; + } + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getQuiet(), kQuietStr); + result += addBoolToString(getSwingVertical(), kSwingVStr); + result += addBoolToString(getSleep(), kSleepStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + result += addLabeledString(getOnTimeEnabled() + ? minsToString(getOnTime()) : kOffStr, + kOnTimerStr); + result += addLabeledString(getOffTimeEnabled() + ? minsToString(getOffTime()) : kOffStr, + kOffTimerStr); + return result; +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikin64::toCommon(const stdAc::state_t *prev) { + stdAc::state_t result; + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::DAIKIN64; + result.model = -1; // No models used. + result.power ^= getPowerToggle(); + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = getSwingVertical() ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.turbo = getTurbo(); + result.quiet = getQuiet(); + result.sleep = getSleep() ? 0 : -1; + result.clock = getClock(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.clean = false; + result.filter = false; + result.beep = false; + result.econo = false; + result.light = false; + return result; +} diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Daikin.h b/lib/IRremoteESP8266-2.7.6/src/ir_Daikin.h similarity index 87% rename from lib/IRremoteESP8266-2.7.4/src/ir_Daikin.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Daikin.h index 42039b07b..3630aa77b 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Daikin.h +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Daikin.h @@ -17,6 +17,8 @@ // Brand: Daikin, Model: FTXB09AXVJU A/C (DAIKIN128) // Brand: Daikin, Model: BRC52B63 remote (DAIKIN128) // Brand: Daikin, Model: ARC480A5 remote (DAIKIN152) +// Brand: Daikin, Model: FFN-C/FCN-F Series A/C (DAIKIN64) +// Brand: Daikin, Model: DGS01 remote (DAIKIN64) #ifndef IR_DAIKIN_H_ #define IR_DAIKIN_H_ @@ -430,6 +432,56 @@ const uint8_t kDaikin152ComfortOffset = 1; // Mask 0b00000010 const uint8_t kDaikin152SensorByte = kDaikin152EconoByte; // Mask 0b00001000 const uint8_t kDaikin152SensorOffset = 3; // Mask 0b00001000 +const uint16_t kDaikin64HdrMark = kDaikin128HdrMark; +const uint16_t kDaikin64BitMark = kDaikin128BitMark; +const uint16_t kDaikin64HdrSpace = kDaikin128HdrSpace; +const uint16_t kDaikin64OneSpace = kDaikin128OneSpace; +const uint16_t kDaikin64ZeroSpace = kDaikin128ZeroSpace; +const uint16_t kDaikin64LdrMark = kDaikin128LeaderMark; +const uint16_t kDaikin64Gap = kDaikin128Gap; +const uint16_t kDaikin64LdrSpace = kDaikin128LeaderSpace; +const uint16_t kDaikin64Freq = kDaikin128Freq; // Hz. +const uint16_t kDaikin64Overhead = 9; + +const uint64_t kDaikin64KnownGoodState = 0x7C16161607204216; +const uint8_t kDaikin64ModeOffset = 8; +const uint8_t kDaikin64ModeSize = 4; // Mask 0b111100000000 +const uint8_t kDaikin64Dry = 0b001; +const uint8_t kDaikin64Cool = 0b010; +const uint8_t kDaikin64Fan = 0b100; +const uint8_t kDaikin64FanOffset = kDaikin64ModeOffset + kDaikin64ModeSize; +const uint8_t kDaikin64FanSize = 4; // Mask 0b1111000000000000 +const uint8_t kDaikin64FanAuto = 0b0001; +const uint8_t kDaikin64FanLow = 0b1000; +const uint8_t kDaikin64FanMed = 0b0100; +const uint8_t kDaikin64FanHigh = 0b0010; +const uint8_t kDaikin64FanQuiet = 0b1001; +const uint8_t kDaikin64FanTurbo = 0b0011; +const uint8_t kDaikin64ClockOffset = kDaikin64FanOffset + kDaikin64FanSize; +const uint8_t kDaikin64ClockMinsSize = 8; +const uint8_t kDaikin64ClockHoursSize = 8; +const uint8_t kDaikin64ClockSize = kDaikin64ClockMinsSize + + kDaikin64ClockHoursSize; // Mask 0b1111111111111111 << 15 +const uint8_t kDaikin64OnTimeOffset = kDaikin64ClockOffset + + kDaikin64ClockSize; +const uint8_t kDaikin64OnTimeSize = 6; +const uint8_t kDaikin64OnTimeHalfHourBit = kDaikin64OnTimeOffset + + kDaikin64OnTimeSize; +const uint8_t kDaikin64OnTimeEnableBit = kDaikin64OnTimeHalfHourBit + 1; +const uint8_t kDaikin64OffTimeOffset = kDaikin64OnTimeEnableBit + 1; +const uint8_t kDaikin64OffTimeSize = 6; +const uint8_t kDaikin64OffTimeHalfHourBit = kDaikin64OffTimeOffset + + kDaikin64OffTimeSize; +const uint8_t kDaikin64OffTimeEnableBit = kDaikin64OffTimeHalfHourBit + 1; +const uint8_t kDaikin64TempOffset = 48; +const uint8_t kDaikin64TempSize = 8; // Mask 0b11111111 << 47 +const uint8_t kDaikin64MinTemp = 16; // Celsius +const uint8_t kDaikin64MaxTemp = 30; // Celsius +const uint8_t kDaikin64SwingVBit = 56; +const uint8_t kDaikin64SleepBit = kDaikin64SwingVBit + 1; +const uint8_t kDaikin64PowerToggleBit = 59; +const uint8_t kDaikin64ChecksumOffset = 60; +const uint8_t kDaikin64ChecksumSize = 4; // Mask 0b1111 << 59 // Legacy defines. #define DAIKIN_COOL kDaikinCool @@ -881,4 +933,63 @@ class IRDaikin152 { void stateReset(); void checksum(); }; + +// Class to emulate a Daikin DGS01 remote. +class IRDaikin64 { + public: + explicit IRDaikin64(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + +#if SEND_DAIKIN64 + void send(const uint16_t repeat = kDaikin64DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_DAIKIN64 + void begin(); + uint64_t getRaw(); + void setRaw(const uint64_t new_state); + static uint8_t calcChecksum(const uint64_t state); + static bool validChecksum(const uint64_t state); + void setPowerToggle(const bool on); + bool getPowerToggle(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setFan(const uint8_t fan); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSleep(const bool on); + bool getSleep(void); + bool getQuiet(void); + void setQuiet(const bool on); + bool getTurbo(void); + void setTurbo(const bool on); + void setClock(const uint16_t mins_since_midnight); + uint16_t getClock(void); + void setOnTimeEnabled(const bool on); + bool getOnTimeEnabled(void); + void setOnTime(const uint16_t mins_since_midnight); + uint16_t getOnTime(void); + void setOffTimeEnabled(const bool on); + bool getOffTimeEnabled(void); + void setOffTime(const uint16_t mins_since_midnight); + uint16_t getOffTime(void); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(const stdAc::state_t *prev = NULL); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint64_t remote_state; + void stateReset(); + void checksum(); +}; #endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Denon.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Denon.cpp similarity index 98% rename from lib/IRremoteESP8266-2.7.4/src/ir_Denon.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Denon.cpp index bd4940714..6dd4cd839 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Denon.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Denon.cpp @@ -40,7 +40,7 @@ const uint64_t kDenonManufacturer = 0x2A4CULL; // nbits: Nr. of bits of data to be sent. Typically kDenonBits. // repeat: Nr. of additional times the message is to be sent. // -// Status: BETA / Should be working. +// Status: STABLE / Should be working. // // Notes: // Some Denon devices use a Kaseikyo/Panasonic 48-bit format @@ -71,7 +71,7 @@ void IRsend::sendDenon(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Should work fine. +// Status: STABLE / Should work fine. // // Ref: // https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Dish.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Dish.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Dish.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Dish.cpp index 834ceda04..291201ed6 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Dish.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Dish.cpp @@ -37,7 +37,7 @@ const uint16_t kDishRptSpace = kDishRptSpaceTicks * kDishTick; // nbits: The bit size of the command being sent. // repeat: The number of times you want the command to be repeated. // -// Status: BETA / Previously working. +// Status: STABLE / Working. // // Note: // Dishplayer is a different protocol. diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Electra.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Electra.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Electra.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Electra.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Electra.h b/lib/IRremoteESP8266-2.7.6/src/ir_Electra.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Electra.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Electra.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Epson.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Epson.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Epson.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Epson.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Fujitsu.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Fujitsu.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Fujitsu.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Fujitsu.h b/lib/IRremoteESP8266-2.7.6/src/ir_Fujitsu.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Fujitsu.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Fujitsu.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_GICable.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_GICable.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_GICable.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_GICable.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_GlobalCache.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_GlobalCache.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_GlobalCache.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Goodweather.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Goodweather.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Goodweather.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Goodweather.cpp index 8b7e0b76e..97a4a9277 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Goodweather.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Goodweather.cpp @@ -34,7 +34,7 @@ using irutils::setBits; // nbits: Nr. of bits of data in the message. (Default is kGoodweatherBits) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: ALPHA / Untested. +// Status: BETA / Needs testing on real device. // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/697 @@ -377,7 +377,7 @@ String IRGoodweatherAc::toString(void) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: ALPHA / Untested. +// Status: BETA / Probably works. bool IRrecv::decodeGoodweather(decode_results* results, uint16_t offset, const uint16_t nbits, const bool strict) { diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Goodweather.h b/lib/IRremoteESP8266-2.7.6/src/ir_Goodweather.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Goodweather.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Goodweather.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Gree.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Gree.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Gree.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Gree.cpp index 735e3b88c..81062d650 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Gree.cpp @@ -50,7 +50,7 @@ using irutils::setBits; // nbytes: Nr. of bytes of data in the array. (>=kGreeStateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: ALPHA / Untested. +// Status: STABLE / Working. // // Ref: // https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp @@ -84,7 +84,7 @@ void IRsend::sendGree(const unsigned char data[], const uint16_t nbytes, // nbits: Nr. of bits of data in the message. (Default is kGreeBits) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: ALPHA / Untested. +// Status: STABLE / Working. // // Ref: // https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Gree.h b/lib/IRremoteESP8266-2.7.6/src/ir_Gree.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Gree.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Gree.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Haier.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Haier.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Haier.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Haier.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Haier.h b/lib/IRremoteESP8266-2.7.6/src/ir_Haier.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Haier.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Haier.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Hitachi.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Hitachi.cpp similarity index 60% rename from lib/IRremoteESP8266-2.7.4/src/ir_Hitachi.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Hitachi.cpp index 02489d010..01cc49d28 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Hitachi.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Hitachi.cpp @@ -37,12 +37,22 @@ const uint16_t kHitachiAc424BitMark = 463; const uint16_t kHitachiAc424OneSpace = 1208; const uint16_t kHitachiAc424ZeroSpace = 372; +// Support for HitachiAc3 protocol +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060 +const uint16_t kHitachiAc3HdrMark = 3400; // Header +const uint16_t kHitachiAc3HdrSpace = 1660; // Header +const uint16_t kHitachiAc3BitMark = 460; +const uint16_t kHitachiAc3OneSpace = 1250; +const uint16_t kHitachiAc3ZeroSpace = 410; + using irutils::addBoolToString; using irutils::addIntToString; using irutils::addLabeledString; using irutils::addModeToString; +using irutils::addModelToString; using irutils::addFanToString; using irutils::addTempToString; +using irutils::minsToString; using irutils::setBit; using irutils::setBits; @@ -54,7 +64,7 @@ using irutils::setBits; // nbytes: Nr. of bytes of data in the array. (>=kHitachiAcStateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: ALPHA / Untested. +// Status: STABLE / Working. // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/417 @@ -80,7 +90,7 @@ void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, // nbytes: Nr. of bytes of data in the array. (>=kHitachiAc1StateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: BETA / Appears to work. +// Status: STABLE / Confirmed Working. // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/453 @@ -107,7 +117,7 @@ void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes, // nbytes: Nr. of bytes of data in the array. (>=kHitachiAc2StateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: BETA / Appears to work. +// Status: STABLE / Expected to work. // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/417 @@ -354,6 +364,358 @@ String IRHitachiAc::toString(void) { return result; } +// Class for handling the remote control on a Hitachi 13 byte A/C message. +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1056 + +IRHitachiAc1::IRHitachiAc1(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRHitachiAc1::stateReset(void) { + for (uint8_t i = 0; i < kHitachiAc1StateLength; i++) remote_state[i] = 0x00; + // Copy in a known good state. + remote_state[0] = 0xB2; + remote_state[1] = 0xAE; + remote_state[2] = 0x4D; + remote_state[3] = 0x91; + remote_state[4] = 0xF0; + remote_state[5] = 0xE1; + remote_state[6] = 0xA4; + remote_state[11] = 0x61; + remote_state[12] = 0x24; +} + +void IRHitachiAc1::begin(void) { _irsend.begin(); } + +uint8_t IRHitachiAc1::calcChecksum(const uint8_t state[], + const uint16_t length) { + uint8_t sum = 0; + for (uint16_t i = kHitachiAc1ChecksumStartByte; i < length - 1; i++) { + sum += reverseBits(GETBITS8(state[i], kLowNibble, kNibbleSize), + kNibbleSize); + sum += reverseBits(GETBITS8(state[i], kHighNibble, kNibbleSize), + kNibbleSize); + } + return reverseBits(sum, 8); +} + +void IRHitachiAc1::checksum(const uint16_t length) { + remote_state[length - 1] = calcChecksum(remote_state, length); +} + +bool IRHitachiAc1::validChecksum(const uint8_t state[], const uint16_t length) { + if (length < 2) return true; // Assume true for lengths that are too short. + return (state[length - 1] == calcChecksum(state, length)); +} + +uint8_t *IRHitachiAc1::getRaw(void) { + checksum(); + return remote_state; +} + +void IRHitachiAc1::setRaw(const uint8_t new_code[], const uint16_t length) { + memcpy(remote_state, new_code, std::min(length, kHitachiAc1StateLength)); +} + +#if SEND_HITACHI_AC +void IRHitachiAc1::send(const uint16_t repeat) { + _irsend.sendHitachiAC1(getRaw(), kHitachiAc1StateLength, repeat); + // Clear the toggle bits as we have actioned them by sending them. + setPowerToggle(false); + setSwingToggle(false); +} +#endif // SEND_HITACHI_AC + +hitachi_ac1_remote_model_t IRHitachiAc1::getModel(void) { + switch (GETBITS8(remote_state[kHitachiAc1ModelByte], kHitachiAc1ModelOffset, + kHitachiAc1ModelSize)) { + case kHitachiAc1Model_B: return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; + default: return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; + } +} + +void IRHitachiAc1::setModel(const hitachi_ac1_remote_model_t model) { + uint8_t value = 0; + switch (model) { + case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: + value = kHitachiAc1Model_B; + break; + default: + value = kHitachiAc1Model_A; // i.e. 'A' mode. + } + setBits(&remote_state[kHitachiAc1ModelByte], kHitachiAc1ModelOffset, + kHitachiAc1ModelSize, value); +} + +bool IRHitachiAc1::getPower(void) { + return GETBIT8(remote_state[kHitachiAc1PowerByte], kHitachiAc1PowerOffset); +} + +void IRHitachiAc1::setPower(const bool on) { + // If the power changes, set the power toggle bit. + if (on != getPower()) setPowerToggle(true); + setBit(&remote_state[kHitachiAc1PowerByte], kHitachiAc1PowerOffset, on); +} + +bool IRHitachiAc1::getPowerToggle(void) { + return GETBIT8(remote_state[kHitachiAc1PowerByte], + kHitachiAc1PowerToggleOffset); +} + +void IRHitachiAc1::setPowerToggle(const bool on) { + setBit(&remote_state[kHitachiAc1PowerByte], kHitachiAc1PowerToggleOffset, on); +} + +void IRHitachiAc1::on(void) { setPower(true); } + +void IRHitachiAc1::off(void) { setPower(false); } + +uint8_t IRHitachiAc1::getMode(void) { + return GETBITS8(remote_state[kHitachiAc1ModeByte], kHitachiAc1ModeOffset, + kHitachiAc1ModeSize); +} + +void IRHitachiAc1::setMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc1Auto: + setTemp(kHitachiAc1TempAuto); + // FALL THRU + case kHitachiAc1Fan: + case kHitachiAc1Heat: + case kHitachiAc1Cool: + case kHitachiAc1Dry: + setBits(&remote_state[kHitachiAc1ModeByte], kHitachiAc1ModeOffset, + kHitachiAc1ModeSize, mode); + setSleep(getSleep()); // Correct the sleep mode if required. + setFan(getFan()); // Correct the fan speed if required. + break; + default: setMode(kHitachiAc1Auto); + } + switch (mode) { + case kHitachiAc1Fan: + case kHitachiAc1Heat: + // Auto fan speed not available in these modes, change if needed. + if (getFan() == kHitachiAc1FanAuto) setFan(kHitachiAc1FanLow); + } +} + +uint8_t IRHitachiAc1::getTemp(void) { + return reverseBits(GETBITS8(remote_state[kHitachiAc1TempByte], + kHitachiAc1TempOffset, kHitachiAc1TempSize), + kHitachiAc1TempSize) + kHitachiAc1TempDelta; +} + +void IRHitachiAc1::setTemp(const uint8_t celsius) { + if (getMode() == kHitachiAc1Auto) return; // Can't change temp in Auto mode. + uint8_t temp = std::min(celsius, kHitachiAcMaxTemp); + temp = std::max(temp, kHitachiAcMinTemp); + temp -= kHitachiAc1TempDelta; + temp = reverseBits(temp, kHitachiAc1TempSize); + setBits(&remote_state[kHitachiAc1TempByte], kHitachiAc1TempOffset, + kHitachiAc1TempSize, temp); +} + +uint8_t IRHitachiAc1::getFan(void) { + return GETBITS8(remote_state[kHitachiAc1FanByte], kHitachiAc1FanOffset, + kHitachiAc1FanSize); +} + +void IRHitachiAc1::setFan(const uint8_t speed, const bool force) { + if (!force) { + switch (getMode()) { + case kHitachiAc1Dry: + setFan(kHitachiAc1FanLow, true); // Dry is locked to Low speed. + return; + case kHitachiAc1Auto: + setFan(kHitachiAc1FanAuto, true); // Auto is locked to Auto speed. + return; + } + } + switch (speed) { + case kHitachiAc1FanAuto: + switch (getMode()) { + case kHitachiAc1Heat: + case kHitachiAc1Fan: return; // Auto speed not allowed in these modes. + } + // FALL THRU + case kHitachiAc1FanHigh: + case kHitachiAc1FanMed: + case kHitachiAc1FanLow: + setBits(&remote_state[kHitachiAc1FanByte], kHitachiAc1FanOffset, + kHitachiAc1FanSize, speed); + break; + default: setFan(kHitachiAc1FanAuto); + } +} + +bool IRHitachiAc1::getSwingToggle(void) { + return GETBIT8(remote_state[kHitachiAc1SwingByte], + kHitachiAc1SwingToggleOffset); +} + +void IRHitachiAc1::setSwingToggle(const bool toggle) { + setBit(&remote_state[kHitachiAc1SwingByte], kHitachiAc1SwingToggleOffset, + toggle); +} + +bool IRHitachiAc1::getSwingV(void) { + return GETBIT8(remote_state[kHitachiAc1SwingByte], kHitachiAc1SwingVOffset); +} + +void IRHitachiAc1::setSwingV(const bool on) { + setBit(&remote_state[kHitachiAc1SwingByte], kHitachiAc1SwingVOffset, on); +} + +bool IRHitachiAc1::getSwingH(void) { + return GETBIT8(remote_state[kHitachiAc1SwingByte], kHitachiAc1SwingHOffset); +} + +void IRHitachiAc1::setSwingH(const bool on) { + setBit(&remote_state[kHitachiAc1SwingByte], kHitachiAc1SwingHOffset, on); +} + +uint8_t IRHitachiAc1::getSleep(void) { + return GETBITS8(remote_state[kHitachiAc1SleepByte], kHitachiAc1SleepOffset, + kHitachiAc1SleepSize); +} + +void IRHitachiAc1::setSleep(const uint8_t mode) { + // Sleep modes only available in Auto & Cool modes, otherwise it's off. + switch (getMode()) { + case kHitachiAc1Auto: + case kHitachiAc1Cool: + setBits(&remote_state[kHitachiAc1SleepByte], kHitachiAc1SleepOffset, + kHitachiAc1SleepSize, std::min(mode, kHitachiAc1Sleep4)); + break; + default: + setBits(&remote_state[kHitachiAc1SleepByte], kHitachiAc1SleepOffset, + kHitachiAc1SleepSize, kHitachiAc1SleepOff); + } +} + +void IRHitachiAc1::setOnTimer(const uint16_t mins) { + const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize); + remote_state[kHitachiAc1OnTimerLowByte] = GETBITS16(mins_lsb, 8, 8); + remote_state[kHitachiAc1OnTimerHighByte] = GETBITS16(mins_lsb, 0, 8); +} + +uint16_t IRHitachiAc1::getOnTimer(void) { + return reverseBits( + (remote_state[kHitachiAc1OnTimerLowByte] << 8) | + remote_state[kHitachiAc1OnTimerHighByte], kHitachiAc1TimerSize); +} + +void IRHitachiAc1::setOffTimer(const uint16_t mins) { + const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize); + remote_state[kHitachiAc1OffTimerLowByte] = GETBITS16(mins_lsb, 8, 8); + remote_state[kHitachiAc1OffTimerHighByte] = GETBITS16(mins_lsb, 0, 8); +} + +uint16_t IRHitachiAc1::getOffTimer(void) { + return reverseBits( + (remote_state[kHitachiAc1OffTimerLowByte] << 8) | + remote_state[kHitachiAc1OffTimerHighByte], kHitachiAc1TimerSize); +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRHitachiAc1::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHitachiAc1Cool; + case stdAc::opmode_t::kHeat: return kHitachiAc1Heat; + case stdAc::opmode_t::kDry: return kHitachiAc1Dry; + case stdAc::opmode_t::kFan: return kHitachiAc1Fan; + default: return kHitachiAc1Auto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRHitachiAc1::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kHitachiAc1FanLow; + case stdAc::fanspeed_t::kMedium: return kHitachiAc1FanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kHitachiAc1FanHigh; + default: return kHitachiAc1FanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRHitachiAc1::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc1Cool: return stdAc::opmode_t::kCool; + case kHitachiAc1Heat: return stdAc::opmode_t::kHeat; + case kHitachiAc1Dry: return stdAc::opmode_t::kDry; + case kHitachiAc1Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRHitachiAc1::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAc1FanHigh: return stdAc::fanspeed_t::kMax; + case kHitachiAc1FanMed: return stdAc::fanspeed_t::kMedium; + case kHitachiAc1FanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRHitachiAc1::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::HITACHI_AC1; + result.model = this->getModel(); + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingV() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingH() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRHitachiAc1::toString(void) { + String result = ""; + result.reserve(170); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::HITACHI_AC1, getModel(), false); + result += addBoolToString(getPower(), kPowerStr); + result += addBoolToString(getPowerToggle(), kPowerToggleStr); + result += addModeToString(getMode(), kHitachiAc1Auto, kHitachiAc1Cool, + kHitachiAc1Heat, kHitachiAc1Dry, kHitachiAc1Fan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kHitachiAc1FanHigh, kHitachiAc1FanLow, + kHitachiAc1FanAuto, kHitachiAc1FanAuto, + kHitachiAc1FanMed); + result += addBoolToString(getSwingToggle(), kSwingVToggleStr); + result += addBoolToString(getSwingV(), kSwingVStr); + result += addBoolToString(getSwingH(), kSwingHStr); + result += addLabeledString(getSleep() ? uint64ToString(getSleep()) : kOffStr, + kSleepStr); + result += addLabeledString(getOnTimer() ? minsToString(getOnTimer()) + : kOffStr, + kOnTimerStr); + result += addLabeledString(getOffTimer() ? minsToString(getOffTimer()) + : kOffStr, + kOffTimerStr); + return result; +} + #if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2) // Decode the supplied Hitachi A/C message. // @@ -367,7 +729,7 @@ String IRHitachiAc::toString(void) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Probably works. +// Status: STABLE / Expected to work. // // Supported devices: // Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA @@ -412,6 +774,9 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset, if (nbits / 8 == kHitachiAcStateLength && !IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength)) return false; + if (nbits / 8 == kHitachiAc1StateLength && + !IRHitachiAc1::validChecksum(results->state, kHitachiAc1StateLength)) + return false; } // Success @@ -800,3 +1165,142 @@ String IRHitachiAc424::toString(void) { result += ')'; return result; } + + +#if SEND_HITACHI_AC3 +// Send HITACHI_AC3 messages +// +// Note: This protocol is almost exactly the same as HitachiAC424 except this +// variant has subtle timing differences. +// There are five(5) typical sizes: +// * kHitachiAc3MinStateLength (Cancel Timer) +// * kHitachiAc3MinStateLength + 2 (Change Temp) +// * kHitachiAc3StateLength - 6 (Change Mode) +// * kHitachiAc3StateLength- 4 (Normal) +// * kHitachiAc3StateLength (Set Timer) +// +// Args: +// data: An array of bytes containing the IR command. +// It is assumed to be in LSBF order for this code. +// nbytes: Nr. of bytes of data in the array. +// repeat: Nr. of times the message is to be repeated. +// +// Status: STABLE / Working fine. +void IRsend::sendHitachiAc3(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // Header + Data + Footer + sendGeneric(kHitachiAc3HdrMark, kHitachiAc3HdrSpace, + kHitachiAc3BitMark, kHitachiAc3OneSpace, + kHitachiAc3BitMark, kHitachiAc3ZeroSpace, + kHitachiAc3BitMark, kHitachiAcMinGap, + data, nbytes, // Bytes + kHitachiAcFreq, false, repeat, kDutyDefault); +} +#endif // SEND_HITACHI_AC3 + + +// Class for handling the remote control on a Hitachi_AC3 53 A/C message +IRHitachiAc3::IRHitachiAc3(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +// Reset to auto fan, cooling, 23° Celcius +void IRHitachiAc3::stateReset(void) { + for (uint8_t i = 0; i < kHitachiAc3StateLength; i++) + remote_state[i] = 0x00; + remote_state[0] = 0x01; + remote_state[1] = 0x10; + remote_state[3] = 0x40; + remote_state[5] = 0xFF; + remote_state[7] = 0xE8; + remote_state[9] = 0x89; + remote_state[11] = 0x0B; + remote_state[13] = 0x3F; + remote_state[15] = 0x15; + remote_state[21] = 0x4B; + remote_state[23] = 0x18; + setInvertedStates(); +} + +void IRHitachiAc3::setInvertedStates(const uint16_t length) { + for (uint8_t i = 3; i < length - 1; i += 2) + remote_state[i + 1] = ~remote_state[i]; +} + +bool IRHitachiAc3::hasInvertedStates(const uint8_t state[], + const uint16_t length) { + for (uint8_t i = 3; i < length - 1; i += 2) + if ((state[i + 1] ^ state[i]) != 0xFF) return false; + return true; +} + +void IRHitachiAc3::begin(void) { _irsend.begin(); } + +uint8_t *IRHitachiAc3::getRaw(void) { + setInvertedStates(); + return remote_state; +} + +void IRHitachiAc3::setRaw(const uint8_t new_code[], const uint16_t length) { + memcpy(remote_state, new_code, std::min(length, kHitachiAc3StateLength)); +} + +#if DECODE_HITACHI_AC3 +// Decode the supplied HitachiAc3 A/C message. +// +// Note: This protocol is almost exactly the same as HitachiAC424 except this +// variant has subtle timing differences and multiple lengths. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// offset: The starting index to use when attempting to decode the raw data. +// Typically/Defaults to kStartOffset. +// nbits: The number of data bits to expect. Typically kHitachiAc3Bits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Works fine. +// +// Supported devices: +// Hitachi PC-LH3B +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1060 +bool IRrecv::decodeHitachiAc3(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset) + return false; // Too short a message to match. + if (strict) { + // Check the requested bit length. + switch (nbits) { + case kHitachiAc3MinBits: // Cancel Timer (Min Size) + case kHitachiAc3MinBits + 2 * 8: // Change Temp + case kHitachiAc3Bits - 6 * 8: // Change Mode + case kHitachiAc3Bits - 4 * 8: // Normal + case kHitachiAc3Bits: // Set Temp (Max Size) + break; + default: return false; + } + } + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHitachiAc3HdrMark, kHitachiAc3HdrSpace, + kHitachiAc3BitMark, kHitachiAc3OneSpace, + kHitachiAc3BitMark, kHitachiAc3ZeroSpace, + kHitachiAc3BitMark, kHitachiAcMinGap, true, + kUseDefTol, 0, false)) + return false; // We failed to find any data. + + // Compliance + if (strict && !IRHitachiAc3::hasInvertedStates(results->state, nbits / 8)) + return false; + // Success + results->decode_type = decode_type_t::HITACHI_AC3; + results->bits = nbits; + return true; +} +#endif // DECODE_HITACHI_AC3 diff --git a/lib/IRremoteESP8266-2.7.6/src/ir_Hitachi.h b/lib/IRremoteESP8266-2.7.6/src/ir_Hitachi.h new file mode 100644 index 000000000..0db3552ff --- /dev/null +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Hitachi.h @@ -0,0 +1,330 @@ +// Hitachi A/C +// +// Copyright 2018-2020 David Conran + +// Supports: +// Brand: Hitachi, Model: RAS-35THA6 remote +// Brand: Hitachi, Model: LT0541-HTA remote +// Brand: Hitachi, Model: Series VI A/C (Circa 2007) +// Brand: Hitachi, Model: RAR-8P2 remote +// Brand: Hitachi, Model: RAS-AJ25H A/C +// Brand: Hitachi, Model: PC-LH3B (HITACHI_AC3) +// Brand: Hitachi, Model: KAZE-312KSDP A/C (HITACHI_AC1) +// Brand: Hitachi, Model: R-LT0541-HTA/Y.K.1.1-1 V2.3 remote (HITACHI_AC1) + +#ifndef IR_HITACHI_H_ +#define IR_HITACHI_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Constants +const uint16_t kHitachiAcFreq = 38000; // Hz. +const uint8_t kHitachiAcAuto = 2; +const uint8_t kHitachiAcHeat = 3; +const uint8_t kHitachiAcCool = 4; +const uint8_t kHitachiAcDry = 5; +const uint8_t kHitachiAcFan = 0xC; +const uint8_t kHitachiAcFanAuto = 1; +const uint8_t kHitachiAcFanLow = 2; +const uint8_t kHitachiAcFanMed = 3; +const uint8_t kHitachiAcFanHigh = 5; +const uint8_t kHitachiAcMinTemp = 16; // 16C +const uint8_t kHitachiAcMaxTemp = 32; // 32C +const uint8_t kHitachiAcAutoTemp = 23; // 23C +const uint8_t kHitachiAcPowerOffset = 0; +const uint8_t kHitachiAcSwingOffset = 7; + +// HitachiAc424 +// Byte[11] +const uint8_t kHitachiAc424ButtonByte = 11; +const uint8_t kHitachiAc424ButtonPowerMode = 0x13; +const uint8_t kHitachiAc424ButtonFan = 0x42; +const uint8_t kHitachiAc424ButtonTempDown = 0x43; +const uint8_t kHitachiAc424ButtonTempUp = 0x44; +const uint8_t kHitachiAc424ButtonSwingV = 0x81; + +// Byte[13] +const uint8_t kHitachiAc424TempByte = 13; +const uint8_t kHitachiAc424TempOffset = 2; +const uint8_t kHitachiAc424TempSize = 6; +const uint8_t kHitachiAc424MinTemp = 16; // 16C +const uint8_t kHitachiAc424MaxTemp = 32; // 32C +const uint8_t kHitachiAc424FanTemp = 27; // 27C + +// Byte[25] +const uint8_t kHitachiAc424ModeByte = 25; +const uint8_t kHitachiAc424Fan = 1; +const uint8_t kHitachiAc424Cool = 3; +const uint8_t kHitachiAc424Dry = 5; +const uint8_t kHitachiAc424Heat = 6; +const uint8_t kHitachiAc424FanByte = kHitachiAc424ModeByte; +const uint8_t kHitachiAc424FanMin = 1; +const uint8_t kHitachiAc424FanLow = 2; +const uint8_t kHitachiAc424FanMedium = 3; +const uint8_t kHitachiAc424FanHigh = 4; +const uint8_t kHitachiAc424FanAuto = 5; +const uint8_t kHitachiAc424FanMax = 6; +const uint8_t kHitachiAc424FanMaxDry = 2; +// Byte[27] +const uint8_t kHitachiAc424PowerByte = 27; +const uint8_t kHitachiAc424PowerOn = 0xF1; +const uint8_t kHitachiAc424PowerOff = 0xE1; + +// HitachiAc1 +// Byte[3] (Model) +const uint8_t kHitachiAc1ModelByte = 3; +const uint8_t kHitachiAc1ModelOffset = 6; // Mask 0b11000000 +const uint8_t kHitachiAc1Model_A = 0b10; +const uint8_t kHitachiAc1Model_B = 0b01; +const uint8_t kHitachiAc1ModelSize = 2; + +// Byte[5] (Mode & Fan) +const uint8_t kHitachiAc1ModeByte = 5; +const uint8_t kHitachiAc1ModeOffset = 4; +const uint8_t kHitachiAc1ModeSize = 4; // Mask 0b11110000 +const uint8_t kHitachiAc1Dry = 0b0010; // 2 +const uint8_t kHitachiAc1Fan = 0b0100; // 4 +const uint8_t kHitachiAc1Cool = 0b0110; // 6 +const uint8_t kHitachiAc1Heat = 0b1001; // 9 +const uint8_t kHitachiAc1Auto = 0b1110; // 14 +const uint8_t kHitachiAc1FanByte = kHitachiAc1ModeByte; +const uint8_t kHitachiAc1FanOffset = 0; +const uint8_t kHitachiAc1FanSize = 4; // Mask 0b0001111 +const uint8_t kHitachiAc1FanAuto = 1; // 0b0001 +const uint8_t kHitachiAc1FanHigh = 2; // 0b0010 +const uint8_t kHitachiAc1FanMed = 4; // 0b0100 +const uint8_t kHitachiAc1FanLow = 8; // 0b1000 +// Byte[6] (Temperature) +// Note: Temp is stored in LSB order. +const uint8_t kHitachiAc1TempByte = 6; +const uint8_t kHitachiAc1TempOffset = 2; +const uint8_t kHitachiAc1TempSize = 5; // Mask 0b01111100 +const uint8_t kHitachiAc1TempDelta = 7; +const uint8_t kHitachiAc1TempAuto = 25; // Celsius +// Note: Timers are nr. of minutes & stored in LSB order. +// Byte[7-8] (Off Timer) +const uint8_t kHitachiAc1TimerSize = 16; // Mask 0b1111111111111111 +const uint8_t kHitachiAc1OffTimerLowByte = 7; +const uint8_t kHitachiAc1OffTimerHighByte = 8; +// Byte[9-10] (On Timer) +const uint8_t kHitachiAc1OnTimerLowByte = 9; +const uint8_t kHitachiAc1OnTimerHighByte = 10; +// Byte[11] (Power/Swing/Sleep) +const uint8_t kHitachiAc1PowerByte = 11; +const uint8_t kHitachiAc1PowerOffset = 5; // Mask 0b00100000 +const uint8_t kHitachiAc1PowerToggleOffset = 4; // Mask 0b00010000 +const uint8_t kHitachiAc1SwingByte = kHitachiAc1PowerByte; +const uint8_t kHitachiAc1SwingHOffset = 7; // Mask 0b10000000 +const uint8_t kHitachiAc1SwingVOffset = 6; // Mask 0b01000000 +const uint8_t kHitachiAc1SwingToggleOffset = 0; // Mask 0b00000001 +const uint8_t kHitachiAc1SleepByte = kHitachiAc1PowerByte; +const uint8_t kHitachiAc1SleepOffset = 1; // Mask 0b00001110 +const uint8_t kHitachiAc1SleepSize = 3; // Mask 0b00001110 +const uint8_t kHitachiAc1SleepOff = 0b000; +const uint8_t kHitachiAc1Sleep1 = 0b001; +const uint8_t kHitachiAc1Sleep2 = 0b010; +const uint8_t kHitachiAc1Sleep3 = 0b011; +const uint8_t kHitachiAc1Sleep4 = 0b100; +// Byte[12] (Checksum) +const uint8_t kHitachiAc1ChecksumStartByte = 5; + + +// Classes +class IRHitachiAc { + public: + explicit IRHitachiAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); +#if SEND_HITACHI_AC + void send(const uint16_t repeat = kHitachiAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_HITACHI_AC + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kHitachiAcStateLength); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kHitachiAcStateLength); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kHitachiAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint8_t remote_state[kHitachiAcStateLength]; + void checksum(const uint16_t length = kHitachiAcStateLength); + uint8_t _previoustemp; +}; + +class IRHitachiAc1 { + public: + explicit IRHitachiAc1(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); +#if SEND_HITACHI_AC1 + void send(const uint16_t repeat = kHitachiAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_HITACHI_AC1 + void begin(void); + void on(void); + void off(void); + void setModel(const hitachi_ac1_remote_model_t model); + hitachi_ac1_remote_model_t getModel(void); + void setPower(const bool on); + bool getPower(void); + void setPowerToggle(const bool on); + bool getPowerToggle(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t speed, const bool force = false); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingToggle(const bool toggle); + bool getSwingToggle(void); + void setSwingV(const bool on); + bool getSwingV(void); + void setSwingH(const bool on); + bool getSwingH(void); + void setSleep(const uint8_t mode); + uint8_t getSleep(void); + void setOnTimer(const uint16_t mins); + uint16_t getOnTimer(void); + void setOffTimer(const uint16_t mins); + uint16_t getOffTimer(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kHitachiAc1StateLength); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kHitachiAc1StateLength); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kHitachiAc1StateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint8_t remote_state[kHitachiAc1StateLength]; + void checksum(const uint16_t length = kHitachiAc1StateLength); +}; + +class IRHitachiAc424 { + public: + explicit IRHitachiAc424(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); +#if SEND_HITACHI_AC424 + void send(const uint16_t repeat = kHitachiAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_HITACHI_AC424 + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp, bool setPrevious = true); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + uint8_t getButton(void); + void setButton(const uint8_t button); + void setSwingVToggle(const bool on); + bool getSwingVToggle(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kHitachiAc424StateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint8_t remote_state[kHitachiAc424StateLength]; + void setInvertedStates(void); + uint8_t _previoustemp; +}; + +class IRHitachiAc3 { + public: + explicit IRHitachiAc3(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); +#if SEND_HITACHI_AC3 + void send(const uint16_t repeat = kHitachiAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_HITACHI_AC3 + void begin(void); + uint8_t getMode(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kHitachiAc3StateLength); + static bool hasInvertedStates(const uint8_t state[], const uint16_t length); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint8_t remote_state[kHitachiAc3StateLength]; + void setInvertedStates(const uint16_t length = kHitachiAc3StateLength); +}; + +#endif // IR_HITACHI_H_ diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Inax.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Inax.cpp similarity index 98% rename from lib/IRremoteESP8266-2.7.4/src/ir_Inax.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Inax.cpp index 9822e750b..b57742a12 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Inax.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Inax.cpp @@ -31,7 +31,7 @@ const uint16_t kInaxMinGap = 40000; // nbits: The bit size of the message being sent. typically kInaxBits. // repeat: The number of times the message is to be repeated. // -// Status: BETA / Should be working. +// Status: STABLE / Working. // // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/706 void IRsend::sendInax(const uint64_t data, const uint16_t nbits, diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_JVC.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_JVC.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_JVC.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_JVC.cpp index 128efdf08..7e1e5de27 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_JVC.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_JVC.cpp @@ -78,7 +78,7 @@ void IRsend::sendJVC(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // A raw JVC message. // -// Status: BETA / Should work fine. +// Status: STABLE / Works fine. // // Ref: // http://www.sbprojects.com/knowledge/ir/jvc.php diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Kelvinator.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Kelvinator.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Kelvinator.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Kelvinator.h b/lib/IRremoteESP8266-2.7.6/src/ir_Kelvinator.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Kelvinator.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Kelvinator.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_LG.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_LG.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_LG.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_LG.cpp index b30dba082..1024548ca 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_LG.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_LG.cpp @@ -157,7 +157,7 @@ void IRsend::sendLG2(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // A raw 28-bit LG message code suitable for sendLG() etc. // -// Status: BETA / Should work. +// Status: STABLE / Works. // // Notes: // e.g. Sequence of bits = address + command + checksum. diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_LG.h b/lib/IRremoteESP8266-2.7.6/src/ir_LG.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_LG.h rename to lib/IRremoteESP8266-2.7.6/src/ir_LG.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Lasertag.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Lasertag.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Lasertag.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Lasertag.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Lego.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Lego.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Lego.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Lego.cpp index 502352218..b2d2f2d0e 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Lego.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Lego.cpp @@ -74,7 +74,7 @@ void IRsend::sendLegoPf(const uint64_t data, const uint16_t nbits, // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Appears to work. +// Status: STABLE / Appears to work. bool IRrecv::decodeLegoPf(decode_results* results, uint16_t offset, const uint16_t nbits, const bool strict) { // Check if can possibly be a valid LEGO message. diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Lutron.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Lutron.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Lutron.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Lutron.cpp index 094932e5d..8ea4f0b3a 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Lutron.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Lutron.cpp @@ -65,7 +65,7 @@ void IRsend::sendLutron(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: ALPHA / Untested. +// Status: STABLE / Working. // // Notes: // diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_MWM.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_MWM.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_MWM.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_MWM.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Magiquest.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Magiquest.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Magiquest.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Magiquest.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Magiquest.h b/lib/IRremoteESP8266-2.7.6/src/ir_Magiquest.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Magiquest.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Magiquest.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Midea.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Midea.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Midea.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Midea.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Midea.h b/lib/IRremoteESP8266-2.7.6/src/ir_Midea.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Midea.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Midea.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Mitsubishi.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Mitsubishi.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Mitsubishi.cpp index 68098f40a..b8d62e109 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Mitsubishi.cpp @@ -107,7 +107,7 @@ using irutils::setBits; // nbits: Nr. of bits of data to be sent. Typically kMitsubishiBits. // repeat: Nr. of additional times the message is to be sent. // -// Status: ALPHA / untested. +// Status: STABLE / Working. // // Notes: // This protocol appears to have no header. @@ -134,7 +134,7 @@ void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / previously working. +// Status: STABLE / Working. // // Notes: // This protocol appears to have no header. @@ -174,7 +174,7 @@ bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t offset, // nbits: Nr. of bits of data to be sent. Typically kMitsubishiBits. // repeat: Nr. of additional times the message is to be sent. // -// Status: ALPHA / untested. +// Status: BETA / Probably works. // // Notes: // Based on a Mitsubishi HC3000 projector's remote. @@ -214,7 +214,7 @@ void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Works with simulated data. +// Status: STABLE / Works. // // Notes: // Hardware supported: @@ -270,7 +270,7 @@ bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t offset, // repeat: Nr. of times the message is to be repeated. // (Default = kMitsubishiACMinRepeat). // -// Status: BETA / Appears to be working. +// Status: STABLE / Working. // void IRsend::sendMitsubishiAC(const unsigned char data[], const uint16_t nbytes, const uint16_t repeat) { @@ -296,7 +296,7 @@ void IRsend::sendMitsubishiAC(const unsigned char data[], const uint16_t nbytes, // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: ALPHA / Under development +// Status: BETA / Probably works // // Ref: // https://www.analysir.com/blog/2015/01/06/reverse-engineering-mitsubishi-ac-infrared-protocol/ @@ -804,7 +804,7 @@ String IRMitsubishiAC::toString(void) { // repeat: Nr. of times the message is to be repeated. // (Default = kMitsubishi136MinRepeat). // -// Status: ALPHA / Probably working. Needs to be tested against a real device. +// Status: BETA / Probably working. Needs to be tested against a real device. // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/888 diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Mitsubishi.h b/lib/IRremoteESP8266-2.7.6/src/ir_Mitsubishi.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Mitsubishi.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Mitsubishi.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_MitsubishiHeavy.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_MitsubishiHeavy.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_MitsubishiHeavy.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266-2.7.6/src/ir_MitsubishiHeavy.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_MitsubishiHeavy.h rename to lib/IRremoteESP8266-2.7.6/src/ir_MitsubishiHeavy.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_NEC.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_NEC.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_NEC.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_NEC.cpp index 2f870f58c..9145f5c24 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_NEC.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_NEC.cpp @@ -44,7 +44,7 @@ void IRsend::sendNEC(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // A raw 32-bit NEC message. // -// Status: BETA / Expected to work. +// Status: STABLE / Expected to work. // // Ref: // http://www.sbprojects.com/knowledge/ir/nec.php diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_NEC.h b/lib/IRremoteESP8266-2.7.6/src/ir_NEC.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_NEC.h rename to lib/IRremoteESP8266-2.7.6/src/ir_NEC.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Neoclima.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Neoclima.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Neoclima.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Neoclima.cpp index 0e410ca67..8c93ef26f 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Neoclima.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Neoclima.cpp @@ -470,7 +470,7 @@ String IRNeoclimaAc::toString(void) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Known working +// Status: STABLE / Known working // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/764 diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Neoclima.h b/lib/IRremoteESP8266-2.7.6/src/ir_Neoclima.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Neoclima.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Neoclima.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Nikai.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Nikai.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Nikai.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Nikai.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Panasonic.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Panasonic.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Panasonic.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Panasonic.cpp index bf472de88..d8b627c9f 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Panasonic.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Panasonic.cpp @@ -82,7 +82,7 @@ using irutils::setBits; // nbits: The number of bits of the message to be sent. (kPanasonicBits). // repeat: The number of times the command is to be repeated. // -// Status: BETA / Should be working. +// Status: STABLE / Should be working. // // Note: // This protocol is a modified version of Kaseikyo. @@ -121,7 +121,7 @@ void IRsend::sendPanasonic(const uint16_t address, const uint32_t data, // Returns: // A raw uint64_t Panasonic message. // -// Status: BETA / Should be working.. +// Status: STABLE / Should be working.. // // Note: // Panasonic 48-bit protocol is a modified version of Kaseikyo. @@ -149,7 +149,7 @@ uint64_t IRsend::encodePanasonic(const uint16_t manufacturer, // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Should be working. +// Status: STABLE / Should be working. // Note: // Panasonic 48-bit protocol is a modified version of Kaseikyo. // Ref: diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Panasonic.h b/lib/IRremoteESP8266-2.7.6/src/ir_Panasonic.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Panasonic.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Panasonic.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Pioneer.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Pioneer.cpp similarity index 97% rename from lib/IRremoteESP8266-2.7.4/src/ir_Pioneer.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Pioneer.cpp index 85343a117..08bbf3c06 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Pioneer.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Pioneer.cpp @@ -41,7 +41,7 @@ const uint32_t kPioneerMinGap = kPioneerMinGapTicks * kPioneerTick; // Typically kPioneerBits. // repeat: The number of times the command is to be repeated. // -// Status: BETA / Expected to be working. +// Status: STABLE / Expected to be working. // // Ref: // http://adrian-kingston.com/IRFormatPioneer.htm @@ -75,7 +75,7 @@ void IRsend::sendPioneer(const uint64_t data, const uint16_t nbits, // Returns: // A raw 64-bit Pioneer message code. // -// Status: BETA / Expected to work. +// Status: STABLE / Expected to work. // // Note: // Address & Command can be take from a decode result OR from the spreadsheets @@ -103,7 +103,7 @@ uint64_t IRsend::encodePioneer(const uint16_t address, const uint16_t command) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Should be working. (Self decodes & real examples) +// Status: STABLE / Should be working. (Self decodes & real examples) // bool IRrecv::decodePioneer(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Pronto.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Pronto.cpp similarity index 98% rename from lib/IRremoteESP8266-2.7.4/src/ir_Pronto.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Pronto.cpp index a408afed5..6b7a779de 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Pronto.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Pronto.cpp @@ -21,7 +21,7 @@ const uint16_t kProntoDataOffset = 4; // len: Nr. of entries in the data[] array. // repeat: Nr. of times to repeat the message. // -// Status: ALPHA / Not tested in the real world. +// Status: STABLE / Known working. // // Note: // Pronto codes are typically represented in hexadecimal. diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_RC5_RC6.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_RC5_RC6.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_RC5_RC6.cpp index ce69aaa3a..370100f9e 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_RC5_RC6.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_RC5_RC6.cpp @@ -184,7 +184,7 @@ uint64_t IRsend::toggleRC5(uint64_t data) { return data ^ kRc5ToggleMask; } // Returns: // A data message suitable for use in sendRC6() with the toggle bit flipped. // -// Status: BETA / Should work fine. +// Status: STABLE / Should work fine. // // Ref: // http://www.sbprojects.com/knowledge/ir/rc6.php diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_RCMM.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_RCMM.cpp similarity index 98% rename from lib/IRremoteESP8266-2.7.4/src/ir_RCMM.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_RCMM.cpp index a2542e467..ea0e05e9d 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_RCMM.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_RCMM.cpp @@ -45,7 +45,7 @@ const uint16_t kRcmmExcess = 50; // nbits: The number of bits of data to send. (Typically 12, 24, or 32[Nokia]) // repeat: The nr. of times the message should be sent. // -// Status: BETA / Should be working. +// Status: STABLE / Should be working. // // Ref: // http://www.sbprojects.com/knowledge/ir/rcmm.php @@ -102,7 +102,7 @@ void IRsend::sendRCMM(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Should be working. +// Status: STABLE / Should be working. // // Ref: // http://www.sbprojects.com/knowledge/ir/rcmm.php diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Samsung.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Samsung.cpp similarity index 93% rename from lib/IRremoteESP8266-2.7.4/src/ir_Samsung.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Samsung.cpp index a31e76953..78b36e146 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Samsung.cpp @@ -72,7 +72,7 @@ using irutils::setBits; // nbits: The bit size of the message being sent. typically kSamsungBits. // repeat: The number of times the message is to be repeated. // -// Status: BETA / Should be working. +// Status: STABLE / Should be working. // // Ref: http://elektrolab.wz.cz/katalog/samsung_protocol.pdf void IRsend::sendSAMSUNG(const uint64_t data, const uint16_t nbits, @@ -92,7 +92,7 @@ void IRsend::sendSAMSUNG(const uint64_t data, const uint16_t nbits, // Returns: // A raw 32-bit Samsung message suitable for sendSAMSUNG(). // -// Status: BETA / Should be working. +// Status: STABLE / Should be working. uint32_t IRsend::encodeSAMSUNG(const uint8_t customer, const uint8_t command) { uint8_t revcustomer = reverseBits(customer, sizeof(customer) * 8); uint8_t revcommand = reverseBits(command, sizeof(command) * 8); @@ -271,7 +271,7 @@ bool IRrecv::decodeSamsung36(decode_results *results, uint16_t offset, // https://github.com/crankyoldgit/IRremoteESP8266/issues/505 void IRsend::sendSamsungAC(const uint8_t data[], const uint16_t nbytes, const uint16_t repeat) { - if (nbytes < kSamsungAcStateLength && nbytes % kSamsungACSectionLength) + if (nbytes < kSamsungAcStateLength && nbytes % kSamsungAcSectionLength) return; // Not an appropriate number of bytes to send a proper message. enableIROut(38); @@ -281,11 +281,11 @@ void IRsend::sendSamsungAC(const uint8_t data[], const uint16_t nbytes, space(kSamsungAcHdrSpace); // Send in 7 byte sections. for (uint16_t offset = 0; offset < nbytes; - offset += kSamsungACSectionLength) { + offset += kSamsungAcSectionLength) { sendGeneric(kSamsungAcSectionMark, kSamsungAcSectionSpace, kSamsungAcBitMark, kSamsungAcOneSpace, kSamsungAcBitMark, kSamsungAcZeroSpace, kSamsungAcBitMark, kSamsungAcSectionGap, - data + offset, kSamsungACSectionLength, // 7 bytes == 56 bits + data + offset, kSamsungAcSectionLength, // 7 bytes == 56 bits 38000, false, 0, 50); // Send in LSBF order } // Complete made up guess at inter-message gap. @@ -379,10 +379,10 @@ void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // Copy/convert the internal state to an extended state. - for (uint16_t i = 0; i < kSamsungACSectionLength; i++) + for (uint16_t i = 0; i < kSamsungAcSectionLength; i++) extended_state[i] = remote_state[i]; - for (uint16_t i = kSamsungACSectionLength; i < kSamsungAcStateLength; i++) - extended_state[i + kSamsungACSectionLength] = remote_state[i]; + for (uint16_t i = kSamsungAcSectionLength; i < kSamsungAcStateLength; i++) + extended_state[i + kSamsungAcSectionLength] = remote_state[i]; // extended_state[8] seems special. This is a guess on how to calculate it. extended_state[8] = (extended_state[1] & 0x9F) | 0x40; // Send it. @@ -425,7 +425,7 @@ void IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) { // Shrink the extended state into a normal state. if (length > kSamsungAcStateLength) { for (uint8_t i = kSamsungAcStateLength; i < length; i++) - remote_state[i - kSamsungACSectionLength] = remote_state[i]; + remote_state[i - kSamsungAcSectionLength] = remote_state[i]; } } @@ -550,14 +550,15 @@ void IRSamsungAc::setQuiet(const bool on) { bool IRSamsungAc::getPowerful(void) { return !(remote_state[8] & kSamsungAcPowerfulMask8) && - GETBITS8(remote_state[10], kSamsungAcPowerful10Offset, - kSamsungAcPowerful10Offset) && + (GETBITS8(remote_state[10], kSamsungAcPowerful10Offset, + kSamsungAcPowerful10Size) == kSamsungAcPowerful10On) && (this->getFan() == kSamsungAcFanTurbo); } void IRSamsungAc::setPowerful(const bool on) { + uint8_t off_value = this->getBreeze() ? kSamsungAcBreezeOn : 0b000; setBits(&remote_state[10], kSamsungAcPowerful10Offset, - kSamsungAcPowerful10Offset, on ? 0b11: 0b00); + kSamsungAcPowerful10Size, on ? kSamsungAcPowerful10On : off_value); if (on) { remote_state[8] &= ~kSamsungAcPowerfulMask8; // Bit needs to be cleared. // Powerful mode sets fan speed to Turbo. @@ -570,6 +571,26 @@ void IRSamsungAc::setPowerful(const bool on) { } } +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 +// Are the vanes closed over the fan outlet, to stop direct wind? Aka. WindFree +bool IRSamsungAc::getBreeze(void) { + return (GETBITS8(remote_state[10], kSamsungAcBreezeOffset, + kSamsungAcBreezeSize) == kSamsungAcBreezeOn) && + (this->getFan() == kSamsungAcFanAuto && !getSwing()); +} + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 +// Closes the vanes over the fan outlet, to stop direct wind. Aka. WindFree +void IRSamsungAc::setBreeze(const bool on) { + uint8_t off_value = this->getPowerful() ? kSamsungAcPowerful10On : 0b000; + setBits(&remote_state[10], kSamsungAcBreezeOffset, + kSamsungAcBreezeSize, on ? kSamsungAcBreezeOn : off_value); + if (on) { + this->setFan(kSamsungAcFanAuto); + this->setSwing(false); + } +} + bool IRSamsungAc::getDisplay(void) { return GETBIT8(remote_state[10], kSamsungAcDisplayOffset); } @@ -695,6 +716,7 @@ String IRSamsungAc::toString(void) { result += addBoolToString(getClean(), kCleanStr); result += addBoolToString(getQuiet(), kQuietStr); result += addBoolToString(getPowerful(), kPowerfulStr); + result += addBoolToString(getBreeze(), kBreezeStr); result += addBoolToString(getDisplay(), kLightStr); result += addBoolToString(getIon(), kIonStr); return result; @@ -726,17 +748,17 @@ bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t offset, if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false; if (!matchSpace(results->rawbuf[offset++], kSamsungAcHdrSpace)) return false; // Section(s) - for (uint16_t pos = 0; pos <= (nbits / 8) - kSamsungACSectionLength; - pos += kSamsungACSectionLength) { + for (uint16_t pos = 0; pos <= (nbits / 8) - kSamsungAcSectionLength; + pos += kSamsungAcSectionLength) { uint16_t used; // Section Header + Section Data (7 bytes) + Section Footer used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, kSamsungACSectionLength * 8, + results->rawlen - offset, kSamsungAcSectionLength * 8, kSamsungAcSectionMark, kSamsungAcSectionSpace, kSamsungAcBitMark, kSamsungAcOneSpace, kSamsungAcBitMark, kSamsungAcZeroSpace, kSamsungAcBitMark, kSamsungAcSectionGap, - pos + kSamsungACSectionLength >= nbits / 8, + pos + kSamsungAcSectionLength >= nbits / 8, _tolerance, 0, false); if (used == 0) return false; offset += used; diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Samsung.h b/lib/IRremoteESP8266-2.7.6/src/ir_Samsung.h similarity index 70% rename from lib/IRremoteESP8266-2.7.4/src/ir_Samsung.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Samsung.h index 52a781b65..31b5ea181 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Samsung.h +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Samsung.h @@ -19,22 +19,59 @@ // Supports: // Brand: Samsung, Model: UA55H6300 TV // Brand: Samsung, Model: DB63-03556X003 remote +// Brand: Samsung, Model: DB93-16761C remote // Brand: Samsung, Model: IEC-R03 remote // Brand: Samsung, Model: AR09FSSDAWKNFA A/C // Brand: Samsung, Model: AR12KSFPEWQNET A/C // Brand: Samsung, Model: AR12HSSDBWKNEU A/C +// Brand: Samsung, Model: AR12NXCXAWKXEU A/C // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/505 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 // Constants +// Byte[1] +// Checksum 0b11110000 ??? +const uint8_t kSamsungAcPower1Offset = 5; // Mask 0b00100000 +const uint8_t kSamsungAcQuiet1Offset = 4; // Mask 0b00010000 +// Byte[5] +const uint8_t kSamsungAcQuiet5Offset = 5; +// Byte[6] +const uint8_t kSamsungAcPower6Offset = 4; // Mask 0b00110000 +const uint8_t kSamsungAcPower6Size = 2; // Bits +// Byte[8] +// Checksum 0b11110000 ??? +const uint8_t kSamsungAcPowerfulMask8 = 0b01010000; +// Byte[9] +const uint8_t kSamsungAcSwingOffset = 4; // Mask 0b01110000 +const uint8_t kSamsungAcSwingSize = 3; // Bits +const uint8_t kSamsungAcSwingMove = 0b010; +const uint8_t kSamsungAcSwingStop = 0b111; +// Byte[10] +const uint8_t kSamsungAcPowerful10Offset = 1; // Mask 0b00001110 +const uint8_t kSamsungAcPowerful10Size = 3; // Mask 0b00001110 +const uint8_t kSamsungAcPowerful10On = 0b011; +// Breeze (aka. WindFree) +const uint8_t kSamsungAcBreezeOffset = kSamsungAcPowerful10Offset; +const uint8_t kSamsungAcBreezeSize = kSamsungAcPowerful10Size; +const uint8_t kSamsungAcBreezeOn = 0b101; +const uint8_t kSamsungAcDisplayOffset = 4; // Mask 0b00010000 +const uint8_t kSamsungAcClean10Offset = 7; // Mask 0b10000000 +// Byte[11] +const uint8_t kSamsungAcIonOffset = 0; // Mask 0b00000001 +const uint8_t kSamsungAcClean11Offset = 1; // Mask 0b00000010 +const uint8_t kSamsungAcMinTemp = 16; // C Mask 0b11110000 +const uint8_t kSamsungAcMaxTemp = 30; // C Mask 0b11110000 +const uint8_t kSamsungAcAutoTemp = 25; // C Mask 0b11110000 +// Byte[12] +const uint8_t kSamsungAcModeOffset = 4; // Mask 0b01110000 const uint8_t kSamsungAcAuto = 0; const uint8_t kSamsungAcCool = 1; const uint8_t kSamsungAcDry = 2; const uint8_t kSamsungAcFan = 3; const uint8_t kSamsungAcHeat = 4; -const uint8_t kSamsungAcModeOffset = 4; // Mask 0b01110000 -const uint8_t kSamsungAcFanOffest = 1; // Mask 0b00001110 +const uint8_t kSamsungAcFanOffest = 1; // Mask 0b00001110 const uint8_t kSamsungAcFanSize = 3; // Bits const uint8_t kSamsungAcFanAuto = 0; const uint8_t kSamsungAcFanLow = 2; @@ -42,28 +79,10 @@ const uint8_t kSamsungAcFanMed = 4; const uint8_t kSamsungAcFanHigh = 5; const uint8_t kSamsungAcFanAuto2 = 6; const uint8_t kSamsungAcFanTurbo = 7; -const uint8_t kSamsungAcMinTemp = 16; // 16C -const uint8_t kSamsungAcMaxTemp = 30; // 30C -const uint8_t kSamsungAcAutoTemp = 25; // 25C -const uint8_t kSamsungAcPower1Offset = 5; -const uint8_t kSamsungAcPower6Offset = 4; // Mask 0b00110000 -const uint8_t kSamsungAcPower6Size = 2; // Bits -const uint8_t kSamsungAcSwingOffset = 4; // Mask 0b01110000 -const uint8_t kSamsungAcSwingSize = 3; // Bits -const uint8_t kSamsungAcSwingMove = 0b010; -const uint8_t kSamsungAcSwingStop = 0b111; -const uint8_t kSamsungAcBeepOffset = 1; -const uint8_t kSamsungAcClean10Offset = 7; -const uint8_t kSamsungAcClean11Offset = 1; // 0b00000010 -const uint8_t kSamsungAcQuiet1Offset = 4; -const uint8_t kSamsungAcQuiet5Offset = 5; -const uint8_t kSamsungAcPowerfulMask8 = 0b01010000; -const uint8_t kSamsungAcPowerful10Offset = 1; // Mask 0b00000110 -const uint8_t kSamsungAcPowerful10Size = 1; // Mask 0b00000110 -const uint8_t kSamsungAcDisplayOffset = 4; // Mask 0b00010000 -const uint8_t kSamsungAcIonOffset = 0; // Mask 0b00000001 +// Byte[13] +const uint8_t kSamsungAcBeepOffset = 1; // Mask 0b00000010 -const uint16_t kSamsungACSectionLength = 7; +const uint16_t kSamsungAcSectionLength = 7; const uint64_t kSamsungAcPowerSection = 0x1D20F00000000; // Classes @@ -103,7 +122,8 @@ class IRSamsungAc { bool getQuiet(void); void setPowerful(const bool on); bool getPowerful(void); - + void setBreeze(const bool on); + bool getBreeze(void); void setDisplay(const bool on); bool getDisplay(void); void setIon(const bool on); diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Sanyo.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Sanyo.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Sanyo.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Sanyo.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Sharp.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Sharp.cpp similarity index 93% rename from lib/IRremoteESP8266-2.7.4/src/ir_Sharp.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Sharp.cpp index f28ca0f4d..363271012 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Sharp.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Sharp.cpp @@ -16,6 +16,7 @@ // Equipment it seems compatible with: // * Sharp LC-52D62U +// * Sharp AH-AxSAY A/C (Remote CRMC-A907 JBEZ) // * // @@ -56,7 +57,7 @@ using irutils::setBits; // nbits: Nr. of bits of data to be sent. Typically kSharpBits. // repeat: Nr. of additional times the message is to be sent. // -// Status: BETA / Previously working fine. +// Status: STABLE / Working fine. // // Notes: // This procedure handles the inversion of bits required per protocol. @@ -102,7 +103,7 @@ void IRsend::sendSharpRaw(const uint64_t data, const uint16_t nbits, // Returns: // An uint32_t containing the raw Sharp message for sendSharpRaw(). // -// Status: BETA / Should work okay. +// Status: STABLE / Works okay. // // Notes: // Assumes the standard Sharp bit sizes. @@ -334,18 +335,51 @@ void IRSharpAc::setRaw(const uint8_t new_code[], const uint16_t length) { memcpy(remote, new_code, std::min(length, kSharpAcStateLength)); } +void IRSharpAc::setPreviousPower(const bool on) { + setBit(&remote[kSharpAcBytePower], kSharpAcBitPreviousPowerOffset, on); +} + +bool IRSharpAc::getPreviousPower(void) { + return GETBIT8(remote[kSharpAcBytePower], kSharpAcBitPreviousPowerOffset); +} + void IRSharpAc::on(void) { setPower(true); } void IRSharpAc::off(void) { setPower(false); } void IRSharpAc::setPower(const bool on) { + setPreviousPower(getPower()); setBit(&remote[kSharpAcBytePower], kSharpAcBitPowerOffset, on); + setButton(kSharpAcButtonPowerMode); +} + +void IRSharpAc::setPower(const bool on, const bool prev) { + setPower(on); + setPreviousPower(prev); } bool IRSharpAc::getPower(void) { return GETBIT8(remote[kSharpAcBytePower], kSharpAcBitPowerOffset); } +void IRSharpAc::setButton(const uint8_t button) { + switch (button) { + case kSharpAcButtonPowerMode: + case kSharpAcButtonTemp: + case kSharpAcButtonFan: + setBits(&remote[kSharpAcByteButton], kSharpAcButtonOffset, + kSharpAcButtonSize, button); + break; + default: + setButton(kSharpAcButtonPowerMode); + } +} + +uint8_t IRSharpAc::getButton(void) { + return GETBITS8(remote[kSharpAcByteButton], kSharpAcButtonOffset, + kSharpAcButtonSize); +} + // Set the temp in deg C void IRSharpAc::setTemp(const uint8_t temp) { switch (this->getMode()) { @@ -353,16 +387,15 @@ void IRSharpAc::setTemp(const uint8_t temp) { case kSharpAcAuto: case kSharpAcDry: remote[kSharpAcByteTemp] = 0; - remote[kSharpAcByteManual] = 0; // When in Dry/Auto this byte is 0. return; default: remote[kSharpAcByteTemp] = 0xC0; - setBit(&remote[kSharpAcByteManual], kSharpAcBitTempManualOffset); } uint8_t degrees = std::max(temp, kSharpAcMinTemp); degrees = std::min(degrees, kSharpAcMaxTemp); setBits(&remote[kSharpAcByteTemp], kLowNibble, kNibbleSize, degrees - kSharpAcMinTemp); + setButton(kSharpAcButtonTemp); } uint8_t IRSharpAc::getTemp(void) { @@ -375,11 +408,10 @@ uint8_t IRSharpAc::getMode(void) { } void IRSharpAc::setMode(const uint8_t mode) { - setBit(&remote[kSharpAcBytePower], kSharpAcBitModeNonAutoOffset, - mode != kSharpAcAuto); switch (mode) { case kSharpAcAuto: case kSharpAcDry: + this->setFan(2); // When Dry or Auto, Fan always 2(Auto) this->setTemp(0); // Dry/Auto have no temp setting. // FALLTHRU case kSharpAcCool: @@ -389,6 +421,7 @@ void IRSharpAc::setMode(const uint8_t mode) { default: this->setMode(kSharpAcAuto); } + setButton(kSharpAcButtonPowerMode); } // Set the speed of the fan @@ -399,14 +432,13 @@ void IRSharpAc::setFan(const uint8_t speed) { case kSharpAcFanMed: case kSharpAcFanHigh: case kSharpAcFanMax: - setBit(&remote[kSharpAcByteManual], kSharpAcBitFanManualOffset, - speed != kSharpAcFanAuto); setBits(&remote[kSharpAcByteFan], kSharpAcFanOffset, kSharpAcFanSize, speed); break; default: this->setFan(kSharpAcFanAuto); } + setButton(kSharpAcButtonFan); } uint8_t IRSharpAc::getFan(void) { @@ -485,8 +517,9 @@ stdAc::state_t IRSharpAc::toCommon(void) { // Convert the internal state into a human readable string. String IRSharpAc::toString(void) { String result = ""; - result.reserve(60); // Reserve some heap for the string to reduce fragging. + result.reserve(80); // Reserve some heap for the string to reduce fragging. result += addBoolToString(getPower(), kPowerStr, false); + result += addBoolToString(getPreviousPower(), kPreviousPowerStr); result += addModeToString(getMode(), kSharpAcAuto, kSharpAcCool, kSharpAcHeat, kSharpAcDry, kSharpAcAuto); result += addTempToString(getTemp()); @@ -506,7 +539,7 @@ String IRSharpAc::toString(void) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Should be working. +// Status: STABLE / Known working. // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/638 diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Sharp.h b/lib/IRremoteESP8266-2.7.6/src/ir_Sharp.h similarity index 81% rename from lib/IRremoteESP8266-2.7.4/src/ir_Sharp.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Sharp.h index e1c81c3a2..03d27d44f 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Sharp.h +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Sharp.h @@ -3,6 +3,7 @@ // Supports: // Brand: Sharp, Model: LC-52D62U TV // Brand: Sharp, Model: AY-ZP40KR A/C +// Brand: Sharp, Model: AH-AxSAY A/C #ifndef IR_SHARP_H_ #define IR_SHARP_H_ @@ -38,17 +39,19 @@ const uint8_t kSharpAcFanHigh = 0b101; // 5 (FAN3) const uint8_t kSharpAcFanMax = 0b111; // 7 (FAN4) const uint8_t kSharpAcByteTemp = 4; const uint8_t kSharpAcBytePower = 5; -const uint8_t kSharpAcBitPowerOffset = 4; -const uint8_t kSharpAcBitModeNonAutoOffset = 5; // 0b00100000 +const uint8_t kSharpAcBitPowerOffset = 4; // 0b000x0000 +const uint8_t kSharpAcBitPreviousPowerOffset = 5; // 0b00x00000 const uint8_t kSharpAcByteMode = 6; const uint8_t kSharpAcModeSize = 2; // Mask 0b00000011; const uint8_t kSharpAcByteFan = kSharpAcByteMode; const uint8_t kSharpAcFanOffset = 4; // Mask 0b01110000 const uint8_t kSharpAcFanSize = 3; // Nr. of Bits -const uint8_t kSharpAcByteManual = 10; -const uint8_t kSharpAcBitFanManualOffset = 0; // 0b00000001 -const uint8_t kSharpAcBitTempManualOffset = 2; // 0b00000100 - +const uint8_t kSharpAcByteButton = 10; +const uint8_t kSharpAcButtonOffset = 0; +const uint8_t kSharpAcButtonSize = 3; // Mask 0b00000xxx +const uint8_t kSharpAcButtonPowerMode = 0b000; // 0 +const uint8_t kSharpAcButtonTemp = 0b100; // 4 +const uint8_t kSharpAcButtonFan = 0b101; // 5 class IRSharpAc { public: @@ -63,13 +66,18 @@ class IRSharpAc { void on(void); void off(void); void setPower(const bool on); + void setPower(const bool on, const bool prev); bool getPower(void); + void setPreviousPower(const bool on); + bool getPreviousPower(void); void setTemp(const uint8_t temp); uint8_t getTemp(void); void setFan(const uint8_t fan); uint8_t getFan(void); void setMode(const uint8_t mode); uint8_t getMode(void); + void setButton(const uint8_t button); + uint8_t getButton(void); uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kSharpAcStateLength); diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Sherwood.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Sherwood.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Sherwood.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Sherwood.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Sony.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Sony.cpp similarity index 98% rename from lib/IRremoteESP8266-2.7.4/src/ir_Sony.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Sony.cpp index c346eb293..9ff0c50cc 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Sony.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Sony.cpp @@ -106,7 +106,7 @@ void IRsend::_sendSony(uint64_t data, uint16_t nbits, uint16_t repeat, // Returns: // A sendSony compatible data message. // -// Status: BETA / Should be working. +// Status: STABLE / Should be working. uint32_t IRsend::encodeSony(uint16_t nbits, uint16_t command, uint16_t address, uint16_t extended) { uint32_t result = 0; @@ -141,7 +141,7 @@ uint32_t IRsend::encodeSony(uint16_t nbits, uint16_t command, uint16_t address, // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Should be working. strict mode is ALPHA / Untested. +// Status: STABLE / Should be working. strict mode is ALPHA / Untested. // // Notes: // SONY protocol, SIRC (Serial Infra-Red Control) can be 12,15,20 bits long. diff --git a/lib/IRremoteESP8266-2.7.6/src/ir_Symphony.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Symphony.cpp new file mode 100644 index 000000000..f0194fdb8 --- /dev/null +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Symphony.cpp @@ -0,0 +1,87 @@ +// Copyright 2020 David Conran + +// Send & decode support for Symphony added by David Conran + +// Supports: +// Brand: Symphony, Model: Air Cooler 3Di + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtimer.h" +#include "IRutils.h" + +// Constants +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1057 +const uint16_t kSymphonyZeroMark = 1250; +const uint16_t kSymphonyZeroSpace = 400; +const uint16_t kSymphonyOneMark = kSymphonyZeroSpace; +const uint16_t kSymphonyOneSpace = kSymphonyZeroMark; +const uint16_t kSymphonyFooterMark = kSymphonyOneMark; +const uint32_t kSymphonyFooterGap = 8000; + +#if SEND_SYMPHONY +// Send a Symphony packet. +// +// Args: +// data: The data we want to send. MSB first. +// nbits: The number of bits of data to send. (Typically 12, 24, or 32[Nokia]) +// repeat: The nr. of times the message should be sent. +// +// Status: STABLE / Should be working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1057 +void IRsend::sendSymphony(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendGeneric(0, 0, + kSymphonyOneMark, kSymphonyOneSpace, + kSymphonyZeroMark, kSymphonyZeroSpace, + kSymphonyFooterMark, kSymphonyFooterGap, + data, nbits, 38000, true, repeat, kDutyDefault); +} +#endif // SEND_SYMPHONY + +#if DECODE_SYMPHONY +// Decode a Symphony packet if possible. +// Places successful decode information in the results pointer. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// offset: The starting index to use when attempting to decode the raw data. +// Typically/Defaults to kStartOffset. +// nbits: Nr. of bits to expect in the data portion. Typically kSymphonyBits +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Should be working. +// +// Ref: +// +bool IRrecv::decodeSymphony(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + uint64_t data = 0; + + if (results->rawlen < 2 * nbits + kFooter + offset - 1) + return false; // Not enough entries to ever be SYMPHONY. + // Compliance + if (strict && nbits != kSymphonyBits) return false; + + if (!matchGeneric(results->rawbuf + offset, &data, results->rawlen - offset, + nbits, + 0, 0, // No Header + kSymphonyOneMark, kSymphonyOneSpace, + kSymphonyZeroMark, kSymphonyZeroSpace, + kSymphonyFooterMark, kSymphonyFooterGap, true, + _tolerance, 0)) + return false; + + // Success + results->value = data; + results->decode_type = decode_type_t::SYMPHONY; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_SYMPHONY diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Tcl.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Tcl.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Tcl.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Tcl.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Tcl.h b/lib/IRremoteESP8266-2.7.6/src/ir_Tcl.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Tcl.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Tcl.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Teco.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Teco.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Teco.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Teco.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Teco.h b/lib/IRremoteESP8266-2.7.6/src/ir_Teco.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Teco.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Teco.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Toshiba.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Toshiba.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Toshiba.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Toshiba.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Toshiba.h b/lib/IRremoteESP8266-2.7.6/src/ir_Toshiba.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Toshiba.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Toshiba.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Trotec.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Trotec.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Trotec.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Trotec.cpp index 563564d36..d8b87e034 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Trotec.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Trotec.cpp @@ -245,7 +245,7 @@ String IRTrotecESP::toString(void) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Probably works. Untested on real devices. +// Status: STABLE / Works. Untested on real devices. // // Ref: bool IRrecv::decodeTrotec(decode_results *results, uint16_t offset, diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Trotec.h b/lib/IRremoteESP8266-2.7.6/src/ir_Trotec.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Trotec.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Trotec.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Vestel.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Vestel.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Vestel.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Vestel.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Vestel.h b/lib/IRremoteESP8266-2.7.6/src/ir_Vestel.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Vestel.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Vestel.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Whirlpool.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/src/ir_Whirlpool.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Whirlpool.cpp index e1ca2e6db..3ba781c4c 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Whirlpool.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Whirlpool.cpp @@ -56,7 +56,7 @@ using irutils::setBits; // nbytes: Nr. of bytes of data in the array. (>=kWhirlpoolAcStateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: ALPHA / Untested. +// Status: BETA / Probably works. // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/509 diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Whirlpool.h b/lib/IRremoteESP8266-2.7.6/src/ir_Whirlpool.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/ir_Whirlpool.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Whirlpool.h diff --git a/lib/IRremoteESP8266-2.7.4/src/ir_Whynter.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Whynter.cpp similarity index 98% rename from lib/IRremoteESP8266-2.7.4/src/ir_Whynter.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Whynter.cpp index 118eb7559..92bad25ce 100644 --- a/lib/IRremoteESP8266-2.7.4/src/ir_Whynter.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/ir_Whynter.cpp @@ -76,7 +76,7 @@ void IRsend::sendWhynter(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA Strict mode is ALPHA. +// Status: STABLE / Working. Strict mode is ALPHA. // // Ref: // https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Whynter.cpp diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/README.md b/lib/IRremoteESP8266-2.7.6/src/locale/README.md similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/locale/README.md rename to lib/IRremoteESP8266-2.7.6/src/locale/README.md diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/de-CH.h b/lib/IRremoteESP8266-2.7.6/src/locale/de-CH.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/locale/de-CH.h rename to lib/IRremoteESP8266-2.7.6/src/locale/de-CH.h diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/de-DE.h b/lib/IRremoteESP8266-2.7.6/src/locale/de-DE.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/locale/de-DE.h rename to lib/IRremoteESP8266-2.7.6/src/locale/de-DE.h diff --git a/lib/IRremoteESP8266-2.7.6/src/locale/defaults.h b/lib/IRremoteESP8266-2.7.6/src/locale/defaults.h new file mode 100644 index 000000000..f96595616 --- /dev/null +++ b/lib/IRremoteESP8266-2.7.6/src/locale/defaults.h @@ -0,0 +1,719 @@ +// Copyright 2019 - David Conran (@crankyoldgit) +// The default text to use throughout the library. +// The library will use this text if no locale (_IR_LOCALE_) is set or if +// the locale doesn't define particular values. +// If they are defined, this file should NOT override them. +// +// This file should contain a #define for every translateable/locale dependant +// string used by the library. Language specific files don't have to include +// everything. +// +// NOTE: ASCII/UTF-8 characters only. Unicode is NOT supported. +// +// The defaults are English (AU) / en-AU. Australia (AU) is pretty much the same +// as English (UK) for this libraries use case. +#ifndef LOCALE_DEFAULTS_H_ +#define LOCALE_DEFAULTS_H_ + +#ifndef D_STR_UNKNOWN +#define D_STR_UNKNOWN "UNKNOWN" +#endif // D_STR_UNKNOWN +#ifndef D_STR_PROTOCOL +#define D_STR_PROTOCOL "Protocol" +#endif // D_STR_PROTOCOL +#ifndef D_STR_POWER +#define D_STR_POWER "Power" +#endif // D_STR_POWER +#ifndef D_STR_PREVIOUS +#define D_STR_PREVIOUS "Previous" +#endif // D_STR_PREVIOUS +#ifndef D_STR_ON +#define D_STR_ON "On" +#endif // D_STR_ON +#ifndef D_STR_OFF +#define D_STR_OFF "Off" +#endif // D_STR_OFF +#ifndef D_STR_MODE +#define D_STR_MODE "Mode" +#endif // D_STR_MODE +#ifndef D_STR_TOGGLE +#define D_STR_TOGGLE "Toggle" +#endif // D_STR_TOGGLE +#ifndef D_STR_TURBO +#define D_STR_TURBO "Turbo" +#endif // D_STR_TURBO +#ifndef D_STR_SUPER +#define D_STR_SUPER "Super" +#endif // D_STR_SUPER +#ifndef D_STR_SLEEP +#define D_STR_SLEEP "Sleep" +#endif // D_STR_SLEEP +#ifndef D_STR_LIGHT +#define D_STR_LIGHT "Light" +#endif // D_STR_LIGHT +#ifndef D_STR_POWERFUL +#define D_STR_POWERFUL "Powerful" +#endif // D_STR_POWERFUL +#ifndef D_STR_QUIET +#define D_STR_QUIET "Quiet" +#endif // D_STR_QUIET +#ifndef D_STR_ECONO +#define D_STR_ECONO "Econo" +#endif // D_STR_ECONO +#ifndef D_STR_SWING +#define D_STR_SWING "Swing" +#endif // D_STR_SWING +#ifndef D_STR_SWINGH +#define D_STR_SWINGH D_STR_SWING"(H)" // Set `D_STR_SWING` first! +#endif // D_STR_SWINGH +#ifndef D_STR_SWINGV +#define D_STR_SWINGV D_STR_SWING"(V)" // Set `D_STR_SWING` first! +#endif // D_STR_SWINGV +#ifndef D_STR_BEEP +#define D_STR_BEEP "Beep" +#endif // D_STR_BEEP +#ifndef D_STR_MOULD +#define D_STR_MOULD "Mould" +#endif // D_STR_MOULD +#ifndef D_STR_CLEAN +#define D_STR_CLEAN "Clean" +#endif // D_STR_CLEAN +#ifndef D_STR_PURIFY +#define D_STR_PURIFY "Purify" +#endif // D_STR_PURIFY +#ifndef D_STR_TIMER +#define D_STR_TIMER "Timer" +#endif // D_STR_TIMER +#ifndef D_STR_ONTIMER +#define D_STR_ONTIMER D_STR_ON " " D_STR_TIMER // Set `D_STR_ON` first! +#endif // D_STR_ONTIMER +#ifndef D_STR_OFFTIMER +#define D_STR_OFFTIMER D_STR_OFF " " D_STR_TIMER // Set `D_STR_OFF` first! +#endif // D_STR_OFFTIMER +#ifndef D_STR_CLOCK +#define D_STR_CLOCK "Clock" +#endif // D_STR_CLOCK +#ifndef D_STR_COMMAND +#define D_STR_COMMAND "Command" +#endif // D_STR_COMMAND +#ifndef D_STR_XFAN +#define D_STR_XFAN "XFan" +#endif // D_STR_XFAN +#ifndef D_STR_HEALTH +#define D_STR_HEALTH "Health" +#endif // D_STR_HEALTH +#ifndef D_STR_MODEL +#define D_STR_MODEL "Model" +#endif // D_STR_MODEL +#ifndef D_STR_TEMP +#define D_STR_TEMP "Temp" +#endif // D_STR_TEMP +#ifndef D_STR_IFEEL +#define D_STR_IFEEL "IFeel" +#endif // D_STR_IFEEL +#ifndef D_STR_HUMID +#define D_STR_HUMID "Humid" +#endif // D_STR_HUMID +#ifndef D_STR_SAVE +#define D_STR_SAVE "Save" +#endif // D_STR_SAVE +#ifndef D_STR_EYE +#define D_STR_EYE "Eye" +#endif // D_STR_EYE +#ifndef D_STR_FOLLOW +#define D_STR_FOLLOW "Follow" +#endif // D_STR_FOLLOW +#ifndef D_STR_ION +#define D_STR_ION "Ion" +#endif // D_STR_ION +#ifndef D_STR_FRESH +#define D_STR_FRESH "Fresh" +#endif // D_STR_FRESH +#ifndef D_STR_HOLD +#define D_STR_HOLD "Hold" +#endif // D_STR_HOLD +#ifndef D_STR_8C_HEAT +#define D_STR_8C_HEAT "8C " D_STR_HEAT // Set `D_STR_HEAT` first! +#endif // D_STR_8C_HEAT +#ifndef D_STR_BUTTON +#define D_STR_BUTTON "Button" +#endif // D_STR_BUTTON +#ifndef D_STR_NIGHT +#define D_STR_NIGHT "Night" +#endif // D_STR_NIGHT +#ifndef D_STR_SILENT +#define D_STR_SILENT "Silent" +#endif // D_STR_SILENT +#ifndef D_STR_FILTER +#define D_STR_FILTER "Filter" +#endif // D_STR_FILTER +#ifndef D_STR_3D +#define D_STR_3D "3D" +#endif // D_STR_3D +#ifndef D_STR_CELSIUS +#define D_STR_CELSIUS "Celsius" +#endif // D_STR_CELSIUS +#ifndef D_STR_UP +#define D_STR_UP "Up" +#endif // D_STR_UP +#ifndef D_STR_TEMPUP +#define D_STR_TEMPUP D_STR_TEMP " " D_STR_UP // Set `D_STR_TEMP` first! +#endif // D_STR_TEMPUP +#ifndef D_STR_DOWN +#define D_STR_DOWN "Down" +#endif // D_STR_DOWN +#ifndef D_STR_TEMPDOWN +#define D_STR_TEMPDOWN D_STR_TEMP " " D_STR_DOWN // Set `D_STR_TEMP` first! +#endif // D_STR_TEMPDOWN +#ifndef D_STR_CHANGE +#define D_STR_CHANGE "Change" +#endif // D_STR_CHANGE +#ifndef D_STR_START +#define D_STR_START "Start" +#endif // D_STR_START +#ifndef D_STR_STOP +#define D_STR_STOP "Stop" +#endif // D_STR_STOP +#ifndef D_STR_MOVE +#define D_STR_MOVE "Move" +#endif // D_STR_MOVE +#ifndef D_STR_SET +#define D_STR_SET "Set" +#endif // D_STR_SET +#ifndef D_STR_CANCEL +#define D_STR_CANCEL "Cancel" +#endif // D_STR_CANCEL +#ifndef D_STR_COMFORT +#define D_STR_COMFORT "Comfort" +#endif // D_STR_COMFORT +#ifndef D_STR_SENSOR +#define D_STR_SENSOR "Sensor" +#endif // D_STR_SENSOR +#ifndef D_STR_WEEKLY +#define D_STR_WEEKLY "Weekly" +#endif // D_STR_WEEKLY +#ifndef D_STR_WEEKLYTIMER +#define D_STR_WEEKLYTIMER D_STR_WEEKLY " " D_STR_TIMER // Needs `D_STR_WEEKLY`! +#endif // D_STR_WEEKLYTIMER +#ifndef D_STR_WIFI +#define D_STR_WIFI "WiFi" +#endif // D_STR_WIFI +#ifndef D_STR_LAST +#define D_STR_LAST "Last" +#endif // D_STR_LAST +#ifndef D_STR_FAST +#define D_STR_FAST "Fast" +#endif // D_STR_FAST +#ifndef D_STR_SLOW +#define D_STR_SLOW "Slow" +#endif // D_STR_SLOW +#ifndef D_STR_AIRFLOW +#define D_STR_AIRFLOW "Air Flow" +#endif // D_STR_AIRFLOW +#ifndef D_STR_STEP +#define D_STR_STEP "Step" +#endif // D_STR_STEP +#ifndef D_STR_NA +#define D_STR_NA "N/A" +#endif // D_STR_NA +#ifndef D_STR_OUTSIDE +#define D_STR_OUTSIDE "Outside" +#endif // D_STR_OUTSIDE +#ifndef D_STR_LOUD +#define D_STR_LOUD "Loud" +#endif // D_STR_LOUD +#ifndef D_STR_UPPER +#define D_STR_UPPER "Upper" +#endif // D_STR_UPPER +#ifndef D_STR_LOWER +#define D_STR_LOWER "Lower" +#endif // D_STR_LOWER +#ifndef D_STR_BREEZE +#define D_STR_BREEZE "Breeze" +#endif // D_STR_BREEZE +#ifndef D_STR_CIRCULATE +#define D_STR_CIRCULATE "Circulate" +#endif // D_STR_CIRCULATE +#ifndef D_STR_CEILING +#define D_STR_CEILING "Ceiling" +#endif // D_STR_CEILING +#ifndef D_STR_WALL +#define D_STR_WALL "Wall" +#endif // D_STR_WALL +#ifndef D_STR_ROOM +#define D_STR_ROOM "Room" +#endif // D_STR_ROOM +#ifndef D_STR_6THSENSE +#define D_STR_6THSENSE "6th Sense" +#endif // D_STR_6THSENSE +#ifndef D_STR_ZONEFOLLOW +#define D_STR_ZONEFOLLOW "Zone Follow" +#endif // D_STR_ZONEFOLLOW +#ifndef D_STR_FIXED +#define D_STR_FIXED "Fixed" +#endif // D_STR_FIXED + +#ifndef D_STR_AUTO +#define D_STR_AUTO "Auto" +#endif // D_STR_AUTO +#ifndef D_STR_AUTOMATIC +#define D_STR_AUTOMATIC "Automatic" +#endif // D_STR_AUTOMATIC +#ifndef D_STR_MANUAL +#define D_STR_MANUAL "Manual" +#endif // D_STR_MANUAL +#ifndef D_STR_COOL +#define D_STR_COOL "Cool" +#endif // D_STR_COOL +#ifndef D_STR_HEAT +#define D_STR_HEAT "Heat" +#endif // D_STR_HEAT +#ifndef D_STR_FAN +#define D_STR_FAN "Fan" +#endif // D_STR_FAN +#ifndef D_STR_FANONLY +#define D_STR_FANONLY "fan_only" +#endif // D_STR_FANONLY +#ifndef D_STR_DRY +#define D_STR_DRY "Dry" +#endif // D_STR_DRY + +#ifndef D_STR_MAX +#define D_STR_MAX "Max" +#endif // D_STR_MAX +#ifndef D_STR_MAXIMUM +#define D_STR_MAXIMUM "Maximum" +#endif // D_STR_MAXIMUM +#ifndef D_STR_MIN +#define D_STR_MIN "Min" +#endif // D_STR_MIN +#ifndef D_STR_MINIMUM +#define D_STR_MINIMUM "Minimum" +#endif // D_STR_MINIMUM +#ifndef D_STR_MED +#define D_STR_MED "Med" +#endif // D_STR_MED +#ifndef D_STR_MEDIUM +#define D_STR_MEDIUM "Medium" +#endif // D_STR_MEDIUM + +#ifndef D_STR_HIGHEST +#define D_STR_HIGHEST "Highest" +#endif // D_STR_HIGHEST +#ifndef D_STR_HIGH +#define D_STR_HIGH "High" +#endif // D_STR_HIGH +#ifndef D_STR_HI +#define D_STR_HI "Hi" +#endif // D_STR_HI +#ifndef D_STR_MID +#define D_STR_MID "Mid" +#endif // D_STR_MID +#ifndef D_STR_MIDDLE +#define D_STR_MIDDLE "Middle" +#endif // D_STR_MIDDLE +#ifndef D_STR_LOW +#define D_STR_LOW "Low" +#endif // D_STR_LOW +#ifndef D_STR_LO +#define D_STR_LO "Lo" +#endif // D_STR_LO +#ifndef D_STR_LOWEST +#define D_STR_LOWEST "Lowest" +#endif // D_STR_LOWEST +#ifndef D_STR_RIGHT +#define D_STR_RIGHT "Right" +#endif // D_STR_RIGHT +#ifndef D_STR_MAXRIGHT +#define D_STR_MAXRIGHT D_STR_MAX " " D_STR_RIGHT // Set `D_STR_MAX` first! +#endif // D_STR_MAXRIGHT +#ifndef D_STR_RIGHTMAX_NOSPACE +#define D_STR_RIGHTMAX_NOSPACE D_STR_RIGHT D_STR_MAX // Set `D_STR_MAX` first! +#endif // D_STR_RIGHTMAX_NOSPACE +#ifndef D_STR_LEFT +#define D_STR_LEFT "Left" +#endif // D_STR_LEFT +#ifndef D_STR_MAXLEFT +#define D_STR_MAXLEFT D_STR_MAX " " D_STR_LEFT // Set `D_STR_MAX` first! +#endif // D_STR_MAXLEFT +#ifndef D_STR_LEFTMAX_NOSPACE +#define D_STR_LEFTMAX_NOSPACE D_STR_LEFT D_STR_MAX // Set `D_STR_MAX` first! +#endif // D_STR_LEFTMAX_NOSPACE +#ifndef D_STR_WIDE +#define D_STR_WIDE "Wide" +#endif // D_STR_WIDE +#ifndef D_STR_CENTRE +#define D_STR_CENTRE "Centre" +#endif // D_STR_CENTRE +#ifndef D_STR_TOP +#define D_STR_TOP "Top" +#endif // D_STR_TOP +#ifndef D_STR_BOTTOM +#define D_STR_BOTTOM "Bottom" +#endif // D_STR_BOTTOM + +// Compound words/phrases/descriptions from pre-defined words. +// Note: Obviously these need to be defined *after* their component words. +#ifndef D_STR_EYEAUTO +#define D_STR_EYEAUTO D_STR_EYE " " D_STR_AUTO +#endif // D_STR_EYEAUTO +#ifndef D_STR_LIGHTTOGGLE +#define D_STR_LIGHTTOGGLE D_STR_LIGHT " " D_STR_TOGGLE +#endif // D_STR_LIGHTTOGGLE +#ifndef D_STR_OUTSIDEQUIET +#define D_STR_OUTSIDEQUIET D_STR_OUTSIDE " " D_STR_QUIET +#endif // D_STR_OUTSIDEQUIET +#ifndef D_STR_POWERTOGGLE +#define D_STR_POWERTOGGLE D_STR_POWER " " D_STR_TOGGLE +#endif // D_STR_POWERTOGGLE +#ifndef D_STR_PREVIOUSPOWER +#define D_STR_PREVIOUSPOWER D_STR_PREVIOUS " " D_STR_POWER +#endif // D_STR_PREVIOUSPOWER +#ifndef D_STR_SENSORTEMP +#define D_STR_SENSORTEMP D_STR_SENSOR " " D_STR_TEMP +#endif // D_STR_SENSORTEMP +#ifndef D_STR_SLEEP_TIMER +#define D_STR_SLEEP_TIMER D_STR_SLEEP " " D_STR_TIMER +#endif // D_STR_SLEEP_TIMER +#ifndef D_STR_SWINGVMODE +#define D_STR_SWINGVMODE D_STR_SWINGV " " D_STR_MODE +#endif // D_STR_SWINGVMODE +#ifndef D_STR_SWINGVTOGGLE +#define D_STR_SWINGVTOGGLE D_STR_SWINGV " " D_STR_TOGGLE +#endif // D_STR_SWINGVTOGGLE + +// Separators +#ifndef D_CHR_TIME_SEP +#define D_CHR_TIME_SEP ':' +#endif // D_CHR_TIME_SEP +#ifndef D_STR_SPACELBRACE +#define D_STR_SPACELBRACE " (" +#endif // D_STR_SPACELBRACE +#ifndef D_STR_COMMASPACE +#define D_STR_COMMASPACE ", " +#endif // D_STR_COMMASPACE +#ifndef D_STR_COLONSPACE +#define D_STR_COLONSPACE ": " +#endif // D_STR_COLONSPACE + +#ifndef D_STR_DAY +#define D_STR_DAY "Day" +#endif // D_STR_DAY +#ifndef D_STR_DAYS +#define D_STR_DAYS D_STR_DAY "s" +#endif // D_STR_DAYS +#ifndef D_STR_HOUR +#define D_STR_HOUR "Hour" +#endif // D_STR_HOUR +#ifndef D_STR_HOURS +#define D_STR_HOURS D_STR_HOUR "s" +#endif // D_STR_HOURS +#ifndef D_STR_MINUTE +#define D_STR_MINUTE "Minute" +#endif // D_STR_MINUTE +#ifndef D_STR_MINUTES +#define D_STR_MINUTES D_STR_MINUTE "s" +#endif // D_STR_MINUTES +#ifndef D_STR_SECOND +#define D_STR_SECOND "Second" +#endif // D_STR_SECOND +#ifndef D_STR_SECONDS +#define D_STR_SECONDS D_STR_SECOND "s" +#endif // D_STR_SECONDS +#ifndef D_STR_NOW +#define D_STR_NOW "Now" +#endif // D_STR_NOW +#ifndef D_STR_THREELETTERDAYS +#define D_STR_THREELETTERDAYS "SunMonTueWedThuFriSat" +#endif // D_STR_THREELETTERDAYS + +#ifndef D_STR_YES +#define D_STR_YES "Yes" +#endif // D_STR_YES +#ifndef D_STR_NO +#define D_STR_NO "No" +#endif // D_STR_NO +#ifndef D_STR_TRUE +#define D_STR_TRUE "True" +#endif // D_STR_TRUE +#ifndef D_STR_FALSE +#define D_STR_FALSE "False" +#endif // D_STR_FALSE + +#ifndef D_STR_REPEAT +#define D_STR_REPEAT "Repeat" +#endif // D_STR_REPEAT +#ifndef D_STR_CODE +#define D_STR_CODE "Code" +#endif // D_STR_CODE +#ifndef D_STR_BITS +#define D_STR_BITS "Bits" +#endif // D_STR_BITS + +// Protocols Names +#ifndef D_STR_AIRWELL +#define D_STR_AIRWELL "AIRWELL" +#endif // D_STR_AIRWELL +#ifndef D_STR_AIWA_RC_T501 +#define D_STR_AIWA_RC_T501 "AIWA_RC_T501" +#endif // D_STR_AIWA_RC_T501 +#ifndef D_STR_AMCOR +#define D_STR_AMCOR "AMCOR" +#endif // D_STR_AMCOR +#ifndef D_STR_ARGO +#define D_STR_ARGO "ARGO" +#endif // D_STR_ARGO +#ifndef D_STR_CARRIER_AC +#define D_STR_CARRIER_AC "CARRIER_AC" +#endif // D_STR_CARRIER_AC +#ifndef D_STR_COOLIX +#define D_STR_COOLIX "COOLIX" +#endif // D_STR_COOLIX +#ifndef D_STR_DAIKIN +#define D_STR_DAIKIN "DAIKIN" +#endif // D_STR_DAIKIN +#ifndef D_STR_DAIKIN128 +#define D_STR_DAIKIN128 "DAIKIN128" +#endif // D_STR_DAIKIN128 +#ifndef D_STR_DAIKIN152 +#define D_STR_DAIKIN152 "DAIKIN152" +#endif // D_STR_DAIKIN152 +#ifndef D_STR_DAIKIN160 +#define D_STR_DAIKIN160 "DAIKIN160" +#endif // D_STR_DAIKIN160 +#ifndef D_STR_DAIKIN176 +#define D_STR_DAIKIN176 "DAIKIN176" +#endif // D_STR_DAIKIN176 +#ifndef D_STR_DAIKIN2 +#define D_STR_DAIKIN2 "DAIKIN2" +#endif // D_STR_DAIKIN2 +#ifndef D_STR_DAIKIN216 +#define D_STR_DAIKIN216 "DAIKIN216" +#endif // D_STR_DAIKIN216 +#ifndef D_STR_DAIKIN64 +#define D_STR_DAIKIN64 "DAIKIN64" +#endif // D_STR_DAIKIN64 +#ifndef D_STR_DENON +#define D_STR_DENON "DENON" +#endif // D_STR_DENON +#ifndef D_STR_DISH +#define D_STR_DISH "DISH" +#endif // D_STR_DISH +#ifndef D_STR_ELECTRA_AC +#define D_STR_ELECTRA_AC "ELECTRA_AC" +#endif // D_STR_ELECTRA_AC +#ifndef D_STR_EPSON +#define D_STR_EPSON "EPSON" +#endif // D_STR_EPSON +#ifndef D_STR_FUJITSU_AC +#define D_STR_FUJITSU_AC "FUJITSU_AC" +#endif // D_STR_FUJITSU_AC +#ifndef D_STR_GICABLE +#define D_STR_GICABLE "GICABLE" +#endif // D_STR_GICABLE +#ifndef D_STR_GLOBALCACHE +#define D_STR_GLOBALCACHE "GLOBALCACHE" +#endif // D_STR_GLOBALCACHE +#ifndef D_STR_GOODWEATHER +#define D_STR_GOODWEATHER "GOODWEATHER" +#endif // D_STR_GOODWEATHER +#ifndef D_STR_GREE +#define D_STR_GREE "GREE" +#endif // D_STR_GREE +#ifndef D_STR_HAIER_AC +#define D_STR_HAIER_AC "HAIER_AC" +#endif // D_STR_HAIER_AC +#ifndef D_STR_HAIER_AC_YRW02 +#define D_STR_HAIER_AC_YRW02 "HAIER_AC_YRW02" +#endif // D_STR_HAIER_AC_YRW02 +#ifndef D_STR_HITACHI_AC +#define D_STR_HITACHI_AC "HITACHI_AC" +#endif // D_STR_HITACHI_AC +#ifndef D_STR_HITACHI_AC1 +#define D_STR_HITACHI_AC1 "HITACHI_AC1" +#endif // D_STR_HITACHI_AC1 +#ifndef D_STR_HITACHI_AC2 +#define D_STR_HITACHI_AC2 "HITACHI_AC2" +#endif // D_STR_HITACHI_AC2 +#ifndef D_STR_HITACHI_AC3 +#define D_STR_HITACHI_AC3 "HITACHI_AC3" +#endif // D_STR_HITACHI_AC3 +#ifndef D_STR_HITACHI_AC424 +#define D_STR_HITACHI_AC424 "HITACHI_AC424" +#endif // D_STR_HITACHI_AC424 +#ifndef D_STR_INAX +#define D_STR_INAX "INAX" +#endif // D_STR_INAX +#ifndef D_STR_JVC +#define D_STR_JVC "JVC" +#endif // D_STR_JVC +#ifndef D_STR_KELVINATOR +#define D_STR_KELVINATOR "KELVINATOR" +#endif // D_STR_KELVINATOR +#ifndef D_STR_LASERTAG +#define D_STR_LASERTAG "LASERTAG" +#endif // D_STR_LASERTAG +#ifndef D_STR_LEGOPF +#define D_STR_LEGOPF "LEGOPF" +#endif // D_STR_LEGOPF +#ifndef D_STR_LG +#define D_STR_LG "LG" +#endif // D_STR_LG +#ifndef D_STR_LG2 +#define D_STR_LG2 "LG2" +#endif // D_STR_LG2 +#ifndef D_STR_LUTRON +#define D_STR_LUTRON "LUTRON" +#endif // D_STR_LUTRON +#ifndef D_STR_MAGIQUEST +#define D_STR_MAGIQUEST "MAGIQUEST" +#endif // D_STR_MAGIQUEST +#ifndef D_STR_MIDEA +#define D_STR_MIDEA "MIDEA" +#endif // D_STR_MIDEA +#ifndef D_STR_MITSUBISHI +#define D_STR_MITSUBISHI "MITSUBISHI" +#endif // D_STR_MITSUBISHI +#ifndef D_STR_MITSUBISHI112 +#define D_STR_MITSUBISHI112 "MITSUBISHI112" +#endif // D_STR_MITSUBISHI112 +#ifndef D_STR_MITSUBISHI136 +#define D_STR_MITSUBISHI136 "MITSUBISHI136" +#endif // D_STR_MITSUBISHI136 +#ifndef D_STR_MITSUBISHI2 +#define D_STR_MITSUBISHI2 "MITSUBISHI2" +#endif // D_STR_MITSUBISHI2 +#ifndef D_STR_MITSUBISHI_AC +#define D_STR_MITSUBISHI_AC "MITSUBISHI_AC" +#endif // D_STR_MITSUBISHI_AC +#ifndef D_STR_MITSUBISHI_HEAVY_152 +#define D_STR_MITSUBISHI_HEAVY_152 "MITSUBISHI_HEAVY_152" +#endif // D_STR_MITSUBISHI_HEAVY_152 +#ifndef D_STR_MITSUBISHI_HEAVY_88 +#define D_STR_MITSUBISHI_HEAVY_88 "MITSUBISHI_HEAVY_88" +#endif // D_STR_MITSUBISHI_HEAVY_88 +#ifndef D_STR_MWM +#define D_STR_MWM "MWM" +#endif // D_STR_MWM +#ifndef D_STR_NEC +#define D_STR_NEC "NEC" +#endif // D_STR_NEC +#ifndef D_STR_NEC_LIKE +#define D_STR_NEC_LIKE D_STR_NEC "_LIKE" +#endif // D_STR_NEC_LIKE +#ifndef D_STR_NEC_NON_STRICT +#define D_STR_NEC_NON_STRICT D_STR_NEC " (NON-STRICT)" +#endif // D_STR_NEC_NON_STRICT +#ifndef D_STR_NEOCLIMA +#define D_STR_NEOCLIMA "NEOCLIMA" +#endif // D_STR_NEOCLIMA +#ifndef D_STR_NIKAI +#define D_STR_NIKAI "NIKAI" +#endif // D_STR_NIKAI +#ifndef D_STR_PANASONIC +#define D_STR_PANASONIC "PANASONIC" +#endif // D_STR_PANASONIC +#ifndef D_STR_PANASONIC_AC +#define D_STR_PANASONIC_AC "PANASONIC_AC" +#endif // D_STR_PANASONIC_AC +#ifndef D_STR_PIONEER +#define D_STR_PIONEER "PIONEER" +#endif // D_STR_PIONEER +#ifndef D_STR_PRONTO +#define D_STR_PRONTO "PRONTO" +#endif // D_STR_PRONTO +#ifndef D_STR_RAW +#define D_STR_RAW "RAW" +#endif // D_STR_RAW +#ifndef D_STR_RC5 +#define D_STR_RC5 "RC5" +#endif // D_STR_RC5 +#ifndef D_STR_RC5X +#define D_STR_RC5X "RC5X" +#endif // D_STR_RC5X +#ifndef D_STR_RC6 +#define D_STR_RC6 "RC6" +#endif // D_STR_RC6 +#ifndef D_STR_RCMM +#define D_STR_RCMM "RCMM" +#endif // D_STR_RCMM +#ifndef D_STR_SAMSUNG +#define D_STR_SAMSUNG "SAMSUNG" +#endif // D_STR_SAMSUNG +#ifndef D_STR_SAMSUNG36 +#define D_STR_SAMSUNG36 "SAMSUNG36" +#endif // D_STR_SAMSUNG36 +#ifndef D_STR_SAMSUNG_AC +#define D_STR_SAMSUNG_AC "SAMSUNG_AC" +#endif // D_STR_SAMSUNG_AC +#ifndef D_STR_SANYO +#define D_STR_SANYO "SANYO" +#endif // D_STR_SANYO +#ifndef D_STR_SANYO_LC7461 +#define D_STR_SANYO_LC7461 "SANYO_LC7461" +#endif // D_STR_SANYO_LC7461 +#ifndef D_STR_SHARP +#define D_STR_SHARP "SHARP" +#endif // D_STR_SHARP +#ifndef D_STR_SHARP_AC +#define D_STR_SHARP_AC "SHARP_AC" +#endif // D_STR_SHARP_AC +#ifndef D_STR_SHERWOOD +#define D_STR_SHERWOOD "SHERWOOD" +#endif // D_STR_SHERWOOD +#ifndef D_STR_SONY +#define D_STR_SONY "SONY" +#endif // D_STR_SONY +#ifndef D_STR_SONY_38K +#define D_STR_SONY_38K "SONY_38K" +#endif // D_STR_SONY_38K +#ifndef D_STR_SYMPHONY +#define D_STR_SYMPHONY "SYMPHONY" +#endif // D_STR_SYMPHONY +#ifndef D_STR_TCL112AC +#define D_STR_TCL112AC "TCL112AC" +#endif // D_STR_TCL112AC +#ifndef D_STR_TECO +#define D_STR_TECO "TECO" +#endif // D_STR_TECO +#ifndef D_STR_TOSHIBA_AC +#define D_STR_TOSHIBA_AC "TOSHIBA_AC" +#endif // D_STR_TOSHIBA_AC +#ifndef D_STR_TROTEC +#define D_STR_TROTEC "TROTEC" +#endif // D_STR_TROTEC +#ifndef D_STR_UNUSED +#define D_STR_UNUSED "UNUSED" +#endif // D_STR_UNUSED +#ifndef D_STR_VESTEL_AC +#define D_STR_VESTEL_AC "VESTEL_AC" +#endif // D_STR_VESTEL_AC +#ifndef D_STR_WHIRLPOOL_AC +#define D_STR_WHIRLPOOL_AC "WHIRLPOOL_AC" +#endif // D_STR_WHIRLPOOL_AC +#ifndef D_STR_WHYNTER +#define D_STR_WHYNTER "WHYNTER" +#endif // D_STR_WHYNTER + +// IRrecvDumpV2 +#ifndef D_STR_TIMESTAMP +#define D_STR_TIMESTAMP "Timestamp" +#endif // D_STR_TIMESTAMP +#ifndef D_STR_LIBRARY +#define D_STR_LIBRARY "Library" +#endif // D_STR_LIBRARY +#ifndef D_STR_MESGDESC +#define D_STR_MESGDESC "Mesg Desc." +#endif // D_STR_MESGDESC +#ifndef D_STR_IRRECVDUMP_STARTUP +#define D_STR_IRRECVDUMP_STARTUP \ + "IRrecvDumpV2 is now running and waiting for IR input on Pin %d" +#endif // D_STR_IRRECVDUMP_STARTUP +#ifndef D_WARN_BUFFERFULL +#define D_WARN_BUFFERFULL \ + "WARNING: IR code is too big for buffer (>= %d). " \ + "This result shouldn't be trusted until this is resolved. " \ + "Edit & increase `kCaptureBufferSize`." +#endif // D_WARN_BUFFERFULL + +#endif // LOCALE_DEFAULTS_H_ diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/en-AU.h b/lib/IRremoteESP8266-2.7.6/src/locale/en-AU.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/locale/en-AU.h rename to lib/IRremoteESP8266-2.7.6/src/locale/en-AU.h diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/en-IE.h b/lib/IRremoteESP8266-2.7.6/src/locale/en-IE.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/locale/en-IE.h rename to lib/IRremoteESP8266-2.7.6/src/locale/en-IE.h diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/en-UK.h b/lib/IRremoteESP8266-2.7.6/src/locale/en-UK.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/locale/en-UK.h rename to lib/IRremoteESP8266-2.7.6/src/locale/en-UK.h diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/en-US.h b/lib/IRremoteESP8266-2.7.6/src/locale/en-US.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/src/locale/en-US.h rename to lib/IRremoteESP8266-2.7.6/src/locale/en-US.h diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/es-ES.h b/lib/IRremoteESP8266-2.7.6/src/locale/es-ES.h similarity index 97% rename from lib/IRremoteESP8266-2.7.4/src/locale/es-ES.h rename to lib/IRremoteESP8266-2.7.6/src/locale/es-ES.h index 821186141..52f22333d 100644 --- a/lib/IRremoteESP8266-2.7.4/src/locale/es-ES.h +++ b/lib/IRremoteESP8266-2.7.6/src/locale/es-ES.h @@ -7,6 +7,8 @@ #define D_STR_UNKNOWN "DESCONOCIDO" #define D_STR_PROTOCOL "Protocolo" #define D_STR_POWER "Poder" +#define D_STR_PREVIOUS "Anterior" +#define D_STR_PREVIOUSPOWER D_STR_POWER " " D_STR_PREVIOUS #define D_STR_ON "Encendido" #define D_STR_OFF "Apagado" #define D_STR_MODE "Modo" diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/fr-FR.h b/lib/IRremoteESP8266-2.7.6/src/locale/fr-FR.h similarity index 97% rename from lib/IRremoteESP8266-2.7.4/src/locale/fr-FR.h rename to lib/IRremoteESP8266-2.7.6/src/locale/fr-FR.h index 5ab10cb62..0eae39e2e 100644 --- a/lib/IRremoteESP8266-2.7.4/src/locale/fr-FR.h +++ b/lib/IRremoteESP8266-2.7.6/src/locale/fr-FR.h @@ -10,6 +10,8 @@ #define D_STR_SLEEP "Pause" #define D_STR_LIGHT "Lumière" #define D_STR_POWERFUL "Puissance" +#define D_STR_PREVIOUS "Precedente" +#define D_STR_PREVIOUSPOWER D_STR_POWER " " D_STR_PREVIOUS #define D_STR_QUIET "Silence" #define D_STR_ECONO "Economie" #define D_STR_BEEP "Bip" diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/it-IT.h b/lib/IRremoteESP8266-2.7.6/src/locale/it-IT.h similarity index 98% rename from lib/IRremoteESP8266-2.7.4/src/locale/it-IT.h rename to lib/IRremoteESP8266-2.7.6/src/locale/it-IT.h index b078a7319..b04a129d2 100644 --- a/lib/IRremoteESP8266-2.7.4/src/locale/it-IT.h +++ b/lib/IRremoteESP8266-2.7.6/src/locale/it-IT.h @@ -8,6 +8,8 @@ #define D_STR_UNKNOWN "SCONOSCIUTO" #define D_STR_PROTOCOL "Protocollo" #define D_STR_POWER "Accensione" +#define D_STR_PREVIOUS "Precedente" +#define D_STR_PREVIOUSPOWER D_STR_POWER " " D_STR_PREVIOUS #define D_STR_ON "Acceso" #define D_STR_OFF "Spento" #define D_STR_MODE "Modalità" diff --git a/lib/IRremoteESP8266-2.7.4/src/locale/defaults.h b/lib/IRremoteESP8266-2.7.6/src/locale/zh-CN.h similarity index 67% rename from lib/IRremoteESP8266-2.7.4/src/locale/defaults.h rename to lib/IRremoteESP8266-2.7.6/src/locale/zh-CN.h index 20e38082a..bcb30392c 100644 --- a/lib/IRremoteESP8266-2.7.4/src/locale/defaults.h +++ b/lib/IRremoteESP8266-2.7.6/src/locale/zh-CN.h @@ -1,64 +1,56 @@ -// Copyright 2019 - David Conran (@crankyoldgit) -// The default text to use throughout the library. -// The library will use this text if no locale (_IR_LOCALE_) is set or if -// the locale doesn't define particular values. -// If they are defined, this file should NOT override them. -// -// This file should contain a #define for every translateable/locale dependant -// string used by the library. Language specific files don't have to include -// everything. -// -// NOTE: ASCII/UTF-8 characters only. Unicode is NOT supported. -// -// The defaults are English (AU) / en-AU. Australia (AU) is pretty much the same -// as English (UK) for this libraries use case. -#ifndef LOCALE_DEFAULTS_H_ -#define LOCALE_DEFAULTS_H_ +// Copyright 2020 - MiaoYi (@Caffreyfans) +// Locale/language file for China / Simplified. +// This file will override the default values located in `defaults.h`. +#ifndef LOCALE_ZH_CN_H_ +#define LOCALE_ZH_CN_H_ #ifndef D_STR_UNKNOWN -#define D_STR_UNKNOWN "UNKNOWN" +#define D_STR_UNKNOWN "未知" #endif // D_STR_UNKNOWN #ifndef D_STR_PROTOCOL -#define D_STR_PROTOCOL "Protocol" +#define D_STR_PROTOCOL "协议" #endif // D_STR_PROTOCOL #ifndef D_STR_POWER -#define D_STR_POWER "Power" +#define D_STR_POWER "电源" #endif // D_STR_POWER +#ifndef D_STR_PREVIOUS +#define D_STR_PREVIOUS "以前" +#endif // D_STR_PREVIOUS #ifndef D_STR_ON -#define D_STR_ON "On" +#define D_STR_ON "开" #endif // D_STR_ON #ifndef D_STR_OFF -#define D_STR_OFF "Off" +#define D_STR_OFF "关" #endif // D_STR_OFF #ifndef D_STR_MODE -#define D_STR_MODE "Mode" +#define D_STR_MODE "模式" #endif // D_STR_MODE #ifndef D_STR_TOGGLE -#define D_STR_TOGGLE "Toggle" +#define D_STR_TOGGLE "切换" #endif // D_STR_TOGGLE #ifndef D_STR_TURBO -#define D_STR_TURBO "Turbo" +#define D_STR_TURBO "强力" #endif // D_STR_TURBO #ifndef D_STR_SUPER -#define D_STR_SUPER "Super" +#define D_STR_SUPER "超级" #endif // D_STR_SUPER #ifndef D_STR_SLEEP -#define D_STR_SLEEP "Sleep" +#define D_STR_SLEEP "睡眠" #endif // D_STR_SLEEP #ifndef D_STR_LIGHT -#define D_STR_LIGHT "Light" +#define D_STR_LIGHT "灯光" #endif // D_STR_LIGHT #ifndef D_STR_POWERFUL -#define D_STR_POWERFUL "Powerful" +#define D_STR_POWERFUL "强劲模式" #endif // D_STR_POWERFUL #ifndef D_STR_QUIET -#define D_STR_QUIET "Quiet" +#define D_STR_QUIET "安静" #endif // D_STR_QUIET #ifndef D_STR_ECONO -#define D_STR_ECONO "Econo" +#define D_STR_ECONO "经济" #endif // D_STR_ECONO #ifndef D_STR_SWING -#define D_STR_SWING "Swing" +#define D_STR_SWING "扫风" #endif // D_STR_SWING #ifndef D_STR_SWINGH #define D_STR_SWINGH D_STR_SWING"(H)" // Set `D_STR_SWING` first! @@ -67,19 +59,19 @@ #define D_STR_SWINGV D_STR_SWING"(V)" // Set `D_STR_SWING` first! #endif // D_STR_SWINGV #ifndef D_STR_BEEP -#define D_STR_BEEP "Beep" +#define D_STR_BEEP "蜂鸣" #endif // D_STR_BEEP #ifndef D_STR_MOULD -#define D_STR_MOULD "Mould" +#define D_STR_MOULD "模子" #endif // D_STR_MOULD #ifndef D_STR_CLEAN -#define D_STR_CLEAN "Clean" +#define D_STR_CLEAN "清洁" #endif // D_STR_CLEAN #ifndef D_STR_PURIFY -#define D_STR_PURIFY "Purify" +#define D_STR_PURIFY "净化" #endif // D_STR_PURIFY #ifndef D_STR_TIMER -#define D_STR_TIMER "Timer" +#define D_STR_TIMER "计时器" #endif // D_STR_TIMER #ifndef D_STR_ONTIMER #define D_STR_ONTIMER D_STR_ON " " D_STR_TIMER // Set `D_STR_ON` first! @@ -88,106 +80,106 @@ #define D_STR_OFFTIMER D_STR_OFF " " D_STR_TIMER // Set `D_STR_OFF` first! #endif // D_STR_OFFTIMER #ifndef D_STR_CLOCK -#define D_STR_CLOCK "Clock" +#define D_STR_CLOCK "时钟" #endif // D_STR_CLOCK #ifndef D_STR_COMMAND -#define D_STR_COMMAND "Command" +#define D_STR_COMMAND "命令" #endif // D_STR_COMMAND #ifndef D_STR_XFAN #define D_STR_XFAN "XFan" #endif // D_STR_XFAN #ifndef D_STR_HEALTH -#define D_STR_HEALTH "Health" +#define D_STR_HEALTH "健康" #endif // D_STR_HEALTH #ifndef D_STR_MODEL -#define D_STR_MODEL "Model" +#define D_STR_MODEL "模式" #endif // D_STR_MODEL #ifndef D_STR_TEMP -#define D_STR_TEMP "Temp" +#define D_STR_TEMP "温度" #endif // D_STR_TEMP #ifndef D_STR_IFEEL #define D_STR_IFEEL "IFeel" #endif // D_STR_IFEEL #ifndef D_STR_HUMID -#define D_STR_HUMID "Humid" +#define D_STR_HUMID "湿度" #endif // D_STR_HUMID #ifndef D_STR_SAVE -#define D_STR_SAVE "Save" +#define D_STR_SAVE "保存" #endif // D_STR_SAVE #ifndef D_STR_EYE -#define D_STR_EYE "Eye" +#define D_STR_EYE "眼" #endif // D_STR_EYE #ifndef D_STR_FOLLOW -#define D_STR_FOLLOW "Follow" +#define D_STR_FOLLOW "跟随" #endif // D_STR_FOLLOW #ifndef D_STR_ION #define D_STR_ION "Ion" #endif // D_STR_ION #ifndef D_STR_FRESH -#define D_STR_FRESH "Fresh" +#define D_STR_FRESH "刷新" #endif // D_STR_FRESH #ifndef D_STR_HOLD -#define D_STR_HOLD "Hold" +#define D_STR_HOLD "保持" #endif // D_STR_HOLD #ifndef D_STR_8C_HEAT #define D_STR_8C_HEAT "8C " D_STR_HEAT // Set `D_STR_HEAT` first! #endif // D_STR_8C_HEAT #ifndef D_STR_BUTTON -#define D_STR_BUTTON "Button" +#define D_STR_BUTTON "按钮" #endif // D_STR_BUTTON #ifndef D_STR_NIGHT -#define D_STR_NIGHT "Night" +#define D_STR_NIGHT "夜间" #endif // D_STR_NIGHT #ifndef D_STR_SILENT -#define D_STR_SILENT "Silent" +#define D_STR_SILENT "安静" #endif // D_STR_SILENT #ifndef D_STR_FILTER -#define D_STR_FILTER "Filter" +#define D_STR_FILTER "过滤" #endif // D_STR_FILTER #ifndef D_STR_3D #define D_STR_3D "3D" #endif // D_STR_3D #ifndef D_STR_CELSIUS -#define D_STR_CELSIUS "Celsius" +#define D_STR_CELSIUS "摄氏度" #endif // D_STR_CELSIUS #ifndef D_STR_UP -#define D_STR_UP "Up" +#define D_STR_UP "上" #endif // D_STR_UP #ifndef D_STR_TEMPUP #define D_STR_TEMPUP D_STR_TEMP " " D_STR_UP // Set `D_STR_TEMP` first! #endif // D_STR_TEMPUP #ifndef D_STR_DOWN -#define D_STR_DOWN "Down" +#define D_STR_DOWN "下" #endif // D_STR_DOWN #ifndef D_STR_TEMPDOWN #define D_STR_TEMPDOWN D_STR_TEMP " " D_STR_DOWN // Set `D_STR_TEMP` first! #endif // D_STR_TEMPDOWN #ifndef D_STR_CHANGE -#define D_STR_CHANGE "Change" +#define D_STR_CHANGE "改变" #endif // D_STR_CHANGE #ifndef D_STR_START -#define D_STR_START "Start" +#define D_STR_START "开始" #endif // D_STR_START #ifndef D_STR_STOP -#define D_STR_STOP "Stop" +#define D_STR_STOP "结束" #endif // D_STR_STOP #ifndef D_STR_MOVE -#define D_STR_MOVE "Move" +#define D_STR_MOVE "移动" #endif // D_STR_MOVE #ifndef D_STR_SET -#define D_STR_SET "Set" +#define D_STR_SET "设置" #endif // D_STR_SET #ifndef D_STR_CANCEL -#define D_STR_CANCEL "Cancel" +#define D_STR_CANCEL "取消" #endif // D_STR_CANCEL #ifndef D_STR_COMFORT -#define D_STR_COMFORT "Comfort" +#define D_STR_COMFORT "舒适" #endif // D_STR_COMFORT #ifndef D_STR_SENSOR -#define D_STR_SENSOR "Sensor" +#define D_STR_SENSOR "传感器" #endif // D_STR_SENSOR #ifndef D_STR_WEEKLY -#define D_STR_WEEKLY "Weekly" +#define D_STR_WEEKLY "每周" #endif // D_STR_WEEKLY #ifndef D_STR_WEEKLYTIMER #define D_STR_WEEKLYTIMER D_STR_WEEKLY " " D_STR_TIMER // Needs `D_STR_WEEKLY`! @@ -196,130 +188,130 @@ #define D_STR_WIFI "WiFi" #endif // D_STR_WIFI #ifndef D_STR_LAST -#define D_STR_LAST "Last" +#define D_STR_LAST "最近" #endif // D_STR_LAST #ifndef D_STR_FAST -#define D_STR_FAST "Fast" +#define D_STR_FAST "快" #endif // D_STR_FAST #ifndef D_STR_SLOW -#define D_STR_SLOW "Slow" +#define D_STR_SLOW "慢" #endif // D_STR_SLOW #ifndef D_STR_AIRFLOW -#define D_STR_AIRFLOW "Air Flow" +#define D_STR_AIRFLOW "空气流动" #endif // D_STR_AIRFLOW #ifndef D_STR_STEP -#define D_STR_STEP "Step" +#define D_STR_STEP "步" #endif // D_STR_STEP #ifndef D_STR_NA -#define D_STR_NA "N/A" +#define D_STR_NA "不适用" #endif // D_STR_NA #ifndef D_STR_OUTSIDE -#define D_STR_OUTSIDE "Outside" +#define D_STR_OUTSIDE "室外" #endif // D_STR_OUTSIDE #ifndef D_STR_LOUD -#define D_STR_LOUD "Loud" +#define D_STR_LOUD "大声" #endif // D_STR_LOUD #ifndef D_STR_UPPER -#define D_STR_UPPER "Upper" +#define D_STR_UPPER "更高" #endif // D_STR_UPPER #ifndef D_STR_LOWER -#define D_STR_LOWER "Lower" +#define D_STR_LOWER "更低" #endif // D_STR_LOWER #ifndef D_STR_BREEZE -#define D_STR_BREEZE "Breeze" +#define D_STR_BREEZE "微风" #endif // D_STR_BREEZE #ifndef D_STR_CIRCULATE -#define D_STR_CIRCULATE "Circulate" +#define D_STR_CIRCULATE "流通" #endif // D_STR_CIRCULATE #ifndef D_STR_CEILING -#define D_STR_CEILING "Ceiling" +#define D_STR_CEILING "天花板" #endif // D_STR_CEILING #ifndef D_STR_WALL -#define D_STR_WALL "Wall" +#define D_STR_WALL "墙" #endif // D_STR_WALL #ifndef D_STR_ROOM -#define D_STR_ROOM "Room" +#define D_STR_ROOM "房间" #endif // D_STR_ROOM #ifndef D_STR_6THSENSE -#define D_STR_6THSENSE "6th Sense" +#define D_STR_6THSENSE "第六感" #endif // D_STR_6THSENSE #ifndef D_STR_ZONEFOLLOW -#define D_STR_ZONEFOLLOW "Zone Follow" +#define D_STR_ZONEFOLLOW "区域跟随" #endif // D_STR_ZONEFOLLOW #ifndef D_STR_FIXED -#define D_STR_FIXED "Fixed" +#define D_STR_FIXED "固定" #endif // D_STR_FIXED #ifndef D_STR_AUTO -#define D_STR_AUTO "Auto" +#define D_STR_AUTO "自动" #endif // D_STR_AUTO #ifndef D_STR_AUTOMATIC -#define D_STR_AUTOMATIC "Automatic" +#define D_STR_AUTOMATIC "自动的" #endif // D_STR_AUTOMATIC #ifndef D_STR_MANUAL -#define D_STR_MANUAL "Manual" +#define D_STR_MANUAL "手动" #endif // D_STR_MANUAL #ifndef D_STR_COOL -#define D_STR_COOL "Cool" +#define D_STR_COOL "制冷" #endif // D_STR_COOL #ifndef D_STR_HEAT -#define D_STR_HEAT "Heat" +#define D_STR_HEAT "加热" #endif // D_STR_HEAT #ifndef D_STR_FAN -#define D_STR_FAN "Fan" +#define D_STR_FAN "风扇" #endif // D_STR_FAN #ifndef D_STR_FANONLY -#define D_STR_FANONLY "fan_only" +#define D_STR_FANONLY "仅风扇" #endif // D_STR_FANONLY #ifndef D_STR_DRY -#define D_STR_DRY "Dry" +#define D_STR_DRY "干燥" #endif // D_STR_DRY #ifndef D_STR_MAX -#define D_STR_MAX "Max" +#define D_STR_MAX "最大" #endif // D_STR_MAX #ifndef D_STR_MAXIMUM -#define D_STR_MAXIMUM "Maximum" +#define D_STR_MAXIMUM "最小" #endif // D_STR_MAXIMUM #ifndef D_STR_MIN -#define D_STR_MIN "Min" +#define D_STR_MIN "最低" #endif // D_STR_MIN #ifndef D_STR_MINIMUM -#define D_STR_MINIMUM "Minimum" +#define D_STR_MINIMUM "最低" #endif // D_STR_MINIMUM #ifndef D_STR_MED -#define D_STR_MED "Med" +#define D_STR_MED "中" #endif // D_STR_MED #ifndef D_STR_MEDIUM -#define D_STR_MEDIUM "Medium" +#define D_STR_MEDIUM "中" #endif // D_STR_MEDIUM #ifndef D_STR_HIGHEST -#define D_STR_HIGHEST "Highest" +#define D_STR_HIGHEST "最高" #endif // D_STR_HIGHEST #ifndef D_STR_HIGH -#define D_STR_HIGH "High" +#define D_STR_HIGH "高" #endif // D_STR_HIGH #ifndef D_STR_HI -#define D_STR_HI "Hi" +#define D_STR_HI "嗨" #endif // D_STR_HI #ifndef D_STR_MID -#define D_STR_MID "Mid" +#define D_STR_MID "中" #endif // D_STR_MID #ifndef D_STR_MIDDLE -#define D_STR_MIDDLE "Middle" +#define D_STR_MIDDLE "居中" #endif // D_STR_MIDDLE #ifndef D_STR_LOW -#define D_STR_LOW "Low" +#define D_STR_LOW "低" #endif // D_STR_LOW #ifndef D_STR_LO -#define D_STR_LO "Lo" +#define D_STR_LO "低" #endif // D_STR_LO #ifndef D_STR_LOWEST -#define D_STR_LOWEST "Lowest" +#define D_STR_LOWEST "最低" #endif // D_STR_LOWEST #ifndef D_STR_RIGHT -#define D_STR_RIGHT "Right" +#define D_STR_RIGHT "右" #endif // D_STR_RIGHT #ifndef D_STR_MAXRIGHT #define D_STR_MAXRIGHT D_STR_MAX " " D_STR_RIGHT // Set `D_STR_MAX` first! @@ -328,7 +320,7 @@ #define D_STR_RIGHTMAX_NOSPACE D_STR_RIGHT D_STR_MAX // Set `D_STR_MAX` first! #endif // D_STR_RIGHTMAX_NOSPACE #ifndef D_STR_LEFT -#define D_STR_LEFT "Left" +#define D_STR_LEFT "左" #endif // D_STR_LEFT #ifndef D_STR_MAXLEFT #define D_STR_MAXLEFT D_STR_MAX " " D_STR_LEFT // Set `D_STR_MAX` first! @@ -337,16 +329,16 @@ #define D_STR_LEFTMAX_NOSPACE D_STR_LEFT D_STR_MAX // Set `D_STR_MAX` first! #endif // D_STR_LEFTMAX_NOSPACE #ifndef D_STR_WIDE -#define D_STR_WIDE "Wide" +#define D_STR_WIDE "扫风" #endif // D_STR_WIDE #ifndef D_STR_CENTRE -#define D_STR_CENTRE "Centre" +#define D_STR_CENTRE "中间" #endif // D_STR_CENTRE #ifndef D_STR_TOP -#define D_STR_TOP "Top" +#define D_STR_TOP "上部" #endif // D_STR_TOP #ifndef D_STR_BOTTOM -#define D_STR_BOTTOM "Bottom" +#define D_STR_BOTTOM "底部" #endif // D_STR_BOTTOM // Compound words/phrases/descriptions from pre-defined words. @@ -363,6 +355,9 @@ #ifndef D_STR_POWERTOGGLE #define D_STR_POWERTOGGLE D_STR_POWER " " D_STR_TOGGLE #endif // D_STR_POWERTOGGLE +#ifndef D_STR_PREVIOUSPOWER +#define D_STR_PREVIOUSPOWER D_STR_PREVIOUS " " D_STR_POWER +#endif // D_STR_PREVIOUSPOWER #ifndef D_STR_SENSORTEMP #define D_STR_SENSORTEMP D_STR_SENSOR " " D_STR_TEMP #endif // D_STR_SENSORTEMP @@ -391,78 +386,80 @@ #endif // D_STR_COLONSPACE #ifndef D_STR_DAY -#define D_STR_DAY "Day" +#define D_STR_DAY "天" #endif // D_STR_DAY #ifndef D_STR_DAYS #define D_STR_DAYS D_STR_DAY "s" #endif // D_STR_DAYS #ifndef D_STR_HOUR -#define D_STR_HOUR "Hour" +#define D_STR_HOUR "时" #endif // D_STR_HOUR #ifndef D_STR_HOURS #define D_STR_HOURS D_STR_HOUR "s" #endif // D_STR_HOURS #ifndef D_STR_MINUTE -#define D_STR_MINUTE "Minute" +#define D_STR_MINUTE "分" #endif // D_STR_MINUTE #ifndef D_STR_MINUTES #define D_STR_MINUTES D_STR_MINUTE "s" #endif // D_STR_MINUTES #ifndef D_STR_SECOND -#define D_STR_SECOND "Second" +#define D_STR_SECOND "秒" #endif // D_STR_SECOND #ifndef D_STR_SECONDS #define D_STR_SECONDS D_STR_SECOND "s" #endif // D_STR_SECONDS #ifndef D_STR_NOW -#define D_STR_NOW "Now" +#define D_STR_NOW "现在" #endif // D_STR_NOW +/* This is not three letter days. Disabled. #ifndef D_STR_THREELETTERDAYS -#define D_STR_THREELETTERDAYS "SunMonTueWedThuFriSat" +#define D_STR_THREELETTERDAYS "周一至周末" #endif // D_STR_THREELETTERDAYS +*/ #ifndef D_STR_YES -#define D_STR_YES "Yes" +#define D_STR_YES "是" #endif // D_STR_YES #ifndef D_STR_NO -#define D_STR_NO "No" +#define D_STR_NO "否" #endif // D_STR_NO #ifndef D_STR_TRUE -#define D_STR_TRUE "True" +#define D_STR_TRUE "正确" #endif // D_STR_TRUE #ifndef D_STR_FALSE -#define D_STR_FALSE "False" +#define D_STR_FALSE "错误" #endif // D_STR_FALSE #ifndef D_STR_REPEAT -#define D_STR_REPEAT "Repeat" +#define D_STR_REPEAT "重复" #endif // D_STR_REPEAT #ifndef D_STR_CODE -#define D_STR_CODE "Code" +#define D_STR_CODE "代码" #endif // D_STR_CODE #ifndef D_STR_BITS -#define D_STR_BITS "Bits" +#define D_STR_BITS "位" #endif // D_STR_BITS // IRrecvDumpV2 #ifndef D_STR_TIMESTAMP -#define D_STR_TIMESTAMP "Timestamp" +#define D_STR_TIMESTAMP "时间戳记" #endif // D_STR_TIMESTAMP #ifndef D_STR_LIBRARY -#define D_STR_LIBRARY "Library" +#define D_STR_LIBRARY "库文件" #endif // D_STR_LIBRARY #ifndef D_STR_MESGDESC -#define D_STR_MESGDESC "Mesg Desc." +#define D_STR_MESGDESC "等等信息" #endif // D_STR_MESGDESC #ifndef D_STR_IRRECVDUMP_STARTUP #define D_STR_IRRECVDUMP_STARTUP \ - "IRrecvDumpV2 is now running and waiting for IR input on Pin %d" + "IRrecvDumpV2 运行当中,等待红外信息输入位于引脚 %d" #endif // D_STR_IRRECVDUMP_STARTUP #ifndef D_WARN_BUFFERFULL #define D_WARN_BUFFERFULL \ - "WARNING: IR code is too big for buffer (>= %d). " \ - "This result shouldn't be trusted until this is resolved. " \ - "Edit & increase `kCaptureBufferSize`." + "警告: 红外编码数组过大(>= %d). " \ + "在解决此问题之前,不应信任此结果. " \ + "编辑并增加 `kCaptureBufferSize` 变量." #endif // D_WARN_BUFFERFULL -#endif // LOCALE_DEFAULTS_H_ +#endif // LOCALE_ZH_CN_H_ diff --git a/lib/IRremoteESP8266-2.7.4/test/IRac_test.cpp b/lib/IRremoteESP8266-2.7.6/test/IRac_test.cpp similarity index 90% rename from lib/IRremoteESP8266-2.7.4/test/IRac_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/IRac_test.cpp index c8c231621..8ddf98b59 100644 --- a/lib/IRremoteESP8266-2.7.4/test/IRac_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/IRac_test.cpp @@ -54,6 +54,8 @@ TEST(TestIRac, Amcor) { ASSERT_EQ(AMCOR, ac._irsend.capture.decode_type); ASSERT_EQ(kAmcorBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Argo) { @@ -103,6 +105,8 @@ TEST(TestIRac, Coolix) { ASSERT_EQ(COOLIX, ac._irsend.capture.decode_type); ASSERT_EQ(kCoolixBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); // Confirm we are sending with a repeat of 1. i.e. two messages. EXPECT_EQ( "f38000d50" // 38kHz Frequency and 50% duty-cycle. @@ -165,6 +169,8 @@ TEST(TestIRac, Daikin) { ASSERT_EQ(DAIKIN, ac._irsend.capture.decode_type); ASSERT_EQ(kDaikinBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Daikin128) { @@ -196,6 +202,8 @@ TEST(TestIRac, Daikin128) { ASSERT_EQ(DAIKIN128, ac._irsend.capture.decode_type); ASSERT_EQ(kDaikin128Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Daikin152) { @@ -222,6 +230,8 @@ TEST(TestIRac, Daikin152) { ASSERT_EQ(DAIKIN152, ac._irsend.capture.decode_type); ASSERT_EQ(kDaikin152Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Daikin160) { @@ -245,6 +255,8 @@ TEST(TestIRac, Daikin160) { ASSERT_EQ(DAIKIN160, ac._irsend.capture.decode_type); ASSERT_EQ(kDaikin160Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Daikin176) { @@ -267,6 +279,8 @@ TEST(TestIRac, Daikin176) { ASSERT_EQ(DAIKIN176, ac._irsend.capture.decode_type); ASSERT_EQ(kDaikin176Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Daikin2) { @@ -303,6 +317,8 @@ TEST(TestIRac, Daikin2) { ASSERT_EQ(DAIKIN2, ac._irsend.capture.decode_type); ASSERT_EQ(kDaikin2Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Daikin216) { @@ -329,6 +345,36 @@ TEST(TestIRac, Daikin216) { ASSERT_EQ(DAIKIN216, ac._irsend.capture.decode_type); ASSERT_EQ(kDaikin216Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); +} + +TEST(TestIRac, Daikin64) { + IRDaikin64 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + char expected[] = + "Power Toggle: On, Mode: 2 (Cool), Temp: 27C, Fan: 8 (Low), " + "Turbo: Off, Quiet: Off, Swing(V): On, Sleep: On, " + "Clock: 17:59, On Timer: Off, Off Timer: Off"; + + ac.begin(); + irac.daikin64(&ac, + true, // Power (Toggle) + stdAc::opmode_t::kCool, // Mode + 27, // Celsius + stdAc::fanspeed_t::kLow, // Fan Speed + stdAc::swingv_t::kAuto, // Veritcal swing + false, // Quiet + false, // Turbo + 360, // Sleep + 17 * 60 + 59); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN64, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin64Bits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Electra) { @@ -392,6 +438,8 @@ TEST(TestIRac, Fujitsu) { ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kFujitsuAcBits - 8, ac._irsend.capture.bits); ASSERT_EQ(ardb1_expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); ac._irsend.reset(); irac.fujitsu(&ac, @@ -413,6 +461,7 @@ TEST(TestIRac, Fujitsu) { ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kFujitsuAcBits, ac._irsend.capture.bits); ASSERT_EQ(arrah2e_expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); ac._irsend.reset(); irac.fujitsu(&ac, fujitsu_ac_remote_model_t::ARRY4, // Model @@ -433,6 +482,7 @@ TEST(TestIRac, Fujitsu) { ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kFujitsuAcBits, ac._irsend.capture.bits); ASSERT_EQ(arry4_expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Goodweather) { @@ -459,6 +509,8 @@ TEST(TestIRac, Goodweather) { ASSERT_EQ(GOODWEATHER, ac._irsend.capture.decode_type); ASSERT_EQ(kGoodweatherBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Gree) { @@ -489,6 +541,8 @@ TEST(TestIRac, Gree) { ASSERT_EQ(GREE, ac._irsend.capture.decode_type); ASSERT_EQ(kGreeBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Haier) { @@ -516,6 +570,8 @@ TEST(TestIRac, Haier) { ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kHaierACBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } @@ -544,6 +600,8 @@ TEST(TestIRac, HaierYrwo2) { ASSERT_EQ(HAIER_AC_YRW02, ac._irsend.capture.decode_type); ASSERT_EQ(kHaierACYRW02Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Hitachi) { @@ -569,6 +627,40 @@ TEST(TestIRac, Hitachi) { ASSERT_EQ(HITACHI_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kHitachiAcBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); +} + +TEST(TestIRac, Hitachi1) { + IRHitachiAc1 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + char expected[] = + "Model: 1 (R-LT0541-HTA-A), Power: On, Power Toggle: Off, " + "Mode: 6 (Cool), Temp: 19C, Fan: 4 (Medium), " + "Swing(V) Toggle: On, Swing(V): On, Swing(H): On, Sleep: 2, " + "On Timer: Off, Off Timer: Off"; + + ac.begin(); + irac.hitachi1(&ac, + hitachi_ac1_remote_model_t::R_LT0541_HTA_A, // Model + true, // Power + false, // Power Toggle + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Vertical swing + stdAc::swingh_t::kLeft, // Horizontal swing + true, // Swing toggle + 5 * 60 + 37); // Sleep + + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC1, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc1Bits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Hitachi424) { @@ -596,6 +688,8 @@ TEST(TestIRac, Hitachi424) { ASSERT_EQ(HITACHI_AC424, ac._irsend.capture.decode_type); ASSERT_EQ(kHitachiAc424Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); ac._irsend.reset(); irac.hitachi424(&ac, @@ -611,6 +705,7 @@ TEST(TestIRac, Hitachi424) { ASSERT_EQ(HITACHI_AC424, ac._irsend.capture.decode_type); ASSERT_EQ(kHitachiAc424Bits, ac._irsend.capture.bits); ASSERT_EQ(expected_swingv, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Kelvinator) { @@ -642,6 +737,8 @@ TEST(TestIRac, Kelvinator) { ASSERT_EQ(KELVINATOR, ac._irsend.capture.decode_type); ASSERT_EQ(kKelvinatorBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, LG) { @@ -666,6 +763,8 @@ TEST(TestIRac, LG) { ASSERT_EQ(LG, ac._irsend.capture.decode_type); ASSERT_EQ(kLgBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Midea) { @@ -692,6 +791,8 @@ TEST(TestIRac, Midea) { ASSERT_EQ(MIDEA, ac._irsend.capture.decode_type); ASSERT_EQ(kMideaBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Mitsubishi) { @@ -719,6 +820,8 @@ TEST(TestIRac, Mitsubishi) { ASSERT_EQ(MITSUBISHI_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kMitsubishiACBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Mitsubishi136) { @@ -743,6 +846,8 @@ TEST(TestIRac, Mitsubishi136) { ASSERT_EQ(MITSUBISHI136, ac._irsend.capture.decode_type); ASSERT_EQ(kMitsubishi136Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, MitsubishiHeavy88) { @@ -771,6 +876,8 @@ TEST(TestIRac, MitsubishiHeavy88) { ASSERT_EQ(MITSUBISHI_HEAVY_88, ac._irsend.capture.decode_type); ASSERT_EQ(kMitsubishiHeavy88Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, MitsubishiHeavy152) { @@ -802,6 +909,8 @@ TEST(TestIRac, MitsubishiHeavy152) { ASSERT_EQ(MITSUBISHI_HEAVY_152, ac._irsend.capture.decode_type); ASSERT_EQ(kMitsubishiHeavy152Bits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Neoclima) { @@ -832,6 +941,8 @@ TEST(TestIRac, Neoclima) { ASSERT_EQ(decode_type_t::NEOCLIMA, ac._irsend.capture.decode_type); ASSERT_EQ(kNeoclimaBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Panasonic) { @@ -862,6 +973,8 @@ TEST(TestIRac, Panasonic) { ASSERT_EQ(PANASONIC_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits); ASSERT_EQ(expected_nke, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); char expected_dke[] = "Model: 3 (DKE), Power: On, Mode: 3 (Cool), Temp: 18C, Fan: 4 (High), " @@ -887,6 +1000,7 @@ TEST(TestIRac, Panasonic) { ASSERT_EQ(PANASONIC_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits); ASSERT_EQ(expected_dke, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Samsung) { @@ -895,7 +1009,8 @@ TEST(TestIRac, Samsung) { IRrecv capture(0); char expected[] = "Power: On, Mode: 0 (Auto), Temp: 28C, Fan: 6 (Auto), Swing: On, " - "Beep: On, Clean: On, Quiet: On, Powerful: Off, Light: On, Ion: Off"; + "Beep: On, Clean: On, Quiet: On, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off"; ac.begin(); irac.samsung(&ac, @@ -918,6 +1033,8 @@ TEST(TestIRac, Samsung) { ASSERT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kSamsungAcBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); ac._irsend.reset(); irac.samsung(&ac, @@ -943,8 +1060,10 @@ TEST(TestIRac, Samsung) { // desired state. char expected_on[] = "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off"; + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off"; ASSERT_EQ(expected_on, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Sharp) { @@ -952,11 +1071,13 @@ TEST(TestIRac, Sharp) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 3 (Medium)"; + "Power: On, Previous Power: On, Mode: 2 (Cool), Temp: 28C, " + "Fan: 3 (Medium)"; ac.begin(); irac.sharp(&ac, true, // Power + true, // Previous Power stdAc::opmode_t::kCool, // Mode 28, // Celsius stdAc::fanspeed_t::kMedium); // Fan speed @@ -966,6 +1087,8 @@ TEST(TestIRac, Sharp) { ASSERT_EQ(SHARP_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kSharpAcBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Tcl112) { @@ -994,6 +1117,8 @@ TEST(TestIRac, Tcl112) { ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type); ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Teco) { @@ -1019,6 +1144,8 @@ TEST(TestIRac, Teco) { ASSERT_EQ(TECO, ac._irsend.capture.decode_type); ASSERT_EQ(kTecoBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Toshiba) { @@ -1039,6 +1166,8 @@ TEST(TestIRac, Toshiba) { ASSERT_EQ(TOSHIBA_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kToshibaACBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Trotec) { @@ -1066,6 +1195,8 @@ TEST(TestIRac, Trotec) { ASSERT_EQ(TROTEC, ac._irsend.capture.decode_type); ASSERT_EQ(kTrotecBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Vestel) { @@ -1093,6 +1224,8 @@ TEST(TestIRac, Vestel) { ASSERT_EQ(VESTEL_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); ac._irsend.reset(); char expected_clocks[] = @@ -1117,6 +1250,7 @@ TEST(TestIRac, Vestel) { ASSERT_EQ(VESTEL_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits); ASSERT_EQ(expected_clocks, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); // Now check it sends both messages during normal operation when the // clock is set. @@ -1183,6 +1317,8 @@ TEST(TestIRac, Whirlpool) { ASSERT_EQ(WHIRLPOOL_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kWhirlpoolAcBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, cmpStates) { @@ -1577,6 +1713,8 @@ TEST(TestIRac, Issue1001) { "On Timer: Off, Off Timer: Off, Sleep: Off, Super: Off, " "Command: 1 (Power)", IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); // Now check if the mode is set to "Off" instead of just change to power off. // i.e. How Home Assistant expects things to work. @@ -1606,6 +1744,7 @@ TEST(TestIRac, Issue1001) { "On Timer: Off, Off Timer: Off, Sleep: Off, Super: Off, " "Command: 1 (Power)", IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } // Check power switching in Daikin2 common a/c handling when from an IR message. diff --git a/lib/IRremoteESP8266-2.7.4/test/IRrecv_test.cpp b/lib/IRremoteESP8266-2.7.6/test/IRrecv_test.cpp similarity index 91% rename from lib/IRremoteESP8266-2.7.4/test/IRrecv_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/IRrecv_test.cpp index fa025a2cb..e66866be1 100644 --- a/lib/IRremoteESP8266-2.7.4/test/IRrecv_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/IRrecv_test.cpp @@ -1589,3 +1589,107 @@ TEST(TestCrudeNoiseFilter, NoiseAtEndOfSample) { "uint64_t data = 0x20DFC03F;\n", resultToSourceCode(&irsend.capture)); } + +TEST(TestManchesterCode, matchManchester) { + IRsendTest irsend(0); + IRrecv irrecv(0); + + const uint16_t rawData[163] = { + 2860, 3862, + 1924, 1952, 1926, 1952, 956, 984, 1924, 1028, 952, 958, 980, 956, 982, + 1882, 1016, 950, 1958, 1920, 1948, 1004, 954, 956, 984, 956, 952, 984, + 974, 966, 974, 964, 974, 1888, 1010, 960, 1948, 1002, 946, 962, 978, + 962, 976, 960, 948, 992, 978, 1886, 982, 984, 1924, 1954, 952, 986, + 3892, 3862, + 1924, 1954, 1924, 1954, 984, 952, 1956, 996, 954, 990, 948, 958, 980, + 1882, 1016, 952, 1958, 1920, 1956, 994, 944, 962, 986, 956, 972, 962, + 976, 962, 978, 964, 952, 1908, 1010, 960, 1948, 1002, 946, 960, 978, + 962, 946, 990, 978, 960, 978, 1888, 1008, 954, 1952, 1928, 982, 956, + 3920, 3830, + 1946, 1934, 1954, 1922, 976, 962, 1946, 1006, 922, 990, 948, 988, 950, + 1910, 1008, 964, 1922, 1952, 1924, 1030, 920, 984, 954, 986, 952, 986, + 952, 984, 954, 986, 952, 1910, 1010, 960, 1928, 1024, 944, 996, 924, + 984, 954, 988, 950, 984, 954, 1910, 1008, 960, 1920, 1958, 980, 958, + 4800}; + irsend.sendRaw(rawData, 163, 38); + irsend.makeDecodeResult(); + + uint16_t offset = 1; + uint64_t result = 0; + uint16_t nbits = 32; + EXPECT_EQ(56, irrecv.matchManchester(irsend.capture.rawbuf + offset, &result, + irsend.capture.rawlen - offset, nbits, + 2860, 3800, 1000, 0, 3800)); + EXPECT_EQ(0x4F2FE7E4, result); + + irsend.reset(); + irsend.sendRaw(rawData, 55, 38); // Send just the bare minimum. + irsend.makeDecodeResult(); + + EXPECT_EQ(55, irrecv.matchManchester(irsend.capture.rawbuf + offset, &result, + irsend.capture.rawlen - offset, nbits, + 2860, 3800, 1000, 0, 3800)); + EXPECT_EQ(0x4F2FE7E4, result); + + irsend.reset(); + irsend.sendRaw(rawData, 52, 38); // Now, just too short. + irsend.makeDecodeResult(); + + EXPECT_EQ(0, irrecv.matchManchester(irsend.capture.rawbuf + offset, &result, + irsend.capture.rawlen - offset, nbits, + 2860, 3800, 1000, 0, 0)); +} + +TEST(TestManchesterCode, ManchesterLoopBackGEThomasTest) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint16_t offset = 1; + uint64_t result = 0; + uint16_t nbits = 32; + irsend.begin(); + irsend.reset(); + irsend.sendManchester(5000, 7000, 1000, 0, 10000, 0x12345678, nbits); + irsend.makeDecodeResult(); + EXPECT_EQ(52, irrecv.matchManchester(irsend.capture.rawbuf + offset, &result, + irsend.capture.rawlen - offset, nbits, + 5000, 7000, 1000, 0, 10000, true, + kUseDefTol, kMarkExcess, true, true)); + EXPECT_EQ(0x12345678, result); + + irsend.reset(); + irsend.sendManchester(5000, 7000, 1000, 0, 10000, 0x87654321, nbits); + irsend.makeDecodeResult(); + EXPECT_EQ(52, irrecv.matchManchester(irsend.capture.rawbuf + offset, &result, + irsend.capture.rawlen - offset, nbits, + 5000, 7000, 1000, 0, 10000, true, + kUseDefTol, kMarkExcess, true, true)); + EXPECT_EQ(0x87654321, result); +} + +TEST(TestManchesterCode, ManchesterLoopBackIEEE802_3Test) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint16_t offset = 1; + uint64_t result = 0; + uint16_t nbits = 32; + irsend.begin(); + irsend.reset(); + irsend.sendManchester(5000, 7000, 1000, 0, 10000, 0x12345678, nbits, 38000, + true, kNoRepeat, kDutyDefault, false); + irsend.makeDecodeResult(); + EXPECT_EQ(52, irrecv.matchManchester(irsend.capture.rawbuf + offset, &result, + irsend.capture.rawlen - offset, nbits, + 5000, 7000, 1000, 0, 10000, true, + kUseDefTol, kMarkExcess, true, false)); + EXPECT_EQ(0x12345678, result); + + irsend.reset(); + irsend.sendManchester(5000, 7000, 1000, 0, 10000, 0x87654321, nbits, 38000, + true, kNoRepeat, kDutyDefault, false); + irsend.makeDecodeResult(); + EXPECT_EQ(54, irrecv.matchManchester(irsend.capture.rawbuf + offset, &result, + irsend.capture.rawlen - offset, nbits, + 5000, 7000, 1000, 0, 10000, true, + kUseDefTol, kMarkExcess, true, false)); + EXPECT_EQ(0x87654321, result); +} diff --git a/lib/IRremoteESP8266-2.7.4/test/IRrecv_test.h b/lib/IRremoteESP8266-2.7.6/test/IRrecv_test.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/IRrecv_test.h rename to lib/IRremoteESP8266-2.7.6/test/IRrecv_test.h diff --git a/lib/IRremoteESP8266-2.7.4/test/IRsend_test.cpp b/lib/IRremoteESP8266-2.7.6/test/IRsend_test.cpp similarity index 89% rename from lib/IRremoteESP8266-2.7.4/test/IRsend_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/IRsend_test.cpp index f2e280504..2f39e323d 100644 --- a/lib/IRremoteESP8266-2.7.4/test/IRsend_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/IRsend_test.cpp @@ -786,3 +786,80 @@ TEST(TestSend, defaultBits) { } } } + +// Tests sendManchester(). + +// Test sending zero bits. +TEST(TestSendManchester, SendZeroBits) { + IRsendTest irsend(0); + irsend.begin(); + irsend.sendManchester(0, 0, 1, 0, 0, 0b1, 0); + EXPECT_EQ("f36000d25m0f38000d50s1m2s1", irsend.outputStr()); + irsend.sendManchester(1, 2, 100, 3, 4, 0b1, 0); + EXPECT_EQ("f38000d50m1s102m200s100m3s4", irsend.outputStr()); +} + +// Test sending zero and one. +TEST(TestSendManchester, SendSingleBit) { + IRsendTest irsend(0); + irsend.begin(); + irsend.sendManchester(1000, 2000, 100, 3000, 4000, 0b0, 1); + EXPECT_EQ("f38000d50m1000s2100m200s200m3100s4000", irsend.outputStr()); + irsend.sendManchester(1000, 2000, 100, 3000, 4000, 0b0, 1, 38, true, + kNoRepeat, kDutyDefault, false); + EXPECT_EQ("f38000d50m1000s2000m100s200m200s100m3000s4000", + irsend.outputStr()); +} + +// Test sending bit order. +TEST(TestSendManchester, TestingBitSendOrder) { + IRsendTest irsend(0); + irsend.begin(); + irsend.sendManchester(1000, 2000, 100, 3000, 0, 0b10, 2); + EXPECT_EQ("f38000d50m1000s2100m200s100m100s200m3100", irsend.outputStr()); + irsend.sendManchester(1000, 2000, 100, 3000, 0, 0b10, 2, 38, false); + EXPECT_EQ("f38000d50m1000s2100m200s200m200s100m3000", irsend.outputStr()); + irsend.sendManchester(1000, 2000, 100, 3000, 0, 0b0001, 4, 38, true); + EXPECT_EQ("f38000d50m1000s2100m200s200m100s100m100s100m200s100m3000", + irsend.outputStr()); + irsend.sendManchester(1000, 2000, 100, 3000, 0, 0b0001, 4, 38, false); + EXPECT_EQ("f38000d50m1000s2100m200s100m100s200m100s100m100s100m3100", + irsend.outputStr()); +} + +// Test sending typical data. +TEST(TestSendManchester, SendTypicalData) { + IRsendTest irsend(0); + irsend.begin(); + // Example from https://en.wikipedia.org/wiki/Manchester_code diagram + irsend.sendManchester(0, 0, 100, 0, 0, 0b10100111001, 11, 38, true); + EXPECT_EQ( + "f38000d50" + "m0s100m200s100" + "m100s200m200s200m100s100m200s100m100s100m100s200m100s100m200s100", + irsend.outputStr()); + irsend.sendManchester(100, 200, 1, 300, 0, 0x1234567890ABCDEF, 64, 38, true); + EXPECT_EQ( + "f38000d50" + "m100s201" + "m2s2m1s1m1s1m2s2m1s1m2s2m1s1m1s1m2s1m1s2m2s2m1s1m1s1m2s2m2s2m2s1m1s2m1s1" + "m2s1m1s1m1s1m1s2m1s1m1s1m2s2m1s1m2s2m1s1m1s1m1s1m2s2m2s2m2s2m2s1m1s1m1s1" + "m1s2m1s1m2s1m1s2m2s1m1s1m1s1m1s2m2s1m1s1m1s1m1s1m300", + irsend.outputStr()); +} + +// Test sending more than expected bits. +TEST(TestSendManchester, SendOverLargeData) { + IRsendTest irsend(0); + irsend.begin(); + irsend.sendManchester(100, 200, 1, 300, 0, 0xFFFFFFFFFFFFFFFF, 70, 38, true); + EXPECT_EQ( + "f38000d50" + "m100s201" + "m2s2m1s1m1s1m1s1m1s1m1s1m2s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1" + "m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1" + "m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1" + "m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1m1s1" + "m300", + irsend.outputStr()); +} diff --git a/lib/IRremoteESP8266-2.7.4/test/IRsend_test.h b/lib/IRremoteESP8266-2.7.6/test/IRsend_test.h similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/IRsend_test.h rename to lib/IRremoteESP8266-2.7.6/test/IRsend_test.h diff --git a/lib/IRremoteESP8266-2.7.4/test/IRutils_test.cpp b/lib/IRremoteESP8266-2.7.6/test/IRutils_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/IRutils_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/IRutils_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/Makefile b/lib/IRremoteESP8266-2.7.6/test/Makefile similarity index 96% rename from lib/IRremoteESP8266-2.7.4/test/Makefile rename to lib/IRremoteESP8266-2.7.6/test/Makefile index 7b1a4b379..dc0574a1d 100644 --- a/lib/IRremoteESP8266-2.7.4/test/Makefile +++ b/lib/IRremoteESP8266-2.7.6/test/Makefile @@ -39,7 +39,8 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_test \ ir_Whirlpool_test ir_Lutron_test ir_Electra_test ir_Pioneer_test \ ir_MWM_test ir_Vestel_test ir_Teco_test ir_Tcl_test ir_Lego_test IRac_test \ ir_MitsubishiHeavy_test ir_Trotec_test ir_Argo_test ir_Goodweather_test \ - ir_Inax_test ir_Neoclima_test ir_Amcor_test ir_Epson_test + ir_Inax_test ir_Neoclima_test ir_Amcor_test ir_Epson_test ir_Symphony_test \ + ir_Airwell_test # All Google Test headers. Usually you shouldn't change this # definition. @@ -85,7 +86,7 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ ir_Hitachi.o ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o \ ir_Pioneer.o ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o ir_Argo.o \ ir_Trotec.o ir_MitsubishiHeavy.o ir_Goodweather.o ir_Inax.o ir_Neoclima.o \ - ir_Amcor.o ir_Epson.o + ir_Amcor.o ir_Epson.o ir_Symphony.o ir_Airwell.o # All the IR Protocol header files. PROTOCOLS_H = $(USER_DIR)/ir_Amcor.h \ @@ -632,3 +633,21 @@ ir_Epson_test.o : ir_Epson_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) ir_Epson_test : $(COMMON_OBJ) ir_Epson_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Symphony.o : $(USER_DIR)/ir_Symphony.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Symphony.cpp + +ir_Symphony_test.o : ir_Symphony_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Symphony_test.cpp + +ir_Symphony_test : $(COMMON_OBJ) ir_Symphony_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Airwell.o : $(USER_DIR)/ir_Airwell.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Airwell.cpp + +ir_Airwell_test.o : ir_Airwell_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Airwell_test.cpp + +ir_Airwell_test : $(COMMON_OBJ) ir_Airwell_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ diff --git a/lib/IRremoteESP8266-2.7.6/test/ir_Airwell_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Airwell_test.cpp new file mode 100644 index 000000000..b58bceced --- /dev/null +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Airwell_test.cpp @@ -0,0 +1,216 @@ +// Copyright 2020 David Conran + +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for decodeAirwell(). + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1069 +// Data from: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1069#issuecomment-604293514 +TEST(TestDecodeAirwell, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + // ON / OFF / 25 degrees heat + const uint16_t rawData_1[163] = { + 2860, 3862, + 1924, 1952, 1926, 1952, 956, 984, 1924, 1028, 952, 958, 980, 956, 982, + 1882, 1016, 950, 1958, 1920, 1948, 1004, 954, 956, 984, 956, 952, 984, + 974, 966, 974, 964, 974, 1888, 1010, 960, 1948, 1002, 946, 962, 978, + 962, 976, 960, 948, 992, 978, 1886, 982, 984, 1924, 1954, 952, 986, + 3892, 3862, + 1924, 1954, 1924, 1954, 984, 952, 1956, 996, 954, 990, 948, 958, 980, + 1882, 1016, 952, 1958, 1920, 1956, 994, 944, 962, 986, 956, 972, 962, + 976, 962, 978, 964, 952, 1908, 1010, 960, 1948, 1002, 946, 960, 978, + 962, 946, 990, 978, 960, 978, 1888, 1008, 954, 1952, 1928, 982, 956, + 3920, 3830, + 1946, 1934, 1954, 1922, 976, 962, 1946, 1006, 922, 990, 948, 988, 950, + 1910, 1008, 964, 1922, 1952, 1924, 1030, 920, 984, 954, 986, 952, 986, + 952, 984, 954, 986, 952, 1910, 1010, 960, 1928, 1024, 944, 996, 924, + 984, 954, 988, 950, 984, 954, 1910, 1008, 960, 1920, 1958, 980, 958, + 4800}; // UNKNOWN 565E2BB3 + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData_1, 163, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::AIRWELL, irsend.capture.decode_type); + ASSERT_EQ(kAirwellBits, irsend.capture.bits); + EXPECT_EQ(0x4F2FE7E4, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + + const uint16_t rawData_2[175] = { + 2862, 3892, + 1894, 1060, 890, 1944, 952, 1014, 922, 1016, 1892, 1062, 886, + 1020, 928, 1042, 896, 1938, 960, 1008, 920, 1020, 928, 1010, 928, 1012, + 1896, 1056, 892, 1016, 922, 1020, 918, 1018, 920, 1018, 920, 1946, 950, + 1014, 1894, 1062, 896, 1010, 928, 1010, 928, 1014, 924, 1014, 922, 1938, + 960, 1012, 1896, 1984, 922, 1014, + 3862, 3894, + 1892, 1062, 896, 1936, 950, 1020, 928, 1008, 1898, 1056, 892, 1016, 952, + 984, 954, 1910, 956, 1012, 924, 1014, 924, 1014, 922, 1014, 1892, 1060, + 916, 992, 926, 1010, 928, 1014, 954, 982, 944, 1918, 958, 1010, 1896, + 1058, 922, 986, 952, 988, 920, 1018, 950, 986, 952, 1914, 954, 1014, + 1892, 1986, 922, 1016, + 3860, 3896, + 1898, 1054, 924, 1908, 958, 1012, 926, 1012, 1896, 1056, 942, 966, 952, + 988, 950, 1910, 956, 1016, 922, 1016, 952, 988, 950, 986, 1922, 1032, + 916, 990, 948, 960, 988, 984, 944, 960, 978, 1918, 978, 990, 1896, 1054, + 924, 982, 946, 994, 954, 984, 954, 988, 950, 1910, 956, 1012, 1896, 1984, + 922, 1018, 4768}; // UNKNOWN 1002DCC8 + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData_2, 175, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::AIRWELL, irsend.capture.decode_type); + ASSERT_EQ(kAirwellBits, irsend.capture.bits); + EXPECT_EQ(0x8F07E7E4, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestDecodeAirwell, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + irsend.sendAirwell(0xB0D0181B); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::AIRWELL, irsend.capture.decode_type); + EXPECT_EQ(kAirwellBits, irsend.capture.bits); + EXPECT_EQ(0xB0D0181B, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + + EXPECT_EQ( + "f38000d50" + "m2850s3800" + "m1900s950m950s1900m1900s950m950s1900m950s950m950s950m950s950" + "m1900s950m950s1900m1900s1900m950s950m950s950m950s950m950s950" + "m950s950m950s950m1900s950m950s1900m950s950m950s950m950s950" + "m950s950m950s950m1900s950m950s1900m1900s950m950s950" + "m2850s3800" + "m1900s950m950s1900m1900s950m950s1900m950s950m950s950m950s950" + "m1900s950m950s1900m1900s1900m950s950m950s950m950s950m950s950" + "m950s950m950s950m1900s950m950s1900m950s950m950s950m950s950" + "m950s950m950s950m1900s950m950s1900m1900s950m950s950" + "m2850s3800" + "m1900s950m950s1900m1900s950m950s1900m950s950m950s950m950s950" + "m1900s950m950s1900m1900s1900m950s950m950s950m950s950m950s950" + "m950s950m950s950m1900s950m950s1900m950s950m950s950m950s950" + "m950s950m950s950m1900s950m950s1900m1900s950m950s950" + "m3800s100000", + irsend.outputStr()); + + irsend.reset(); + irsend.sendAirwell(0x4F2FE7E4); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::AIRWELL, irsend.capture.decode_type); + EXPECT_EQ(kAirwellBits, irsend.capture.bits); + EXPECT_EQ(0x4F2FE7E4, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ( + "f38000d50" + "m2850s3800" + "m1900s1900m1900s1900m950s950m1900s950m950s950m950s950m950" + "s1900m950s950m1900s1900m1900s950m950s950m950s950m950s950" + "m950s950m950s950m950s1900m950s950m1900s950m950s950m950" + "s950m950s950m950s950m950s1900m950s950m1900s1900m950s950" + "m3800s3800" + "m1900s1900m1900s1900m950s950m1900s950m950s950m950s950m950" + "s1900m950s950m1900s1900m1900s950m950s950m950s950m950s950" + "m950s950m950s950m950s1900m950s950m1900s950m950s950m950" + "s950m950s950m950s950m950s1900m950s950m1900s1900m950s950" + "m3800s3800" + "m1900s1900m1900s1900m950s950m1900s950m950s950m950s950m950" + "s1900m950s950m1900s1900m1900s950m950s950m950s950m950s950" + "m950s950m950s950m950s1900m950s950m1900s950m950s950m950" + "s950m950s950m950s950m950s1900m950s950m1900s1900m950s950" + "m4750s100000", + irsend.outputStr()); + + irsend.reset(); + irsend.sendAirwell(0x70F8181B); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::AIRWELL, irsend.capture.decode_type); + EXPECT_EQ(kAirwellBits, irsend.capture.bits); + EXPECT_EQ(0x70F8181B, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1069 +// Data from: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1069#issuecomment-607659677 +TEST(TestDecodeAirwell, RealExample2) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + // "When I change from 22° to 23° :" + const uint16_t rawData[175] = { + 2890, 2918, + 924, 1006, 946, 1886, 1986, 1922, 978, 958, 1952, 1000, 952, 956, 974, + 964, 976, 1884, 1016, 952, 1948, 1006, 944, 962, 980, 958, 982, 958, 952, + 982, 978, 960, 980, 956, 986, 956, 974, 1888, 1014, 954, 1948, 1002, 948, + 960, 980, 960, 950, 986, 1006, 962, 980, 1854, 1036, 932, 1948, 1928, 984, + 954, + 3918, 2920, + 922, 1008, 942, 1888, 1984, 1926, 984, 950, 1952, 1000, 952, 956, 984, + 956, 954, 1908, 1014, 952, 1948, 1002, 948, 960, 950, 986, 984, 956, 954, + 984, 956, 984, 946, 992, 948, 992, 948, 1910, 1012, 958, 1954, 996, 922, + 984, 956, 986, 956, 980, 950, 988, 954, 1910, 1010, 956, 1946, 1932, 978, + 962, + 3922, 2914, + 918, 1010, 920, 1912, 1980, 1928, 982, 956, 1944, 1006, 924, 982, 958, + 982, 948, 1912, 1008, 958, 1952, 1000, 920, 988, 952, 982, 958, 984, 958, + 978, 952, 986, 986, 952, 958, 984, 946, 1912, 1010, 962, 1948, 1004, 926, + 980, 950, 988, 952, 984, 956, 982, 948, 1916, 1016, 952, 1950, 1926, 984, + 952, 4830}; // UNKNOWN 8E34167B + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 175, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::AIRWELL, irsend.capture.decode_type); + ASSERT_EQ(kAirwellBits, irsend.capture.bits); + EXPECT_EQ(0xB0C0181B, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + + // Resend it as a synthetic to see if it decodes to the same value. + irsend.reset(); + irsend.sendAirwell(0xB0C0181B); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::AIRWELL, irsend.capture.decode_type); + ASSERT_EQ(kAirwellBits, irsend.capture.bits); + EXPECT_EQ(0xB0C0181B, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("AIRWELL", typeToString(decode_type_t::AIRWELL)); + ASSERT_EQ(decode_type_t::AIRWELL, strToDecodeType("AIRWELL")); + ASSERT_FALSE(hasACState(decode_type_t::AIRWELL)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::AIRWELL)); + ASSERT_EQ(kAirwellBits, IRsend::defaultBits(decode_type_t::AIRWELL)); + ASSERT_EQ(kAirwellMinRepeats, IRsend::minRepeats(decode_type_t::AIRWELL)); +} diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Aiwa_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Aiwa_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Aiwa_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Amcor_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Amcor_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Amcor_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Amcor_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Argo_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Argo_test.cpp similarity index 95% rename from lib/IRremoteESP8266-2.7.4/test/ir_Argo_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Argo_test.cpp index d5a5d17ad..3ab890adc 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Argo_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Argo_test.cpp @@ -1,5 +1,6 @@ // Copyright 2019 David Conran #include "ir_Argo.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -105,6 +106,12 @@ TEST(TestDecodeArgo, SyntheticDecode) { EXPECT_EQ(decode_type_t::ARGO, irsend.capture.decode_type); EXPECT_EQ(kArgoBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Power: On, Mode: 0 (Cool), Fan: 0 (Auto), Temp: 20C, Room Temp: 21C, " + "Max: On, IFeel: On, Night: On", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Carrier_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Carrier_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Carrier_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Coolix_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Coolix_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Coolix_test.cpp index 20a46c7b4..45d53a12f 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Coolix_test.cpp @@ -773,6 +773,8 @@ TEST(TestCoolixACClass, Issue985) { EXPECT_EQ(kCoolixBits, ac._irsend.capture.bits); EXPECT_EQ(kCoolixOff, ac._irsend.capture.value); EXPECT_EQ("Power: Off", IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); ac._irsend.reset(); @@ -793,6 +795,7 @@ TEST(TestCoolixACClass, Issue985) { EXPECT_EQ( "Power: On, Mode: 0 (Cool), Fan: 5 (Auto), Temp: 20C, Zone Follow: Off, " "Sensor Temp: Off", IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); ac._irsend.reset(); @@ -808,6 +811,7 @@ TEST(TestCoolixACClass, Issue985) { EXPECT_EQ(kCoolixBits, ac._irsend.capture.bits); EXPECT_EQ(kCoolixOff, ac._irsend.capture.value); EXPECT_EQ("Power: Off", IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestCoolixACClass, PowerStateWithSetRaw) { diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Daikin_test.cpp similarity index 92% rename from lib/IRremoteESP8266-2.7.4/test/ir_Daikin_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Daikin_test.cpp index 60d39d9a3..e37c6b00a 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Daikin_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Daikin_test.cpp @@ -1545,6 +1545,11 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ(decode_type_t::DAIKIN216, strToDecodeType("DAIKIN216")); ASSERT_TRUE(hasACState(decode_type_t::DAIKIN216)); ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN216)); + + ASSERT_EQ("DAIKIN64", typeToString(decode_type_t::DAIKIN64)); + ASSERT_EQ(decode_type_t::DAIKIN64, strToDecodeType("DAIKIN64")); + ASSERT_FALSE(hasACState(decode_type_t::DAIKIN64)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN64)); } // https://github.com/crankyoldgit/IRremoteESP8266/issues/582#issuecomment-453863879 @@ -2581,6 +2586,8 @@ TEST(TestDecodeDaikin128, RealExample) { "On Timer: Off, On Timer: 07:30, Off Timer: Off, Off Timer: 22:00, " "Light Toggle: 0 (Off)", IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t result, prev; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); } // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/827 @@ -2971,6 +2978,8 @@ TEST(TestDecodeDaikin152, RealExample) { "Power: Off, Mode: 0 (Auto), Temp: 26C, Fan: 2 (UNKNOWN), Swing(V): Off, " "Powerful: Off, Quiet: On, Econo: Off, Sensor: Off, Comfort: Off", IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t result, prev; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); } // https://github.com/crankyoldgit/IRremoteESP8266/issues/873 @@ -3006,6 +3015,8 @@ TEST(TestDecodeDaikin152, SyntheticExample) { "Power: On, Mode: 3 (Cool), Temp: 20C, Fan: 1 (Low), Swing(V): On, " "Powerful: Off, Quiet: Off, Econo: Off, Sensor: Off, Comfort: Off", IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t result, prev; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); } TEST(TestDaikin2ClassNew, Issue908) { @@ -3400,3 +3411,282 @@ TEST(TestDaikin2Class, Issue1035) { ac.toString()); ASSERT_FALSE(ac.toCommon().power); } + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 +// Data from: +// https://docs.google.com/spreadsheets/d/1sxjLQCRLMFM1FQpttBXsye2JG5hHIe2BrKnKDuPV9Bw/edit#gid=726071135&range=A1 +TEST(TestDecodeDaikin64, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + uint16_t rawData[137] = { + 9864, 9778, 9810, 9728, 4666, 2482, 384, 342, 390, 922, 386, 928, 388, + 348, 388, 920, 386, 348, 390, 342, 384, 330, 414, 342, 390, 922, 386, 348, + 390, 342, 382, 352, 384, 352, 382, 924, 386, 356, 382, 344, 384, 350, 388, + 346, 390, 342, 386, 348, 386, 928, 388, 348, 388, 354, 388, 888, 412, 928, + 390, 896, 416, 348, 386, 348, 388, 346, 388, 342, 384, 358, 388, 340, 384, + 926, 384, 932, 386, 346, 388, 922, 384, 350, 384, 348, 384, 358, 382, 338, + 394, 922, 388, 928, 386, 350, 384, 926, 390, 344, 388, 342, 386, 356, 388, + 338, 382, 928, 382, 932, 390, 344, 386, 924, 390, 344, 388, 314, 414, 356, + 384, 344, 386, 346, 390, 926, 386, 924, 388, 924, 388, 926, 386, 924, 388, + 350, 388, 20258, 4670}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 137, kDaikin64Freq); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::DAIKIN64, irsend.capture.decode_type); + ASSERT_EQ(kDaikin64Bits, irsend.capture.bits); + EXPECT_EQ(0x7C16161607204216, irsend.capture.value); + EXPECT_EQ( + "Power Toggle: On, Mode: 2 (Cool), Temp: 16C, Fan: 4 (Medium), " + "Turbo: Off, Quiet: Off, Swing(V): Off, Sleep: Off, " + "Clock: 07:20, On Timer: Off, Off Timer: Off", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t result, prev; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); +} + +TEST(TestDecodeDaikin64, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + irsend.begin(); + irsend.reset(); + irsend.sendDaikin64(0x7C16161607204216); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::DAIKIN64, irsend.capture.decode_type); + ASSERT_EQ(kDaikin64Bits, irsend.capture.bits); + EXPECT_EQ(0x7C16161607204216, irsend.capture.value); +} + +TEST(TestDaikin64Class, ChecksumAndSetGetRaw) { + IRDaikin64 ac(kGpioUnused); + + const uint64_t valid = 0x7C16161607204216; + const uint64_t invalid = 0x1C16161607204216; + ASSERT_NE(valid, invalid); + ASSERT_EQ(0x07, IRDaikin64::calcChecksum(valid)); + ASSERT_TRUE(IRDaikin64::validChecksum(valid)); + ASSERT_FALSE(IRDaikin64::validChecksum(invalid)); + ac.setRaw(valid); + ASSERT_EQ(valid, ac.getRaw()); + ac.setRaw(invalid); + ASSERT_EQ(valid, ac.getRaw()); +} + +TEST(TestDaikin64Class, Temperature) { + IRDaikin64 ac(0); + ac.begin(); + ac.setTemp(0); + EXPECT_EQ(kDaikin64MinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikin64MaxTemp, ac.getTemp()); + + ac.setTemp(kDaikin64MinTemp); + EXPECT_EQ(kDaikin64MinTemp, ac.getTemp()); + + ac.setTemp(kDaikin64MaxTemp); + EXPECT_EQ(kDaikin64MaxTemp, ac.getTemp()); + + ac.setTemp(kDaikin64MinTemp - 1); + EXPECT_EQ(kDaikin64MinTemp, ac.getTemp()); + + ac.setTemp(kDaikin64MaxTemp + 1); + EXPECT_EQ(kDaikin64MaxTemp, ac.getTemp()); + + ac.setTemp(kDaikin64MinTemp + 1); + EXPECT_EQ(kDaikin64MinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); + + // Ref: https://docs.google.com/spreadsheets/d/1sxjLQCRLMFM1FQpttBXsye2JG5hHIe2BrKnKDuPV9Bw/edit#gid=1521758824&range=R2:AG2 + const uint64_t deg16 = 0x8C16105500001216; + ac.setRaw(deg16); + EXPECT_EQ(16, ac.getTemp()); +} + +TEST(TestDaikin64Class, OperatingMode) { + IRDaikin64 ac(0); + ac.begin(); + + ac.setMode(kDaikin64Cool); + EXPECT_EQ(kDaikin64Cool, ac.getMode()); + + ac.setMode(kDaikin64Fan); + EXPECT_EQ(kDaikin64Fan, ac.getMode()); + + ac.setMode(kDaikin64Dry); + EXPECT_EQ(kDaikin64Dry, ac.getMode()); + + ac.setMode(kDaikin64Fan + 1); + EXPECT_EQ(kDaikin64Cool, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kDaikin64Cool, ac.getMode()); + + // Ref: https://docs.google.com/spreadsheets/d/1sxjLQCRLMFM1FQpttBXsye2JG5hHIe2BrKnKDuPV9Bw/edit#gid=1521758824&range=R2:AG2 + const uint64_t cool = 0x8C16105500001216; + ac.setMode(kDaikin64Dry); + ac.setRaw(cool); + EXPECT_EQ(kDaikin64Cool, ac.getMode()); +} + +TEST(TestDaikin64Class, PowerToggle) { + IRDaikin64 ac(0); + ac.begin(); + + ac.setPowerToggle(true); + EXPECT_TRUE(ac.getPowerToggle()); + ac.setPowerToggle(false); + EXPECT_FALSE(ac.getPowerToggle()); + ac.setPowerToggle(true); + EXPECT_TRUE(ac.getPowerToggle()); +} + +TEST(TestDaikin64Class, FanSpeed) { + IRDaikin64 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kDaikin64FanAuto, ac.getFan()); + ac.setFan(255); + EXPECT_EQ(kDaikin64FanAuto, ac.getFan()); + ac.setFan(5); + EXPECT_EQ(kDaikin64FanAuto, ac.getFan()); + + ac.setFan(kDaikin64FanHigh); + EXPECT_EQ(kDaikin64FanHigh, ac.getFan()); + + // Beyond Quiet should default to Auto. + ac.setFan(kDaikin64FanQuiet + 1); + EXPECT_EQ(kDaikin64FanAuto, ac.getFan()); + + ac.setFan(kDaikin64FanMed); + EXPECT_EQ(kDaikin64FanMed, ac.getFan()); + + ac.setFan(kDaikin64FanLow); + EXPECT_EQ(kDaikin64FanLow, ac.getFan()); + + ac.setFan(kDaikin64FanTurbo); + EXPECT_EQ(kDaikin64FanTurbo, ac.getFan()); + + ac.setFan(kDaikin64FanAuto); + EXPECT_EQ(kDaikin64FanAuto, ac.getFan()); + + ac.setFan(kDaikin64FanQuiet); + EXPECT_EQ(kDaikin64FanQuiet, ac.getFan()); +} + +TEST(TestDaikin64Class, Turbo) { + IRDaikin64 ac(kGpioUnused); + ac.begin(); + ac.setFan(kDaikin64FanAuto); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + EXPECT_EQ(kDaikin64FanTurbo, ac.getFan()); + ac.setTurbo(false); + EXPECT_NE(kDaikin64FanTurbo, ac.getFan()); + EXPECT_FALSE(ac.getTurbo()); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestDaikin64Class, Quiet) { + IRDaikin64 ac(kGpioUnused); + ac.begin(); + ac.setFan(kDaikin64FanAuto); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_EQ(kDaikin64FanQuiet, ac.getFan()); + ac.setQuiet(false); + EXPECT_NE(kDaikin64FanQuiet, ac.getFan()); + EXPECT_FALSE(ac.getQuiet()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); +} + +TEST(TestDaikin64Class, Sleep) { + IRDaikin64 ac(kGpioUnused); + ac.begin(); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestDaikin64Class, SwingVertical) { + IRDaikin64 ac(kGpioUnused); + ac.begin(); + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingVertical()); + ac.setSwingVertical(false); + EXPECT_FALSE(ac.getSwingVertical()); + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingVertical()); +} + +TEST(TestDaikin64Class, Clock) { + IRDaikin64 ac(kGpioUnused); + ac.begin(); + + ac.setClock(0); + EXPECT_EQ(0, ac.getClock()); + ac.setClock(23 * 60 + 59); + EXPECT_EQ(23 * 60 + 59, ac.getClock()); + ac.setClock(23 * 60 + 59 + 1); + EXPECT_EQ(0, ac.getClock()); + ac.setClock(24 * 60 + 99); + EXPECT_EQ(0, ac.getClock()); +} + +// Test human readable output. +TEST(TestDaikin64Class, HumanReadable) { + IRDaikin64 ac(kGpioUnused); + EXPECT_EQ( + "Power Toggle: On, Mode: 2 (Cool), Temp: 16C, Fan: 4 (Medium), " + "Turbo: Off, Quiet: Off, Swing(V): Off, Sleep: Off, " + "Clock: 07:20, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.setPowerToggle(false); + ac.setMode(kDaikin64Fan); + ac.setTemp(30); + ac.setFan(kDaikin64FanAuto); + ac.setSwingVertical(true); + EXPECT_EQ( + "Power Toggle: Off, Mode: 4 (Fan), Temp: 30C, Fan: 1 (Auto), " + "Turbo: Off, Quiet: Off, Swing(V): On, Sleep: Off, " + "Clock: 07:20, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.setTurbo(true); + ac.setOffTimeEnabled(true); + ac.setOffTime(23 * 60 + 30); + EXPECT_EQ( + "Power Toggle: Off, Mode: 4 (Fan), Temp: 30C, Fan: 3 (Turbo), " + "Turbo: On, Quiet: Off, Swing(V): On, Sleep: Off, " + "Clock: 07:20, On Timer: Off, Off Timer: 23:30", + ac.toString()); + ac.setQuiet(true); + ac.setSleep(true); + ac.setClock(12 * 60 + 31); + ac.setOnTimeEnabled(true); + ac.setOnTime(8 * 60 + 59); + ac.setOffTimeEnabled(false); + EXPECT_EQ( + "Power Toggle: Off, Mode: 4 (Fan), Temp: 30C, Fan: 9 (Quiet), " + "Turbo: Off, Quiet: On, Swing(V): On, Sleep: On, " + "Clock: 12:31, On Timer: 08:30, Off Timer: Off", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Denon_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Denon_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Denon_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Denon_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Dish_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Dish_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Dish_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Dish_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Electra_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Electra_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Electra_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Electra_test.cpp index b49482433..1fddf537b 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Electra_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Electra_test.cpp @@ -103,6 +103,8 @@ TEST(TestDecodeElectraAC, RealExampleDecode) { "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), " "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off", IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } TEST(TestIRElectraAcClass, Power) { diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Epson_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Epson_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Epson_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Epson_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Fujitsu_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Fujitsu_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Fujitsu_test.cpp index 2aead1e13..aa8d2d941 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Fujitsu_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Fujitsu_test.cpp @@ -1,5 +1,5 @@ // Copyright 2017 Jonny Graham, David Conran - +#include "IRac.h" #include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" @@ -316,6 +316,12 @@ TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { ASSERT_EQ(kFujitsuAcMinBits + 8, irsend.capture.bits); uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Power: Off, Mode: 0 (Auto), Temp: 16C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); irsend.reset(); diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_GICable_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_GICable_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_GICable_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_GICable_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_GlobalCache_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_GlobalCache_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_GlobalCache_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Goodweather_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Goodweather_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Goodweather_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Goodweather_test.cpp index 6345d2398..be07b9d09 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Goodweather_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Goodweather_test.cpp @@ -1,6 +1,7 @@ // Copyright 2019 David Conran #include "ir_Goodweather.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -276,11 +277,12 @@ uint16_t rawData_FAD2BE31[197] = { EXPECT_EQ(0x0, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); EXPECT_FALSE(irsend.capture.repeat); - ac.setRaw(irsend.capture.value); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 22C, Fan: 3 (Low), Turbo: -, Light: -, " "Sleep: -, Swing: 1 (Slow), Command: 4 (Swing)", - ac.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Gree_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Gree_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Gree_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Gree_test.cpp index 6c8672c77..a824cd10a 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Gree_test.cpp @@ -1,6 +1,7 @@ // Copyright 2017 David Conran #include "ir_Gree.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRremoteESP8266.h" @@ -576,7 +577,9 @@ TEST(TestDecodeGree, NormalRealExample) { "Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 26C, Fan: 1 (Low), " "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " "Swing(V) Mode: Manual, Swing(V): 2 (UNKNOWN), Timer: Off", - irgree.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } TEST(TestGreeClass, toCommon) { diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Haier_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Haier_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Haier_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Haier_test.cpp index ee55d2866..cdd2894df 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Haier_test.cpp @@ -1,6 +1,7 @@ // Copyright 2018 David Conran #include "ir_Haier.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -818,13 +819,13 @@ TEST(TestDecodeHaierAC, RealExample1) { EXPECT_FALSE(irsend.capture.repeat); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); - IRHaierAC haier(0); - haier.setRaw(irsend.capture.state); EXPECT_EQ( "Command: 1 (On), Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), " "Swing: 0 (Off), Sleep: Off, Health: Off, " "Clock: 00:01, On Timer: Off, Off Timer: Off", - haier.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } // Decode a "real" example message. diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Hitachi_test.cpp similarity index 65% rename from lib/IRremoteESP8266-2.7.4/test/ir_Hitachi_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Hitachi_test.cpp index 1f04ee8ee..aef8008ff 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Hitachi_test.cpp @@ -572,6 +572,12 @@ TEST(TestDecodeHitachiAC1, NormalRealExample) { EXPECT_EQ(HITACHI_AC1, irsend.capture.decode_type); ASSERT_EQ(kHitachiAc1Bits, irsend.capture.bits); EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, kHitachiAc1Bits); + EXPECT_EQ( + "Model: 2 (R-LT0541-HTA-B), Power: Off, Power Toggle: On, " + "Mode: 6 (Cool), Temp: 23C, Fan: 1 (Auto), " + "Swing(V) Toggle: Off, Swing(V): Off, Swing(H): Off, Sleep: Off, " + "On Timer: Off, Off Timer: Off", + IRAcUtils::resultAcToString(&irsend.capture)); } // Tests for sendHitachiAC2(). @@ -809,13 +815,18 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ("HITACHI_AC1", typeToString(decode_type_t::HITACHI_AC1)); ASSERT_EQ(decode_type_t::HITACHI_AC1, strToDecodeType("HITACHI_AC1")); ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC1)); - ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC1)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC1)); ASSERT_EQ("HITACHI_AC2", typeToString(decode_type_t::HITACHI_AC2)); ASSERT_EQ(decode_type_t::HITACHI_AC2, strToDecodeType("HITACHI_AC2")); ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC2)); ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC2)); + ASSERT_EQ("HITACHI_AC3", typeToString(decode_type_t::HITACHI_AC3)); + ASSERT_EQ(decode_type_t::HITACHI_AC3, strToDecodeType("HITACHI_AC3")); + ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC3)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC3)); + ASSERT_EQ("HITACHI_AC424", typeToString(decode_type_t::HITACHI_AC424)); ASSERT_EQ(decode_type_t::HITACHI_AC424, strToDecodeType("HITACHI_AC424")); ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC424)); @@ -915,6 +926,8 @@ TEST(TestDecodeHitachiAc424, RealExample) { "Power: On, Mode: 3 (Cool), Temp: 23C, Fan: 5 (Auto), " "Swing(V) Toggle: Off, Button: 19 (Power/Mode)", IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } TEST(TestDecodeHitachiAc424, SyntheticExample) { @@ -1113,7 +1126,7 @@ TEST(TestIRHitachiAc424Class, HumanReadable) { } TEST(TestIRHitachiAc424Class, toCommon) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.setPower(true); ac.setMode(kHitachiAc424Cool); ac.setTemp(20); @@ -1139,3 +1152,652 @@ TEST(TestIRHitachiAc424Class, toCommon) { ASSERT_EQ(-1, ac.toCommon().sleep); ASSERT_EQ(-1, ac.toCommon().clock); } + +TEST(TestDecodeHitachiAc3, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + uint8_t expected[kHitachiAc3StateLength - 4] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE6, 0x19, 0x89, 0x76, 0x01, + 0xFE, 0x3F, 0xC0, 0x2F, 0xD0, 0x18, 0xE7, 0x00, 0xFF, 0xA0, 0x5F}; + + irsend.reset(); + irsend.sendHitachiAc3(expected, kHitachiAc3StateLength - 4); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(HITACHI_AC3, irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc3Bits - 32, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); +} + +// Decode a 'real' HitachiAc3 message. +TEST(TestDecodeHitachiAc3, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + uint8_t expected[kHitachiAc3StateLength - 4] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE6, 0x19, 0x89, 0x76, 0x01, + 0xFE, 0x3F, 0xC0, 0x2F, 0xD0, 0x18, 0xE7, 0x00, 0xFF, 0xA0, 0x5F}; + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-597519432 + uint16_t rawData[371] = { + // Power Off + 3422, 1660, 464, 1264, 438, 426, 438, 402, 486, 426, 440, 402, 462, 402, + 488, 428, 438, 402, 460, 404, 488, 426, 440, 424, 436, 428, 462, 1240, + 466, 398, 462, 404, 486, 426, 438, 402, 464, 400, 490, 400, 464, 426, 438, + 402, 488, 428, 436, 428, 462, 376, 486, 404, 474, 416, 438, 400, 488, 402, + 462, 426, 438, 426, 458, 1246, 464, 426, 436, 1244, 488, 1266, 434, 1268, + 436, 1242, 486, 1242, 462, 1240, 464, 426, 462, 1266, 438, 1242, 462, + 1240, 514, 1214, 466, 1236, 462, 1266, 464, 1264, 438, 1266, 438, 1240, + 488, 400, 466, 424, 440, 402, 486, 404, 462, 402, 464, 402, 488, 426, 438, + 402, 462, 402, 488, 1264, 438, 1242, 460, 428, 462, 428, 436, 1264, 440, + 1240, 488, 1240, 464, 1240, 460, 402, 490, 424, 440, 1264, 438, 1242, 512, + 402, 438, 426, 466, 398, 464, 1266, 440, 400, 462, 402, 488, 1264, 438, + 402, 462, 402, 482, 432, 438, 1264, 436, 404, 488, 1240, 462, 1264, 438, + 402, 486, 1246, 456, 1266, 438, 1238, 488, 402, 462, 1240, 462, 402, 512, + 402, 438, 428, 438, 426, 462, 430, 434, 428, 438, 402, 512, 376, 462, + 1240, 464, 1264, 458, 1270, 436, 1242, 462, 1240, 486, 1242, 460, 1242, + 462, 1242, 486, 1240, 464, 1240, 464, 1238, 492, 1264, 436, 1266, 440, + 400, 488, 426, 436, 430, 434, 430, 460, 404, 462, 426, 438, 402, 488, 426, + 436, 1264, 466, 1238, 462, 1242, 462, 1266, 438, 1238, 490, 1264, 438, + 426, 438, 1240, 486, 406, 462, 402, 462, 400, 488, 428, 436, 426, 438, + 402, 484, 1268, 438, 426, 436, 1242, 488, 1266, 436, 402, 462, 402, 502, + 386, 464, 1240, 464, 1240, 488, 426, 438, 426, 438, 402, 488, 1240, 462, + 1266, 438, 1242, 486, 428, 440, 424, 438, 1266, 460, 1268, 438, 1264, 438, + 404, 486, 428, 438, 426, 438, 402, 490, 400, 462, 426, 436, 402, 490, 426, + 438, 1240, 462, 1268, 460, 1268, 434, 1266, 436, 1240, 514, 1238, 438, + 1240, 488, 1240, 460, 406, 464, 426, 436, 402, 488, 402, 462, 402, 462, + 1264, 462, 404, 462, 1266, 434, 1268, 464, 1264, 438, 1242, 464, 1238, + 488, 1266, 438, 402, 462, 1268, 458, 430, 410}; + + irsend.reset(); + irsend.sendRaw(rawData, 371, kHitachiAcFreq); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC3, irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc3Bits - 32, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestDecodeHitachiAc3, SyntheticTempChangeExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint16_t expectedLength = kHitachiAc3MinStateLength + 2; + uint8_t expected[expectedLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE3, 0x1C, 0x89, 0x76, 0x08, + 0xF7, 0x3F, 0xC0, 0x15, 0xEA}; + + irsend.reset(); + irsend.sendHitachiAc3(expected, expectedLength); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(HITACHI_AC3, irsend.capture.decode_type); + ASSERT_EQ(expectedLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); +} + +// Decode a 'real' Temp Change HitachiAc3 message. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-597592537 +TEST(TestDecodeHitachiAc3, RealTempChangeExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected[kHitachiAc3MinStateLength + 2] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE3, 0x1C, 0x89, 0x76, 0x08, + 0xF7, 0x3F, 0xC0, 0x15, 0xEA}; + const uint16_t expectedBits = kHitachiAc3MinBits + 2 * 8; + + const uint16_t rawData[275] = { + // Change Temp + 3422, 1636, 490, 1242, 484, 380, 488, 400, 466, 426, 432, 406, 490, 400, + 464, 426, 462, 398, 466, 376, 492, 400, 458, 404, 490, 400, 466, 1262, + 434, 430, 464, 400, 490, 400, 458, 380, 490, 374, 490, 426, 432, 430, 464, + 400, 464, 426, 460, 402, 468, 372, 490, 400, 460, 404, 490, 374, 490, 400, + 484, 378, 490, 398, 464, 1240, 490, 398, 466, 1238, 464, 1266, 434, 1268, + 466, 1238, 466, 1240, 484, 1218, 490, 400, 460, 1246, 484, 1242, 466, + 1214, 488, 1240, 486, 1218, 490, 1214, 488, 1266, 434, 1242, 490, 1214, + 490, 424, 460, 404, 464, 374, 492, 424, 434, 430, 466, 400, 464, 426, 464, + 376, 492, 1236, 464, 1240, 488, 402, 466, 374, 490, 400, 458, 1244, 490, + 1238, 496, 1234, 436, 404, 488, 400, 464, 1240, 484, 1244, 464, 1238, 466, + 426, 434, 430, 466, 400, 464, 1264, 434, 406, 490, 374, 490, 1264, 458, + 404, 466, 400, 466, 424, 466, 1238, 464, 400, 466, 1238, 486, 1216, 490, + 400, 464, 1240, 486, 1240, 466, 1238, 460, 432, 432, 430, 468, 374, 488, + 426, 462, 1242, 464, 400, 466, 426, 434, 404, 490, 374, 490, 1240, 484, + 1244, 466, 1240, 462, 428, 436, 1242, 490, 1212, 490, 1264, 466, 1236, + 466, 1214, 490, 1240, 488, 1240, 466, 1236, 466, 1264, 434, 1268, 464, + 400, 494, 400, 430, 406, 488, 376, 488, 428, 432, 432, 462, 402, 462, 426, + 458, 1220, 488, 1214, 490, 1240, 484, 406, 466, 1212, 490, 426, 462, 1216, + 488, 400, 464, 402, 488, 402, 462, 374, 492, 1262, 460, 380, 488, 1240, + 464, 428, 462, 1214, 492, 1236, 462, 1216, 490}; + + irsend.reset(); + irsend.sendRaw(rawData, 275, kHitachiAcFreq); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC3, irsend.capture.decode_type); + ASSERT_EQ(expectedBits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, expectedBits); +} + +// Decode a 'real' Cancel Timer HitachiAc3 message. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-598631576 +TEST(TestDecodeHitachiAc3, RealCancelTimerExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected[kHitachiAc3MinStateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE2, 0x1D, 0x89, 0x76, 0x0D, + 0xF2, 0x3F, 0xC0}; + const uint16_t expectedBits = kHitachiAc3MinBits; + + const uint16_t rawData[243] = { + // Cancel Timer + 3368, 1688, 460, 1244, 456, 434, 458, 406, 456, 408, 480, 410, 458, 406, + 458, 406, 484, 406, 458, 406, 458, 432, 428, 464, 456, 380, 458, 1242, + 458, 432, 456, 406, 486, 378, 430, 458, 456, 408, 458, 406, 456, 434, 456, + 410, 454, 408, 454, 434, 460, 404, 458, 406, 454, 436, 456, 408, 458, 406, + 458, 432, 456, 434, 430, 1246, 484, 404, 484, 1218, 458, 1244, 484, 1244, + 460, 1244, 456, 1270, 430, 1274, 460, 404, 484, 1218, 486, 1244, 456, + 1244, 456, 1248, 454, 1272, 460, 1268, 432, 1246, 484, 1244, 458, 1244, + 462, 404, 456, 434, 458, 432, 432, 408, 456, 434, 460, 404, 460, 404, 484, + 406, 432, 432, 458, 1298, 402, 432, 460, 404, 460, 404, 482, 1244, 460, + 1270, 462, 1214, 456, 1272, 460, 404, 456, 1246, 486, 1244, 458, 1244, + 458, 406, 454, 460, 434, 404, 460, 1268, 430, 460, 432, 406, 458, 1244, + 456, 434, 458, 406, 456, 432, 460, 1268, 432, 406, 458, 1244, 484, 1272, + 432, 430, 432, 1298, 378, 1298, 458, 1244, 484, 406, 460, 1242, 458, 406, + 458, 1246, 456, 1272, 458, 406, 458, 432, 458, 404, 460, 404, 456, 408, + 484, 1272, 432, 432, 432, 406, 454, 1272, 458, 1246, 458, 1244, 484, 1268, + 436, 1242, 458, 1298, 402, 1274, 456, 1244, 458, 1244, 486, 1242, 458, + 408, 486, 378, 454, 460, 434, 406, 458, 406, 484, 406, 458, 406, 458, 408, + 452, 1302, 434, 1244, 430}; + + irsend.reset(); + irsend.sendRaw(rawData, 243, kHitachiAcFreq); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC3, irsend.capture.decode_type); + ASSERT_EQ(expectedBits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, expectedBits); +} + +TEST(TestDecodeHitachiAc3, SyntheticCancelTimerExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected[kHitachiAc3MinStateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE2, 0x1D, 0x89, 0x76, 0x0D, + 0xF2, 0x3F, 0xC0}; + + irsend.reset(); + irsend.sendHitachiAc3(expected, kHitachiAc3MinStateLength); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(HITACHI_AC3, irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc3MinBits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); +} + +// Decode a 'real' Set Timer HitachiAc3 message. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-598631576 +TEST(TestDecodeHitachiAc3, RealSetTimerExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected[kHitachiAc3StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE8, 0x17, 0x89, 0x76, 0x0B, + 0xF4, 0x3F, 0xC0, 0x15, 0xEA, 0x00, 0xFF, 0x00, 0xFF, 0x4B, 0xB4, 0x18, + 0xE7, 0x00, 0xFF}; + const uint16_t expectedBits = kHitachiAc3Bits; + + const uint16_t rawData[435] = { + // Set Timer + 3364, 1668, 486, 1244, 458, 406, 456, 436, 430, 434, 430, 434, 456, 408, + 484, 432, 428, 432, 414, 450, 458, 432, 432, 432, 430, 436, 458, 1270, + 430, 408, 456, 408, 454, 462, 404, 460, 428, 408, 486, 404, 456, 408, 458, + 406, 484, 406, 456, 434, 432, 408, 484, 406, 454, 436, 404, 432, 486, 430, + 430, 408, 456, 432, 458, 1270, 406, 434, 456, 1246, 484, 1244, 456, 1274, + 432, 1244, 486, 1268, 432, 1244, 454, 412, 454, 1272, 430, 1272, 456, + 1274, 456, 1246, 456, 1244, 458, 1270, 458, 1246, 456, 1254, 450, 1246, + 484, 408, 456, 434, 430, 408, 486, 430, 404, 460, 430, 408, 486, 404, 458, + 406, 458, 406, 486, 404, 458, 432, 430, 1272, 456, 408, 458, 1244, 458, + 1244, 456, 1274, 456, 1246, 456, 1270, 460, 1250, 426, 460, 404, 1270, + 484, 432, 404, 462, 402, 460, 458, 1244, 456, 386, 452, 460, 458, 1244, + 458, 406, 458, 406, 486, 430, 416, 1260, 458, 406, 484, 1270, 406, 1296, + 430, 410, 482, 1246, 456, 1246, 456, 1246, 456, 434, 458, 1244, 456, 1272, + 458, 408, 458, 1244, 456, 410, 484, 404, 454, 410, 456, 408, 484, 406, + 458, 432, 430, 1246, 484, 406, 454, 1248, 458, 1244, 486, 1248, 428, 1270, + 456, 1266, 464, 1244, 458, 1246, 456, 1246, 484, 1270, 430, 1246, 456, + 434, 430, 460, 406, 432, 430, 432, 484, 432, 406, 432, 458, 406, 484, 432, + 432, 1266, 434, 1274, 430, 1298, 428, 408, 456, 1246, 486, 430, 432, 1270, + 404, 434, 484, 432, 430, 408, 456, 406, 460, 1268, 458, 408, 456, 1246, + 486, 406, 456, 1244, 456, 1248, 456, 1300, 430, 408, 456, 408, 484, 432, + 430, 434, 428, 434, 460, 430, 404, 434, 458, 406, 486, 1242, 458, 1270, + 430, 1246, 484, 1244, 456, 1270, 432, 1248, 484, 1244, 456, 1246, 456, + 408, 486, 404, 456, 408, 456, 432, 462, 430, 406, 458, 432, 432, 428, + 436, 456, 1246, 456, 1246, 484, 1244, 454, 1268, 438, 1244, 488, 1266, + 430, 1246, 458, 1244, 486, 1244, 430, 1298, 430, 434, 460, 1242, 458, 430, + 430, 408, 486, 1242, 456, 434, 404, 462, 456, 430, 432, 1246, 456, 408, + 486, 1270, 430, 1246, 460, 404, 482, 1246, 458, 406, 456, 408, 484, 404, + 458, 1270, 430, 1274, 458, 432, 430, 408, 456, 406, 486, 1244, 458, 1242, + 458, 1246, 484, 406, 458, 432, 430, 1246, 486, 1242, 456, 1272, 430, 434, + 458, 406, 458, 406, 458, 406, 486, 404, 458, 406, 458, 430, 430, 460, 430, + 1270, 430, 1248, 484, 1244, 456, 1244, 458, 1246, 484, 1244, 456, 1246, + 460, 1244, 458}; // UNKNOWN D3A5A0BA + + irsend.reset(); + irsend.sendRaw(rawData, 435, kHitachiAcFreq); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC3, irsend.capture.decode_type); + ASSERT_EQ(expectedBits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, expectedBits); +} + +TEST(TestDecodeHitachiAc3, SyntheticSetTimerExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected[kHitachiAc3StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE8, 0x17, 0x89, 0x76, 0x0B, + 0xF4, 0x3F, 0xC0, 0x15, 0xEA, 0x00, 0xFF, 0x00, 0xFF, 0x4B, 0xB4, 0x18, + 0xE7, 0x00, 0xFF}; + + irsend.reset(); + irsend.sendHitachiAc3(expected, kHitachiAc3StateLength); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(HITACHI_AC3, irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc3Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); +} + +// Decode a 'real' Change Mode HitachiAc3 message. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-598631576 +TEST(TestDecodeHitachiAc3, RealChangeModeExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected[kHitachiAc3StateLength - 6] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE5, 0x1A, 0x89, 0x76, 0x04, + 0xFB, 0x3F, 0xC0, 0x1B, 0xE4, 0x14, 0xEB, 0x02, 0xFD}; + const uint16_t expectedBits = kHitachiAc3Bits - 6 * 8; + + const uint16_t rawData[339] = { + // Change Mode + 3364, 1666, 488, 1268, 404, 434, 454, 434, 460, 404, 458, 406, 458, 406, + 484, 432, 432, 430, 432, 408, 484, 406, 458, 432, 432, 406, 486, 1242, + 458, 430, 430, 408, 454, 436, 456, 408, 458, 406, 484, 432, 428, 434, 430, + 408, 484, 406, 458, 430, 432, 408, 484, 430, 430, 432, 432, 408, 482, 408, + 456, 408, 458, 432, 458, 1246, 458, 430, 430, 1246, 486, 1242, 458, 1270, + 432, 1246, 486, 1242, 458, 1268, 432, 434, 430, 1272, 456, 1246, 458, + 1270, 460, 1244, 458, 1246, 456, 1272, 460, 1242, 456, 1270, 434, 1246, + 486, 430, 430, 434, 432, 406, 484, 430, 432, 432, 430, 408, 484, 432, 432, + 432, 430, 1246, 484, 406, 458, 1246, 456, 408, 486, 404, 458, 1268, 434, + 1270, 430, 1274, 456, 408, 458, 1270, 460, 404, 458, 1244, 458, 1246, 486, + 430, 430, 432, 460, 378, 488, 1240, 460, 404, 458, 408, 486, 1242, 458, + 432, 430, 434, 460, 430, 432, 1246, 458, 406, 488, 1240, 430, 1272, 458, + 406, 486, 1242, 430, 1298, 430, 1274, 428, 432, 430, 436, 456, 408, 482, + 1248, 458, 406, 430, 462, 456, 404, 458, 432, 430, 408, 486, 1266, 436, + 1242, 458, 406, 486, 1242, 456, 1246, 458, 1244, 488, 1240, 458, 1244, + 458, 1272, 460, 1242, 456, 1246, 458, 1246, 486, 1242, 458, 1270, 432, + 406, 458, 458, 434, 430, 432, 406, 486, 406, 456, 408, 458, 432, 484, 406, + 430, 1272, 460, 1216, 486, 1242, 456, 1246, 458, 406, 486, 1268, 432, + 1244, 458, 406, 486, 404, 460, 432, 430, 406, 488, 402, 458, 1272, 428, + 434, 460, 404, 460, 1242, 458, 1246, 480, 1244, 462, 428, 432, 432, 460, + 1244, 458, 432, 430, 1246, 488, 402, 456, 408, 458, 406, 486, 1268, 432, + 1246, 460, 430, 460, 1244, 458, 406, 458, 1244, 486, 1242, 458, 1244, 458, + 432, 462, 1242, 456, 408, 456, 406, 486, 428, 434, 406, 458, 406, 456, + 434, 458, 1244, 460, 430, 462, 1240, 458, 1244, 460, 1244, 486, 1244, 458, + 1242, 488, 1214, 460}; // UNKNOWN C1EA1036 + + irsend.reset(); + irsend.sendRaw(rawData, 339, kHitachiAcFreq); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC3, irsend.capture.decode_type); + ASSERT_EQ(expectedBits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, expectedBits); +} + +TEST(TestDecodeHitachiAc3, SyntheticChangeModeExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected[kHitachiAc3StateLength - 6] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE5, 0x1A, 0x89, 0x76, 0x04, + 0xFB, 0x3F, 0xC0, 0x1B, 0xE4, 0x14, 0xEB, 0x02, 0xFD}; + const uint16_t expectedBits = kHitachiAc3Bits - 6 * 8; + + irsend.reset(); + irsend.sendHitachiAc3(expected, kHitachiAc3StateLength - 6); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(HITACHI_AC3, irsend.capture.decode_type); + ASSERT_EQ(expectedBits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestHitachiAc3Class, hasInvertedStates) { + const uint8_t good_state[kHitachiAc3StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE8, 0x17, 0x89, 0x76, 0x0B, + 0xF4, 0x3F, 0xC0, 0x15, 0xEA, 0x00, 0xFF, 0x00, 0xFF, 0x4B, 0xB4, 0x18, + 0xE7, 0x00, 0xFF}; + // bad_state[kHitachiAc3MinStateLength + 1] has been modified to be different. + // i.e. Anything larger than kHitachiAc3MinStateLength should fail. + // kHitachiAc3MinStateLength or shorter should pass. + const uint8_t bad_state[kHitachiAc3StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE8, 0x17, 0x89, 0x76, 0x0B, + 0xF4, 0x3F, 0xC0, 0x15, 0xE0, 0x00, 0xFF, 0x00, 0xFF, 0x4B, 0xB4, 0x18, + 0xE7, 0x00, 0xFF}; + + EXPECT_TRUE(IRHitachiAc3::hasInvertedStates(good_state, + kHitachiAc3StateLength)); + + for (uint8_t len = kHitachiAc3StateLength; + len > kHitachiAc3MinStateLength; + len -= 2) { + EXPECT_FALSE(IRHitachiAc3::hasInvertedStates(bad_state, len)); + } + EXPECT_TRUE(IRHitachiAc3::hasInvertedStates(bad_state, + kHitachiAc3MinStateLength)); + EXPECT_TRUE(IRHitachiAc3::hasInvertedStates(bad_state, + kHitachiAc3MinStateLength - 2)); +} + +// HitachiAc1 Class tests + +TEST(TestIRHitachiAc1Class, SetAndGetPower) { + IRHitachiAc1 ac(kGpioUnused); + ac.on(); + ac.setPowerToggle(false); + EXPECT_TRUE(ac.getPower()); + EXPECT_FALSE(ac.getPowerToggle()); + ac.off(); + EXPECT_FALSE(ac.getPower()); + EXPECT_TRUE(ac.getPowerToggle()); + ac.setPowerToggle(false); + EXPECT_FALSE(ac.getPowerToggle()); + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + EXPECT_TRUE(ac.getPowerToggle()); + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); + EXPECT_TRUE(ac.getPowerToggle()); +} + +TEST(TestIRHitachiAc1Class, SetAndGetTemp) { + IRHitachiAc1 ac(kGpioUnused); + ac.setMode(kHitachiAc1Cool); // All temps possible in Cool mode. + ac.setTemp(26); + EXPECT_EQ(26, ac.getTemp()); + ac.setTemp(kHitachiAcMinTemp); + EXPECT_EQ(kHitachiAcMinTemp, ac.getTemp()); + ac.setTemp(kHitachiAcMinTemp - 1); + EXPECT_EQ(kHitachiAcMinTemp, ac.getTemp()); + ac.setTemp(kHitachiAcMaxTemp); + EXPECT_EQ(kHitachiAcMaxTemp, ac.getTemp()); + ac.setTemp(kHitachiAcMaxTemp + 1); + EXPECT_EQ(kHitachiAcMaxTemp, ac.getTemp()); + + // Can't change temp in Auto mode. + ac.setMode(kHitachiAc1Auto); // All temps possible in Cool mode. + EXPECT_EQ(kHitachiAc1TempAuto, ac.getTemp()); + ac.setTemp(kHitachiAcMinTemp); + EXPECT_EQ(kHitachiAc1TempAuto, ac.getTemp()); + + // Ref: https://docs.google.com/spreadsheets/d/10eKpJEWJppUYktPRLCcAIwzfFXjtkOZNyn1reh5MFfU/edit#gid=0&range=B46 + const uint8_t cool_31_auto[kHitachiAc1StateLength] = { + 0xB2, 0xAE, 0x4D, 0x91, 0xF0, 0x61, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x68}; + ac.setRaw(cool_31_auto); + EXPECT_EQ(31, ac.getTemp()); +} + +TEST(TestIRHitachiAc1Class, SetAndGetMode) { + IRHitachiAc1 ac(kGpioUnused); + ac.setMode(kHitachiAc1Auto); + EXPECT_EQ(kHitachiAc1Auto, ac.getMode()); + ac.setMode(kHitachiAc1Cool); + EXPECT_EQ(kHitachiAc1Cool, ac.getMode()); + ac.setMode(kHitachiAc1Fan); + EXPECT_EQ(kHitachiAc1Fan, ac.getMode()); + ac.setMode(kHitachiAc1Heat); + EXPECT_EQ(kHitachiAc1Heat, ac.getMode()); + ac.setMode(kHitachiAc1Dry); + EXPECT_EQ(kHitachiAc1Dry, ac.getMode()); + ac.setMode(0); + EXPECT_EQ(kHitachiAc1Auto, ac.getMode()); + ac.setMode(255); + EXPECT_EQ(kHitachiAc1Auto, ac.getMode()); +} + +TEST(TestIRHitachiAc1Class, SetAndGetFan) { + IRHitachiAc1 ac(kGpioUnused); + ac.setMode(kHitachiAc1Cool); // All speeds possible in Cool mode. + ac.setFan(kHitachiAc1FanAuto); + EXPECT_EQ(kHitachiAc1FanAuto, ac.getFan()); + ac.setFan(kHitachiAc1FanLow); + EXPECT_EQ(kHitachiAc1FanLow, ac.getFan()); + ac.setFan(kHitachiAc1FanHigh); + EXPECT_EQ(kHitachiAc1FanHigh, ac.getFan()); + ac.setFan(kHitachiAc1FanMed); + EXPECT_EQ(kHitachiAc1FanMed, ac.getFan()); + + ac.setFan(255); + EXPECT_EQ(kHitachiAc1FanAuto, ac.getFan()); + ac.setFan(0); + EXPECT_EQ(kHitachiAc1FanAuto, ac.getFan()); + + // Can't change speed in Auto mode. Locked to auto speed. + ac.setMode(kHitachiAc1Auto); + EXPECT_EQ(kHitachiAc1FanAuto, ac.getFan()); + ac.setFan(kHitachiAc1FanLow); + EXPECT_EQ(kHitachiAc1FanAuto, ac.getFan()); + // Can't change speed in Dry mode. Locked to low speed. + ac.setMode(kHitachiAc1Dry); + EXPECT_EQ(kHitachiAc1FanLow, ac.getFan()); + ac.setFan(kHitachiAc1FanHigh); + EXPECT_EQ(kHitachiAc1FanLow, ac.getFan()); +} + +TEST(TestIRHitachiAc1Class, HumanReadable) { + IRHitachiAc1 ac(kGpioUnused); + + // Ref: https://docs.google.com/spreadsheets/d/10eKpJEWJppUYktPRLCcAIwzfFXjtkOZNyn1reh5MFfU/edit#gid=0&range=A47:B47 + const uint8_t cool_32_auto[kHitachiAc1StateLength] = { + 0xB2, 0xAE, 0x4D, 0x91, 0xF0, 0x61, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x04}; + ac.setRaw(cool_32_auto); + EXPECT_EQ( + "Model: 1 (R-LT0541-HTA-A), Power: On, Power Toggle: On, Mode: 6 (Cool), " + "Temp: 32C, Fan: 1 (Auto), Swing(V) Toggle: Off, " + "Swing(V): Off, Swing(H): Off, " + "Sleep: Off, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.setModel(hitachi_ac1_remote_model_t::R_LT0541_HTA_B); + ac.setSwingV(true); + ac.setSwingToggle(true); + ac.setSleep(kHitachiAc1Sleep2); + ac.setPowerToggle(false); + ac.setOnTimer(2 * 60 + 39); + ac.setOffTimer(10 * 60 + 17); + EXPECT_EQ( + "Model: 2 (R-LT0541-HTA-B), Power: On, Power Toggle: Off, " + "Mode: 6 (Cool), Temp: 32C, Fan: 1 (Auto), " + "Swing(V) Toggle: On, Swing(V): On, Swing(H): Off, Sleep: 2, " + "On Timer: 02:39, Off Timer: 10:17", + ac.toString()); +} + +TEST(TestIRHitachiAc1Class, Checksum) { + // Reverse Table: 2=4, B=D, A=5, E=7, 1=8, 3=C, 0=0, 6=6, 9=9, F=F + IRHitachiAc1 ac(kGpioUnused); + // Ref: https://docs.google.com/spreadsheets/d/10eKpJEWJppUYktPRLCcAIwzfFXjtkOZNyn1reh5MFfU/edit#gid=0&range=A47:B47 + const uint8_t cool_32_auto[kHitachiAc1StateLength] = { + 0xB2, 0xAE, 0x4D, 0x91, 0xF0, 0x61, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x04}; + // Reversed: 4D, 75, B2, 89, 0F, 86, 33, 00, 00, 00, 00, 0C, 20 + EXPECT_TRUE(ac.validChecksum(cool_32_auto)); + EXPECT_EQ(0x04, ac.calcChecksum(cool_32_auto)); + + // Ref: https://docs.google.com/spreadsheets/d/10eKpJEWJppUYktPRLCcAIwzfFXjtkOZNyn1reh5MFfU/edit#gid=0&range=B46 + const uint8_t cool_31_auto[kHitachiAc1StateLength] = { + 0xB2, 0xAE, 0x4D, 0x91, 0xF0, 0x61, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x68}; + // Reversed: 4D, 75, B2, 89, 0F, 86, 31, 00, 00, 00, 00, 04, 16 + EXPECT_TRUE(ac.validChecksum(cool_31_auto)); + EXPECT_EQ(0x68, ac.calcChecksum(cool_31_auto)); + + // Ref: https://docs.google.com/spreadsheets/d/10eKpJEWJppUYktPRLCcAIwzfFXjtkOZNyn1reh5MFfU/edit#gid=0&range=B13 + const uint8_t auto_25_auto_swing_on[kHitachiAc1StateLength] = { + 0xB2, 0xAE, 0x4D, 0x91, 0xF0, 0xE1, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x61, + 0x24}; + // Reversed: 4D, 75, B2, 89, 0F, 87, 25, 00, 00, 00, 00, 86, 42 + EXPECT_TRUE(ac.validChecksum(auto_25_auto_swing_on)); + EXPECT_EQ(0x24, ac.calcChecksum(auto_25_auto_swing_on)); + // Ref: https://docs.google.com/spreadsheets/d/10eKpJEWJppUYktPRLCcAIwzfFXjtkOZNyn1reh5MFfU/edit#gid=0&range=B45 + const uint8_t cool_30_auto[kHitachiAc1StateLength] = { + 0xB2, 0xAE, 0x4D, 0x91, 0xF0, 0x61, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x20, + 0xC4}; + EXPECT_TRUE(ac.validChecksum(cool_30_auto)); + EXPECT_EQ(0xC4, ac.calcChecksum(cool_30_auto)); +} + +TEST(TestIRHitachiAc1Class, toCommon) { + IRHitachiAc1 ac(kGpioUnused); + ac.setPower(true); + ac.setMode(kHitachiAc1Cool); + ac.setTemp(20); + ac.setFan(kHitachiAc1FanHigh); + ac.setSwingV(false); + ac.setSwingH(true); + ac.setModel(hitachi_ac1_remote_model_t::R_LT0541_HTA_B); + // Now test it. + ASSERT_EQ(decode_type_t::HITACHI_AC1, ac.toCommon().protocol); + ASSERT_EQ(2, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestIRHitachiAc1Class, ReconstructKnownGood) { + IRHitachiAc1 ac(kGpioUnused); + const uint8_t known_good[kHitachiAc1StateLength] = { + 0xB2, 0xAE, 0x4D, 0x51, 0xF0, 0x61, 0x84, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x98}; + ac.stateReset(); + ac.setPower(false); + ac.setPowerToggle(true); + ac.setMode(kHitachiAc1Cool); + ac.setTemp(23); + ac.setFan(kHitachiAc1FanAuto); + ac.setSwingV(false); + ac.setSwingH(false); + ac.setSwingToggle(false); + ac.setModel(hitachi_ac1_remote_model_t::R_LT0541_HTA_B); + + EXPECT_STATE_EQ(known_good, ac.getRaw(), kHitachiAc1Bits); + EXPECT_EQ( + "Model: 2 (R-LT0541-HTA-B), Power: Off, Power Toggle: On, " + "Mode: 6 (Cool), Temp: 23C, Fan: 1 (Auto), " + "Swing(V) Toggle: Off, Swing(V): Off, Swing(H): Off, Sleep: Off, " + "On Timer: Off, Off Timer: Off", + ac.toString()); +} + +TEST(TestIRHitachiAc1Class, Swing) { + IRHitachiAc1 ac(kGpioUnused); + ac.setSwingV(false); + EXPECT_FALSE(ac.getSwingV()); + ac.setSwingToggle(false); + EXPECT_FALSE(ac.getSwingToggle()); + + ac.setSwingV(true); + EXPECT_TRUE(ac.getSwingV()); + EXPECT_FALSE(ac.getSwingToggle()); + ac.setSwingToggle(true); + EXPECT_TRUE(ac.getSwingV()); + EXPECT_TRUE(ac.getSwingToggle()); + + ac.setSwingV(false); + EXPECT_FALSE(ac.getSwingV()); + ac.setSwingToggle(false); + EXPECT_FALSE(ac.getSwingToggle()); + + const uint8_t swing_on_with_toggle[kHitachiAc1StateLength] = { + 0xB2, 0xAE, 0x4D, 0x91, 0xF0, 0xE1, 0xA4, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x24}; + ac.setRaw(swing_on_with_toggle); + EXPECT_TRUE(ac.getSwingV()); + EXPECT_FALSE(ac.getSwingH()); + EXPECT_TRUE(ac.getSwingToggle()); + const uint8_t swing_off_with_toggle[kHitachiAc1StateLength] = { + 0xB2, 0xAE, 0x4D, 0x91, 0xF0, 0xE1, 0xA4, + 0x00, 0x00, 0x00, 0x00, 0x21, 0x44}; + ac.setRaw(swing_off_with_toggle); + EXPECT_FALSE(ac.getSwingV()); + EXPECT_FALSE(ac.getSwingH()); + EXPECT_TRUE(ac.getSwingToggle()); + ac.setSwingToggle(true); + EXPECT_STATE_EQ(swing_off_with_toggle, ac.getRaw(), kHitachiAc1Bits); +} + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1056#issuecomment-609374682 +TEST(TestIRHitachiAc1Class, FanSpeedInDryMode) { + IRHitachiAc1 ac(kGpioUnused); + // IRhvac {"Vendor":"HITACHI_AC1", "Power":"On","Mode":"dry","FanSpeed":"low" + // "Temp":22.5} + const uint8_t state[kHitachiAc1StateLength] = { // Code generated by Tasmota. + 0xB2, 0xAE, 0x4D, 0x91, 0xF0, 0x21, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x31, 0x0C}; + ac.setRaw(state); + EXPECT_EQ( + "Model: 1 (R-LT0541-HTA-A), Power: On, Power Toggle: On, Mode: 2 (Dry), " + "Temp: 22C, Fan: 1 (Auto), " + "Swing(V) Toggle: On, Swing(V): Off, Swing(H): Off, Sleep: Off, " + "On Timer: Off, Off Timer: Off", + ac.toString()); + ac.stateReset(); + // Build it via the build order in IRac::hitachi1() + ac.setModel(hitachi_ac1_remote_model_t::R_LT0541_HTA_A); + ac.setPower(true); + ac.setPowerToggle(true); + ac.setMode(kHitachiAc1Dry); + ac.setTemp(22.5); + ac.setFan(kHitachiAc1FanLow); + ac.setSwingV(false); + ac.setSwingH(false); + ac.setSwingToggle(true); + ac.setSleep(0); + EXPECT_EQ( + "Model: 1 (R-LT0541-HTA-A), Power: On, Power Toggle: On, Mode: 2 (Dry), " + "Temp: 22C, Fan: 8 (Low), " + "Swing(V) Toggle: On, Swing(V): Off, Swing(H): Off, Sleep: Off, " + "On Timer: Off, Off Timer: Off", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Inax_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Inax_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Inax_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Inax_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_JVC_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_JVC_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_JVC_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_JVC_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Kelvinator_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.7.4/test/ir_Kelvinator_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Kelvinator_test.cpp index e01a585bd..73ad6581d 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Kelvinator_test.cpp @@ -1,6 +1,7 @@ // Copyright 2017 David Conran #include "ir_Kelvinator.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRremoteESP8266.h" @@ -519,6 +520,13 @@ TEST(TestDecodeKelvinator, NormalSynthetic) { EXPECT_EQ(KELVINATOR, irsend.capture.decode_type); ASSERT_EQ(kKelvinatorBits, irsend.capture.bits); EXPECT_STATE_EQ(kelv_code, irsend.capture.state, kKelvinatorBits); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 1 (Low), Turbo: Off, " + "Quiet: Off, XFan: On, Ion: Off, Light: Off, " + "Swing(H): Off, Swing(V): Off", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } TEST(TestKelvinatorClass, toCommon) { diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_LG_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_LG_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_LG_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_LG_test.cpp index 703ea3c6c..3c04c902f 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_LG_test.cpp @@ -843,6 +843,8 @@ TEST(TestDecodeLG2, Issue1008) { ASSERT_EQ(LG2, ac._irsend.capture.decode_type); ASSERT_EQ(kLgBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRLgAcClass, DifferentModels) { @@ -864,6 +866,8 @@ TEST(TestIRLgAcClass, DifferentModels) { ASSERT_EQ(LG, ac._irsend.capture.decode_type); // Not "LG2" ASSERT_EQ(kLgBits, ac._irsend.capture.bits); ASSERT_EQ(expected1, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); ac.setModel(lg_ac_remote_model_t::AKB75215403); // aka. 2 @@ -879,4 +883,5 @@ TEST(TestIRLgAcClass, DifferentModels) { ASSERT_EQ(LG2, ac._irsend.capture.decode_type); // Not "LG" ASSERT_EQ(kLgBits, ac._irsend.capture.bits); ASSERT_EQ(expected2, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Lasertag_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Lasertag_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Lasertag_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Lego_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Lego_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Lego_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Lego_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Lutron_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Lutron_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Lutron_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_MWM_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_MWM_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_MWM_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_MWM_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Magiquest_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Magiquest_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Magiquest_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Midea_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Midea_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Midea_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Midea_test.cpp index a43076fea..4c437a576 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Midea_test.cpp @@ -1,6 +1,7 @@ // Copyright 2017 David Conran #include "ir_Midea.h" +#include "IRac.h" #include "IRsend.h" #include "IRsend_test.h" #include "gtest/gtest.h" @@ -669,6 +670,12 @@ TEST(TestDecodeMidea, DecodeRealExample) { EXPECT_EQ(MIDEA, irsend.capture.decode_type); EXPECT_EQ(kMideaBits, irsend.capture.bits); EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 18C/65F, Fan: 0 (Auto), " + "Sleep: Off, Swing(V) Toggle: Off", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } TEST(TestMideaACClass, toCommon) { diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_MitsubishiHeavy_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.7.4/test/ir_MitsubishiHeavy_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_MitsubishiHeavy_test.cpp index a8226feb8..d182fd941 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_MitsubishiHeavy_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_MitsubishiHeavy_test.cpp @@ -1,6 +1,7 @@ // Copyright 2019 David Conran #include "ir_MitsubishiHeavy.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRremoteESP8266.h" @@ -742,7 +743,9 @@ TEST(TestDecodeMitsubishiHeavy, ZmsRealExample) { "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " "Swing(V): 0 (Auto), Swing(H): 0 (Auto), Silent: Off, Turbo: Off, " "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", - ac.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } // Decode a Synthetic MitsubishiHeavy 152Bit message. @@ -847,7 +850,9 @@ TEST(TestDecodeMitsubishiHeavy, ZjsSyntheticExample) { "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " "Swing(V): 0 (Off), Swing(H): 6 (Left Right), Turbo: Off, Econo: Off, " "3D: Off, Clean: Off", - ac.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } TEST(TestMitsubishiHeavy152AcClass, toCommon) { diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Mitsubishi_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Mitsubishi_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Mitsubishi_test.cpp index 4f759c046..8e1c1bf51 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Mitsubishi_test.cpp @@ -1230,6 +1230,8 @@ TEST(TestDecodeMitsubishi136, DecodeRealExample) { "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 3 (High), " "Swing(V): 3 (Highest), Quiet: Off", IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } // Self decode a synthetic example. @@ -1719,6 +1721,8 @@ TEST(TestDecodeMitsubishi112, DecodeRealExample) { "Power: On, Mode: 3 (Cool), Temp: 23C, Fan: 2 (Quiet), " "Swing(V): 7 (Auto), Swing(H): 12 (Auto), Quiet: On", IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } // Self decode a synthetic example. diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_NEC_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_NEC_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_NEC_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_NEC_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Neoclima_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Neoclima_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.7.4/test/ir_Neoclima_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Neoclima_test.cpp index c743e7ba5..b395ee8dd 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Neoclima_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Neoclima_test.cpp @@ -2,6 +2,7 @@ #include "ir_Neoclima.h" #include +#include "IRac.h" #include "IRsend.h" #include "IRsend_test.h" #include "IRrecv.h" @@ -12,6 +13,7 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ("NEOCLIMA", typeToString(decode_type_t::NEOCLIMA)); ASSERT_EQ(decode_type_t::NEOCLIMA, strToDecodeType("NEOCLIMA")); ASSERT_TRUE(hasACState(decode_type_t::NEOCLIMA)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::NEOCLIMA)); } // Test sending typical data only. @@ -83,7 +85,9 @@ TEST(TestDecodeNeoclima, RealExample) { "Swing(V): Off, Swing(H): On, Sleep: Off, Turbo: Off, Hold: Off, " "Ion: Off, Eye: Off, Light: Off, Follow: Off, 8C Heat: Off, Fresh: Off, " "Button: 0 (Power)", - ac.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } // Self decode. diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Nikai_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Nikai_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Nikai_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Panasonic_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Panasonic_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Panasonic_test.cpp index 7f022ed17..fbce6de4e 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Panasonic_test.cpp @@ -1,6 +1,7 @@ // Copyright 2017, 2018 David Conran #include "ir_Panasonic.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -889,14 +890,13 @@ TEST(TestDecodePanasonicAC, SyntheticExample) { EXPECT_EQ(kPanasonicAcBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); EXPECT_FALSE(irsend.capture.repeat); - - IRPanasonicAc pana(0); - pana.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 4 (JKE), Power: Off, Mode: 3 (Cool), Temp: 25C, " "Fan: 7 (Auto), Swing(V): 15 (Auto), Quiet: Off, " "Powerful: Off, Clock: 00:00, On Timer: Off, Off Timer: Off", - pana.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } // Tests for general utility functions. diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Pioneer_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Pioneer_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Pioneer_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Pronto_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Pronto_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Pronto_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_RC5_RC6_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_RC5_RC6_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_RC5_RC6_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_RCMM_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_RCMM_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_RCMM_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Samsung_test.cpp similarity index 94% rename from lib/IRremoteESP8266-2.7.4/test/ir_Samsung_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Samsung_test.cpp index c44572934..52165b780 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Samsung_test.cpp @@ -1,7 +1,8 @@ -// Copyright 2017, 2018, 2019 David Conran +// Copyright 2017-2020 David Conran #include #include "ir_Samsung.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -571,6 +572,13 @@ TEST(TestIRSamsungAcClass, SetAndGetPowerful) { EXPECT_FALSE(ac.getPowerful()); EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + // Breeze and Powerful/Turbo are mutually exclusive. + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + ac.setBreeze(true); + EXPECT_TRUE(ac.getBreeze()); + EXPECT_FALSE(ac.getPowerful()); + // Actual powerful on & off states from: // https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-500120270 uint8_t on[kSamsungAcStateLength] = { @@ -581,7 +589,8 @@ TEST(TestIRSamsungAcClass, SetAndGetPowerful) { EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 7 (Turbo), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: On, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: On, Breeze: Off, " + "Light: On, Ion: Off", ac.toString()); uint8_t off[kSamsungAcStateLength] = { @@ -592,7 +601,8 @@ TEST(TestIRSamsungAcClass, SetAndGetPowerful) { EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", ac.toString()); } @@ -658,7 +668,8 @@ TEST(TestIRSamsungAcClass, HumanReadable) { IRSamsungAc samsung(0); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 2 (Low), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", samsung.toString()); samsung.setTemp(kSamsungAcMaxTemp); samsung.setMode(kSamsungAcHeat); @@ -669,24 +680,28 @@ TEST(TestIRSamsungAcClass, HumanReadable) { samsung.setClean(true); EXPECT_EQ( "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 5 (High), Swing: Off, " - "Beep: On, Clean: On, Quiet: Off, Powerful: Off, Light: On, Ion: Off", + "Beep: On, Clean: On, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", samsung.toString()); samsung.setQuiet(true); EXPECT_EQ( "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 0 (Auto), Swing: Off, " - "Beep: On, Clean: On, Quiet: On, Powerful: Off, Light: On, Ion: Off", + "Beep: On, Clean: On, Quiet: On, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", samsung.toString()); samsung.setQuiet(false); samsung.setPowerful(true); EXPECT_EQ( "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 7 (Turbo), Swing: Off, " - "Beep: On, Clean: On, Quiet: Off, Powerful: On, Light: On, Ion: Off", + "Beep: On, Clean: On, Quiet: Off, Powerful: On, Breeze: Off, " + "Light: On, Ion: Off", samsung.toString()); samsung.setIon(true); samsung.setDisplay(false); EXPECT_EQ( "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 7 (Turbo), Swing: Off, " - "Beep: On, Clean: On, Quiet: Off, Powerful: On, Light: Off, Ion: On", + "Beep: On, Clean: On, Quiet: Off, Powerful: On, Breeze: Off, " + "Light: Off, Ion: On", samsung.toString()); } @@ -790,7 +805,8 @@ TEST(TestDecodeSamsungAC, DecodeRealExample) { samsung.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 2 (Low), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", samsung.toString()); } @@ -839,7 +855,8 @@ TEST(TestDecodeSamsungAC, DecodeRealExample2) { samsung.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", samsung.toString()); } @@ -898,7 +915,8 @@ TEST(TestDecodeSamsungAC, DecodePowerOnSample) { samsung.setRaw(irsend.capture.state, kSamsungAcExtendedStateLength); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", samsung.toString()); } @@ -958,7 +976,8 @@ TEST(TestDecodeSamsungAC, DecodePowerOffSample) { samsung.setRaw(irsend.capture.state, kSamsungAcExtendedStateLength); EXPECT_EQ( "Power: Off, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", samsung.toString()); } @@ -1005,7 +1024,8 @@ TEST(TestDecodeSamsungAC, DecodeHeatSample) { samsung.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 4 (Heat), Temp: 17C, Fan: 0 (Auto), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", samsung.toString()); } @@ -1047,13 +1067,13 @@ TEST(TestDecodeSamsungAC, DecodeCoolSample) { ASSERT_EQ(SAMSUNG_AC, irsend.capture.decode_type); EXPECT_EQ(kSamsungAcBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); - - IRSamsungAc samsung(0); - samsung.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off", - samsung.toString()); + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } TEST(TestDecodeSamsungAC, Issue604DecodeExtended) { @@ -1110,7 +1130,8 @@ TEST(TestDecodeSamsungAC, Issue604DecodeExtended) { samsung.setRaw(irsend.capture.state, irsend.capture.bits / 8); EXPECT_EQ( "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", samsung.toString()); } @@ -1307,7 +1328,7 @@ TEST(TestIRSamsungAcClass, Issue604SendPowerHack) { "m586s100000"; std::string text = "Power: On, Mode: 1 (Cool), Temp: 23C, Fan: 4 (Med), " "Swing: On, Beep: Off, Clean: Off, Quiet: Off, " - "Powerful: Off, Light: On, Ion: Off"; + "Powerful: Off, Breeze: Off, Light: On, Ion: Off"; // Don't do a setPower()/on()/off() as that will trigger the special message. // So it should only be the normal "settings" message. ac.setTemp(23); @@ -1434,7 +1455,8 @@ TEST(TestDecodeSamsungAC, Issue734QuietSetting) { ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: On, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: On, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", ac.toString()); // Make sure the ac class state is in something wildly different first. @@ -1456,7 +1478,8 @@ TEST(TestDecodeSamsungAC, Issue734QuietSetting) { ac.setQuiet(true); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: On, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: On, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", ac.toString()); // Check it matches the known good/expected state. EXPECT_STATE_EQ(expectedState, ac.getRaw(), kSamsungAcBits); @@ -1506,6 +1529,52 @@ TEST(TestDecodeSamsungAC, Issue734PowerfulOff) { ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Light: On, Ion: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", + ac.toString()); +} + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 +TEST(TestIRSamsungAcClass, SetAndGetBreeze) { + IRSamsungAc ac(kGpioUnused); + ac.setFan(kSamsungAcFanMed); + ac.setBreeze(false); + EXPECT_FALSE(ac.getBreeze()); + EXPECT_EQ(kSamsungAcFanMed, ac.getFan()); + ac.setBreeze(true); + EXPECT_TRUE(ac.getBreeze()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); // Breeze sets fan to auto. + ac.setBreeze(false); + EXPECT_FALSE(ac.getBreeze()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + + // Breeze and Powerful/Turbo are mutually exclusive. + ac.setBreeze(true); + EXPECT_TRUE(ac.getBreeze()); + ac.setPowerful(true); + EXPECT_FALSE(ac.getBreeze()); + + // Check against real messages. + // MODE FAN, 24C WINDFREE ON + const uint8_t on[14] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0xB2, 0xFE, 0x7B, 0x80, + 0x31, 0xF0}; + ac.setRaw(on); + ASSERT_TRUE(ac.getBreeze()); + EXPECT_EQ( + "Power: On, Mode: 3 (Fan), Temp: 24C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: On, " + "Light: On, Ion: Off", + ac.toString()); + // MODE FAN, 24C WINDFREE OFF, FAN = LOW + const uint8_t off[14] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0xC2, 0xFE, 0x71, 0x80, + 0x35, 0xF0}; + ac.setRaw(off); + ASSERT_FALSE(ac.getBreeze()); + EXPECT_EQ( + "Power: On, Mode: 3 (Fan), Temp: 24C, Fan: 2 (Low), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " + "Light: On, Ion: Off", ac.toString()); } diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Sanyo_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Sanyo_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Sanyo_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Sharp_test.cpp similarity index 83% rename from lib/IRremoteESP8266-2.7.4/test/ir_Sharp_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Sharp_test.cpp index e605e0d05..173c94f2a 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Sharp_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Sharp_test.cpp @@ -1,6 +1,7 @@ // Copyright 2017 David Conran #include "ir_Sharp.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -407,12 +408,11 @@ TEST(TestDecodeSharpAc, RealExample) { ASSERT_EQ(SHARP_AC, irsend.capture.decode_type); ASSERT_EQ(kSharpAcBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); - - IRSharpAc ac(0); - ac.begin(); - ac.setRaw(irsend.capture.state); - EXPECT_EQ("Power: On, Mode: 2 (Cool), Temp: 27C, Fan: 2 (Auto)", - ac.toString()); + EXPECT_EQ("Power: On, Previous Power: On, Mode: 2 (Cool), Temp: 27C, " + "Fan: 2 (Auto)", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } // https://github.com/crankyoldgit/IRremoteESP8266/issues/638#issue-421064165 @@ -588,23 +588,25 @@ TEST(TestSharpAcClass, ReconstructKnownState) { uint8_t on_auto_auto[kSharpAcStateLength] = { 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x11, 0x20, 0x00, 0x08, 0x80, 0x00, 0xE0, 0x01}; - ac.on(); - ac.setMode(kSharpAcAuto); + ac.setPower(true, false); ac.setTemp(kSharpAcMinTemp); ac.setFan(kSharpAcFanAuto); + ac.setMode(kSharpAcAuto); EXPECT_STATE_EQ(on_auto_auto, ac.getRaw(), kSharpAcBits); - EXPECT_EQ("Power: On, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto)", + EXPECT_EQ("Power: On, Previous Power: Off, Mode: 0 (Auto), Temp: 15C, " + "Fan: 2 (Auto)", ac.toString()); uint8_t cool_auto_28[kSharpAcStateLength] = { 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, 0x51}; ac.stateReset(); - ac.on(); + ac.setPower(true, true); ac.setMode(kSharpAcCool); - ac.setTemp(28); ac.setFan(kSharpAcFanAuto); - EXPECT_EQ("Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 2 (Auto)", + ac.setTemp(28); + EXPECT_EQ("Power: On, Previous Power: On, Mode: 2 (Cool), Temp: 28C, " + "Fan: 2 (Auto)", ac.toString()); EXPECT_STATE_EQ(cool_auto_28, ac.getRaw(), kSharpAcBits); } @@ -619,49 +621,56 @@ TEST(TestSharpAcClass, KnownStates) { 0x31}; ASSERT_TRUE(ac.validChecksum(off_auto_auto)); ac.setRaw(off_auto_auto); - EXPECT_EQ("Power: Off, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto)", + EXPECT_EQ("Power: Off, Previous Power: On, Mode: 0 (Auto), Temp: 15C, " + "Fan: 2 (Auto)", ac.toString()); uint8_t on_auto_auto[kSharpAcStateLength] = { 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x11, 0x20, 0x00, 0x08, 0x80, 0x00, 0xE0, 0x01}; ASSERT_TRUE(ac.validChecksum(on_auto_auto)); ac.setRaw(on_auto_auto); - EXPECT_EQ("Power: On, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto)", + EXPECT_EQ("Power: On, Previous Power: Off, Mode: 0 (Auto), Temp: 15C, " + "Fan: 2 (Auto)", ac.toString()); uint8_t cool_auto_28[kSharpAcStateLength] = { 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, 0x51}; ASSERT_TRUE(ac.validChecksum(cool_auto_28)); ac.setRaw(cool_auto_28); - EXPECT_EQ("Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 2 (Auto)", + EXPECT_EQ("Power: On, Previous Power: On, Mode: 2 (Cool), Temp: 28C, " + "Fan: 2 (Auto)", ac.toString()); uint8_t cool_fan1_28[kSharpAcStateLength] = { 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x42, 0x00, 0x08, 0x80, 0x05, 0xE0, 0x21}; ASSERT_TRUE(ac.validChecksum(cool_fan1_28)); ac.setRaw(cool_fan1_28); - EXPECT_EQ("Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 4 (Low)", + EXPECT_EQ("Power: On, Previous Power: On, Mode: 2 (Cool), Temp: 28C, " + "Fan: 4 (Low)", ac.toString()); uint8_t cool_fan2_28[kSharpAcStateLength] = { 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x32, 0x00, 0x08, 0x80, 0x05, 0xE0, 0x51}; ASSERT_TRUE(ac.validChecksum(cool_fan2_28)); ac.setRaw(cool_fan2_28); - EXPECT_EQ("Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 3 (Medium)", + EXPECT_EQ("Power: On, Previous Power: On, Mode: 2 (Cool), Temp: 28C, " + "Fan: 3 (Medium)", ac.toString()); uint8_t cool_fan3_28[kSharpAcStateLength] = { 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x52, 0x00, 0x08, 0x80, 0x05, 0xE0, 0x31}; ASSERT_TRUE(ac.validChecksum(cool_fan3_28)); ac.setRaw(cool_fan3_28); - EXPECT_EQ("Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 5 (UNKNOWN)", + EXPECT_EQ("Power: On, Previous Power: On, Mode: 2 (Cool), Temp: 28C, " + "Fan: 5 (UNKNOWN)", ac.toString()); uint8_t cool_fan4_28[kSharpAcStateLength] = { 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x72, 0x00, 0x08, 0x80, 0x05, 0xE0, 0x11}; ASSERT_TRUE(ac.validChecksum(cool_fan4_28)); ac.setRaw(cool_fan4_28); - EXPECT_EQ("Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 7 (High)", + EXPECT_EQ("Power: On, Previous Power: On, Mode: 2 (Cool), Temp: 28C, " + "Fan: 7 (High)", ac.toString()); /* Unsupported / Not yet reverse engineered. uint8_t cool_fan4_28_ion_on[kSharpAcStateLength] = { @@ -683,7 +692,8 @@ TEST(TestSharpAcClass, KnownStates) { 0x11}; ASSERT_TRUE(ac.validChecksum(dry_auto)); ac.setRaw(dry_auto); - EXPECT_EQ("Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto)", + EXPECT_EQ("Power: On, Previous Power: On, Mode: 3 (Dry), Temp: 15C, " + "Fan: 2 (Auto)", ac.toString()); } @@ -714,3 +724,89 @@ TEST(TestSharpAcClass, toCommon) { ASSERT_EQ(-1, ac.toCommon().sleep); ASSERT_EQ(-1, ac.toCommon().clock); } + +TEST(TestSharpAcClass, PreviousPower) { + IRSharpAc ac(kGpioUnused); + ac.setPower(false, false); + EXPECT_FALSE(ac.getPower()); + EXPECT_FALSE(ac.getPreviousPower()); + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + EXPECT_FALSE(ac.getPreviousPower()); + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); + EXPECT_TRUE(ac.getPreviousPower()); + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + EXPECT_FALSE(ac.getPreviousPower()); + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + EXPECT_TRUE(ac.getPreviousPower()); + ac.setPreviousPower(false); + EXPECT_TRUE(ac.getPower()); + EXPECT_FALSE(ac.getPreviousPower()); + ac.setPreviousPower(true); + EXPECT_TRUE(ac.getPower()); + EXPECT_TRUE(ac.getPreviousPower()); + ac.setPower(true, false); + EXPECT_TRUE(ac.getPower()); + EXPECT_FALSE(ac.getPreviousPower()); + + // Data from: https://github.com/crankyoldgit/IRremoteESP8266/pull/1074#discussion_r403407146 + // Command ON (previously OFF) -> 0xAA 5A CF 10 CB 11 22 00 08 80 00 E0 51 + const uint8_t on_prev_off[] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCB, 0x11, 0x22, + 0x00, 0x08, 0x80, 0x00, 0xE0, 0x51}; + ac.setRaw(on_prev_off); + EXPECT_TRUE(ac.getPower()); + EXPECT_FALSE(ac.getPreviousPower()); + // Command ON (previously ON) -> 0xAA 5A CF 10 CB 31 22 00 08 80 04 E0 31 + const uint8_t on_prev_on[] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCB, 0x31, 0x22, + 0x00, 0x08, 0x80, 0x04, 0xE0, 0x31}; + ac.setRaw(on_prev_on); + EXPECT_TRUE(ac.getPower()); + EXPECT_TRUE(ac.getPreviousPower()); + // Command OFF (previously ON) -> 0xAA 5A CF 10 CB 21 22 00 08 80 00 E0 61 + const uint8_t off_prev_on[] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCB, 0x21, 0x22, + 0x00, 0x08, 0x80, 0x00, 0xE0, 0x61}; + ac.setRaw(off_prev_on); + EXPECT_FALSE(ac.getPower()); + EXPECT_TRUE(ac.getPreviousPower()); + /* Extra test data if needed. + // Power:OFF Mode:2(Cool) Fan:2(Auto) Temp:22 + const uint8_t collect1[13] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xC7, 0x21, 0x22, + 0x00, 0x08, 0x80, 0x00, 0xE0, 0xA1}; + // Power:ON Mode:2(Cool) Fan:2(Auto) Temp:22 + const uint8_t collect2[13] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xC7, 0x11, 0x22, + 0x00, 0x08, 0x80, 0x00, 0xE0, 0x91}; + // Power:ON Mode:2(Cool) Fan:2(Auto) Temp:23 + const uint8_t collect3[13] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xC8, 0x31, 0x22, + 0x00, 0x08, 0x80, 0x04, 0xE0, 0x01}; + // Power:ON Mode:2(Cool) Fan:2(Auto) Temp:22 + const uint8_t collect4[13] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xC7, 0x31, 0x22, + 0x00, 0x08, 0x80, 0x04, 0xE0, 0xF1}; + // Power:ON Mode:3(Dry) Fan:Automaticly 2(Auto) + // Temp:Automaticly 15 + const uint8_t collect5[13] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x31, 0x23, + 0x00, 0x08, 0x80, 0x00, 0xE0, 0x11}; + // Power:ON Mode:2(Cool) Fan:2(Auto) Temp:22 + const uint8_t collect6[13] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xC7, 0x31, 0x22, + 0x00, 0x08, 0x80, 0x00, 0xE0, 0xB1}; + // Power:ON Mode:2(Cool) Fan:3(Medium) Temp:22 + const uint8_t collect7[13] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xC7, 0x31, 0x32, + 0x00, 0x08, 0x80, 0x05, 0xE0, 0xF1}; + // Power:OFF Mode:2(Cool) Fan:3(Medium) Temp:22 + const uint8_t collect8[13] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xC7, 0x21, 0x32, + 0x00, 0x08, 0x80, 0x00, 0xE0, 0xB1}; + */ +} diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Sherwood_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Sherwood_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Sherwood_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Sony_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Sony_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Sony_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Sony_test.cpp diff --git a/lib/IRremoteESP8266-2.7.6/test/ir_Symphony_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Symphony_test.cpp new file mode 100644 index 000000000..ee4aadfda --- /dev/null +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Symphony_test.cpp @@ -0,0 +1,157 @@ +// Copyright 2020 David Conran + +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendSymphony(). + +// Test sending typical data only. +TEST(TestSendSymphony, SendDataOnly) { + IRsendTest irsend(kGpioUnused); + irsend.begin(); + irsend.sendSymphony(0x137); + EXPECT_EQ( + "f38000d50" + "m1250s400m1250s400m400s1250m1250s400m1250s400m400s1250m400s1250m1250s400" + "m400s1250m400s1250m400s1250" + "m400s8000" + "m1250s400m1250s400m400s1250m1250s400m1250s400m400s1250m400s1250m1250s400" + "m400s1250m400s1250m400s1250" + "m400s8000", + irsend.outputStr()); +} + +// Tests for decodeSymphony(). + +// Decode normal Symphony messages. +TEST(TestDecodeSymphony, SyntheticSelfDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + // Real-life Symphony code from an actual capture/decode. + irsend.reset(); + irsend.sendSymphony(0x123); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::SYMPHONY, irsend.capture.decode_type); + EXPECT_EQ(kSymphonyBits, irsend.capture.bits); + EXPECT_EQ(0x123, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); +} + +// Decode a real Symphony message. +TEST(TestDecodeSymphony, RealMessageDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + // Real-life Symphony code from an actual capture/decode. + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1057#issue-577216614 + irsend.reset(); + const uint16_t button_1[95] = { + 1296, 412, 1294, 386, 420, 1224, 1322, 390, 1290, 390, 420, 1224, 452, + 1220, 1314, 394, 420, 1222, 482, 1190, 480, 1192, 452, 7960, + 1290, 420, 1290, 390, 418, 1226, 1318, 394, 1262, 416, 420, 1224, 454, + 1220, 1292, 416, 422, 1222, 450, 1222, 452, 1218, 454, 8208, + 1296, 414, 1292, 386, 418, 1226, 1292, 422, 1260, 420, 424, 1218, 454, + 1226, 1312, 390, 420, 1224, 454, 1220, 482, 1186, 454, 7960, + 1318, 392, 1264, 416, 392, 1252, 1318, 394, 1288, 394, 418, 1224, 452, + 1224, 1292, 422, 414, 1222, 458, 1214, 450, 1222, 454}; + irsend.sendRaw(button_1, 95, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::SYMPHONY, irsend.capture.decode_type); + EXPECT_EQ(kSymphonyBits, irsend.capture.bits); + EXPECT_EQ(0x137, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1057#issuecomment-596038442 + irsend.reset(); + const uint16_t power[23] = { + 1308, 368, 1310, 368, 448, 1222, 1310, 372, 1308, 400, 442, 1198, 472, + 1198, 1284, 396, 444, 1224, 470, 1200, 470, 1198, 472}; + irsend.sendRaw(power, 23, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::SYMPHONY, irsend.capture.decode_type); + EXPECT_EQ(kSymphonyBits, irsend.capture.bits); + EXPECT_EQ(0x137, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + irsend.reset(); + const uint16_t swing[23] = { + 1290, 418, 1286, 392, 422, 1248, 1284, 400, 1294, 386, 422, 1248, 422, + 1250, 444, 1228, 424, 1248, 446, 1226, 1258, 420, 446}; + irsend.sendRaw(swing, 23, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::SYMPHONY, irsend.capture.decode_type); + EXPECT_EQ(kSymphonyBits, irsend.capture.bits); + EXPECT_EQ(0x13E, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); +} + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1057#issuecomment-596543641 +TEST(TestDecodeSymphony, RealMessageSentViaLibrary) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + // Generated by the library/ESP8266. + const uint16_t rawdata_lib[47] = { + 1222, 430, 1250, 436, 410, 1242, 1274, 446, 1274, 412, 414, 1266, 410, + 1264, 1252, 436, 436, 1240, 410, 1270, 438, 1242, 410, 8012, 1254, 434, + 1258, 432, 438, 1240, 1276, 416, 1252, 434, 438, 1234, 412, 1270, 1250, + 442, 412, 1264, 438, 1238, 410, 1270, 438}; + irsend.sendRaw(rawdata_lib, 47, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::SYMPHONY, irsend.capture.decode_type); + EXPECT_EQ(kSymphonyBits, irsend.capture.bits); + EXPECT_EQ(0x137, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + irsend.reset(); + // Generated by the real remote. + const uint16_t rawdata_remote[47] = { + 1286, 396, 1286, 396, 446, 1226, 1288, 400, 1294, 388, 444, 1228, 446, + 1226, 1286, 396, 444, 1226, 448, 1226, 468, 1204, 448, 7968, 1286, 396, + 1286, 396, 470, 1202, 1286, 400, 1286, 396, 446, 1224, 448, 1226, 1288, + 396, 446, 1226, 472, 1200, 448, 1226, 472}; + irsend.sendRaw(rawdata_remote, 47, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::SYMPHONY, irsend.capture.decode_type); + EXPECT_EQ(kSymphonyBits, irsend.capture.bits); + EXPECT_EQ(0x137, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); +} + + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("SYMPHONY", typeToString(decode_type_t::SYMPHONY)); + ASSERT_EQ(decode_type_t::SYMPHONY, strToDecodeType("SYMPHONY")); + ASSERT_FALSE(hasACState(decode_type_t::SYMPHONY)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::SYMPHONY)); + ASSERT_EQ(kSymphonyBits, IRsendTest::defaultBits(decode_type_t::SYMPHONY)); + ASSERT_EQ(kSymphonyDefaultRepeat, + IRsendTest::minRepeats(decode_type_t::SYMPHONY)); +} diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Tcl_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Tcl_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Tcl_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Teco_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Teco_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.7.4/test/ir_Teco_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Teco_test.cpp index 635e93a44..f4196c3ae 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Teco_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Teco_test.cpp @@ -1,6 +1,7 @@ // Copyright 2019 David Conran #include "ir_Teco.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -387,7 +388,6 @@ TEST(TestDecodeTeco, NormalDecodeWithStrict) { TEST(TestDecodeTeco, RealNormalExample) { IRsendTest irsend(0); IRrecv irrecv(0); - IRTecoAc ac(0); irsend.begin(); uint16_t rawData1[73] = { @@ -408,12 +408,12 @@ TEST(TestDecodeTeco, RealNormalExample) { EXPECT_EQ(expected1, irsend.capture.value); EXPECT_EQ(0, irsend.capture.address); EXPECT_EQ(0, irsend.capture.command); - ac.begin(); - ac.setRaw(irsend.capture.value); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 0 (Auto), Sleep: On, " "Swing: On, Light: Off, Humid: Off, Save: Off, Timer: Off", - ac.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); uint16_t rawData2[73] = { 9048, 4472, 636, 548, 636, 1654, 638, 546, 642, 1650, 642, 546, 638, @@ -433,12 +433,11 @@ TEST(TestDecodeTeco, RealNormalExample) { EXPECT_EQ(expected2, irsend.capture.value); EXPECT_EQ(0, irsend.capture.address); EXPECT_EQ(0, irsend.capture.command); - ac.begin(); - ac.setRaw(irsend.capture.value); EXPECT_EQ( "Power: On, Mode: 2 (Dry), Temp: 21C, Fan: 2 (Medium), Sleep: Off, " "Swing: On, Light: Off, Humid: Off, Save: Off, Timer: Off", - ac.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Toshiba_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Toshiba_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Toshiba_test.cpp index 66c258ae4..e0ae82987 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Toshiba_test.cpp @@ -1,5 +1,6 @@ // Copyright 2017 David Conran #include "ir_Toshiba.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -499,6 +500,11 @@ TEST(TestDecodeToshibaAC, SyntheticExample) { ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); ASSERT_EQ(kToshibaACBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Power: On, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto)", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } // Test decoding against captures from a real Toshiba A/C remote. diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Trotec_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Trotec_test.cpp similarity index 95% rename from lib/IRremoteESP8266-2.7.4/test/ir_Trotec_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Trotec_test.cpp index 43cb3fc06..4e8d639fe 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Trotec_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Trotec_test.cpp @@ -1,5 +1,6 @@ // Copyright 2019 David Conran #include "ir_Trotec.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -97,11 +98,11 @@ TEST(TestDecodeTrotec, SyntheticDecode) { EXPECT_EQ(decode_type_t::TROTEC, irsend.capture.decode_type); EXPECT_EQ(kTrotecBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); - IRTrotecESP ac(0); - ac.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 2 (Medium), Sleep: On", - ac.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } @@ -176,4 +177,5 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ("TROTEC", typeToString(decode_type_t::TROTEC)); ASSERT_EQ(decode_type_t::TROTEC, strToDecodeType("TROTEC")); ASSERT_TRUE(hasACState(decode_type_t::TROTEC)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::TROTEC)); } diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Vestel_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Vestel_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Vestel_test.cpp index d20a882a6..d3f1febf8 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Vestel_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Vestel_test.cpp @@ -1,6 +1,7 @@ // Copyright 2019 David Conran #include "ir_Vestel.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -441,7 +442,6 @@ TEST(TestDecodeVestelAc, NormalDecodeWithStrict) { TEST(TestDecodeVestelAc, RealNormalExample) { IRsendTest irsend(0); IRrecv irrecv(0); - IRVestelAc ac(0); irsend.begin(); uint16_t rawData[115] = { @@ -465,12 +465,12 @@ TEST(TestDecodeVestelAc, RealNormalExample) { EXPECT_EQ(0xF4410001FF1201ULL, irsend.capture.value); EXPECT_EQ(0, irsend.capture.address); EXPECT_EQ(0, irsend.capture.command); - ac.begin(); - ac.setRaw(irsend.capture.value); EXPECT_EQ( "Power: On, Mode: 4 (Heat), Temp: 16C, Fan: 1 (Auto), Sleep: Off, " "Turbo: Off, Ion: On, Swing: Off", - ac.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } TEST(TestDecodeVestelAc, RealTimerExample) { diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Whirlpool_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.7.4/test/ir_Whirlpool_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Whirlpool_test.cpp index 901318326..a6374a7f6 100644 --- a/lib/IRremoteESP8266-2.7.4/test/ir_Whirlpool_test.cpp +++ b/lib/IRremoteESP8266-2.7.6/test/ir_Whirlpool_test.cpp @@ -1,6 +1,7 @@ // Copyright 2018 David Conran #include "ir_Whirlpool.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -66,13 +67,13 @@ TEST(TestDecodeWhirlpoolAC, SyntheticDecode) { EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); - IRWhirlpoolAc ac(0); - ac.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 1 (DG11J13A), Power Toggle: Off, Mode: 1 (Auto), Temp: 25C, " "Fan: 0 (Auto), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " "Off Timer: Off, Sleep: Off, Super: Off, Command: 2 (Temp)", - ac.toString()); + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } TEST(TestDecodeWhirlpoolAC, Real26CFanAutoCoolingSwingOnClock1918) { diff --git a/lib/IRremoteESP8266-2.7.4/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Whynter_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/test/ir_Whynter_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Whynter_test.cpp diff --git a/lib/IRremoteESP8266-2.7.4/tools/Makefile b/lib/IRremoteESP8266-2.7.6/tools/Makefile similarity index 96% rename from lib/IRremoteESP8266-2.7.4/tools/Makefile rename to lib/IRremoteESP8266-2.7.6/tools/Makefile index 0ee8cc563..cfbf2e33c 100644 --- a/lib/IRremoteESP8266-2.7.4/tools/Makefile +++ b/lib/IRremoteESP8266-2.7.6/tools/Makefile @@ -51,7 +51,8 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o ir_Pioneer.o \ ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o \ ir_MitsubishiHeavy.o ir_Goodweather.o ir_Inax.o ir_Argo.o \ - ir_Trotec.o ir_Neoclima.o ir_Amcor.o ir_Epson.o + ir_Trotec.o ir_Neoclima.o ir_Amcor.o ir_Epson.o ir_Symphony.o \ + ir_Airwell.o # Common object files COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o IRtext.o IRac.o $(PROTOCOLS) @@ -237,5 +238,11 @@ ir_Amcor.o : $(USER_DIR)/ir_Amcor.cpp $(USER_DIR)/ir_Amcor.h $(GTEST_HEADERS) ir_Epson.o : $(USER_DIR)/ir_Epson.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Epson.cpp +ir_Symphony.o : $(USER_DIR)/ir_Symphony.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Symphony.cpp + +ir_Airwell.o : $(USER_DIR)/ir_Airwell.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Airwell.cpp + IRac.o : $(USER_DIR)/IRac.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/IRac.cpp diff --git a/lib/IRremoteESP8266-2.7.4/tools/RawToGlobalCache.sh b/lib/IRremoteESP8266-2.7.6/tools/RawToGlobalCache.sh similarity index 100% rename from lib/IRremoteESP8266-2.7.4/tools/RawToGlobalCache.sh rename to lib/IRremoteESP8266-2.7.6/tools/RawToGlobalCache.sh diff --git a/lib/IRremoteESP8266-2.7.4/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266-2.7.6/tools/auto_analyse_raw_data.py similarity index 100% rename from lib/IRremoteESP8266-2.7.4/tools/auto_analyse_raw_data.py rename to lib/IRremoteESP8266-2.7.6/tools/auto_analyse_raw_data.py diff --git a/lib/IRremoteESP8266-2.7.4/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266-2.7.6/tools/auto_analyse_raw_data_test.py similarity index 100% rename from lib/IRremoteESP8266-2.7.4/tools/auto_analyse_raw_data_test.py rename to lib/IRremoteESP8266-2.7.6/tools/auto_analyse_raw_data_test.py diff --git a/lib/IRremoteESP8266-2.7.4/tools/gc_decode.cpp b/lib/IRremoteESP8266-2.7.6/tools/gc_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/tools/gc_decode.cpp rename to lib/IRremoteESP8266-2.7.6/tools/gc_decode.cpp diff --git a/lib/IRremoteESP8266-2.7.4/tools/generate_irtext_h.sh b/lib/IRremoteESP8266-2.7.6/tools/generate_irtext_h.sh similarity index 100% rename from lib/IRremoteESP8266-2.7.4/tools/generate_irtext_h.sh rename to lib/IRremoteESP8266-2.7.6/tools/generate_irtext_h.sh diff --git a/lib/IRremoteESP8266-2.7.4/tools/mkkeywords b/lib/IRremoteESP8266-2.7.6/tools/mkkeywords similarity index 100% rename from lib/IRremoteESP8266-2.7.4/tools/mkkeywords rename to lib/IRremoteESP8266-2.7.6/tools/mkkeywords diff --git a/lib/IRremoteESP8266-2.7.4/tools/mode2_decode.cpp b/lib/IRremoteESP8266-2.7.6/tools/mode2_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.4/tools/mode2_decode.cpp rename to lib/IRremoteESP8266-2.7.6/tools/mode2_decode.cpp diff --git a/lib/IRremoteESP8266-2.7.4/tools/scrape_supported_devices.py b/lib/IRremoteESP8266-2.7.6/tools/scrape_supported_devices.py similarity index 100% rename from lib/IRremoteESP8266-2.7.4/tools/scrape_supported_devices.py rename to lib/IRremoteESP8266-2.7.6/tools/scrape_supported_devices.py diff --git a/lib/OpenTherm-0.9.0/LICENSE b/lib/OpenTherm-0.9.0/LICENSE new file mode 100644 index 000000000..42a9cdf66 --- /dev/null +++ b/lib/OpenTherm-0.9.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Ihor Melnyk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/OpenTherm-0.9.0/README.md b/lib/OpenTherm-0.9.0/README.md new file mode 100644 index 000000000..9482c7403 --- /dev/null +++ b/lib/OpenTherm-0.9.0/README.md @@ -0,0 +1,65 @@ +# OpenTherm Arduino/ESP8266 Library + +This library provides implementation of OpenTherm protocol. + +OpenTherm Library is based on OpenTherm protocol specification v2.2 and works with all OpenTherm compatible boilers. Library can be easily installed into Arduino IDE and compiled for Arduino, ESP8266 and other similar controllers. + +OpenTherm protocol requires simple low voltage twowire connection to boiler, but voltage levels (7..15V) still much higher than Arduino/ESP8266 levels, which requires [OpenTherm Adapter](http://ihormelnyk.com/opentherm_adapter). + +This version of library uses interrupts to achieve better stability and synchronization with boiler. + +## Using OpenTherm Library you will be able: +- control your boiler remotely (get status, switch on/off heating/water heating, set water temperature and much more) +- make custom thermostat + +## Configuration and Usage: +```c +#include +``` +You have to choose 2 controller GPIO pins which will be used for communication and connected to [OpenTherm Adapter](http://ihormelnyk.com/opentherm_adapter). Controller(Arduino/ESP8266) input pin should support interrupts. +Controller output pin should be connected to OpenTherm Adapter input pin and vise versa. +```c +const int inPin = 2; +const int outPin = 3; +``` +Define OpenTherm class instance using constructor which accepts as arguments pin numbers: +```c +OpenTherm ot(inPin, outPin); +``` +Define interrupts handler function for specified above instance: +```c +void handleInterrupt() { + ot.handleInterrupt(); +} +``` +Use begin function to initialize OpenTherm instance, specify interrupts handler function as argument +```c +void setup() +{ + ot.begin(handleInterrupt); +} +``` +According to OpenTherm Protocol specification master (controller) must communicate at least every 1 sec. So lets make some requests in loop function: +```c +void loop() +{ + //Set/Get Boiler Status + ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling); + //Set Boiler Temperature to 64 degrees C + ot.setBoilerTemperature(64); + //Get Boiler Temperature + float temperature = ot.getBoilerTemperature(); + delay(1000); +} +``` + +In details [OpenTherm Library](http://ihormelnyk.com/opentherm_library) described [here](http://ihormelnyk.com/opentherm_library). + +## OpenTherm Adapter Schematic +![opentherm adapter schmetic](http://ihormelnyk.com/Content/Pages/opentherm_adapter/opentherm_adapter_schematic.png) + +## Arduino UNO Connection +![opentherm adapter arduino](http://ihormelnyk.com/Content/Pages/opentherm_adapter/opentherm_adapter_arduino_connection.png) + +## License +Copyright (c) 2018 [Ihor Melnyk](http://ihormelnyk.com). Licensed under the [MIT license](/LICENSE?raw=true). diff --git a/lib/OpenTherm-0.9.0/keywords.txt b/lib/OpenTherm-0.9.0/keywords.txt new file mode 100644 index 000000000..881d1da7d --- /dev/null +++ b/lib/OpenTherm-0.9.0/keywords.txt @@ -0,0 +1,40 @@ +####################################### +# Syntax Coloring Map For OpenTherm +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +OpenTherm KEYWORD1 +OpenThermStatus KEYWORD1 +OpenThermResponseStatus KEYWORD1 +OpenThermRequestType KEYWORD1 +OpenThermMessageID KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +isReady KEYWORD2 +sendRequest KEYWORD2 +sendRequestAync KEYWORD2 +buildRequest KEYWORD2 +getLastResponseStatus KEYWORD2 +handleInterrupt KEYWORD2 +process KEYWORD2 +end KEYWORD2 +doSomething KEYWORD2 + +setBoilerStatus KEYWORD2 +setBoilerTemperature KEYWORD2 +getBoilerTemperature KEYWORD2 + +####################################### +# Instances (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### \ No newline at end of file diff --git a/lib/OpenTherm-0.9.0/library.properties b/lib/OpenTherm-0.9.0/library.properties new file mode 100644 index 000000000..003b43eef --- /dev/null +++ b/lib/OpenTherm-0.9.0/library.properties @@ -0,0 +1,10 @@ +name=OpenTherm Library +version=0.9.0 +author=Ihor Melnyk +maintainer=Ihor Melnyk +sentence=OpenTherm Library for HVAC system control communication using Arduino and ESP8266 hardware. +paragraph=OpenTherm Library is based on OpenTherm protocol specification v2.2 and works with all OpenTherm compatible boilers. +category=Communication +url=https://github.com/ihormelnyk/opentherm_library +architectures=* +includes=OpenTherm.h diff --git a/lib/OpenTherm-0.9.0/src/OpenTherm.cpp b/lib/OpenTherm-0.9.0/src/OpenTherm.cpp new file mode 100644 index 000000000..a08608649 --- /dev/null +++ b/lib/OpenTherm-0.9.0/src/OpenTherm.cpp @@ -0,0 +1,410 @@ +/* +OpenTherm.cpp - OpenTherm Communication Library For Arduino, ESP8266 +Copyright 2018, Ihor Melnyk +*/ + +#include "OpenTherm.h" + +OpenTherm::OpenTherm(int inPin, int outPin, bool isSlave): + status(OpenThermStatus::NOT_INITIALIZED), + inPin(inPin), + outPin(outPin), + isSlave(isSlave), + response(0), + responseStatus(OpenThermResponseStatus::NONE), + responseTimestamp(0), + handleInterruptCallback(NULL), + processResponseCallback(NULL) +{ +} + +void OpenTherm::begin(void(*handleInterruptCallback)(void), void(*processResponseCallback)(unsigned long, int)) +{ + pinMode(inPin, INPUT); + pinMode(outPin, OUTPUT); + if (handleInterruptCallback != NULL) { + this->handleInterruptCallback = handleInterruptCallback; + attachInterrupt(digitalPinToInterrupt(inPin), handleInterruptCallback, CHANGE); + } + activateBoiler(); + status = OpenThermStatus::READY; + this->processResponseCallback = processResponseCallback; +} + +void OpenTherm::begin(void(*handleInterruptCallback)(void)) +{ + begin(handleInterruptCallback, NULL); +} + +bool ICACHE_RAM_ATTR OpenTherm::isReady() +{ + return status == OpenThermStatus::READY; +} + +int ICACHE_RAM_ATTR OpenTherm::readState() { + return digitalRead(inPin); +} + +void OpenTherm::setActiveState() { + digitalWrite(outPin, LOW); +} + +void OpenTherm::setIdleState() { + digitalWrite(outPin, HIGH); +} + +void OpenTherm::activateBoiler() { + setIdleState(); + delay(1000); +} + +void OpenTherm::sendBit(bool high) { + if (high) setActiveState(); else setIdleState(); + delayMicroseconds(500); + if (high) setIdleState(); else setActiveState(); + delayMicroseconds(500); +} + +bool OpenTherm::sendRequestAync(unsigned long request) +{ + //Serial.println("Request: " + String(request, HEX)); + noInterrupts(); + const bool ready = isReady(); + interrupts(); + + if (!ready) + return false; + + status = OpenThermStatus::REQUEST_SENDING; + response = 0; + responseStatus = OpenThermResponseStatus::NONE; + + sendBit(HIGH); //start bit + for (int i = 31; i >= 0; i--) { + sendBit(bitRead(request, i)); + } + sendBit(HIGH); //stop bit + setIdleState(); + + status = OpenThermStatus::RESPONSE_WAITING; + responseTimestamp = micros(); + return true; +} + +unsigned long OpenTherm::sendRequest(unsigned long request) +{ + if (!sendRequestAync(request)) return 0; + while (!isReady()) { + process(); + yield(); + } + return response; +} + +bool OpenTherm::sendResponse(unsigned long request) +{ + status = OpenThermStatus::REQUEST_SENDING; + response = 0; + responseStatus = OpenThermResponseStatus::NONE; + + sendBit(HIGH); //start bit + for (int i = 31; i >= 0; i--) { + sendBit(bitRead(request, i)); + } + sendBit(HIGH); //stop bit + setIdleState(); + status = OpenThermStatus::READY; + return true; +} + +OpenThermResponseStatus OpenTherm::getLastResponseStatus() +{ + return responseStatus; +} + +void ICACHE_RAM_ATTR OpenTherm::handleInterrupt() +{ + if (isReady()) + { + if (isSlave && readState() == HIGH) { + status = OpenThermStatus::RESPONSE_WAITING; + } + else { + return; + } + } + + unsigned long newTs = micros(); + if (status == OpenThermStatus::RESPONSE_WAITING) { + if (readState() == HIGH) { + status = OpenThermStatus::RESPONSE_START_BIT; + responseTimestamp = newTs; + } + else { + status = OpenThermStatus::RESPONSE_INVALID; + responseTimestamp = newTs; + } + } + else if (status == OpenThermStatus::RESPONSE_START_BIT) { + if ((newTs - responseTimestamp < 750) && readState() == LOW) { + status = OpenThermStatus::RESPONSE_RECEIVING; + responseTimestamp = newTs; + responseBitIndex = 0; + } + else { + status = OpenThermStatus::RESPONSE_INVALID; + responseTimestamp = newTs; + } + } + else if (status == OpenThermStatus::RESPONSE_RECEIVING) { + if ((newTs - responseTimestamp) > 750) { + if (responseBitIndex < 32) { + response = (response << 1) | !readState(); + responseTimestamp = newTs; + responseBitIndex++; + } + else { //stop bit + status = OpenThermStatus::RESPONSE_READY; + responseTimestamp = newTs; + } + } + } +} + +void OpenTherm::process() +{ + noInterrupts(); + OpenThermStatus st = status; + unsigned long ts = responseTimestamp; + interrupts(); + + if (st == OpenThermStatus::READY) return; + unsigned long newTs = micros(); + if (st != OpenThermStatus::NOT_INITIALIZED && (newTs - ts) > 1000000) { + status = OpenThermStatus::READY; + responseStatus = OpenThermResponseStatus::TIMEOUT; + if (processResponseCallback != NULL) { + processResponseCallback(response, responseStatus); + } + } + else if (st == OpenThermStatus::RESPONSE_INVALID) { + status = OpenThermStatus::DELAY; + responseStatus = OpenThermResponseStatus::INVALID; + if (processResponseCallback != NULL) { + processResponseCallback(response, responseStatus); + } + } + else if (st == OpenThermStatus::RESPONSE_READY) { + status = OpenThermStatus::DELAY; + responseStatus = (isSlave ? isValidRequest(response) : isValidResponse(response)) ? OpenThermResponseStatus::SUCCESS : OpenThermResponseStatus::INVALID; + if (processResponseCallback != NULL) { + processResponseCallback(response, responseStatus); + } + } + else if (st == OpenThermStatus::DELAY) { + if ((newTs - ts) > 100000) { + status = OpenThermStatus::READY; + } + } +} + +bool OpenTherm::parity(unsigned long frame) //odd parity +{ + byte p = 0; + while (frame > 0) + { + if (frame & 1) p++; + frame = frame >> 1; + } + return (p & 1); +} + +OpenThermMessageType OpenTherm::getMessageType(unsigned long message) +{ + OpenThermMessageType msg_type = static_cast((message >> 28) & 7); + return msg_type; +} + +OpenThermMessageID OpenTherm::getDataID(unsigned long frame) +{ + return (OpenThermMessageID)((frame >> 16) & 0xFF); +} + +unsigned long OpenTherm::buildRequest(OpenThermMessageType type, OpenThermMessageID id, unsigned int data) +{ + unsigned long request = data; + if (type == OpenThermMessageType::WRITE_DATA) { + request |= 1ul << 28; + } + request |= ((unsigned long)id) << 16; + if (OpenTherm::parity(request)) request |= (1ul << 31); + return request; +} + +unsigned long OpenTherm::buildResponse(OpenThermMessageType type, OpenThermMessageID id, unsigned int data) +{ + unsigned long response = data; + response |= type << 28; + response |= ((unsigned long)id) << 16; + if (OpenTherm::parity(response)) response |= (1ul << 31); + return response; +} + +bool OpenTherm::isValidResponse(unsigned long response) +{ + if (OpenTherm::parity(response)) return false; + byte msgType = (response << 1) >> 29; + return msgType == READ_ACK || msgType == WRITE_ACK; +} + +bool OpenTherm::isValidRequest(unsigned long request) +{ + if (OpenTherm::parity(request)) return false; + byte msgType = (request << 1) >> 29; + return msgType == READ_DATA || msgType == WRITE_DATA; +} + +void OpenTherm::end() { + if (this->handleInterruptCallback != NULL) { + detachInterrupt(digitalPinToInterrupt(inPin)); + } +} + +const char *OpenTherm::statusToString(OpenThermResponseStatus status) +{ + switch (status) { + case NONE: return "NONE"; + case SUCCESS: return "SUCCESS"; + case INVALID: return "INVALID"; + case TIMEOUT: return "TIMEOUT"; + default: return "UNKNOWN"; + } +} + +const char *OpenTherm::messageTypeToString(OpenThermMessageType message_type) +{ + switch (message_type) { + case READ_DATA: return "READ_DATA"; + case WRITE_DATA: return "WRITE_DATA"; + case INVALID_DATA: return "INVALID_DATA"; + case RESERVED: return "RESERVED"; + case READ_ACK: return "READ_ACK"; + case WRITE_ACK: return "WRITE_ACK"; + case DATA_INVALID: return "DATA_INVALID"; + case UNKNOWN_DATA_ID: return "UNKNOWN_DATA_ID"; + default: return "UNKNOWN"; + } +} + +//building requests + +unsigned long OpenTherm::buildSetBoilerStatusRequest(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2) { + unsigned int data = enableCentralHeating | (enableHotWater << 1) | (enableCooling << 2) | (enableOutsideTemperatureCompensation << 3) | (enableCentralHeating2 << 4); + data <<= 8; + return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Status, data); +} + +unsigned long OpenTherm::buildSetBoilerTemperatureRequest(float temperature) { + unsigned int data = temperatureToData(temperature); + return buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TSet, data); +} + +unsigned long OpenTherm::buildSetHotWaterTemperatureRequest(float temperature) { + unsigned int data = temperatureToData(temperature); + return buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TdhwSet, data); +} + +unsigned long OpenTherm::buildGetBoilerTemperatureRequest() { + return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Tboiler, 0); +} + +unsigned long OpenTherm::buildSlaveConfigurationRequest() { + return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::SConfigSMemberIDcode, 0); +} + +//parsing responses +bool OpenTherm::isFault(unsigned long response) { + return response & 0x1; +} + +bool OpenTherm::isCentralHeatingActive(unsigned long response) { + return response & 0x2; +} + +bool OpenTherm::isHotWaterActive(unsigned long response) { + return response & 0x4; +} + +bool OpenTherm::isFlameOn(unsigned long response) { + return response & 0x8; +} + +bool OpenTherm::isCoolingActive(unsigned long response) { + return response & 0x10; +} + +bool OpenTherm::isDiagnostic(unsigned long response) { + return response & 0x40; +} + +uint16_t OpenTherm::getUInt(const unsigned long response) { + const uint16_t u88 = response & 0xffff; + return u88; +} + +float OpenTherm::getFloat(const unsigned long response) { + const uint16_t u88 = getUInt(response); + const float f = (u88 & 0x8000) ? -(0x10000L - u88) / 256.0f : u88 / 256.0f; + return f; +} + +unsigned int OpenTherm::temperatureToData(float temperature) { + if (temperature < 0) temperature = 0; + if (temperature > 100) temperature = 100; + unsigned int data = (unsigned int)(temperature * 256); + return data; +} + +//basic requests + +unsigned long OpenTherm::setBoilerStatus(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2) { + return sendRequest(buildSetBoilerStatusRequest(enableCentralHeating, enableHotWater, enableCooling, enableOutsideTemperatureCompensation, enableCentralHeating2)); +} + +bool OpenTherm::setBoilerTemperature(float temperature) { + unsigned long response = sendRequest(buildSetBoilerTemperatureRequest(temperature)); + return isValidResponse(response); +} + +bool OpenTherm::setHotWaterTemperature(float temperature) { + unsigned long response = sendRequest(buildSetHotWaterTemperatureRequest(temperature)); + return isValidResponse(response); +} + +float OpenTherm::getBoilerTemperature() { + unsigned long response = sendRequest(buildGetBoilerTemperatureRequest()); + return isValidResponse(response) ? getFloat(response) : 0; +} + +float OpenTherm::getReturnTemperature() { + unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Tret, 0)); + return isValidResponse(response) ? getFloat(response) : 0; +} + +float OpenTherm::getModulation() { + unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0)); + return isValidResponse(response) ? getFloat(response) : 0; +} + +float OpenTherm::getPressure() { + unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0)); + return isValidResponse(response) ? getFloat(response) : 0; +} + +unsigned char OpenTherm::getFault() { + return ((sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0)) >> 8) & 0xff); +} + +unsigned long OpenTherm::getSlaveConfiguration() { + return sendRequest(buildSlaveConfigurationRequest()); +} \ No newline at end of file diff --git a/lib/OpenTherm-0.9.0/src/OpenTherm.h b/lib/OpenTherm-0.9.0/src/OpenTherm.h new file mode 100644 index 000000000..22bf965ad --- /dev/null +++ b/lib/OpenTherm-0.9.0/src/OpenTherm.h @@ -0,0 +1,193 @@ +/* +OpenTherm.h - OpenTherm Library for the ESP8266/Arduino platform +https://github.com/ihormelnyk/OpenTherm +http://ihormelnyk.com/pages/OpenTherm +Licensed under MIT license +Copyright 2018, Ihor Melnyk + +Frame Structure: +P MGS-TYPE SPARE DATA-ID DATA-VALUE +0 000 0000 00000000 00000000 00000000 +*/ + +#ifndef OpenTherm_h +#define OpenTherm_h + +#include +#include + +enum OpenThermResponseStatus { + NONE, + SUCCESS, + INVALID, + TIMEOUT +}; + + +enum OpenThermMessageType { + /* Master to Slave */ + READ_DATA = B000, + READ = READ_DATA, // for backwared compatibility + WRITE_DATA = B001, + WRITE = WRITE_DATA, // for backwared compatibility + INVALID_DATA = B010, + RESERVED = B011, + /* Slave to Master */ + READ_ACK = B100, + WRITE_ACK = B101, + DATA_INVALID = B110, + UNKNOWN_DATA_ID = B111 +}; + +typedef OpenThermMessageType OpenThermRequestType; // for backwared compatibility + +enum OpenThermMessageID { + Status, // flag8 / flag8 Master and Slave Status flags. + TSet, // f8.8 Control setpoint ie CH water temperature setpoint (°C) + MConfigMMemberIDcode, // flag8 / u8 Master Configuration Flags / Master MemberID Code + SConfigSMemberIDcode, // flag8 / u8 Slave Configuration Flags / Slave MemberID Code + Command, // u8 / u8 Remote Command + ASFflags, // / OEM-fault-code flag8 / u8 Application-specific fault flags and OEM fault code + RBPflags, // flag8 / flag8 Remote boiler parameter transfer-enable & read/write flags + CoolingControl, // f8.8 Cooling control signal (%) + TsetCH2, // f8.8 Control setpoint for 2e CH circuit (°C) + TrOverride, // f8.8 Remote override room setpoint + TSP, // u8 / u8 Number of Transparent-Slave-Parameters supported by slave + TSPindexTSPvalue, // u8 / u8 Index number / Value of referred-to transparent slave parameter. + FHBsize, // u8 / u8 Size of Fault-History-Buffer supported by slave + FHBindexFHBvalue, // u8 / u8 Index number / Value of referred-to fault-history buffer entry. + MaxRelModLevelSetting, // f8.8 Maximum relative modulation level setting (%) + MaxCapacityMinModLevel, // u8 / u8 Maximum boiler capacity (kW) / Minimum boiler modulation level(%) + TrSet, // f8.8 Room Setpoint (°C) + RelModLevel, // f8.8 Relative Modulation Level (%) + CHPressure, // f8.8 Water pressure in CH circuit (bar) + DHWFlowRate, // f8.8 Water flow rate in DHW circuit. (litres/minute) + DayTime, // special / u8 Day of Week and Time of Day + Date, // u8 / u8 Calendar date + Year, // u16 Calendar year + TrSetCH2, // f8.8 Room Setpoint for 2nd CH circuit (°C) + Tr, // f8.8 Room temperature (°C) + Tboiler, // f8.8 Boiler flow water temperature (°C) + Tdhw, // f8.8 DHW temperature (°C) + Toutside, // f8.8 Outside temperature (°C) + Tret, // f8.8 Return water temperature (°C) + Tstorage, // f8.8 Solar storage temperature (°C) + Tcollector, // f8.8 Solar collector temperature (°C) + TflowCH2, // f8.8 Flow water temperature CH2 circuit (°C) + Tdhw2, // f8.8 Domestic hot water temperature 2 (°C) + Texhaust, // s16 Boiler exhaust temperature (°C) + TdhwSetUBTdhwSetLB = 48, // s8 / s8 DHW setpoint upper & lower bounds for adjustment (°C) + MaxTSetUBMaxTSetLB, // s8 / s8 Max CH water setpoint upper & lower bounds for adjustment (°C) + HcratioUBHcratioLB, // s8 / s8 OTC heat curve ratio upper & lower bounds for adjustment + TdhwSet = 56, // f8.8 DHW setpoint (°C) (Remote parameter 1) + MaxTSet, // f8.8 Max CH water setpoint (°C) (Remote parameters 2) + Hcratio, // f8.8 OTC heat curve ratio (°C) (Remote parameter 3) + RemoteOverrideFunction = 100, // flag8 / - Function of manual and program changes in master and remote room setpoint. + OEMDiagnosticCode = 115, // u16 OEM-specific diagnostic/service code + BurnerStarts, // u16 Number of starts burner + CHPumpStarts, // u16 Number of starts CH pump + DHWPumpValveStarts, // u16 Number of starts DHW pump/valve + DHWBurnerStarts, // u16 Number of starts burner during DHW mode + BurnerOperationHours, // u16 Number of hours that burner is in operation (i.e. flame on) + CHPumpOperationHours, // u16 Number of hours that CH pump has been running + DHWPumpValveOperationHours, // u16 Number of hours that DHW pump has been running or DHW valve has been opened + DHWBurnerOperationHours, // u16 Number of hours that burner is in operation during DHW mode + OpenThermVersionMaster, // f8.8 The implemented version of the OpenTherm Protocol Specification in the master. + OpenThermVersionSlave, // f8.8 The implemented version of the OpenTherm Protocol Specification in the slave. + MasterVersion, // u8 / u8 Master product version number and type + SlaveVersion, // u8 / u8 Slave product version number and type +}; + +enum OpenThermStatus { + NOT_INITIALIZED, + READY, + DELAY, + REQUEST_SENDING, + RESPONSE_WAITING, + RESPONSE_START_BIT, + RESPONSE_RECEIVING, + RESPONSE_READY, + RESPONSE_INVALID +}; + +class OpenTherm +{ +public: + OpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false); + volatile OpenThermStatus status; + void begin(void(*handleInterruptCallback)(void)); + void begin(void(*handleInterruptCallback)(void), void(*processResponseCallback)(unsigned long, int)); + bool isReady(); + unsigned long sendRequest(unsigned long request); + bool sendResponse(unsigned long request); + bool sendRequestAync(unsigned long request); + static unsigned long buildRequest(OpenThermMessageType type, OpenThermMessageID id, unsigned int data); + static unsigned long buildResponse(OpenThermMessageType type, OpenThermMessageID id, unsigned int data); + OpenThermResponseStatus getLastResponseStatus(); + const char *statusToString(OpenThermResponseStatus status); + void handleInterrupt(); + void process(); + void end(); + + static bool parity(unsigned long frame); + OpenThermMessageType getMessageType(unsigned long message); + OpenThermMessageID getDataID(unsigned long frame); + const char *messageTypeToString(OpenThermMessageType message_type); + bool isValidRequest(unsigned long request); + bool isValidResponse(unsigned long response); + + //requests + unsigned long buildSetBoilerStatusRequest(bool enableCentralHeating, bool enableHotWater = false, bool enableCooling = false, bool enableOutsideTemperatureCompensation = false, bool enableCentralHeating2 = false); + unsigned long buildSetBoilerTemperatureRequest(float temperature); + unsigned long buildGetBoilerTemperatureRequest(); + unsigned long buildSetHotWaterTemperatureRequest(float temperature); + unsigned long buildSlaveConfigurationRequest(); + + + //responses + static bool isFault(unsigned long response); + static bool isCentralHeatingActive(unsigned long response); + static bool isHotWaterActive(unsigned long response); + static bool isFlameOn(unsigned long response); + static bool isCoolingActive(unsigned long response); + static bool isDiagnostic(unsigned long response); + static uint16_t getUInt(const unsigned long response); + static float getFloat(const unsigned long response); + static unsigned int temperatureToData(float temperature); + + //basic requests + unsigned long setBoilerStatus(bool enableCentralHeating, bool enableHotWater = false, bool enableCooling = false, bool enableOutsideTemperatureCompensation = false, bool enableCentralHeating2 = false); + bool setBoilerTemperature(float temperature); + bool setHotWaterTemperature(float temperature); + float getBoilerTemperature(); + float getReturnTemperature(); + float getModulation(); + float getPressure(); + unsigned char getFault(); + unsigned long getSlaveConfiguration(); + +private: + const int inPin; + const int outPin; + const bool isSlave; + + volatile unsigned long response; + volatile OpenThermResponseStatus responseStatus; + volatile unsigned long responseTimestamp; + volatile byte responseBitIndex; + + int readState(); + void setActiveState(); + void setIdleState(); + void activateBoiler(); + + void sendBit(bool high); + void(*handleInterruptCallback)(); + void(*processResponseCallback)(unsigned long, int); +}; + +#ifndef ICACHE_RAM_ATTR +#define ICACHE_RAM_ATTR +#endif + +#endif // OpenTherm_h \ No newline at end of file diff --git a/lib/TasmotaSerial-2.4.1/README.md b/lib/TasmotaSerial-3.0.0/README.md similarity index 86% rename from lib/TasmotaSerial-2.4.1/README.md rename to lib/TasmotaSerial-3.0.0/README.md index 019aafc07..d2196ed4c 100644 --- a/lib/TasmotaSerial-2.4.1/README.md +++ b/lib/TasmotaSerial-3.0.0/README.md @@ -1,6 +1,7 @@ # TasmotaSerial Implementation of software serial with hardware serial fallback library for the ESP8266 +Implementation of dual UART hardware serial for the ESP32 Allows for several instances to be active at the same time. diff --git a/lib/TasmotaSerial-2.4.1/examples/swsertest/swsertest.ino b/lib/TasmotaSerial-3.0.0/examples/swsertest/swsertest.ino similarity index 100% rename from lib/TasmotaSerial-2.4.1/examples/swsertest/swsertest.ino rename to lib/TasmotaSerial-3.0.0/examples/swsertest/swsertest.ino diff --git a/lib/TasmotaSerial-2.4.1/keywords.txt b/lib/TasmotaSerial-3.0.0/keywords.txt similarity index 96% rename from lib/TasmotaSerial-2.4.1/keywords.txt rename to lib/TasmotaSerial-3.0.0/keywords.txt index 9cf6d825c..f9bde9254 100644 --- a/lib/TasmotaSerial-2.4.1/keywords.txt +++ b/lib/TasmotaSerial-3.0.0/keywords.txt @@ -1,6 +1,6 @@ ####################################### # Syntax Coloring Map for TasmotaSerial -# (esp8266) +# (esp8266 and esp32) ####################################### ####################################### diff --git a/lib/TasmotaSerial-2.4.1/library.json b/lib/TasmotaSerial-3.0.0/library.json similarity index 70% rename from lib/TasmotaSerial-2.4.1/library.json rename to lib/TasmotaSerial-3.0.0/library.json index 64cde09c9..19197cce4 100644 --- a/lib/TasmotaSerial-2.4.1/library.json +++ b/lib/TasmotaSerial-3.0.0/library.json @@ -1,15 +1,17 @@ { "name": "TasmotaSerial", - "version": "2.4.1", + "version": "3.0.0", "keywords": [ "serial", "io", "TasmotaSerial" ], - "description": "Implementation of software serial with hardware serial fallback for ESP8266.", + "description": "Implementation of software serial with hardware serial fallback for ESP8266 and ESP32.", "repository": { "type": "git", "url": "https://github.com/arendst/Tasmota/lib/TasmotaSerial" }, "frameworks": "arduino", - "platforms": "espressif8266" + "platforms": [ + "espressif8266", "espressif32" + ] } diff --git a/lib/TasmotaSerial-2.4.1/library.properties b/lib/TasmotaSerial-3.0.0/library.properties similarity index 71% rename from lib/TasmotaSerial-2.4.1/library.properties rename to lib/TasmotaSerial-3.0.0/library.properties index b326d7404..d1d9e718a 100644 --- a/lib/TasmotaSerial-2.4.1/library.properties +++ b/lib/TasmotaSerial-3.0.0/library.properties @@ -1,9 +1,9 @@ name=TasmotaSerial -version=2.4.1 +version=3.0.0 author=Theo Arends maintainer=Theo Arends -sentence=Implementation of software serial with hardware serial fallback for ESP8266. +sentence=Implementation of software serial with hardware serial fallback for ESP8266 and ESP32. paragraph= category=Signal Input/Output url= -architectures=esp8266 +architectures=esp8266,esp32 diff --git a/lib/TasmotaSerial-2.4.1/src/TasmotaSerial.cpp b/lib/TasmotaSerial-3.0.0/src/TasmotaSerial.cpp similarity index 90% rename from lib/TasmotaSerial-2.4.1/src/TasmotaSerial.cpp rename to lib/TasmotaSerial-3.0.0/src/TasmotaSerial.cpp index 1fad7c0f5..6b41d068c 100644 --- a/lib/TasmotaSerial-2.4.1/src/TasmotaSerial.cpp +++ b/lib/TasmotaSerial-3.0.0/src/TasmotaSerial.cpp @@ -1,5 +1,5 @@ /* - TasmotaSerial.cpp - Minimal implementation of software serial for Tasmota + TasmotaSerial.cpp - Implementation of software serial with hardware serial fallback for Tasmota Copyright (C) 2020 Theo Arends @@ -27,6 +27,8 @@ extern "C" { #include +#ifdef ESP8266 + // for STAGE and pre-2.6, we can have a single wrapper using attachInterruptArg() void ICACHE_RAM_ATTR callRxRead(void *self) { ((TasmotaSerial*)self)->rxRead(); }; @@ -79,6 +81,12 @@ static void (*ISRList[16])() = { tms_isr_15 }; +#else // ESP32 + + static int tasmota_serial_index = 2; // Allow UART2 and UART1 only + +#endif // ESP8266 + TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fallback, int nwmode, int buffer_size) { m_valid = false; @@ -87,12 +95,13 @@ TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fal m_stop_bits = 1; m_nwmode = nwmode; serial_buffer_size = buffer_size; - if (!((isValidGPIOpin(receive_pin)) && (isValidGPIOpin(transmit_pin) || transmit_pin == 16))) { - return; - } m_rx_pin = receive_pin; m_tx_pin = transmit_pin; m_in_pos = m_out_pos = 0; +#ifdef ESP8266 + if (!((isValidGPIOpin(receive_pin)) && (isValidGPIOpin(transmit_pin) || transmit_pin == 16))) { + return; + } if (hardware_fallback && (((3 == m_rx_pin) && (1 == m_tx_pin)) || ((3 == m_rx_pin) && (-1 == m_tx_pin)) || ((-1 == m_rx_pin) && (1 == m_tx_pin)))) { m_hardserial = true; } @@ -120,11 +129,16 @@ TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fal digitalWrite(m_tx_pin, HIGH); } } +#else // ESP32 + if (transmit_pin > 33) { return; } // GPIO34 - GPIO39 are Input only + m_hardserial = true; +#endif // ESP8266 - ESP32 m_valid = true; } TasmotaSerial::~TasmotaSerial() { +#ifdef ESP8266 if (!m_hardserial) { if (m_rx_pin > -1) { detachInterrupt(m_rx_pin); @@ -134,6 +148,7 @@ TasmotaSerial::~TasmotaSerial() } } } +#endif // ESP8266 } bool TasmotaSerial::isValidGPIOpin(int pin) @@ -144,6 +159,7 @@ bool TasmotaSerial::isValidGPIOpin(int pin) bool TasmotaSerial::begin(long speed, int stop_bits) { m_stop_bits = ((stop_bits -1) &1) +1; if (m_hardserial) { +#ifdef ESP8266 Serial.flush(); if (2 == m_stop_bits) { Serial.begin(speed, SERIAL_8N2); @@ -153,6 +169,21 @@ bool TasmotaSerial::begin(long speed, int stop_bits) { if (m_hardswap) { Serial.swap(); } +#else // ESP32 + if (tasmota_serial_index > 0) { // We only support UART1 and UART2 and keep UART0 for debugging + m_uart = tasmota_serial_index; + tasmota_serial_index--; + TSerial = new HardwareSerial(m_uart); + if (2 == m_stop_bits) { + TSerial->begin(speed, SERIAL_8N2, m_rx_pin, m_tx_pin); + } else { + TSerial->begin(speed, SERIAL_8N1, m_rx_pin, m_tx_pin); + } + } else { + m_valid = false; + } +// Serial.printf("TSR: Using UART%d\n", m_uart); +#endif // ESP8266 - ESP32 } else { // Use getCycleCount() loop to get as exact timing as possible m_bit_time = ESP.getCpuFreqMHz() * 1000000 / speed; @@ -168,12 +199,20 @@ bool TasmotaSerial::begin() { } bool TasmotaSerial::hardwareSerial() { +#ifdef ESP8266 return m_hardserial; +#else + return false; // On ESP32 do not mess with Serial0 buffers +#endif } void TasmotaSerial::flush() { if (m_hardserial) { +#ifdef ESP8266 Serial.flush(); +#else + TSerial->flush(); +#endif } else { m_in_pos = m_out_pos = 0; } @@ -181,7 +220,11 @@ void TasmotaSerial::flush() { int TasmotaSerial::peek() { if (m_hardserial) { +#ifdef ESP8266 return Serial.peek(); +#else + return TSerial->peek(); +#endif } else { if ((-1 == m_rx_pin) || (m_in_pos == m_out_pos)) return -1; return m_buffer[m_out_pos]; @@ -191,7 +234,11 @@ int TasmotaSerial::peek() { int TasmotaSerial::read() { if (m_hardserial) { +#ifdef ESP8266 return Serial.read(); +#else + return TSerial->read(); +#endif } else { if ((-1 == m_rx_pin) || (m_in_pos == m_out_pos)) return -1; uint32_t ch = m_buffer[m_out_pos]; @@ -203,7 +250,11 @@ int TasmotaSerial::read() int TasmotaSerial::available() { if (m_hardserial) { +#ifdef ESP8266 return Serial.available(); +#else + return TSerial->available(); +#endif } else { int avail = m_in_pos - m_out_pos; if (avail < 0) avail += serial_buffer_size; @@ -248,7 +299,11 @@ void TasmotaSerial::_fast_write(uint8_t b) { size_t TasmotaSerial::write(uint8_t b) { if (m_hardserial) { +#ifdef ESP8266 return Serial.write(b); +#else + return TSerial->write(b); +#endif } else { if (-1 == m_tx_pin) return 0; if (m_high_speed) { diff --git a/lib/TasmotaSerial-2.4.1/src/TasmotaSerial.h b/lib/TasmotaSerial-3.0.0/src/TasmotaSerial.h similarity index 90% rename from lib/TasmotaSerial-2.4.1/src/TasmotaSerial.h rename to lib/TasmotaSerial-3.0.0/src/TasmotaSerial.h index 3ef4ee43b..42a3f120e 100644 --- a/lib/TasmotaSerial-2.4.1/src/TasmotaSerial.h +++ b/lib/TasmotaSerial-3.0.0/src/TasmotaSerial.h @@ -1,5 +1,5 @@ /* - TasmotaSerial.h - Minimal implementation of software serial for Tasmota + TasmotaSerial.h - Implementation of software serial with hardware serial fallback for Tasmota Copyright (C) 2020 Theo Arends @@ -36,6 +36,10 @@ #include #include +#ifdef ESP32 +#include +#endif + class TasmotaSerial : public Stream { public: TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fallback = 0, int nwmode = 0, int buffer_size = TM_SERIAL_BUFFER_SIZE); @@ -55,6 +59,10 @@ class TasmotaSerial : public Stream { uint32_t getLoopReadMetric(void) const { return m_bit_follow_metric; } +#ifdef ESP32 + uint32_t getUart(void) const { return m_uart; } +#endif + using Print::write; private: @@ -83,6 +91,12 @@ class TasmotaSerial : public Stream { uint8_t *m_buffer; void _fast_write(uint8_t b); // IRAM minimized version + +#ifdef ESP32 + HardwareSerial *TSerial; + int m_uart = 0; +#endif + }; #endif // TasmotaSerial_h diff --git a/lib/Unishox-1.0-shadinger/generator/generator.c b/lib/Unishox-1.0-shadinger/generator/generator.c new file mode 100644 index 000000000..81c46649e --- /dev/null +++ b/lib/Unishox-1.0-shadinger/generator/generator.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2019 Siara Logics (cc) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Arundale R. + * + */ + +// Pre-compute c_95[] and l_95[] + +#include +#include +#include +#include +#include +#include + +typedef unsigned char byte; + +enum {SHX_SET1 = 0, SHX_SET1A, SHX_SET1B, SHX_SET2, SHX_SET3, SHX_SET4, SHX_SET4A}; +char us_vcodes[] = {0, 2, 3, 4, 10, 11, 12, 13, 14, 30, 31}; +char us_vcode_lens[] = {2, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5}; +char us_sets[][11] = + {{ 0, ' ', 'e', 0, 't', 'a', 'o', 'i', 'n', 's', 'r'}, + { 0, 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b', 'g', 'w'}, + {'f', 'y', 'v', 'k', 'q', 'j', 'x', 'z', 0, 0, 0}, + { 0, '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'}, + {'.', ',', '-', '/', '?', '+', ' ', '(', ')', '$', '@'}, + {';', '#', ':', '<', '^', '*', '"', '{', '}', '[', ']'}, + {'=', '%', '\'', '>', '&', '_', '!', '\\', '|', '~', '`'}}; + // {{ 0, ' ', 'e', 0, 't', 'a', 'o', 'i', 'n', 's', 'r'}, + // { 0, 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b', 'g', 'w'}, + // {'f', 'y', 'v', 'k', 'q', 'j', 'x', 'z', 0, 0, 0}, + // { 0, '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'}, + // {'.', ',', '-', '/', '=', '+', ' ', '(', ')', '$', '%'}, + // {'&', ';', ':', '<', '>', '*', '"', '{', '}', '[', ']'}, + // {'@', '?', '\'', '^', '#', '_', '!', '\\', '|', '~', '`'}}; + +unsigned int c_95[95] ; +unsigned char l_95[95] ; + + +void init_coder() { + for (int i = 0; i < 7; i++) { + for (int j = 0; j < 11; j++) { + char c = us_sets[i][j]; + if (c != 0 && c != 32) { + int ascii = c - 32; + //int prev_code = c_95[ascii]; + //int prev_code_len = l_95[ascii]; + switch (i) { + case SHX_SET1: // just us_vcode + c_95[ascii] = (us_vcodes[j] << (16 - us_vcode_lens[j])); + l_95[ascii] = us_vcode_lens[j]; + //checkPreus_vcodes(c, prev_code, prev_code_len, c_95[ascii], l_95[ascii]); + if (c >= 'a' && c <= 'z') { + ascii -= ('a' - 'A'); + //prev_code = c_95[ascii]; + //prev_code_len = l_95[ascii]; + c_95[ascii] = (2 << 12) + (us_vcodes[j] << (12 - us_vcode_lens[j])); + l_95[ascii] = 4 + us_vcode_lens[j]; + } + break; + case SHX_SET1A: // 000 + us_vcode + c_95[ascii] = 0 + (us_vcodes[j] << (13 - us_vcode_lens[j])); + l_95[ascii] = 3 + us_vcode_lens[j]; + //checkPreus_vcodes(c, prev_code, prev_code_len, c_95[ascii], l_95[ascii]); + if (c >= 'a' && c <= 'z') { + ascii -= ('a' - 'A'); + //prev_code = c_95[ascii]; + //prev_code_len = l_95[ascii]; + c_95[ascii] = (2 << 12) + 0 + (us_vcodes[j] << (9 - us_vcode_lens[j])); + l_95[ascii] = 4 + 3 + us_vcode_lens[j]; + } + break; + case SHX_SET1B: // 00110 + us_vcode + c_95[ascii] = (6 << 11) + (us_vcodes[j] << (11 - us_vcode_lens[j])); + l_95[ascii] = 5 + us_vcode_lens[j]; + //checkPreus_vcodes(c, prev_code, prev_code_len, c_95[ascii], l_95[ascii]); + if (c >= 'a' && c <= 'z') { + ascii -= ('a' - 'A'); + //prev_code = c_95[ascii]; + //prev_code_len = l_95[ascii]; + c_95[ascii] = (2 << 12) + (6 << 7) + (us_vcodes[j] << (7 - us_vcode_lens[j])); + l_95[ascii] = 4 + 5 + us_vcode_lens[j]; + } + break; + case SHX_SET2: // 0011100 + us_vcode + c_95[ascii] = (28 << 9) + (us_vcodes[j] << (9 - us_vcode_lens[j])); + l_95[ascii] = 7 + us_vcode_lens[j]; + break; + case SHX_SET3: // 0011101 + us_vcode + c_95[ascii] = (29 << 9) + (us_vcodes[j] << (9 - us_vcode_lens[j])); + l_95[ascii] = 7 + us_vcode_lens[j]; + break; + case SHX_SET4: // 0011110 + us_vcode + c_95[ascii] = (30 << 9) + (us_vcodes[j] << (9 - us_vcode_lens[j])); + l_95[ascii] = 7 + us_vcode_lens[j]; + break; + case SHX_SET4A: // 0011111 + us_vcode + c_95[ascii] = (31 << 9) + (us_vcodes[j] << (9 - us_vcode_lens[j])); + l_95[ascii] = 7 + us_vcode_lens[j]; + } + //checkPreus_vcodes(c, prev_code, prev_code_len, c_95[ascii], l_95[ascii]); + } + } + } + c_95[0] = 16384; + l_95[0] = 3; + +} + +int main(int argv, char *args[]) { + init_coder(); + + printf("uint16_t c_95[95] PROGMEM = {"); + for (uint8_t i = 0; i<95; i++) { + if (i) { printf(", "); } + printf("0x%04X", c_95[i]); + } + printf(" };\n"); + + printf("uint8_t l_95[95] PROGMEM = {"); + for (uint8_t i = 0; i<95; i++) { + if (i) { printf(", "); } + printf("%6d", l_95[i]); + } + printf(" };\n"); + + printf("\n\n"); + + printf("uint16_t c_95[95] PROGMEM = {"); + for (uint8_t i = 0; i<95; i++) { + if (i) { printf(", "); } + printf("%5d", c_95[i]); + } + printf(" };\n"); + + printf("uint8_t l_95[95] PROGMEM = {"); + for (uint8_t i = 0; i<95; i++) { + if (i) { printf(", "); } + printf("%5d", l_95[i]); + } + printf(" };\n"); + + + printf("uint16_t cl_95[95] PROGMEM = {"); + for (uint8_t i = 0; i<95; i++) { + if (i) { printf(", "); } + printf("0x%04X + %2d", c_95[i], l_95[i]); + } + printf(" };\n"); + +} \ No newline at end of file diff --git a/lib/Unishox-1.0-shadinger/generator/remapping.xlsx b/lib/Unishox-1.0-shadinger/generator/remapping.xlsx new file mode 100644 index 000000000..94a82ecde Binary files /dev/null and b/lib/Unishox-1.0-shadinger/generator/remapping.xlsx differ diff --git a/lib/Unishox-1.0-shadinger/library.properties b/lib/Unishox-1.0-shadinger/library.properties new file mode 100644 index 000000000..138b2027c --- /dev/null +++ b/lib/Unishox-1.0-shadinger/library.properties @@ -0,0 +1,8 @@ +name=Unishox Compressor Decompressor highly customized and optimized for ESP8266 and Tasmota +version=1.0 +author=Arundale Ramanathan, Stephan Hadinger +maintainer=Arun , Stephan +sentence=Unishox compression for Tasmota Rules +paragraph=It is based on Unishox hybrid encoding technique. This version has specific Unicode code removed for size. +url=https://github.com/siara-cc/Unishox +architectures=esp8266 diff --git a/lib/Unishox-1.0-shadinger/src/unishox.cpp b/lib/Unishox-1.0-shadinger/src/unishox.cpp new file mode 100644 index 000000000..72c9d3915 --- /dev/null +++ b/lib/Unishox-1.0-shadinger/src/unishox.cpp @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2019 Siara Logics (cc) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Arundale R. + * + */ + +/* + * + * This is a highly modified and optimized version of Unishox + * for Tasmota, aimed at compressing `Rules` which are typically + * short strings from 50 to 500 bytes. + * + * - moved to C++ (but still C-style) + * - c_95[] and l_95[] are pre-computed + * - all arrays in PROGMEM + * - removed all Unicode specific code to get code smaller, Unicode is rare in rules and encoded as pure binary + * - removed prev_lines management to reduce code size, we don't track previous encodings + * - using C++ const instead of #define + * - reusing the Unicode market to encode pure binary, which is 3 bits instead of 9 + * - reverse binary encoding to 255-byte, favoring short encoding for values above 127, typical of Unicode + * - remove 2 bits encoding for Counts, since it could lead to a series of more than 8 consecutive 0-bits and output NULL char. + * Minimum encoding is 5 bits, which means spending 3+1=4 more bits for values in the range 0..3 + * - removed CRLF encoding and reusing entry for RPT, saving 3 bits for repeats. Note: any CR will be binary encded + * - add safeguard to the output size (len_out), note that the compress buffer needs to be 4 bytes larger than actual compressed output. + * This is needed to avoid crash, since output can have ~30 bits + * - combined c_95[] and l_95[] to a single array to save space + * - Changed mapping of some characters in Set3, Set4 and Set4A, favoring frequent characters in rules and javascript + * - Added escape mechanism to ensure we never output NULL char. The marker is 0x2A which looked rare in preliminary tests + * + * @author Stephan Hadinger + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "unishox.h" + +typedef unsigned char byte; +// we squeeze both c_95[] and l_95[] in a sinle array. +// c_95[] uses only the 3 upper nibbles (or 12 most signifcant bits), while the last nibble encodes length (3..13) +uint16_t cl_95[95] PROGMEM = {0x4000 + 3, 0x3F80 + 11, 0x3D80 + 11, 0x3C80 + 10, 0x3BE0 + 12, 0x3E80 + 10, 0x3F40 + 11, 0x3EC0 + 10, 0x3BA0 + 11, 0x3BC0 + 11, 0x3D60 + 11, 0x3B60 + 11, 0x3A80 + 10, 0x3AC0 + 10, 0x3A00 + 9, 0x3B00 + 10, 0x38C0 + 10, 0x3900 + 10, 0x3940 + 11, 0x3960 + 11, 0x3980 + 11, 0x39A0 + 11, 0x39C0 + 11, 0x39E0 + 12, 0x39F0 + 12, 0x3880 + 10, 0x3CC0 + 10, 0x3C00 + 9, 0x3D00 + 10, 0x3E00 + 9, 0x3F00 + 10, 0x3B40 + 11, 0x3BF0 + 12, 0x2B00 + 8, 0x21C0 + 11, 0x20C0 + 10, 0x2100 + 10, 0x2600 + 7, 0x2300 + 11, 0x21E0 + 12, 0x2140 + 11, 0x2D00 + 8, 0x2358 + 13, 0x2340 + 12, 0x2080 + 10, 0x21A0 + 11, 0x2E00 + 8, 0x2C00 + 8, 0x2180 + 11, 0x2350 + 13, 0x2F80 + 9, 0x2F00 + 9, 0x2A00 + 8, 0x2160 + 11, 0x2330 + 12, 0x21F0 + 12, 0x2360 + 13, 0x2320 + 12, 0x2368 + 13, 0x3DE0 + 12, 0x3FA0 + 11, 0x3DF0 + 12, 0x3D40 + 11, 0x3F60 + 11, 0x3FF0 + 12, 0xB000 + 4, 0x1C00 + 7, 0x0C00 + 6, 0x1000 + 6, 0x6000 + 3, 0x3000 + 7, 0x1E00 + 8, 0x1400 + 7, 0xD000 + 4, 0x3580 + 9, 0x3400 + 8, 0x0800 + 6, 0x1A00 + 7, 0xE000 + 4, 0xC000 + 4, 0x1800 + 7, 0x3500 + 9, 0xF800 + 5, 0xF000 + 5, 0xA000 + 4, 0x1600 + 7, 0x3300 + 8, 0x1F00 + 8, 0x3600 + 9, 0x3200 + 8, 0x3680 + 9, 0x3DA0 + 11, 0x3FC0 + 11, 0x3DC0 + 11, 0x3FE0 + 12 }; +// Original version with c/l separate +// uint16_t c_95[95] PROGMEM = {0x4000, 0x3F80, 0x3D80, 0x3C80, 0x3BE0, 0x3E80, 0x3F40, 0x3EC0, 0x3BA0, 0x3BC0, 0x3D60, 0x3B60, 0x3A80, 0x3AC0, 0x3A00, 0x3B00, 0x38C0, 0x3900, 0x3940, 0x3960, 0x3980, 0x39A0, 0x39C0, 0x39E0, 0x39F0, 0x3880, 0x3CC0, 0x3C00, 0x3D00, 0x3E00, 0x3F00, 0x3B40, 0x3BF0, 0x2B00, 0x21C0, 0x20C0, 0x2100, 0x2600, 0x2300, 0x21E0, 0x2140, 0x2D00, 0x2358, 0x2340, 0x2080, 0x21A0, 0x2E00, 0x2C00, 0x2180, 0x2350, 0x2F80, 0x2F00, 0x2A00, 0x2160, 0x2330, 0x21F0, 0x2360, 0x2320, 0x2368, 0x3DE0, 0x3FA0, 0x3DF0, 0x3D40, 0x3F60, 0x3FF0, 0xB000, 0x1C00, 0x0C00, 0x1000, 0x6000, 0x3000, 0x1E00, 0x1400, 0xD000, 0x3580, 0x3400, 0x0800, 0x1A00, 0xE000, 0xC000, 0x1800, 0x3500, 0xF800, 0xF000, 0xA000, 0x1600, 0x3300, 0x1F00, 0x3600, 0x3200, 0x3680, 0x3DA0, 0x3FC0, 0x3DC0, 0x3FE0 }; +// uint8_t l_95[95] PROGMEM = { 3, 11, 11, 10, 12, 10, 11, 10, 11, 11, 11, 11, 10, 10, 9, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 10, 10, 9, 10, 9, 10, 11, 12, 8, 11, 10, 10, 7, 11, 12, 11, 8, 13, 12, 10, 11, 8, 8, 11, 13, 9, 9, 8, 11, 12, 12, 13, 12, 13, 12, 11, 12, 11, 11, 12, 4, 7, 6, 6, 3, 7, 8, 7, 4, 9, 8, 6, 7, 4, 4, 7, 9, 5, 5, 4, 7, 8, 8, 9, 8, 9, 11, 11, 11, 12 }; + +enum {SHX_STATE_1 = 1, SHX_STATE_2}; // removed Unicode state + +enum {SHX_SET1 = 0, SHX_SET1A, SHX_SET1B, SHX_SET2, SHX_SET3, SHX_SET4, SHX_SET4A}; +// changed mapping in Set3, Set4, Set4A to accomodate frequencies in Rules and Javascript +char sets[][11] PROGMEM = + {{ 0, ' ', 'e', 0, 't', 'a', 'o', 'i', 'n', 's', 'r'}, + { 0, 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b', 'g', 'w'}, + {'f', 'y', 'v', 'k', 'q', 'j', 'x', 'z', 0, 0, 0}, + { 0, '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'}, + {'.', ',', '-', '/', '?', '+', ' ', '(', ')', '$', '@'}, + {';', '#', ':', '<', '^', '*', '"', '{', '}', '[', ']'}, + {'=', '%', '\'', '>', '&', '_', '!', '\\', '|', '~', '`'}}; + // {{ 0, ' ', 'e', 0, 't', 'a', 'o', 'i', 'n', 's', 'r'}, + // { 0, 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b', 'g', 'w'}, + // {'f', 'y', 'v', 'k', 'q', 'j', 'x', 'z', 0, 0, 0}, + // { 0, '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'}, + // {'.', ',', '-', '/', '=', '+', ' ', '(', ')', '$', '%'}, + // {'&', ';', ':', '<', '>', '*', '"', '{', '}', '[', ']'}, + // {'@', '?', '\'', '^', '#', '_', '!', '\\', '|', '~', '`'}}; + +// Decoder is designed for using less memory, not speed +// Decode lookup table for code index and length +// First 2 bits 00, Next 3 bits indicate index of code from 0, +// last 3 bits indicate code length in bits +// 0, 1, 2, 3, 4, +char us_vcode[32] PROGMEM = + {2 + (0 << 3), 3 + (3 << 3), 3 + (1 << 3), 4 + (6 << 3), 0, +// 5, 6, 7, 8, 9, 10 + 4 + (4 << 3), 3 + (2 << 3), 4 + (8 << 3), 0, 0, 0, +// 11, 12, 13, 14, 15 + 4 + (7 << 3), 0, 4 + (5 << 3), 0, 5 + (9 << 3), +// 16, 17, 18, 19, 20, 21, 22, 23 + 0, 0, 0, 0, 0, 0, 0, 0, +// 24, 25, 26, 27, 28, 29, 30, 31 + 0, 0, 0, 0, 0, 0, 0, 5 + (10 << 3)}; +// 0, 1, 2, 3, 4, 5, 6, 7, +char us_hcode[32] PROGMEM = + {1 + (1 << 3), 2 + (0 << 3), 0, 3 + (2 << 3), 0, 0, 0, 5 + (3 << 3), +// 8, 9, 10, 11, 12, 13, 14, 15, + 0, 0, 0, 0, 0, 0, 0, 5 + (5 << 3), +// 16, 17, 18, 19, 20, 21, 22, 23 + 0, 0, 0, 0, 0, 0, 0, 5 + (4 << 3), +// 24, 25, 26, 27, 28, 29, 30, 31 + 0, 0, 0, 0, 0, 0, 0, 5 + (6 << 3)}; + +const char ESCAPE_MARKER = 0x2A; // Escape any null char + +const uint16_t TERM_CODE = 0x37C0; // 0b0011011111000000 +const uint16_t TERM_CODE_LEN = 10; +const uint16_t DICT_CODE = 0x0000; +const uint16_t DICT_CODE_LEN = 5; +const uint16_t DICT_OTHER_CODE = 0x0000; // not used +const uint16_t DICT_OTHER_CODE_LEN = 6; +// const uint16_t RPT_CODE = 0x2370; +// const uint16_t RPT_CODE_LEN = 13; +const uint16_t RPT_CODE_TASMOTA = 0x3780; +const uint16_t RPT_CODE_TASMOTA_LEN = 10; +const uint16_t BACK2_STATE1_CODE = 0x2000; // 0010 = back to lower case +const uint16_t BACK2_STATE1_CODE_LEN = 4; +const uint16_t BACK_FROM_UNI_CODE = 0xFE00; +const uint16_t BACK_FROM_UNI_CODE_LEN = 8; +// const uint16_t CRLF_CODE = 0x3780; +// const uint16_t CRLF_CODE_LEN = 10; +const uint16_t LF_CODE = 0x3700; +const uint16_t LF_CODE_LEN = 9; +const uint16_t TAB_CODE = 0x2400; +const uint16_t TAB_CODE_LEN = 7; +// const uint16_t UNI_CODE = 0x8000; // Unicode disabled +// const uint16_t UNI_CODE_LEN = 3; +// const uint16_t UNI_STATE_SPL_CODE = 0xF800; +// const uint16_t UNI_STATE_SPL_CODE_LEN = 5; +// const uint16_t UNI_STATE_DICT_CODE = 0xFC00; +// const uint16_t UNI_STATE_DICT_CODE_LEN = 7; +// const uint16_t CONT_UNI_CODE = 0x2800; +// const uint16_t CONT_UNI_CODE_LEN = 7; +const uint16_t ALL_UPPER_CODE = 0x2200; +const uint16_t ALL_UPPER_CODE_LEN = 8; +const uint16_t SW2_STATE2_CODE = 0x3800; +const uint16_t SW2_STATE2_CODE_LEN = 7; +const uint16_t ST2_SPC_CODE = 0x3B80; +const uint16_t ST2_SPC_CODE_LEN = 11; +const uint16_t BIN_CODE_TASMOTA = 0x8000; +const uint16_t BIN_CODE_TASMOTA_LEN = 3; +// const uint16_t BIN_CODE = 0x2000; +// const uint16_t BIN_CODE_LEN = 9; + +#define NICE_LEN 5 + +// uint16_t mask[] PROGMEM = {0x8000, 0xC000, 0xE000, 0xF000, 0xF800, 0xFC00, 0xFE00, 0xFF00}; +uint8_t mask[] PROGMEM = {0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF}; + +int append_bits(char *out, size_t ol, unsigned int code, int clen, byte state) { + + byte cur_bit; + byte blen; + unsigned char a_byte; + + if (state == SHX_STATE_2) { + // remove change state prefix + if ((code >> 9) == 0x1C) { + code <<= 7; + clen -= 7; + } + //if (code == 14272 && clen == 10) { + // code = 9084; + // clen = 14; + //} + } + while (clen > 0) { + cur_bit = ol % 8; + blen = (clen > 8 ? 8 : clen); + // a_byte = (code & pgm_read_word(&mask[blen - 1])) >> 8; + // a_byte = (code & (pgm_read_word(&mask[blen - 1]) << 8)) >> 8; + a_byte = (code >> 8) & pgm_read_word(&mask[blen - 1]); + a_byte >>= cur_bit; + if (blen + cur_bit > 8) + blen = (8 - cur_bit); + if (out) { // if out == nullptr, then we are in dry-run mode + if (cur_bit == 0) + out[ol / 8] = a_byte; + else + out[ol / 8] |= a_byte; + } + code <<= blen; + ol += blen; + if ((out) && (0 == ol % 8)) { // if out == nullptr, dry-run mode. We miss the escaping of characters in the length + // we completed a full byte + char last_c = out[(ol / 8) - 1]; + if ((0 == last_c) || (ESCAPE_MARKER == last_c)) { + out[ol / 8] = 1 + last_c; // increment to 0x01 or 0x2B + out[(ol / 8) -1] = ESCAPE_MARKER; // replace old value with marker + ol += 8; // add one full byte + } + } + clen -= blen; + } + return ol; +} + +// First five bits are code and Last three bits of codes represent length +// removing last 2 bytes, unused, we will never have values above 600 bytes +// const byte codes[7] = {0x01, 0x82, 0xC3, 0xE5, 0xED, 0xF5, 0xFD}; +// const byte bit_len[7] = {2, 5, 7, 9, 12, 16, 17}; +// const uint16_t adder[7] = {0, 4, 36, 164, 676, 4772, 0}; +byte codes[] PROGMEM = { 0x82, 0xC3, 0xE5, 0xED, 0xF5 }; +byte bit_len[] PROGMEM = { 5, 7, 9, 12, 16 }; +// uint16_t adder[7] PROGMEM = { 0, 32, 160, 672, 4768 }; // no more used + +int encodeCount(char *out, int ol, int count) { + int till = 0; + int base = 0; + for (int i = 0; i < sizeof(bit_len); i++) { + uint32_t bit_len_i = pgm_read_byte(&bit_len[i]); + till += (1 << bit_len_i); + if (count < till) { + byte codes_i = pgm_read_byte(&codes[i]); + ol = append_bits(out, ol, (codes_i & 0xF8) << 8, codes_i & 0x07, 1); + // ol = append_bits(out, ol, (count - pgm_read_word(&adder[i])) << (16 - bit_len_i), bit_len_i, 1); + ol = append_bits(out, ol, (count - base) << (16 - bit_len_i), bit_len_i, 1); + return ol; + } + base = till; + } + return ol; +} + +int matchOccurance(const char *in, int len, int l, char *out, int *ol, byte *state, byte *is_all_upper) { + int j, k; + int longest_dist = 0; + int longest_len = 0; + for (j = l - NICE_LEN; j >= 0; j--) { + for (k = l; k < len && j + k - l < l; k++) { + if (in[k] != in[j + k - l]) + break; + } + // while ((((unsigned char) in[k]) >> 6) == 2) + // k--; // Skip partial UTF-8 matches + //if ((in[k - 1] >> 3) == 0x1E || (in[k - 1] >> 4) == 0x0E || (in[k - 1] >> 5) == 0x06) + // k--; + if (k - l > NICE_LEN - 1) { + int match_len = k - l - NICE_LEN; + int match_dist = l - j - NICE_LEN + 1; + if (match_len > longest_len) { + longest_len = match_len; + longest_dist = match_dist; + } + } + } + if (longest_len) { + if (*state == SHX_STATE_2 || *is_all_upper) { + *is_all_upper = 0; + *state = SHX_STATE_1; + *ol = append_bits(out, *ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, *state); + } + *ol = append_bits(out, *ol, DICT_CODE, DICT_CODE_LEN, 1); + *ol = encodeCount(out, *ol, longest_len); + *ol = encodeCount(out, *ol, longest_dist); + l += (longest_len + NICE_LEN); + l--; + return l; + } + return -l; +} + +// Compress a buffer. +// Inputs: +// - in: non-null pointer to a buffer of bytes to be compressed. Progmem is not valid. Null bytes are valid. +// - len: size of the input buffer. 0 is valid for empty buffer +// - out: pointer to output buffer. out is nullptr, the compressor does a dry-run and reports the compressed size without writing bytes +// - len_out: length in bytes of the output buffer. +// Output: +// - if >= 0: size of the compressed buffer. The output buffer does not contain NULL bytes, and it is not NULL terminated +// - if < 0: an error occured, most certainly the output buffer was not large enough +int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out) { + + char *ptr; + byte bits; + byte state; + + int l, ll, ol; + char c_in, c_next; + byte is_upper, is_all_upper; + + ol = 0; + state = SHX_STATE_1; + is_all_upper = 0; + for (l=0; l 0) { + continue; + } + l = -l; + } + if (state == SHX_STATE_2) { // if Set2 + if ((c_in >= ' ' && c_in <= '@') || + (c_in >= '[' && c_in <= '`') || + (c_in >= '{' && c_in <= '~')) { + } else { + state = SHX_STATE_1; // back to Set1 and lower case + ol = append_bits(out, ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, state); + } + } + + is_upper = 0; + if (c_in >= 'A' && c_in <= 'Z') + is_upper = 1; + else { + if (is_all_upper) { + is_all_upper = 0; + ol = append_bits(out, ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, state); + } + } + + c_next = 0; + if (l+1 < len) + c_next = in[l+1]; + + if (c_in >= 32 && c_in <= 126) { + if (is_upper && !is_all_upper) { + for (ll=l+5; ll>=l && ll 'Z') + break; + } + if (ll == l-1) { + ol = append_bits(out, ol, ALL_UPPER_CODE, ALL_UPPER_CODE_LEN, state); // CapsLock + is_all_upper = 1; + } + } + if (state == SHX_STATE_1 && c_in >= '0' && c_in <= '9') { + ol = append_bits(out, ol, SW2_STATE2_CODE, SW2_STATE2_CODE_LEN, state); // Switch to sticky Set2 + state = SHX_STATE_2; + } + c_in -= 32; + if (is_all_upper && is_upper) + c_in += 32; + if (c_in == 0 && state == SHX_STATE_2) + ol = append_bits(out, ol, ST2_SPC_CODE, ST2_SPC_CODE_LEN, state); // space from Set2 ionstead of Set1 + else { + // ol = append_bits(out, ol, pgm_read_word(&c_95[c_in]), pgm_read_byte(&l_95[c_in]), state); // original version with c/l in split arrays + uint16_t cl = pgm_read_word(&cl_95[c_in]); + ol = append_bits(out, ol, cl & 0xFFF0, cl & 0x000F, state); + } + } else + // if (c_in == 13 && c_next == 10) { // CRLF disabled + // ol = append_bits(out, ol, CRLF_CODE, CRLF_CODE_LEN, state); // CRLF + // l++; + // } else + if (c_in == 10) { + ol = append_bits(out, ol, LF_CODE, LF_CODE_LEN, state); // LF + } else + if (c_in == '\t') { + ol = append_bits(out, ol, TAB_CODE, TAB_CODE_LEN, state); // TAB + } else { + ol = append_bits(out, ol, BIN_CODE_TASMOTA, BIN_CODE_TASMOTA_LEN, state); // Binary, we reuse the Unicode marker which 3 bits instead of 9 + ol = encodeCount(out, ol, (unsigned char) 255 - c_in); + } + + // check that we have some headroom in the output buffer + if (ol / 8 >= len_out - 4) { + return -1; // we risk overflow and crash + } + } + + bits = ol % 8; + if (bits) { + ol = append_bits(out, ol, TERM_CODE, 8 - bits, 1); // 0011 0111 1100 0000 TERM = 0011 0111 11 + } + return ol/8+(ol%8?1:0); +} + +int getBitVal(const char *in, int bit_no, int count) { + char c_in = in[bit_no >> 3]; + if ((bit_no >> 3) && (ESCAPE_MARKER == in[(bit_no >> 3) - 1])) { // if previous byte is a marker, decrement + c_in--; + } + return (c_in & (0x80 >> (bit_no % 8)) ? 1 << count : 0); +} + +// Returns: +// 0..11 +// or -1 if end of stream +int getCodeIdx(char *code_type, const char *in, int len, int *bit_no_p) { + int code = 0; + int count = 0; + do { + // detect marker + if (ESCAPE_MARKER == in[*bit_no_p >> 3]) { + *bit_no_p += 8; // skip marker + } + if (*bit_no_p >= len) + return -1; // invalid state + code += getBitVal(in, *bit_no_p, count); + (*bit_no_p)++; + count++; + uint8_t code_type_code = pgm_read_byte(&code_type[code]); + if (code_type_code && (code_type_code & 0x07) == count) { + return code_type_code >> 3; + } + } while (count < 5); + return 1; // skip if code not found +} + +int getNumFromBits(const char *in, int bit_no, int count) { + int ret = 0; + while (count--) { + if (ESCAPE_MARKER == in[bit_no >> 3]) { + bit_no += 8; // skip marker + } + ret += getBitVal(in, bit_no++, count); + } + return ret; +} + +// const byte bit_len[7] = {5, 2, 7, 9, 12, 16, 17}; +// const uint16_t adder[7] = {4, 0, 36, 164, 676, 4772, 0}; + +// byte bit_len[7] PROGMEM = { 5, 7, 9, 12, 16 }; +// byte bit_len_read[7] PROGMEM = {5, 2, 7, 9, 12, 16 }; +// uint16_t adder_read[7] PROGMEM = {4, 0, 36, 164, 676, 4772, 0}; +// uint16_t adder_read[] PROGMEM = {0, 0, 32, 160, 672, 4768 }; + +// byte bit_len[7] PROGMEM = { 5, 7, 9, 12, 16 }; +// uint16_t adder_read[] PROGMEM = {0, 32, 160, 672, 4768 }; + +// Code size optimized, recalculate adder[] like in encodeCount +int readCount(const char *in, int *bit_no_p, int len) { + int idx = getCodeIdx(us_hcode, in, len, bit_no_p); + if (idx >= 1) idx--; // we skip v = 1 (code '0') since we no more accept 2 bits encoding + if ((idx >= sizeof(bit_len)) || (idx < 0)) return 0; // unsupported or end of stream + + int base; + int till = 0; + byte bit_len_idx; // bit_len[0] + for (uint32_t i = 0; i <= idx; i++) { + base = till; + bit_len_idx = pgm_read_byte(&bit_len[i]); + till += (1 << bit_len_idx); + } + int count = getNumFromBits(in, *bit_no_p, bit_len_idx) + base; + + (*bit_no_p) += bit_len_idx; + return count; +} + +int decodeRepeat(const char *in, int len, char *out, int ol, int *bit_no) { + int dict_len = readCount(in, bit_no, len) + NICE_LEN; + int dist = readCount(in, bit_no, len) + NICE_LEN - 1; + memcpy(out + ol, out + ol - dist, dict_len); + ol += dict_len; + + return ol; +} + +int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out) { + + int dstate; + int bit_no; + byte is_all_upper; + + int ol = 0; + bit_no = 0; + dstate = SHX_SET1; + is_all_upper = 0; + + len <<= 3; // *8, len in bits + out[ol] = 0; + while (bit_no < len) { + int h, v; + char c = 0; + byte is_upper = is_all_upper; + int orig_bit_no = bit_no; + v = getCodeIdx(us_vcode, in, len, &bit_no); // read vCode + if (v < 0) break; // end of stream + h = dstate; // Set1 or Set2 + if (v == 0) { // Switch which is common to Set1 and Set2, first entry + h = getCodeIdx(us_hcode, in, len, &bit_no); // read hCode + if (h < 0) break; // end of stream + if (h == SHX_SET1) { // target is Set1 + if (dstate == SHX_SET1) { // Switch from Set1 to Set1 us UpperCase + if (is_all_upper) { // if CapsLock, then back to LowerCase + is_upper = is_all_upper = 0; + continue; + } + v = getCodeIdx(us_vcode, in, len, &bit_no); // read again vCode + if (v < 0) break; // end of stream + if (v == 0) { + h = getCodeIdx(us_hcode, in, len, &bit_no); // read second hCode + if (h < 0) break; // end of stream + if (h == SHX_SET1) { // If double Switch Set1, the CapsLock + is_all_upper = 1; + continue; + } + } + is_upper = 1; // anyways, still uppercase + } else { + dstate = SHX_SET1; // if Set was not Set1, switch to Set1 + continue; + } + } else + if (h == SHX_SET2) { // If Set2, switch dstate to Set2 + if (dstate == SHX_SET1) // TODO: is this test useful, there are only 2 states possible + dstate = SHX_SET2; + continue; + } + if (h != SHX_SET1) { // all other Sets (why not else) + v = getCodeIdx(us_vcode, in, len, &bit_no); // we changed set, now read vCode for char + if (v < 0) break; // end of stream + } + } + + if (v == 0 && h == SHX_SET1A) { + if (is_upper) { + out[ol++] = 255 - readCount(in, &bit_no, len); // binary + } else { + ol = decodeRepeat(in, len, out, ol, &bit_no); // dist + } + continue; + } + + if (h == SHX_SET1 && v == 3) { + // was Unicode, will do Binary instead + out[ol++] = 255 - readCount(in, &bit_no, len); // binary + continue; + } + if (h < 7 && v < 11) // TODO: are these the actual limits? Not 11x7 ? + c = pgm_read_byte(&sets[h][v]); + if (c >= 'a' && c <= 'z') { + if (is_upper) + c -= 32; // go to UpperCase for letters + } else { // handle all other cases + if (is_upper && dstate == SHX_SET1 && v == 1) + c = '\t'; // If UpperCase Space, change to TAB + if (h == SHX_SET1B) { + if (8 == v) { // was LF or RPT, now only LF + // if (is_upper) { // rpt + // int count = readCount(in, &bit_no, len); + // count += 4; + // char rpt_c = out[ol - 1]; + // while (count--) + // out[ol++] = rpt_c; + // } else { + out[ol++] = '\n'; + // } + continue; + } + if (9 == v) { // was CRLF, now RPT + // out[ol++] = '\r'; // CRLF removed + // out[ol++] = '\n'; + int count = readCount(in, &bit_no, len); + count += 4; + if (ol + count >= len_out) { + return -1; // overflow + } + char rpt_c = out[ol - 1]; + while (count--) + out[ol++] = rpt_c; + continue; + } + if (10 == v) { + break; // TERM, stop decoding + } + } + } + out[ol++] = c; + + if (ol >= len_out) { + return -1; // overflow + } + } + + return ol; + +} diff --git a/lib/Unishox-1.0-shadinger/src/unishox.h b/lib/Unishox-1.0-shadinger/src/unishox.h new file mode 100644 index 000000000..4d6b81641 --- /dev/null +++ b/lib/Unishox-1.0-shadinger/src/unishox.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Siara Logics (cc) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Arundale R. + * + */ +#ifndef unishox +#define unishox + +extern int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out); +extern int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out); + +#endif + diff --git a/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp b/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp index a3c73f3fb..850b2bb8a 100644 --- a/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp +++ b/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp @@ -66,9 +66,13 @@ RA8876::RA8876(int8_t cs,int8_t mosi,int8_t miso,int8_t sclk,int8_t bp) : Render //#define RA8876_CS_LOW digitalWrite(m_csPin, LOW) //#define RA8876_CS_HIGH digitalWrite(m_csPin, HIGH) +#ifdef ESP8266 #define RA8876_CS_LOW GPOC=(1< 76 kb / sec // can use any pin diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h index 99459b198..3fc8ea190 100644 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h @@ -68,8 +68,8 @@ public: int16_t width; int16_t height; - Epd(); - ~Epd(); +// Epd(); +// ~Epd(); int Init(const unsigned char* lut); void Init(int8_t p); void SendCommand(unsigned char command); diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp index e2fe5dd34..8450a9647 100644 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp @@ -130,14 +130,14 @@ int Epd42::Init(void) { SendCommand(PANEL_SETTING); // SendData(0xbf); // KW-BF KWR-AF BWROTP 0f // SendData(0x0b); -// SendData(0x0F); //300x400 Red mode, LUT from OTP -// SendData(0x1F); //300x400 B/W mode, LUT from OTP - SendData(0x3F); //300x400 B/W mode, LUT set by register -// SendData(0x2F); //300x400 Red mode, LUT set by register +// SendData(0x0F); //300x400 Red mode, LUT from OTP +// SendData(0x1F); //300x400 B/W mode, LUT from OTP + SendData(0x3F); //300x400 B/W mode, LUT set by register +// SendData(0x2F); //300x400 Red mode, LUT set by register SendCommand(PLL_CONTROL); SendData(0x3C); // 3A 100Hz 29 150Hz 39 200Hz 31 171Hz 3C 50Hz (default) 0B 10Hz - //SendData(0x0B); //0B is 10Hz + //SendData(0x0B); //0B is 10Hz /* EPD hardware init end */ return 0; } @@ -502,12 +502,15 @@ const unsigned char lut_wb_quick[] PROGMEM = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - - #define PIN_OUT_SET 0x60000304 #define PIN_OUT_CLEAR 0x60000308 +#ifdef ESP32 +#define SSPI_USEANYPIN 1 +#define PWRITE digitalWrite +#else #define PWRITE ydigitalWrite +#endif #ifndef SSPI_USEANYPIN // uses about 2.75 usecs, 365 kb /sec @@ -530,6 +533,7 @@ void ICACHE_RAM_ATTR Epd42::fastSPIwrite(uint8_t d,uint8_t dc) { } #else +#ifndef ESP32 extern void ICACHE_RAM_ATTR ydigitalWrite(uint8_t pin, uint8_t val) { //stopWaveform(pin); if(pin < 16){ @@ -540,6 +544,7 @@ extern void ICACHE_RAM_ATTR ydigitalWrite(uint8_t pin, uint8_t val) { else GP16O &= ~1; } } +#endif // about 13 us => 76 kb / sec // can use any pin void Epd42::fastSPIwrite(uint8_t d,uint8_t dc) { diff --git a/libesp32/ESP32-Mail-Client/LICENSE b/libesp32/ESP32-Mail-Client/LICENSE new file mode 100755 index 000000000..2b2f9d774 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 mobizt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libesp32/ESP32-Mail-Client/README.md b/libesp32/ESP32-Mail-Client/README.md new file mode 100755 index 000000000..bd4cb6328 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/README.md @@ -0,0 +1,2138 @@ +# Mail Client Arduino Library for ESP32 v 2.1.4 + +This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download via SMTP and IMAP servers. + +The library was test and work well with ESP32s based module. + +Copyright (c) 2019 K. Suwatchai (Mobizt). + +![ESP32 Mail](/media/images/esp32-mail.png) + +## Tested Devices + +This following devices were tested and work well. + + * Sparkfun ESP32 Thing + * NodeMCU-32 + * WEMOS LOLIN32 + * TTGO T8 V1.8 + * M5Stack ESP32 + + + +## Features + +* Support Email sending with or without attachment via IMAP server. + +* Support SSL/TLS and STARTTLS protocols. + +* Working with SD card allows large file attachment supported or SPIFFS for small file size attachment. + +* Support Email reading via search and fetch modes (with or without attachment downloads). + +* Support large attachment download via SD card or SPIFFS for small file size attachment. + +* Message text and its header are able to download and save to SD card or SPIFFS. + +* Support Email message fetch and search via IMAP command as in RFC 3501 (depending on IMAP server implementation). + +* Support Ethernet. + +* Built-in Time function. + + + + +## Prerequisites + + +For library version 1.2.0 or newer, STARTTLS was supported and can be enable automatically when port 587 for SMTP was used or can set manually thrugh smtpData.setSTARTTLS(true) and for IMAP through imapData.setSTARTTLS(true). + + + +## Installing + + +Click on **Clone or download** dropdown at the top of repository, select **Download ZIP** and save file on your computer. + +From Arduino IDE, goto menu **Sketch** -> **Include Library** -> **Add .ZIP Library...** and choose **ESP32-Mail-Client-master.zip** that previously downloaded. + +Go to menu **Files** -> **Examples** -> **ESP32-Mail-Client-master** and choose one from examples + + + +## Usages + + +__Declaration and Initialization__ + + + +**The first thing to do to use this library.** + +```C++ + + +//1. Include ESP32 Mail Client library (this library) + +#include "ESP32_MailClient.h" + + +//2. For sending Email, declare Email Sending data object in global scope. +SMTPData smtpData; + +//Or + +//For receiving Email, declare Email receiving data object in global scope. +IMAPData imapData; + + +//3 Setup SMTP server login credential in setup() + +smtpData.setLogin("smtp.gmail.com", 587, "YOUR_EMAIL_ACCOUNT@gmail.com", "YOUR_EMAIL_PASSWORD"); + +//Or + +//Setup IMAP server login credential in setup() + +imapData.setLogin("imap.gmail.com", 993, "YOUR_EMAIL_ACCOUNT@gmail.com", "YOUR_EMAIL_PASSWORD"); + + +//4 For SMTP, set some custom message header (optional) +smtpData.addCustomMessageHeader("Date: Sat, 10 Aug 2019 21:39:56 -0700 (PDT)"); + +smtpData.addCustomMessageHeader("Message-ID: <10000.30000@gmail.com>"); + + +//5 To debug for SMTP + +smtpData.setDebug(true); + +//Or IMAP +imapData.setDebug(true); + + +//6. Send Email +MailClient.sendMail(smtpData)); + +//Or Receive Email + +MailClient.readdMail(imapData)); + + + + + +``` + +___ + + +__Send and Receive Email__ + + +**Compose Email** + +This library allows you to set sender, recipient, importance (priority), CC, BCC and attachment data (binary or from SD card file). + +To set sender, use `smtpData.setSender` e.g. `smtpData.setSender("Jarvis", "SOME_EMAIL_ACCOUNT@SOME_EMAIL.com")`. + +To set priority, use `smtpData.setPriority` e.g. `smtpData.setPriority("High")`. + +To set message subject, use `smtpData.setSubject` e.g. `smtpData.setSubject("ESP32 Send Mail Test")`. + +To set message text, use `smtpData.setMessage` e.g. `smtpData.setMessage("This is plain text message", false);`. + +To set sender, use `smtpData.addRecipient` e.g. `smtpData.addRecipient("SOME_RECIPIENT@SOME_MAIL.com")`. + +To add attachment, use `smtpData.addAttachData` e.g. `smtpData.addAttachData("test.png", "image/png", (uint8_t *)imageData, sizeof imageData);`. + + +When completed all required message data, sending Email `MailClient.sendMail(smtpData)`. + + + +**Get Email** + +To read or receive Email, mailbox folder should be assigned via `imapData.setFolder` e.g. `imapData.setFolder("INBOX")`. + +Then set search criteria to search specified mailbox folder via `imapData.setSearchCriteria` e.g. `imapData.setSearchCriteria("UID SEARCH ALL")`. + +Then set search limit to limut the memory and time usages `imapData.setSearchLimit`. + +From search criteria, UID of message will be available to fetch or read. + +Whit search, body message and attachment can be ignore to reduce the network data usage. + +Begin receive Email `MailClient.readMail(imapData)`. + +From above settings, you will get the following header information + +Messsage UID via `imapData.getUID`. + +Messsage ID via `imapData.getMessageID`. + +Accept Language via `imapData.getAcceptLanguage`. + +Content Language via `imapData.getContentLanguage`. + +Sender via `imapData.getFrom`. + +Sender Charset via `imapData.getFromCharset`. + +Recipient via `imapData.getTo`. + +Recipient Charset via `imapData.getToCharset`. + +CC via `imapData.getCC`. + +CC Charset via `imapData.getCCCharset`. + +Date via `imapData.getDate`. + +Subject via `imapData.getSubject`. + +Subject Charset via `imapData.getSubjectCharset`. + +In addition, by setting search criteria, the following infomation are available. + +Mailbox folder count via `imapData.getFolderCount`. + +Mailbox folder name via `imapData.getFolder`. + +Supported flags count via `imapData.getFlagCount`. + +Supported flags name via `imapData.getFlag`. + +Total message in folder via `imapData.totalMessages`. + +Total message from search result via `imapData.searchCount`. + +Available message from search result (limited by `imapData.setSearchLimit`) via `imapData.availableMessages`. + +When fetch specific message via `imapData.setFetchUID`, availability of attachment file can be determined via +`imapData.getAttachmentCount` for that message which will be automatically download by setting `imapData.setDownloadAttachment(true)` +prior to `MailClient.readMail`. + + + +See [full examples](https://github.com/mobizt/ESP32-Mail-Client/tree/master/examples) for all features usages. + + + + + +## All Supported Functions + + +**These are all functions available from the library and the descriptions.** + + +__Global functions__ + + +**Sending Email via SMTP server.** + +param *`smtpData`* - SMTP Data object to hold data and instances. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool sendMail(SMTPData &smtpData); +``` + + + + +**Reading Email via IMAP server.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool readMail(IMAPData &imapData); +``` + + + + + +**Set the argument to the Flags for message.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +param *`msgUID`* - The UID of message. + +param *`flags`* - The flag list. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool setFlag(IMAPData &imapData, int msgUID, const String &flags); +``` + + + + + +**Add the argument to the Flags for message.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +param *`msgUID`* - The UID of message. + +param *`flags`* - The flag list. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool addFlag(IMAPData &imapData, int msgUID, const String &flags); +``` + + + + + + +**Remove the argument from the Flags for message.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +param *`msgUID`* - The UID of message. + +param *`flags`* - The flag list. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool removeFlag(IMAPData &imapData, int msgUID, const String &flags); +``` + + + + + +**Get the Email sending error details.** + +return - *`Error details string (String object).`* + +```C++ +String smtpErrorReason(); +``` + + + + +**Get the Email reading error details.** + +return - *`Error details string (String object).`* + +```C++ +String imapErrorReason(); +``` + + + + + +**Init SD card with GPIO pins.** + +param *`sck`* -SPI Clock pin. + +param *`miso`* - SPI MISO pin. + +param *`m0si`* - SPI MOSI pin. + +param *`ss`* - SPI Chip/Slave Select pin. + +return *`Boolean`* type status indicates the success of operation. + + +```C++ +bool sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss); +``` + + + + + + +**Init SD card with default GPIO pins.** + +return *`Boolean`* type status indicates the success of operation. + + +```C++ +bool sdBegin(void); +``` + + + + + + + +### IMAPData object call for receiving Email. + + +**Set the IMAP server login credentials.** + +param *`host`* - IMAP server e.g. imap.gmail.com. + +param *`port`* - IMAP port e.g. 993 for gmail. + +param *`loginEmail`* - The Email address of account. + +param *`loginPassword`* - The account password. + +param *`rootCA`* - Root CA certificate base64 string + +```C++ +void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword); + +void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA); + +``` + + + + + +**Set STARTTLS mode to enable STARTTLS protocol.** + +param *`starttls`* - bool flag that enables STARTTLS mode + +```C++ +void setSTARTTLS(bool starttls); +``` + + + + + + +**Set debug print to serial.** + +param *`debug`* - bool flag to enable debug + +```C++ +void setDebug(bool debug); +``` + + + + + + +**Set the mailbox folder to search or fetch.** + +param *`folderName`* - Known mailbox folder. Default value is INBOX + +```C++ +void setFolder(const String &folderName); +``` + + + +**Set the maximum message buffer size for text/html result from search or fetch the message.** + +param *`size`* - The message size in byte. + +```C++ +void setMessageBufferSize(size_t size); +``` + + + +**Set the maximum attachment file size to be downloaded.** + +param *`size`* - The attachement file size in byte. + +```C++ +void setAttachmentSizeLimit(size_t size); +``` + + + +**Set the search criteria used in selected mailbox search.** + +In case of message UID was set via setFetchUID function, search operation will not process, + +you need to clear message UID by calling imapData.setFetchUID("") to clear. + +param *`criteria`* - Search criteria String. + +If folder is not set, the INBOX folder will be used + +Example: + +*`SINCE 10-Feb-2019`* will search all messages that received since 10 Feb 2019 + +*`UID SEARCH ALL`* will seach all message which will return the message UID that can be use later for fetch one or more messages. + + +Search criteria can be consisted these keywords + + +*`ALL`* - All messages in the mailbox; the default initial key for ANDing. + +*`ANSWERED`* - Messages with the \Answered flag set. + +*`BCC`* - Messages that contain the specified string in the envelope structure's BCC field. + +*`BEFORE`* - Messages whose internal date (disregarding time and timezone) is earlier than the specified date. + +*`BODY`* - Messages that contain the specified string in the body of the message. + +*`CC`* - Messages that contain the specified string in the envelope structure's CC field. + +*`DELETED`* - Messages with the \Deleted flag set. + +*`DRAFT`* - Messages with the \Draft flag set. + +*`FLAGGED`* - Messages with the \Flagged flag set. + +*`FROM`* - Messages that contain the specified string in the envelope structure's FROM field. + +*`HEADER`* - Messages that have a header with the specified field-name (as defined in [RFC-2822]) + +and that contains the specified string in the text of the header (what comes after the colon). + +If the string to search is zero-length, this matches all messages that have a header line with + +the specified field-name regardless of the contents. + +*`KEYWORD`* - Messages with the specified keyword flag set. + +*`LARGER`* - Messages with an (RFC-2822) size larger than the specified number of octets. + +*`NEW`* - Messages that have the \Recent flag set but not the \Seen flag. + +This is functionally equivalent to `*"(RECENT UNSEEN)"*`. + +*`NOT`* - Messages that do not match the specified search key. + +*`OLD`* - Messages that do not have the \Recent flag set. This is functionally equivalent to + +*`"NOT RECENT"`* (as opposed to *`"NOT NEW"`*). + +*`ON`* - Messages whose internal date (disregarding time and timezone) is within the specified date. + +*`OR`* - Messages that match either search key. + +*`RECENT`* - Messages that have the \Recent flag set. + +*`SEEN`* - Messages that have the \Seen flag set. + +*`SENTBEFORE`* - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is earlier than the specified date. + +*`SENTON`* - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is within the specified date. + +*`SENTSINCE`* - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is within or later than the specified date. + +*`SINCE`* - Messages whose internal date (disregarding time and timezone) is within or later than the specified date. + +*`SMALLER`* - Messages with an (RFC-2822) size smaller than the specified number of octets. + +*`SUBJECT`* - Messages that contain the specified string in the envelope structure's SUBJECT field. + +*`TEXT`* - Messages that contain the specified string in the header or body of the message. + +*`TO`* - Messages that contain the specified string in the envelope structure's TO field. + +*`UID`* - Messages with unique identifiers corresponding to the specified unique identifier set. + +Sequence set ranges are permitted. + +*`UNANSWERED`* - Messages that do not have the \Answered flag set. + +*`UNDELETED`* - Messages that do not have the \Deleted flag set. + +*`UNDRAFT`* - Messages that do not have the \Draft flag set. + +*`UNFLAGGED`* - Messages that do not have the \Flagged flag set. + +*`UNKEYWORD`* - Messages that do not have the specified keyword flag set. + +*`UNSEEN`* - Messages that do not have the \Seen flag set. + +```C++ +void setSearchCriteria(const String &criteria); +``` + + + + + +**Set to search the unseen message.** + +param *`unseenSearch`* - Boolean flag to enable unseen message search. + +This function will be overridden (omitted) by setFetchUID as setSearchCriteria. + +```C++ +void setSearchUnseenMessage(bool unseenSearch); +``` + + + + + + +**Set the download folder.** + +param *`path`* - Path in SD card. + +All text/html message and attachemnts will be saved to message UID folder which created in defined path + +e.g. *`"/{DEFINED_PATH}/{MESSAGE_UID}/{ATTACHMENT_FILE...}"`*. + +```C++ +void setSaveFilePath(const String &path); +``` + + + + +**Specify message UID to fetch or read.** + +param *`fetchUID`* - The message UID. + +Specify the message UID to fetch (read) only specific message instead of search. + +```C++ +void setFetchUID(const String &fetchUID); +``` + + + + + +**Set storage type to save download attached file or messages.** + +param *`storageType`* - The storage type to save file, MailClientStorageType::SD or MailClientStorageType::SPIFFS + +```C++ +void setFileStorageType(uint8_t storageType); +``` + + + + + + +**Enable/disable attachment download.** + +param *`download`* - Boolean flag to enable/disable attachment download. + +```C++ +void setDownloadAttachment(bool download); +``` + + + +**Enable/disable html message result.** + +param *`htmlFormat`* - Boolean flag to enable/disable html message result. + +The default value is false. + +```C++ +void setHTMLMessage(bool htmlFormat); +``` + + + + +**Enable/disable plain text message result.** + +param *`textFormat`* - Boolean flag to enable/disable plain text message result. + +The default value is true. + +```C++ +void setTextMessage(bool textFormat); +``` + + + + + +**Set the maximum message to search.** + +param *`limit`* - Any number from 0 to 65535. + +The default value is 20. + +```C++ +void setSearchLimit(uint16_t limit); +``` + + + +**Enable/disable recent sort result.** + +param *`recentSort`* - Boolean flag to enable/disable recent message sort result. + +The default value is true. + +```C++ +void setRecentSort(bool recentSort); +``` + + + +**Assign callback function that return status of message fetching or reading.** + +param *`readCallback`* - The function that accept readStatusCallback as parameter. + +```C++ +void setReadCallback(readStatusCallback readCallback); +``` + + + +**Enable/disable attachement download progress while fetching or receiving message.** + +param *`report`* - Boolean flag to enable/disable attachement download progress while fetching or receiving message. + +To get the download status, Callback function should be set via setReadCallback. + +```C++ +void setDownloadReport(bool report); +``` + + + +**Determine only message header is return when search.** + +```C++ +bool isHeaderOnly(); +``` + + + +**Get the sender name/Email for selected message from search result.** + +param *`messageIndex`* - The index of message. + +return *`Sender name/Email String.`* + +```C++ +String getFrom(uint16_t messageIndex); +``` + + + +**Get the sender name/Email charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`Sender name/Email charactor encoding which use for decoding.`* + +```C++ +String getFromCharset(uint16_t messageIndex); +``` + + + +**Get the recipient name/Email for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Recipient name/Email String.`* + +```C++ +String getTo(uint16_t messageIndex); +``` + + + + +**Get the recipient name/Email charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`Recipient name/Email charactor encoding which use in decoding to local language.`* + +```C++ +String getToCharset(uint16_t messageIndex); +``` + + + + +**Get the CC name/Email for selected message index of IMAPData result.** + +param *`messageIndex`* - The index of message. + +return *`CC name/Email String.`* + +```C++ +String getCC(uint16_t messageIndex); +``` + + + +**Get the CC name/Email charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`CC name/Email charactor encoding which use in decoding to local language.`* + +```C++ +String getCCCharset(uint16_t messageIndex); +``` + + + + +**Get the message subject for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Message subject name/Email String.`* + +```C++ +String getSubject(uint16_t messageIndex); +``` + + + + +**Get the message subject charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`Message subject charactor encoding which use in decoding to local language.`* + +```C++ +String getSubjectCharset(uint16_t messageIndex); +``` + + + +**Get the html message for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The html message String or empty String upon the setHTMLMessage was set.`* + +```C++ +String getHTMLMessage(uint16_t messageIndex); +``` + + + +**Get the plain text message for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The plain text message String or empty String upon the setTextMessage was set.`* + +```C++ +String getTextMessage(uint16_t messageIndex); +``` + + +**Get the html message charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`Html message charactor encoding which use in decoding to local language.`* + +```C++ +String getHTMLMessgaeCharset(uint16_t messageIndex); +``` + + + + +**Get the text message charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`The text message charactor encoding which use in decoding to local language.`* + +```C++ +String getTextMessgaeCharset(uint16_t messageIndex); +``` + + + + +**Get the date of received message for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The date String.`* + +```C++ +String getDate(uint16_t messageIndex); +``` + + + + +**Get the message UID for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`UID String that can be use in setFetchUID.`* + +```C++ +String getUID(uint16_t messageIndex); +``` + + + + +**Get the message number for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The message number which vary upon search criteria and sorting.`* + +```C++ +String getNumber(uint16_t messageIndex); +``` + + + + +**Get the message ID for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The message ID String.`* + +```C++ +String getMessageID(uint16_t messageIndex); +``` + + + + +**Get the accept language for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The accept language String.`* + +```C++ +String getAcceptLanguage(uint16_t messageIndex); +``` + + + +**Get the content language of text or html for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The content language String.`* + +```C++ +String getContentLanguage(uint16_t messageIndex); +``` + + + + +**Determine fetch error status for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Fetch error status.`* + +```C++ +bool isFetchMessageFailed(uint16_t messageIndex); +``` + + + +**Get fetch error reason for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Fetch error reason String for selected message index.`* + +```C++ +String getFetchMessageFailedReason(uint16_t messageIndex); +``` + + + + +**Determine the attachment download error for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Fetch status.`* + +```C++ +bool isDownloadAttachmentFailed(uint16_t messageIndex, size_t attachmentIndex); +``` + + + + +**Get the attachment download error reason for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Download error reason String for selected message index.`* + +```C++ +String getDownloadAttachmentFailedReason(uint16_t messageIndex, size_t attachmentIndex); +``` + + + +**Determine the downloaded/saved text message error status for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Text message download status.`* + +```C++ +bool isDownloadMessageFailed(uint16_t messageIndex); +``` + + + + + +**Get the attachment or message downloadeds error reason for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Downloaded error reason String for selected message index.`* + +```C++ +String getDownloadMessageFailedReason(uint16_t messageIndex); +``` + + + + +**Assign the download and decode flags for html message download.** + +param *`download`* - Boolean flag to enable/disable message download. + +return *`decoded`* - Boolean flag to enable/disable html message decoding (support utf8 and base64 encoding). + +```C++ +void saveHTMLMessage(bool download, bool decoded); +``` + + + + +**Assign the download and decode flags for plain text message download.** + +param *`download`* - Boolean flag to enable/disable message download. + +return *`decoded`* - Boolean flag to enable/disable plain text message decoding (support utf8 and base64 encoding). + +```C++ +void saveTextMessage(bool download, bool decoded); +``` + + + + +**Determine the mailbox folder count.** + +return *`Folder count number.`* + +```C++ +uint16_t getFolderCount(); +``` + + + + +**Get the mailbox folder name at selected index.** + +param *`folderIndex`* - Index of folder. + +return *`Folder name String.`* + +Use folder name from this function for fetch or search. + +```C++ +String getFolder(uint16_t folderIndex); +``` + + + +**Determin the number of supported flags count.** + +return *`Flag count number.`* + +```C++ +uint16_t getFlagCount(); +``` + + + + +**Get the flag name for selected index.** + +param *`folderIndex`* - Index of folder. + +return *`Flag name String.`* + +Use flags from this function for fetch or search. + +```C++ +String getFlag(uint16_t flagIndex); +``` + + + + +**Get the number of message in selected mailbox folder.** + +return *`Total message number.`* + +```C++ +size_t totalMessages(); +``` + + + + +**Get the number of message from search result.** + +return *`Search result number.`* + +```C++ +size_t searchCount(); +``` + + + + +**Get the number of message available from search result which less than search limit.** + +return *`Available message number.`* + +```C++ +size_t availableMessages(); +``` + + + + + +**Get the number of attachments for selected message index from search result.** + +param *`messageIndex`* - Index of message. + +return *`Number of attachments`* + +```C++ +size_t getAttachmentCount(uint16_t messageIndex); +``` + + + + + +**Get file name of attachment for selected attachment index and message index from search result.** + +param *`messageIndex`* - Index of message. + +param *`attachmentIndex`* - Index of attachment. + +return *`The attachment file name String at the selected index.`* + +```C++ +String getAttachmentFileName(size_t messageIndex, size_t attachmentIndex); +``` + + + + +**Get the name of attachment for selected attachment index and message index from search result.** + +param *`messageIndex`* - Index of message. + +param *`attachmentIndex`* - Index of attachment. + +return *`The attachment name String at the selected index.`* + +```C++ +String getAttachmentName(size_t messageIndex, size_t attachmentIndex); +``` + + + + +**Get attachment file size for selected attachment index and message index from search result.** + +param *`messageIndex`* - Index of message. + +param *`attachmentIndex`* - Index of attachment. + +return *`The attachment file size in byte at the selected index.`* + +```C++ +int getAttachmentFileSize(size_t messageIndex, size_t attachmentIndex); +``` + + + + +**Get creation date of attachment for selected attachment index and message index from search result.** + +param *`messageIndex`* - Index of message. + +param *`attachmentIndex`* - Index of attachment. + +return *`The attachment creation date String at the selected index.`* + +```C++ +String getAttachmentCreationDate(size_t messageIndex, size_t attachmentIndex); +``` + + + + +**Get attachment file type for selected attachment index and message index from search result.** + +param *`messageIndex`* - Index of message. + +param *`attachmentIndex`* - Index of attachment. + +return *`File MIME String at the selected index`* e.g. image/jpeg. + +```C++ +String getAttachmentType(size_t messageIndex, size_t attachmentIndex); +``` + + + + +**Clear IMAPData object data.** + +```C++ +void empty(); +``` + + + + +### SMTPData object call for sending Email. + + +**Set SMTP server login credentials** + +param *`host`* - SMTP server e.g. smtp.gmail.com + +param *`port`* - SMTP port. + +param *`loginEmail`* - The account Email. + +param *`loginPassword`* - The account password. + +param *`rootCA`* - Root CA certificate base64 string + +```C++ +void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword); + +void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA); + +``` + + + + + +**Set STARTTLS mode to enable STARTTLS protocol.** + +param *`starttls`* - bool flag that enables STARTTLS mode + +```C++ +void setSTARTTLS(bool starttls); +``` + + + + + + +**Set debug print to serial.** + +param *`debug`* - bool flag to enable debug + +```C++ +void setDebug(bool debug); +``` + + + + + + +**Set Sender info** + +param *`fromName`* - Sender's name + +param *`senderEmail`* - Sender's Email. + +```C++ +void setSender(const String &fromName, const String &senderEmail); +``` + + + + + +**Get Sender's name** + +return *`Sender's name String.`* + +```C++ +String getFromName(); +``` + + + + + +**Get Sender's Email.** + +return *`Sender's Email String.`* + +```C++ +String getSenderEmail(); +``` + + + + +**Set Email priority or importance** + +param *`priority`* - Number from 1 to 5, 1 for highest, 3 for normal and 5 for lowest priority + +```C++ +void setPriority(int priority); +``` + + + + + +**Set Email priority or importance.** + +param *`priority`* - String (High, Normal or Low) + +```C++ +void setPriority(const String &priority); +``` + + + + +**Get Email priority** + +return *`Number`* represents Email priority (1 for highest, 3 for normal, 5 for low priority). + +```C++ +uint8_t getPriority(); +``` + + + + +**Add one or more recipient** + +param *`email`* - Recipient Email String of one recipient. + +To add multiple recipients, call addRecipient for each recipient. + +```C++ +void addRecipient(const String &email); +``` + + + + +**Remove recipient** + +param *`email`* - Recipient Email String. + +```C++ +void removeRecipient(const String &email); +``` + + + + +**Remove recipient** + +param *`index`* - Index of recipients in Email object that previously added. + +```C++ +void removeRecipient(uint8_t index); +``` + + + + +**Clear all recipients** +```C++ +void clearRecipient(); +``` + + + + +**Get one recipient** + +param *`index`* - Index of recipients. + +return *`Recipient Email`* String at the index. + +```C++ +String getRecipient(uint8_t index); +``` + + + + + +**Get number of recipients** + +return *`Number`* of recipients. + +```C++ +uint8_t recipientCount(); +``` + + + + +**Set the Email subject.** + +param *`subject`* - The subject. + +```C++ +void setSubject(const String &subject); +``` + + + + +**Get the Email subject.** + +return *`Subject String.`* + +```C++ +String getSubject(); +``` + + + + +**Set the Email message** + +param *`message`* - The message can be in normal text or html format. + +param *`htmlFormat`* - The html format flag, True for send the message as html format + +```C++ +void setMessage(const String &message, bool htmlFormat); +``` + + + + +**Get the message** + +return *`Message String.`* + +```C++ +String getMessage(); +``` + + + + +**Determine message is being send in html format.** + +return *`Boolean`* status. + +```C++ +bool htmlFormat(); +``` + + + + + +**Add Carbon Copy (CC) Email.** + +param *`email`* - The CC Email String. + +```C++ +void addCC(const String &email); +``` + + + + + +**Remove specified Carbon Copy (CC) Email** + +param *`email`* - The CC Email String to remove. + +```C++ +void removeCC(const String &email); +``` + + + + + +**Remove specified Carbon Copy (CC) Email** + +param *`index`* - The CC Email index to remove. + +```C++ +void removeCC(uint8_t index); +``` + + + + + +**Clear all Carbon Copy (CC) Emails** + +```C++ +void clearCC(); +``` + + + + + +**Get Carbon Copy (CC) Email at specified index.** + +param *`index`* - The CC Email index to get. +return *`The CC Email string`* at the index. + +```C++ +String getCC(uint8_t index); +``` + + + + + +**Get the number of Carbon Copy (CC) Email.** + +return *`Number`* of CC Emails. + +```C++ +uint8_t ccCount(); +``` + + + + + +**Add Blind Carbon Copy (BCC) Email** + +param *`email`* - The BCC Email String. + +```C++ +void addBCC(const String &email); +``` + + + + + +**Remove specified Blind Carbon Copy (BCC) Email** + +param *`email`* - The BCC Email String to remove. + +```C++ +void removeBCC(const String &email); +``` + + + + + +**Remove specified Blind Carbon Copy (BCC) Email** + +param *`index`* - The BCC Email index to remove. + +```C++ +void removeBCC(uint8_t index); +``` + + + + + + +**Clear all Blind Carbon Copy (BCC) Emails** + +```C++ +void clearBCC(); +``` + + + + + +**Get Blind Carbon Copy (BCC) Email at specified index.** + +param *`index`* - The BCC Email index to get. + +return *`The BCC Email string`* at the index. + +```C++ +String getBCC(uint8_t index); +``` + + + + + +**Get the number of Blind Carbon Copy (BCC) Email** + +return *`Number`* of BCC Emails. + +```C++ +uint8_t bccCount(); +``` + + + + + +**Add attchement data (binary) from internal memory (flash or ram).** + +param *`fileName`* - The file name String that recipient can be saved. + +param *`mimeType`* - The MIME type of file (image/jpeg, image/png, text/plain...). Can be empty String. + +param *`data`* - The byte array of data (uint8_t). + +param *`size`* - The data length in byte. + +```C++ +void addAttachData(const String &fileName, const String &mimeType, uint8_t *data, uint16_t size); +``` + + + + + +**Remove specified attachment data.** + +param *`fileName`* - The file name of the attachment data to remove. + +```C++ +void removeAttachData(const String &fileName); +``` + + + + + +**Remove specified attachment data.** + +param *`index`* - The index of the attachment data (count only data type attachment) to remove. + +```C++ +void removeAttachData(uint8_t index); +``` + + + + +**Get the number of attachment data.** + +return *`Number`* of attach data. + +```C++ +uint8_t attachDataCount(); +``` + + + +**Add attchement file from SD card** + +param *`fileName`* - The file name String that recipient can be saved. + +param *`mimeType`* - The MIME type of file (image/jpeg, image/png, text/plain...). Can be omitted. + +```C++ +void addAttachFile(const String &filePath, const String &mimeType = ""); +``` + + + + +**Remove specified attachment file from Email object.** + +param *`fileName`* - The file name of the attachment file to remove. + +```C++ +void removeAttachFile(const String &filePath); +``` + + + + +**Set storage type for all attach files.** + +param *`storageType`* - The storage type to read attach file, MailClientStorageType::SD or MailClientStorageType::SPIFFS. + +```C++ +void setFileStorageType(uint8_t storageType); +``` + + + + + + +**Remove specified attachment file.** + +param *`index`* - The index of the attachment file (count only file type attachment) to remove. + +```C++ +void removeAttachFile(uint8_t index); +``` + + + + +**Clear all attachment data.** + +```C++ +void clearAttachData(); +``` + + + + +**Clear all attachment file.** + +```C++ +void clearAttachFile(); +``` + + + + +**Clear all attachments (both data and file type attachments).** + +```C++ +void clearAttachment(); +``` + + + + +**Get number of attachments (both data and file type attachments).** + +return *`Number`* of all attachemnts. + +```C++ +uint8_t attachFileCount(); +``` + + + + + +**Add one or more custom message header field.** + +param *`field`* - custom header String inform of FIELD: VALUE + +This header field will add to message header. + +```C++ +void addCustomMessageHeader(const String &field); +``` + + + + + + +**Remove one custom message header field that previously added..** + +param *`field`* - custom custom message header field String to remove. + +```C++ +void removeCustomMessageHeader(const String &field); +``` + + + + + +**Remove one custom message header field that previously added by its index.** + +param *`index`* - custom message header field index (number) to remove. + + +```C++ +void removeCustomMessageHeader(uint8_t index); +``` + + + + + +**Clear all ccustom message header field that previously added.** + +```C++ +void clearCustomMessageHeader(); +``` + + + + + + +**Get the number of custom message header field that previously added.** + +return *`Number`* of custom message header field. + +```C++ +uint8_t CustomMessageHeaderCount(); +``` + + + + + + +**Get custom message header field that previously added by index.** + +param *`index`* - The custom message header field index to get. + +return *`The custom message header field string at the index`*. + +```C++ +String getCustomMessageHeader(uint8_t index); +``` + + + + + +**Clear all data from Email object to free memory.** + +```C++ +void empty(); +``` + + + + + +**Set the Email sending status callback function to Email object.** + +param *`sendCallback`* - The callback function that accept the sendStatusCallback param. + +```C++ +void setSendCallback(sendStatusCallback sendCallback); +``` + + + + +__MailClient.Time functions__ + + +**Get the time from NTP server and set to device.** + +param *`gmtOffset`* - The GMT time offset in hour. + +param *`daylightOffset`* - The Daylight time offset in hour. + +return - *`Boolean`* type status indicates the success of operation. + +This requires internet connectivity. + +```C++ +bool setClock(float gmtOffset, float daylightOffset); +``` + + + + + + +**Get the Unix time.** + +return - *`uint32_t`* value of current Unix time. + +```C++ +uint32_t getUnixTime(); +``` + + + + + + +**Get timestamp from defined year, month, date, hour, minute, and second.** + +param *`year`* - Year. +param *`mon`* - Month (1 to 12). +param *`date`* - Date. +param *`hour`* - Hour. +param *`mins`* - Minute. +param *`sec`* - Second. + +return - *`time_t`* value of timestamp. + +```C++ +time_t getTimestamp(int year, int mon, int date, int hour, int mins, int sec); +``` + + + + + + +**Get current year.** + +return - *`int`* value of current year. + +```C++ +int getYear(); +``` + + + + + + +**Get current month.** + +return - *`int`* value of current month. + +```C++ +int getMonth(); +``` + + + + + +**Get current date.** + +return - *`int`* value of current date. + +```C++ +int getDay(); +``` + + + + + + +**Get current day of week.** + +return - *`int`* value of day of week. + +1 for sunday and 7 for saturday + +```C++ +int getDayOfWeek(); +``` + + + + + +**Get current day of week in String.** + +return - *`String`* value of day of week. + +Returns sunday, monday, tuesday, wednesday, thurseday, friday and saturday. + +```C++ +String getDayOfWeekString(); +``` + + + + + + +**Get current hour.** + +return - *`int`* value of current hour (0 to 23). + +```C++ +int getHour(); +``` + + + + + + +**Get current minute.** + +return - *`int`* value of current minute (0 to 59). + +```C++ +int getMin(); +``` + + + + + + +**Get current second.** + +return - *`int`* value of current second (0 to 59). + +```C++ +int getSecond(); +``` + + + + + + + +**Get the total days of current year.** + +return - *`int`* value of total days of current year. + +```C++ +int getNumberOfDayThisYear(); +``` + + + + + + +**Get the total days of from January 1, 1970 to specific date.** + +param *`year`* - Year from 1970. +param *`mon`* - Month (1 to 12). +param *`date`* - Date. + +return - *`int`* value of total days. + +```C++ +int getTotalDays(int year, int month, int day); +``` + + + + + +**Get the day of week from specific date.** + +param *`year`* - Year. +param *`month`* - Month (1 to 12). +param *`day`* - Date. + +return - *`int`* value of day of week. + +1 for sunday and 7 for saturday + +```C++ +int dayofWeek(int year, int month, int day); +``` + + + + + + +**Get the second of current hour.** + +return - *`int`* value of current second. + +```C++ +int getCurrentSecond(); +``` + + + + + +**Get the current timestamp.** + +return - *`uint64_t`* value of current timestamp. + +```C++ +uint64_t getCurrentTimestamp(); +``` + + + + + + +**Get time (year, month, day, hour, minute, and second) from second counted from January 1, 1970.** + +param *`secCount`* - The seconds from January 1, 1970 00.00. +param *`yrs`* - The return year. +param *`months`* - The return month. +param *`days`* - The return day. +param *`hr`* - The return hour. +param *`min`* - The return minute. +param *`sec`* - The return second. + +```C++ +void getTimeFromSec(int secCount, int &yrs, int &months, int &days, int &hr, int &min, int &sec); +``` + + + + + + +## License + +The MIT License (MIT) + +Copyright (c) 2019 K. Suwatchai (Mobizt) + + +Permission is hereby granted, free of charge, to any person returning a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + diff --git a/libesp32/ESP32-Mail-Client/examples/Receive_email/Receive_email.ino b/libesp32/ESP32-Mail-Client/examples/Receive_email/Receive_email.ino new file mode 100755 index 000000000..4c3540898 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/examples/Receive_email/Receive_email.ino @@ -0,0 +1,278 @@ +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +//To receive Email for Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + +/* + =========================================================================================================================== + To prevent stack overrun in case of you want to download email attachments in IMAP readMail, + increase the stack size in app_main() in esp32 main.cpp will help by change the stack size from 8192 to any more value + as following + + xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE); + to + xTaskCreatePinnedToCore(loopTask, "loopTask", 16384, NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE); + + For Arduino, file esp32's main.cpp is at C:\Users\USER_NAME\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.1\cores\esp32\main.cpp + And for platformIO, that file is at C:\Users\USER_NAME\.platformio\packages\framework-arduinoespressif32\cores\esp32\main.cpp + =========================================================================================================================== + +*/ + +#include +#include "ESP32_MailClient.h" +#include "SD.h" + +#define WIFI_SSID "YOUR_WIFI_SSID" +#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" + + + +//The Email Reading data object contains config and data that received +IMAPData imapData; + +//Callback function to get the Email reading status +void readCallback(ReadStatus info); + +//List all files in SD card +void printDirectory(File &dir, int depth); + +void readEmail(); + +unsigned long lastTime = 0; + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + Serial.println(); + + MailClient.sdBegin(); + //MailClient.sdBegin(14,2,15,13); //SCK, MISO, MOSI,SS for TTGO T8 v1.7 or 1.8 + + File dir = SD.open("/"); + + printDirectory(dir, 0); + + Serial.println(); + + imapData.setLogin("imap.gmail.com", 993, "YOUR_EMAIL_ACCOUNT@gmail.com", "YOUR_EMAIL_PASSWORD"); + imapData.setFolder("INBOX"); + + //Clear fetch UID + //If fetch UID was set, no search is perform. + imapData.setFetchUID(""); + + //imapData.setSearchCriteria("UID SINCE 10-Feb-2019"); + //imapData.setSearchCriteria("UID 700:*"); + //imapData.setSearchCriteria("UID SEARCH NOT SEEN"); + //imapData.setSearchCriteria("UID SEARCH UNSEEN"); + imapData.setSearchCriteria("UID SEARCH ALL"); + + //To fetch or read one message UID = 320 + //imapData.setFechUID("320"); + + //Set SD folder to save download messages and attachments + imapData.setSaveFilePath("/email_data"); + + //Save attachament + imapData.setDownloadAttachment(true); + + //Set fetch/search result to return html message + imapData.setHTMLMessage(true); + + //Set fetch/search result to return text message + imapData.setTextMessage(true); + + //Set to save html message in SD card with decoded content. + imapData.saveHTMLMessage(true, true); + + //Set to save text message in SD card with decoded content. + imapData.saveTextMessage(true, true); + + //Set the maximum result when search criteria was set. + imapData.setSearchLimit(10); + + //Set the sort order of returning message upon most recent received email. + imapData.setRecentSort(true); + + //Set the return tex/html message size in byte. + imapData.setMessageBufferSize(200); + + //Set the maximum attachment size 5 MB (each file) + imapData.setAttachmentSizeLimit(1024 * 1024 * 5); + + //Set the Email receive callback function. + imapData.setReadCallback(readCallback); + + //Set to get attachment downloading progress status. + imapData.setDownloadReport(true); + + //Set the storage types to save download attachments or messages (SD is default) + //imapData.setFileStorageType(MailClientStorageType::SPIFFS) + imapData.setFileStorageType(MailClientStorageType::SD); + + MailClient.readMail(imapData); +} + +void readEmail() +{ + + Serial.println(); + Serial.println("Read Email..."); + + imapData.setFetchUID("10"); + imapData.setSearchCriteria(""); + MailClient.readMail(imapData); + + imapData.setFetchUID("11"); + imapData.setSearchCriteria(""); + MailClient.readMail(imapData); + + imapData.setFetchUID("12"); + imapData.setSearchCriteria(""); + MailClient.readMail(imapData); +} + +void loop() +{ + + if (millis() - lastTime > 1000 * 60 * 3) + { + + lastTime = millis(); + Serial.println(ESP.getFreeHeap()); + + readEmail(); + } +} + +//Callback function to get the Email reading status +void readCallback(ReadStatus msg) +{ + //Print the current status + Serial.println("INFO: " + msg.info()); + + if (msg.status() != "") + Serial.println("STATUS: " + msg.status()); + + //Show the result when reading finished + if (msg.success()) + { + + for (int i = 0; i < imapData.availableMessages(); i++) + { + Serial.println("================="); + + //Search result number which varied upon search crieria + Serial.println("Messsage Number: " + imapData.getNumber(i)); + + //UID only available when assigned UID keyword in setSearchCriteria + //e.g. imapData.setSearchCriteria("UID SEARCH ALL"); + Serial.println("Messsage UID: " + imapData.getUID(i)); + Serial.println("Messsage ID: " + imapData.getMessageID(i)); + Serial.println("Accept Language: " + imapData.getAcceptLanguage(i)); + Serial.println("Content Language: " + imapData.getContentLanguage(i)); + Serial.println("From: " + imapData.getFrom(i)); + Serial.println("From Charset: " + imapData.getFromCharset(i)); + Serial.println("To: " + imapData.getTo(i)); + Serial.println("To Charset: " + imapData.getToCharset(i)); + Serial.println("CC: " + imapData.getCC(i)); + Serial.println("CC Charset: " + imapData.getCCCharset(i)); + Serial.println("Date: " + imapData.getDate(i)); + Serial.println("Subject: " + imapData.getSubject(i)); + Serial.println("Subject Charset: " + imapData.getSubjectCharset(i)); + + //If setHeaderOnly to false; + if (!imapData.isHeaderOnly()) + { + Serial.println("Text Message: " + imapData.getTextMessage(i)); + Serial.println("Text Message Charset: " + imapData.getTextMessgaeCharset(i)); + Serial.println("HTML Message: " + imapData.getHTMLMessage(i)); + Serial.println("HTML Message Charset: " + imapData.getHTMLMessgaeCharset(i)); + if (imapData.isFetchMessageFailed(i)) + Serial.println("Fetch Error: " + imapData.getFetchMessageFailedReason(i)); + + if (imapData.isDownloadMessageFailed(i)) + Serial.println("Save Content Error: " + imapData.getDownloadMessageFailedReason(i)); + + if (imapData.getAttachmentCount(i) > 0) + { + + Serial.println("**************"); + Serial.println("Attachment: " + String(imapData.getAttachmentCount(i)) + " file(s)"); + + for (int j = 0; j < imapData.getAttachmentCount(i); j++) + { + Serial.println("File Index: " + String(j + 1)); + Serial.println("Filename: " + imapData.getAttachmentFileName(i, j)); + Serial.println("Name: " + imapData.getAttachmentName(i, j)); + Serial.println("Size: " + String(imapData.getAttachmentFileSize(i, j))); + Serial.println("Type: " + imapData.getAttachmentType(i, j)); + Serial.println("Creation Date: " + imapData.getAttachmentCreationDate(i, j)); + if (imapData.isDownloadAttachmentFailed(i, j)) + Serial.println("Download Attachment Error: " + imapData.getDownloadAttachmentFailedReason(i, j)); + } + } + } + + Serial.println(); + } + } +} + +//List all files in SD card +void printDirectory(File &dir, int depth) +{ + while (true) + { + File entry = dir.openNextFile(); + if (!entry) + break; + + for (uint8_t i = 0; i < depth; i++) + Serial.print("| "); + + std::string name = entry.name(); + if (entry.isDirectory()) + { + Serial.print("+----" + String(name.substr(name.find_last_of("/\\") + 1).c_str()) + "\r\n"); + printDirectory(entry, depth + 1); + } + else + { + Serial.print("+--" + String(name.substr(name.find_last_of("/\\") + 1).c_str())); + Serial.print("\t\t\t("); + Serial.print(entry.size(), DEC); + Serial.println(")"); + } + entry.close(); + } +} diff --git a/libesp32/ESP32-Mail-Client/examples/Send_email/Send_email.ino b/libesp32/ESP32-Mail-Client/examples/Send_email/Send_email.ino new file mode 100755 index 000000000..4d4a5b1aa --- /dev/null +++ b/libesp32/ESP32-Mail-Client/examples/Send_email/Send_email.ino @@ -0,0 +1,180 @@ + + +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +//To receive Email for Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + + +#include +#include "ESP32_MailClient.h" +#include "SD.h" + +//For demo only +#include "image.h" + +#define WIFI_SSID "YOUR_WIFI_SSID" +#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" + + +//The Email Sending data object contains config and data to send +SMTPData smtpData; + +//Callback function to get the Email sending status +void sendCallback(SendStatus info); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + + Serial.println("Mounting SD Card..."); + + if (SD.begin()) // MailClient.sdBegin(14,2,15,13) for TTGO T8 v1.7 or 1.8 + { + + Serial.println("Preparing attach file..."); + + File file = SD.open("/text_file.txt", FILE_WRITE); + file.print("Hello World!\r\nHello World!"); + file.close(); + + file = SD.open("/binary_file.dat", FILE_WRITE); + + static uint8_t buf[512]; + + buf[0] = 'H'; + buf[1] = 'E'; + buf[2] = 'A'; + buf[3] = 'D'; + file.write(buf, 4); + + size_t i; + memset(buf, 0xff, 512); + for (i = 0; i < 2048; i++) + { + file.write(buf, 512); + } + + buf[0] = 'T'; + buf[1] = 'A'; + buf[2] = 'I'; + buf[3] = 'L'; + file.write(buf, 4); + + file.close(); + } + else + { + Serial.println("SD Card Monting Failed"); + } + + Serial.println(); + + + Serial.println("Sending email..."); + + //Set the Email host, port, account and password + smtpData.setLogin("outlook.office365.com", 587, "YOUR_EMAIL_ACCOUNT@outlook.com", "YOUR_EMAIL_PASSWORD"); + + //For library version 1.2.0 and later which STARTTLS protocol was supported,the STARTTLS will be + //enabled automatically when port 587 was used, or enable it manually using setSTARTTLS function. + //smtpData.setSTARTTLS(true); + + //Set the sender name and Email + smtpData.setSender("ESP32", "SOME_EMAIL_ACCOUNT@SOME_EMAIL.com"); + + //Set Email priority or importance High, Normal, Low or 1 to 5 (1 is highest) + smtpData.setPriority("High"); + + //Set the subject + smtpData.setSubject("ESP32 SMTP Mail Sending Test"); + + //Set the message - normal text or html format + smtpData.setMessage("
Hello World! - From ESP32
", true); + + //Add recipients, can add more than one recipient + smtpData.addRecipient("SOME_RECIPIENT@SOME_MAIL.com"); + + + + //Add attachments, can add the file or binary data from flash memory, file in SD card + //Data from internal memory + smtpData.addAttachData("firebase_logo.png", "image/png", (uint8_t *)dummyImageData, sizeof dummyImageData); + + //Add attach files from SD card + //Comment these two lines, if no SD card connected + //Two files that previousely created. + smtpData.addAttachFile("/binary_file.dat"); + smtpData.addAttachFile("/text_file.txt"); + + + //Add some custom header to message + //See https://tools.ietf.org/html/rfc822 + //These header fields can be read from raw or source of message when it received) + smtpData.addCustomMessageHeader("Date: Sat, 10 Aug 2019 21:39:56 -0700 (PDT)"); + //Be careful when set Message-ID, it should be unique, otherwise message will not store + //smtpData.addCustomMessageHeader("Message-ID: "); + + //Set the storage types to read the attach files (SD is default) + //smtpData.setFileStorageType(MailClientStorageType::SPIFFS); + smtpData.setFileStorageType(MailClientStorageType::SD); + + + + smtpData.setSendCallback(sendCallback); + + //Start sending Email, can be set callback function to track the status + if (!MailClient.sendMail(smtpData)) + Serial.println("Error sending Email, " + MailClient.smtpErrorReason()); + + //Clear all data from Email object to free memory + smtpData.empty(); + +} + +void loop() +{ +} + +//Callback function to get the Email sending status +void sendCallback(SendStatus msg) +{ + //Print the current status + Serial.println(msg.info()); + + //Do something when complete + if (msg.success()) + { + Serial.println("----------------"); + } +} + diff --git a/libesp32/ESP32-Mail-Client/examples/Send_email/image.h b/libesp32/ESP32-Mail-Client/examples/Send_email/image.h new file mode 100755 index 000000000..4b8b3542d --- /dev/null +++ b/libesp32/ESP32-Mail-Client/examples/Send_email/image.h @@ -0,0 +1,1074 @@ +#include + +static const uint8_t dummyImageData[] PROGMEM = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, +0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x02, 0x58, 0x08, 0x06, 0x00, 0x00, 0x00, 0x9A, 0x76, 0x82, +0x70, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, +0x65, 0x00, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x49, 0x6D, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, +0x64, 0x79, 0x71, 0xC9, 0x65, 0x3C, 0x00, 0x00, 0x42, 0x9A, 0x49, 0x44, 0x41, 0x54, 0x78, 0xDA, +0xEC, 0xDD, 0x7F, 0x90, 0x6C, 0xD9, 0x41, 0xD8, 0xF7, 0xD3, 0xF3, 0xE6, 0xFD, 0xD8, 0xDD, 0xA7, +0xDD, 0xD1, 0xB2, 0x20, 0x88, 0x70, 0x5E, 0x63, 0xB0, 0xB4, 0x20, 0xE1, 0x37, 0x26, 0x50, 0x05, +0x68, 0x57, 0xAF, 0x57, 0x3F, 0xA1, 0x70, 0x15, 0x4A, 0x59, 0x80, 0x88, 0xA9, 0xF8, 0x29, 0x02, +0x1B, 0x12, 0x5C, 0xBB, 0x40, 0x28, 0x48, 0x19, 0x67, 0xA5, 0x42, 0xC6, 0x8A, 0xA3, 0x64, 0xE5, +0x18, 0x63, 0x91, 0x22, 0x48, 0xB8, 0xCA, 0x95, 0xC4, 0x71, 0x2C, 0x1C, 0x40, 0x6F, 0x77, 0xE3, +0x18, 0xA5, 0xE2, 0x3F, 0x53, 0x05, 0x55, 0xB6, 0x14, 0x6C, 0x09, 0xF4, 0x0B, 0xAD, 0x24, 0x76, +0xB5, 0xDA, 0xDD, 0x37, 0x3F, 0x7B, 0xBA, 0xEF, 0x49, 0x9F, 0xDB, 0x7D, 0x7B, 0x6E, 0xFF, 0x98, +0x79, 0x3D, 0x33, 0xDD, 0x3D, 0x7D, 0xBB, 0x3F, 0x9F, 0xD2, 0xCC, 0x9B, 0xD7, 0x33, 0xD3, 0xD3, +0x73, 0x67, 0xDE, 0xD5, 0xF9, 0xEE, 0xE9, 0x73, 0x6E, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0F, 0x35, 0x87, 0x00, +0x58, 0x35, 0xF1, 0xFF, 0xFC, 0xBE, 0x46, 0xEF, 0xCD, 0x17, 0x6A, 0x6F, 0x7E, 0xF2, 0x0F, 0x1D, +0x11, 0x00, 0x10, 0x20, 0x00, 0xD3, 0x8E, 0x8E, 0x9B, 0x9D, 0x3F, 0x1E, 0xED, 0xBC, 0x6C, 0x1E, +0xDE, 0x18, 0x8B, 0xB7, 0x3E, 0xDC, 0x79, 0xF9, 0xAD, 0xDA, 0x5B, 0x9E, 0xFA, 0x98, 0x23, 0x05, +0x00, 0x02, 0x04, 0xE0, 0x2C, 0xE1, 0x51, 0xEF, 0xFC, 0xF1, 0x91, 0x81, 0xF0, 0x18, 0x8C, 0x8F, +0xB2, 0x0F, 0x74, 0x22, 0xE4, 0x67, 0x1C, 0x35, 0x00, 0x10, 0x20, 0x00, 0xA7, 0x89, 0x8F, 0x14, +0x1D, 0xBF, 0xDF, 0x79, 0xD9, 0x08, 0xB1, 0x1D, 0x42, 0xF3, 0xAB, 0x9D, 0x97, 0x17, 0x43, 0x68, +0x37, 0x43, 0xC8, 0x3A, 0x2F, 0x17, 0xEE, 0xEA, 0xBC, 0x5C, 0x09, 0xE1, 0xF2, 0xFD, 0x21, 0xAC, +0xDF, 0x53, 0x7C, 0xDA, 0x87, 0x3B, 0x11, 0xF2, 0x4E, 0x47, 0x0F, 0x00, 0x04, 0x08, 0xC0, 0x49, +0x03, 0xE4, 0x0F, 0x42, 0x9A, 0xF9, 0xD8, 0x7F, 0x2E, 0x84, 0xDD, 0x2F, 0x87, 0x3C, 0x42, 0xC6, +0xCF, 0x7C, 0x74, 0x02, 0xE4, 0x6A, 0x08, 0x57, 0xAF, 0x75, 0xCE, 0x8A, 0x17, 0xD2, 0xDF, 0xFE, +0xE3, 0x4E, 0x84, 0xFC, 0xB6, 0x23, 0x08, 0x00, 0x02, 0x04, 0x60, 0xD2, 0xF8, 0xB8, 0xD9, 0xF9, +0xE3, 0x43, 0x61, 0xFB, 0xF3, 0xDD, 0x99, 0x8F, 0xFC, 0xC6, 0x78, 0x87, 0x33, 0x62, 0x27, 0x3E, +0x5E, 0xF6, 0xE7, 0xD3, 0xCC, 0xC8, 0x0B, 0x9D, 0x0F, 0xFE, 0xA6, 0xDA, 0x5B, 0x9E, 0x7E, 0xC1, +0x91, 0x04, 0x80, 0xE9, 0x5A, 0x73, 0x08, 0x80, 0x25, 0xF5, 0x78, 0xD8, 0xFB, 0xF2, 0xE4, 0xF1, +0x91, 0x7F, 0x4C, 0x3B, 0xE4, 0xC1, 0x12, 0x5B, 0x1B, 0x9D, 0xBF, 0x3D, 0xE6, 0x10, 0x02, 0xC0, +0xF4, 0x99, 0x01, 0x01, 0x96, 0x4E, 0xBE, 0xF0, 0x3C, 0x6B, 0x7E, 0x3A, 0xBC, 0xF8, 0x47, 0x93, +0xC7, 0xC7, 0xE1, 0x67, 0x87, 0x70, 0xD7, 0x2B, 0x42, 0xB8, 0xF2, 0x8A, 0x34, 0xFB, 0x61, 0x16, +0x04, 0x00, 0xA6, 0xCC, 0x0C, 0x08, 0xB0, 0x8C, 0xEA, 0x61, 0xEF, 0xB9, 0xD3, 0xC5, 0x47, 0x92, +0x3E, 0x37, 0xB6, 0xCD, 0x82, 0x00, 0x80, 0x00, 0x01, 0x98, 0x48, 0x23, 0xB4, 0xB6, 0x4E, 0x17, +0x1F, 0xF9, 0x9B, 0xED, 0xEE, 0xA2, 0xF5, 0x10, 0x1E, 0x8F, 0x4F, 0xBF, 0xA5, 0xEE, 0x70, 0x02, +0x80, 0x00, 0x01, 0x38, 0xCE, 0xB5, 0xD0, 0xDA, 0x3D, 0x5D, 0x7C, 0x14, 0xD2, 0xCE, 0x59, 0x69, +0xAB, 0xDE, 0xB4, 0x96, 0x04, 0x00, 0x10, 0x20, 0x00, 0x47, 0x3A, 0xD8, 0xAA, 0x9F, 0x29, 0x3E, +0x0A, 0xDD, 0x59, 0x90, 0x9B, 0x66, 0x41, 0x00, 0x40, 0x80, 0x00, 0x1C, 0x2D, 0x6B, 0x6D, 0x9E, +0x39, 0x3E, 0x92, 0xB4, 0x83, 0x56, 0x3B, 0x9F, 0x49, 0xF9, 0x90, 0x83, 0x0A, 0x00, 0x02, 0x04, +0x60, 0x34, 0x29, 0x9E, 0x7E, 0xEB, 0x46, 0xC8, 0xF6, 0x36, 0xCE, 0x1C, 0x1F, 0x85, 0x9D, 0x2F, +0xA6, 0xD7, 0x8D, 0xF8, 0xF4, 0x5B, 0x1A, 0x8E, 0x2E, 0x00, 0x08, 0x10, 0x80, 0x61, 0x9B, 0x77, +0x5E, 0xFF, 0x71, 0x82, 0xC5, 0xE9, 0x69, 0x31, 0x7B, 0x7A, 0x09, 0xD1, 0x5A, 0x10, 0x00, 0x10, +0x20, 0x00, 0x23, 0xEA, 0x21, 0x66, 0xD3, 0x89, 0x8F, 0x42, 0x77, 0x2D, 0x48, 0x23, 0x3E, 0xFD, +0xE6, 0x86, 0xC3, 0x0B, 0x00, 0x02, 0x04, 0x60, 0x30, 0x40, 0xF2, 0x19, 0x8B, 0x29, 0xC5, 0x47, +0xFA, 0x9C, 0x74, 0x7F, 0xDD, 0x2B, 0xAA, 0x5B, 0x0B, 0x02, 0x00, 0x02, 0x04, 0xA0, 0xA4, 0xBD, +0x77, 0x63, 0xAA, 0xF1, 0x51, 0xE8, 0xCE, 0x82, 0xD4, 0xE3, 0xD3, 0x6F, 0xBE, 0xE9, 0x20, 0x03, +0x80, 0x00, 0x01, 0xE8, 0xCA, 0x0E, 0xEA, 0x53, 0x8F, 0x8F, 0xFC, 0x7E, 0x9B, 0xC5, 0x2C, 0x88, +0xB5, 0x20, 0x00, 0x20, 0x40, 0x00, 0x8A, 0x50, 0xD8, 0xAF, 0x4F, 0x3D, 0x3E, 0x0A, 0x69, 0x16, +0x24, 0xB6, 0xD3, 0x2C, 0xC8, 0xBB, 0x1D, 0x68, 0x00, 0x10, 0x20, 0xC0, 0x8A, 0x8B, 0x4F, 0xBF, +0x75, 0x33, 0xB4, 0x9B, 0xB3, 0x89, 0x8F, 0x3C, 0x6E, 0x9A, 0xDD, 0x2B, 0xA4, 0x87, 0xF0, 0x68, +0x27, 0x42, 0x36, 0x1C, 0x71, 0x00, 0x10, 0x20, 0xC0, 0x6A, 0xAB, 0x87, 0xF6, 0xDE, 0x6C, 0xE2, +0xA3, 0xB0, 0xF7, 0x5C, 0x9A, 0x05, 0x49, 0xF1, 0xF1, 0x98, 0xC3, 0x0D, 0x00, 0x02, 0x04, 0x58, +0x6D, 0x9B, 0xDD, 0x2B, 0x97, 0xCF, 0x28, 0x3E, 0xF2, 0x0F, 0x6B, 0x9B, 0x05, 0x01, 0x00, 0x01, +0x02, 0x90, 0xBB, 0x16, 0x62, 0x6B, 0x76, 0xF1, 0x51, 0x48, 0x6B, 0x41, 0xB2, 0x66, 0x8A, 0x8F, +0x27, 0x1C, 0x72, 0x00, 0x10, 0x20, 0xC0, 0xAA, 0x6A, 0xBE, 0x50, 0x9F, 0x79, 0x7C, 0xE4, 0x9F, +0x12, 0x8B, 0x6D, 0x79, 0x6F, 0xC6, 0xA7, 0xDF, 0x5C, 0x77, 0xE0, 0x01, 0x40, 0x80, 0x00, 0x2B, +0x29, 0x6B, 0xCC, 0x25, 0x3E, 0x92, 0xFD, 0xE7, 0xBB, 0x8B, 0xD2, 0x6D, 0xCB, 0x0B, 0x00, 0x02, +0x04, 0x58, 0x3D, 0xF1, 0xE9, 0xB7, 0xD4, 0x7B, 0x41, 0x30, 0xFB, 0xF8, 0x28, 0xEC, 0x3C, 0x93, +0x5E, 0xA7, 0x59, 0x90, 0x86, 0x9F, 0x00, 0x00, 0x08, 0x10, 0x60, 0xB5, 0xD4, 0x43, 0x6B, 0x77, +0x7E, 0xF1, 0x91, 0x34, 0x5F, 0x0C, 0xA1, 0xB5, 0x95, 0xDE, 0x32, 0x0B, 0x02, 0x00, 0x02, 0x04, +0x58, 0x31, 0x9B, 0x21, 0x3B, 0x98, 0x5F, 0x7C, 0x14, 0xBA, 0x6B, 0x41, 0x1A, 0x66, 0x41, 0x00, +0x40, 0x80, 0x00, 0xAB, 0xE5, 0x5A, 0x77, 0x0B, 0xDE, 0x39, 0xC6, 0x47, 0x72, 0xB0, 0x65, 0x16, +0x04, 0x00, 0x04, 0x08, 0xB0, 0x72, 0x5A, 0xDB, 0x9B, 0x73, 0x8F, 0x8F, 0xC2, 0xD6, 0xE7, 0xD3, +0xEB, 0x34, 0x0B, 0x72, 0xD3, 0x0F, 0x02, 0x00, 0x04, 0x08, 0xB0, 0x0A, 0x62, 0x56, 0x3F, 0x97, +0xF8, 0x48, 0xD2, 0xE2, 0xF7, 0xB4, 0x2B, 0x96, 0x59, 0x10, 0x00, 0x10, 0x20, 0xC0, 0x8A, 0x68, +0xEF, 0xD4, 0xCF, 0x25, 0x3E, 0x0A, 0x69, 0x2D, 0x48, 0x8C, 0xF5, 0xF8, 0xD4, 0x9B, 0x6E, 0xFA, +0x61, 0x00, 0x80, 0x00, 0x01, 0x96, 0x58, 0x7C, 0xFA, 0x2D, 0x8D, 0xD0, 0x6E, 0x9E, 0x5F, 0x7C, +0x24, 0xD9, 0x7E, 0x08, 0xFB, 0xCF, 0xA6, 0xB7, 0x9E, 0xE8, 0x44, 0xC8, 0x86, 0x9F, 0x0A, 0x00, +0x08, 0x10, 0x60, 0x79, 0xD5, 0x47, 0x77, 0xC0, 0x9A, 0x63, 0x7C, 0x14, 0x5F, 0x2B, 0x9F, 0x05, +0x69, 0xA7, 0xF8, 0x78, 0xCC, 0x8F, 0x04, 0x00, 0x04, 0x08, 0xB0, 0xCC, 0x01, 0x32, 0xB0, 0x03, +0xD6, 0x39, 0xC4, 0x47, 0xFE, 0x66, 0x3B, 0x84, 0xFD, 0xE7, 0xD2, 0x5B, 0x8F, 0x9A, 0x05, 0x01, +0x00, 0x01, 0x02, 0x2C, 0xAB, 0xAC, 0x75, 0x3D, 0x1F, 0xFC, 0x9F, 0x67, 0x7C, 0x14, 0xF6, 0x9E, +0x2D, 0x66, 0x41, 0x2C, 0x48, 0x07, 0x00, 0x01, 0x02, 0x2C, 0xA5, 0xFE, 0x02, 0xF4, 0x73, 0x8E, +0x8F, 0xFC, 0xE6, 0x4E, 0x08, 0xED, 0x3C, 0x93, 0xDE, 0x7A, 0x2C, 0x3E, 0xF5, 0xA6, 0xBA, 0x1F, +0x0E, 0x00, 0x08, 0x10, 0x60, 0xD9, 0x64, 0xCD, 0xCD, 0x85, 0x88, 0x8F, 0x42, 0xF3, 0xF9, 0xEE, +0xD6, 0xBC, 0x66, 0x41, 0x00, 0x40, 0x80, 0x00, 0xCB, 0x25, 0x3E, 0xFD, 0x96, 0x7A, 0x88, 0xAD, +0xC5, 0x89, 0x8F, 0x42, 0x5A, 0x90, 0x1E, 0xC2, 0x4D, 0xB3, 0x20, 0x00, 0x20, 0x40, 0x80, 0xE5, +0x4A, 0x90, 0x7A, 0x38, 0xD8, 0x5E, 0xAC, 0xF8, 0x48, 0xD2, 0x2C, 0x48, 0x6B, 0x2B, 0xBD, 0xF5, +0x21, 0x3F, 0x23, 0x00, 0x10, 0x20, 0xC0, 0xF2, 0x68, 0xF4, 0x9E, 0xEE, 0xB4, 0x38, 0xF1, 0x51, +0xE8, 0xCE, 0x82, 0x34, 0xE2, 0x53, 0x6F, 0x6C, 0xF8, 0x31, 0x01, 0x80, 0x00, 0x01, 0x96, 0xC3, +0xB5, 0x89, 0x03, 0x64, 0x9E, 0xF1, 0x91, 0xA4, 0x19, 0x90, 0xD6, 0xED, 0xF4, 0x96, 0xB5, 0x20, +0x00, 0x20, 0x40, 0x80, 0xA5, 0xD0, 0xDA, 0xAE, 0x2F, 0x64, 0x7C, 0x14, 0x5F, 0xF3, 0x70, 0x16, +0xE4, 0x6D, 0x7E, 0x58, 0x00, 0x20, 0x40, 0x80, 0xAA, 0xCB, 0x5A, 0x9B, 0x0B, 0x1B, 0x1F, 0xC9, +0xC1, 0x56, 0x08, 0xFB, 0xCF, 0xA7, 0xB7, 0x9E, 0xF0, 0xC3, 0x02, 0x00, 0x01, 0x02, 0x54, 0x58, +0x7C, 0xFA, 0xCD, 0x1B, 0x21, 0xDB, 0xDB, 0x58, 0xD8, 0xF8, 0x28, 0x74, 0x67, 0x41, 0xEA, 0xF1, +0xA9, 0x37, 0xDE, 0xF4, 0x53, 0x03, 0x40, 0x80, 0x00, 0x54, 0xD7, 0x66, 0x68, 0xED, 0x2E, 0x76, +0x7C, 0x24, 0x69, 0x8D, 0x4A, 0x77, 0x16, 0xC4, 0x5A, 0x10, 0x00, 0x04, 0x88, 0x43, 0x00, 0x54, +0x58, 0x3D, 0xC4, 0x6C, 0xB1, 0xE3, 0xA3, 0x90, 0xAE, 0x8E, 0x1E, 0xDB, 0x69, 0x16, 0xE4, 0xDD, +0x7E, 0x6C, 0x00, 0x08, 0x10, 0x80, 0xAA, 0x06, 0x48, 0xF7, 0x5A, 0x1B, 0x8B, 0x1D, 0x1F, 0xF9, +0xFB, 0xDB, 0x21, 0xEC, 0x3D, 0x97, 0xDE, 0x7A, 0xB4, 0x13, 0x21, 0x1B, 0x7E, 0x74, 0x00, 0x08, +0x10, 0x80, 0xAA, 0x69, 0xEF, 0xDD, 0xA8, 0x44, 0x7C, 0x14, 0xF6, 0x9E, 0x4D, 0x21, 0x92, 0xE2, +0xE3, 0x31, 0x3F, 0x3C, 0x00, 0x04, 0x08, 0x40, 0xD5, 0x64, 0xCD, 0x8D, 0xCA, 0xC4, 0x47, 0xFE, +0xB1, 0xED, 0x6E, 0x84, 0x84, 0xF8, 0x68, 0x7C, 0xEA, 0x0D, 0x75, 0x3F, 0x40, 0x00, 0x04, 0x08, +0x40, 0xB5, 0x02, 0x64, 0xB3, 0x32, 0xF1, 0x51, 0xD8, 0xFD, 0x52, 0x11, 0x4E, 0x16, 0xA4, 0x03, +0xB0, 0x92, 0x6A, 0x0E, 0x01, 0x50, 0x45, 0xF1, 0xE9, 0x37, 0x6F, 0x86, 0x9D, 0x67, 0xFE, 0x20, +0xEC, 0x3F, 0x57, 0x9D, 0xF8, 0x28, 0xBE, 0xDE, 0xE5, 0xFB, 0x43, 0xB8, 0xE7, 0xCF, 0xA5, 0xB7, +0xBE, 0xA9, 0xF6, 0xD6, 0x7F, 0xF5, 0x99, 0xCA, 0x1D, 0xFB, 0x27, 0x1B, 0xF5, 0x90, 0xD6, 0xDF, +0x74, 0x7D, 0xA6, 0xF6, 0x7D, 0x1F, 0xFB, 0x8C, 0xDF, 0x48, 0x00, 0x26, 0xB5, 0xEE, 0x10, 0x00, +0x15, 0x55, 0x0F, 0xED, 0xBD, 0xEA, 0xC5, 0x47, 0x92, 0xB6, 0xE4, 0xBD, 0xEB, 0x15, 0x21, 0xAC, +0x5D, 0x4A, 0xB3, 0x20, 0xEF, 0xAC, 0x48, 0x74, 0x14, 0x6B, 0x57, 0xFE, 0x5A, 0x29, 0x3E, 0x8A, +0xF7, 0xA5, 0x00, 0xF9, 0xAD, 0xCE, 0xCB, 0x07, 0x3A, 0x31, 0xF2, 0x82, 0x5F, 0x4D, 0x00, 0x8E, +0xE3, 0x29, 0x58, 0x40, 0x55, 0x6D, 0x86, 0xD6, 0x4E, 0xF5, 0xE2, 0xA3, 0xB0, 0xFD, 0xF9, 0xF4, +0xFA, 0x66, 0x7C, 0xEA, 0x0D, 0x8D, 0x0A, 0xC4, 0xC7, 0xDB, 0x3A, 0x7F, 0x7C, 0x3A, 0x74, 0x9F, +0x36, 0x56, 0x1F, 0x1B, 0x83, 0xDD, 0xF7, 0x7D, 0xBA, 0xF7, 0xB1, 0x00, 0x20, 0x40, 0x80, 0xA5, +0x73, 0x2D, 0x5F, 0xD4, 0x5D, 0xC5, 0xF8, 0x48, 0x0E, 0xB6, 0x42, 0x6F, 0x0B, 0xE1, 0x85, 0x5E, +0x0B, 0xD2, 0x09, 0x8A, 0x9B, 0x9D, 0x3F, 0x3E, 0xD2, 0x79, 0xD9, 0xE8, 0x2F, 0xA2, 0xBF, 0xFD, +0xA9, 0xC1, 0x97, 0xEE, 0xEE, 0x5E, 0xA1, 0xFB, 0x31, 0xF1, 0x23, 0xF1, 0xD6, 0x8D, 0x9B, 0x7E, +0x3D, 0x01, 0x38, 0x8A, 0x35, 0x20, 0x40, 0x25, 0xC5, 0xDF, 0xFD, 0xAE, 0xDF, 0x0F, 0x5B, 0x9F, +0x6D, 0x54, 0x32, 0x3E, 0x0A, 0x17, 0xAF, 0x86, 0xF0, 0xB2, 0x6F, 0x4E, 0x6F, 0x3D, 0x52, 0x7B, +0xEB, 0xBF, 0xFA, 0xD8, 0x02, 0xC6, 0x47, 0xBD, 0xF3, 0xC7, 0x1F, 0xE4, 0x61, 0x91, 0x16, 0xCF, +0x1F, 0x86, 0xC6, 0x98, 0xFF, 0x37, 0xB9, 0x10, 0xC2, 0x95, 0x07, 0x3A, 0x2F, 0x5F, 0x9F, 0xFE, +0x96, 0x9E, 0x86, 0xF5, 0x97, 0x6A, 0xDF, 0xFF, 0x7F, 0x7F, 0xC6, 0x6F, 0x2A, 0x00, 0xC3, 0xCC, +0x80, 0x00, 0x15, 0x2D, 0x90, 0xAC, 0xDA, 0xF1, 0x91, 0x1C, 0xCE, 0x82, 0x3C, 0xB1, 0xA0, 0x47, +0xF9, 0x89, 0x3C, 0x3E, 0xB6, 0x3F, 0xD7, 0xDD, 0xBD, 0xEB, 0xB8, 0x19, 0xA7, 0xD8, 0xEA, 0x7E, +0x4C, 0xFA, 0xD8, 0xF4, 0x39, 0x21, 0x7C, 0xC8, 0x2F, 0x29, 0x00, 0x02, 0x04, 0x58, 0x8E, 0xF6, +0x78, 0xFA, 0xCD, 0x1B, 0x21, 0x3B, 0xA8, 0x76, 0x7C, 0x14, 0xB6, 0xF2, 0xB5, 0x20, 0x9B, 0xF1, +0xA9, 0x37, 0xDC, 0x5C, 0xA8, 0x63, 0xFC, 0x64, 0x23, 0x05, 0xDE, 0xDB, 0xC2, 0xCE, 0x17, 0xBA, +0x8B, 0xE6, 0x27, 0xFD, 0x7E, 0x9B, 0xCF, 0x17, 0x11, 0xD2, 0x88, 0xB7, 0x6E, 0x34, 0xFC, 0xB6, +0x02, 0x20, 0x40, 0x80, 0x65, 0xB0, 0x19, 0xDA, 0xBB, 0xD5, 0x8F, 0x8F, 0x24, 0x6B, 0x16, 0x03, +0xFC, 0x45, 0x5B, 0x0B, 0xF2, 0x78, 0x3E, 0x3B, 0x93, 0x5F, 0x38, 0xF1, 0x84, 0xDF, 0x6F, 0x8A, +0x90, 0x0A, 0xAC, 0x6F, 0x01, 0x40, 0x80, 0x00, 0x9C, 0x20, 0x40, 0x9A, 0xD5, 0x8F, 0x8F, 0xC2, +0xEE, 0x97, 0xD3, 0xEB, 0x7A, 0x7C, 0xF2, 0x91, 0xC7, 0x16, 0xE1, 0xE0, 0xF6, 0x66, 0x3F, 0x1A, +0xF9, 0x53, 0xAA, 0x4E, 0xFB, 0xFD, 0x76, 0x3F, 0xD7, 0x2C, 0x08, 0x00, 0x02, 0x04, 0x58, 0x0A, +0xD7, 0xEE, 0x3C, 0x03, 0x52, 0x91, 0xF8, 0x48, 0xD2, 0x2C, 0x48, 0x77, 0xC0, 0xFE, 0x78, 0x27, +0x42, 0x36, 0x16, 0xE0, 0xF8, 0x76, 0x67, 0x3F, 0x0E, 0xB6, 0x4E, 0xFF, 0xFD, 0xB6, 0xB6, 0xCC, +0x82, 0x00, 0x20, 0x40, 0x80, 0x25, 0xD1, 0xDA, 0xDA, 0x5C, 0x9A, 0xF8, 0x28, 0x3E, 0xBF, 0xBB, +0xC3, 0x54, 0x71, 0xB1, 0xBF, 0x73, 0xD3, 0xBB, 0x8E, 0x47, 0x23, 0x6C, 0x7D, 0xEE, 0xEC, 0xDF, +0xEF, 0xE1, 0x5A, 0x10, 0xD7, 0x06, 0x01, 0x40, 0x80, 0x00, 0x15, 0x16, 0x63, 0x7D, 0xA9, 0xE2, +0x23, 0xFF, 0xA3, 0x1D, 0xC2, 0xFE, 0x73, 0xE9, 0xAD, 0x47, 0xCF, 0x79, 0x16, 0xE4, 0x89, 0x7C, +0x4D, 0x4A, 0xD6, 0x3C, 0xFB, 0xF7, 0x9B, 0xEE, 0xA3, 0xF9, 0x7C, 0xF7, 0x3E, 0x01, 0x40, 0x80, +0x00, 0x95, 0xD5, 0xDA, 0xA9, 0x2F, 0x55, 0x7C, 0x14, 0xD2, 0x2C, 0x48, 0xD6, 0x4C, 0xF1, 0x71, +0x2E, 0x4F, 0x5B, 0xEA, 0x5D, 0x74, 0xB0, 0x7E, 0xF4, 0xDA, 0x8F, 0x53, 0x7C, 0xBF, 0xDD, 0xFB, +0xAA, 0xC7, 0x5B, 0xAF, 0xBF, 0xE9, 0x17, 0x17, 0x00, 0x01, 0x02, 0x54, 0x4E, 0x7C, 0xFA, 0xCD, +0x8D, 0xF1, 0xFF, 0x75, 0xBE, 0xE2, 0xF1, 0x91, 0xDF, 0xD4, 0x2E, 0x16, 0xA4, 0x3F, 0x16, 0x9F, +0x7C, 0xA4, 0x7E, 0x0E, 0x87, 0xF7, 0xF1, 0xA3, 0x67, 0x3F, 0x4E, 0xF9, 0xFD, 0xE6, 0xBB, 0x7C, +0x7D, 0x25, 0x04, 0x6B, 0x41, 0x00, 0x10, 0x20, 0x40, 0x45, 0x6D, 0x8C, 0x0E, 0x90, 0x97, 0x20, +0x3E, 0x0A, 0xCD, 0x7E, 0x00, 0xCC, 0x75, 0xC0, 0x7E, 0xFC, 0xEC, 0xC7, 0x19, 0x8F, 0x6F, 0xF7, +0x22, 0x86, 0x66, 0x41, 0x00, 0x10, 0x20, 0x40, 0x25, 0x6D, 0x86, 0xD6, 0xEE, 0x72, 0xC6, 0x47, +0xA1, 0x3B, 0x0B, 0x72, 0x33, 0x3E, 0xF9, 0xC8, 0xE6, 0x9C, 0xE2, 0xA3, 0xFB, 0xB4, 0xAF, 0x14, +0x0A, 0xB3, 0x88, 0xBB, 0x74, 0x9F, 0xDD, 0xEB, 0x89, 0x3C, 0xD1, 0x89, 0x90, 0x0D, 0xBF, 0xC2, +0x00, 0x02, 0x04, 0xA0, 0x3A, 0xB2, 0xD6, 0xF5, 0xFC, 0xA9, 0x4A, 0xCB, 0x1A, 0x1F, 0xC9, 0xE1, +0x85, 0xFC, 0xE6, 0xB5, 0x78, 0xFB, 0xB1, 0x34, 0x43, 0x31, 0x7A, 0xD1, 0xC1, 0x29, 0x1E, 0xDF, +0x05, 0xD9, 0xE5, 0x0B, 0x00, 0x01, 0x02, 0x70, 0x32, 0xFD, 0x05, 0xE8, 0x4B, 0x1A, 0x1F, 0x85, +0xEE, 0x2C, 0x48, 0x23, 0x3E, 0xF9, 0x48, 0x63, 0x96, 0x87, 0xB3, 0x37, 0xFB, 0xF1, 0x68, 0x2F, +0x10, 0x66, 0x13, 0x1F, 0xF9, 0x6D, 0xED, 0x62, 0x16, 0xE4, 0x51, 0xB3, 0x20, 0x00, 0x02, 0x04, +0xA0, 0x3A, 0xB2, 0xFD, 0xCD, 0xA5, 0x8F, 0x8F, 0xE4, 0xE0, 0xF6, 0xBC, 0x2E, 0xE4, 0xF7, 0x58, +0x3E, 0x33, 0x31, 0x30, 0xFB, 0x31, 0xA3, 0xE3, 0x6B, 0x16, 0x04, 0x00, 0x01, 0x02, 0x54, 0x49, +0x7C, 0xEA, 0x4D, 0xF5, 0xC1, 0xFF, 0x4A, 0xBF, 0xA4, 0xF1, 0x51, 0x3C, 0xCE, 0xED, 0x2F, 0xA4, +0xD7, 0x69, 0x16, 0x64, 0x26, 0x17, 0xF2, 0x1B, 0x3F, 0xFB, 0x31, 0xC3, 0xB8, 0x33, 0x0B, 0x02, +0x80, 0x00, 0x01, 0x2A, 0xA6, 0x1E, 0x0E, 0xB6, 0x56, 0x23, 0x3E, 0x92, 0xF6, 0x6E, 0xC8, 0xB7, +0xC5, 0x9D, 0xDD, 0x5A, 0x90, 0xC7, 0xF3, 0xEB, 0x8E, 0xF4, 0x77, 0xBE, 0x9A, 0xC3, 0xCC, 0x52, +0x77, 0xA1, 0xFB, 0xB9, 0x5D, 0xEB, 0x04, 0x00, 0x01, 0x02, 0x70, 0x12, 0x8D, 0xA3, 0xAF, 0xD0, +0xBD, 0x64, 0xF1, 0x51, 0x1E, 0xB0, 0xA7, 0x0B, 0xF9, 0x3D, 0xF9, 0xC8, 0xCD, 0x69, 0x1E, 0xC8, +0xF8, 0x64, 0xA3, 0x1E, 0xD2, 0x53, 0xA1, 0xE6, 0x19, 0x1F, 0x83, 0xDF, 0xD3, 0x63, 0xF1, 0xD6, +0xEB, 0xEB, 0x7E, 0xA5, 0x01, 0x04, 0x08, 0xC0, 0x22, 0xBB, 0xEF, 0xC4, 0x01, 0x52, 0xE5, 0xF8, +0x48, 0xF2, 0x0B, 0xF9, 0xE5, 0xB3, 0x20, 0x8F, 0x77, 0x22, 0x64, 0x9A, 0x4F, 0x5B, 0x7A, 0xFC, +0xF0, 0xBE, 0xE7, 0xBC, 0xA6, 0x66, 0xFF, 0x7C, 0xAE, 0x75, 0x02, 0x80, 0x00, 0x01, 0x38, 0x99, +0xD6, 0xD6, 0xC9, 0xAE, 0x8B, 0x51, 0xF5, 0xF8, 0x28, 0xEC, 0x7C, 0x21, 0xBF, 0x90, 0x5F, 0x98, +0xD2, 0xE2, 0xED, 0xDE, 0xEC, 0xC7, 0xCD, 0xEE, 0x4C, 0xC4, 0xBC, 0x17, 0xF4, 0xF7, 0x74, 0x67, +0x41, 0x6E, 0x9A, 0x05, 0x01, 0x58, 0x3D, 0xEB, 0x0E, 0x01, 0x50, 0x19, 0x59, 0x6B, 0xF2, 0x00, +0x59, 0x96, 0xF8, 0xC8, 0x3F, 0xA6, 0xB7, 0x78, 0xFB, 0xAE, 0xAF, 0x7F, 0x34, 0x3E, 0xF9, 0xC8, +0x07, 0x6A, 0xDF, 0xF7, 0xFB, 0x2F, 0x9C, 0xF1, 0x81, 0x3E, 0x9E, 0x5F, 0xCC, 0x71, 0xFF, 0x2B, +0xE7, 0x13, 0x1F, 0x49, 0x9A, 0x05, 0xB9, 0xF2, 0xB5, 0x21, 0x5C, 0xB8, 0x92, 0x66, 0x41, 0xDE, +0xE9, 0x97, 0x7B, 0xCC, 0x21, 0xFE, 0xC4, 0x8F, 0xA6, 0x19, 0xAF, 0xB4, 0x01, 0x41, 0x39, 0xD2, +0xFE, 0xB0, 0xF3, 0xF2, 0xB1, 0xDA, 0xB7, 0xFD, 0xCF, 0x2F, 0x38, 0x42, 0x40, 0x55, 0xD5, 0x1C, +0x02, 0xA0, 0x12, 0x83, 0xB1, 0xA7, 0xDE, 0xB4, 0x11, 0xF6, 0xBE, 0xFC, 0xD5, 0xC3, 0x35, 0x0B, +0x2B, 0x12, 0x1F, 0xFD, 0xB3, 0xF5, 0x85, 0x10, 0x36, 0xBE, 0x2D, 0xFD, 0xF9, 0x9E, 0x4E, 0x80, +0xBC, 0xFB, 0xD4, 0x8F, 0xF2, 0xC9, 0x46, 0x8A, 0xB8, 0x3F, 0x08, 0x2F, 0x7D, 0xB2, 0xD8, 0xE6, +0x77, 0xFE, 0xF1, 0x51, 0xB8, 0x78, 0x4F, 0x08, 0x2F, 0xFB, 0x96, 0xF4, 0xD6, 0x23, 0xB5, 0xEF, +0xFF, 0x7F, 0x3E, 0xE6, 0xB7, 0x7C, 0x20, 0x3C, 0xD2, 0xC6, 0x03, 0x37, 0x8F, 0x0C, 0xD2, 0xDA, +0x85, 0x0F, 0x77, 0xDE, 0xFA, 0x19, 0x21, 0x02, 0x54, 0x91, 0xA7, 0x60, 0x01, 0x55, 0xB1, 0x99, +0xFF, 0x57, 0xFB, 0x55, 0x8C, 0x8F, 0x62, 0xD0, 0xD9, 0x8D, 0xAF, 0x47, 0x7B, 0x4F, 0xA1, 0x3A, +0xAD, 0x27, 0x4A, 0xD7, 0x18, 0x39, 0xBF, 0xF8, 0x48, 0xC7, 0x2C, 0xED, 0x68, 0x36, 0x9F, 0x6B, +0x9D, 0x54, 0x29, 0x3E, 0x52, 0x20, 0x7E, 0x7A, 0x20, 0x3E, 0xE2, 0x41, 0x08, 0xD9, 0xCE, 0xE1, +0x56, 0xC9, 0x29, 0x46, 0xBB, 0xEF, 0xFF, 0x74, 0xEF, 0xE3, 0x01, 0x04, 0x08, 0xC0, 0x0C, 0xDC, +0xF9, 0x1A, 0x20, 0xCB, 0x1A, 0x1F, 0x85, 0xF4, 0x34, 0xAC, 0x6C, 0xFF, 0xD4, 0x5B, 0xD8, 0xC6, +0x5B, 0x37, 0x1A, 0x9D, 0xAF, 0xDD, 0x98, 0x68, 0x16, 0x69, 0xD6, 0xF1, 0x51, 0xE8, 0x3E, 0x96, +0x46, 0xBC, 0xF5, 0x70, 0x43, 0x7C, 0xE4, 0x31, 0xF1, 0xFB, 0x9D, 0x97, 0x8D, 0x3C, 0x3A, 0x9A, +0xCF, 0x74, 0x8E, 0xCF, 0xBF, 0xEF, 0xBC, 0x7C, 0xAA, 0xF3, 0xB3, 0xFF, 0xEC, 0xE1, 0xDB, 0xCD, +0x2F, 0x77, 0xA3, 0x24, 0x7D, 0x5C, 0xE7, 0xE3, 0x45, 0x08, 0x20, 0x40, 0x00, 0x66, 0x15, 0x20, +0xC7, 0xFD, 0x57, 0xFB, 0x65, 0x8F, 0x8F, 0xE2, 0xEB, 0x15, 0x8B, 0xB7, 0x4F, 0x37, 0x0B, 0xF2, +0x78, 0x7E, 0x0C, 0x4F, 0x33, 0xFB, 0x31, 0x8B, 0xF8, 0x48, 0xCC, 0x82, 0x8C, 0xC6, 0xC7, 0xC1, +0x73, 0xDD, 0xD0, 0x68, 0xBD, 0x18, 0x46, 0xA2, 0x3B, 0x85, 0x47, 0xEB, 0xF9, 0x4E, 0x90, 0x7C, +0xBA, 0xFB, 0xFE, 0x6E, 0x84, 0x3C, 0xE1, 0xF4, 0x00, 0x08, 0x10, 0x80, 0x69, 0x6B, 0xEF, 0xDD, +0x58, 0xF9, 0xF8, 0x48, 0x0E, 0xB7, 0xB0, 0x3D, 0xD1, 0xA0, 0x33, 0x9F, 0xFD, 0x48, 0xD7, 0x51, +0x39, 0xCD, 0xEC, 0xC7, 0xAC, 0xE2, 0xA3, 0x50, 0x5C, 0xF1, 0x7D, 0x45, 0x67, 0x41, 0x06, 0xE2, +0x23, 0xCD, 0x7A, 0x1C, 0x3C, 0x3B, 0xC1, 0x27, 0xB5, 0xBB, 0x33, 0x24, 0x29, 0x56, 0xD2, 0xB1, +0xFB, 0xC4, 0x8F, 0xBE, 0xCD, 0x49, 0x02, 0x10, 0x20, 0x00, 0xD3, 0xD4, 0xBD, 0x7A, 0xF6, 0x6A, +0xC7, 0x47, 0x61, 0xEB, 0x73, 0xE9, 0xA6, 0xB7, 0xC5, 0x5B, 0x8D, 0x93, 0x0C, 0xD8, 0x9F, 0x08, +0xCD, 0xE7, 0x4F, 0x3E, 0xFB, 0x31, 0xEB, 0xF8, 0xC8, 0xE3, 0x72, 0xE6, 0x57, 0x7C, 0xAF, 0x4E, +0x7C, 0x74, 0x67, 0x35, 0x26, 0x97, 0x62, 0xA5, 0x7D, 0x3B, 0xBD, 0xF5, 0x83, 0x4E, 0x12, 0x80, +0x00, 0x01, 0x98, 0x6A, 0x80, 0xEC, 0x6F, 0x8A, 0x8F, 0xDE, 0x5F, 0x4F, 0xF8, 0xB4, 0xA5, 0x78, +0xEB, 0xC6, 0xCD, 0x90, 0x16, 0xF1, 0x9F, 0x74, 0xF6, 0x63, 0x1E, 0xF1, 0x51, 0xE8, 0x3E, 0xB6, +0xCD, 0x78, 0xEB, 0xE1, 0x9B, 0xE2, 0xE3, 0x84, 0xD2, 0x9A, 0x90, 0x34, 0xBB, 0x05, 0x20, 0x40, +0x00, 0xA6, 0x34, 0x50, 0x7B, 0xEA, 0x4D, 0x9B, 0xA1, 0x7D, 0xB0, 0xBA, 0xF1, 0x11, 0x07, 0xBF, +0x74, 0xFE, 0xD7, 0xCE, 0x80, 0xBD, 0xF3, 0x67, 0x23, 0x9B, 0x6C, 0x16, 0xE4, 0xF1, 0x7C, 0xF6, +0xE3, 0x24, 0x57, 0x91, 0x9F, 0x67, 0x7C, 0xE4, 0x81, 0x79, 0x78, 0xC5, 0x77, 0xF1, 0x71, 0xD2, +0x3B, 0xEB, 0xFC, 0xDB, 0x68, 0xBF, 0x54, 0x77, 0xA6, 0x00, 0x04, 0x08, 0xC0, 0xF4, 0xD4, 0xF3, +0xA7, 0xE9, 0xAC, 0x6A, 0x7C, 0x24, 0xB5, 0xEE, 0xDD, 0xF5, 0xEF, 0xB1, 0xB9, 0xD5, 0x79, 0xC9, +0x07, 0xAD, 0x1F, 0x3A, 0xF6, 0x5E, 0xBA, 0xB3, 0x1F, 0xF5, 0x13, 0xCD, 0x7E, 0xCC, 0x3B, 0x3E, +0x0A, 0xDD, 0xC7, 0x58, 0x5F, 0xF6, 0x59, 0x90, 0xA9, 0xC6, 0x47, 0x71, 0x9C, 0xB3, 0xFD, 0xCE, +0xFD, 0xBE, 0xA3, 0xE1, 0x54, 0x01, 0x08, 0x10, 0x80, 0xE9, 0xD8, 0xEC, 0x07, 0xC8, 0x2A, 0xC6, +0xC7, 0xF0, 0xDD, 0x15, 0x6F, 0xA7, 0xC5, 0xDB, 0x31, 0xD4, 0xB3, 0x8F, 0x36, 0x8E, 0x1B, 0xB0, +0x3F, 0x1E, 0xF6, 0x9F, 0x9D, 0x7C, 0xF6, 0xE3, 0xBC, 0xE2, 0x23, 0x49, 0x8F, 0x71, 0xEF, 0xD9, +0xEE, 0x63, 0x16, 0x1F, 0x27, 0x3B, 0xCE, 0xD9, 0x76, 0x37, 0xD4, 0x01, 0x04, 0x08, 0xC0, 0x54, +0x5C, 0xCB, 0x77, 0xFD, 0x59, 0xC5, 0x35, 0x1F, 0xC5, 0x2D, 0x71, 0xB4, 0x4D, 0x62, 0x67, 0xC0, +0x1E, 0xF7, 0x9F, 0x4F, 0x6F, 0x8F, 0x1D, 0xB0, 0x67, 0xB7, 0x6E, 0xBC, 0xBB, 0x73, 0xDC, 0xEA, +0x61, 0xE7, 0x4B, 0x73, 0x78, 0xDC, 0x53, 0x3A, 0xE6, 0x69, 0x16, 0xA4, 0xF3, 0x98, 0xE3, 0xAD, +0x87, 0xDF, 0x2D, 0x3E, 0x4E, 0x70, 0x9C, 0xB3, 0x03, 0x01, 0x02, 0x08, 0x10, 0x80, 0xA9, 0x69, +0xBE, 0x50, 0x5F, 0xC9, 0xF8, 0xA8, 0x8D, 0xC6, 0x47, 0x2C, 0xBE, 0x44, 0xF1, 0xD2, 0x1B, 0xB0, +0xB7, 0x3F, 0xDA, 0x78, 0x6C, 0x28, 0x3E, 0xD2, 0xAE, 0x61, 0x8F, 0xE6, 0x33, 0x0A, 0x77, 0xBA, +0x80, 0xE3, 0xA2, 0xC4, 0x47, 0xFE, 0xE9, 0xED, 0x62, 0x16, 0xE4, 0xD1, 0x4E, 0x84, 0x6C, 0x88, +0x8F, 0x09, 0x8F, 0x73, 0xF7, 0xC2, 0x84, 0x37, 0x9C, 0x2C, 0x00, 0x01, 0x02, 0x30, 0x95, 0xD1, +0x5B, 0xBB, 0xB1, 0x52, 0xF1, 0xD1, 0x8B, 0x8B, 0x38, 0x66, 0x03, 0xAC, 0xEE, 0xAB, 0x5A, 0xE7, +0x7D, 0xBD, 0x97, 0x56, 0x67, 0xE0, 0xB9, 0x9B, 0x22, 0x23, 0x3C, 0xDE, 0xFE, 0xBD, 0xC6, 0x46, +0xE9, 0x83, 0x1F, 0xEB, 0x1C, 0xB7, 0x8D, 0xB8, 0xF7, 0xEC, 0x8C, 0x1F, 0xF7, 0x34, 0x8F, 0x79, +0xEF, 0xB1, 0xEC, 0xFE, 0x59, 0xFA, 0x99, 0xA7, 0xEF, 0xE5, 0x31, 0xF1, 0x71, 0x82, 0xE3, 0xDC, +0xBE, 0xBD, 0xE1, 0x64, 0x01, 0x08, 0x10, 0x80, 0xB3, 0x0E, 0xB5, 0x9E, 0x7A, 0xE3, 0x46, 0xEF, +0xE9, 0x25, 0xAB, 0x11, 0x1F, 0xC7, 0x7D, 0x44, 0x3E, 0x03, 0x52, 0xEB, 0x7F, 0xA9, 0xD8, 0x8B, +0x94, 0x6C, 0xE7, 0xD9, 0x81, 0x01, 0x7B, 0xFB, 0xA3, 0x37, 0x36, 0x62, 0x9A, 0xFD, 0xD8, 0xED, +0xCE, 0x7E, 0xC4, 0x52, 0xD4, 0x2C, 0x7C, 0x7C, 0x74, 0x83, 0x73, 0x69, 0x66, 0x41, 0xE6, 0x16, +0x1F, 0xF9, 0xBB, 0xB2, 0x4D, 0x67, 0x0C, 0x40, 0x80, 0x00, 0x9C, 0xDD, 0xE6, 0xC0, 0x0E, 0x58, +0xCB, 0x1E, 0x1F, 0xC3, 0xEB, 0xCF, 0xE3, 0x60, 0x7C, 0x14, 0x33, 0x23, 0x87, 0x4F, 0xC3, 0x4A, +0x05, 0xD2, 0x89, 0x8C, 0x4E, 0x6C, 0x74, 0xDE, 0x7C, 0xB4, 0xF5, 0xBB, 0x8D, 0x6E, 0x88, 0xA4, +0x20, 0xB9, 0xD3, 0xEC, 0xC7, 0x22, 0xC6, 0x47, 0x21, 0xCD, 0x82, 0x74, 0x2F, 0x3E, 0x59, 0xD9, +0x59, 0x90, 0xB9, 0xC6, 0x47, 0xFE, 0xEE, 0x83, 0xB4, 0x13, 0x96, 0x08, 0x01, 0x04, 0x08, 0xC0, +0xD9, 0x03, 0x64, 0xD2, 0xEB, 0x57, 0x2C, 0xC7, 0xCC, 0xC7, 0x9D, 0xE2, 0xA3, 0xF8, 0xB2, 0xB1, +0xF3, 0x6A, 0x60, 0x16, 0xA4, 0x9D, 0x0F, 0xD8, 0x3F, 0x12, 0x62, 0xED, 0xD1, 0xB4, 0x43, 0x56, +0xCC, 0x17, 0xEE, 0x8F, 0xDE, 0xE7, 0xC2, 0xC7, 0x47, 0x7E, 0x5B, 0xE7, 0xB1, 0xEF, 0x7C, 0x31, +0xBD, 0xF5, 0x78, 0xBC, 0xF5, 0x70, 0x5D, 0x7C, 0x4C, 0x70, 0x9C, 0xB3, 0xBD, 0xF4, 0xBA, 0xEE, +0x94, 0x01, 0x08, 0x10, 0x80, 0xB3, 0xB9, 0x36, 0xD9, 0x0C, 0xC8, 0x12, 0xC4, 0xC7, 0xD8, 0xA7, +0x48, 0x75, 0xC2, 0x63, 0x4C, 0x7C, 0xA4, 0x57, 0x31, 0x94, 0x66, 0x43, 0xD2, 0x2C, 0xC8, 0xCE, +0x97, 0x42, 0xFB, 0xC5, 0xD8, 0x88, 0xCD, 0xE6, 0x46, 0xDC, 0xFB, 0xEA, 0x40, 0xC0, 0x0C, 0xDC, +0x7D, 0xB6, 0xE0, 0xF1, 0x51, 0xD8, 0xEF, 0x5F, 0x3C, 0xB1, 0x52, 0xDB, 0xF2, 0x9E, 0x4B, 0x7C, +0xE4, 0x1F, 0x76, 0xD0, 0x0D, 0x76, 0x00, 0x01, 0x02, 0x70, 0x06, 0xAD, 0xAD, 0x09, 0x06, 0x54, +0xCB, 0xB3, 0xE6, 0x63, 0x64, 0xF6, 0x23, 0xC4, 0x81, 0x2F, 0x55, 0x9E, 0xF9, 0x08, 0xA5, 0x97, +0x7C, 0x16, 0x64, 0xFB, 0xF9, 0x90, 0x6D, 0x35, 0x43, 0xEB, 0xCB, 0x5F, 0xEC, 0x7D, 0x66, 0x6D, +0xF4, 0x4B, 0xC5, 0x8A, 0xC4, 0x47, 0xA1, 0x3B, 0x0B, 0x72, 0xB3, 0x2A, 0xB3, 0x20, 0xE7, 0x16, +0x1F, 0x79, 0x58, 0xE6, 0x33, 0x20, 0xD7, 0x9D, 0x34, 0x00, 0x01, 0x02, 0x70, 0xB6, 0x41, 0x6A, +0x7D, 0x25, 0xE2, 0x23, 0xDD, 0x5C, 0x1B, 0xBD, 0x2D, 0xF6, 0x6E, 0x8C, 0xF1, 0x30, 0x3E, 0x0E, +0xD7, 0x81, 0xD4, 0xBA, 0x8B, 0xD2, 0x7B, 0x1F, 0x9B, 0xBD, 0xD4, 0x09, 0x93, 0x17, 0x3F, 0x17, +0xB2, 0xE7, 0x9E, 0x0F, 0xB1, 0x15, 0x06, 0x67, 0x3E, 0x4A, 0xB3, 0x26, 0x95, 0x89, 0x8F, 0xA4, +0x42, 0xB3, 0x20, 0xE7, 0x1A, 0x1F, 0xFD, 0x08, 0xD9, 0xAD, 0x3B, 0x69, 0x00, 0x02, 0x04, 0xE0, +0x2C, 0x5A, 0x3B, 0xF5, 0xA5, 0x8F, 0x8F, 0xE1, 0xBB, 0x8E, 0x43, 0x33, 0x21, 0xF1, 0xF0, 0x69, +0x57, 0xFD, 0xA7, 0x62, 0xC5, 0xDA, 0xC0, 0xDB, 0xB1, 0xD5, 0x79, 0xD9, 0xE9, 0xBC, 0xDD, 0xDC, +0xEA, 0x1E, 0xB6, 0xAF, 0xC4, 0x7E, 0xA4, 0x1C, 0x5E, 0x3C, 0x64, 0x1A, 0x87, 0x6E, 0x8E, 0xF1, +0x51, 0xD8, 0xFE, 0xD3, 0xF4, 0x7A, 0xA1, 0x67, 0x41, 0x16, 0x22, 0x3E, 0xF2, 0x4F, 0x6B, 0x79, +0x0A, 0x16, 0x20, 0x40, 0x00, 0x4E, 0x3D, 0x04, 0x7B, 0xEA, 0x8D, 0x8D, 0xDE, 0x7F, 0xFD, 0x5E, +0xEE, 0xF8, 0xB8, 0xC3, 0xEC, 0x47, 0x71, 0xC3, 0xE1, 0x4C, 0x46, 0x6D, 0xE0, 0x82, 0x84, 0xF9, +0xD3, 0xAF, 0x5E, 0x1C, 0xBC, 0xFF, 0xB8, 0xDF, 0x7D, 0x29, 0x1E, 0xF7, 0xD8, 0xB5, 0x20, 0x55, +0x88, 0x8F, 0xA4, 0xD9, 0x19, 0xCC, 0x1F, 0xE4, 0x61, 0xF5, 0x21, 0xF1, 0x71, 0x07, 0xD9, 0x7E, +0xDA, 0x09, 0xAB, 0xEE, 0xEC, 0x01, 0x08, 0x10, 0x80, 0xD3, 0xD9, 0x18, 0x1F, 0x20, 0x4B, 0x78, +0x9D, 0x8F, 0x63, 0x66, 0x3F, 0x8A, 0x99, 0x8F, 0x10, 0x4B, 0x97, 0xF3, 0x28, 0xDF, 0x65, 0x3A, +0x44, 0xCD, 0xD1, 0xAF, 0xD1, 0x4E, 0x51, 0x92, 0xC5, 0xC3, 0x59, 0x90, 0xB2, 0xDA, 0x49, 0x0F, +0xC3, 0x39, 0xC5, 0x47, 0x61, 0x37, 0x5F, 0x0B, 0xD2, 0x88, 0xB7, 0x1E, 0x6E, 0x88, 0x8F, 0xE3, +0x02, 0xC4, 0x4E, 0x58, 0x80, 0x00, 0x01, 0x38, 0x8B, 0xCD, 0xD0, 0xDA, 0x5D, 0xFA, 0xF8, 0x38, +0x6A, 0xF6, 0xA3, 0xBF, 0x08, 0x3D, 0x94, 0x9F, 0x86, 0x55, 0x8A, 0x89, 0x22, 0x4A, 0xB6, 0x8F, +0xD8, 0xCE, 0x77, 0x2F, 0xA6, 0xFF, 0x20, 0x5E, 0xFE, 0xD0, 0xC3, 0x59, 0x90, 0x2A, 0xC5, 0x47, +0x92, 0x66, 0x40, 0xD2, 0x4B, 0x8C, 0x0B, 0xB3, 0x16, 0x64, 0xE1, 0xE2, 0x23, 0xBF, 0x8B, 0x7C, +0x27, 0xAC, 0x86, 0x53, 0x07, 0x20, 0x40, 0x00, 0x4E, 0x23, 0x6B, 0x5D, 0xCF, 0xAF, 0x07, 0xB1, +0xCC, 0xF1, 0x11, 0x8F, 0x9A, 0xFD, 0xE8, 0xBE, 0x23, 0x96, 0xAF, 0x60, 0x1E, 0x6B, 0x83, 0xBB, +0x5F, 0xA5, 0x3F, 0x8E, 0x98, 0xFD, 0x28, 0xEE, 0xB4, 0xFD, 0xD5, 0x38, 0x1A, 0x2E, 0xE5, 0xF0, +0x89, 0x33, 0x38, 0x66, 0xD3, 0x8E, 0x8F, 0xC2, 0xEE, 0x33, 0xF9, 0xE0, 0x3A, 0x7E, 0xF4, 0xA1, +0x73, 0x1F, 0x60, 0x2F, 0x64, 0x7C, 0xE4, 0xFF, 0x66, 0xF2, 0x19, 0x90, 0x6B, 0x4E, 0x1E, 0x80, +0x00, 0x01, 0x38, 0x8D, 0x81, 0x05, 0xE8, 0x4B, 0x18, 0x1F, 0x45, 0x04, 0x0C, 0x07, 0xC9, 0x98, +0x40, 0x89, 0xB1, 0x36, 0x70, 0x7B, 0x7F, 0x46, 0x64, 0x3B, 0x1E, 0xFB, 0xB8, 0xD3, 0x7F, 0x10, +0xCF, 0xB6, 0x0F, 0x1F, 0xD1, 0xC9, 0x66, 0x41, 0x16, 0x28, 0x3E, 0xD2, 0x63, 0x49, 0x33, 0x20, +0xCD, 0x7C, 0xA0, 0x7F, 0xAE, 0xB3, 0x20, 0x0B, 0x1B, 0x1F, 0xFD, 0x08, 0xD9, 0xAF, 0x3B, 0x79, +0x00, 0x02, 0x04, 0xE0, 0x74, 0x03, 0xA9, 0xCD, 0xA5, 0x8E, 0x8F, 0x78, 0xE7, 0x9D, 0xAF, 0x62, +0xB9, 0x1C, 0x86, 0x17, 0x91, 0xEF, 0xA7, 0x41, 0x79, 0xBC, 0xE3, 0xE3, 0xCE, 0xB7, 0xE7, 0x3D, +0x6E, 0x16, 0xA4, 0x0A, 0xF1, 0x51, 0xD8, 0xC9, 0x77, 0xC4, 0x4A, 0xB3, 0x20, 0x37, 0xC5, 0xC7, +0x51, 0xDA, 0x0D, 0x27, 0x0F, 0x40, 0x80, 0x00, 0x9C, 0x74, 0x58, 0xF6, 0xD4, 0x1B, 0xEB, 0xDD, +0xA7, 0x5F, 0x2D, 0x69, 0x7C, 0x1C, 0x15, 0x24, 0x61, 0x30, 0x44, 0x06, 0xD7, 0x83, 0x0C, 0xC5, +0xC8, 0x76, 0x9C, 0xE8, 0x71, 0xA7, 0x6B, 0x82, 0x14, 0xB3, 0x20, 0xFD, 0x47, 0x38, 0x70, 0x55, +0xF5, 0x8A, 0xC4, 0x47, 0x5E, 0x53, 0xCD, 0xEE, 0xB5, 0x41, 0xCE, 0x61, 0x16, 0xA4, 0x1A, 0xF1, +0x91, 0xFA, 0x63, 0xDB, 0x4E, 0x58, 0x80, 0x00, 0x01, 0x38, 0x85, 0x7A, 0x68, 0x6D, 0x2D, 0x67, +0x7C, 0x94, 0x76, 0xB4, 0x1A, 0x78, 0xBB, 0xFC, 0xF7, 0x71, 0xB3, 0x1F, 0xE5, 0x19, 0x93, 0x34, +0xFB, 0xD1, 0x9E, 0xFC, 0x71, 0x17, 0xB3, 0x20, 0xF1, 0x8E, 0xB3, 0x20, 0x0B, 0x1C, 0x1F, 0x85, +0xEE, 0x8E, 0x58, 0xF5, 0x79, 0xCE, 0x82, 0x54, 0x26, 0x3E, 0xF2, 0x3D, 0x99, 0x0F, 0xBA, 0xFF, +0x7E, 0x00, 0x04, 0x08, 0xC0, 0x89, 0x46, 0x52, 0x8D, 0xD0, 0x6E, 0x2E, 0x5F, 0x7C, 0x84, 0xD1, +0xC1, 0x7F, 0xEC, 0x7D, 0xED, 0x71, 0xB3, 0x1C, 0x47, 0x4E, 0x50, 0xEC, 0xC4, 0x13, 0x3D, 0xEE, +0x7C, 0x16, 0xE4, 0xA5, 0xC1, 0xBB, 0x1F, 0x88, 0x9A, 0x58, 0x91, 0xF8, 0xC8, 0x6B, 0x6A, 0xBE, +0xB3, 0x20, 0x95, 0x8A, 0x8F, 0xFC, 0xCF, 0x3C, 0x40, 0x5C, 0x90, 0x10, 0x10, 0x20, 0x00, 0x27, +0x74, 0xDF, 0xD1, 0x17, 0x21, 0xAC, 0x70, 0x7C, 0x0C, 0xAF, 0xE3, 0xE8, 0x0D, 0xFC, 0x63, 0xA8, +0x0D, 0xCC, 0x7C, 0x94, 0x2F, 0x3A, 0x38, 0x32, 0x4B, 0x52, 0x9E, 0xFD, 0x38, 0xC1, 0xE3, 0x6E, +0xDF, 0x8E, 0xBD, 0x67, 0xB5, 0x95, 0x66, 0x41, 0x6A, 0x53, 0x1C, 0x14, 0xCF, 0x23, 0x3E, 0x0A, +0x69, 0x16, 0x24, 0xB6, 0x67, 0x3E, 0x0B, 0x52, 0xB9, 0xF8, 0xC8, 0x7F, 0xD0, 0xF9, 0xF3, 0xED, +0xEC, 0x84, 0x05, 0x08, 0x10, 0x80, 0x13, 0x69, 0x6D, 0x9D, 0xF2, 0xBF, 0xE0, 0x56, 0x2B, 0x3E, +0x62, 0x88, 0x63, 0x9F, 0x92, 0xD5, 0xBF, 0x79, 0x60, 0x1B, 0xDE, 0xCE, 0x4B, 0xD6, 0x79, 0xD9, +0x8A, 0xA7, 0x7B, 0xDC, 0x9D, 0xCF, 0xCD, 0xFA, 0xCF, 0x6A, 0xAB, 0x95, 0xBE, 0x5E, 0x1C, 0xFC, +0xDA, 0x8B, 0x1E, 0x1F, 0xF9, 0xF7, 0xD2, 0x89, 0xD3, 0xBD, 0x67, 0xD3, 0x5B, 0x4F, 0x74, 0x22, +0x64, 0x43, 0x7C, 0x0C, 0x1F, 0x9F, 0x7D, 0x33, 0x20, 0x80, 0x00, 0x01, 0x38, 0xD9, 0x60, 0xB9, +0x75, 0x8A, 0x01, 0x54, 0x85, 0x16, 0x9C, 0x1F, 0x16, 0x48, 0x77, 0xF6, 0x23, 0x0C, 0x46, 0x47, +0x7F, 0xD7, 0xAA, 0xE1, 0x87, 0xB9, 0x5B, 0x2E, 0x93, 0x93, 0x2B, 0x66, 0x41, 0x62, 0x2C, 0x05, +0x50, 0x6D, 0x06, 0x03, 0xE0, 0x79, 0x1C, 0xCF, 0xBD, 0x3F, 0x4B, 0xB3, 0x20, 0x29, 0x3E, 0x1E, +0x13, 0x1F, 0xC3, 0xB7, 0xB7, 0x04, 0x08, 0x20, 0x40, 0x00, 0x26, 0x1E, 0x53, 0x3D, 0xF5, 0x86, +0x8D, 0xD0, 0xDE, 0x3F, 0xE1, 0x7F, 0xD5, 0xAE, 0x46, 0x7C, 0x94, 0x2F, 0x34, 0x38, 0x6E, 0xDB, +0xDD, 0xC3, 0x1B, 0x6B, 0xA3, 0x3B, 0x54, 0x65, 0x45, 0x80, 0x9C, 0xE1, 0x71, 0x77, 0xEE, 0xA3, +0xFD, 0x52, 0x3C, 0x7C, 0x2C, 0xE3, 0xB6, 0xF7, 0x8D, 0xF3, 0x3A, 0x86, 0x67, 0xFD, 0xF9, 0xB5, +0x8B, 0x59, 0x90, 0x47, 0xA7, 0x39, 0x0B, 0x52, 0xF9, 0xF8, 0xC8, 0x7F, 0xCE, 0xFB, 0x1B, 0xF1, +0x13, 0xEF, 0xD8, 0x70, 0x36, 0x01, 0x04, 0x08, 0xC0, 0x64, 0x36, 0x43, 0x7B, 0x77, 0xB9, 0xE2, +0x23, 0x8E, 0xF9, 0xCB, 0xF0, 0x16, 0xBB, 0x21, 0x8C, 0xDD, 0xA9, 0x6A, 0x60, 0xF6, 0x23, 0x3B, +0xFB, 0x60, 0x36, 0xBB, 0x9D, 0xD6, 0x29, 0xC7, 0xC1, 0xF0, 0x38, 0x4D, 0x84, 0x9C, 0x67, 0x7C, +0x14, 0xA6, 0x3C, 0x0B, 0xB2, 0x14, 0xF1, 0x91, 0xBF, 0xBF, 0xD9, 0xFD, 0x77, 0x04, 0x20, 0x40, +0x00, 0x26, 0xD2, 0xBB, 0x06, 0xC8, 0x92, 0xC4, 0xC7, 0xC0, 0x67, 0xC6, 0x23, 0xB7, 0xDD, 0x1D, +0x89, 0x8E, 0x91, 0xD9, 0x8F, 0x6C, 0x6A, 0x03, 0xFE, 0xF6, 0x71, 0xDB, 0xF2, 0xCE, 0xFC, 0x18, +0x4E, 0x71, 0x80, 0x9E, 0x7E, 0x4F, 0xF2, 0x6D, 0x79, 0xE3, 0xA3, 0xF1, 0xA3, 0xAF, 0x3B, 0xD3, +0x7F, 0xF1, 0x5F, 0x9A, 0xF8, 0xC8, 0x7F, 0x5F, 0xF6, 0x05, 0x08, 0x20, 0x40, 0x00, 0x4E, 0x14, +0x20, 0x07, 0x93, 0x5C, 0x03, 0xA4, 0x22, 0xF1, 0x91, 0x3E, 0xAD, 0x36, 0xF4, 0xF9, 0xE5, 0xB5, +0x1F, 0xA5, 0x87, 0x76, 0xE4, 0xEC, 0xC7, 0x56, 0xD6, 0x8D, 0x90, 0x29, 0x0D, 0xF8, 0xD3, 0x85, +0x09, 0xD3, 0xD6, 0xBC, 0xA5, 0x06, 0x1A, 0x9D, 0x05, 0x59, 0xF4, 0xF8, 0x28, 0xA4, 0x59, 0x90, +0xAC, 0x99, 0xE2, 0xE3, 0x09, 0xF1, 0x51, 0x04, 0xC8, 0x5E, 0x7A, 0x6D, 0x27, 0x2C, 0x40, 0x80, +0x00, 0x4C, 0xA4, 0xBD, 0x77, 0x63, 0x69, 0xE2, 0xA3, 0xFF, 0xA5, 0xCB, 0x4F, 0xBB, 0x2A, 0x45, +0x48, 0xF9, 0x42, 0x83, 0xE5, 0x87, 0x59, 0x5E, 0xEA, 0xD1, 0xEE, 0xBC, 0xB1, 0x37, 0x8D, 0x01, +0xED, 0xE0, 0x7D, 0x14, 0x17, 0x27, 0x0C, 0x47, 0xCD, 0x82, 0xC4, 0x0A, 0xC4, 0x47, 0x71, 0x3F, +0x3B, 0x69, 0x5B, 0xDE, 0x70, 0x33, 0xFE, 0xDE, 0xEB, 0xEA, 0x2B, 0x1F, 0x1F, 0xF9, 0xC7, 0xA6, +0x9D, 0x06, 0xDA, 0x66, 0x40, 0x00, 0x01, 0x02, 0x30, 0x59, 0x80, 0x34, 0x37, 0x96, 0x26, 0x3E, +0xF2, 0xD9, 0x8F, 0x38, 0x72, 0x5B, 0x79, 0xED, 0xC7, 0xE1, 0xCE, 0x57, 0xB5, 0xD1, 0x87, 0x99, +0x6E, 0xD8, 0x9E, 0x7E, 0x7C, 0xE4, 0x01, 0x92, 0x66, 0x41, 0xF6, 0x87, 0x3E, 0xE2, 0xB8, 0x59, +0x90, 0x45, 0x8D, 0x8F, 0xF4, 0x47, 0xBA, 0x30, 0x61, 0xF7, 0xBA, 0x31, 0x27, 0xBA, 0x38, 0xE1, +0x52, 0xC6, 0x47, 0xFF, 0x07, 0xBC, 0x5B, 0x77, 0x32, 0x01, 0x04, 0x08, 0xC0, 0x44, 0x03, 0xA7, +0xE3, 0xAE, 0x61, 0x50, 0xBD, 0xAD, 0x76, 0x63, 0x2C, 0x87, 0x47, 0xB9, 0x3A, 0xC6, 0xAC, 0xF5, +0x18, 0x78, 0xC8, 0xB1, 0xFB, 0xB4, 0xAB, 0xBD, 0x29, 0x6C, 0xF7, 0x7B, 0x54, 0xEB, 0xBD, 0xD8, +0x5B, 0x93, 0x32, 0x6E, 0x16, 0xA4, 0x56, 0xFA, 0xD4, 0x45, 0x8E, 0x8F, 0xDE, 0x63, 0x8D, 0xBB, +0x5F, 0x4C, 0x7F, 0xBD, 0x99, 0xFD, 0xEE, 0x64, 0xB3, 0x20, 0x4B, 0x1D, 0x1F, 0xF9, 0xBF, 0xA3, +0xA6, 0x00, 0x01, 0x04, 0x08, 0xC0, 0x1D, 0xC7, 0x5A, 0x4F, 0xBD, 0x61, 0x33, 0x64, 0x07, 0xCB, +0x11, 0x1F, 0x71, 0xCC, 0x35, 0x36, 0x86, 0x77, 0xBE, 0x8A, 0x43, 0xB3, 0x1F, 0xE5, 0x05, 0x19, +0xE9, 0xCF, 0xED, 0xD9, 0xC5, 0x47, 0xFE, 0xDE, 0xFD, 0x62, 0xBD, 0xF2, 0x98, 0xB5, 0x20, 0x55, +0x8A, 0x8F, 0x62, 0x16, 0xE4, 0x60, 0x2B, 0xDD, 0x7C, 0xC7, 0xB5, 0x20, 0x4B, 0x1F, 0x1F, 0xF9, +0xE7, 0x36, 0x3B, 0xDF, 0xE7, 0x3B, 0x1A, 0xCE, 0x2A, 0x80, 0x00, 0x01, 0x38, 0xDE, 0xC6, 0xF8, +0x2D, 0x78, 0x2B, 0x78, 0x91, 0xC1, 0x81, 0x41, 0xFC, 0x11, 0xB3, 0x1F, 0x63, 0x3F, 0xB3, 0xF7, +0x8E, 0xD4, 0x61, 0x7B, 0xB3, 0xFF, 0x1E, 0xD2, 0x2C, 0xC8, 0xD1, 0x6B, 0x41, 0x2A, 0x12, 0x1F, +0xC5, 0x31, 0x4E, 0x3B, 0x62, 0xC5, 0xDA, 0xDB, 0xDA, 0xBF, 0xF3, 0x50, 0x63, 0xA5, 0xE3, 0x23, +0xE9, 0x96, 0x65, 0xDD, 0x29, 0x05, 0x10, 0x20, 0x00, 0xC7, 0x6B, 0x84, 0xD6, 0x6E, 0xF5, 0xE3, +0xA3, 0xBF, 0xF3, 0xD5, 0xF0, 0xCD, 0xC7, 0xCF, 0x7E, 0xE4, 0x8B, 0xD5, 0x8B, 0xA9, 0x88, 0xED, +0xF9, 0x7C, 0x0F, 0xF9, 0x2C, 0xC8, 0xEE, 0x40, 0x1B, 0xF5, 0x5F, 0x4D, 0x67, 0x68, 0x3D, 0xA7, +0xF8, 0x48, 0x7F, 0x36, 0xB7, 0xBB, 0xB3, 0x20, 0x71, 0xFC, 0x5A, 0x90, 0x95, 0x89, 0x8F, 0x3C, +0x40, 0x9A, 0x02, 0x04, 0x10, 0x20, 0x00, 0x13, 0xB8, 0x36, 0x78, 0x0D, 0x90, 0x6A, 0xCE, 0x7C, +0x0C, 0x3C, 0x8C, 0x09, 0x67, 0x3F, 0x06, 0x86, 0xFB, 0x69, 0xF6, 0xE3, 0x20, 0xCE, 0xED, 0x7B, +0xC8, 0x5E, 0x18, 0x9E, 0x05, 0xA9, 0xD8, 0xCC, 0x47, 0xE8, 0x3E, 0xF6, 0xBC, 0xDF, 0xBA, 0x3B, +0x62, 0x35, 0x0E, 0x7E, 0x7B, 0x70, 0x16, 0x64, 0xA5, 0xE2, 0x23, 0xBF, 0x9F, 0xFC, 0xA9, 0x8C, +0x37, 0x9C, 0x52, 0x00, 0x01, 0x02, 0x70, 0x9C, 0xE6, 0x0B, 0xF5, 0x65, 0x88, 0x8F, 0x93, 0xCF, +0x7E, 0x94, 0x66, 0x3E, 0xCE, 0x34, 0xFB, 0x71, 0xBA, 0xCF, 0x4B, 0xD7, 0x04, 0x49, 0xBB, 0x62, +0x95, 0x1A, 0x69, 0xF2, 0xEB, 0x82, 0x2C, 0x52, 0x7C, 0xA4, 0x97, 0xE6, 0x56, 0xC8, 0x9A, 0x69, +0x16, 0xA4, 0xF6, 0xF8, 0xCA, 0xC6, 0x47, 0xA1, 0xBD, 0x55, 0x77, 0x52, 0x01, 0x16, 0xCD, 0xBA, +0x43, 0x00, 0x2C, 0x94, 0x98, 0x35, 0x2A, 0x1F, 0x1F, 0xC3, 0xD7, 0xFA, 0x28, 0x7F, 0xC8, 0xF0, +0xEC, 0x47, 0x2F, 0x3E, 0x06, 0xBE, 0xFA, 0xA9, 0x67, 0x3F, 0xCE, 0xF6, 0x3D, 0xA4, 0xEB, 0x82, +0xAC, 0xDD, 0x53, 0x6A, 0xA7, 0xDA, 0x22, 0x0C, 0xD0, 0x4F, 0x16, 0x1F, 0xC5, 0xF1, 0xCD, 0xB6, +0xBE, 0x10, 0x6A, 0xF7, 0x3D, 0xD8, 0xD8, 0xFF, 0xDF, 0x1F, 0x6E, 0x5C, 0xFA, 0xD6, 0x6F, 0x7C, +0x61, 0x25, 0xE3, 0xA3, 0x5B, 0x96, 0x02, 0x04, 0x58, 0x38, 0x66, 0x40, 0x80, 0xC5, 0x69, 0x8F, +0xA7, 0xDE, 0xB0, 0xD1, 0x7D, 0xDE, 0x7A, 0x85, 0xE3, 0x63, 0xDC, 0xC0, 0x7D, 0xF8, 0xAA, 0xE7, +0xE5, 0xD9, 0x8F, 0xF2, 0x00, 0xBB, 0x78, 0xB9, 0x9D, 0x9D, 0xC3, 0xF7, 0xD0, 0x19, 0xB4, 0x77, +0xA2, 0xA7, 0x7D, 0x7B, 0xE8, 0x1E, 0x4F, 0x35, 0x0B, 0x72, 0xCE, 0xF1, 0x91, 0x75, 0x6E, 0x3B, +0xD8, 0x0D, 0xD9, 0xEE, 0xF3, 0x21, 0x5C, 0xB9, 0xF8, 0xA1, 0x95, 0x8D, 0x8F, 0xFC, 0x7E, 0xD3, +0x4E, 0x58, 0x3F, 0xE2, 0x82, 0x84, 0x80, 0x00, 0x01, 0x38, 0x62, 0x10, 0xB6, 0x39, 0xBA, 0x00, +0xBD, 0x62, 0xF1, 0x11, 0x8F, 0x5E, 0xFB, 0xD1, 0x7F, 0x8A, 0x50, 0x3F, 0x38, 0xE2, 0xE8, 0xB5, +0x40, 0xD2, 0xAE, 0x57, 0xED, 0xF9, 0x0F, 0x8A, 0x8B, 0x2B, 0xB5, 0xE7, 0x3B, 0x62, 0xB5, 0x8F, +0xB8, 0x2E, 0x48, 0x55, 0xE2, 0x23, 0xBD, 0x9D, 0x37, 0xDC, 0x76, 0xB8, 0xF4, 0x1F, 0x7E, 0x6D, +0x7D, 0x65, 0xE3, 0x23, 0x3D, 0xDE, 0xF6, 0x4E, 0x7A, 0xA3, 0xEE, 0xE4, 0x02, 0x08, 0x10, 0x80, +0xF1, 0x36, 0x7B, 0x3B, 0xF7, 0x54, 0x33, 0x3E, 0x8E, 0xF8, 0x90, 0xE1, 0xB5, 0x1F, 0xA1, 0xB4, +0xDC, 0x63, 0xE4, 0xEE, 0x76, 0xE2, 0x9C, 0xBF, 0x87, 0xC3, 0xF8, 0xC8, 0x75, 0x06, 0xEE, 0xC5, +0x2C, 0xC8, 0xC0, 0x5A, 0x90, 0x38, 0xC9, 0x57, 0x5A, 0x9C, 0xF8, 0xA8, 0xDD, 0x73, 0x6F, 0xB8, +0xFC, 0xED, 0xDF, 0x19, 0xC2, 0x85, 0xCE, 0xFF, 0xCD, 0xAD, 0x6A, 0x7C, 0xE4, 0x3F, 0xCF, 0x66, +0xF7, 0xDF, 0x15, 0x80, 0x00, 0x01, 0x18, 0xEB, 0x88, 0x6B, 0x80, 0x54, 0x20, 0x3E, 0xCA, 0x83, +0xF4, 0x81, 0x01, 0x7B, 0x6D, 0x60, 0xED, 0x47, 0x2C, 0x1E, 0x77, 0xF9, 0x62, 0x7F, 0xA7, 0x9E, +0xFD, 0x98, 0x72, 0x7C, 0xF4, 0xB4, 0x6F, 0x77, 0x67, 0x41, 0x06, 0xAE, 0x0B, 0x52, 0x9B, 0xFD, +0x63, 0x99, 0x6A, 0x7C, 0x5C, 0xFF, 0xDE, 0x50, 0x5B, 0xBF, 0xB8, 0xDA, 0xF1, 0x91, 0xBF, 0x99, +0xEF, 0x84, 0x75, 0xDD, 0xA9, 0x05, 0x10, 0x20, 0x00, 0xE3, 0xB4, 0xB6, 0x26, 0xDF, 0x32, 0x74, +0x91, 0x67, 0x3E, 0x6A, 0x83, 0x17, 0x33, 0x2F, 0x07, 0x4A, 0xB1, 0xE0, 0x7C, 0x64, 0xA7, 0xDB, +0x13, 0xCF, 0x7E, 0xCC, 0x26, 0x3E, 0x72, 0xA5, 0x59, 0x90, 0x50, 0xCC, 0xDE, 0x1C, 0x3B, 0x0B, +0x22, 0x3E, 0x16, 0x32, 0x3E, 0xFA, 0x45, 0xB9, 0x53, 0x77, 0x72, 0x01, 0x04, 0x08, 0xC0, 0xF8, +0x41, 0x59, 0x7D, 0xC2, 0x8F, 0x5B, 0xAC, 0xF8, 0x18, 0x5E, 0xA8, 0xDD, 0xFF, 0x7B, 0x6D, 0x60, +0xE6, 0x23, 0xF6, 0x9F, 0x7B, 0x55, 0x1B, 0xDD, 0x0D, 0x6B, 0xE7, 0x24, 0xB3, 0x1F, 0x33, 0x8C, +0x8F, 0x62, 0xCC, 0x9A, 0x66, 0x41, 0x0E, 0x4A, 0x03, 0xFD, 0x5E, 0x10, 0x88, 0x8F, 0x8A, 0xC5, +0x47, 0xF7, 0xA7, 0xE9, 0x29, 0x58, 0x80, 0x00, 0x01, 0x18, 0x3F, 0x4E, 0x9A, 0xE0, 0xBF, 0xD4, +0x56, 0x20, 0x3E, 0x46, 0x66, 0x0C, 0xCA, 0x0B, 0xCE, 0xC3, 0xE1, 0x62, 0xF4, 0xFE, 0x27, 0x66, +0xE1, 0x04, 0xB3, 0x1F, 0xB3, 0x8F, 0x8F, 0x5C, 0x96, 0xC6, 0xEF, 0x71, 0x20, 0xA4, 0x46, 0xBF, +0x57, 0xF1, 0xB1, 0xF8, 0xF1, 0x91, 0x7E, 0x96, 0x7B, 0x76, 0xC2, 0x02, 0x04, 0x08, 0xC0, 0xC8, +0xF0, 0xE9, 0xC9, 0x47, 0x1A, 0xA1, 0xDD, 0xAC, 0x56, 0x7C, 0x1C, 0xF3, 0xA1, 0xFD, 0x6D, 0x77, +0x63, 0x77, 0xE6, 0xE3, 0x70, 0xF0, 0x5C, 0x1B, 0xBD, 0xCB, 0xDD, 0x38, 0xE1, 0xDD, 0xCF, 0x29, +0x3E, 0x8A, 0x71, 0xEB, 0x76, 0xDA, 0x9A, 0x77, 0xA8, 0x0F, 0xE2, 0xD0, 0xFD, 0x9C, 0xF9, 0x21, +0x89, 0x8F, 0x99, 0xC6, 0x47, 0x1E, 0xF6, 0x7B, 0xE9, 0xF5, 0x86, 0xB3, 0x0C, 0x20, 0x40, 0x00, +0x06, 0x6D, 0x1C, 0xBB, 0x03, 0xD6, 0x02, 0xC7, 0xC7, 0x51, 0x17, 0x1D, 0xEC, 0x0E, 0x9A, 0xCB, +0x37, 0xD6, 0x06, 0x67, 0x3F, 0xF2, 0x51, 0x7E, 0x98, 0x70, 0xF6, 0x63, 0xBE, 0xF1, 0xD1, 0x1F, +0xBB, 0xBE, 0x18, 0xFB, 0xD7, 0x2C, 0xE9, 0x3F, 0x04, 0x33, 0x1F, 0xD5, 0x89, 0x8F, 0xFC, 0x43, +0xF2, 0x85, 0xE8, 0x0D, 0xA7, 0x18, 0x40, 0x80, 0x00, 0x0C, 0xDA, 0x3C, 0x72, 0x07, 0xAC, 0x45, +0x8D, 0x8F, 0x38, 0xFE, 0xB6, 0xD8, 0x0B, 0x8D, 0xF2, 0x75, 0x3E, 0x46, 0xAE, 0xAB, 0x71, 0xA2, +0xD9, 0x8F, 0xF3, 0x89, 0x8F, 0xBC, 0x8F, 0xB6, 0xF3, 0x6B, 0xD9, 0xF5, 0x1F, 0x45, 0x39, 0xA8, +0xE2, 0x34, 0x8E, 0xAB, 0xF8, 0x98, 0xFD, 0xE3, 0xCD, 0xF2, 0x19, 0x90, 0x6B, 0x4E, 0x31, 0x80, +0x00, 0x01, 0x18, 0x18, 0x24, 0xB5, 0xAE, 0x77, 0xF7, 0x7E, 0xAD, 0x48, 0x7C, 0x0C, 0x7F, 0xC4, +0xC8, 0x8E, 0x57, 0x71, 0xE4, 0xB3, 0x47, 0x66, 0x3F, 0xD2, 0xB7, 0xBB, 0x1D, 0x67, 0xFC, 0x3D, +0x9C, 0x3E, 0x3E, 0xFA, 0x0F, 0xF3, 0xAB, 0xDD, 0x59, 0x90, 0x7C, 0xB6, 0x26, 0x4E, 0xE3, 0x21, +0x8A, 0x8F, 0xB9, 0xC5, 0x47, 0xFF, 0x87, 0x68, 0x27, 0x2C, 0x40, 0x80, 0x00, 0x0C, 0x6A, 0x6D, +0x6D, 0x54, 0x2A, 0x3E, 0x62, 0x18, 0xDD, 0x15, 0xAA, 0x3F, 0xD3, 0x11, 0x4B, 0x7F, 0x3F, 0x66, +0xF6, 0xA3, 0x02, 0xF1, 0x91, 0xB7, 0xE1, 0x7E, 0xE7, 0x7E, 0xF6, 0xE2, 0xE0, 0xA3, 0x3A, 0xF5, +0x2C, 0x88, 0xF8, 0x98, 0x7B, 0x7C, 0x74, 0x35, 0x9C, 0x64, 0x00, 0x01, 0x02, 0x30, 0x30, 0xA6, +0x3A, 0x68, 0x54, 0x26, 0x3E, 0x86, 0x1F, 0x62, 0x1C, 0xDC, 0x19, 0x6A, 0xDC, 0x43, 0x2F, 0x0F, +0xA8, 0x73, 0x69, 0xF6, 0x63, 0x2F, 0x2E, 0x7C, 0x7C, 0x14, 0x8F, 0x25, 0xAD, 0x05, 0x09, 0xE3, +0x62, 0xAA, 0x17, 0x11, 0x27, 0x5A, 0x44, 0x2F, 0x3E, 0xE6, 0xFF, 0x78, 0xB3, 0xED, 0xB4, 0x13, +0x56, 0xDD, 0x89, 0x06, 0x10, 0x20, 0x00, 0x21, 0xDF, 0x01, 0xAB, 0x9E, 0x8F, 0x2E, 0xAB, 0x12, +0x1F, 0x47, 0xCE, 0x7E, 0x0C, 0xAE, 0xD3, 0x2E, 0x66, 0x3F, 0x46, 0xBE, 0x9D, 0xF4, 0xF7, 0xED, +0xEA, 0xC4, 0x47, 0x3E, 0x7E, 0xDD, 0xEF, 0xBE, 0x14, 0xB7, 0x0C, 0xEE, 0x88, 0x25, 0x3E, 0x16, +0x3A, 0x3E, 0xF2, 0x1F, 0x60, 0xBE, 0x10, 0x5D, 0x80, 0x00, 0x02, 0x04, 0xA0, 0xA7, 0x1E, 0x0E, +0xB6, 0xAA, 0x11, 0x1F, 0xC3, 0x63, 0xCC, 0xFE, 0xE0, 0x39, 0x1E, 0x5E, 0xFB, 0xE3, 0xA8, 0xCB, +0x85, 0x14, 0xB7, 0xB7, 0xC2, 0x31, 0xB3, 0x1F, 0x8B, 0x17, 0x1F, 0x85, 0xD6, 0xF3, 0xF1, 0x70, +0x2B, 0xE1, 0x13, 0xCD, 0x82, 0x88, 0x8F, 0x73, 0x8D, 0x8F, 0xFC, 0xD3, 0xED, 0x84, 0x05, 0x08, +0x10, 0x80, 0xB2, 0x46, 0xBE, 0x05, 0x6F, 0x15, 0xE2, 0x63, 0xEC, 0xEC, 0x47, 0xEC, 0xEE, 0x7C, +0x35, 0x34, 0x0E, 0xED, 0xCF, 0x7E, 0x94, 0x9F, 0x7A, 0x95, 0xFE, 0xDC, 0xAA, 0x5E, 0x7C, 0xE4, +0xB7, 0xB4, 0xBA, 0xBB, 0x62, 0x85, 0x70, 0x92, 0x59, 0x10, 0xF1, 0x71, 0xEE, 0xF1, 0x91, 0xB4, +0xF3, 0x1F, 0x9C, 0x9D, 0xB0, 0x00, 0x01, 0x02, 0xD0, 0x73, 0x5F, 0x68, 0xEF, 0x2F, 0x7E, 0x7C, +0x0C, 0x8F, 0x33, 0xCB, 0xD7, 0xF9, 0xB8, 0xD3, 0xEC, 0x47, 0x21, 0xFD, 0x87, 0xE8, 0x83, 0x38, +0x83, 0xEF, 0x61, 0xB6, 0xF1, 0xD1, 0x1F, 0xC7, 0xBE, 0x74, 0x87, 0x59, 0x10, 0xF1, 0xB1, 0x78, +0xF1, 0x51, 0xC8, 0xF6, 0xEB, 0x4E, 0x35, 0x80, 0x00, 0x01, 0x48, 0x5A, 0x5B, 0x9B, 0x55, 0x89, +0x8F, 0x81, 0x41, 0x76, 0x71, 0x9D, 0x8F, 0x49, 0x66, 0x3F, 0x8A, 0x97, 0xED, 0xEA, 0xC6, 0x47, +0xFE, 0xDE, 0xD2, 0x2C, 0x48, 0xFF, 0xA3, 0xCB, 0xB3, 0x20, 0x51, 0x7C, 0x2C, 0x64, 0x7C, 0x74, +0x7F, 0x78, 0x9B, 0x4E, 0x36, 0x80, 0x00, 0x01, 0x48, 0xB2, 0xD3, 0x0E, 0x8C, 0xE6, 0x1C, 0x1F, +0xE5, 0x19, 0x8E, 0xF2, 0x75, 0x3E, 0xC6, 0xCD, 0x7E, 0xC4, 0x30, 0xB8, 0x3B, 0x56, 0x32, 0x76, +0xF6, 0xA3, 0x3A, 0xF1, 0x51, 0xC8, 0x67, 0x41, 0xDA, 0xC7, 0xEC, 0x88, 0x25, 0x3E, 0x16, 0x2F, +0x3E, 0xF2, 0x7F, 0x67, 0x3B, 0x1B, 0xF1, 0x13, 0x3F, 0xBC, 0xE1, 0x84, 0x03, 0x08, 0x10, 0x80, +0xF6, 0xDE, 0x29, 0x06, 0x45, 0x73, 0x8E, 0x8F, 0xFE, 0xE0, 0x3A, 0x0C, 0x2E, 0x38, 0x1F, 0x7A, +0xDE, 0x51, 0x7F, 0xF6, 0x63, 0x28, 0x44, 0xC6, 0xCF, 0x7E, 0x54, 0x2F, 0x3E, 0xF2, 0x8F, 0x6C, +0x75, 0x7E, 0x64, 0x5B, 0x83, 0xDF, 0xDE, 0x61, 0x84, 0xC5, 0xC1, 0x35, 0x2F, 0xE2, 0x63, 0x71, +0x1E, 0x6F, 0x96, 0x5F, 0xD2, 0xDE, 0x2C, 0x08, 0x20, 0x40, 0x80, 0xD5, 0x16, 0x9F, 0x7C, 0xA4, +0x11, 0xDA, 0xBB, 0x8B, 0x1F, 0x1F, 0xFD, 0x01, 0x73, 0x1C, 0xB9, 0xEA, 0x79, 0x2C, 0x6F, 0xC1, +0x3B, 0xBC, 0x1E, 0xA4, 0xF8, 0xC0, 0xB4, 0xEB, 0xD5, 0x41, 0xAC, 0x7C, 0x7C, 0xF4, 0x9B, 0xF1, +0x76, 0x77, 0x16, 0x64, 0x60, 0x2D, 0x48, 0xCD, 0xCC, 0xC7, 0xC2, 0xC6, 0x47, 0x7E, 0xB0, 0x0F, +0x04, 0x08, 0x20, 0x40, 0x00, 0x42, 0xDA, 0x82, 0x37, 0x1F, 0xC9, 0x2E, 0x70, 0x7C, 0x8C, 0x1B, +0x74, 0x8E, 0x59, 0xFB, 0x31, 0xF6, 0xB6, 0xE2, 0x4B, 0xEE, 0x2C, 0x4F, 0x7C, 0xE4, 0xB2, 0x14, +0x21, 0xE5, 0x7B, 0x89, 0x23, 0x4F, 0x3B, 0x2B, 0xAF, 0x81, 0x11, 0x1F, 0xE7, 0x1C, 0x1F, 0xF9, +0xCF, 0x6C, 0x2F, 0xBD, 0xB6, 0x13, 0x16, 0x20, 0x40, 0x00, 0x01, 0xD2, 0xBF, 0x06, 0xC8, 0x22, +0xC6, 0xC7, 0x98, 0xA7, 0x16, 0x75, 0xEF, 0xA5, 0x36, 0x30, 0xD3, 0x31, 0x30, 0xFB, 0x11, 0xC6, +0xCC, 0x7E, 0xB4, 0x97, 0x28, 0x3E, 0x7A, 0xDA, 0x5B, 0xC5, 0x5A, 0x90, 0x30, 0xB8, 0x06, 0xA6, +0x36, 0xF8, 0xFD, 0x8B, 0x8F, 0x05, 0x88, 0x8F, 0xFC, 0xCD, 0xF4, 0xC3, 0x6A, 0x9B, 0x01, 0x01, +0x04, 0x08, 0xB0, 0xE2, 0xDA, 0x7B, 0x37, 0x16, 0x36, 0x3E, 0x86, 0x07, 0x9D, 0xB5, 0xC3, 0xD0, +0x18, 0x37, 0xCB, 0x31, 0x7C, 0x6D, 0x8C, 0xFE, 0x97, 0xDD, 0x89, 0x4B, 0x17, 0x1F, 0xB9, 0x4E, +0x38, 0xB4, 0xBE, 0x9A, 0xF5, 0x1E, 0x57, 0x6D, 0xE0, 0x70, 0xF5, 0x8F, 0x53, 0x14, 0x1F, 0x0B, +0x11, 0x1F, 0xFD, 0x9F, 0xD9, 0x9E, 0x00, 0x01, 0x04, 0x08, 0xB0, 0xEA, 0x01, 0xB2, 0x3F, 0xC1, +0x02, 0xF4, 0x73, 0x8A, 0x8F, 0xDE, 0x9A, 0x8F, 0xD1, 0x9D, 0x65, 0x6B, 0x47, 0xAC, 0xFD, 0xA8, +0x8D, 0xEE, 0x86, 0xB5, 0x5B, 0xCC, 0x7E, 0x2C, 0x59, 0x7C, 0xF4, 0x06, 0xE4, 0x69, 0x4B, 0xDE, +0xEC, 0x60, 0x68, 0x8C, 0x5E, 0x9A, 0x29, 0xCA, 0x17, 0xE4, 0x67, 0xE2, 0x63, 0x21, 0xE2, 0x23, +0x0F, 0x90, 0x7D, 0xBB, 0x60, 0x01, 0x02, 0x04, 0x58, 0x71, 0x59, 0x73, 0xB3, 0x2A, 0xF1, 0x31, +0xB0, 0xDB, 0xD3, 0xD0, 0xC7, 0xF6, 0xD7, 0xA7, 0x97, 0x27, 0x3B, 0xD2, 0xE4, 0xC0, 0x76, 0x5C, +0xDA, 0xF8, 0xE8, 0x37, 0xE4, 0x8B, 0x71, 0x20, 0x2C, 0xCA, 0xF1, 0xD1, 0x0F, 0xB2, 0x4C, 0x7C, +0x9C, 0x7B, 0x7C, 0xE4, 0xEF, 0x6A, 0x86, 0xF8, 0x89, 0x1F, 0x6E, 0x38, 0xF1, 0x00, 0x02, 0x04, +0x58, 0x49, 0xF1, 0xC9, 0x47, 0x36, 0x7B, 0x5B, 0x83, 0x2E, 0x56, 0x7C, 0x1C, 0x35, 0xE8, 0x2C, +0x6F, 0xBB, 0x5B, 0x9E, 0x01, 0x29, 0x66, 0x3F, 0x86, 0xBF, 0xEC, 0x6E, 0x9C, 0xCA, 0xE0, 0x75, +0x91, 0xE3, 0x23, 0x6F, 0xC8, 0xED, 0xEE, 0xD6, 0xBC, 0xFD, 0x19, 0x8E, 0x70, 0x78, 0x21, 0xC6, +0xF2, 0x8B, 0xF8, 0x38, 0xE7, 0xF8, 0xC8, 0x7F, 0x58, 0xFB, 0xE9, 0x75, 0xDD, 0xD9, 0x07, 0x10, +0x20, 0xC0, 0xAA, 0xDA, 0x38, 0x7A, 0x0B, 0xDE, 0xF3, 0x8F, 0x8F, 0x91, 0xD9, 0x8F, 0x72, 0x78, +0x0C, 0x7C, 0x42, 0x6D, 0x70, 0xF6, 0x23, 0x1F, 0xE8, 0x75, 0x5E, 0x76, 0xB2, 0xA5, 0x8F, 0x8F, +0xE2, 0xA6, 0xD6, 0x73, 0xF1, 0xF0, 0x1A, 0x28, 0x9D, 0xD0, 0x28, 0x66, 0x3C, 0x06, 0xE2, 0x23, +0x13, 0x1F, 0xE7, 0x1A, 0x1F, 0xF9, 0xEF, 0xE5, 0x9E, 0x00, 0x01, 0x04, 0x08, 0xB0, 0xD2, 0x1A, +0xA1, 0xB5, 0xBB, 0x78, 0xF1, 0x11, 0xC7, 0xDF, 0xE5, 0xC0, 0x16, 0xBB, 0xF1, 0xF0, 0xA2, 0x83, +0x61, 0xDC, 0x0E, 0xBB, 0xBB, 0xD9, 0x99, 0xBF, 0x8D, 0xAA, 0xC4, 0x47, 0x3E, 0xAE, 0xDD, 0xEF, +0xBE, 0x94, 0x9F, 0x76, 0x55, 0x04, 0x47, 0x7F, 0xE6, 0x23, 0x8A, 0x8F, 0x73, 0x8D, 0x8F, 0xFC, +0xC3, 0xF2, 0x05, 0x49, 0x37, 0x9C, 0x7A, 0x00, 0x01, 0x02, 0xAC, 0xAA, 0x6B, 0xA3, 0xD7, 0x00, +0x59, 0x8C, 0xA7, 0x5D, 0x1D, 0x35, 0xFB, 0x31, 0x6E, 0x07, 0xAC, 0xD1, 0xD9, 0x8F, 0x38, 0x74, +0xDD, 0x8F, 0xE5, 0x8E, 0x8F, 0xE2, 0xED, 0xF6, 0x0B, 0x45, 0x98, 0xD5, 0x3A, 0xD1, 0xD1, 0x9B, +0xF9, 0x28, 0x3D, 0x15, 0xAB, 0x76, 0xB7, 0xF8, 0x58, 0x88, 0xFB, 0x6D, 0x6F, 0xD5, 0x9D, 0x7A, +0x00, 0x01, 0x02, 0xAC, 0xA6, 0xE6, 0x0B, 0xF5, 0x85, 0x8B, 0x8F, 0xF4, 0x47, 0x6D, 0xF4, 0x6E, +0x63, 0x18, 0x5C, 0xE3, 0x71, 0xF4, 0xEC, 0x47, 0xE7, 0xD5, 0x56, 0x3C, 0xE3, 0x43, 0xA9, 0x5E, +0x7C, 0xA4, 0x83, 0x96, 0x9E, 0xDD, 0x93, 0x9E, 0x51, 0x57, 0xC4, 0x47, 0x2C, 0x3D, 0xED, 0x2A, +0xA4, 0xF8, 0xD8, 0x14, 0x1F, 0x0B, 0x71, 0xBF, 0xB1, 0x25, 0x40, 0x00, 0x01, 0x02, 0xAC, 0xA8, +0x98, 0x35, 0x16, 0x2A, 0x3E, 0x86, 0xFF, 0x3A, 0x66, 0xED, 0xC7, 0xB8, 0x4F, 0x8D, 0xE5, 0x0B, +0x80, 0xA4, 0x09, 0x9D, 0xBD, 0x78, 0x86, 0x87, 0x52, 0xCD, 0xF8, 0x28, 0x8E, 0x4F, 0xBE, 0x23, +0x56, 0x7A, 0xDA, 0x55, 0xBB, 0x37, 0x0B, 0xD2, 0x5B, 0xF3, 0x71, 0x45, 0x7C, 0x2C, 0xCE, 0xFD, +0xC6, 0x03, 0x3B, 0x61, 0x01, 0x02, 0x04, 0x58, 0xC1, 0xF6, 0x78, 0xF2, 0x91, 0x8D, 0xC3, 0x1D, +0xB0, 0x16, 0x24, 0x3E, 0x8E, 0x99, 0xFD, 0x88, 0x43, 0xC1, 0x51, 0xEC, 0xF2, 0x34, 0xF4, 0x81, +0xBD, 0x6D, 0x77, 0x57, 0x33, 0x3E, 0x92, 0x7C, 0x16, 0x64, 0xEB, 0x70, 0xF6, 0x43, 0x7C, 0x2C, +0xE0, 0xFD, 0xB6, 0x77, 0xD2, 0x6B, 0xD7, 0x03, 0x01, 0x04, 0x08, 0xB0, 0x72, 0x36, 0xBB, 0x3B, +0x60, 0x2D, 0xD6, 0x56, 0xBB, 0x47, 0xCD, 0x7E, 0x1C, 0x37, 0x03, 0x72, 0x38, 0xFA, 0x0E, 0xA7, +0x9E, 0xFD, 0x58, 0x86, 0xF8, 0x28, 0xC2, 0x2C, 0x35, 0x46, 0xB1, 0xE0, 0xFC, 0xCA, 0x5F, 0x12, +0x1F, 0x0B, 0x77, 0xBF, 0xDD, 0xF0, 0x77, 0x45, 0x74, 0x40, 0x80, 0x00, 0xAB, 0x18, 0x20, 0xFB, +0x8B, 0x13, 0x1F, 0x13, 0xCC, 0x7E, 0x94, 0x07, 0xD9, 0x87, 0x57, 0xFC, 0x2E, 0xAD, 0x1D, 0xD9, +0x12, 0x1F, 0xF9, 0x9F, 0xAD, 0x74, 0x59, 0x94, 0x97, 0x85, 0xBB, 0xBE, 0x43, 0x7C, 0x2C, 0xE4, +0xFD, 0xC6, 0xFC, 0xD2, 0xF5, 0xD7, 0x9D, 0x82, 0x00, 0x01, 0x02, 0xAC, 0x98, 0x78, 0xCC, 0x35, +0x40, 0xE6, 0x1C, 0x1F, 0xC3, 0x37, 0x4F, 0x32, 0xFB, 0x11, 0x87, 0xC2, 0x21, 0x8D, 0xE9, 0xF6, +0xE3, 0x29, 0x1E, 0xCA, 0x92, 0xC5, 0x47, 0xE7, 0x65, 0x6D, 0xE3, 0xDE, 0x70, 0xF5, 0x75, 0xE2, +0x63, 0xA1, 0xEF, 0xB7, 0x7D, 0xBB, 0xEE, 0x1C, 0x04, 0x08, 0x10, 0x60, 0xB5, 0xB4, 0xB6, 0xCE, +0x70, 0x2D, 0x82, 0xE9, 0x5F, 0xE1, 0xFC, 0xE4, 0xB3, 0x1F, 0xA5, 0x4A, 0x39, 0xE5, 0xDA, 0x8F, +0x65, 0x8D, 0x8F, 0x7B, 0xDF, 0xDA, 0x89, 0x8F, 0x4B, 0xE2, 0x63, 0xA1, 0xEF, 0x37, 0xB6, 0x3D, +0x05, 0x0B, 0x10, 0x20, 0xC0, 0x8A, 0x89, 0x59, 0x7D, 0x21, 0xE2, 0x63, 0xF8, 0x5D, 0x77, 0x9A, +0xFD, 0xE8, 0x8D, 0xB4, 0x07, 0xEE, 0x29, 0xCD, 0x7E, 0x1C, 0xC4, 0x13, 0x3E, 0x14, 0xF1, 0x21, +0x3E, 0xCE, 0xF1, 0x7E, 0xBB, 0x3B, 0x61, 0x89, 0x10, 0x40, 0x80, 0x00, 0x2B, 0xA4, 0xB5, 0x73, +0x8A, 0x00, 0x99, 0x4D, 0x7C, 0x4C, 0x3C, 0xFB, 0x91, 0x95, 0xB6, 0xDC, 0x3D, 0xC3, 0xEC, 0x87, +0xF8, 0x10, 0x1F, 0xE7, 0x7E, 0xBF, 0xED, 0xBD, 0xF4, 0xBA, 0x1E, 0x00, 0x04, 0x08, 0xB0, 0x0A, +0xE2, 0x93, 0x8D, 0xC6, 0xE1, 0x16, 0xBC, 0xE7, 0x1C, 0x1F, 0x71, 0xC2, 0xD9, 0x8F, 0x52, 0x78, +0x0C, 0xDC, 0x5B, 0x33, 0x9E, 0x68, 0xF6, 0x43, 0x7C, 0x88, 0x8F, 0xF3, 0xBF, 0xDF, 0x50, 0x2C, +0x44, 0x37, 0x03, 0x02, 0x08, 0x10, 0x60, 0x65, 0x6C, 0x9C, 0x2C, 0x40, 0x66, 0x14, 0x1F, 0xBD, +0xB1, 0xF5, 0x48, 0x90, 0x0C, 0xDD, 0x58, 0xCC, 0x7C, 0xC4, 0xE1, 0x87, 0x73, 0xC2, 0x9D, 0xAF, +0xC4, 0x87, 0xF8, 0x58, 0x88, 0xF8, 0x48, 0xB2, 0x7C, 0x06, 0xC4, 0x4E, 0x58, 0x80, 0x00, 0x01, +0x56, 0xC6, 0x66, 0x68, 0x4D, 0xBA, 0x03, 0xD6, 0x0C, 0xE3, 0x23, 0x1E, 0x7F, 0xD5, 0xF3, 0xC3, +0xAB, 0x9C, 0xC7, 0xF1, 0xEB, 0x41, 0xF6, 0x7A, 0x57, 0x3E, 0x17, 0x1F, 0xE2, 0xA3, 0x4A, 0xF1, +0x51, 0x68, 0xEF, 0xB8, 0x18, 0x21, 0x20, 0x40, 0x80, 0x15, 0x91, 0xB5, 0xAE, 0x87, 0x38, 0xC9, +0xC8, 0x7D, 0x86, 0xF1, 0x71, 0x54, 0x90, 0x0C, 0xAD, 0xFD, 0xE8, 0x2E, 0x38, 0xAF, 0x8D, 0x3E, +0x92, 0x74, 0xC3, 0x4E, 0x9C, 0xF0, 0xA1, 0x88, 0x0F, 0xF1, 0xB1, 0x60, 0xF1, 0xD1, 0x2D, 0x90, +0x86, 0x93, 0x11, 0x20, 0x40, 0x80, 0xD5, 0xD0, 0xDA, 0x9A, 0xE0, 0xBF, 0xBC, 0xCE, 0x76, 0xE6, +0xA3, 0x3F, 0xE3, 0x31, 0xB0, 0xAE, 0xA3, 0x36, 0x30, 0xD3, 0x11, 0x4B, 0x33, 0x1F, 0x23, 0x77, +0x3B, 0xE1, 0xEC, 0x87, 0xF8, 0x10, 0x1F, 0x8B, 0x19, 0x1F, 0x31, 0x7F, 0x1A, 0x56, 0xFC, 0xC4, +0x0F, 0xD7, 0x9D, 0x90, 0x00, 0x01, 0x02, 0x2C, 0xBF, 0xEC, 0xA0, 0x71, 0x6E, 0xF1, 0x31, 0xAC, +0x76, 0x18, 0x22, 0xC3, 0x33, 0x1F, 0xA1, 0xDC, 0x1F, 0xE5, 0x77, 0x66, 0x61, 0xA2, 0xD9, 0x0F, +0xF1, 0x21, 0x3E, 0x16, 0x36, 0x3E, 0x92, 0xEE, 0x85, 0x40, 0x05, 0x08, 0x20, 0x40, 0x80, 0xE5, +0x16, 0x9F, 0x6C, 0xD4, 0x8F, 0x7F, 0xFA, 0xD5, 0x8C, 0xE3, 0x23, 0x0E, 0x7D, 0x95, 0xFE, 0xDF, +0x6B, 0x03, 0x33, 0x1F, 0x87, 0xB3, 0x23, 0xB5, 0xD1, 0xDD, 0xB0, 0x76, 0xEF, 0x3C, 0xFB, 0x21, +0x3E, 0xC4, 0xC7, 0x42, 0xC7, 0x47, 0xBF, 0xA4, 0x43, 0xC3, 0x59, 0x09, 0x10, 0x20, 0xC0, 0xB2, +0xAB, 0x87, 0xD6, 0xD6, 0xC2, 0xC4, 0xC7, 0xC8, 0xD3, 0xB0, 0xCA, 0x0B, 0xCE, 0x43, 0x69, 0x21, +0x7A, 0xF9, 0x3E, 0xEE, 0x30, 0xFB, 0x21, 0x3E, 0xC4, 0xC7, 0xE2, 0xC7, 0x47, 0x47, 0x7B, 0x3B, +0xBD, 0xBE, 0xE6, 0x94, 0x04, 0x08, 0x10, 0x60, 0xD9, 0x35, 0x42, 0xBB, 0x39, 0xFF, 0xF8, 0x38, +0x66, 0x4C, 0xD6, 0xDF, 0x76, 0xB7, 0x37, 0xF3, 0x71, 0xB8, 0xFB, 0x55, 0x6D, 0xF4, 0x21, 0xEE, +0xC6, 0x63, 0x1F, 0xAA, 0xF8, 0x10, 0x1F, 0x95, 0x88, 0x8F, 0x42, 0xB6, 0x5F, 0x77, 0x4A, 0x02, +0xE6, 0x6D, 0xDD, 0x21, 0x00, 0xE6, 0xEC, 0xBE, 0xD1, 0x6B, 0x80, 0xCC, 0x2F, 0x3E, 0x46, 0x66, +0x3F, 0x8A, 0xF0, 0x88, 0xA5, 0xAB, 0x9C, 0x87, 0xEE, 0xA0, 0xFC, 0xA4, 0xB3, 0x1F, 0xE2, 0x43, +0x7C, 0x54, 0x2A, 0x3E, 0xF2, 0x77, 0xB5, 0x1A, 0x4E, 0x49, 0xC0, 0xBC, 0x99, 0x01, 0x01, 0xE6, +0xAB, 0xB5, 0xB5, 0x79, 0x2E, 0xF1, 0x11, 0xC7, 0xDF, 0x16, 0x87, 0xDE, 0x55, 0x0C, 0xC0, 0xC7, +0x7E, 0xFC, 0xF6, 0xD1, 0xB3, 0x1F, 0x8B, 0x1C, 0x1F, 0x51, 0x7C, 0x88, 0x8F, 0xA3, 0x64, 0x3B, +0x69, 0x27, 0x2C, 0xD7, 0x03, 0x01, 0x04, 0x08, 0xB0, 0xC4, 0xB2, 0xD6, 0xE6, 0xDC, 0xE3, 0x63, +0xF8, 0xAB, 0x0D, 0x5F, 0xD5, 0x7C, 0x64, 0xF6, 0x63, 0xCC, 0xDA, 0x8F, 0xB4, 0xE8, 0x7C, 0x37, +0x56, 0x32, 0x3E, 0x6A, 0xC7, 0xC4, 0xC7, 0xC0, 0x6D, 0xE2, 0x63, 0xB5, 0xE2, 0x23, 0xFF, 0xF7, +0x98, 0xCF, 0x46, 0x6E, 0x3A, 0x31, 0x01, 0x02, 0x04, 0x58, 0x5E, 0xED, 0xBD, 0x8D, 0xB9, 0xC7, +0x47, 0x31, 0x0A, 0x1F, 0xBA, 0x2D, 0x8E, 0xB9, 0xDB, 0x63, 0x67, 0x3F, 0x2A, 0x16, 0x1F, 0xE1, +0xC8, 0xF8, 0x08, 0x03, 0x8B, 0xEC, 0x8B, 0xDD, 0xBE, 0x32, 0xF1, 0xB1, 0x5A, 0xF1, 0x91, 0x7F, +0xD8, 0x81, 0x00, 0x01, 0x04, 0x08, 0xB0, 0xBC, 0xE2, 0x93, 0x8D, 0x46, 0xF7, 0xDA, 0x03, 0xF3, +0x5F, 0x70, 0x3E, 0x3C, 0xF0, 0x2E, 0xDE, 0x38, 0x1C, 0x80, 0x0F, 0x87, 0x48, 0x39, 0x9A, 0x42, +0xF7, 0xC2, 0x83, 0x15, 0x8B, 0x8F, 0x91, 0x9B, 0xFA, 0xC7, 0xA0, 0x76, 0x78, 0x2C, 0x86, 0x9E, +0x76, 0x75, 0x41, 0x7C, 0xAC, 0x4E, 0x7C, 0xE4, 0xBF, 0xDB, 0x76, 0xC2, 0x02, 0x04, 0x08, 0xB0, +0xD4, 0x05, 0x12, 0xEB, 0x21, 0x6B, 0xCD, 0x77, 0xD0, 0x39, 0x76, 0xF6, 0x23, 0xE6, 0x3B, 0x5F, +0x95, 0x2F, 0xED, 0x51, 0x1E, 0x88, 0x8F, 0x7C, 0xFE, 0x76, 0xF5, 0xE3, 0x23, 0xF6, 0xB7, 0x19, +0xAE, 0x1D, 0xBE, 0xAF, 0xF4, 0xB4, 0xAB, 0xAC, 0x17, 0x1F, 0x2F, 0x13, 0x1F, 0xAB, 0x13, 0x1F, +0xFD, 0x4F, 0x69, 0x99, 0x01, 0x01, 0x04, 0x08, 0xB0, 0xB4, 0x8E, 0xB9, 0x06, 0xC8, 0xEC, 0x06, +0x9D, 0x83, 0xB3, 0x1F, 0xA5, 0x8B, 0x81, 0x8C, 0x59, 0xFB, 0x31, 0x32, 0x84, 0x4B, 0xBD, 0x34, +0x34, 0xFB, 0x51, 0x85, 0xF8, 0x18, 0xFF, 0x71, 0x83, 0xF1, 0x31, 0x3C, 0xF3, 0x21, 0x3E, 0x56, +0x30, 0x3E, 0x92, 0x6C, 0x5F, 0x80, 0x00, 0x02, 0x04, 0x58, 0x52, 0xED, 0xBD, 0x1B, 0x73, 0x1D, +0x74, 0x8E, 0xCC, 0x7E, 0xC4, 0xFE, 0x75, 0x3F, 0x06, 0x16, 0xA4, 0x97, 0x67, 0x3F, 0xCA, 0x41, +0x92, 0xFE, 0xDC, 0xAA, 0x66, 0x7C, 0x8C, 0x5D, 0x74, 0x5E, 0xBA, 0xBD, 0x7C, 0xBD, 0x93, 0x35, +0xF1, 0xB1, 0xBA, 0xF1, 0x91, 0x07, 0xC8, 0xDE, 0x86, 0x9D, 0xB0, 0x00, 0x01, 0x02, 0x2C, 0xA7, +0x6C, 0xFF, 0xE4, 0x83, 0x9C, 0x33, 0x0E, 0x3A, 0x63, 0x69, 0x0F, 0xDA, 0xB1, 0x3B, 0x5F, 0x1D, +0x37, 0x8C, 0x4B, 0xEB, 0x73, 0x0F, 0xE2, 0x72, 0xC4, 0x47, 0x2C, 0xCF, 0x7C, 0x1C, 0x06, 0x97, +0xF8, 0x58, 0xF1, 0xF8, 0xC8, 0x3F, 0xDD, 0x42, 0x74, 0x60, 0xBE, 0x5C, 0x88, 0x10, 0x98, 0x63, +0x80, 0x34, 0x4F, 0x36, 0xC8, 0x39, 0xEB, 0xA0, 0xB3, 0x56, 0x8C, 0xCD, 0x0E, 0xFF, 0xB3, 0xFF, +0x89, 0x66, 0x3F, 0xB6, 0x97, 0x34, 0x3E, 0x7A, 0x17, 0x59, 0x5C, 0x7B, 0x79, 0x27, 0x3E, 0xDE, +0x22, 0x3E, 0x56, 0x3A, 0x3E, 0xF2, 0x7F, 0x97, 0x7B, 0xE9, 0x75, 0xDD, 0x09, 0x0A, 0x98, 0x17, +0x33, 0x20, 0xC0, 0x7C, 0x86, 0x49, 0xB7, 0x6E, 0x6C, 0x86, 0x76, 0x73, 0x7E, 0x83, 0xCE, 0xFE, +0x0C, 0x47, 0x3C, 0xFA, 0xBA, 0x1F, 0xC3, 0xB7, 0x95, 0x3F, 0xBF, 0x34, 0xFB, 0x51, 0xF9, 0xF8, +0x18, 0xF8, 0x36, 0x4B, 0x33, 0x1F, 0xE2, 0x43, 0x7C, 0x08, 0x10, 0x40, 0x80, 0x00, 0x4B, 0x6C, +0xA3, 0xBB, 0x05, 0xEF, 0x9C, 0x06, 0x9D, 0xB5, 0xA1, 0x01, 0x5A, 0xEF, 0xBF, 0xFC, 0x0F, 0x07, +0x4A, 0x3E, 0xFB, 0x31, 0x3C, 0x9E, 0xCB, 0xD7, 0x7E, 0x64, 0x95, 0x8D, 0x8F, 0x30, 0x1C, 0x1F, +0xE3, 0x9E, 0x76, 0x25, 0x3E, 0xC4, 0x47, 0xFF, 0xAE, 0xDA, 0x69, 0x27, 0xAC, 0x1B, 0x01, 0x60, +0x4E, 0x3C, 0x05, 0x0B, 0x98, 0x97, 0x46, 0xEF, 0xAA, 0xCB, 0xB3, 0x1F, 0x74, 0x0E, 0xED, 0x76, +0x35, 0xFA, 0x94, 0xAB, 0xD2, 0x33, 0xB3, 0xC6, 0xCD, 0x88, 0xA4, 0x5D, 0xAF, 0x5A, 0xD5, 0x8D, +0x8F, 0x28, 0x3E, 0xC4, 0xC7, 0x49, 0x65, 0x7B, 0x75, 0xA7, 0x28, 0x40, 0x80, 0x00, 0xCB, 0xE6, +0xDA, 0x1D, 0x03, 0x64, 0x56, 0x03, 0xFE, 0x31, 0x6B, 0x3F, 0x46, 0x6E, 0x2B, 0xBF, 0x6F, 0x27, +0x2E, 0xC7, 0xD3, 0xAE, 0xC4, 0x87, 0xF8, 0x98, 0x38, 0x40, 0xF6, 0x05, 0x08, 0x30, 0x37, 0x9E, +0x82, 0x05, 0xCC, 0x47, 0xF3, 0x85, 0xFA, 0xCC, 0x07, 0x9D, 0xBD, 0xBD, 0x65, 0xE3, 0x40, 0x64, +0x1C, 0x0E, 0xCA, 0xC3, 0xB8, 0xD9, 0x8F, 0x30, 0x3A, 0xFB, 0x11, 0x5B, 0xCB, 0xB7, 0xDB, 0x55, +0x58, 0x5F, 0x0F, 0x57, 0x1B, 0xFF, 0x51, 0x2F, 0x3E, 0xBE, 0x2C, 0x3E, 0xC4, 0xC7, 0x70, 0x81, +0x84, 0xF8, 0x89, 0x1F, 0x6E, 0x38, 0x51, 0x01, 0x02, 0x04, 0x58, 0x1E, 0xB1, 0xBD, 0x39, 0xFB, +0x41, 0x67, 0xE9, 0x7E, 0x6A, 0xA5, 0x59, 0x80, 0x81, 0x40, 0x39, 0xBC, 0x2D, 0x0E, 0x8F, 0xE9, +0xD2, 0xED, 0xDB, 0x4B, 0x10, 0x1F, 0x61, 0x34, 0x3E, 0xD2, 0xCC, 0xC7, 0xDA, 0xD5, 0xAB, 0xDD, +0xF0, 0x68, 0x3D, 0x2F, 0x3E, 0xC4, 0xC7, 0xA0, 0xF6, 0x76, 0x7A, 0x5D, 0x77, 0xA2, 0x02, 0x04, +0x08, 0xB0, 0x1C, 0xED, 0x71, 0xEB, 0xC6, 0x46, 0x88, 0x07, 0x1B, 0x33, 0x1D, 0x74, 0xF6, 0xEE, +0x67, 0x78, 0x41, 0x79, 0x2C, 0x0D, 0xCC, 0x07, 0xA2, 0x23, 0x0E, 0x0E, 0xD8, 0xF3, 0xB7, 0x77, +0x3A, 0xAF, 0xDA, 0xD3, 0x78, 0x3C, 0x8B, 0x17, 0x1F, 0x17, 0xEE, 0xBF, 0xAF, 0x1B, 0x1F, 0xE9, +0xA9, 0x57, 0xE2, 0x43, 0x7C, 0x0C, 0xEB, 0x3E, 0x3D, 0x52, 0x80, 0x00, 0x02, 0x04, 0x58, 0x1A, +0x9B, 0xA1, 0xB5, 0x3B, 0xD7, 0xF8, 0x18, 0x79, 0x1A, 0xD6, 0xF0, 0xEC, 0xC7, 0xD0, 0xC2, 0xF3, +0x98, 0x75, 0xFE, 0xB2, 0x9D, 0x89, 0x0F, 0xF1, 0xB1, 0x7A, 0xF1, 0x91, 0x7F, 0x99, 0xFC, 0x62, +0x84, 0x76, 0xC2, 0x02, 0x04, 0x08, 0xB0, 0x44, 0x01, 0x32, 0xBC, 0x00, 0x7D, 0xD6, 0x8B, 0xBC, +0xCB, 0xDB, 0xEE, 0x0E, 0x47, 0x47, 0xAC, 0x0D, 0x3D, 0x94, 0xCE, 0x8D, 0xBB, 0x71, 0x0A, 0x63, +0x3D, 0xF1, 0x21, 0x3E, 0xAA, 0xFA, 0xB5, 0x3A, 0x5A, 0x2F, 0x6D, 0x38, 0x55, 0x01, 0x02, 0x04, +0x58, 0x16, 0x83, 0xD7, 0x00, 0x99, 0x41, 0x7C, 0x8C, 0xCC, 0x7E, 0x14, 0x6F, 0x97, 0x16, 0x65, +0x77, 0xD5, 0x06, 0x66, 0x3F, 0xF2, 0xF8, 0x48, 0x13, 0x1F, 0x3B, 0x71, 0x6A, 0x8F, 0x45, 0x7C, +0x88, 0x8F, 0xCA, 0xC5, 0x47, 0xFE, 0x25, 0x8F, 0x59, 0xA7, 0x05, 0x20, 0x40, 0x80, 0x4A, 0x69, +0xEF, 0xDD, 0x98, 0x59, 0x7C, 0xC4, 0xF1, 0xEF, 0x1A, 0xDE, 0x62, 0xB7, 0x18, 0xA0, 0x0F, 0x7E, +0x7C, 0xEF, 0x2F, 0x67, 0x9E, 0xFD, 0x10, 0x1F, 0xE2, 0xA3, 0xE2, 0xF1, 0x91, 0x7F, 0xD9, 0x83, +0x10, 0x3F, 0xFE, 0x43, 0x22, 0x04, 0x10, 0x20, 0xC0, 0x12, 0xC8, 0x9A, 0xF5, 0x99, 0xC4, 0xC7, +0xF0, 0xDF, 0xC6, 0xEC, 0x78, 0x35, 0x38, 0xFB, 0x51, 0x5E, 0xFB, 0x11, 0xBB, 0x7F, 0xB6, 0xC3, +0x19, 0x67, 0x3F, 0xC4, 0x87, 0xF8, 0x58, 0x82, 0xF8, 0xC8, 0xFF, 0x9D, 0xEE, 0xA4, 0xD7, 0x75, +0x27, 0x2C, 0x40, 0x80, 0x00, 0xD5, 0xD7, 0xDA, 0xA9, 0xCF, 0x24, 0x3E, 0x62, 0x77, 0x3C, 0x3E, +0xFC, 0xEE, 0x18, 0x6A, 0x23, 0xE3, 0xDD, 0xC1, 0xD9, 0x8F, 0x78, 0xF8, 0x70, 0xB6, 0xCF, 0x32, +0xFB, 0x21, 0x3E, 0xC4, 0xC7, 0x92, 0xC4, 0x47, 0xFE, 0xE5, 0x53, 0x8D, 0x07, 0x33, 0x20, 0x80, +0x00, 0x01, 0xAA, 0x2D, 0xDE, 0xBA, 0xB1, 0x19, 0xB2, 0xFD, 0x99, 0x0D, 0xD0, 0x06, 0xB6, 0xD1, +0x2D, 0x47, 0x48, 0x69, 0xD7, 0xAB, 0xC1, 0x10, 0x29, 0xC5, 0x47, 0x1A, 0x6F, 0xED, 0xC5, 0xA9, +0x3D, 0x16, 0xF1, 0x21, 0x3E, 0x2A, 0x1B, 0x1F, 0x49, 0xB6, 0x97, 0x5E, 0x5F, 0x77, 0xD6, 0x02, +0x04, 0x08, 0x50, 0xF5, 0x04, 0xA9, 0x8F, 0xEC, 0x80, 0x35, 0x8D, 0x01, 0xDA, 0x31, 0xB3, 0x1F, +0xA5, 0x26, 0x19, 0x1C, 0xA8, 0x0F, 0xCF, 0x9E, 0x6C, 0x8B, 0x0F, 0xF1, 0x21, 0x3E, 0x06, 0x23, +0x64, 0xA7, 0xEE, 0x9C, 0x05, 0x08, 0x10, 0xA0, 0xEA, 0xC6, 0x5F, 0x03, 0x64, 0x0A, 0x03, 0xB4, +0xA3, 0x66, 0x3F, 0xC6, 0xAD, 0xFD, 0xC8, 0x9F, 0x76, 0x55, 0xBE, 0xAB, 0xB4, 0xF3, 0xD5, 0xA9, +0x66, 0x3F, 0xC4, 0x87, 0xF8, 0x58, 0xD2, 0xF8, 0xC8, 0x1F, 0x8E, 0x9D, 0xB0, 0x00, 0x01, 0x02, +0x54, 0x5D, 0xD6, 0xBA, 0xDE, 0x7B, 0x6E, 0xF9, 0xF4, 0x06, 0x68, 0x13, 0xCC, 0x7E, 0x1C, 0x6E, +0xC1, 0x5B, 0xEB, 0x6E, 0xB5, 0x3B, 0x7C, 0x51, 0xC2, 0x97, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0xA3, +0xFF, 0x5E, 0xF7, 0xD2, 0x4E, 0x58, 0x75, 0x27, 0x2E, 0x40, 0x80, 0x00, 0xD5, 0xD5, 0xDA, 0x3A, +0xC3, 0xC5, 0xCD, 0xE2, 0x9D, 0xC7, 0xB5, 0x47, 0xCD, 0x7E, 0x0C, 0xDD, 0xC7, 0xC0, 0x4D, 0xE9, +0xA2, 0xCF, 0x07, 0x71, 0x6A, 0x8F, 0x45, 0x7C, 0x88, 0x8F, 0xA5, 0x88, 0x8F, 0x5E, 0x80, 0x04, +0x3B, 0x61, 0x01, 0x02, 0x04, 0xA8, 0xB4, 0x98, 0x35, 0xA6, 0x3E, 0x40, 0x9B, 0x78, 0xF6, 0x63, +0x70, 0x31, 0x7A, 0xFF, 0xED, 0x6D, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0xF8, 0x87, 0x96, 0xEA, 0x3C, +0x34, 0x02, 0x80, 0x00, 0x01, 0x2A, 0xD9, 0x1E, 0xB7, 0x5E, 0x7F, 0xCA, 0x05, 0xE8, 0xF1, 0xD8, +0x77, 0x4D, 0x36, 0xFB, 0x31, 0xF4, 0xB4, 0xAB, 0xC2, 0x89, 0x67, 0x3F, 0xC4, 0x87, 0xF8, 0x58, +0x91, 0xF8, 0x48, 0xBA, 0x33, 0x20, 0xD7, 0x9C, 0xBD, 0x00, 0x01, 0x02, 0x54, 0x55, 0x3D, 0xB4, +0xB6, 0xA6, 0x3B, 0x40, 0xBB, 0xD3, 0x75, 0x3F, 0xFA, 0x5B, 0xED, 0xD6, 0xC6, 0x6F, 0xCB, 0xBB, +0x2D, 0x3E, 0xC4, 0x87, 0xF8, 0x38, 0x3E, 0x42, 0xEC, 0x84, 0x05, 0xCC, 0xD6, 0xBA, 0x43, 0x00, +0xCC, 0xD0, 0x66, 0x68, 0x9F, 0x64, 0x06, 0x24, 0xDE, 0xF1, 0xDD, 0x47, 0x5D, 0xF5, 0xBC, 0xFF, +0x94, 0xAB, 0xD2, 0xCC, 0x47, 0xF9, 0xF6, 0x5C, 0x33, 0x9E, 0x60, 0xF6, 0x43, 0x7C, 0x88, 0x8F, +0x15, 0x8C, 0x8F, 0xAE, 0x86, 0x53, 0x17, 0x30, 0x4B, 0x66, 0x40, 0x80, 0x59, 0xBA, 0x36, 0xF9, +0x53, 0xB0, 0x4E, 0x31, 0x40, 0x1B, 0x59, 0xFB, 0x11, 0x47, 0x6F, 0x2B, 0xDF, 0xFD, 0x96, 0xF8, +0x10, 0x1F, 0xE2, 0xE3, 0x8E, 0xDA, 0xDB, 0x76, 0xC2, 0x02, 0x04, 0x08, 0x50, 0x51, 0xAD, 0xAD, +0x09, 0xAF, 0x29, 0x70, 0xE7, 0x99, 0x8F, 0x81, 0x2D, 0x74, 0xFB, 0xB3, 0x1F, 0xB5, 0xD2, 0x4C, +0x47, 0x1C, 0x78, 0xFF, 0xE1, 0x40, 0xBE, 0x77, 0x1F, 0xE9, 0x9A, 0x1F, 0xED, 0x29, 0x3C, 0x16, +0xF1, 0x21, 0x3E, 0x96, 0x39, 0x3E, 0xF2, 0x87, 0x9C, 0x2F, 0x44, 0x17, 0x20, 0x80, 0x00, 0x01, +0x2A, 0x28, 0x6B, 0x4D, 0x10, 0x20, 0x27, 0x1C, 0xA0, 0xD5, 0x0E, 0x43, 0x64, 0x78, 0xE6, 0xA3, +0xFC, 0xEC, 0xAB, 0x81, 0xBB, 0x4D, 0x6F, 0xEF, 0x4C, 0xF2, 0x75, 0xC4, 0x87, 0xF8, 0x58, 0xF1, +0xF8, 0x38, 0x0C, 0x10, 0x17, 0x24, 0x04, 0x04, 0x08, 0x50, 0x41, 0xED, 0xBD, 0x8D, 0x33, 0x0F, +0xD0, 0xE2, 0xD0, 0x47, 0xF6, 0xFF, 0x5E, 0x1B, 0x98, 0xF9, 0x38, 0x9C, 0xFD, 0x38, 0x1C, 0xD4, +0xF7, 0x3F, 0x67, 0xA2, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0x47, 0xF7, 0xDF, 0xED, 0x76, 0x7A, +0x6D, 0x27, 0x2C, 0x40, 0x80, 0x00, 0xD5, 0x12, 0x6F, 0xBD, 0xBE, 0x71, 0xFC, 0xFA, 0x8F, 0xD3, +0xC5, 0xC7, 0xE0, 0xD3, 0xB0, 0x86, 0x16, 0x9C, 0x87, 0xA1, 0xA7, 0x5D, 0xA5, 0x3F, 0xB3, 0x30, +0xC1, 0xDA, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0x80, 0x6C, 0xDF, 0x0C, 0x08, 0x20, 0x40, 0x80, +0xCA, 0x39, 0xE6, 0x1A, 0x20, 0x67, 0x18, 0xA0, 0xF5, 0xB7, 0xDD, 0x8D, 0xA3, 0xD1, 0x11, 0x6B, +0xA3, 0xE3, 0xE3, 0xDD, 0x78, 0x87, 0x2F, 0x27, 0x3E, 0xC4, 0x87, 0xF8, 0x18, 0xFD, 0x36, 0x5A, +0x02, 0x04, 0x10, 0x20, 0x40, 0x05, 0x03, 0xE4, 0x60, 0xEB, 0xCC, 0x03, 0xB4, 0xF1, 0xDB, 0xEE, +0xC6, 0xC1, 0xAB, 0x9C, 0x87, 0xEE, 0xC0, 0x7E, 0x60, 0xF6, 0x23, 0x49, 0xB3, 0x1F, 0xC7, 0xAE, +0xFD, 0x10, 0x1F, 0xE2, 0x43, 0x7C, 0x8C, 0x95, 0xED, 0x6C, 0xC4, 0x8F, 0xFF, 0xD0, 0x86, 0xD3, +0x18, 0x20, 0x40, 0x80, 0xEA, 0x68, 0xEF, 0x5E, 0x3F, 0xD3, 0x00, 0x2D, 0x8E, 0xBF, 0x2D, 0x0E, +0xBD, 0xAB, 0x18, 0xC4, 0x87, 0x38, 0x66, 0x8C, 0x7C, 0xEC, 0xEC, 0x87, 0xF8, 0x10, 0x1F, 0xE2, +0xE3, 0xE8, 0x6F, 0x27, 0x5F, 0x34, 0x65, 0x16, 0x04, 0x10, 0x20, 0x40, 0x95, 0x02, 0xA4, 0x59, +0x3F, 0xEB, 0x00, 0x6D, 0x74, 0xF6, 0x23, 0x0E, 0x5C, 0x60, 0x70, 0x38, 0x3A, 0x26, 0x9F, 0xFD, +0x10, 0x1F, 0xE2, 0x43, 0x7C, 0x1C, 0xFF, 0x2D, 0xED, 0x0B, 0x10, 0x40, 0x80, 0x00, 0x15, 0x93, +0x35, 0x37, 0x4F, 0x3D, 0x40, 0x8B, 0xDD, 0x31, 0xFB, 0xF0, 0x00, 0x38, 0x0E, 0xDD, 0x78, 0xEC, +0xEC, 0xC7, 0xF6, 0x51, 0xB3, 0x1F, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x3B, 0xFF, 0xFB, 0xDD, 0x4B, +0xAF, 0xED, 0x84, 0x05, 0x08, 0x10, 0xA0, 0x1A, 0xE2, 0xAD, 0xD7, 0x6F, 0x1E, 0x2E, 0x40, 0x3F, +0xDD, 0x00, 0xAD, 0xBC, 0x93, 0x55, 0x2C, 0x6D, 0x87, 0x15, 0x4B, 0xBB, 0x5E, 0x0D, 0x86, 0x48, +0xE9, 0x73, 0xD2, 0xB3, 0x47, 0x76, 0xA3, 0xF8, 0x10, 0x1F, 0xE2, 0xE3, 0xD4, 0xDF, 0x5A, 0x3B, +0xBD, 0x98, 0x01, 0x01, 0x04, 0x08, 0x50, 0x19, 0x1B, 0xA1, 0xBD, 0x7B, 0xBA, 0x01, 0xDA, 0xC8, +0xEC, 0x47, 0xEC, 0xEF, 0x7C, 0x55, 0xDE, 0x5D, 0x77, 0x60, 0x30, 0x3F, 0xFC, 0xF9, 0xDB, 0xE2, +0x43, 0x7C, 0x88, 0x8F, 0x33, 0xCB, 0x76, 0xEB, 0x4E, 0x65, 0x80, 0x00, 0x01, 0x2A, 0x22, 0x36, +0x42, 0x7B, 0xFF, 0xEC, 0xE3, 0xDE, 0x18, 0x07, 0x2F, 0x40, 0x38, 0x66, 0xED, 0x47, 0x7F, 0x28, +0x58, 0xDC, 0x96, 0xD6, 0x7E, 0xEC, 0x45, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0x99, 0xBF, 0xCD, 0x7D, +0x01, 0x02, 0x08, 0x10, 0xA0, 0x32, 0xAE, 0x1D, 0x7F, 0x11, 0xC2, 0x63, 0xC6, 0x75, 0xB5, 0xA1, +0x41, 0xDE, 0xD0, 0xEC, 0x47, 0xFF, 0x2A, 0xE7, 0xC5, 0x80, 0xBE, 0x74, 0x11, 0xC2, 0xF1, 0xB3, +0x1F, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x53, 0xE9, 0xFC, 0x1B, 0x8E, 0x1F, 0x7F, 0x7B, 0xC3, 0xE9, +0x0C, 0x10, 0x20, 0xC0, 0xE2, 0x6B, 0xBE, 0x50, 0x3F, 0xDB, 0xD8, 0x37, 0x0E, 0x5D, 0xF7, 0x23, +0x0C, 0x0C, 0xEA, 0x8F, 0x1C, 0x0E, 0x1E, 0x84, 0xA1, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, +0xA9, 0x7F, 0x0F, 0xB2, 0x7C, 0x16, 0xB3, 0x1E, 0x00, 0x04, 0x08, 0xB0, 0xF8, 0x83, 0x97, 0x53, +0x2C, 0x5E, 0xED, 0xCF, 0x7E, 0xC4, 0x81, 0xDB, 0x26, 0x9A, 0xFD, 0x28, 0xDE, 0xDE, 0x16, 0x1F, +0xE2, 0x43, 0x7C, 0x4C, 0xED, 0xF7, 0xA0, 0x3B, 0x8B, 0x29, 0x40, 0x00, 0x01, 0x02, 0x2C, 0xF8, +0xD8, 0xE5, 0xD6, 0xC3, 0x1B, 0x21, 0x3B, 0x38, 0xD5, 0x15, 0x94, 0x63, 0x2C, 0x3F, 0xED, 0xAA, +0x34, 0xEE, 0x9B, 0x74, 0xF6, 0xE3, 0x20, 0x8A, 0x0F, 0xF1, 0x21, 0x3E, 0xA6, 0xF5, 0x7B, 0x10, +0xD3, 0x3F, 0xAA, 0x70, 0xC3, 0x59, 0x0D, 0x10, 0x20, 0xC0, 0xA2, 0xDB, 0xEC, 0xEE, 0x80, 0x75, +0xC6, 0x41, 0xDE, 0xA9, 0x67, 0x3F, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x53, 0xFB, 0x3D, 0x68, 0x6F, +0xD5, 0x9D, 0xD2, 0x00, 0x01, 0x02, 0x54, 0x20, 0x40, 0x4E, 0xB8, 0x00, 0x7D, 0x68, 0xB7, 0xAB, +0x63, 0x67, 0x3F, 0xE2, 0xD0, 0xC7, 0x24, 0xFD, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0x54, +0x7F, 0x0F, 0x62, 0x4B, 0x80, 0x00, 0x02, 0x04, 0x58, 0x78, 0x1B, 0x27, 0x9A, 0x01, 0x39, 0xE2, +0xB9, 0x55, 0x03, 0x57, 0x3D, 0x2F, 0xCF, 0x7E, 0x0C, 0xDD, 0x96, 0xDF, 0x70, 0x3B, 0x13, 0x1F, +0xE2, 0x43, 0x7C, 0xCC, 0xE2, 0xF7, 0x20, 0x1E, 0xA4, 0x9D, 0xB0, 0x5C, 0x90, 0x10, 0x10, 0x20, +0xC0, 0x02, 0x6B, 0xEF, 0x4D, 0xFE, 0x9C, 0xF1, 0xDE, 0xC0, 0xA7, 0xBF, 0x85, 0xEE, 0xD0, 0xEC, +0x47, 0xFF, 0x0A, 0xE7, 0xA5, 0xF7, 0x87, 0xE1, 0xAB, 0x9E, 0xA7, 0x5D, 0xAF, 0xDA, 0xE2, 0x43, +0x7C, 0x88, 0x8F, 0x99, 0xFC, 0x1E, 0xB4, 0x77, 0xD2, 0xEB, 0xBA, 0x13, 0x1B, 0x20, 0x40, 0x80, +0xC5, 0x95, 0x35, 0x27, 0x1B, 0xAC, 0x0C, 0x0F, 0x7C, 0x6A, 0xA5, 0xF0, 0x18, 0x5A, 0xFB, 0x31, +0x72, 0x5B, 0xF9, 0x7D, 0x3B, 0x99, 0xF8, 0x10, 0x1F, 0xE2, 0x63, 0x56, 0xBF, 0x07, 0xDD, 0x9D, +0xB0, 0xCC, 0x80, 0x00, 0x53, 0xB5, 0xEE, 0x10, 0x00, 0x53, 0xD5, 0xDA, 0xB9, 0x73, 0x80, 0x94, +0x06, 0x3E, 0xE5, 0xAB, 0x98, 0xC7, 0x70, 0x38, 0xC0, 0x0F, 0xE3, 0x66, 0x3F, 0xC2, 0xF0, 0xEC, +0x47, 0x27, 0x3E, 0xDA, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x99, 0xFD, 0x1E, 0x74, 0x77, 0xC2, 0xBA, +0xEE, 0xC4, 0x06, 0x4C, 0x93, 0x19, 0x10, 0x60, 0x7A, 0x63, 0x9A, 0x5B, 0x0F, 0x6F, 0xDE, 0xF1, +0x0A, 0xE8, 0xE3, 0x06, 0x3E, 0xB5, 0xC3, 0xD0, 0x18, 0x9E, 0xF9, 0x28, 0x6E, 0x8B, 0xC3, 0xE3, +0xC3, 0x74, 0x3F, 0x3B, 0x53, 0x18, 0x30, 0x8A, 0x0F, 0xF1, 0x21, 0x3E, 0x8E, 0xD7, 0xBE, 0x5D, +0x0F, 0x00, 0x02, 0x04, 0x58, 0x50, 0xF5, 0x63, 0x03, 0x64, 0x68, 0xE0, 0x13, 0x87, 0xDE, 0x88, +0xA5, 0x41, 0xFE, 0x40, 0x74, 0xC4, 0xC1, 0xDB, 0xF3, 0xD7, 0x29, 0x3E, 0xCE, 0x3A, 0xFB, 0x21, +0x3E, 0xC4, 0x87, 0xF8, 0x98, 0x84, 0xA7, 0x60, 0x01, 0x02, 0x04, 0x58, 0x58, 0x9B, 0xA1, 0xB5, +0x7B, 0xE2, 0xF8, 0x18, 0x59, 0x84, 0x3E, 0x3C, 0xFB, 0x51, 0x7E, 0xDA, 0x55, 0x7A, 0x23, 0x2D, +0xFB, 0x38, 0xEB, 0xEC, 0x87, 0xF8, 0x10, 0x1F, 0xE2, 0x63, 0x32, 0xD9, 0x9E, 0x9D, 0xB0, 0x00, +0x01, 0x02, 0x2C, 0xA8, 0xAC, 0x75, 0x3D, 0xC4, 0xF6, 0xC9, 0x07, 0x3E, 0xE5, 0x6D, 0x77, 0x87, +0xA3, 0x23, 0xD6, 0x4A, 0x77, 0xD3, 0xBB, 0x9F, 0xDD, 0x78, 0xB6, 0x71, 0xA3, 0xF8, 0x10, 0x1F, +0xE2, 0x63, 0x72, 0xED, 0xBD, 0xF4, 0x7A, 0xC3, 0x09, 0x0E, 0x10, 0x20, 0xC0, 0xE2, 0x69, 0x6D, +0x6D, 0x4C, 0x32, 0xF0, 0x39, 0xEA, 0xA2, 0x83, 0xB1, 0x7C, 0x6D, 0x8F, 0xD0, 0x0D, 0x80, 0xC3, +0xD9, 0x8F, 0xDE, 0x8D, 0x67, 0x9D, 0xFD, 0x10, 0x1F, 0xE2, 0x43, 0x7C, 0x9C, 0xF0, 0xF3, 0xF3, +0x85, 0xE8, 0x0D, 0x27, 0x38, 0x40, 0x80, 0x00, 0x0B, 0x38, 0xD0, 0xC9, 0x1A, 0x27, 0x1E, 0xF8, +0x8C, 0xD9, 0x62, 0xB7, 0x18, 0xEC, 0x97, 0xEB, 0xA4, 0x7F, 0x57, 0x67, 0x99, 0xFD, 0x10, 0x1F, +0xE2, 0x43, 0x7C, 0x9C, 0x5C, 0x96, 0xCF, 0x80, 0x5C, 0x73, 0x82, 0x03, 0x04, 0x08, 0xB0, 0x58, +0xE3, 0x9C, 0x5B, 0x0F, 0x0F, 0x2E, 0x40, 0x3F, 0x6A, 0xB0, 0x3F, 0x10, 0x1E, 0xA5, 0x1B, 0x47, +0x66, 0x3F, 0x8A, 0x81, 0x7F, 0xE9, 0x86, 0xF4, 0xEC, 0xAE, 0xED, 0x53, 0x0E, 0xA8, 0xC4, 0x87, +0xF8, 0x10, 0x1F, 0xA7, 0xD7, 0x9E, 0x60, 0x7B, 0x6D, 0x00, 0x01, 0x02, 0xCC, 0x59, 0x3D, 0xB4, +0xB6, 0x4E, 0x36, 0xF0, 0x29, 0xAF, 0xFD, 0x08, 0x83, 0x03, 0xFE, 0xF2, 0xBE, 0xBB, 0xFD, 0xBB, +0x13, 0x1F, 0xE2, 0x43, 0x7C, 0x9C, 0xD3, 0xEF, 0x41, 0xBB, 0xE1, 0x14, 0x07, 0x08, 0x10, 0x60, +0xD1, 0x6C, 0x86, 0x76, 0xF3, 0xD8, 0x81, 0xCF, 0x71, 0x6B, 0x3F, 0xC2, 0xC8, 0xEC, 0x47, 0x1C, +0xDC, 0xFD, 0x2A, 0xCD, 0x7E, 0xEC, 0x9D, 0x62, 0x50, 0x25, 0x3E, 0xC4, 0x87, 0xF8, 0x38, 0xBB, +0xEE, 0x4E, 0x58, 0x75, 0xA7, 0x39, 0x40, 0x80, 0x00, 0x8B, 0xE4, 0x5A, 0x68, 0xEF, 0x9F, 0x68, +0x3C, 0x58, 0x5E, 0xFB, 0x51, 0xDE, 0xF9, 0x2A, 0x0E, 0x0F, 0xA0, 0xD2, 0x5F, 0xB7, 0xC5, 0x87, +0xF8, 0x10, 0x1F, 0xE7, 0xF6, 0x7B, 0x90, 0xE5, 0x0B, 0xD1, 0x05, 0x08, 0x20, 0x40, 0x80, 0x05, +0x72, 0xB0, 0xB5, 0x39, 0xD1, 0x10, 0xF0, 0xB8, 0xB5, 0x1F, 0x43, 0xD7, 0xFF, 0xE8, 0x7F, 0x60, +0xDA, 0xF9, 0xEA, 0xA4, 0xB3, 0x1F, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x29, 0xDE, 0xB7, 0x9D, 0xB0, +0x00, 0x01, 0x02, 0x2C, 0xDC, 0xE0, 0xA7, 0x35, 0xF9, 0x85, 0xCA, 0x86, 0x77, 0xBE, 0x2A, 0x6D, +0xC1, 0x3B, 0xF6, 0x02, 0x85, 0x2F, 0x89, 0x0F, 0xF1, 0x21, 0x3E, 0xCE, 0x2D, 0x3E, 0x92, 0xF6, +0x76, 0xFA, 0x37, 0x6E, 0x27, 0x2C, 0x40, 0x80, 0x00, 0x0B, 0xA4, 0xBD, 0xB7, 0x71, 0xC7, 0x61, +0xE0, 0xB1, 0xB3, 0x1F, 0x71, 0xF4, 0x63, 0x92, 0xF4, 0x1F, 0x5E, 0x0F, 0x4E, 0x30, 0xB8, 0x12, +0x1F, 0xE2, 0x43, 0x7C, 0xCC, 0xE8, 0xEB, 0xB4, 0xEB, 0x4E, 0x74, 0x80, 0x00, 0x01, 0x16, 0x63, +0xFC, 0x73, 0xEB, 0xE1, 0xC6, 0xC4, 0xEB, 0x3F, 0xC6, 0xCE, 0x7E, 0xC4, 0xEE, 0xE0, 0x7F, 0xE0, +0xB6, 0x70, 0xF2, 0xB5, 0x1F, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x19, 0x7E, 0xAD, 0x13, 0xCC, 0x72, +0x02, 0x08, 0x10, 0x60, 0xC6, 0x06, 0xAF, 0x01, 0x32, 0x6E, 0x28, 0x78, 0xE4, 0xEC, 0x47, 0x1C, +0x8A, 0x93, 0x92, 0x93, 0xCC, 0x7E, 0x88, 0x0F, 0xF1, 0x21, 0x3E, 0x66, 0x2B, 0xDB, 0xD9, 0x88, +0x1F, 0x7F, 0xFB, 0x86, 0xD3, 0x1D, 0x20, 0x40, 0x80, 0xC5, 0x08, 0x90, 0x83, 0xAD, 0x89, 0xC6, +0x86, 0xFD, 0xEB, 0x7E, 0xA4, 0xB1, 0x53, 0x16, 0x07, 0x03, 0x20, 0x84, 0xD3, 0xCD, 0x7E, 0x88, +0x0F, 0xF1, 0x21, 0x3E, 0xE6, 0x10, 0x20, 0xF9, 0x42, 0x74, 0xB3, 0x20, 0x80, 0x00, 0x01, 0x16, +0x40, 0x7B, 0xF7, 0xFA, 0x91, 0xC3, 0xC1, 0xF2, 0xAE, 0x56, 0xA5, 0xC0, 0xC8, 0x9F, 0x76, 0x35, +0x34, 0x66, 0x1C, 0xB8, 0xEE, 0x47, 0xDA, 0xF5, 0x6A, 0x92, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, +0xC7, 0x9C, 0xBE, 0xAE, 0x00, 0x01, 0x04, 0x08, 0xB0, 0x30, 0x01, 0xD2, 0xAC, 0x1F, 0xF9, 0xBE, +0x5A, 0x29, 0x3C, 0x42, 0xED, 0xF0, 0x69, 0x57, 0x31, 0x8C, 0xAE, 0x07, 0x29, 0x8F, 0x25, 0x77, +0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x0B, 0x13, 0x1F, 0xF9, 0xBF, 0xF3, 0xED, 0xF4, 0xDA, 0x4E, 0x58, +0xC0, 0x99, 0xAD, 0x3B, 0x04, 0xC0, 0x99, 0x65, 0xCD, 0xCD, 0xB1, 0x43, 0xC2, 0x58, 0x1E, 0x1E, +0xD6, 0xFA, 0xBB, 0x5D, 0xC5, 0xE1, 0xEB, 0x7C, 0xC4, 0x31, 0xB3, 0x1F, 0x6D, 0xF1, 0x21, 0x3E, +0xC4, 0xC7, 0xC2, 0xC4, 0x47, 0xFF, 0x31, 0xB4, 0xCD, 0x80, 0x00, 0x67, 0x66, 0x06, 0x04, 0x38, +0xDB, 0x78, 0xE4, 0xD6, 0xC3, 0xF5, 0x23, 0x77, 0xC0, 0xAA, 0x1D, 0x86, 0xC6, 0xF0, 0xCC, 0x47, +0xF1, 0xB4, 0xAC, 0x91, 0x31, 0xD5, 0x24, 0xB3, 0x1F, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x73, 0xFA, +0x8F, 0x0D, 0x7B, 0x02, 0x04, 0x10, 0x20, 0xC0, 0xB9, 0xEB, 0x04, 0xC8, 0xEE, 0xF8, 0x61, 0x61, +0x79, 0x06, 0xA4, 0x34, 0xF3, 0x71, 0xB8, 0x36, 0xA4, 0x76, 0x18, 0x06, 0xC5, 0xE7, 0xEC, 0xDE, +0x61, 0xF6, 0x43, 0x7C, 0x88, 0x0F, 0xF1, 0x71, 0x8E, 0x01, 0xB2, 0x6F, 0x17, 0x2C, 0x40, 0x80, +0x00, 0xE7, 0xAE, 0x11, 0xDA, 0xCD, 0xB1, 0xF1, 0x31, 0xB2, 0x08, 0x7D, 0x78, 0xF6, 0xA3, 0x3C, +0xB6, 0xCA, 0x77, 0xC5, 0x0A, 0xC7, 0xEF, 0x7C, 0x25, 0x3E, 0xC4, 0x87, 0xF8, 0x38, 0xE7, 0xC7, +0xD3, 0x0C, 0xF1, 0xE3, 0x6F, 0x6F, 0x38, 0xED, 0x01, 0x02, 0x04, 0x38, 0x4F, 0xD7, 0x8E, 0xBA, +0x06, 0xC8, 0xE0, 0x95, 0xCE, 0x0F, 0x83, 0x23, 0x96, 0x66, 0x3F, 0x06, 0xC6, 0x58, 0xBB, 0xF1, +0xE8, 0xB1, 0xA5, 0xF8, 0x10, 0x1F, 0xE2, 0xE3, 0xFC, 0x65, 0x7B, 0xE9, 0x75, 0xDD, 0x69, 0x0F, +0x10, 0x20, 0xC0, 0xF9, 0x69, 0xBE, 0x50, 0x1F, 0x19, 0x1A, 0xC6, 0xC3, 0xA7, 0x5D, 0x0D, 0x2C, +0x30, 0xEF, 0x7F, 0x40, 0x6D, 0x74, 0xFD, 0x47, 0x9A, 0xFD, 0x38, 0x6A, 0xED, 0x87, 0xF8, 0x10, +0x1F, 0xE2, 0x63, 0x11, 0x1E, 0x94, 0x00, 0x01, 0x04, 0x08, 0xB0, 0x00, 0xB2, 0xD6, 0xE6, 0x51, +0x03, 0xA8, 0xE1, 0x2D, 0x76, 0x8B, 0x20, 0x08, 0x71, 0xCC, 0x38, 0xEB, 0xA8, 0xD9, 0x0F, 0xF1, +0x21, 0x3E, 0xC4, 0xC7, 0x62, 0xC4, 0x47, 0xFE, 0x47, 0xBB, 0xF3, 0xD2, 0xBA, 0xE1, 0xC4, 0x07, +0x08, 0x10, 0xE0, 0x7C, 0x86, 0x24, 0xB7, 0x1E, 0xDE, 0xE8, 0x0C, 0x46, 0x36, 0x06, 0x86, 0x87, +0xBD, 0x8B, 0x0C, 0x0E, 0xAC, 0xF7, 0x88, 0x83, 0xE3, 0xC7, 0x89, 0x67, 0x3F, 0xC4, 0x87, 0xF8, +0x10, 0x1F, 0x8B, 0x13, 0x1F, 0xFD, 0x7F, 0xAF, 0x7B, 0x75, 0x67, 0x3F, 0x40, 0x80, 0x00, 0xE7, +0x65, 0x33, 0xB4, 0x76, 0x46, 0x07, 0x50, 0xBD, 0x0B, 0x0C, 0x8E, 0xDC, 0x7C, 0xD4, 0xEC, 0xC7, +0xD6, 0x98, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0xE2, 0xC5, 0x47, 0x7E, 0x53, 0x4B, 0x80, +0x00, 0x02, 0x04, 0x38, 0x37, 0xF5, 0xB4, 0x00, 0x3D, 0x96, 0x06, 0x50, 0x83, 0x33, 0x21, 0xA1, +0xB4, 0xEF, 0xEE, 0xE1, 0x70, 0x66, 0xE0, 0xA2, 0x83, 0x69, 0xCB, 0xDD, 0xBD, 0x28, 0x3E, 0xC4, +0x87, 0xF8, 0xA8, 0x42, 0x7C, 0xE4, 0x37, 0x1F, 0xD8, 0x09, 0x0B, 0x10, 0x20, 0xC0, 0xB9, 0x8D, +0x4F, 0xEA, 0xA1, 0xB5, 0x5B, 0x1A, 0xFD, 0x87, 0xFE, 0xEC, 0x47, 0x1C, 0x09, 0x8E, 0xDA, 0xE8, +0x70, 0x26, 0xDD, 0xB0, 0x2D, 0x3E, 0xC4, 0x87, 0xF8, 0xA8, 0x4C, 0x7C, 0xE4, 0xFF, 0xD1, 0x60, +0x3B, 0xBD, 0x76, 0x3D, 0x10, 0x40, 0x80, 0x00, 0xE7, 0xA0, 0xBD, 0x77, 0x63, 0x60, 0xB7, 0xAB, +0xF2, 0xD8, 0x65, 0xCC, 0xDA, 0x8F, 0x81, 0xEB, 0x81, 0x24, 0x69, 0xED, 0xC7, 0x5E, 0x14, 0x1F, +0xE2, 0x43, 0x7C, 0x54, 0x25, 0x3E, 0xF2, 0x7F, 0xB7, 0xF9, 0xB6, 0xDB, 0xAE, 0x88, 0x0E, 0x08, +0x10, 0xE0, 0x1C, 0x64, 0xCD, 0xFA, 0x60, 0x1D, 0x0C, 0xCE, 0x7E, 0x1C, 0x6E, 0xC1, 0x5B, 0x1B, +0x7F, 0x81, 0xC2, 0x2D, 0xF1, 0x21, 0x3E, 0xC4, 0x47, 0xA5, 0xE2, 0x23, 0xFF, 0x90, 0x83, 0xF4, +0xFA, 0xBA, 0x13, 0x20, 0x20, 0x40, 0x80, 0xF9, 0x0F, 0x55, 0x0E, 0xB6, 0xEB, 0x03, 0x51, 0x51, +0x8C, 0x5F, 0xE2, 0x98, 0xB1, 0xD5, 0xF0, 0x3A, 0xF3, 0x34, 0x86, 0xD9, 0x8F, 0xE2, 0x43, 0x7C, +0x88, 0x8F, 0x2A, 0xC5, 0x47, 0xA1, 0x7D, 0xBB, 0xEE, 0x0C, 0x08, 0x08, 0x10, 0x60, 0xBE, 0x43, +0x95, 0x8F, 0x3E, 0xB4, 0x19, 0xB2, 0xFD, 0xA1, 0xC0, 0x98, 0x60, 0xF6, 0xA3, 0x78, 0x5A, 0xD6, +0xB6, 0xF8, 0x10, 0x1F, 0xE2, 0xA3, 0x92, 0xF1, 0x91, 0x7F, 0x78, 0xDB, 0x53, 0xB0, 0x00, 0x01, +0x02, 0xCC, 0x7D, 0x0C, 0x95, 0xEF, 0x80, 0x75, 0xEA, 0xD9, 0x8F, 0x83, 0x28, 0x3E, 0xC4, 0x87, +0xF8, 0xA8, 0x62, 0x7C, 0xE4, 0x9F, 0x92, 0xEF, 0x84, 0x25, 0x42, 0x00, 0x01, 0x02, 0xCC, 0xD5, +0xE6, 0xE1, 0x0E, 0x58, 0x27, 0x9D, 0xFD, 0xC8, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x55, 0x8D, 0x8F, +0xA4, 0x9D, 0xFF, 0xDB, 0xAF, 0x3B, 0x0D, 0x02, 0x02, 0x04, 0x98, 0x9F, 0xEC, 0xE0, 0x5A, 0xC8, +0xDA, 0xA7, 0x98, 0xFD, 0xE8, 0xFC, 0xAD, 0x29, 0x3E, 0xC4, 0x87, 0xF8, 0xA8, 0x6C, 0x7C, 0xE4, +0x9F, 0x7A, 0xD0, 0xFD, 0x8F, 0x10, 0x00, 0x02, 0x04, 0x98, 0x9B, 0x56, 0x79, 0x01, 0x7A, 0x6D, +0x30, 0x38, 0xCA, 0xA1, 0x50, 0xBA, 0x2D, 0x1F, 0xEF, 0xBC, 0x94, 0x89, 0x0F, 0xF1, 0x21, 0x3E, +0xAA, 0x1C, 0x1F, 0xF9, 0x7F, 0x80, 0xD8, 0x4B, 0x57, 0x44, 0xB7, 0x13, 0x16, 0x20, 0x40, 0x80, +0x79, 0x8E, 0x5F, 0xB2, 0xC6, 0xE8, 0x53, 0xAE, 0x86, 0xAE, 0xF3, 0x11, 0x86, 0xAE, 0x7A, 0xBE, +0x9B, 0x75, 0xAF, 0x7C, 0x2E, 0x3E, 0xC4, 0x87, 0xF8, 0xA8, 0x6E, 0x7C, 0xF4, 0x23, 0x64, 0xDF, +0xC5, 0x08, 0x01, 0x01, 0x02, 0xCC, 0x47, 0xF6, 0x7B, 0x0F, 0xD5, 0x43, 0xBB, 0x39, 0x76, 0xED, +0xC7, 0xC8, 0x6D, 0xFD, 0x4F, 0xEA, 0xDC, 0xB2, 0x13, 0xC5, 0x87, 0xF8, 0x10, 0x1F, 0xCB, 0x10, +0x1F, 0xDD, 0x7F, 0xD4, 0x0D, 0x67, 0x43, 0x40, 0x80, 0x00, 0xF3, 0x1A, 0xC3, 0xD4, 0x63, 0xEB, +0x76, 0x3F, 0x16, 0xC2, 0xB8, 0xD9, 0x8F, 0xD2, 0xAC, 0x48, 0xFE, 0x2A, 0x5D, 0xF1, 0xBC, 0x2D, +0x3E, 0xC4, 0x87, 0xF8, 0x58, 0x8E, 0xF8, 0x08, 0xF9, 0xD3, 0xB0, 0xE2, 0xC7, 0xDF, 0x5E, 0x77, +0x42, 0x04, 0x04, 0x08, 0x30, 0x8F, 0x61, 0xCC, 0x66, 0x9A, 0x01, 0x19, 0x9E, 0xF9, 0x28, 0x16, +0x9C, 0x0F, 0x8C, 0xBD, 0xD2, 0x5F, 0xD2, 0xB2, 0x8F, 0xA1, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, +0x47, 0xA8, 0xF6, 0xF1, 0xB7, 0x13, 0x16, 0x20, 0x40, 0x80, 0x39, 0xBA, 0x16, 0xDB, 0x07, 0x03, +0x33, 0x1F, 0xFD, 0xD9, 0x8F, 0x58, 0x2B, 0x05, 0x44, 0x6F, 0xD0, 0xB3, 0x3B, 0x38, 0xFB, 0x21, +0x3E, 0xC4, 0x87, 0xF8, 0x08, 0xD5, 0x3F, 0xFE, 0xDD, 0x9D, 0xB0, 0x1A, 0x4E, 0x87, 0x80, 0x00, +0x01, 0x66, 0xEF, 0x60, 0x6B, 0x33, 0x94, 0x9F, 0x72, 0x55, 0x9E, 0xFD, 0x08, 0xA5, 0xA7, 0x5D, +0xA5, 0x3F, 0x87, 0x66, 0x3F, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x61, 0x39, 0x8E, 0x7F, 0xDA, 0x09, +0x2B, 0x84, 0x6B, 0x4E, 0x88, 0x80, 0x00, 0x01, 0x66, 0x2F, 0x3B, 0xA8, 0x17, 0x63, 0x9B, 0x7E, +0x6B, 0x14, 0x31, 0xD1, 0x1B, 0x88, 0x1D, 0xEE, 0x7C, 0x75, 0x78, 0x21, 0x10, 0xF1, 0x21, 0x3E, +0xC4, 0x47, 0x58, 0xAE, 0xE3, 0x9F, 0xED, 0xD7, 0x9D, 0x10, 0x01, 0x01, 0x02, 0xCC, 0x5E, 0x7B, +0xBF, 0x1E, 0xCB, 0xD7, 0xF6, 0x08, 0xDD, 0x98, 0x18, 0x78, 0xDA, 0x55, 0x3E, 0x38, 0x09, 0xFD, +0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0x47, 0x58, 0xBE, 0xE3, 0x1F, 0x5B, 0x0D, 0x27, 0x44, 0x40, +0x80, 0x00, 0xB3, 0x6D, 0x8F, 0xDF, 0x79, 0xB8, 0x11, 0xD3, 0x16, 0xBC, 0x61, 0x30, 0x1C, 0x42, +0xE9, 0x69, 0x57, 0xFD, 0xF1, 0xD8, 0x76, 0x1C, 0xB9, 0x3A, 0xBA, 0xF8, 0x10, 0x1F, 0xE2, 0x63, +0x89, 0x8E, 0x7F, 0xB6, 0x93, 0x76, 0xC2, 0x72, 0x3D, 0x10, 0x40, 0x80, 0x00, 0x33, 0x95, 0x5F, +0x03, 0x64, 0x70, 0xF6, 0x23, 0xE6, 0x33, 0x1F, 0x03, 0x63, 0xB1, 0xB4, 0xE8, 0x7C, 0x37, 0x8A, +0x0F, 0xF1, 0x21, 0x3E, 0x96, 0x35, 0x3E, 0xF2, 0x00, 0xC9, 0xFF, 0x63, 0xC4, 0xA6, 0xD3, 0x22, +0x20, 0x40, 0x80, 0x59, 0x8E, 0x6F, 0xEA, 0xF1, 0x60, 0xAB, 0x14, 0x0F, 0x61, 0xE4, 0xCA, 0xE7, +0xB9, 0x6D, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0x52, 0xC7, 0x47, 0xFE, 0x25, 0x0F, 0x04, 0x08, 0x20, +0x40, 0x80, 0x19, 0x8F, 0x37, 0xDA, 0xBB, 0xD7, 0x43, 0x39, 0x3A, 0xCA, 0x17, 0x21, 0x2C, 0xB4, +0x3B, 0x7F, 0xDF, 0x8D, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x65, 0x3F, 0xFE, 0xED, 0xED, 0xF4, 0xDA, +0x4E, 0x58, 0x80, 0x00, 0x01, 0x66, 0xA8, 0xD5, 0xAC, 0x1F, 0xEE, 0x7C, 0x15, 0x07, 0xC6, 0x3F, +0xFD, 0xB8, 0xD8, 0x16, 0x1F, 0xE2, 0x43, 0x7C, 0xAC, 0xCC, 0xF1, 0xCF, 0xF6, 0xCD, 0x80, 0x00, +0x02, 0x04, 0x98, 0xE1, 0x50, 0x27, 0x6B, 0x6E, 0x1E, 0x5E, 0x00, 0xA4, 0x56, 0xDA, 0x82, 0xB7, +0xF7, 0xFE, 0xF4, 0x8C, 0x8C, 0xDD, 0x28, 0x3E, 0xC4, 0x87, 0xF8, 0x58, 0x95, 0xE3, 0x1F, 0x5B, +0x02, 0x04, 0x10, 0x20, 0xC0, 0x6C, 0xB4, 0xFE, 0xC5, 0xC3, 0xF5, 0xD0, 0xDE, 0x3F, 0x5C, 0x70, +0x1E, 0xC7, 0x8C, 0x83, 0xB6, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x2B, 0x75, 0xFC, 0xB3, 0xBD, 0x0D, +0x3B, 0x61, 0x01, 0x02, 0x04, 0x98, 0xD1, 0x70, 0x27, 0xD6, 0xE3, 0xC1, 0xCE, 0xC0, 0xF0, 0x27, +0x96, 0x87, 0x42, 0x69, 0x43, 0x9C, 0x66, 0x14, 0x1F, 0xE2, 0x43, 0x7C, 0xAC, 0xD2, 0xF1, 0xB7, +0x10, 0x1D, 0x10, 0x20, 0xC0, 0x0C, 0xC7, 0x3C, 0x8D, 0x98, 0x15, 0x5B, 0xF0, 0x76, 0x03, 0xA3, +0x56, 0x7E, 0xF7, 0xB6, 0xF8, 0x10, 0x1F, 0xE2, 0x63, 0xE5, 0x8E, 0x7F, 0xB6, 0x97, 0x5E, 0xD7, +0x9D, 0x20, 0x01, 0x01, 0x02, 0xCC, 0x62, 0xD8, 0x73, 0x2D, 0x5D, 0x03, 0x24, 0x14, 0x31, 0x11, +0x4A, 0x5B, 0xF1, 0xA6, 0x9B, 0xF7, 0xA3, 0xF8, 0x10, 0x1F, 0xE2, 0x63, 0xD5, 0x8E, 0xBF, 0x00, +0x01, 0x04, 0x08, 0x30, 0x33, 0xFB, 0x2F, 0xD4, 0x87, 0xAF, 0xED, 0xD1, 0x7F, 0x0A, 0xD6, 0xED, +0x4C, 0x7C, 0x88, 0x0F, 0xF1, 0xB1, 0x8A, 0xC7, 0x3F, 0xA6, 0x7D, 0xB7, 0x5B, 0x37, 0x9C, 0x20, +0x01, 0x01, 0x02, 0x4C, 0x5F, 0x36, 0xBA, 0xDB, 0x4D, 0x2D, 0x8D, 0x89, 0xD2, 0x15, 0xCF, 0x0F, +0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x2B, 0x7B, 0xFC, 0xB3, 0xBD, 0xBA, 0x13, 0x24, 0x20, 0x40, 0x80, +0xA9, 0x6A, 0x7E, 0xE4, 0xA1, 0x8D, 0x4E, 0x80, 0x6C, 0xE4, 0x4F, 0xBF, 0x0A, 0x83, 0x8B, 0xCF, +0xB3, 0xDB, 0x41, 0x7C, 0x88, 0x0F, 0xF1, 0xB1, 0xCA, 0xC7, 0x3F, 0xDB, 0x17, 0x20, 0x80, 0x00, +0x01, 0xA6, 0xAC, 0x16, 0x36, 0x63, 0x6B, 0xA7, 0x5F, 0x1A, 0xB5, 0x62, 0x4C, 0xB4, 0x1B, 0xBA, +0x57, 0x3E, 0x17, 0x1F, 0xE2, 0x43, 0x7C, 0xAC, 0xEE, 0xF1, 0x8F, 0x07, 0x21, 0x7E, 0xFC, 0xED, +0x0D, 0x27, 0x4A, 0x40, 0x80, 0x00, 0xD3, 0x54, 0x4F, 0x0B, 0xD0, 0x63, 0xAF, 0x46, 0x8A, 0x3F, +0xB3, 0xDB, 0x51, 0x7C, 0x88, 0x0F, 0xF1, 0xB1, 0xEA, 0xC7, 0xDF, 0x42, 0x74, 0x40, 0x80, 0x00, +0x33, 0x18, 0x9F, 0xD5, 0x63, 0x6B, 0xB7, 0x3B, 0x01, 0xD2, 0x1B, 0x18, 0xC5, 0xAD, 0xCE, 0x5B, +0x2D, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0xCA, 0x1F, 0xFF, 0xAC, 0x29, 0x40, 0x00, 0x01, 0x02, 0x4C, +0x59, 0x7B, 0xAF, 0xBF, 0xCB, 0x4D, 0xAD, 0xB7, 0xDB, 0x55, 0x9A, 0xFD, 0x10, 0x1F, 0xE2, 0x43, +0x7C, 0x38, 0xFE, 0xF9, 0xC5, 0x08, 0xED, 0x84, 0x05, 0x08, 0x10, 0x60, 0xBA, 0x01, 0xD2, 0xAC, +0x97, 0xC7, 0x6C, 0xD9, 0x56, 0xE7, 0xCF, 0xAC, 0x37, 0x56, 0x12, 0x1F, 0xE2, 0x43, 0x7C, 0x38, +0xFE, 0xD9, 0xEE, 0x86, 0x13, 0x25, 0x30, 0x89, 0x75, 0x87, 0x00, 0x98, 0x48, 0x6B, 0xA7, 0x17, +0x20, 0xBD, 0xD9, 0x8F, 0xAD, 0xD8, 0x9F, 0xF9, 0x08, 0xE2, 0x43, 0x7C, 0x88, 0x0F, 0xC7, 0x3F, +0xB6, 0x37, 0x9D, 0x28, 0x81, 0x49, 0x98, 0x01, 0x01, 0xEE, 0xA8, 0xF9, 0xDB, 0x0F, 0x6D, 0x86, +0x6C, 0xBF, 0xD3, 0x1E, 0xB5, 0xFC, 0xEF, 0xF9, 0xEC, 0x47, 0xBB, 0x74, 0x15, 0x74, 0xF1, 0x21, +0x3E, 0xC4, 0x87, 0xE3, 0xDF, 0xDD, 0x09, 0x4B, 0x84, 0x00, 0x02, 0x04, 0x98, 0x8A, 0x7C, 0x07, +0xAC, 0x7C, 0x8C, 0x94, 0xC2, 0xE3, 0x76, 0x31, 0x58, 0xAA, 0x85, 0xDA, 0xC5, 0xF5, 0x70, 0xF5, +0xAD, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x95, 0x3F, 0xFE, 0xED, 0xED, 0xF4, 0xBD, 0xD4, 0x9D, 0x2E, +0x01, 0x01, 0x02, 0x4C, 0xC3, 0x66, 0x68, 0xEF, 0x75, 0xC7, 0x18, 0x2F, 0xC5, 0x7C, 0xED, 0x47, +0xFE, 0xD4, 0xAB, 0xF5, 0x4E, 0x7C, 0xA4, 0x99, 0x8F, 0x97, 0x8B, 0x0F, 0xF1, 0x21, 0x3E, 0x56, +0xFE, 0xF8, 0x77, 0x77, 0xC2, 0x32, 0x03, 0x02, 0xDC, 0x91, 0x35, 0x20, 0xC0, 0x1D, 0xD5, 0xB2, +0x83, 0x6B, 0x31, 0x3D, 0xE7, 0xAA, 0xF3, 0xBF, 0x6C, 0xBB, 0x3B, 0x64, 0x4A, 0x33, 0x1F, 0xF7, +0xBC, 0x59, 0x7C, 0x88, 0x0F, 0xF1, 0xE1, 0xF8, 0xF7, 0xBE, 0x97, 0x98, 0x07, 0xC8, 0x75, 0x67, +0x4C, 0x40, 0x80, 0x00, 0x67, 0xD7, 0xDA, 0xAE, 0xA7, 0xB5, 0xE7, 0x69, 0xF6, 0x23, 0x8D, 0x33, +0x52, 0x7C, 0xDC, 0xFD, 0x26, 0xF1, 0x21, 0x3E, 0xC4, 0x87, 0xE3, 0x3F, 0xF4, 0xBD, 0xB4, 0x8B, +0xCD, 0x2A, 0x00, 0x04, 0x08, 0x70, 0x06, 0xB5, 0xD0, 0x6E, 0x84, 0x56, 0xCC, 0x17, 0x9F, 0x87, +0x4B, 0x9D, 0xF8, 0x78, 0xE3, 0xEB, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x8E, 0xFF, 0x98, 0xEF, 0xC5, +0x4E, 0x58, 0xC0, 0x9D, 0x59, 0x03, 0x02, 0x1C, 0xAB, 0xFD, 0x3B, 0xAF, 0xAB, 0xA7, 0xA7, 0x56, +0xB4, 0xBE, 0x22, 0x3E, 0xC4, 0x87, 0xF8, 0x70, 0xFC, 0xEF, 0xF0, 0xBD, 0x64, 0x7B, 0x21, 0x7E, +0xFC, 0xAF, 0xD4, 0x9D, 0x39, 0x01, 0x01, 0x02, 0x9C, 0x45, 0x3D, 0xBB, 0x7D, 0x3B, 0xC4, 0xAC, +0x13, 0x1F, 0x6F, 0x10, 0x1F, 0xE2, 0x43, 0x7C, 0x38, 0xFE, 0xC7, 0x7C, 0x2F, 0xED, 0xDD, 0xFC, +0x9C, 0xE1, 0xB4, 0x09, 0x08, 0x10, 0xE0, 0xD4, 0x0E, 0xF6, 0xB3, 0xCD, 0xD6, 0xED, 0x2C, 0xDC, +0xD5, 0x89, 0x8F, 0x35, 0xF1, 0x21, 0x3E, 0xC4, 0x87, 0xE3, 0x7F, 0xCC, 0xF7, 0x12, 0xDB, 0x07, +0x61, 0xFF, 0xF6, 0x41, 0xC3, 0x99, 0x13, 0x38, 0x8E, 0x35, 0x20, 0xC0, 0xB1, 0x6E, 0x7F, 0x39, +0xBB, 0xB6, 0x7E, 0xED, 0x7A, 0xB8, 0x78, 0xE5, 0x6A, 0x58, 0x13, 0x1F, 0xE2, 0x43, 0x7C, 0x88, +0x8F, 0x23, 0xEC, 0xBE, 0x90, 0x85, 0xBD, 0x17, 0xB7, 0x42, 0xBC, 0xBC, 0x77, 0xCD, 0x99, 0x13, +0x10, 0x20, 0xC0, 0xA9, 0xAD, 0x7D, 0xE3, 0xD7, 0xBD, 0x3A, 0x76, 0xCE, 0x14, 0xB7, 0xFF, 0xF4, +0xD9, 0xB0, 0xBE, 0xF6, 0x5C, 0xB8, 0xE7, 0x81, 0x0B, 0x61, 0xED, 0xD4, 0x67, 0x0E, 0xF1, 0x21, +0x3E, 0xC4, 0xC7, 0xB2, 0xC5, 0xC7, 0xFE, 0x56, 0x0C, 0xBB, 0x5F, 0xCD, 0x42, 0xD6, 0xEA, 0xBD, +0x3F, 0xDB, 0xDD, 0x70, 0xE6, 0x04, 0x8E, 0x1D, 0x5B, 0x38, 0x04, 0xC0, 0x71, 0xFE, 0xCD, 0xAD, +0x7B, 0xDF, 0x71, 0xB0, 0xBD, 0xFB, 0xFE, 0xB8, 0xF7, 0xEC, 0x56, 0x6B, 0x2F, 0x86, 0x17, 0xFF, +0xB4, 0x15, 0xB6, 0x9F, 0xCB, 0xF2, 0x8B, 0x11, 0x8A, 0x0F, 0xF1, 0x21, 0x3E, 0x56, 0x37, 0x3E, +0xD2, 0xF9, 0xE0, 0xF6, 0x17, 0xDB, 0x61, 0xFB, 0xD9, 0x76, 0x11, 0x1F, 0x2F, 0x74, 0x5E, 0xDE, +0xF3, 0xE4, 0x2F, 0xFC, 0x7F, 0x3F, 0xE4, 0xCC, 0x09, 0x1C, 0xA7, 0xE6, 0x10, 0x00, 0x93, 0xF8, +0xF8, 0x7B, 0x36, 0x5F, 0xF9, 0x75, 0xAF, 0x6A, 0xBF, 0xFF, 0xC2, 0xC5, 0xF8, 0x8E, 0xFC, 0xE4, +0xB1, 0x16, 0xC2, 0x95, 0x7B, 0xD7, 0xC2, 0x95, 0x8D, 0x49, 0xFE, 0x3B, 0x86, 0xF8, 0x10, 0x1F, +0xE2, 0x63, 0x59, 0xE2, 0x23, 0x6B, 0x85, 0xB0, 0xF3, 0x95, 0x76, 0x68, 0xEE, 0xC4, 0xF2, 0x6D, +0xFF, 0x70, 0x6D, 0x3D, 0xFC, 0xD2, 0xD7, 0xFC, 0xC8, 0xA7, 0x5E, 0x70, 0xB6, 0x04, 0x04, 0x08, +0x30, 0x55, 0x9F, 0xFC, 0x6F, 0xAF, 0x5F, 0x7F, 0xF9, 0x9F, 0x6B, 0x7F, 0xB0, 0x33, 0xD8, 0xF8, +0xEE, 0xF4, 0xF7, 0xB5, 0xF5, 0x5A, 0xB8, 0xAB, 0x13, 0x21, 0x97, 0xAE, 0xD6, 0xC4, 0x87, 0xF8, +0x10, 0x1F, 0x4B, 0x1C, 0x1F, 0x69, 0xD6, 0x33, 0x3D, 0xD5, 0x6A, 0xEF, 0xA5, 0xC3, 0xE9, 0xCF, +0xAC, 0x1D, 0x7E, 0x77, 0xED, 0x42, 0xF8, 0x9B, 0x9D, 0xF0, 0xF8, 0x8C, 0xB3, 0x23, 0x20, 0x40, +0x80, 0x99, 0xFA, 0xDC, 0x07, 0xFF, 0xE2, 0x0F, 0xDE, 0x73, 0x7F, 0xF6, 0xAB, 0xB5, 0xB5, 0xF0, +0x8D, 0xE9, 0xEF, 0xEB, 0x57, 0xBA, 0x21, 0x92, 0xFE, 0x14, 0x1F, 0xE2, 0x43, 0x7C, 0x2C, 0x57, +0x7C, 0x74, 0x17, 0x98, 0x0F, 0x3C, 0xF5, 0xF2, 0x0F, 0x3B, 0x2F, 0x3F, 0xD3, 0x09, 0x8F, 0x8F, +0x39, 0x1B, 0x02, 0x02, 0x04, 0x98, 0x9B, 0xFF, 0xE3, 0xCD, 0xDF, 0xB5, 0xFE, 0x9D, 0x3F, 0xBA, +0xF7, 0x5F, 0x5C, 0xBE, 0x1A, 0xDF, 0xDB, 0x09, 0x91, 0xAB, 0xE9, 0xB6, 0x8B, 0x77, 0xD7, 0xC2, +0xDD, 0xF7, 0xAF, 0x9D, 0x61, 0xA1, 0xBA, 0xF8, 0x30, 0xF8, 0x15, 0x1F, 0x8B, 0xF2, 0xBD, 0x0C, +0x2F, 0x30, 0xEF, 0x04, 0xC8, 0x9F, 0x76, 0xFE, 0xAD, 0xFF, 0xED, 0x4E, 0x78, 0x7C, 0xD8, 0x19, +0x10, 0x10, 0x20, 0xC0, 0xB9, 0xF9, 0xDC, 0x07, 0xFF, 0xE2, 0x03, 0x97, 0xEE, 0x8A, 0x7F, 0xEB, +0xE2, 0x5D, 0xF1, 0xC7, 0xBB, 0x21, 0x12, 0xF3, 0xD9, 0x90, 0xCB, 0xF7, 0xAE, 0xE5, 0x6B, 0x45, +0xC4, 0x87, 0xC1, 0xAF, 0xF8, 0xA8, 0x56, 0x7C, 0xA4, 0x05, 0xE6, 0x29, 0x3C, 0x0E, 0xF6, 0xFA, +0xDF, 0x57, 0x5A, 0xDB, 0xF1, 0xF7, 0x3B, 0x2F, 0x1F, 0xB0, 0xCE, 0x03, 0x10, 0x20, 0xC0, 0x22, +0x85, 0xC8, 0x83, 0x57, 0x5E, 0x96, 0xFD, 0xF2, 0xFA, 0xE5, 0xF8, 0xF6, 0xFC, 0x04, 0xD3, 0x89, +0x8F, 0x22, 0x44, 0xC4, 0x87, 0xC1, 0xAF, 0xF8, 0x58, 0x7C, 0x69, 0xA6, 0x63, 0x78, 0x81, 0x79, +0xC7, 0x87, 0x5F, 0xFC, 0x42, 0xFB, 0xE7, 0xFF, 0xFC, 0xCF, 0x7E, 0xFA, 0x39, 0x67, 0x39, 0x40, +0x80, 0x00, 0x0B, 0xE9, 0x8B, 0xBF, 0xF5, 0xDA, 0xB7, 0xAE, 0x5F, 0x8C, 0xEF, 0xBD, 0x70, 0x29, +0x7C, 0x67, 0xFA, 0x7B, 0x5A, 0xA8, 0x9E, 0x9E, 0x96, 0x95, 0x9E, 0x9E, 0x25, 0x3E, 0x0C, 0x7E, +0xC5, 0xC7, 0x02, 0x7E, 0x17, 0xF9, 0x02, 0xF3, 0xF6, 0xC0, 0x02, 0xF3, 0x8E, 0x8F, 0xED, 0xBD, +0x98, 0xFD, 0xC4, 0x2B, 0xFF, 0xFA, 0x9F, 0x7C, 0xCA, 0x59, 0x0D, 0x10, 0x20, 0xC0, 0xC2, 0xFB, +0x17, 0x6F, 0xFC, 0x8E, 0xF5, 0xEF, 0xFA, 0xB1, 0xE6, 0x8F, 0x5C, 0xBE, 0x27, 0xBE, 0xEF, 0xF8, +0x85, 0xEA, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0xF3, 0x94, 0xA2, 0x23, 0xC5, 0x47, 0xB1, 0xC0, 0x3C, +0xB6, 0xC3, 0xBF, 0xAD, 0x75, 0x77, 0xB6, 0xFA, 0x98, 0x33, 0x19, 0x20, 0x40, 0x80, 0x2A, 0x86, +0xC8, 0x95, 0xEF, 0x7D, 0xD7, 0xFE, 0xE3, 0x6B, 0xEB, 0xE1, 0xA7, 0x8B, 0x85, 0xEA, 0x97, 0xAE, +0xAE, 0xE5, 0x21, 0x72, 0xC7, 0x85, 0xEA, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x99, 0x49, 0x4F, 0xB3, +0x4A, 0x4F, 0xB7, 0xB2, 0xC0, 0x1C, 0x10, 0x20, 0xC0, 0x52, 0xFA, 0xF7, 0x7F, 0xEF, 0xDB, 0xBF, +0xFE, 0xBE, 0xFF, 0x20, 0xFE, 0xF2, 0xC5, 0x2B, 0xF1, 0xC7, 0xF3, 0x93, 0x4F, 0xEF, 0x42, 0x86, +0x47, 0x2E, 0x54, 0x17, 0x1F, 0xE2, 0x43, 0x7C, 0xCC, 0xC4, 0xE0, 0x02, 0xF3, 0xFE, 0x15, 0xCC, +0x2D, 0x30, 0x07, 0x04, 0x08, 0xB0, 0x9C, 0xFE, 0xE4, 0x03, 0xDF, 0xFE, 0x9A, 0xAB, 0x5F, 0x97, +0x7D, 0x60, 0xFD, 0x52, 0x78, 0x53, 0x11, 0x22, 0x77, 0xDF, 0x7F, 0x61, 0xF0, 0x42, 0x86, 0xE2, +0x43, 0x7C, 0x88, 0x8F, 0xA9, 0x4B, 0x57, 0x30, 0x4F, 0xE1, 0xB1, 0xBF, 0x95, 0x95, 0xBF, 0x17, +0x0B, 0xCC, 0x01, 0x01, 0x02, 0xAC, 0x86, 0x2F, 0xFC, 0xC6, 0x6B, 0x7F, 0xE0, 0xF2, 0xD5, 0xF8, +0xFE, 0xB5, 0xF5, 0xF0, 0x60, 0xFA, 0x7B, 0x5A, 0xA8, 0x7E, 0xCF, 0x03, 0x6B, 0x61, 0xFD, 0x72, +0xD5, 0x06, 0x8E, 0xE2, 0x43, 0x7C, 0x2C, 0xF8, 0xA1, 0xCC, 0x8A, 0x75, 0x1E, 0xE5, 0x05, 0xE6, +0xD1, 0x02, 0x73, 0x40, 0x80, 0x00, 0xAB, 0x27, 0x2D, 0x54, 0xFF, 0xEE, 0x77, 0x36, 0xDF, 0x75, +0xE1, 0x62, 0x7C, 0xF7, 0xDA, 0x85, 0xF0, 0xF5, 0xB1, 0x33, 0xE8, 0xBC, 0x78, 0xA5, 0xBB, 0x63, +0xD6, 0x85, 0x4B, 0xD3, 0x3C, 0x3D, 0x89, 0x0F, 0xF1, 0xB1, 0x9A, 0xC7, 0xBF, 0x08, 0x8F, 0xD1, +0x05, 0xE6, 0x9F, 0xFC, 0x98, 0x33, 0x10, 0x20, 0x40, 0x80, 0x95, 0xF5, 0xB9, 0x7F, 0xF4, 0xDA, +0x07, 0x2E, 0x5D, 0x8D, 0xBF, 0xB0, 0x7E, 0x29, 0xFE, 0x64, 0xB1, 0x50, 0xFD, 0xF2, 0xD5, 0xB5, +0x3C, 0x44, 0xCE, 0x7E, 0x21, 0x43, 0xF1, 0x21, 0x3E, 0x56, 0xEF, 0xF8, 0x77, 0x17, 0x98, 0x8F, +0xBB, 0x82, 0xF9, 0x27, 0x3F, 0xEC, 0x8C, 0x03, 0x08, 0x10, 0x80, 0x9E, 0x7F, 0xF3, 0x5F, 0xBF, +0xF6, 0x95, 0xAF, 0x78, 0x30, 0x7B, 0xFF, 0x85, 0x8B, 0xE1, 0x1D, 0xF9, 0x09, 0xAA, 0xB7, 0x50, +0xFD, 0xCA, 0xA9, 0xAF, 0xA8, 0x2E, 0x3E, 0xC4, 0xC7, 0x6A, 0x1D, 0xFF, 0xE3, 0xAF, 0x60, 0xFE, +0x49, 0x0B, 0xCC, 0x01, 0x01, 0x02, 0x30, 0xCE, 0x67, 0x7F, 0xED, 0x35, 0xAF, 0xBB, 0xFB, 0xE5, +0xF9, 0xFA, 0x90, 0xEF, 0x4E, 0x7F, 0x4F, 0xEB, 0x43, 0xF2, 0x2B, 0xAA, 0x5F, 0x3D, 0xC9, 0x29, +0x4B, 0x7C, 0x88, 0x8F, 0xD5, 0x39, 0xFE, 0xA3, 0x0B, 0xCC, 0x73, 0xBD, 0x05, 0xE6, 0x7F, 0x62, +0x81, 0x39, 0x20, 0x40, 0x00, 0x26, 0x0A, 0x91, 0x7F, 0xF4, 0x9A, 0x1F, 0xBC, 0xE7, 0xFE, 0xF8, +0xAB, 0xC5, 0x85, 0x0C, 0xD3, 0xBA, 0x90, 0x7B, 0xEE, 0x9F, 0xE4, 0x42, 0x86, 0xE2, 0x43, 0x7C, +0xAC, 0xC6, 0xF1, 0x1F, 0xBF, 0xC0, 0xBC, 0xB8, 0x82, 0xF9, 0x1F, 0x5B, 0x60, 0x0E, 0x08, 0x10, +0x80, 0x93, 0xFA, 0x48, 0xE3, 0xFA, 0xFA, 0x77, 0xBF, 0xB3, 0xF5, 0x0B, 0x17, 0xAF, 0xC4, 0x5F, +0x2C, 0xD6, 0x87, 0xA4, 0x85, 0xEA, 0xF7, 0x3C, 0x70, 0xE1, 0x88, 0x0B, 0x19, 0x8A, 0x0F, 0xF1, +0xB1, 0x1A, 0xC7, 0xDF, 0x02, 0x73, 0x40, 0x80, 0x00, 0xCC, 0xD0, 0x67, 0x7F, 0xED, 0x35, 0x0F, +0x5C, 0xBC, 0x3B, 0xFC, 0xAD, 0xCB, 0xF7, 0xC4, 0xC7, 0x8A, 0xDB, 0xD2, 0xDA, 0x90, 0xF4, 0xD4, +0xAC, 0xC3, 0xF5, 0x21, 0xE2, 0x43, 0x7C, 0x2C, 0xFF, 0xF1, 0xB7, 0xC0, 0x1C, 0x10, 0x20, 0x00, +0xF3, 0x0D, 0x91, 0x07, 0xAF, 0xDC, 0x1B, 0x7F, 0x79, 0xFD, 0x72, 0x78, 0x7B, 0x7E, 0x12, 0x5B, +0x2B, 0x42, 0x64, 0x56, 0xA7, 0x33, 0xF1, 0x21, 0x3E, 0x16, 0x83, 0x05, 0xE6, 0x80, 0x00, 0x01, +0x38, 0x47, 0xCF, 0x7C, 0xF8, 0x35, 0x6F, 0x5D, 0xBF, 0x18, 0xDF, 0x7B, 0xE1, 0x52, 0xF8, 0xCE, +0x34, 0x70, 0x3C, 0xDD, 0x42, 0x75, 0xF1, 0x21, 0x3E, 0x16, 0xFF, 0xF8, 0x5B, 0x60, 0x0E, 0x08, +0x10, 0x80, 0x05, 0x91, 0xD6, 0x87, 0x7C, 0xCF, 0xBB, 0x0E, 0xDE, 0xB5, 0x7E, 0x29, 0xFE, 0x52, +0xB1, 0x50, 0x3D, 0xAD, 0x0F, 0x49, 0x21, 0x72, 0xE7, 0x85, 0xEA, 0xE2, 0x43, 0x7C, 0x2C, 0xF6, +0xF1, 0x3F, 0x6A, 0x81, 0x79, 0x27, 0x3C, 0x7E, 0xAA, 0x13, 0x1E, 0x7F, 0xE4, 0x0C, 0x00, 0x08, +0x10, 0x80, 0x73, 0xF2, 0xCF, 0x1F, 0x7A, 0xCD, 0x95, 0x87, 0x7E, 0x2A, 0x7B, 0x7C, 0x6D, 0x3D, +0xFC, 0x74, 0xF9, 0x42, 0x86, 0x29, 0x44, 0xC6, 0x2F, 0x54, 0x17, 0x1F, 0xE2, 0x63, 0xB1, 0x8F, +0xFF, 0xB8, 0x05, 0xE6, 0xED, 0x83, 0xF8, 0x33, 0xAF, 0xF8, 0x6B, 0x9F, 0xFA, 0x97, 0xFE, 0xC5, +0x03, 0x02, 0x04, 0x60, 0x41, 0xFC, 0xBB, 0xF7, 0x7D, 0xDB, 0x2B, 0xEE, 0x7B, 0x65, 0x7C, 0xEF, +0xC5, 0x2B, 0xE1, 0xC7, 0x8B, 0xDB, 0x52, 0x84, 0x9C, 0xEC, 0x42, 0x86, 0xE2, 0x43, 0x7C, 0x9C, +0x9F, 0xE1, 0x05, 0xE6, 0x1D, 0x2F, 0x74, 0xC2, 0xE3, 0xE7, 0xBF, 0xEE, 0xC7, 0x3E, 0xF5, 0x1B, +0xFE, 0x85, 0x03, 0x02, 0x04, 0x60, 0x41, 0xFD, 0xF1, 0x7F, 0xFF, 0x6D, 0xAF, 0xB9, 0xF7, 0x1B, +0xE2, 0x6F, 0x14, 0x17, 0x32, 0x4C, 0xF1, 0x51, 0x84, 0x88, 0xF8, 0x10, 0x1F, 0x8B, 0x78, 0xFC, +0x87, 0x17, 0x98, 0xC7, 0x2C, 0x6C, 0x75, 0x7E, 0x6F, 0xFF, 0xBB, 0x60, 0x81, 0x39, 0x20, 0x40, +0x00, 0xAA, 0xE3, 0xF3, 0xFF, 0xE3, 0xB7, 0xFE, 0xC0, 0x95, 0x97, 0x85, 0x74, 0x45, 0xF5, 0x07, +0xD3, 0xDF, 0xD3, 0x42, 0xF5, 0xBB, 0xEF, 0x5F, 0x0B, 0x97, 0xEE, 0xAE, 0x89, 0x0F, 0xF1, 0xB1, +0x10, 0x8E, 0x5A, 0x60, 0xDE, 0x79, 0x79, 0x4F, 0x27, 0x3C, 0x3E, 0xE3, 0x5F, 0x31, 0x20, 0x40, +0x00, 0x2A, 0xE6, 0x9F, 0x3F, 0xF4, 0x9A, 0xF5, 0xEF, 0xFD, 0x1B, 0xD9, 0xBB, 0xD6, 0x2F, 0x85, +0xF7, 0x97, 0x2F, 0x64, 0x98, 0x42, 0x24, 0x5D, 0x59, 0x5D, 0x7C, 0x88, 0x8F, 0x73, 0x79, 0x14, +0x47, 0x2C, 0x30, 0x6F, 0x6E, 0xC7, 0x9F, 0xFF, 0x86, 0xFF, 0xEC, 0x53, 0xFF, 0xAF, 0x7F, 0xB9, +0x80, 0x00, 0x01, 0xA8, 0xB8, 0xCF, 0xFE, 0xDA, 0xB7, 0x3E, 0x70, 0xF9, 0x6A, 0xF8, 0x85, 0xF5, +0xCB, 0xE1, 0x27, 0x0F, 0x17, 0xAA, 0xD7, 0xCE, 0xB0, 0x50, 0x5D, 0x7C, 0x88, 0x8F, 0xD3, 0x19, +0x5E, 0x60, 0xDE, 0xF1, 0x99, 0xD6, 0x5E, 0xFC, 0x09, 0x0B, 0xCC, 0x01, 0x01, 0x02, 0xB0, 0x9C, +0x21, 0xF2, 0xEA, 0xBB, 0x36, 0xC2, 0xBB, 0x2F, 0x5C, 0x0C, 0xEF, 0x48, 0x03, 0xD2, 0xE2, 0x42, +0x86, 0x27, 0x5B, 0xA8, 0x2E, 0x3E, 0xC4, 0xC7, 0xC9, 0x59, 0x60, 0x0E, 0x20, 0x40, 0x80, 0x95, +0x0E, 0x91, 0x07, 0x5F, 0x77, 0xF7, 0xCB, 0xF3, 0xF5, 0x21, 0xFD, 0x85, 0xEA, 0x77, 0xDF, 0x7F, +0x61, 0xCA, 0x17, 0x32, 0x14, 0x1F, 0xE2, 0xC3, 0x02, 0x73, 0x00, 0x01, 0x02, 0x50, 0xF2, 0x85, +0xFF, 0xE9, 0xC1, 0xBF, 0x7A, 0xF9, 0x6A, 0x78, 0x5F, 0x71, 0x21, 0xC3, 0xB4, 0x50, 0xFD, 0xEA, +0x03, 0xD3, 0xB8, 0x90, 0xA1, 0xF8, 0x58, 0xF5, 0xF8, 0xB0, 0xC0, 0x1C, 0x40, 0x80, 0x00, 0x8C, +0xF5, 0xCF, 0xBE, 0xE7, 0xD5, 0xEB, 0xAF, 0xFB, 0xC9, 0xDA, 0x2F, 0x5C, 0xBC, 0x12, 0x7E, 0xB1, +0xBC, 0x50, 0xFD, 0x9E, 0x07, 0x2E, 0x9C, 0x71, 0x7D, 0x88, 0xF8, 0x58, 0xC5, 0xF8, 0xB0, 0xC0, +0x1C, 0x40, 0x80, 0x00, 0x4C, 0xE4, 0xB3, 0xFF, 0xF0, 0xC1, 0x07, 0x2E, 0xDF, 0x1B, 0xFE, 0x6E, +0xF9, 0x42, 0x86, 0xE9, 0x8A, 0xEA, 0x69, 0xC7, 0xAC, 0x93, 0xAF, 0x0F, 0x11, 0x1F, 0xAB, 0x18, +0x1F, 0xFB, 0x5B, 0x69, 0x9D, 0x47, 0xDB, 0x02, 0x73, 0x00, 0x01, 0x02, 0x70, 0xA2, 0x10, 0x79, +0xF5, 0x5D, 0x2F, 0x0F, 0x1F, 0xB8, 0x70, 0x31, 0x7C, 0x5F, 0x7E, 0xA2, 0x3C, 0xF1, 0x42, 0x75, +0xF1, 0xB1, 0x6A, 0xF1, 0x91, 0xD6, 0x79, 0x6C, 0x3D, 0x6B, 0x81, 0x39, 0x80, 0x00, 0x01, 0x38, +0x83, 0x67, 0x3E, 0xF4, 0xE0, 0x5B, 0x2F, 0x5E, 0x09, 0xEF, 0x2E, 0x16, 0xAA, 0xA7, 0xF5, 0x21, +0x69, 0xDB, 0xDE, 0xE3, 0x17, 0xAA, 0x8B, 0x8F, 0x55, 0x8A, 0x8F, 0x76, 0xB3, 0xBB, 0xB3, 0x95, +0x05, 0xE6, 0x00, 0x02, 0x04, 0x60, 0x2A, 0xD2, 0xFA, 0x90, 0x87, 0xFE, 0xF3, 0x5A, 0xBA, 0x90, +0xE1, 0x2F, 0x15, 0x0B, 0xD5, 0xD3, 0xFA, 0x90, 0x14, 0x22, 0xA3, 0x0B, 0xD5, 0xC5, 0xC7, 0xAA, +0xC4, 0x87, 0x05, 0xE6, 0x00, 0x02, 0x04, 0x60, 0xA6, 0xD2, 0xFA, 0x90, 0xBB, 0x36, 0xC2, 0xCF, +0xAD, 0xAD, 0x87, 0x9F, 0x2E, 0x16, 0xAA, 0x5F, 0xBA, 0xBB, 0x96, 0x6F, 0xDD, 0xDB, 0x5D, 0xA8, +0x2E, 0x3E, 0x56, 0x21, 0x3E, 0x8A, 0x05, 0xE6, 0x7B, 0x2F, 0x0E, 0x5C, 0x48, 0xD0, 0x02, 0x73, +0x00, 0x01, 0x02, 0x30, 0x1B, 0x7F, 0xF4, 0x77, 0x1F, 0x7C, 0xC5, 0xFD, 0xD7, 0xF2, 0xF5, 0x21, +0xEF, 0x28, 0x6E, 0xBB, 0xF2, 0xB2, 0x5A, 0xB8, 0xEB, 0xE5, 0xB3, 0xBE, 0x90, 0xA1, 0xF8, 0x38, +0xEF, 0xF8, 0x18, 0xB7, 0xC0, 0xBC, 0x13, 0x22, 0x3F, 0xF5, 0xCA, 0xBF, 0xFE, 0xC7, 0x4F, 0xFA, +0x97, 0x01, 0x20, 0x40, 0x00, 0x66, 0x2A, 0x2D, 0x54, 0xBF, 0xFB, 0xE5, 0xE1, 0xC3, 0x6B, 0xEB, +0xB1, 0x7F, 0x21, 0xC3, 0x2B, 0xF7, 0xAD, 0xE5, 0x4F, 0xCD, 0x12, 0x1F, 0xCB, 0x15, 0x1F, 0xE3, +0x16, 0x98, 0x37, 0x77, 0xE2, 0x7F, 0xF5, 0x0D, 0xEF, 0xFC, 0xD4, 0x07, 0xFD, 0x4B, 0x00, 0x10, +0x20, 0x00, 0x73, 0xF5, 0xF9, 0x5F, 0x7F, 0xF5, 0x0F, 0xDC, 0x75, 0x5F, 0xF8, 0x60, 0xF9, 0x42, +0x86, 0x77, 0x7F, 0xCD, 0x5A, 0xFE, 0xF4, 0x2C, 0xF1, 0x51, 0xED, 0xF8, 0x18, 0xB7, 0xC0, 0xBC, +0xF3, 0xF2, 0xF7, 0xD7, 0xD6, 0xC3, 0xFB, 0x2D, 0x30, 0x07, 0x10, 0x20, 0x00, 0xE7, 0xE6, 0x7F, +0xFB, 0xAE, 0x6F, 0x59, 0x7F, 0xF8, 0x6F, 0x5E, 0x48, 0x0B, 0xD5, 0xDF, 0x5F, 0xBE, 0x90, 0x61, +0x7A, 0x5A, 0xD6, 0xF4, 0xAE, 0xA8, 0x2E, 0x3E, 0xE6, 0x15, 0x1F, 0xE3, 0x16, 0x98, 0x67, 0xED, +0xF0, 0x4F, 0xD6, 0x2E, 0x84, 0x5F, 0xB2, 0xC0, 0x1C, 0x40, 0x80, 0x00, 0x2C, 0x8C, 0xCF, 0xFE, +0xEA, 0xAB, 0x1E, 0xB8, 0xB2, 0x51, 0xFB, 0xB9, 0x4E, 0x88, 0xFC, 0x62, 0x71, 0x5B, 0xBA, 0x90, +0x61, 0x0A, 0x91, 0xB3, 0x5D, 0x51, 0x5D, 0x7C, 0xCC, 0x23, 0x3E, 0x2C, 0x30, 0x07, 0x10, 0x20, +0x00, 0x55, 0x0D, 0x91, 0x57, 0xDF, 0xF5, 0xF2, 0xDA, 0xBB, 0xCB, 0x0B, 0xD5, 0x53, 0x84, 0x4C, +0x7E, 0x21, 0x43, 0xF1, 0x31, 0xEF, 0xF8, 0xB0, 0xC0, 0x1C, 0x40, 0x80, 0x00, 0x54, 0xDE, 0x33, +0xBF, 0xF9, 0xEA, 0x81, 0x0B, 0x19, 0xA6, 0xF8, 0x28, 0x42, 0x44, 0x7C, 0x2C, 0x46, 0x7C, 0x58, +0x60, 0x0E, 0x20, 0x40, 0x00, 0x96, 0xCE, 0x17, 0x7E, 0xE3, 0xD5, 0x7F, 0xF5, 0xF2, 0xD5, 0xF0, +0xBE, 0xF2, 0x42, 0xF5, 0xAB, 0x5F, 0xBB, 0xC0, 0xEB, 0x43, 0x56, 0x20, 0x3E, 0x2C, 0x30, 0x07, +0x10, 0x20, 0x00, 0x4B, 0x2D, 0x2D, 0x54, 0x6F, 0xFC, 0xEC, 0x85, 0xC7, 0x6B, 0xB5, 0xF0, 0x58, +0x79, 0xA1, 0x7A, 0xDA, 0x31, 0xEB, 0xC2, 0xA5, 0x05, 0x3A, 0x15, 0x2F, 0x79, 0x7C, 0x58, 0x60, +0x0E, 0x20, 0x40, 0x00, 0x56, 0x4A, 0x5A, 0xA8, 0x7E, 0xF9, 0xDE, 0xDA, 0xAF, 0x5C, 0xBC, 0x12, +0x7E, 0xA2, 0xB8, 0x6D, 0x61, 0x16, 0xAA, 0x2F, 0x71, 0x7C, 0x1C, 0xB5, 0xC0, 0xBC, 0xF3, 0xF2, +0x9E, 0x4E, 0x78, 0x7C, 0xCC, 0x6F, 0x26, 0x80, 0x00, 0x01, 0x58, 0xF6, 0x10, 0x49, 0x0B, 0xD5, +0xD3, 0x15, 0xD5, 0xBF, 0x2F, 0x3F, 0x19, 0xF7, 0x2E, 0x64, 0x78, 0x6E, 0x0B, 0xD5, 0x97, 0x38, +0x3E, 0xC6, 0x2D, 0x30, 0x6F, 0xED, 0xC7, 0x9F, 0x7F, 0xC5, 0x7F, 0xFA, 0xA9, 0x7F, 0xE6, 0x37, +0x11, 0x40, 0x80, 0x00, 0xAC, 0x94, 0xB4, 0x50, 0xFD, 0xD2, 0xDD, 0xF9, 0xF5, 0x43, 0x5E, 0x9B, +0xFE, 0x9E, 0xD6, 0x87, 0xA4, 0xD9, 0x90, 0xCB, 0x57, 0xE7, 0x78, 0x7A, 0x5E, 0xD2, 0xF8, 0x18, +0xB7, 0xC0, 0xBC, 0x7D, 0x10, 0xFE, 0xCE, 0xD7, 0xFD, 0xD8, 0x27, 0xDF, 0xEF, 0x37, 0x0F, 0x40, +0x80, 0x00, 0xAC, 0xAC, 0x7F, 0xFA, 0x1D, 0xF5, 0xF5, 0xD7, 0x3F, 0x76, 0x39, 0x5D, 0xC8, 0xF0, +0x97, 0x8A, 0x85, 0xEA, 0x69, 0x5D, 0xC8, 0x3D, 0x5F, 0x33, 0x87, 0x85, 0xEA, 0x4B, 0x18, 0x1F, +0xC3, 0x0B, 0xCC, 0x93, 0xAC, 0x15, 0xFE, 0x8E, 0x05, 0xE6, 0x00, 0x02, 0x04, 0x80, 0x92, 0xE2, +0x42, 0x86, 0x17, 0xD6, 0xC3, 0x4F, 0x97, 0x17, 0xAA, 0xDF, 0xF3, 0xB5, 0x17, 0x66, 0xB3, 0x3E, +0x64, 0xC9, 0xE2, 0xC3, 0x02, 0x73, 0x00, 0x01, 0x02, 0xC0, 0x29, 0x3C, 0xF3, 0x9B, 0xAF, 0xFA, +0xE6, 0xF5, 0xCB, 0xB5, 0xF7, 0x96, 0x2F, 0x64, 0x98, 0x16, 0xAA, 0xA7, 0x1D, 0xB3, 0xA6, 0xB6, +0x3E, 0x64, 0x89, 0xE2, 0xC3, 0x02, 0x73, 0x00, 0x01, 0x02, 0xC0, 0x14, 0x7C, 0xE9, 0xB7, 0x5E, +0xF5, 0x9D, 0x17, 0x2E, 0xD6, 0xFE, 0x87, 0xB5, 0xF5, 0xF0, 0x3D, 0xF9, 0x09, 0xBB, 0xB7, 0x50, +0xFD, 0xAE, 0x8D, 0x33, 0x56, 0xC8, 0x12, 0xC5, 0x87, 0x05, 0xE6, 0x00, 0x02, 0x04, 0x80, 0x29, +0xFB, 0xFC, 0xAF, 0xBF, 0xEA, 0x07, 0xEE, 0xBA, 0xAF, 0xF6, 0xC1, 0xF2, 0x85, 0x0C, 0x4F, 0xBD, +0x50, 0x7D, 0x49, 0xE2, 0xC3, 0x02, 0x73, 0x00, 0x01, 0x02, 0xC0, 0x0C, 0xA5, 0x85, 0xEA, 0x8F, +0xFC, 0x97, 0x97, 0x1F, 0xED, 0x44, 0xC8, 0xBB, 0xCB, 0xEB, 0x43, 0x52, 0x88, 0x4C, 0xBC, 0x50, +0x7D, 0x09, 0xE2, 0x63, 0xDC, 0x02, 0xF3, 0x4E, 0x8C, 0xFC, 0x37, 0x9D, 0x63, 0xF0, 0x3E, 0x0B, +0xCC, 0x01, 0x04, 0x08, 0x00, 0x53, 0x56, 0x2C, 0x54, 0x5F, 0xBF, 0x14, 0x7E, 0xB1, 0xB8, 0xED, +0xD2, 0xDD, 0xE9, 0x8A, 0xEA, 0x77, 0x58, 0xA8, 0x5E, 0xF1, 0xF8, 0x48, 0x4F, 0xB1, 0x4A, 0xE1, +0x51, 0x5E, 0x60, 0xDE, 0x6A, 0xC6, 0xFF, 0x75, 0xFD, 0x52, 0xED, 0x17, 0x2D, 0x30, 0x07, 0x10, +0x20, 0x00, 0xCC, 0x3E, 0x44, 0x5E, 0x7D, 0xCF, 0x03, 0xB5, 0x74, 0xFD, 0x90, 0xBF, 0x5C, 0xDC, +0x96, 0x2E, 0x62, 0x98, 0x66, 0x44, 0x46, 0x16, 0xAA, 0x57, 0x38, 0x3E, 0xC6, 0x2D, 0x30, 0xCF, +0xDA, 0xE1, 0x5F, 0xAF, 0x5D, 0x08, 0x7F, 0xDB, 0x02, 0x73, 0x00, 0x01, 0x02, 0xC0, 0x9C, 0x3D, +0xF3, 0x9B, 0xAF, 0x7A, 0xEB, 0xC5, 0x2B, 0xB5, 0xC7, 0xCB, 0x0B, 0xD5, 0x53, 0x84, 0xA4, 0x18, +0xA9, 0x7A, 0x7C, 0xA4, 0x05, 0xE6, 0x69, 0x5B, 0xDD, 0xD2, 0x3A, 0x0F, 0x0B, 0xCC, 0x01, 0x04, +0x08, 0x00, 0x8B, 0xE0, 0x4B, 0xFF, 0xF8, 0xD5, 0x7F, 0xA3, 0x7C, 0x21, 0xC3, 0xF4, 0x74, 0xAC, +0xBB, 0xEF, 0x5F, 0xCB, 0x9F, 0x9E, 0x55, 0xB5, 0xF8, 0x48, 0x0B, 0xCC, 0xB7, 0xBF, 0x92, 0xE5, +0xEB, 0x3D, 0x7A, 0xF2, 0x05, 0xE6, 0xFF, 0xF2, 0x57, 0x6E, 0x7F, 0xE0, 0x3F, 0xF9, 0xC4, 0x97, +0x5A, 0x7E, 0xDA, 0x00, 0x02, 0x04, 0x80, 0x05, 0x90, 0x2F, 0x54, 0xFF, 0xF9, 0xCB, 0x8F, 0xD7, +0x6A, 0xE1, 0xB1, 0xDA, 0x5A, 0x3C, 0x5C, 0xA8, 0xBE, 0x31, 0x87, 0x2B, 0xAA, 0x4F, 0x21, 0x3E, +0xD2, 0x85, 0x04, 0xB7, 0x9F, 0x6D, 0x5B, 0x60, 0x0E, 0x20, 0x40, 0x00, 0xA8, 0x92, 0xCF, 0xFC, +0x83, 0x6F, 0x79, 0xE0, 0xEE, 0xFB, 0xD7, 0xFE, 0xC1, 0xF0, 0x85, 0x0C, 0x53, 0x88, 0xCC, 0xE4, +0x8A, 0xEA, 0x67, 0x8C, 0x0F, 0x0B, 0xCC, 0x01, 0x04, 0x08, 0x00, 0xCB, 0x11, 0x22, 0xAF, 0xBE, +0xFA, 0xB5, 0x6B, 0xBF, 0x51, 0x5B, 0x0B, 0x0F, 0xE5, 0x27, 0xFC, 0xB5, 0xEE, 0x42, 0xF5, 0xF4, +0x32, 0xB5, 0x2B, 0xAA, 0x9F, 0x21, 0x3E, 0x2C, 0x30, 0x07, 0x10, 0x20, 0x00, 0x2C, 0xA1, 0x67, +0x7E, 0xF3, 0x2F, 0xBC, 0xF5, 0xD2, 0xDD, 0xF9, 0x8E, 0x59, 0xAF, 0x2D, 0x42, 0xE4, 0xEE, 0xFB, +0x2F, 0x9C, 0xEE, 0x42, 0x86, 0x53, 0x8A, 0x0F, 0x0B, 0xCC, 0x01, 0x04, 0x08, 0x00, 0x4B, 0xEC, +0x7F, 0xB9, 0xFE, 0x8D, 0xEB, 0x8D, 0x9F, 0xBD, 0xEB, 0x5D, 0x17, 0xEF, 0xAA, 0xBD, 0xAF, 0xF3, +0xD7, 0x8D, 0x74, 0x5B, 0xBA, 0xA2, 0xFA, 0xD5, 0x07, 0x66, 0xB9, 0x3E, 0x64, 0x34, 0x3E, 0x2C, +0x30, 0x07, 0x40, 0x80, 0x00, 0xAC, 0x90, 0xB4, 0x3E, 0xE4, 0xAE, 0x97, 0xD7, 0x7E, 0xEE, 0xC2, +0x7A, 0xED, 0xA7, 0xCB, 0x57, 0x54, 0x4F, 0x3B, 0x66, 0x5D, 0xB8, 0x34, 0xCD, 0xFF, 0x4B, 0x18, +0x8C, 0x0F, 0x0B, 0xCC, 0x01, 0x10, 0x20, 0x00, 0x2B, 0xEC, 0x99, 0xDF, 0xFC, 0x0B, 0xDF, 0xBC, +0x7E, 0xB9, 0xF6, 0xDE, 0xE1, 0x85, 0xEA, 0x57, 0xEE, 0xAD, 0x4D, 0x21, 0x44, 0xE2, 0x40, 0x78, +0xA4, 0xA7, 0x5A, 0x59, 0x60, 0x0E, 0x80, 0x00, 0x01, 0x20, 0x7C, 0xE5, 0x9F, 0xBE, 0xAA, 0xD1, +0x89, 0x84, 0x5F, 0x29, 0x2E, 0x64, 0x98, 0xA4, 0x00, 0x49, 0x0B, 0xD5, 0xD3, 0xCC, 0xC8, 0xC9, +0x77, 0xCD, 0x8A, 0xF9, 0x82, 0xF2, 0xE6, 0x4E, 0x0C, 0xCD, 0xDB, 0xD9, 0xC0, 0x8C, 0x47, 0x6C, +0x87, 0x7F, 0x5D, 0xB3, 0xC0, 0x1C, 0x40, 0x80, 0x38, 0x04, 0x00, 0xFC, 0xD9, 0x3F, 0x79, 0xD5, +0x5F, 0xB9, 0x70, 0x31, 0xBC, 0xBF, 0xF3, 0x66, 0xBD, 0x7C, 0x7B, 0x8A, 0x91, 0x22, 0x44, 0xD6, +0x3B, 0x6F, 0xA7, 0x05, 0xEC, 0xE5, 0x19, 0x92, 0xB4, 0x96, 0x23, 0x05, 0x47, 0xAB, 0xF3, 0x67, +0x5A, 0x50, 0x7E, 0xB0, 0x1B, 0xCB, 0xEB, 0x3B, 0x0A, 0x9F, 0xE9, 0xBC, 0xBC, 0xA7, 0x13, 0x1E, +0x1F, 0x76, 0xA4, 0x01, 0x10, 0x20, 0x00, 0xF4, 0xA5, 0x1D, 0xB3, 0xD6, 0x2F, 0xD7, 0x1E, 0xED, +0xC4, 0xC8, 0xF7, 0x9F, 0xE8, 0x13, 0xE3, 0xD8, 0xDD, 0xAE, 0x7E, 0x7B, 0xEF, 0xA5, 0xF8, 0xEB, +0xAF, 0xFC, 0x89, 0x4F, 0x3D, 0xE9, 0xC8, 0x02, 0x20, 0x40, 0x00, 0x38, 0x52, 0x5A, 0xAC, 0x7E, +0xF9, 0x65, 0xB5, 0x37, 0x5E, 0x58, 0xAF, 0x35, 0x6A, 0x6B, 0xA1, 0xB1, 0xB6, 0x1E, 0x1E, 0xBC, +0x53, 0x7C, 0xC4, 0x2C, 0xFC, 0xDB, 0xCE, 0x9B, 0x7F, 0xB8, 0x76, 0x21, 0xFC, 0x5F, 0x2F, 0x7D, +0xB1, 0xFD, 0xBB, 0xDF, 0xF4, 0xD8, 0x9F, 0x3C, 0xE7, 0x48, 0x02, 0x20, 0x40, 0x00, 0x38, 0xB1, +0xB4, 0x8D, 0xEF, 0x77, 0xFF, 0xF8, 0x95, 0x6F, 0x7E, 0xD9, 0x2B, 0xD6, 0xBE, 0x61, 0xF8, 0x7D, +0x9D, 0xD8, 0xF8, 0xE2, 0x37, 0x3D, 0xFA, 0xC7, 0xFF, 0xCE, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xC7, +0xFF, 0x2F, 0xC0, 0x00, 0x23, 0xF2, 0x4C, 0x01, 0x21, 0x26, 0xDF, 0x39, 0x00, 0x00, 0x00, 0x00, +0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82}; diff --git a/libesp32/ESP32-Mail-Client/examples/Set_flag/Set_flag.ino b/libesp32/ESP32-Mail-Client/examples/Set_flag/Set_flag.ino new file mode 100755 index 000000000..e9ff771e4 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/examples/Set_flag/Set_flag.ino @@ -0,0 +1,70 @@ +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +#include +#include "ESP32_MailClient.h" + +#define WIFI_SSID "YOUR_WIFI_SSID" +#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" + +//The Email Reading data object contains config and data that received +IMAPData imapData; + +void readEmail(); + +unsigned long lastTime = 0; + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + Serial.println(); + + Serial.println(); + + imapData.setLogin("imap.gmail.com", 993, "YOUR_EMAIL_ACCOUNT@gmail.com", "YOUR_EMAIL_PASSWORD"); + imapData.setFolder("INBOX"); + imapData.setDebug(true); + + //Set \Seen and \Answered to flags for message with UID 100 + MailClient.setFlag(imapData, 100, "\\Seen \\Answered"); + + //Add \Seen and \Answered to flags for message with UID 100 + //MailClient.addFlag(imapData, 100, "\\Seen \\Answered"); + + //Remove \Seen and \Answered from flags for message with UID 100 + //MailClient.removeFlag(imapData, 100, "\\Seen \\Answered"); +} + + + +void loop() +{ + +} + diff --git a/libesp32/ESP32-Mail-Client/examples/Time/Time.ino b/libesp32/ESP32-Mail-Client/examples/Time/Time.ino new file mode 100755 index 000000000..4726064d3 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/examples/Time/Time.ino @@ -0,0 +1,136 @@ +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +#include +#include "ESP32_MailClient.h" + +#define WIFI_SSID "YOUR_WIFI_SSID" +#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" + + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + Serial.println(); + + //Set Clock + //GMT offset (3 Hrs), Daylight offset (0 Hrs) + MailClient.Time.setClock(3, 0); + + Serial.println("Number of Days This Year (since January): " + String(MailClient.Time.getNumberOfDayThisYear())); + Serial.println("Day of Week Number: " + String(MailClient.Time.getDayOfWeek())); + Serial.println("Day of Week String: : " + String(MailClient.Time.getDayOfWeekString())); + Serial.println("Total seconds today: : " + String(MailClient.Time.getCurrentSecond())); + Serial.println(); +} + +void loop() +{ + + if (!MailClient.Time.clockReady) + return; + + //Print out current date and time + int d = MailClient.Time.getDay(); + int m = MailClient.Time.getMonth(); + int y = MailClient.Time.getYear(); + int hr = MailClient.Time.getHour(); + int min = MailClient.Time.getMin(); + int sec = MailClient.Time.getSec(); + Serial.print("Current Time (GMT+3): "); + Serial.print(d); + Serial.print("/"); + Serial.print(m); + Serial.print("/"); + Serial.print(y); + Serial.print(" "); + Serial.print(hr); + Serial.print(":"); + Serial.print(min); + Serial.print(":"); + Serial.println(sec); + + uint32_t todayFromMidnightTimestamp = MailClient.Time.getTimestamp(y, m, d, 0, 0, 0); + uint32_t currentTimestamp = MailClient.Time.getUnixTime(); + uint32_t totalSecondsFromMidnight = currentTimestamp - todayFromMidnightTimestamp; + + //Assumed we countdown until 15:00:00 everyday + uint8_t targetSec = 0; + uint8_t targetMin = 0; + uint8_t targetHr = 15; + uint32_t targetSecondsFromMidnight = targetHr * 60 * 60 + targetMin * 60 + targetSec; + + if (targetSecondsFromMidnight >= totalSecondsFromMidnight) + { + uint32_t diffSeconds = targetSecondsFromMidnight - totalSecondsFromMidnight; + int remainYrs, remainMonths, remainDays, remainHr, remainMin, remainSec; + MailClient.Time.getTimeFromSec(diffSeconds, remainYrs, remainMonths, remainDays, remainHr, remainMin, remainSec); + Serial.print("Everyday countdown until 15:00:00 is "); + Serial.print(remainHr); + Serial.print(" Hr, "); + Serial.print(remainMin); + Serial.print(" Min and "); + Serial.print(remainSec); + Serial.println(" Sec to go."); + } + else + { + Serial.println("Everyday countdown until 15:00:00 was passed."); + } + + //Assumed we countdown until 18/12/2019 8:30:45 + uint32_t targetTimestamp = MailClient.Time.getTimestamp(2019, 12, 18, 8, 30, 45); + if (targetTimestamp >= currentTimestamp) + { + uint32_t diffSeconds = targetTimestamp - currentTimestamp; + int remainYrs, remainMonths, remainDays, remainHr, remainMin, remainSec; + MailClient.Time.getTimeFromSec(diffSeconds, remainYrs, remainMonths, remainDays, remainHr, remainMin, remainSec); + Serial.print("One time countdown until 18/12/2019 8:30:45 is "); + Serial.print(remainYrs); + Serial.print(" Years, "); + Serial.print(remainMonths); + Serial.print(" Months, "); + Serial.print(remainDays); + Serial.print(" Days, "); + Serial.print(remainHr); + Serial.print(" Hr, "); + Serial.print(remainMin); + Serial.print(" Min and "); + Serial.print(remainSec); + Serial.println(" Sec to go."); + } + else + { + Serial.println("One time countdown until 18/12/2019 8:30:45 was finished."); + } + Serial.println(); + + delay(1000); +} + diff --git a/libesp32/ESP32-Mail-Client/keywords.txt b/libesp32/ESP32-Mail-Client/keywords.txt new file mode 100755 index 000000000..81b7ee9b2 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/keywords.txt @@ -0,0 +1,166 @@ +####################################### +# Syntax Coloring Map ESP32-Mail-Client +####################################### + +####################################### +# Classes (KEYWORD1) +####################################### + +IMAPData KEYWORD1 +SMTPData KEYWORD1 +attachmentData KEYWORD1 +SendStatus KEYWORD1 +messageBodyData KEYWORD1 +DownloadProgress KEYWORD1 +MessageData KEYWORD1 + +TIME KEYWORD1 + + +################################## +# Methods and Functions (KEYWORD2) +################################## + +sendMail KEYWORD2 +readMail KEYWORD2 +smtpErrorReason KEYWORD2 +imapErrorReason KEYWORD2 +sdBegin KEYWORD2 +setFlag KEYWORD2 +addFlag KEYWORD2 +removeFlag KEYWORD2 + + +setClock KEYWORD2 +getUnixTime KEYWORD2 +getTimestamp KEYWORD2 +getYear KEYWORD2 +getMonth KEYWORD2 +getDay KEYWORD2 +getDayOfWeek KEYWORD2 +getDayOfWeekString KEYWORD2 +getHour KEYWORD2 +getMin KEYWORD2 +getSec KEYWORD2 +getNumberOfDayThisYear KEYWORD2 +getTotalDays KEYWORD2 +dayofweek KEYWORD2 +getCurrentSecond KEYWORD2 +getCurrentTimestamp KEYWORD2 +getTimeFromSec KEYWORD2 + +######################################### +# Methods for IMAP Data object (KEYWORD2) +######################################### + +setLogin KEYWORD2 +setSTARTTLS KEYWORD2 +setDebug KEYWORD2 +setFolder KEYWORD2 +setMessageBufferSize KEYWORD2 +setAttachmentSizeLimit KEYWORD2 +setSearchCriteria KEYWORD2 +setSaveFilePath KEYWORD2 +setFechUID KEYWORD2 +setDownloadAttachment KEYWORD2 +setHTMLMessage KEYWORD2 +setTextMessage KEYWORD2 +setSearchLimit KEYWORD2 +setRecentSort KEYWORD2 +setReadCallback KEYWORD2 +setDownloadReport KEYWORD2 +isHeaderOnly KEYWORD2 +getFrom KEYWORD2 +getFromCharset KEYWORD2 +getTo KEYWORD2 +getToCharset KEYWORD2 +getCC KEYWORD2 +getCCCharset KEYWORD2 +getSubject KEYWORD2 +getSubjectCharset KEYWORD2 +getHTMLMessage KEYWORD2 +getTextMessage KEYWORD2 +getHTMLMessgaeCharset KEYWORD2 +getTextMessgaeCharset KEYWORD2 +getDate KEYWORD2 +getUID KEYWORD2 +getNumber KEYWORD2 +getMessageID KEYWORD2 +getAcceptLanguage KEYWORD2 +getContentLanguage KEYWORD2 +isFetchMessageFailed KEYWORD2 +getFetchMessageFailedReason KEYWORD2 +isDownloadAttachmentFailed KEYWORD2 +getDownloadAttachmentFailedReason KEYWORD2 +isDownloadMessageFailed KEYWORD2 +getDownloadMessageFailedReason KEYWORD2 +saveHTMLMessage KEYWORD2 +saveTextMessage KEYWORD2 +getFolderCount KEYWORD2 +getFolder KEYWORD2 +getFlagCount KEYWORD2 +getFlag KEYWORD2 +totalMessages KEYWORD2 +searchCount KEYWORD2 +availableMessages KEYWORD2 +getAttachmentCount KEYWORD2 +getAttachmentFileName KEYWORD2 +getAttachmentName KEYWORD2 +getAttachmentFileSize KEYWORD2 +getAttachmentCreationDate KEYWORD2 +getAttachmentType KEYWORD2 +empty KEYWORD2 +clearMessageData KEYWORD2 + +######################################### +# Methods for SMTP Data object (KEYWORD2) +######################################### + +setSender KEYWORD2 +getFromName KEYWORD2 +getSenderEmail KEYWORD2 +setPriority KEYWORD2 +getPriority KEYWORD2 +addRecipient KEYWORD2 +removeRecipient KEYWORD2 +clearRecipient KEYWORD2 +getRecipient KEYWORD2 +recipientCount KEYWORD2 +setSubject KEYWORD2 +getSubject KEYWORD2 +setMessage KEYWORD2 +getMessage KEYWORD2 +htmlFormat KEYWORD2 +addCC KEYWORD2 +removeCC KEYWORD2 +clearCC KEYWORD2 +getCC KEYWORD2 +ccCount KEYWORD2 +addBCC KEYWORD2 +removeBCC KEYWORD2 +clearBCC KEYWORD2 +getBCC KEYWORD2 +bccCount KEYWORD2 +addAttachData KEYWORD2 +removeAttachData KEYWORD2 +attachDataCount KEYWORD2 +addAttachFile KEYWORD2 +removeAttachFile KEYWORD2 +clearAttachData KEYWORD2 +clearAttachFile KEYWORD2 +clearAttachment KEYWORD2 +attachFileCount KEYWORD2 +setSendCallback KEYWORD2 + + +############################################################ +# Functions for ReadStatus and SendStatus classes (KEYWORD2) +############################################################ + +SendStatus KEYWORD2 +info KEYWORD2 +success KEYWORD2 +ReadStatus KEYWORD2 +status KEYWORD2 + +clockReady KEYWORD3 \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/library.properties b/libesp32/ESP32-Mail-Client/library.properties new file mode 100755 index 000000000..6ff993e8b --- /dev/null +++ b/libesp32/ESP32-Mail-Client/library.properties @@ -0,0 +1,17 @@ +name=ESP32 Mail Client + +version=2.1.4 + +author=Mobizt + +maintainer=Mobizt + +sentence=Mail Client Arduino Library for ESP32 + +paragraph=This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download through SMTP and IMAP servers. + +category=Communication + +url=https://github.com/mobizt/ESP32-Mail-Client + +architectures=esp32 diff --git a/libesp32/ESP32-Mail-Client/media/images/esp32-mail.jpg b/libesp32/ESP32-Mail-Client/media/images/esp32-mail.jpg new file mode 100755 index 000000000..d4c62c865 Binary files /dev/null and b/libesp32/ESP32-Mail-Client/media/images/esp32-mail.jpg differ diff --git a/libesp32/ESP32-Mail-Client/media/images/esp32-mail.png b/libesp32/ESP32-Mail-Client/media/images/esp32-mail.png new file mode 100755 index 000000000..459238782 Binary files /dev/null and b/libesp32/ESP32-Mail-Client/media/images/esp32-mail.png differ diff --git a/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.cpp b/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.cpp new file mode 100755 index 000000000..39df83f7e --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.cpp @@ -0,0 +1,200 @@ +/* + * Customized version of ESP32 HTTPClient Library. + * Allow custom header and payload with STARTTLS support + * + * v 1.0.0 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * HTTPClient Arduino library for ESP32 + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the HTTPClient for Arduino. + * Port to ESP32 by Evandro Luis Copercini (2017), + * changed fingerprints to CA verification. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * +*/ + +#ifndef ESP32MailHTTPClient_CPP +#define ESP32MailHTTPClient_CPP + +#ifdef ESP32 + +#include "ESP32MailHTTPClient.h" + +class TransportTraits +{ +public: + virtual ~TransportTraits() {} + + virtual std::unique_ptr create() + { + return std::unique_ptr(new WiFiClient()); + } + + virtual bool + verify(WiFiClient &client, const char *host, bool starttls, DebugMsgCallback cb) + { + return true; + } +}; + +class TLSTraits : public TransportTraits +{ +public: + TLSTraits(const char *CAcert, const char *clicert = nullptr, const char *clikey = nullptr) : _cacert(CAcert), _clicert(clicert), _clikey(clikey) {} + + std::unique_ptr create() override + { + return std::unique_ptr(new WiFiClientSecureESP32()); + } + + bool verify(WiFiClient &client, const char *host, bool starttls, DebugMsgCallback cb) override + { + WiFiClientSecureESP32 &wcs = static_cast(client); + wcs.setCACert(_cacert); + wcs.setCertificate(_clicert); + wcs.setPrivateKey(_clikey); + wcs.setSTARTTLS(starttls); + wcs.setDebugCB(cb); + return true; + } + +protected: + const char *_cacert; + const char *_clicert; + const char *_clikey; +}; + +ESP32MailHTTPClient::ESP32MailHTTPClient() {} + +ESP32MailHTTPClient::~ESP32MailHTTPClient() +{ + if (_client) + _client->stop(); +} + +bool ESP32MailHTTPClient::begin(const char *host, uint16_t port, const char *uri, const char *CAcert) +{ + transportTraits.reset(nullptr); + + _host = host; + _port = port; + _uri = uri; + transportTraits = TransportTraitsPtr(new TLSTraits(CAcert)); + return true; +} + +bool ESP32MailHTTPClient::connected() +{ + if (_client) + return ((_client->available() > 0) || _client->connected()); + return false; +} + +bool ESP32MailHTTPClient::sendHeader(const char *header) +{ + if (!connected()) + return false; + return (_client->write(header, strlen(header)) == strlen(header)); +} + +int ESP32MailHTTPClient::sendRequest(const char *header, const char *payload) +{ + size_t size = strlen(payload); + if (strlen(header) > 0) + { + if (!connect()) + return HTTPC_ERROR_CONNECTION_REFUSED; + if (!sendHeader(header)) + return HTTPC_ERROR_SEND_HEADER_FAILED; + } + if (size > 0) + if (_client->write(&payload[0], size) != size) + return HTTPC_ERROR_SEND_PAYLOAD_FAILED; + + return 0; +} + +WiFiClient *ESP32MailHTTPClient::getStreamPtr(void) +{ + if (connected()) + return _client.get(); + return nullptr; +} + +bool ESP32MailHTTPClient::connect(void) +{ + if (connected()) + { + while (_client->available() > 0) + _client->read(); + return true; + } + + if (!transportTraits) + return false; + + _client = transportTraits->create(); + + if (!transportTraits->verify(*_client, _host.c_str(), false, _debugCallback)) + { + _client->stop(); + return false; + } + + if (!_client->connect(_host.c_str(), _port)) + return false; + + return connected(); +} + +bool ESP32MailHTTPClient::connect(bool starttls) +{ + if (connected()) + { + while (_client->available() > 0) + _client->read(); + return true; + } + + if (!transportTraits) + return false; + + _client = transportTraits->create(); + + if (!transportTraits->verify(*_client, _host.c_str(), starttls, _debugCallback)) + { + _client->stop(); + return false; + } + + if (!_client->connect(_host.c_str(), _port)) + return false; + + return connected(); +} + +void ESP32MailHTTPClient::setDebugCallback(DebugMsgCallback cb) +{ + _debugCallback = std::move(cb); +} + +#endif //ESP32 + +#endif //ESP32MailHTTPClient_CPP diff --git a/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.h b/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.h new file mode 100755 index 000000000..5df2c8d2d --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.h @@ -0,0 +1,107 @@ +/* + * Customized version of ESP32 HTTPClient Library. + * Allow custom header and payload with STARTTLS support + * + * v 1.0.0 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * HTTPClient Arduino library for ESP32 + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the HTTPClient for Arduino. + * Port to ESP32 by Evandro Luis Copercini (2017), + * changed fingerprints to CA verification. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * +*/ + +#ifndef ESP32MailHTTPClient_H +#define ESP32MailHTTPClient_H + +#ifdef ESP32 + +#include +#include + +#include +#include "WiFiClientSecureESP32.h" + +class ESP32MailHTTPClient : public HTTPClient +{ +public: + ESP32MailHTTPClient(); + ~ESP32MailHTTPClient(); + + /** + * Initialization of new http connection. + * \param host - Host name without protocols. + * \param port - Server's port. + * \param uri - The URI of resource. + * \param CAcert - The Base64 encode root certificate string + * \return True as default. + * If no certificate string provided, use (const char*)NULL to CAcert param + */ + bool begin(const char *host, uint16_t port, const char *uri, const char *CAcert); + + /** + * Check the http connection status. + * \return True if connected. + */ + bool connected(); + + /** + * Establish http connection if header provided and send it, send payload if provided. + * \param header - The header string (constant chars array). + * \param payload - The payload string (constant chars array), optional. + * \return http status code, Return zero if new http connection and header and/or payload sent + * with no error or no header and payload provided. If obly payload provided, no new http connection was established. + */ + int sendRequest(const char *header, const char *payload); + + /** + * Send extra header without making new http connection (if sendRequest has been called) + * \param header - The header string (constant chars array). + * \return True if header sending success. + * Need to call sendRequest with header first. + */ + bool sendHeader(const char *header); + + /** + * Get the WiFi client pointer. + * \return WiFi client pointer. + */ + WiFiClient *getStreamPtr(void); + + uint16_t tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; + bool connect(void); + bool connect(bool starttls); + void setDebugCallback(DebugMsgCallback cb); + +protected: + TransportTraitsPtr transportTraits; + std::unique_ptr _client; + DebugMsgCallback _debugCallback = NULL; + + std::string _host = ""; + std::string _uri = ""; + uint16_t _port = 0; +}; + +#endif //ESP32 + +#endif //ESP32MailHTTPClient_H diff --git a/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.cpp b/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.cpp new file mode 100755 index 000000000..8e37b566b --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.cpp @@ -0,0 +1,191 @@ +/* + * ESP32 Internet Time Helper Arduino Library v 1.0.1 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person returning a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef ESP32TimeHelper_CPP +#define ESP32TimeHelper_CPP + +#ifdef ESP32 + +#include "ESP32TimeHelper.h" + +ESP32TimeHelper::ESP32TimeHelper() +{ +} +uint32_t ESP32TimeHelper::getUnixTime() +{ + uint32_t utime = (msec_time_diff + millis()) / 1000; + return utime; +} + +time_t ESP32TimeHelper::getTimestamp(int year, int mon, int date, int hour, int mins, int sec) +{ + struct tm timeinfo; + timeinfo.tm_year = year - 1900; + timeinfo.tm_mon = mon - 1; + timeinfo.tm_mday = date; + timeinfo.tm_hour = hour; + timeinfo.tm_min = mins; + timeinfo.tm_sec = sec; + time_t ts = mktime(&timeinfo); + return ts; +} + +bool ESP32TimeHelper::setClock(float gmtOffset, float daylightOffset) +{ + TZ = gmtOffset; + DST_MN = daylightOffset; + configTime((TZ)*3600, (DST_MN)*60, "pool.ntp.org", "time.nist.gov", NULL); + + now = time(nullptr); + int cnt = 0; + while (now < 8 * 3600 * 2 && cnt < 20) + { + delay(50); + now = time(nullptr); + cnt++; + } + + uint64_t tmp = now; + tmp = tmp * 1000; + msec_time_diff = tmp - millis(); + + getLocalTime(&timeinfo); + + clockReady = now > 8 * 3600 * 2; + return clockReady; +} + +int ESP32TimeHelper::getYear() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_year + 1900; +} +int ESP32TimeHelper::getMonth() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_mon + 1; +} +int ESP32TimeHelper::getDay() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_mday; +} + +int ESP32TimeHelper::getDayOfWeek() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_wday; +} +String ESP32TimeHelper::getDayOfWeekString() +{ + getLocalTime(&timeinfo); + return dow[timeinfo.tm_wday]; +} + +int ESP32TimeHelper::getHour() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_hour; +} + +int ESP32TimeHelper::getMin() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_min; +} +int ESP32TimeHelper::getSec() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_sec; +} +int ESP32TimeHelper::getNumberOfDayThisYear() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_yday + 1; +} + +int ESP32TimeHelper::totalDays(int y, int m, int d) +{ + static char daytab[2][13] = + { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; + int daystotal = d; + for (int year = 1; year <= y; year++) + { + int max_month = (year < y ? 12 : m - 1); + int leap = (year % 4 == 0); + if (year % 100 == 0 && year % 400 != 0) + leap = 0; + for (int month = 1; month <= max_month; month++) + { + daystotal += daytab[leap][month]; + } + } + return daystotal; +} +int ESP32TimeHelper::getTotalDays(int year, int month, int day) +{ + return totalDays(year, month, day) - totalDays(1970, 1, 1); +} + +int ESP32TimeHelper::dayofWeek(int year, int month, int day) /* 1 <= m <= 12, y > 1752 (in the U.K.) */ +{ + static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; + year -= month < 3; + return (year + year / 4 - year / 100 + year / 400 + t[month - 1] + day) % 7; +} + +int ESP32TimeHelper::getCurrentSecond() +{ + return (timeinfo.tm_hour * 3600) + (timeinfo.tm_min * 60) + timeinfo.tm_sec; +} +uint64_t ESP32TimeHelper::getCurrentTimestamp() +{ + return now; +} +void ESP32TimeHelper::getTimeFromSec(int secCount, int &yrs, int &months, int &days, int &hr, int &min, int &sec) +{ + int _yrs = secCount / (365 * 24 * 3600); + secCount = secCount - _yrs * (365 * 24 * 3600); + yrs = _yrs; + int _months = secCount / (30* 24 * 3600); + secCount = secCount - _months * (30 * 24 * 3600); + months = _months; + int _days = secCount / (24 * 3600); + secCount = secCount - _days * (24 * 3600); + days = _days; + int _hr = secCount / 3600; + secCount = secCount - _hr * 3600; + hr = _hr; + int _min = secCount / 60; + secCount = secCount - _min * 60; + min = _min; + sec = secCount; +} + +#endif //ESP32 + +#endif //ESP32TimeHelper_CPP \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.h b/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.h new file mode 100755 index 000000000..e4feff648 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.h @@ -0,0 +1,73 @@ +/* + * ESP32 Internet Time Helper Arduino Library v 1.0.1 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person returning a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef ESP32TimeHelper_H +#define ESP32TimeHelper_H + +#ifdef ESP32 + + +#include +#include +#include + +class ESP32TimeHelper +{ +public: + ESP32TimeHelper(); + bool clockReady = false; + bool setClock(float gmtOffset, float daylightOffset); + uint32_t getUnixTime(); + time_t getTimestamp(int year, int mon, int date, int hour, int mins, int sec); + int getYear(); + int getMonth(); + int getDay(); + int getDayOfWeek(); + String getDayOfWeekString(); + int getHour(); + int getMin(); + int getSec(); + int getNumberOfDayThisYear(); + int getTotalDays(int year, int month, int day); + int dayofWeek(int year, int month, int day); + int getCurrentSecond(); + uint64_t getCurrentTimestamp(); + void getTimeFromSec(int secCount, int &yrs, int &months, int &days, int &hr, int &min, int &sec); + +private: + time_t now; + uint64_t msec_time_diff = 0; + struct tm timeinfo; + float TZ = 0.0; + float DST_MN = 0.0; + + bool setClock(); + int totalDays(int y, int m, int d); + const char *dow[20] = {"sunday", "monday", "tuesday", "wednesday", "thurseday", "friday", "saturday"}; +}; + +#endif //ESP32 + +#endif //ESP32TimeHelper_H diff --git a/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp new file mode 100755 index 000000000..85cb0663e --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp @@ -0,0 +1,4875 @@ +/* + *Mail Client Arduino Library for ESP32, version 2.1.4 + * + * April 12, 2020 + * + * This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download through SMTP and IMAP servers. + * + * The library supports all ESP32 MCU based modules. + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef ESP32_MailClient_CPP +#define ESP32_MailClient_CPP + +#ifdef ESP32 + +#include "ESP32_MailClient.h" + +struct ESP32_MailClient::IMAP_COMMAND_TYPE +{ + static const uint8_t LOGIN = 0; + static const uint8_t LIST = 1; + static const uint8_t SELECT = 2; + static const uint8_t EXAMINE = 3; + static const uint8_t STATUS = 4; + static const uint8_t SEARCH = 5; + static const uint8_t FETCH_BODY_HEADER = 6; + static const uint8_t FETCH_BODY_MIME = 7; + static const uint8_t FETCH_BODY_TEXT = 8; + static const uint8_t FETCH_BODY_ATTACHMENT = 9; + static const uint8_t LOGOUT = 10; +}; + +struct ESP32_MailClient::IMAP_HEADER_TYPE +{ + static const uint8_t FROM = 1; + static const uint8_t TO = 2; + static const uint8_t CC = 3; + static const uint8_t SUBJECT = 4; + static const uint8_t DATE = 5; + static const uint8_t MSG_ID = 6; + static const uint8_t CONT_LANG = 7; + static const uint8_t ACCEPT_LANG = 8; +}; + + + +bool ESP32_MailClient::readMail(IMAPData &imapData) +{ + + std::string buf; + std::string command = "$"; + + size_t mailIndex = 0; + int messageDataIndex = 0; + int partID = 1; + int _partID = 1; + bool res = false; + bool _res = false; + bool starttls = imapData._starttls; + bool connected = false; + + int bufSize = 50; + + char *_val = new char[bufSize]; + char *_part = new char[bufSize]; + + unsigned long dataTime = 0; + + int count = 0; + + imapData._net->setDebugCallback(NULL); + + if (imapData._debug) + { + ESP32MailDebugInfo(ESP32_MAIL_STR_225); + ESP32MailDebug(imapData._host.c_str()); + ESP32MailDebug(String(imapData._port).c_str()); + } + + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_50; + imapData._cbData._status = ESP32_MAIL_STR_51; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + imapData._net->setDebugCallback(ESP32MailDebug); + + if (imapData._rootCA.size() > 0) + imapData._net->begin(imapData._host.c_str(), imapData._port, ESP32_MAIL_STR_202, (const char *)imapData._rootCA.front()); + else + imapData._net->begin(imapData._host.c_str(), imapData._port, ESP32_MAIL_STR_202, (const char *)NULL); + + while (!imapData._net->connected() && count < 10) + { + + count++; + + if (!imapData._net->connect(starttls)) + { + + _imapStatus = IMAP_STATUS_SERVER_CONNECT_FAILED; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + } + else + { + break; + } + } + + if (!imapData._net->connect(starttls)) + { + goto out; + } + + connected = true; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_54; + imapData._cbData._status = ESP32_MAIL_STR_55; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_228); + + //Don't expect handshake from some servers + dataTime = millis(); + + while (imapData._net->connected() && !imapData._net->getStreamPtr()->available() && millis() - 500 < dataTime) + delay(0); + + if (imapData._net->connected() && imapData._net->getStreamPtr()->available()) + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData.clearMessageData(); + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_56; + imapData._cbData._status = ESP32_MAIL_STR_57; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_229); + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_130); + imapData._net->getStreamPtr()->print(imapData._loginEmail.c_str()); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_131); + imapData._net->getStreamPtr()->println(imapData._loginPassword.c_str()); + + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::LOGIN)) + { + _imapStatus = IMAP_STATUS_LOGIN_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._fetchUID.length() > 0) + imapData._headerOnly = false; + else + imapData._headerOnly = true; + + if (imapData._headerOnly) + { + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_58; + imapData._cbData._status = ESP32_MAIL_STR_59; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_230); + + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_133); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::LIST)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + imapData._cbData.empty(); + } + + if (imapData._readCallback) + { + + imapData._cbData._info = ESP32_MAIL_STR_60; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + for (size_t i = 0; i < imapData._folders.size(); i++) + { + imapData._cbData._info = imapData._folders[i]; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + imapData._cbData._info = ESP32_MAIL_STR_61 + imapData._currentFolder + ESP32_MAIL_STR_97; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_231); + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_135); + imapData._net->getStreamPtr()->print(imapData._currentFolder.c_str()); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_136); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::EXAMINE)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._headerOnly) + { + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_62 + imapData._nextUID; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + imapData._cbData._info = ESP32_MAIL_STR_63; + memset(_val, 0, bufSize); + itoa(imapData._totalMessage, _val, 10); + imapData._cbData._info += _val; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + imapData._cbData._info = ESP32_MAIL_STR_64; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + for (size_t i = 0; i < imapData._flag.size(); i++) + { + imapData._cbData._info = imapData._flag[i]; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + imapData._cbData._info = ESP32_MAIL_STR_65; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + imapData._cbData._info = ESP32_MAIL_STR_66; + imapData._cbData._status = ESP32_MAIL_STR_67; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + + imapData._msgNum.clear(); + imapData._uidSearch = false; + imapData._msgID.clear(); + imapData._contentLanguage.clear(); + imapData._acceptLanguage.clear(); + imapData._attachmentCount.clear(); + imapData._totalAttachFileSize.clear(); + imapData._downloadedByte.clear(); + imapData._error.clear(); + imapData._errorMsg.clear(); + imapData._searchCount = 0; + + if (imapData._headerOnly) + { + + if (imapData._searchCriteria != "") + { + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_232); + + if (imapData._searchCriteria.find(ESP32_MAIL_STR_137) != std::string::npos) + { + imapData._uidSearch = true; + command += ESP32_MAIL_STR_138; + } + command += ESP32_MAIL_STR_139; + + for (size_t i = 0; i < imapData._searchCriteria.length(); i++) + { + if (imapData._searchCriteria[i] != ' ' && imapData._searchCriteria[i] != '\r' && imapData._searchCriteria[i] != '\n' && imapData._searchCriteria[i] != '$') + buf.append(1, imapData._searchCriteria[i]); + + if (imapData._searchCriteria[i] == ' ') + { + if ((imapData._uidSearch && buf == ESP32_MAIL_STR_140) || (imapData._unseen && buf.find(ESP32_MAIL_STR_224) != std::string::npos)) + buf.clear(); + + if (buf != ESP32_MAIL_STR_141 && buf != "") + { + command += ESP32_MAIL_STR_131; + command += buf; + } + + buf.clear(); + } + } + + if (imapData._unseen && imapData._searchCriteria.find(ESP32_MAIL_STR_223) == std::string::npos) + command += ESP32_MAIL_STR_223; + + if (buf.length() > 0) + { + command += ESP32_MAIL_STR_131; + command += buf; + } + + imapData._net->getStreamPtr()->println(command.c_str()); + + std::string().swap(command); + + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::SEARCH, 1)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + + imapData._cbData._info = ESP32_MAIL_STR_68; + memset(_val, 0, bufSize); + itoa(imapData._emailNumMax, _val, 10); + imapData._cbData._info += _val; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + if (imapData._msgNum.size() > 0) + { + + imapData._cbData._info = ESP32_MAIL_STR_69; + memset(_val, 0, bufSize); + itoa(imapData._searchCount, _val, 10); + imapData._cbData._info += _val; + imapData._cbData._info += ESP32_MAIL_STR_70; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + imapData._cbData._info = ESP32_MAIL_STR_71; + memset(_val, 0, bufSize); + itoa(imapData._msgNum.size(), _val, 10); + imapData._cbData._info += _val; + imapData._cbData._info += ESP32_MAIL_STR_70; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + else + { + imapData._cbData._info = ESP32_MAIL_STR_72; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + } + else + { + + imapData._msgNum.push_back(imapData._totalMessage); + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_73; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + } + else + { + + imapData._msgNum.push_back(atoi(imapData._fetchUID.c_str())); + } + + for (int i = 0; i < imapData._messageDataInfo.size(); i++) + imapData._messageDataInfo[i].clear(); + + imapData._messageDataInfo.clear(); + + for (int i = 0; i < imapData._msgNum.size(); i++) + { + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_233); + + if (imapData._readCallback) + { + + imapData._cbData._info = ESP32_MAIL_STR_74; + memset(_val, 0, bufSize); + itoa(i + 1, _val, 10); + imapData._cbData._info += _val; + + imapData._cbData._status = ""; + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._cbData._info += ESP32_MAIL_STR_75; + else + imapData._cbData._info += ESP32_MAIL_STR_76; + + memset(_val, 0, bufSize); + itoa(imapData._msgNum[i], _val, 10); + imapData._cbData._info += _val; + imapData._cbData._status = ESP32_MAIL_STR_77; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + imapData._date.push_back(std::string()); + imapData._subject.push_back(std::string()); + imapData._subject_charset.push_back(std::string()); + imapData._from.push_back(std::string()); + imapData._from_charset.push_back(std::string()); + imapData._to.push_back(std::string()); + imapData._to_charset.push_back(std::string()); + imapData._cc.push_back(std::string()); + imapData._attachmentCount.push_back(0); + imapData._totalAttachFileSize.push_back(0); + imapData._downloadedByte.push_back(0); + imapData._messageDataCount.push_back(0); + imapData._error.push_back(false); + imapData._errorMsg.push_back(std::string()); + imapData._cc_charset.push_back(std::string()); + imapData._msgID.push_back(std::string()); + imapData._acceptLanguage.push_back(std::string()); + imapData._contentLanguage.push_back(std::string()); + + std::vector d = std::vector(); + + imapData._messageDataInfo.push_back(d); + + std::vector().swap(d); + + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_142); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_143); + + imapData._net->getStreamPtr()->print(imapData._msgNum[i]); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_144); + + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::FETCH_BODY_HEADER, 0, mailIndex)) + { + if (imapData._headerOnly) + _imapStatus = IMAP_STATUS_IMAP_RESPONSE_FAILED; + else + _imapStatus = IMAP_STATUS_BAD_COMMAND; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (!imapData._headerOnly) + { + + messageDataIndex = 0; + partID = 1; + _partID = 1; + res = false; + _res = false; + + do + { + + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_142); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_143); + + imapData._net->getStreamPtr()->print(imapData._msgNum[i]); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_147); + imapData._net->getStreamPtr()->print(partID); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_148); + + memset(_part, 0, bufSize); + memset(_val, 0, bufSize); + itoa(partID, _val, 10); + strcpy(_part, _val); + res = waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::FETCH_BODY_MIME, 0, mailIndex, messageDataIndex, _part); + if (res) + { + + if (imapData._messageDataInfo[mailIndex].size() < messageDataIndex + 1) + { + messageBodyData b; + imapData._messageDataInfo[mailIndex].push_back(b); + b.empty(); + imapData._messageDataCount[mailIndex] = imapData._messageDataInfo[mailIndex].size(); + } + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == "") + continue; + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType.find(ESP32_MAIL_STR_149) != std::string::npos) + { + do + { + + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_142); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_143); + + imapData._net->getStreamPtr()->print(imapData._msgNum[i]); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_147); + imapData._net->getStreamPtr()->print(partID); + imapData._net->getStreamPtr()->print("."); + imapData._net->getStreamPtr()->print(_partID); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_148); + + memset(_part, 0, bufSize); + memset(_val, 0, bufSize); + itoa(partID, _val, 10); + strcpy(_part, _val); + strcat(_part, "."); + memset(_val, 0, bufSize); + itoa(_partID, _val, 10); + strcat(_part, _val); + _res = waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::FETCH_BODY_MIME, 0, mailIndex, messageDataIndex, _part); + + if (_res) + { + messageDataIndex++; + _partID++; + } + + } while (_res); + } + else + { + messageDataIndex++; + } + partID++; + } + + } while (res); + + if (imapData._saveHTMLMsg || imapData._saveTextMsg || imapData._downloadAttachment) + { + + if (!_sdOk) + { + if (imapData._storageType == MailClientStorageType::SD) + { + _sdOk = sdTest(); + if (_sdOk) + if (!SD.exists(imapData._savePath.c_str())) + createDirs(imapData._savePath); + } + else if (imapData._storageType == MailClientStorageType::SPIFFS) + _sdOk = SPIFFS.begin(true); + } + } + + if (imapData._messageDataInfo[mailIndex].size() > 0) + { + if (imapData._attachmentCount[mailIndex] > 0 && imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_78; + memset(_val, 0, bufSize); + itoa(imapData._attachmentCount[mailIndex], _val, 10); + imapData._cbData._info += _val; + imapData._cbData._info += ESP32_MAIL_STR_79; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + for (int j = 0; j < imapData._messageDataInfo[mailIndex].size(); j++) + { + if (imapData._messageDataInfo[mailIndex][j]._disposition == ESP32_MAIL_STR_153) + { + imapData._cbData._info = imapData._messageDataInfo[mailIndex][j]._filename; + imapData._cbData._info += ESP32_MAIL_STR_83; + memset(_val, 0, bufSize); + itoa(imapData._messageDataInfo[mailIndex][j]._size, _val, 10); + imapData._cbData._info += _val; + imapData._cbData._info += ESP32_MAIL_STR_82; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + + if (imapData._downloadAttachment && _sdOk) + { + imapData._cbData._info = ESP32_MAIL_STR_80; + imapData._cbData._status = ESP32_MAIL_STR_81; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + + for (int j = 0; j < imapData._messageDataInfo[mailIndex].size(); j++) + { + + if (imapData._messageDataInfo[mailIndex][j]._disposition == "") + { + + if (!imapData._textFormat && imapData._messageDataInfo[mailIndex][j]._contentType != ESP32_MAIL_STR_154) + continue; + + if (!imapData._htmlFormat && imapData._messageDataInfo[mailIndex][j]._contentType != ESP32_MAIL_STR_155) + continue; + + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_142); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_143); + + imapData._net->getStreamPtr()->print(imapData._msgNum[i]); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_147); + imapData._net->getStreamPtr()->print(imapData._messageDataInfo[mailIndex][j]._part.c_str()); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_156); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::FETCH_BODY_TEXT, imapData._message_buffer_size, mailIndex, j)) + { + _imapStatus = IMAP_STATUS_IMAP_RESPONSE_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + } + } + else if (imapData._messageDataInfo[mailIndex][j]._disposition == ESP32_MAIL_STR_153 && _sdOk) + { + + if (imapData._downloadAttachment) + { + if (imapData._messageDataInfo[mailIndex][j]._size <= imapData._attacement_max_size) + { + + if (_sdOk) + { + + if (j < imapData._messageDataInfo[mailIndex].size() - 1) + if (imapData._messageDataInfo[mailIndex][j + 1]._size > imapData._attacement_max_size) + imapData._downloadedByte[mailIndex] += imapData._messageDataInfo[mailIndex][j + 1]._size; + + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_142); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_143); + + imapData._net->getStreamPtr()->print(imapData._msgNum[i]); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_147); + imapData._net->getStreamPtr()->print(imapData._messageDataInfo[mailIndex][j]._part.c_str()); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_156); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::FETCH_BODY_ATTACHMENT, imapData._message_buffer_size, mailIndex, j)) + { + _imapStatus = IMAP_STATUS_IMAP_RESPONSE_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + } + + delay(0); + } + } + else + { + if (j == imapData._messageDataInfo[mailIndex].size() - 1) + imapData._downloadedByte[mailIndex] += imapData._messageDataInfo[mailIndex][j]._size; + } + } + } + } + } + + if (imapData._storageType == MailClientStorageType::SD) + { + if (_sdOk) + SD.end(); + } + else if (imapData._storageType == MailClientStorageType::SPIFFS) + { + if (_sdOk) + SPIFFS.end(); + } + + _sdOk = false; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_84; + memset(_val, 0, bufSize); + itoa(ESP.getFreeHeap(), _val, 10); + imapData._cbData._info += _val; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + mailIndex++; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_85; + imapData._cbData._status = ESP32_MAIL_STR_86; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_234); + + if (imapData._net->connected()) + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_146); + + if (!waitIMAPResponse(imapData, 0)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_98; + imapData._cbData._status = ESP32_MAIL_STR_96; + imapData._cbData._success = true; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_235); + + if (imapData._net->connected()) + { + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData._net->getStreamPtr()->stop(); + } + + imapData._cbData.empty(); + delete[] _val; + delete[] _part; + std::string().swap(command); + std::string().swap(buf); + + return true; + +out: + + if (connected) + { + if (imapData._net->connected()) + { + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + imapData._net->getStreamPtr()->stop(); + } + } + + imapData._cbData.empty(); + delete[] _val; + delete[] _part; + std::string().swap(command); + std::string().swap(buf); + + return false; +} + +bool ESP32_MailClient::setFlag(IMAPData &imapData, int msgUID, const String &flag) +{ + return _setFlag(imapData, msgUID, flag, 0); +} + +bool ESP32_MailClient::addFlag(IMAPData &imapData, int msgUID, const String &flag) +{ + return _setFlag(imapData, msgUID, flag, 1); +} + +bool ESP32_MailClient::removeFlag(IMAPData &imapData, int msgUID, const String &flag) +{ + return _setFlag(imapData, msgUID, flag, 2); +} + +bool ESP32_MailClient::_setFlag(IMAPData &imapData, int msgUID, const String &flag, uint8_t action) +{ + + std::string buf; + + bool starttls = imapData._starttls; + bool connected = false; + + int bufSize = 50; + + char *_val = new char[bufSize]; + char *_part = new char[bufSize]; + + unsigned long dataTime = 0; + + int count = 0; + + imapData._net->setDebugCallback(NULL); + + if (imapData._debug) + { + ESP32MailDebugInfo(ESP32_MAIL_STR_225); + ESP32MailDebug(imapData._host.c_str()); + ESP32MailDebug(String(imapData._port).c_str()); + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_50; + imapData._cbData._status = ESP32_MAIL_STR_51; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + imapData._net->setDebugCallback(ESP32MailDebug); + + if (imapData._rootCA.size() > 0) + imapData._net->begin(imapData._host.c_str(), imapData._port, ESP32_MAIL_STR_202, (const char *)imapData._rootCA.front()); + else + imapData._net->begin(imapData._host.c_str(), imapData._port, ESP32_MAIL_STR_202, (const char *)NULL); + + while (!imapData._net->connected() && count < 10) + { + + count++; + + if (!imapData._net->connect(starttls)) + { + + _imapStatus = IMAP_STATUS_SERVER_CONNECT_FAILED; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + } + else + { + break; + } + } + + if (!imapData._net->connect(starttls)) + { + goto out; + } + + connected = true; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_54; + imapData._cbData._status = ESP32_MAIL_STR_55; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_228); + + //Don't expect handshake from some servers + dataTime = millis(); + + while (imapData._net->connected() && !imapData._net->getStreamPtr()->available() && millis() - 500 < dataTime) + delay(0); + + if (imapData._net->connected() && imapData._net->getStreamPtr()->available()) + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData.clearMessageData(); + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_56; + imapData._cbData._status = ESP32_MAIL_STR_57; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_229); + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_130); + imapData._net->getStreamPtr()->print(imapData._loginEmail.c_str()); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_131); + imapData._net->getStreamPtr()->println(imapData._loginPassword.c_str()); + + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::LOGIN)) + { + _imapStatus = IMAP_STATUS_LOGIN_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_58; + imapData._cbData._status = ESP32_MAIL_STR_59; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_230); + + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_133); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::LIST)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + imapData._cbData.empty(); + } + + if (imapData._readCallback) + { + + imapData._cbData._info = ESP32_MAIL_STR_60; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + for (size_t i = 0; i < imapData._folders.size(); i++) + { + imapData._cbData._info = imapData._folders[i]; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + imapData._cbData._info = ESP32_MAIL_STR_61 + imapData._currentFolder + ESP32_MAIL_STR_97; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_248); + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_247); + imapData._net->getStreamPtr()->print(imapData._currentFolder.c_str()); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_136); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::EXAMINE)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._debug) + { + if (action == 0) + ESP32MailDebugInfo(ESP32_MAIL_STR_253); + else if (action == 1) + ESP32MailDebugInfo(ESP32_MAIL_STR_254); + else + ESP32MailDebugInfo(ESP32_MAIL_STR_255); + } + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_249); + imapData._net->getStreamPtr()->print(msgUID); + if (action == 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_250); + else if (action == 1) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_251); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_252); + imapData._net->getStreamPtr()->print(flag); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_192); + + if (!getIMAPResponse(imapData)) + { + _imapStatus = IMAP_STATUS_PARSE_FLAG_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_85; + imapData._cbData._status = ESP32_MAIL_STR_86; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_234); + + if (imapData._net->connected()) + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_146); + + if (!waitIMAPResponse(imapData, 0)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_98; + imapData._cbData._status = ESP32_MAIL_STR_96; + imapData._cbData._success = true; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_235); + + if (imapData._net->connected()) + { + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData._net->getStreamPtr()->stop(); + } + + imapData._cbData.empty(); + delete[] _val; + delete[] _part; + std::string().swap(buf); + + return true; + +out: + + if (connected) + { + if (imapData._net->connected()) + { + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + imapData._net->getStreamPtr()->stop(); + } + } + + imapData._cbData.empty(); + delete[] _val; + delete[] _part; + std::string().swap(buf); + return false; +} + + +bool ESP32_MailClient::smtpClientAvailable(SMTPData &smtpData, bool available) +{ + + if (!smtpData._net->getStreamPtr()) + return false; + + if (available) + return smtpData._net->getStreamPtr()->connected() && smtpData._net->getStreamPtr()->available(); + else + return smtpData._net->getStreamPtr()->connected() && !smtpData._net->getStreamPtr()->available(); +} + +bool ESP32_MailClient::imapClientAvailable(IMAPData &imapData, bool available) +{ + + if (!imapData._net->getStreamPtr()) + return false; + + if (available) + return imapData._net->getStreamPtr()->connected() && imapData._net->getStreamPtr()->available(); + else + return imapData._net->getStreamPtr()->connected() && !imapData._net->getStreamPtr()->available(); +} + +void ESP32_MailClient::createDirs(std::string dirs) +{ + std::string dir = ""; + int count = 0; + for (int i = 0; i < dirs.length(); i++) + { + dir.append(1, dirs[i]); + count++; + if (dirs[i] == '/') + { + if (dir.length() > 0) + SD.mkdir(dir.substr(0, dir.length() - 1).c_str()); + count = 0; + } + } + if (count > 0) + SD.mkdir(dir.c_str()); + std::string().swap(dir); +} + +bool ESP32_MailClient::sdTest() +{ + + if (_sdConfigSet) + sdBegin(_sck, _miso, _mosi, _ss); + else + sdBegin(); + + File file = SD.open(ESP32_MAIL_STR_204, FILE_WRITE); + if (!file) + return false; + + if (!file.write(32)) + return false; + file.close(); + + file = SD.open(ESP32_MAIL_STR_204); + if (!file) + return false; + + while (file.available()) + { + if (file.read() != 32) + return false; + } + file.close(); + + SD.remove(ESP32_MAIL_STR_204); + + return true; +} + +bool ESP32_MailClient::sendMail(SMTPData &smtpData) +{ + + _smtpStatus = 0; + std::string buf; + std::string buf2; + int bufSize = 50; + bool starttls = smtpData._starttls; + bool connected = false; + char *_val = new char[bufSize]; + int res = 0; + + smtpData._net->setDebugCallback(NULL); + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_120; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + { + ESP32MailDebugInfo(ESP32_MAIL_STR_236); + ESP32MailDebug(smtpData._host.c_str()); + ESP32MailDebug(String(smtpData._port).c_str()); + } + + + if (smtpData._debug) + smtpData._net->setDebugCallback(ESP32MailDebug); + + if (smtpData._rootCA.size() > 0) + smtpData._net->begin(smtpData._host.c_str(), smtpData._port, ESP32_MAIL_STR_202, (const char *)smtpData._rootCA.front()); + else + smtpData._net->begin(smtpData._host.c_str(), smtpData._port, ESP32_MAIL_STR_202, (const char *)NULL); + + if (smtpData._port == 587) + starttls = true; + + if (!smtpData._net->connect(starttls)) + { + _smtpStatus = SMTP_STATUS_SERVER_CONNECT_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_238); + + connected = true; + + if (!starttls) + { + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_121; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (waitSMTPResponse(smtpData) != 220) + { + _smtpStatus = SMTP_STATUS_SMTP_RESPONSE_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_122; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_239); + + smtpData._net->getStreamPtr()->println(ESP32_MAIL_STR_5); + + if (waitSMTPResponse(smtpData) != 250) + { + _smtpStatus = SMTP_STATUS_IDENTIFICATION_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_123; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_240); + + smtpData._net->getStreamPtr()->println(ESP32_MAIL_STR_4); + + if (waitSMTPResponse(smtpData) != 334) + { + _smtpStatus = SMTP_STATUS_AUTHEN_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_124; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_241); + + smtpData._net->getStreamPtr()->println(base64_encode_string((const unsigned char *)smtpData._loginEmail.c_str(), smtpData._loginEmail.length()).c_str()); + + if (waitSMTPResponse(smtpData) != 334) + { + _smtpStatus = SMTP_STATUS_USER_LOGIN_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + smtpData._net->getStreamPtr()->println(base64_encode_string((const unsigned char *)smtpData._loginPassword.c_str(), smtpData._loginPassword.length()).c_str()); + + if (waitSMTPResponse(smtpData) != 235) + { + _smtpStatus = SMTP_STATUS_PASSWORD_LOGIN_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_125; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_242); + + if (smtpData._priority > 0 && smtpData._priority <= 5) + { + memset(_val, 0, bufSize); + itoa(smtpData._priority, _val, 10); + + buf2 += ESP32_MAIL_STR_17; + buf2 += _val; + buf2 += ESP32_MAIL_STR_34; + + if (smtpData._priority == 1) + { + buf2 += ESP32_MAIL_STR_18; + buf2 += ESP32_MAIL_STR_21; + } + else if (smtpData._priority == 3) + { + buf2 += ESP32_MAIL_STR_19; + buf2 += ESP32_MAIL_STR_22; + } + else if (smtpData._priority == 5) + { + buf2 += ESP32_MAIL_STR_20; + buf2 += ESP32_MAIL_STR_23; + } + } + + buf2 += ESP32_MAIL_STR_10; + + if (smtpData._fromName.length() > 0) + buf2 += smtpData._fromName; + + buf2 += ESP32_MAIL_STR_14; + buf2 += smtpData._senderEmail; + buf2 += ESP32_MAIL_STR_15; + buf2 += ESP32_MAIL_STR_34; + + buf += ESP32_MAIL_STR_8; + buf += ESP32_MAIL_STR_14; + buf += smtpData._senderEmail; + buf += ESP32_MAIL_STR_15; + smtpData._net->getStreamPtr()->println(buf.c_str()); + + if (waitSMTPResponse(smtpData) != 250) + { + _smtpStatus = SMTP_STATUS_SEND_HEADER_SENDER_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + for (uint8_t i = 0; i < smtpData._recipient.size(); i++) + { + if (i == 0) + { + buf2 += ESP32_MAIL_STR_11; + buf2 += ESP32_MAIL_STR_14; + buf2 += smtpData._recipient[i]; + buf2 += ESP32_MAIL_STR_15; + } + else + { + buf2 += ESP32_MAIL_STR_13; + buf2 += smtpData._recipient[i]; + buf2 += ESP32_MAIL_STR_15; + } + + if (i == smtpData._recipient.size() - 1) + buf2 += ESP32_MAIL_STR_34; + + buf.clear(); + + buf += ESP32_MAIL_STR_9; + buf += ESP32_MAIL_STR_14; + buf += smtpData._recipient[i]; + buf += ESP32_MAIL_STR_15; + + smtpData._net->getStreamPtr()->println(buf.c_str()); + + if (waitSMTPResponse(smtpData) != 250) + { + _smtpStatus = SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + } + + for (uint8_t i = 0; i < smtpData._cc.size(); i++) + { + + if (i == 0) + { + buf2 += ESP32_MAIL_STR_12; + buf2 += ESP32_MAIL_STR_14; + buf2 += smtpData._cc[i]; + buf2 += ESP32_MAIL_STR_15; + } + else + { + buf2 += ESP32_MAIL_STR_13; + buf2 += smtpData._cc[i]; + buf2 += ESP32_MAIL_STR_15; + } + + if (i == smtpData.ccCount() - 1) + buf2 += ESP32_MAIL_STR_34; + + buf.clear(); + + buf += ESP32_MAIL_STR_9; + buf += ESP32_MAIL_STR_14; + buf += smtpData._cc[i]; + buf += ESP32_MAIL_STR_15; + smtpData._net->getStreamPtr()->println(buf.c_str()); + + if (waitSMTPResponse(smtpData) != 250) + { + _smtpStatus = SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + } + + for (uint8_t i = 0; i < smtpData._bcc.size(); i++) + { + buf.clear(); + buf += ESP32_MAIL_STR_9; + buf += ESP32_MAIL_STR_14; + buf += smtpData._bcc[i]; + buf += ESP32_MAIL_STR_15; + smtpData._net->getStreamPtr()->println(buf.c_str()); + + if (waitSMTPResponse(smtpData) != 250) + { + _smtpStatus = SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_126; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_243); + + smtpData._net->getStreamPtr()->println(ESP32_MAIL_STR_16); + + if (waitSMTPResponse(smtpData) != 354) + { + _smtpStatus = SMTP_STATUS_SEND_BODY_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + smtpData._net->getStreamPtr()->print(buf2.c_str()); + + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_24); + smtpData._net->getStreamPtr()->println(smtpData._subject.c_str()); + + if (smtpData._customMessageHeader.size() > 0) + for (uint8_t k = 0; k < smtpData._customMessageHeader.size(); k++) + smtpData._net->getStreamPtr()->println(smtpData._customMessageHeader[k].c_str()); + + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_3); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_1); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_2); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_35); + + buf.clear(); + + set_message_header(buf, smtpData._message, smtpData._htmlFormat); + + smtpData._net->getStreamPtr()->print(buf.c_str()); + + if (smtpData._attach._index > 0) + { + smtpData._cbData._info = ESP32_MAIL_STR_127; + smtpData._cbData._success = false; + if (smtpData._sendCallback) + smtpData._sendCallback(smtpData._cbData); + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_244); + } + + for (uint8_t i = 0; i < smtpData._attach._index; i++) + { + if (smtpData._attach._type[i] == 0) + { + + smtpData._cbData._info = smtpData._attach._filename[i]; + smtpData._cbData._success = false; + if (smtpData._sendCallback) + smtpData._sendCallback(smtpData._cbData); + if (smtpData._debug) + ESP32MailDebug(smtpData._attach._filename[i].c_str()); + + buf.clear(); + set_attachment_header(i, buf, smtpData._attach); + smtpData._net->getStreamPtr()->print(buf.c_str()); + send_base64_encode_mime_data(smtpData._net->getStreamPtr(), smtpData._attach._buf[i].front(), smtpData._attach._size[i]); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_34); + } + else + { + + if (!_sdOk) + { + if (smtpData._storageType == MailClientStorageType::SD) + _sdOk = sdTest(); + else if (smtpData._storageType == MailClientStorageType::SPIFFS) + _sdOk = SPIFFS.begin(true); + } + + if (!_sdOk) + continue; + + bool file_existed = false; + if (smtpData._storageType == MailClientStorageType::SD) + file_existed = SD.exists(smtpData._attach._filename[i].c_str()); + else if (smtpData._storageType == MailClientStorageType::SPIFFS) + file_existed = SPIFFS.exists(smtpData._attach._filename[i].c_str()); + + if (file_existed) + { + smtpData._cbData._info = smtpData._attach._filename[i]; + smtpData._cbData._success = false; + if (smtpData._sendCallback) + smtpData._sendCallback(smtpData._cbData); + + if (smtpData._debug) + ESP32MailDebug(smtpData._attach._filename[i].c_str()); + + buf.clear(); + set_attachment_header(i, buf, smtpData._attach); + smtpData._net->getStreamPtr()->print(buf.c_str()); + + File file; + if (smtpData._storageType == MailClientStorageType::SD) + file = SD.open(smtpData._attach._filename[i].c_str(), FILE_READ); + else if (smtpData._storageType == MailClientStorageType::SPIFFS) + file = SPIFFS.open(smtpData._attach._filename[i].c_str(), FILE_READ); + + send_base64_encode_mime_file(smtpData._net->getStreamPtr(), file); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_34); + } + } + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_245); + + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_33); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_2); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_33); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_37); + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_128; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + res = waitSMTPResponse(smtpData); + + if (res != 250 && res != -1000) + { + _smtpStatus = SMTP_STATUS_SEND_BODY_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_129; + smtpData._cbData._success = true; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_246); + + if (smtpData._net->connected()) + smtpData._net->getStreamPtr()->stop(); + + smtpData._cbData.empty(); + + std::string().swap(buf); + std::string().swap(buf2); + delete[] _val; + + return true; + +failed: + + if (connected) + { + if (smtpData._net->connected()) + smtpData._net->getStreamPtr()->stop(); + } + + smtpData._cbData.empty(); + std::string().swap(buf); + std::string().swap(buf2); + delete[] _val; + return false; +} + +String ESP32_MailClient::smtpErrorReason() +{ + return smtpErrorReasonStr().c_str(); +} + +std::string ESP32_MailClient::smtpErrorReasonStr() +{ + std::string res = ""; + switch (_smtpStatus) + { + case SMTP_STATUS_SERVER_CONNECT_FAILED: + res = ESP32_MAIL_STR_38; + break; + case SMTP_STATUS_SMTP_RESPONSE_FAILED: + res = ESP32_MAIL_STR_39; + break; + case SMTP_STATUS_IDENTIFICATION_FAILED: + res = ESP32_MAIL_STR_41; + break; + case SMTP_STATUS_AUTHEN_NOT_SUPPORT: + res = ESP32_MAIL_STR_42; + break; + case SMTP_STATUS_AUTHEN_FAILED: + res = ESP32_MAIL_STR_43; + break; + case SMTP_STATUS_USER_LOGIN_FAILED: + res = ESP32_MAIL_STR_44; + break; + case SMTP_STATUS_PASSWORD_LOGIN_FAILED: + res = ESP32_MAIL_STR_47; + break; + case SMTP_STATUS_SEND_HEADER_SENDER_FAILED: + res = ESP32_MAIL_STR_48; + break; + case SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED: + res = ESP32_MAIL_STR_222; + break; + case SMTP_STATUS_SEND_BODY_FAILED: + res = ESP32_MAIL_STR_49; + break; + case MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL: + res = ESP32_MAIL_STR_221; + break; + default: + res = ""; + } + return res; +} + +String ESP32_MailClient::imapErrorReason() +{ + std::string res = ""; + switch (_imapStatus) + { + case IMAP_STATUS_SERVER_CONNECT_FAILED: + res = ESP32_MAIL_STR_38; + break; + case IMAP_STATUS_IMAP_RESPONSE_FAILED: + res = ESP32_MAIL_STR_40; + break; + case IMAP_STATUS_LOGIN_FAILED: + res = ESP32_MAIL_STR_45; + break; + case IMAP_STATUS_BAD_COMMAND: + res = ESP32_MAIL_STR_46; + break; + case IMAP_STATUS_PARSE_FLAG_FAILED: + res = ESP32_MAIL_STR_256; + break; + case MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL: + res = ESP32_MAIL_STR_221; + break; + default: + res = ""; + } + return res.c_str(); +} + +std::string ESP32_MailClient::imapErrorReasonStr() +{ + std::string res = ""; + + switch (_imapStatus) + { + case IMAP_STATUS_SERVER_CONNECT_FAILED: + res = ESP32_MAIL_STR_38; + break; + case IMAP_STATUS_IMAP_RESPONSE_FAILED: + res = ESP32_MAIL_STR_40; + break; + case IMAP_STATUS_LOGIN_FAILED: + res = ESP32_MAIL_STR_45; + break; + case IMAP_STATUS_BAD_COMMAND: + res = ESP32_MAIL_STR_46; + break; + case IMAP_STATUS_PARSE_FLAG_FAILED: + res = ESP32_MAIL_STR_256; + break; + case MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL: + res = ESP32_MAIL_STR_221; + break; + default: + res = ""; + } + return res; +} + +void ESP32_MailClient::ESP32MailDebugError() +{ + size_t dbgInfoLen = strlen_P(ESP32_MAIL_STR_227) + 1; + char *dbgInfo = new char[dbgInfoLen]; + memset(dbgInfo, 0, dbgInfoLen); + strcpy_P(dbgInfo, ESP32_MAIL_STR_227); + ESP32MailDebugLine(dbgInfo, false); + delete[] dbgInfo; +} + +void ESP32_MailClient::ESP32MailDebugInfo(PGM_P info) +{ + size_t dbgInfoLen = strlen_P(info) + 1; + char *dbgInfo = new char[dbgInfoLen]; + memset(dbgInfo, 0, dbgInfoLen); + strcpy_P(dbgInfo, info); + ESP32MailDebug(dbgInfo); + delete[] dbgInfo; +} + +bool ESP32_MailClient::sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss) +{ + _sck = sck; + _miso = miso; + _mosi = mosi; + _ss = ss; + _sdConfigSet = true; + SPI.begin(_sck, _miso, _mosi, _ss); + return SD.begin(_ss, SPI); +} + +bool ESP32_MailClient::sdBegin(void) +{ + _sdConfigSet = false; + return SD.begin(); +} + +void ESP32_MailClient::set_message_header(string &header, string &message, bool htmlFormat) +{ + header += ESP32_MAIL_STR_33; + header += ESP32_MAIL_STR_2; + header += ESP32_MAIL_STR_34; + if (!htmlFormat) + header += ESP32_MAIL_STR_27; + else + header += ESP32_MAIL_STR_28; + + header += ESP32_MAIL_STR_29; + header += ESP32_MAIL_STR_34; + + header += message; + header += ESP32_MAIL_STR_34; + header += ESP32_MAIL_STR_34; +} + +void ESP32_MailClient::set_attachment_header(uint8_t index, std::string &header, attachmentData &attach) +{ + + header += ESP32_MAIL_STR_33; + header += ESP32_MAIL_STR_2; + header += ESP32_MAIL_STR_34; + + header += ESP32_MAIL_STR_25; + + if (attach._mime_type[index].length() == 0) + header += ESP32_MAIL_STR_32; + else + header += attach._mime_type[index]; + + header += ESP32_MAIL_STR_26; + + std::string filename(attach._filename[index]); + + size_t found = filename.find_last_of("/\\"); + + if (found != std::string::npos) + { + filename.clear(); + filename += attach._filename[index].substr(found + 1); + } + + header += filename; + header += ESP32_MAIL_STR_36; + + header += ESP32_MAIL_STR_30; + header += filename; + header += ESP32_MAIL_STR_36; + + header += ESP32_MAIL_STR_31; + header += ESP32_MAIL_STR_34; + + std::string().swap(filename); +} + +int ESP32_MailClient::waitSMTPResponse(SMTPData &smtpData) +{ + + long dataTime = millis(); + char c = '\0'; + std::string lineBuf = ""; + int lfCount = 0; + size_t p1 = 0; + int resCode = -1000; + + while (smtpClientAvailable(smtpData, false) && millis() - dataTime < smtpData._net->tcpTimeout) + delay(0); + + dataTime = millis(); + if (smtpClientAvailable(smtpData, true)) + { + while (smtpClientAvailable(smtpData, true)) + { + int r = smtpData._net->getStreamPtr()->read(); + + if (r < 0) + continue; + + c = (char)r; + + lineBuf.append(1, c); + if (c == '\n') + { + dataTime = millis(); + if (lfCount == 0) + { + p1 = lineBuf.find(" "); + if (p1 != std::string::npos) + resCode = atoi(lineBuf.substr(0, p1).c_str()); + } + if (smtpData._debug) + ESP32MailDebug(lineBuf.c_str()); + lineBuf.clear(); + lfCount++; + } + + if (millis() - dataTime > smtpData._net->tcpTimeout + 30000) + break; + } + } + std::string().swap(lineBuf); + return resCode; +} + +bool ESP32_MailClient::getIMAPResponse(IMAPData &imapData) +{ + long dataTime = millis(); + char c = '\0'; + bool success = false; + std::string str = ""; + while (imapClientAvailable(imapData, false) && millis() - dataTime < imapData._net->tcpTimeout) + delay(0); + + dataTime = millis(); + if (imapClientAvailable(imapData, true)) + { + while (imapClientAvailable(imapData, true)) + { + int r = imapData._net->getStreamPtr()->read(); + if (r < 0) + continue; + c = (char)r; + if (c == '\n') + { + if (imapData._debug) + ESP32MailDebug(str.c_str()); + str.clear(); + } + else + str += c; + + if (str.find(ESP32_MAIL_STR_132) != std::string::npos) + success = true; + } + } + + std::string().swap(str); + return success; +} + +bool ESP32_MailClient::waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandType, int maxChar, int mailIndex, int messageDataIndex, std::string part) +{ + + long dataTime = millis(); + + char c = 0; + std::string lineBuf = ""; + std::string msgNumBuf = ""; + std::string filepath = ""; + std::string hpath = ""; + std::string tmp = ""; + std::string msgID = ""; + std::string from = ""; + std::string to = ""; + std::string subject = ""; + std::string date = ""; + std::string cc = ""; + std::string from_charset = ""; + std::string to_charset = ""; + std::string cc_charset = ""; + std::string subject_charset = ""; + std::string acceptLanguage = ""; + std::string contentLanguage = ""; + + int bufSize = 100; + char *dest = new char[bufSize]; + char *buf = new char[bufSize]; + + int readCount = 0; + int lfCount = 0; + int charCount = 0; + size_t p1 = 0; + size_t p2 = 0; + size_t p3 = 0; + size_t payloadLength = 0; + size_t outputLength; + + bool completeResp = false; + bool validResponse = false; + bool downloadReq = false; + size_t currentDownloadByte = 0; + + int max = imapData._emailNumMax; + if (!imapData._recentSort) + max = max - 1; + + uint8_t headerType = 0; + + File file; + int reportState = 0; + int downloadedByte = 0; + + if (imapCommandType == IMAP_COMMAND_TYPE::LIST) + std::vector() + .swap(imapData._folders); + + while (imapClientAvailable(imapData, false) && millis() - dataTime < imapData._net->tcpTimeout) + delay(0); + + dataTime = millis(); + if (imapClientAvailable(imapData, true)) + { + while (imapClientAvailable(imapData, true) || !completeResp) + { + + int r = imapData._net->getStreamPtr()->read(); + + if (r < 0) + continue; + + c = (char)r; + + if (payloadLength > 0 && !completeResp) + charCount++; + + if (imapCommandType == IMAP_COMMAND_TYPE::SEARCH && lfCount == 0) + { + delay(0); + if (c == ' ') + { + p3 = msgNumBuf.find(ESP32_MAIL_STR_257); + if (p3 != std::string::npos) + { + validResponse = false; + break; + } + + if (msgNumBuf != ESP32_MAIL_STR_183 && msgNumBuf != ESP32_MAIL_STR_141 && imapData._msgNum.size() <= max) + { + imapData._msgNum.push_back(atoi(msgNumBuf.c_str())); + + if (imapData._msgNum.size() > imapData._emailNumMax && imapData._recentSort) + imapData._msgNum.erase(imapData._msgNum.begin()); + imapData._searchCount++; + } + + msgNumBuf.clear(); + } + else if (c != '\r' && c != '\n') + { + msgNumBuf.append(1, c); + } + } + + if (c != '\r' && c != '\n' && imapCommandType != IMAP_COMMAND_TYPE::SEARCH) + lineBuf.append(1, c); + + if (validResponse && imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_TEXT && lfCount > 0) + { + + if (payloadLength > 0 && charCount < payloadLength - 1) + { + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._transfer_encoding != ESP32_MAIL_STR_160) + { + if (charCount < maxChar) + imapData._messageDataInfo[mailIndex][messageDataIndex]._text.append(1, c); + + if (imapData._saveHTMLMsg || imapData._saveTextMsg) + { + + if (!imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite = true; + + if (_sdOk) + { + downloadReq = true; + + filepath.clear(); + + filepath = imapData._savePath; + filepath += ESP32_MAIL_STR_202; + + char *midx = new char[50]; + memset(midx, 0, 50); + itoa(imapData._msgNum[mailIndex], midx, 10); + + filepath += midx; + + delete[] midx; + + if (imapData._storageType == MailClientStorageType::SD) + if (!SD.exists(filepath.c_str())) + createDirs(filepath); + + if (!imapData._headerSaved) + hpath = filepath + ESP32_MAIL_STR_203; + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_155) + { + if (imapData._saveDecodedText) + filepath += ESP32_MAIL_STR_161; + else + filepath += ESP32_MAIL_STR_162; + } + else if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_154) + { + if (imapData._saveDecodedHTML) + filepath += ESP32_MAIL_STR_163; + else + filepath += ESP32_MAIL_STR_164; + } + + if (imapData._storageType == MailClientStorageType::SD) + file = SD.open(filepath.c_str(), FILE_WRITE); + else if (imapData._storageType == MailClientStorageType::SPIFFS) + file = SPIFFS.open(filepath.c_str(), FILE_WRITE); + } + else + { + + if (imapData._messageDataCount[mailIndex] == messageDataIndex + 1) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._error = true; + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError.clear(); + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError = ESP32_MAIL_STR_89; + } + } + } + if (_sdOk) + file.write(c); + } + } + } + + if (millis() - dataTime > imapData._net->tcpTimeout + (30 * 1000) || (payloadLength > 0 && charCount == payloadLength && completeResp)) + { + + if (charCount < payloadLength || !completeResp) + clientReadAll(imapData._net->getStreamPtr()); + + break; + } + } + + if (c == '\n') + { + dataTime = millis(); + + if (lfCount == 0) + { + if (imapData._debug) + ESP32MailDebug(lineBuf.c_str()); + + if (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_TEXT || + imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_MIME || + imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_HEADER || + imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_ATTACHMENT) + { + + p1 = lineBuf.find(ESP32_MAIL_STR_165); + if (p1 != std::string::npos) + validResponse = true; + } + + p1 = lineBuf.find(ESP32_MAIL_STR_166); + if (p1 != std::string::npos) + validResponse = true; + } + + p1 = lineBuf.find(ESP32_MAIL_STR_211); + p2 = lineBuf.find(ESP32_MAIL_STR_158); + p3 = lineBuf.find(ESP32_MAIL_STR_159); + + if (p1 != std::string::npos || p2 != std::string::npos || p3 != std::string::npos) + { + + validResponse = true; + + if (p2 != std::string::npos || p3 != std::string::npos) + validResponse = false; + + if (payloadLength == 0) + { + if (imapCommandType == IMAP_COMMAND_TYPE::LOGIN || + imapCommandType == IMAP_COMMAND_TYPE::LIST || + imapCommandType == IMAP_COMMAND_TYPE::EXAMINE || + imapCommandType == IMAP_COMMAND_TYPE::SEARCH || + imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_MIME || + imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_HEADER) + { + + //Cyrus server 3.0 does not comply to rfc3501 as it resonses the CAPABILITY after received LOGIN command with no CAPABILITY command requested. + if (lineBuf.find(ESP32_MAIL_STR_134) == std::string::npos && lineBuf.find(ESP32_MAIL_STR_145) == std::string::npos) + completeResp = true; + + //Some servers e.g. STRATO E-Mail-Server does not reply any error when fetching none existing MIME header part at defined index. + if (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_MIME) + validResponse = false; + } + } + else + { + + if ((payloadLength > 0 && charCount >= payloadLength) || imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_MIME) + { + completeResp = true; + } + } + } + + if (imapCommandType == IMAP_COMMAND_TYPE::SEARCH && lfCount > 0) + { + completeResp = true; + validResponse = true; + } + + tmp = lineBuf; + std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); + + if (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_MIME && lfCount > 0) + { + + if (payloadLength > 0 && validResponse) + { + + if (imapData._messageDataInfo[mailIndex].size() < messageDataIndex + 1) + { + messageBodyData b; + imapData._messageDataInfo[mailIndex].push_back(b); + imapData._messageDataCount[mailIndex] = imapData._messageDataInfo[mailIndex].size(); + } + + p1 = tmp.find(ESP32_MAIL_STR_167); + if (p1 != std::string::npos) + { + + p2 = lineBuf.find(";", p1 + strlen(ESP32_MAIL_STR_167)); + if (p2 != std::string::npos) + { + + imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_167), p2 - p1 - strlen(ESP32_MAIL_STR_167)); + + p1 = tmp.find(ESP32_MAIL_STR_168, p2); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_136, p1 + strlen(ESP32_MAIL_STR_168)); + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._charset = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_168), p2 - p1 - strlen(ESP32_MAIL_STR_168)); + } + else if (tmp.find(ESP32_MAIL_STR_169, p2) != std::string::npos) + { + p1 = tmp.find(ESP32_MAIL_STR_169, p2); + imapData._messageDataInfo[mailIndex][messageDataIndex]._charset = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_169)); + } + + p1 = tmp.find(ESP32_MAIL_STR_170, p2); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_136, p1 + strlen(ESP32_MAIL_STR_170)); + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._name = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_170), p2 - p1 - strlen(ESP32_MAIL_STR_170)); + } + else if (tmp.find(ESP32_MAIL_STR_171, p2) != std::string::npos) + { + p1 = tmp.find(ESP32_MAIL_STR_171, p2); + imapData._messageDataInfo[mailIndex][messageDataIndex]._name = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_171)); + } + } + } + + p1 = tmp.find(ESP32_MAIL_STR_172); + if (p1 != std::string::npos) + { + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + strlen(ESP32_MAIL_STR_172)); + + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._transfer_encoding = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_172), p2 - p1 - strlen(ESP32_MAIL_STR_172)); + else + imapData._messageDataInfo[mailIndex][messageDataIndex]._transfer_encoding = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_172)); + } + + p1 = tmp.find(ESP32_MAIL_STR_174); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + strlen(ESP32_MAIL_STR_174)); + + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._descr = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_174), p2 - p1 - strlen(ESP32_MAIL_STR_174)); + else + imapData._messageDataInfo[mailIndex][messageDataIndex]._descr = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_174)); + } + + p1 = tmp.find(ESP32_MAIL_STR_175); + if (p1 != std::string::npos) + { + + p2 = lineBuf.find(";", p1 + strlen(ESP32_MAIL_STR_175)); + + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._disposition = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_175), p2 - p1 - strlen(ESP32_MAIL_STR_175)); + else + imapData._messageDataInfo[mailIndex][messageDataIndex]._disposition = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_175)); + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._disposition == ESP32_MAIL_STR_153) + imapData._attachmentCount[mailIndex]++; + } + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._disposition != "") + { + + p1 = tmp.find(ESP32_MAIL_STR_176); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_136, p1 + strlen(ESP32_MAIL_STR_176)); + + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._filename = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_176), p2 - p1 - strlen(ESP32_MAIL_STR_176)); + } + else if (tmp.find(ESP32_MAIL_STR_177) != std::string::npos) + { + + p1 = tmp.find(ESP32_MAIL_STR_177); + imapData._messageDataInfo[mailIndex][messageDataIndex]._filename = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_177)); + } + + p1 = tmp.find(ESP32_MAIL_STR_178); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(";", p1 + strlen(ESP32_MAIL_STR_178) + 1); + if (p2 != std::string::npos) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._size = atoi(lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_178), p2 - p1 - strlen(ESP32_MAIL_STR_178)).c_str()); + imapData._totalAttachFileSize[mailIndex] += imapData._messageDataInfo[mailIndex][messageDataIndex]._size; + } + else + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._size = atoi(lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_178)).c_str()); + imapData._totalAttachFileSize[mailIndex] += imapData._messageDataInfo[mailIndex][messageDataIndex]._size; + } + } + + p1 = tmp.find(ESP32_MAIL_STR_179); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_136, p1 + strlen(ESP32_MAIL_STR_179)); + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._creation_date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_179), p2 - p1 - strlen(ESP32_MAIL_STR_179)); + } + else if (tmp.find(ESP32_MAIL_STR_180) != std::string::npos) + { + p1 = tmp.find(ESP32_MAIL_STR_180); + imapData._messageDataInfo[mailIndex][messageDataIndex]._creation_date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_180)); + } + + p1 = tmp.find(ESP32_MAIL_STR_181); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_136, p1 + strlen(ESP32_MAIL_STR_181)); + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._modification_date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_181), p2 - p1 - strlen(ESP32_MAIL_STR_181)); + } + else if (tmp.find(ESP32_MAIL_STR_182) != std::string::npos) + { + p1 = tmp.find(ESP32_MAIL_STR_182); + imapData._messageDataInfo[mailIndex][messageDataIndex]._modification_date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_182)); + } + } + + imapData._messageDataInfo[mailIndex][messageDataIndex]._part = part; + } + } + + if (imapCommandType == IMAP_COMMAND_TYPE::SEARCH && lfCount == 0) + { + + if (msgNumBuf.length() > 0 && msgNumBuf != ESP32_MAIL_STR_183 && msgNumBuf != ESP32_MAIL_STR_141 && imapData._msgNum.size() <= max) + { + imapData._msgNum.push_back(atoi(msgNumBuf.c_str())); + imapData._searchCount++; + + if (imapData._msgNum.size() > imapData._emailNumMax && imapData._recentSort) + imapData._msgNum.erase(imapData._msgNum.begin()); + } + + if (imapData._recentSort) + std::sort(imapData._msgNum.begin(), imapData._msgNum.end(), compFunc); + } + + if (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_HEADER) + { + + uint8_t _headerType = 0; + + p1 = tmp.find(ESP32_MAIL_STR_184); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::FROM; + _headerType = IMAP_HEADER_TYPE::FROM; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + strlen(ESP32_MAIL_STR_184)); + if (p2 != std::string::npos) + from = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_184), p2 - p1 - strlen(ESP32_MAIL_STR_184)); + else + from = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_184)); + + if (from[0] == '=' && from[1] == '?') + { + p1 = from.find("?", 2); + + if (p1 != std::string::npos) + from_charset = from.substr(2, p1 - 2); + } + + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, from.c_str(), bufSize); + from = dest; + } + + p1 = tmp.find(ESP32_MAIL_STR_185); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::TO; + _headerType = IMAP_HEADER_TYPE::TO; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + to = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_185), p2 - p1 - strlen(ESP32_MAIL_STR_185)); + else + to = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_185)); + + if (to[0] == '=' && to[1] == '?') + { + p1 = to.find("?", 2); + + if (p1 != std::string::npos) + to_charset = to.substr(2, p1 - 2); + } + + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, to.c_str(), bufSize); + to = dest; + } + + p1 = tmp.find(ESP32_MAIL_STR_186); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::CC; + _headerType = IMAP_HEADER_TYPE::CC; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + cc = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_186), p2 - p1 - strlen(ESP32_MAIL_STR_186)); + else + cc = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_186)); + + if (cc[0] == '=' && cc[1] == '?') + { + p1 = cc.find("?", 2); + + if (p1 != std::string::npos) + cc_charset = cc.substr(2, p1 - 2); + } + + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, cc.c_str(), bufSize); + cc = dest; + } + + p1 = tmp.find(ESP32_MAIL_STR_187); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::SUBJECT; + _headerType = IMAP_HEADER_TYPE::SUBJECT; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + + memset(dest, 0, bufSize); + if (p2 != std::string::npos) + subject = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_187), p2 - p1 - strlen(ESP32_MAIL_STR_187)); + else + subject = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_187)); + + if (subject[0] == '=' && subject[1] == '?') + { + p1 = subject.find("?", 2); + if (p1 != std::string::npos) + subject_charset = subject.substr(2, p1 - 2); + } + + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, subject.c_str(), bufSize); + subject = dest; + } + p1 = tmp.find(ESP32_MAIL_STR_188); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::DATE; + _headerType = IMAP_HEADER_TYPE::DATE; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_188), p2 - p1 - strlen(ESP32_MAIL_STR_188)); + else + date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_188)); + } + + p1 = tmp.find(ESP32_MAIL_STR_189); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::MSG_ID; + _headerType = IMAP_HEADER_TYPE::MSG_ID; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + msgID = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_189), p2 - p1 - strlen(ESP32_MAIL_STR_189)); + else + msgID = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_189)); + } + + p1 = tmp.find(ESP32_MAIL_STR_190); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::ACCEPT_LANG; + _headerType = IMAP_HEADER_TYPE::ACCEPT_LANG; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + acceptLanguage = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_190), p2 - p1 - strlen(ESP32_MAIL_STR_190)); + else + acceptLanguage = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_190)); + } + + p1 = tmp.find(ESP32_MAIL_STR_191); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::CONT_LANG; + _headerType = IMAP_HEADER_TYPE::CONT_LANG; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + contentLanguage = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_191), p2 - p1 - strlen(ESP32_MAIL_STR_191)); + else + contentLanguage = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_191)); + } + + if (_headerType == 0 && charCount < payloadLength && payloadLength > 0) + { + if (headerType == IMAP_HEADER_TYPE::FROM) + { + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, lineBuf.c_str(), bufSize); + from += dest; + } + else if (headerType == IMAP_HEADER_TYPE::TO) + { + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, lineBuf.c_str(), bufSize); + to += dest; + } + else if (headerType == IMAP_HEADER_TYPE::CC) + { + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, lineBuf.c_str(), bufSize); + cc += dest; + } + else if (headerType == IMAP_HEADER_TYPE::SUBJECT) + { + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, lineBuf.c_str(), bufSize); + subject += dest; + } + } + } + + if (imapCommandType == IMAP_COMMAND_TYPE::LIST) + { + p1 = lineBuf.find(ESP32_MAIL_STR_195); + p2 = lineBuf.find(ESP32_MAIL_STR_196); + + if (p1 != std::string::npos && p2 == std::string::npos) + { + p2 = lineBuf.find_last_of(ESP32_MAIL_STR_136); + if (p2 != std::string::npos) + { + p1 = lineBuf.find_last_of(ESP32_MAIL_STR_136, p2 - 1); + if (p1 != std::string::npos) + imapData._folders.push_back(lineBuf.substr(p1 + 1, p2 - p1 - 1)); + } + } + } + + if (imapCommandType == IMAP_COMMAND_TYPE::SELECT || imapCommandType == IMAP_COMMAND_TYPE::EXAMINE) + { + + p1 = lineBuf.find(ESP32_MAIL_STR_197); + if (p1 != std::string::npos) + { + p1 = lineBuf.find(ESP32_MAIL_STR_198); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_192); + if (p2 != std::string::npos) + { + string _tmp; + + _tmp = lineBuf.substr(p1 + 1, p2 - p1 - 1).c_str(); + msgNumBuf.clear(); + + for (size_t i = 0; i < _tmp.length(); i++) + { + if (_tmp[i] != '\\' && _tmp[i] != ' ' && _tmp[i] != '\r' && _tmp[i] != '\n') + msgNumBuf.append(1, _tmp[i]); + + if (_tmp[i] == ' ') + { + imapData._flag.push_back(msgNumBuf); + msgNumBuf.clear(); + } + } + if (msgNumBuf.length() > 0) + { + imapData._flag.push_back(msgNumBuf); + } + + std::string().swap(_tmp); + } + } + } + + p2 = lineBuf.find(ESP32_MAIL_STR_199); + if (p2 != std::string::npos) + imapData._totalMessage = atoi(lineBuf.substr(2, p2 - 2).c_str()); + + p1 = lineBuf.find(ESP32_MAIL_STR_200); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_156, p1 + 10); + if (p2 != std::string::npos) + imapData._nextUID = lineBuf.substr(p1 + 10, p2 - p1 - 10); + } + } + + if (validResponse && imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_TEXT && lfCount > 0 && (charCount < maxChar || imapData._saveHTMLMsg || imapData._saveTextMsg)) + { + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._transfer_encoding == ESP32_MAIL_STR_160) + { + + unsigned char *decoded = base64_decode_char((const unsigned char *)lineBuf.c_str(), lineBuf.length(), &outputLength); + + if (decoded) + { + if (charCount < maxChar) + imapData._messageDataInfo[mailIndex][messageDataIndex]._text.append((char *)decoded, outputLength); + + if (imapData._saveHTMLMsg || imapData._saveTextMsg) + { + + if (!imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite) + { + + imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite = true; + + if (_sdOk) + { + + downloadReq = true; + + filepath.clear(); + filepath += imapData._savePath; + filepath += ESP32_MAIL_STR_202; + + char *midx = new char[50]; + memset(midx, 0, 50); + itoa(imapData._msgNum[mailIndex], midx, 10); + + filepath += midx; + + delete[] midx; + + if (imapData._storageType == MailClientStorageType::SD) + if (!SD.exists(filepath.c_str())) + createDirs(filepath); + + if (!imapData._headerSaved) + hpath = filepath + ESP32_MAIL_STR_203; + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_155) + { + if (imapData._saveDecodedText) + filepath += ESP32_MAIL_STR_161; + else + filepath += ESP32_MAIL_STR_162; + } + else if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_154) + { + if (imapData._saveDecodedHTML) + filepath += ESP32_MAIL_STR_163; + else + filepath += ESP32_MAIL_STR_164; + } + + if (imapData._storageType == MailClientStorageType::SD) + file = SD.open(filepath.c_str(), FILE_WRITE); + else if (imapData._storageType == MailClientStorageType::SPIFFS) + file = SPIFFS.open(filepath.c_str(), FILE_WRITE); + } + else + { + if (imapData._messageDataCount[mailIndex] == messageDataIndex + 1) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._error = true; + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError.clear(); + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError = ESP32_MAIL_STR_89; + } + } + } + + if (_sdOk) + { + if ((imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_155 && imapData._saveDecodedText) || + (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_154 && imapData._saveDecodedHTML)) + file.write((const uint8_t *)decoded, outputLength); + else + file.write((const uint8_t *)lineBuf.c_str(), lineBuf.length()); + } + } + + delete[] decoded; + } + } + } + + if (validResponse && imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_ATTACHMENT && lfCount > 0) + { + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._transfer_encoding == ESP32_MAIL_STR_160) + { + + if (!imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite) + { + + imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite = true; + + if (_sdOk) + { + + downloadReq = true; + + filepath.clear(); + filepath += imapData._savePath; + filepath += ESP32_MAIL_STR_202; + + char *midx = new char[50]; + memset(midx, 0, 50); + itoa(imapData._msgNum[mailIndex], midx, 10); + + filepath += midx; + + delete[] midx; + + if (imapData._storageType == MailClientStorageType::SD) + if (!SD.exists(filepath.c_str())) + createDirs(filepath); + + filepath += ESP32_MAIL_STR_202; + + filepath += imapData._messageDataInfo[mailIndex][messageDataIndex]._filename; + + if (imapData._storageType == MailClientStorageType::SD) + file = SD.open(filepath.c_str(), FILE_WRITE); + else if (imapData._storageType == MailClientStorageType::SPIFFS) + file = SPIFFS.open(filepath.c_str(), FILE_WRITE); + } + else + { + if (imapData._messageDataCount[mailIndex] == messageDataIndex + 1) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._error = true; + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError.clear(); + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError = ESP32_MAIL_STR_89; + } + } + } + + if (_sdOk) + { + + unsigned char *decoded = base64_decode_char((const unsigned char *)lineBuf.c_str(), lineBuf.length(), &outputLength); + + downloadedByte += outputLength; + + if (downloadedByte > imapData._messageDataInfo[mailIndex][messageDataIndex]._size) + continue; + + if (decoded) + { + file.write((const uint8_t *)decoded, outputLength); + + if (imapData._storageType == MailClientStorageType::SPIFFS) + delayMicroseconds(1); + else + yield(); + + if (imapData._downloadReport) + { + imapData._downloadedByte[mailIndex] += outputLength; + currentDownloadByte += outputLength; + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._size == 0) + { + if (payloadLength > 36) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._size = base64DecodeSize(lineBuf, payloadLength - (payloadLength / 36)); + imapData._totalAttachFileSize[mailIndex] += imapData._messageDataInfo[mailIndex][messageDataIndex]._size; + } + } + + int p = 0; + + if (imapData._totalAttachFileSize[mailIndex] > 0) + p = 100 * imapData._downloadedByte[mailIndex] / imapData._totalAttachFileSize[mailIndex]; + + if ((p % 5 == 0) && (p <= 100)) + { + + if (imapData._readCallback && reportState != -1) + { + memset(buf, 0, bufSize); + itoa(p, buf, 10); + + std::string dl = ESP32_MAIL_STR_90 + imapData._messageDataInfo[mailIndex][messageDataIndex]._filename + ESP32_MAIL_STR_91 + buf + ESP32_MAIL_STR_92; + + if (imapData._readCallback) + { + imapData._cbData._info = dl; + imapData._cbData._status = dl; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + std::string().swap(dl); + } + reportState = -1; + } + else + reportState = 0; + } + + delete[] decoded; + } + + if (millis() - dataTime > imapData._net->tcpTimeout + 1000 * 60 * 5) + break; + } + } + } + + if (lfCount == 0) + { + p1 = lineBuf.find_last_of(ESP32_MAIL_STR_193); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_194, p1 + 1); + if (p2 != std::string::npos) + payloadLength = atoi(lineBuf.substr(p1 + 1, p2 - p1 - 1).c_str()); + } + } + + lineBuf.clear(); + lfCount++; + std::string().swap(tmp); + } + + readCount++; + } + + if (imapData._error.size() > 0 && mailIndex > -1) + { + if (validResponse && !imapData._error[mailIndex]) + { + imapData._errorMsg[mailIndex].clear(); + imapData._errorMsg[mailIndex] = ""; + } + } + + if (millis() - dataTime > imapData._net->tcpTimeout) + { + + if (downloadReq) + { + if (imapData._messageDataCount[mailIndex] == messageDataIndex + 1) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._error = true; + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError.clear(); + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError = ESP32_MAIL_STR_93; + } + } + else + { + + if (imapData._error.size() > 0 && mailIndex > -1) + { + imapData._error[mailIndex] = true; + imapData._errorMsg[mailIndex].clear(); + imapData._errorMsg[mailIndex] = ESP32_MAIL_STR_95; + } + } + } + } + + if (validResponse && (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_ATTACHMENT || imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_TEXT) && messageDataIndex != -1) + { + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite) + file.close(); + } + + if (validResponse && imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_ATTACHMENT && imapData._messageDataInfo[mailIndex][messageDataIndex]._size != currentDownloadByte) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._size = currentDownloadByte; + } + + if (hpath != "") + { + + if (imapData._storageType == MailClientStorageType::SD) + file = SD.open(hpath.c_str(), FILE_WRITE); + else if (imapData._storageType == MailClientStorageType::SPIFFS) + file = SPIFFS.open(hpath.c_str(), FILE_WRITE); + + file.print(ESP32_MAIL_STR_99); + file.println(imapData._date[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_100); + if (imapData._uidSearch) + file.println(imapData._msgNum[mailIndex]); + else + file.println(); + + file.print(ESP32_MAIL_STR_101); + file.println(imapData._msgNum[mailIndex]); + + file.print(ESP32_MAIL_STR_102); + file.println(imapData._acceptLanguage[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_103); + file.println(imapData._contentLanguage[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_104); + file.println(imapData._from[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_105); + file.println(imapData._from_charset[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_106); + file.println(imapData._to[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_107); + file.println(imapData._to_charset[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_108); + file.println(imapData._cc[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_109); + file.println(imapData._cc_charset[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_110); + file.println(imapData._subject[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_111); + file.println(imapData._subject_charset[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_112); + file.println(imapData._messageDataInfo[mailIndex][messageDataIndex]._charset.c_str()); + + if (imapData._attachmentCount[mailIndex] > 0) + { + + file.print(ESP32_MAIL_STR_113); + file.println(imapData._attachmentCount[mailIndex]); + + for (int j = 0; j < imapData._attachmentCount[mailIndex]; j++) + { + file.print(ESP32_MAIL_STR_114); + file.println(j + 1); + + file.print(ESP32_MAIL_STR_115); + file.println(imapData.getAttachmentFileName(mailIndex, j)); + + file.print(ESP32_MAIL_STR_116); + file.println(imapData.getAttachmentName(mailIndex, j)); + + file.print(ESP32_MAIL_STR_117); + file.println(imapData.getAttachmentFileSize(mailIndex, j)); + + file.print(ESP32_MAIL_STR_118); + file.println(imapData.getAttachmentType(mailIndex, j)); + + file.print(ESP32_MAIL_STR_119); + file.println(imapData.getAttachmentCreationDate(mailIndex, j)); + } + } + + file.close(); + imapData._headerSaved = true; + } + + if (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_HEADER) + { + if (from != "") + { + imapData._msgID[mailIndex] = msgID; + imapData._from[mailIndex] = from; + imapData._to[mailIndex] = to; + imapData._cc[mailIndex] = cc; + imapData._subject[mailIndex] = subject; + imapData._date[mailIndex] = date; + imapData._from_charset[mailIndex] = from_charset; + imapData._to_charset[mailIndex] = to_charset; + imapData._cc_charset[mailIndex] = cc_charset; + imapData._subject_charset[mailIndex] = subject_charset; + imapData._contentLanguage[mailIndex] = contentLanguage; + imapData._acceptLanguage[mailIndex] = acceptLanguage; + } + } + + delete[] buf; + delete[] dest; + + std::string().swap(lineBuf); + std::string().swap(msgNumBuf); + std::string().swap(filepath); + std::string().swap(hpath); + std::string().swap(tmp); + + std::string().swap(msgID); + std::string().swap(from); + std::string().swap(to); + std::string().swap(subject); + std::string().swap(date); + std::string().swap(cc); + std::string().swap(from_charset); + std::string().swap(to_charset); + std::string().swap(cc_charset); + std::string().swap(subject_charset); + std::string().swap(contentLanguage); + std::string().swap(acceptLanguage); + + return validResponse; +} + +void ESP32_MailClient::clientReadAll(WiFiClient *client) +{ + if (client) + { + if (client->available() > 0) + client->read(); + } +} + +double ESP32_MailClient::base64DecodeSize(std::string lastBase64String, int length) +{ + double result = 0; + int padding = 0; + if (lastBase64String != "") + { + + if (lastBase64String[lastBase64String.length() - 1] == '=' && lastBase64String[lastBase64String.length() - 2] == '=') + padding = 2; + else if (lastBase64String[lastBase64String.length() - 1] == '=') + padding = 1; + } + result = (ceil(length / 4) * 3) - padding; + return result; +} + +unsigned char *ESP32_MailClient::base64_decode_char(const unsigned char *src, size_t len, size_t *out_len) +{ + + unsigned char *out, *pos, block[4], tmp; + size_t i, count, olen; + int pad = 0; + size_t extra_pad; + + unsigned char *dtable = new unsigned char[256]; + + memset(dtable, 0x80, 256); + + for (i = 0; i < sizeof(base64_table) - 1; i++) + dtable[base64_table[i]] = (unsigned char)i; + dtable['='] = 0; + + count = 0; + for (i = 0; i < len; i++) + { + if (dtable[src[i]] != 0x80) + count++; + } + + if (count == 0) + goto exit; + extra_pad = (4 - count % 4) % 4; + + olen = (count + extra_pad) / 4 * 3; + pos = out = (unsigned char *)malloc(olen); + if (out == NULL) + goto exit; + + count = 0; + for (i = 0; i < len + extra_pad; i++) + { + unsigned char val; + + if (i >= len) + val = '='; + else + val = src[i]; + tmp = dtable[val]; + if (tmp == 0x80) + continue; + + if (val == '=') + pad++; + block[count] = tmp; + count++; + if (count == 4) + { + *pos++ = (block[0] << 2) | (block[1] >> 4); + *pos++ = (block[1] << 4) | (block[2] >> 2); + *pos++ = (block[2] << 6) | block[3]; + count = 0; + if (pad) + { + if (pad == 1) + pos--; + else if (pad == 2) + pos -= 2; + else + { + free(out); + goto exit; + } + break; + } + } + } + + *out_len = pos - out; + delete[] dtable; + return out; + +exit: + delete[] dtable; + return NULL; +} + +std::string ESP32_MailClient::base64_encode_string(const unsigned char *src, size_t len) +{ + unsigned char *out, *pos; + const unsigned char *end, *in; + + size_t olen; + + olen = 4 * ((len + 2) / 3); + + if (olen < len) + return std::string(); + + std::string outStr = ""; + outStr.resize(olen); + out = (unsigned char *)&outStr[0]; + + end = src + len; + in = src; + pos = out; + + while (end - in >= 3) + { + *pos++ = base64_table[in[0] >> 2]; + *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + *pos++ = base64_table[in[2] & 0x3f]; + in += 3; + } + + if (end - in) + { + *pos++ = base64_table[in[0] >> 2]; + if (end - in == 1) + { + *pos++ = base64_table[(in[0] & 0x03) << 4]; + *pos++ = '='; + } + else + { + *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = base64_table[(in[1] & 0x0f) << 2]; + } + *pos++ = '='; + } + + return outStr; +} + +void ESP32_MailClient::send_base64_encode_mime_data(WiFiClient *client, const unsigned char *src, size_t len) +{ + + const unsigned char *end, *in; + + size_t olen; + + olen = 4 * ((len + 2) / 3); + + if (olen < len) + return; + + end = src + len; + in = src; + + size_t chunkSize = 936; + size_t byteAdd = 0; + size_t byteSent = 0; + + int dByte = 0; + unsigned char *buf = new unsigned char[chunkSize]; + memset(buf, 0, chunkSize); + + while (end - in >= 3) + { + buf[byteAdd++] = base64_table[in[0] >> 2]; + buf[byteAdd++] = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + buf[byteAdd++] = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + buf[byteAdd++] = base64_table[in[2] & 0x3f]; + dByte += 4; + if (dByte == 76) + { + if(byteAdd + 1 < chunkSize) + { + buf[byteAdd++] = 0x0d; + buf[byteAdd++] = 0x0a; + } + dByte = 0; + } + if (byteAdd >= chunkSize - 4) + { + byteSent += byteAdd; + client->write(buf, byteAdd); + memset(buf, 0, chunkSize); + byteAdd = 0; + } + in += 3; + } + + if (byteAdd > 0) + client->write(buf, byteAdd); + + if (end - in) + { + memset(buf, 0, chunkSize); + byteAdd = 0; + + buf[byteAdd++] = base64_table[in[0] >> 2]; + if (end - in == 1) + { + buf[byteAdd++] = base64_table[(in[0] & 0x03) << 4]; + buf[byteAdd++] = '='; + } + else + { + buf[byteAdd++] = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + buf[byteAdd++] = base64_table[(in[1] & 0x0f) << 2]; + } + buf[byteAdd++] = '='; + + client->write(buf, byteAdd); + memset(buf, 0, chunkSize); + } + delete[] buf; +} + +void ESP32_MailClient::send_base64_encode_mime_file(WiFiClient *client, File file) +{ + + if (!file) + return; + + size_t chunkSize = 936; + size_t byteAdd = 0; + size_t byteSent = 0; + + unsigned char *buf = new unsigned char[chunkSize]; + memset(buf, 0, chunkSize); + + size_t len = file.size(); + size_t fbufIndex = 0; + unsigned char *fbuf = new unsigned char[3]; + + int dByte = 0; + + while (file.available()) + { + memset(fbuf, 0, 3); + if (len - fbufIndex >= 3) + { + file.read(fbuf, 3); + buf[byteAdd++] = base64_table[fbuf[0] >> 2]; + buf[byteAdd++] = base64_table[((fbuf[0] & 0x03) << 4) | (fbuf[1] >> 4)]; + buf[byteAdd++] = base64_table[((fbuf[1] & 0x0f) << 2) | (fbuf[2] >> 6)]; + buf[byteAdd++] = base64_table[fbuf[2] & 0x3f]; + dByte += 4; + if (dByte == 76) + { + if(byteAdd + 1 < chunkSize) + { + buf[byteAdd++] = 0x0d; + buf[byteAdd++] = 0x0a; + } + dByte = 0; + } + if (byteAdd >= chunkSize - 4) + { + byteSent += byteAdd; + client->write(buf, byteAdd); + memset(buf, 0, chunkSize); + byteAdd = 0; + } + fbufIndex += 3; + } + else + { + if (len - fbufIndex == 1) + { + fbuf[0] = file.read(); + } + else if (len - fbufIndex == 2) + { + fbuf[0] = file.read(); + fbuf[1] = file.read(); + } + break; + } + } + + file.close(); + if (byteAdd > 0) + client->write(buf, byteAdd); + + if (len - fbufIndex > 0) + { + memset(buf, 0, chunkSize); + byteAdd = 0; + buf[byteAdd++] = base64_table[fbuf[0] >> 2]; + if (len - fbufIndex == 1) + { + buf[byteAdd++] = base64_table[(fbuf[0] & 0x03) << 4]; + buf[byteAdd++] = '='; + } + else + { + buf[byteAdd++] = base64_table[((fbuf[0] & 0x03) << 4) | (fbuf[1] >> 4)]; + buf[byteAdd++] = base64_table[(fbuf[1] & 0x0f) << 2]; + } + buf[byteAdd++] = '='; + client->write(buf, byteAdd); + } + delete[] buf; + delete[] fbuf; +} + +IMAPData::IMAPData() {} +IMAPData::~IMAPData() +{ + empty(); + _net.reset(); + _net.release(); +} + +void IMAPData::setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA) +{ + + _host.clear(); + _port = port; + _loginEmail.clear(); + _loginPassword.clear(); + + _host = host.c_str(); + _loginEmail = loginEmail.c_str(); + _loginPassword = loginPassword.c_str(); + + _rootCA.clear(); + if (strlen(rootCA) > 0) + _rootCA.push_back((char *)rootCA); +} + +void IMAPData::setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword) +{ + _host.clear(); + _port = port; + _loginEmail.clear(); + _loginPassword.clear(); + + _host = host.c_str(); + _loginEmail = loginEmail.c_str(); + _loginPassword = loginPassword.c_str(); +} + +void IMAPData::setSTARTTLS(bool starttls) +{ + _starttls = starttls; +} + +void IMAPData::setDebug(bool debug) +{ + _debug = debug; +} + +void IMAPData::setFolder(const String &folderName) +{ + _currentFolder.clear(); + _currentFolder = folderName.c_str(); +} +void IMAPData::setMessageBufferSize(size_t size) +{ + _message_buffer_size = size; +} + +void IMAPData::setAttachmentSizeLimit(size_t size) +{ + _attacement_max_size = size; +} + +void IMAPData::setSearchCriteria(const String &criteria) +{ + _searchCriteria.clear(); + _searchCriteria = criteria.c_str(); +} + +void IMAPData::setSearchUnseenMessage(bool unseenSearch) +{ + _unseen = unseenSearch; +} + +void IMAPData::setSaveFilePath(const String &path) +{ + _savePath.clear(); + if (path.c_str()[0] != '/') + { + _savePath = "/"; + _savePath += path.c_str(); + } + else + _savePath = path.c_str(); +} + +void IMAPData::setFetchUID(const String &fetchUID) +{ + _fetchUID.clear(); + string tmp = fetchUID.c_str(); + std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::toupper); + if (tmp.find(ESP32_MAIL_STR_140) != std::string::npos || tmp.find(ESP32_MAIL_STR_212) != std::string::npos || + tmp.find(ESP32_MAIL_STR_213) != std::string::npos || tmp.find(ESP32_MAIL_STR_214) != std::string::npos || tmp.find(ESP32_MAIL_STR_215) != std::string::npos || + tmp.find(ESP32_MAIL_STR_216) != std::string::npos || tmp.find(ESP32_MAIL_STR_217) != std::string::npos || tmp.find(ESP32_MAIL_STR_218) != std::string::npos || + tmp.find(ESP32_MAIL_STR_219) != std::string::npos || tmp.find(ESP32_MAIL_STR_220) != std::string::npos) + _fetchUID = ESP32_MAIL_STR_183; + else + _fetchUID = fetchUID.c_str(); + + std::string().swap(tmp); +} + +void IMAPData::setFileStorageType(uint8_t storageType) +{ + _storageType = storageType; +} + +void IMAPData::setDownloadAttachment(bool download) +{ + _downloadAttachment = download; +} +void IMAPData::setRecentSort(bool recentSort) +{ + _recentSort = recentSort; +} + +void IMAPData::setHTMLMessage(bool htmlFormat) +{ + _htmlFormat = htmlFormat; +} +void IMAPData::setTextMessage(bool textFormat) +{ + _textFormat = textFormat; +} + +void IMAPData::setSearchLimit(uint16_t limit) +{ + if (limit <= MAX_EMAIL_SEARCH_LIMIT) + _emailNumMax = limit; +} + +bool IMAPData::isHeaderOnly() +{ + return _headerOnly; +} + +void IMAPData::saveHTMLMessage(bool download, bool decoded) +{ + _saveDecodedHTML = decoded; + _saveHTMLMsg = download; +} +void IMAPData::saveTextMessage(bool download, bool decoded) +{ + _saveDecodedText = decoded; + _saveTextMsg = download; +} + +void IMAPData::setReadCallback(readStatusCallback readCallback) +{ + _readCallback = std::move(readCallback); +} + +void IMAPData::setDownloadReport(bool report) +{ + _downloadReport = report; +} + +uint16_t IMAPData::getFolderCount() +{ + return _folders.size(); +} +String IMAPData::getFolder(uint16_t folderIndex) +{ + if (folderIndex < _folders.size()) + return _folders[folderIndex].c_str(); + return std::string().c_str(); +} + +uint16_t IMAPData::getFlagCount() +{ + return _flag.size(); +} +String IMAPData::getFlag(uint16_t flagIndex) +{ + if (flagIndex < _flag.size()) + return _flag[flagIndex].c_str(); + return std::string().c_str(); +} + +size_t IMAPData::totalMessages() +{ + return _totalMessage; +} + +size_t IMAPData::searchCount() +{ + return _searchCount; +} + +size_t IMAPData::availableMessages() +{ + return _msgNum.size(); +} + +size_t IMAPData::getAttachmentCount(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _attachmentCount[messageIndex]; + return 0; +} + +String IMAPData::getAttachmentFileName(size_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._filename.c_str(); + id++; + } + } + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getAttachmentName(size_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._name.c_str(); + id++; + } + } + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +int IMAPData::getAttachmentFileSize(size_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._size; + id++; + } + } + } + else + return 0; + } + else + return 0; + + return 0; +} + +String IMAPData::getAttachmentCreationDate(size_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._creation_date.c_str(); + id++; + } + } + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getAttachmentType(size_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._contentType.c_str(); + id++; + } + } + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getFrom(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _from[messageIndex].c_str(); + return std::string().c_str(); +} + +String IMAPData::getFromCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _from_charset[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getTo(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _to[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getToCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _to_charset[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getCC(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _cc[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getCCCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _cc_charset[messageIndex].c_str(); + return std::string().c_str(); +} + +String IMAPData::getSubject(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _subject[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getSubjectCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _subject_charset[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getHTMLMessage(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return getMessage(messageIndex, true); + return std::string().c_str(); +} + +String IMAPData::getTextMessage(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return getMessage(messageIndex, false); + return std::string().c_str(); +} + +String IMAPData::getMessage(uint16_t messageIndex, bool htmlFormat) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._contentType == ESP32_MAIL_STR_155 && !htmlFormat) + return _messageDataInfo[messageIndex][i]._text.c_str(); + else if (_messageDataInfo[messageIndex][i]._contentType == ESP32_MAIL_STR_154 && htmlFormat) + return _messageDataInfo[messageIndex][i]._text.c_str(); + } + return std::string().c_str(); + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getHTMLMessgaeCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._contentType == ESP32_MAIL_STR_154) + return _messageDataInfo[messageIndex][i]._charset.c_str(); + } + return std::string().c_str(); + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getTextMessgaeCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._contentType == ESP32_MAIL_STR_155) + return _messageDataInfo[messageIndex][i]._charset.c_str(); + } + return std::string().c_str(); + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getDate(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _date[messageIndex].c_str(); + return std::string().c_str(); +} + +String IMAPData::getUID(uint16_t messageIndex) +{ + char *buf = new char[50]; + memset(buf, 0, 50); + if (_uidSearch) + { + if (messageIndex < _msgNum.size()) + itoa(_msgNum[messageIndex], buf, 10); + } + + String v = buf; + delete[] buf; + return v; +} + +String IMAPData::getNumber(uint16_t messageIndex) +{ + char *buf = new char[50]; + memset(buf, 0, 50); + + if (messageIndex < _msgNum.size()) + { + if (!_uidSearch) + itoa(_msgNum[messageIndex], buf, 10); + else + itoa(_msgNum[messageIndex] + 1, buf, 10); + } + + String v = buf; + delete[] buf; + return v; +} + +String IMAPData::getMessageID(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _msgID[messageIndex].c_str(); + return std::string().c_str(); +} + +String IMAPData::getAcceptLanguage(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _acceptLanguage[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getContentLanguage(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _contentLanguage[messageIndex].c_str(); + return std::string().c_str(); +} + +bool IMAPData::isFetchMessageFailed(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _error[messageIndex]; + return false; +} +String IMAPData::getFetchMessageFailedReason(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _errorMsg[messageIndex].c_str(); + return std::string().c_str(); +} + +bool IMAPData::isDownloadAttachmentFailed(uint16_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._error; + id++; + } + } + } + else + return false; + } + else + return false; + + return false; +} + +String IMAPData::getDownloadAttachmentFailedReason(uint16_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._downloadError.c_str(); + id++; + } + } + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + return std::string().c_str(); +} + +bool IMAPData::isDownloadMessageFailed(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + bool res = false; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == "") + { + res |= _messageDataInfo[messageIndex][i]._error; + } + } + + return res; + } + else + return false; + } + else + return false; + + return false; +} +String IMAPData::getDownloadMessageFailedReason(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + string res = ""; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == "") + { + if (_messageDataInfo[messageIndex][i]._downloadError != "") + res = _messageDataInfo[messageIndex][i]._downloadError; + } + } + + return res.c_str(); + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +void IMAPData::empty() +{ + std::string().swap(_host); + std::string().swap(_loginEmail); + std::string().swap(_loginPassword); + std::string().swap(_currentFolder); + std::string().swap(_nextUID); + std::string().swap(_searchCriteria); + std::vector().swap(_date); + std::vector().swap(_subject); + std::vector().swap(_subject_charset); + std::vector().swap(_from); + std::vector().swap(_from_charset); + std::vector().swap(_to); + std::vector().swap(_to_charset); + std::vector().swap(_cc); + std::vector().swap(_cc_charset); + std::vector().swap(_msgNum); + std::vector().swap(_folders); + std::vector().swap(_flag); + std::vector().swap(_msgID); + std::vector().swap(_acceptLanguage); + std::vector().swap(_contentLanguage); + std::vector().swap(_attachmentCount); + std::vector().swap(_totalAttachFileSize); + std::vector().swap(_downloadedByte); + std::vector().swap(_error); + std::vector>().swap(_messageDataInfo); + std::vector().swap(_errorMsg); +} + +void IMAPData::clearMessageData() +{ + std::vector().swap(_date); + std::vector().swap(_subject); + std::vector().swap(_subject_charset); + std::vector().swap(_from); + std::vector().swap(_from_charset); + std::vector().swap(_to); + std::vector().swap(_to_charset); + std::vector().swap(_cc); + std::vector().swap(_cc_charset); + std::vector().swap(_msgNum); + std::vector().swap(_msgID); + std::vector().swap(_contentLanguage); + std::vector().swap(_acceptLanguage); + std::vector().swap(_folders); + std::vector().swap(_flag); + std::vector().swap(_attachmentCount); + std::vector>().swap(_messageDataInfo); + std::vector().swap(_totalAttachFileSize); + std::vector().swap(_downloadedByte); + std::vector().swap(_messageDataCount); + std::vector().swap(_errorMsg); + std::vector().swap(_error); + _searchCount = 0; +} + +messageBodyData::messageBodyData() +{ +} +messageBodyData::~messageBodyData() +{ + empty(); +} + +void messageBodyData::empty() +{ + std::string().swap(_text); + std::string().swap(_filename); + std::string().swap(_savePath); + std::string().swap(_name); + std::string().swap(_disposition); + std::string().swap(_contentType); + std::string().swap(_descr); + std::string().swap(_transfer_encoding); + std::string().swap(_creation_date); + std::string().swap(_modification_date); + std::string().swap(_charset); + std::string().swap(_part); + std::string().swap(_downloadError); +} + +attachmentData::attachmentData() {} +attachmentData::~attachmentData() +{ + + std::vector>().swap(_buf); + std::vector().swap(_filename); + std::vector().swap(_id); + std::vector().swap(_type); + std::vector().swap(_size); + std::vector().swap(_mime_type); +} + +void attachmentData::add(const String &fileName, const String &mimeType, uint8_t *data, size_t size) +{ + _filename.push_back(fileName.c_str()); + _mime_type.push_back(mimeType.c_str()); + + if (size > 0) + { + std::vector d = std::vector(); + d.push_back(data); + _buf.push_back(d); + _size.push_back(size); + _type.push_back(0); + } + else + { + _buf.push_back(std::vector()); + _size.push_back(0); + _type.push_back(1); + } + + _id.push_back(_index); + _index++; +} + +void attachmentData::remove(uint8_t index) +{ + _buf.erase(_buf.begin() + index); + _filename.erase(_filename.begin() + index); + _type.erase(_type.begin() + index); + _size.erase(_size.begin() + index); + _mime_type.erase(_mime_type.begin() + index); + _id.erase(_id.begin() + index); +} + +void attachmentData::free() +{ + std::vector>().swap(_buf); + std::vector().swap(_filename); + std::vector().swap(_id); + std::vector().swap(_type); + std::vector().swap(_size); + std::vector().swap(_mime_type); + _index = 0; +} + +String attachmentData::getFileName(uint8_t index) +{ + return _filename[index].c_str(); +} + +String attachmentData::getMimeType(uint8_t index) +{ + return _mime_type[index].c_str(); +} + +uint8_t *attachmentData::getData(uint8_t index) +{ + uint8_t *ptr = _buf[index].front(); + return ptr; +} + +uint16_t attachmentData::getSize(uint8_t index) +{ + return _size[index]; +} + +uint8_t attachmentData::getCount() +{ + return _index; +} + +uint8_t attachmentData::getType(uint8_t index) +{ + return _type[index]; +} + +SMTPData::SMTPData() {} + +SMTPData::~SMTPData() +{ + empty(); + _net.reset(); + _net.release(); +} + +void SMTPData::setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA) +{ + + _host.clear(); + _port = port; + _loginEmail.clear(); + _loginPassword.clear(); + + _host = host.c_str(); + _loginEmail = loginEmail.c_str(); + _loginPassword = loginPassword.c_str(); + + _rootCA.clear(); + if (strlen(rootCA) > 0) + _rootCA.push_back((char *)rootCA); +} + +void SMTPData::setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword) +{ + + _host.clear(); + _port = port; + _loginEmail.clear(); + _loginPassword.clear(); + + _host = host.c_str(); + _loginEmail = loginEmail.c_str(); + _loginPassword = loginPassword.c_str(); + + _rootCA.clear(); +} + +void SMTPData::setSTARTTLS(bool starttls) +{ + _starttls = starttls; +} + +void SMTPData::setDebug(bool debug) +{ + _debug = debug; +} + +void SMTPData::setSender(const String &fromName, const String &senderEmail) +{ + + _fromName.clear(); + _senderEmail.clear(); + + _fromName += fromName.c_str(); + _senderEmail += senderEmail.c_str(); +} + +String SMTPData::getFromName() +{ + return _fromName.c_str(); +} + +String SMTPData::getSenderEmail() +{ + return _senderEmail.c_str(); +} + +void SMTPData::setPriority(int priority) +{ + _priority = priority; +} +void SMTPData::setPriority(const String &priority) +{ + if (priority == ESP32_MAIL_STR_205 || priority == ESP32_MAIL_STR_206) + _priority = 1; + else if (priority == ESP32_MAIL_STR_207 || priority == ESP32_MAIL_STR_208) + _priority = 3; + else if (priority == ESP32_MAIL_STR_209 || priority == ESP32_MAIL_STR_210) + _priority = 5; +} + +uint8_t SMTPData::getPriority() +{ + return _priority; +} + +void SMTPData::addRecipient(const String &email) +{ + _recipient.insert(_recipient.end(), email.c_str()); +} + +void SMTPData::removeRecipient(const String &email) +{ + for (uint8_t i = 0; i < _recipient.size(); i++) + if (_recipient[i].c_str() == email.c_str()) + _recipient.erase(_recipient.begin() + i); +} + +void SMTPData::removeRecipient(uint8_t index) +{ + _recipient.erase(_recipient.begin() + index); +} + +void SMTPData::clearRecipient() +{ + std::vector().swap(_recipient); +} + +uint8_t SMTPData::recipientCount() +{ + return _recipient.size(); +} + +String SMTPData::getRecipient(uint8_t index) +{ + if (index >= _recipient.size()) + return std::string().c_str(); + return _recipient[index].c_str(); +} + +void SMTPData::setSubject(const String &subject) +{ + _subject = subject.c_str(); +} + +String SMTPData::getSubject() +{ + return _subject.c_str(); +} + +void SMTPData::setMessage(const String &message, bool htmlFormat) +{ + _message.clear(); + _message += message.c_str(); + _htmlFormat = htmlFormat; +} + +void SMTPData::clrMessage(bool htmlFormat) +{ + _message.clear(); + _htmlFormat = htmlFormat; +} + +void SMTPData::addMessage(const String &message) +{ + _message += message.c_str(); +} + +String SMTPData::getMessage() +{ + return _message.c_str(); +} + +bool SMTPData::htmlFormat() +{ + return _htmlFormat; +} +void SMTPData::addCC(const String &email) +{ + _cc.push_back(email.c_str()); +} + +void SMTPData::removeCC(const String &email) +{ + for (uint8_t i = 0; i < _cc.size(); i++) + if (_cc[i].c_str() == email.c_str()) + _cc.erase(_cc.begin() + i); +} + +void SMTPData::removeCC(uint8_t index) +{ + _cc.erase(_cc.begin() + index); +} +void SMTPData::clearCC() +{ + std::vector().swap(_cc); +} + +uint8_t SMTPData::ccCount() +{ + return _cc.size(); +} + +String SMTPData::getCC(uint8_t index) +{ + if (index >= _cc.size()) + return std::string().c_str(); + return _cc[index].c_str(); +} + +void SMTPData::addBCC(const String &email) +{ + _bcc.push_back(email.c_str()); +} + +void SMTPData::removeBCC(const String &email) +{ + for (uint8_t i = 0; i < _bcc.size(); i++) + if (_bcc[i].c_str() == email.c_str()) + _bcc.erase(_bcc.begin() + i); +} + +void SMTPData::removeBCC(uint8_t index) +{ + _bcc.erase(_bcc.begin() + index); +} + +void SMTPData::clearBCC() +{ + std::vector().swap(_bcc); +} + +uint8_t SMTPData::bccCount() +{ + return _bcc.size(); +} + +String SMTPData::getBCC(uint8_t index) +{ + if (index >= _bcc.size()) + return std::string().c_str(); + return _bcc[index].c_str(); +} + +void SMTPData::addAttachData(const String &fileName, const String &mimeType, uint8_t *data, size_t size) +{ + _attach.add(fileName, mimeType, data, size); +} + +void SMTPData::removeAttachData(const String &fileName) +{ + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getFileName(i) == fileName && _attach.getType(i) == 0) + { + _attach.remove(i); + } +} + +void SMTPData::removeAttachData(uint8_t index) +{ + uint8_t id = 0; + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 0) + { + if (id == index) + { + _attach.remove(i); + break; + } + id++; + } +} + +uint8_t SMTPData::attachDataCount() +{ + uint8_t count = 0; + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 0) + count++; + + return count; +} + +void SMTPData::addAttachFile(const String &filePath, const String &mimeType) +{ + _attach.add(filePath, mimeType, NULL, 0); +} + +void SMTPData::removeAttachFile(const String &filePath) +{ + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getFileName(i) == filePath && _attach.getType(i) == 1) + { + _attach.remove(i); + } +} + +void SMTPData::removeAttachFile(uint8_t index) +{ + uint8_t id = 0; + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 1) + { + if (id == index) + { + _attach.remove(i); + break; + } + id++; + } +} + +void SMTPData::setFileStorageType(uint8_t storageType) +{ + _storageType = storageType; +} + +void SMTPData::clearAttachData() +{ + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 0) + _attach.remove(i); +} + +void SMTPData::clearAttachFile() +{ + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 1) + _attach.remove(i); +} + +void SMTPData::clearAttachment() +{ + _attach.free(); +} + +uint8_t SMTPData::attachFileCount() +{ + uint8_t count = 0; + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 1) + count++; + + return count; +} + +void SMTPData::addCustomMessageHeader(const String &commmand) +{ + _customMessageHeader.insert(_customMessageHeader.end(), commmand.c_str()); +} + +void SMTPData::removeCustomMessageHeader(const String &commmand) +{ + for (uint8_t i = 0; i < _customMessageHeader.size(); i++) + if (_customMessageHeader[i].c_str() == commmand.c_str()) + _customMessageHeader.erase(_customMessageHeader.begin() + i); +} + +void SMTPData::removeCustomMessageHeader(uint8_t index) +{ + _customMessageHeader.erase(_customMessageHeader.begin() + index); +} + +void SMTPData::clearCustomMessageHeader() +{ + std::vector().swap(_customMessageHeader); +} + +uint8_t SMTPData::CustomMessageHeaderCount() +{ + return _customMessageHeader.size(); +} + +String SMTPData::getCustomMessageHeader(uint8_t index) +{ + if (index >= _customMessageHeader.size()) + return std::string().c_str(); + return _customMessageHeader[index].c_str(); +} + +void SMTPData::empty() +{ + std::string().swap(_host); + std::string().swap(_loginEmail); + std::string().swap(_loginPassword); + std::string().swap(_fromName); + std::string().swap(_senderEmail); + std::string().swap(_subject); + std::string().swap(_message); + clearRecipient(); + clearCustomMessageHeader(); + clearCC(); + clearBCC(); + clearAttachment(); +} + +void SMTPData::setSendCallback(sendStatusCallback sendCallback) +{ + _sendCallback = std::move(sendCallback); +} + +ReadStatus::ReadStatus() +{ +} +ReadStatus::~ReadStatus() +{ + empty(); +} + +String ReadStatus::status() +{ + return _status.c_str(); +} +String ReadStatus::info() +{ + return _info.c_str(); +} + +bool ReadStatus::success() +{ + return _success; +} +void ReadStatus::empty() +{ + std::string().swap(_info); + std::string().swap(_status); +} + +SendStatus::SendStatus() +{ +} + +SendStatus::~SendStatus() +{ + empty(); +} + +String SendStatus::info() +{ + return _info.c_str(); +} +bool SendStatus::success() +{ + return _success; +} +void SendStatus::empty() +{ + std::string().swap(_info); +} + +ESP32_MailClient MailClient = ESP32_MailClient(); + +#endif //ESP32 + +#endif //ESP32_MailClient_CPP diff --git a/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h new file mode 100755 index 000000000..943cd62f7 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h @@ -0,0 +1,1908 @@ +/* + *Mail Client Arduino Library for ESP32, version 2.1.4 + * + * April 12, 2020 + * + * This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download through SMTP and IMAP servers. + * + * The library supports all ESP32 MCU based modules. + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + + + +#ifndef ESP32_MailClient_H +#define ESP32_MailClient_H + +#ifdef ESP32 + +#include +#include "WiFiClientSecureESP32.h" +#include +#include +#include +#include +#include +#include +#include +#include "RFC2047.h" +#include "ESP32MailHTTPClient.h" +#include "ESP32TimeHelper.h" + +#define FORMAT_SPIFFS_IF_FAILED true + +static RFC2047 RFC2047Decoder; + +using namespace std; + +#define SMTP_STATUS_SERVER_CONNECT_FAILED 1 +#define SMTP_STATUS_SMTP_RESPONSE_FAILED 2 +#define SMTP_STATUS_IDENTIFICATION_FAILED 3 +#define SMTP_STATUS_AUTHEN_NOT_SUPPORT 4 +#define SMTP_STATUS_AUTHEN_FAILED 5 +#define SMTP_STATUS_USER_LOGIN_FAILED 6 +#define SMTP_STATUS_PASSWORD_LOGIN_FAILED 7 +#define SMTP_STATUS_SEND_HEADER_SENDER_FAILED 8 +#define SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED 9 +#define SMTP_STATUS_SEND_BODY_FAILED 10 + +#define IMAP_STATUS_SERVER_CONNECT_FAILED 1 +#define IMAP_STATUS_IMAP_RESPONSE_FAILED 2 +#define IMAP_STATUS_LOGIN_FAILED 3 +#define IMAP_STATUS_BAD_COMMAND 4 +#define IMAP_STATUS_PARSE_FLAG_FAILED 5 + +#define MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL 100 + +#define MAX_EMAIL_SEARCH_LIMIT 1000 + +static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +class IMAPData; +class SMTPData; +class attachmentData; +class SendStatus; +class messageBodyData; +class DownloadProgress; +class MessageData; + +struct MailClientStorageType +{ + static const uint8_t SPIFFS = 0; + static const uint8_t SD = 1; +}; + +static const char ESP32_MAIL_STR_1[] PROGMEM = "Content-Type: multipart/mixed; boundary=\""; +static const char ESP32_MAIL_STR_2[] PROGMEM = "{BOUNDARY}"; +static const char ESP32_MAIL_STR_3[] PROGMEM = "Mime-Version: 1.0\r\n"; +static const char ESP32_MAIL_STR_4[] PROGMEM = "AUTH LOGIN"; +static const char ESP32_MAIL_STR_5[] PROGMEM = "HELO dude"; +static const char ESP32_MAIL_STR_6[] PROGMEM = "EHLO dude"; +static const char ESP32_MAIL_STR_7[] PROGMEM = "QUIT"; +static const char ESP32_MAIL_STR_8[] PROGMEM = "MAIL FROM:"; +static const char ESP32_MAIL_STR_9[] PROGMEM = "RCPT TO:"; +static const char ESP32_MAIL_STR_10[] PROGMEM = "From: "; +static const char ESP32_MAIL_STR_11[] PROGMEM = "To: "; +static const char ESP32_MAIL_STR_12[] PROGMEM = "Cc: "; +static const char ESP32_MAIL_STR_13[] PROGMEM = ",<"; +static const char ESP32_MAIL_STR_14[] PROGMEM = "<"; +static const char ESP32_MAIL_STR_15[] PROGMEM = ">"; +static const char ESP32_MAIL_STR_16[] PROGMEM = "DATA"; +static const char ESP32_MAIL_STR_17[] PROGMEM = "X-Priority: "; +static const char ESP32_MAIL_STR_18[] PROGMEM = "X-MSMail-Priority: High\r\n"; +static const char ESP32_MAIL_STR_19[] PROGMEM = "X-MSMail-Priority: Normal\r\n"; +static const char ESP32_MAIL_STR_20[] PROGMEM = "X-MSMail-Priority: Low\r\n"; +static const char ESP32_MAIL_STR_21[] PROGMEM = "Importance: High\r\n"; +static const char ESP32_MAIL_STR_22[] PROGMEM = "Importance: Normal\r\n"; +static const char ESP32_MAIL_STR_23[] PROGMEM = "Importance: Low\r\n"; +static const char ESP32_MAIL_STR_24[] PROGMEM = "Subject: "; +static const char ESP32_MAIL_STR_25[] PROGMEM = "Content-Type: "; +static const char ESP32_MAIL_STR_26[] PROGMEM = "; Name=\""; +static const char ESP32_MAIL_STR_27[] PROGMEM = "Content-type: text/plain; charset=us-ascii\r\n"; +static const char ESP32_MAIL_STR_28[] PROGMEM = "Content-type: text/html; charset=\"UTF-8\"\r\n"; +static const char ESP32_MAIL_STR_29[] PROGMEM = "Content-transfer-encoding: 7bit\r\n"; +static const char ESP32_MAIL_STR_30[] PROGMEM = "Content-Disposition: attachment; filename=\""; +static const char ESP32_MAIL_STR_31[] PROGMEM = "Content-transfer-encoding: base64\r\n"; +static const char ESP32_MAIL_STR_32[] PROGMEM = "application/octet-stream"; +static const char ESP32_MAIL_STR_33[] PROGMEM = "--"; +static const char ESP32_MAIL_STR_34[] PROGMEM = "\r\n"; +static const char ESP32_MAIL_STR_35[] PROGMEM = "\"\r\n\r\n"; +static const char ESP32_MAIL_STR_36[] PROGMEM = "\"\r\n"; +static const char ESP32_MAIL_STR_37[] PROGMEM = "\r\n.\r\n"; +static const char ESP32_MAIL_STR_38[] PROGMEM = "could not connect to server"; +static const char ESP32_MAIL_STR_39[] PROGMEM = "could not handle SMTP server response"; +static const char ESP32_MAIL_STR_40[] PROGMEM = "could not handle IMAP server response"; +static const char ESP32_MAIL_STR_41[] PROGMEM = "identification failed"; +static const char ESP32_MAIL_STR_42[] PROGMEM = "authentication is not support"; +static const char ESP32_MAIL_STR_43[] PROGMEM = "authentication failed"; +static const char ESP32_MAIL_STR_44[] PROGMEM = "login account is not valid"; +static const char ESP32_MAIL_STR_45[] PROGMEM = "could not sigin"; +static const char ESP32_MAIL_STR_46[] PROGMEM = "could not parse command"; +static const char ESP32_MAIL_STR_47[] PROGMEM = "login password is not valid"; +static const char ESP32_MAIL_STR_48[] PROGMEM = "send header failed"; +static const char ESP32_MAIL_STR_49[] PROGMEM = "send body failed"; +static const char ESP32_MAIL_STR_50[] PROGMEM = "Connecting to IMAP server..."; +static const char ESP32_MAIL_STR_51[] PROGMEM = "initialize"; +static const char ESP32_MAIL_STR_52[] PROGMEM = "failed"; +static const char ESP32_MAIL_STR_53[] PROGMEM = "Error, "; +static const char ESP32_MAIL_STR_54[] PROGMEM = "IMAP server connected"; +static const char ESP32_MAIL_STR_55[] PROGMEM = "server_connected"; +static const char ESP32_MAIL_STR_56[] PROGMEM = "Sign in..."; +static const char ESP32_MAIL_STR_57[] PROGMEM = "signin"; +static const char ESP32_MAIL_STR_58[] PROGMEM = "Lising folders..."; +static const char ESP32_MAIL_STR_59[] PROGMEM = "listing"; +static const char ESP32_MAIL_STR_60[] PROGMEM = ":::::::Message folders:::::::"; +static const char ESP32_MAIL_STR_61[] PROGMEM = "Reading "; +static const char ESP32_MAIL_STR_62[] PROGMEM = "Predicted next UID: "; +static const char ESP32_MAIL_STR_63[] PROGMEM = "Total Message: "; +static const char ESP32_MAIL_STR_64[] PROGMEM = "::::::::::::Flags::::::::::::"; +static const char ESP32_MAIL_STR_65[] PROGMEM = "::::::::::Messages:::::::::::"; +static const char ESP32_MAIL_STR_66[] PROGMEM = "Searching messages..."; +static const char ESP32_MAIL_STR_67[] PROGMEM = "searching"; +static const char ESP32_MAIL_STR_68[] PROGMEM = "Search limit:"; +static const char ESP32_MAIL_STR_69[] PROGMEM = "Found "; +static const char ESP32_MAIL_STR_70[] PROGMEM = " messages"; +static const char ESP32_MAIL_STR_71[] PROGMEM = "Show "; +static const char ESP32_MAIL_STR_72[] PROGMEM = "Could not found any Email for defined criteria"; +static const char ESP32_MAIL_STR_73[] PROGMEM = "Search criteria is not set, fetch the recent message"; +static const char ESP32_MAIL_STR_74[] PROGMEM = "Feching message "; +static const char ESP32_MAIL_STR_75[] PROGMEM = ", UID: "; +static const char ESP32_MAIL_STR_76[] PROGMEM = ", Number: "; +static const char ESP32_MAIL_STR_77[] PROGMEM = "fetching"; +static const char ESP32_MAIL_STR_78[] PROGMEM = "Attachment ("; +static const char ESP32_MAIL_STR_79[] PROGMEM = ")"; +static const char ESP32_MAIL_STR_80[] PROGMEM = "Downloading attachments..."; +static const char ESP32_MAIL_STR_81[] PROGMEM = "downloading"; +static const char ESP32_MAIL_STR_82[] PROGMEM = " bytes"; +static const char ESP32_MAIL_STR_83[] PROGMEM = " - "; +static const char ESP32_MAIL_STR_84[] PROGMEM = "Free Heap: "; +static const char ESP32_MAIL_STR_85[] PROGMEM = "Sign out..."; +static const char ESP32_MAIL_STR_86[] PROGMEM = "signout"; +static const char ESP32_MAIL_STR_87[] PROGMEM = "Finished"; +static const char ESP32_MAIL_STR_88[] PROGMEM = "finished"; +static const char ESP32_MAIL_STR_89[] PROGMEM = "SD card mount failed"; +static const char ESP32_MAIL_STR_90[] PROGMEM = "download "; +static const char ESP32_MAIL_STR_91[] PROGMEM = ", "; +static const char ESP32_MAIL_STR_92[] PROGMEM = "%"; +static const char ESP32_MAIL_STR_93[] PROGMEM = "connection timeout"; +static const char ESP32_MAIL_STR_94[] PROGMEM = "WiFi connection lost"; +static const char ESP32_MAIL_STR_95[] PROGMEM = "no server response"; +static const char ESP32_MAIL_STR_96[] PROGMEM = "finished"; +static const char ESP32_MAIL_STR_97[] PROGMEM = " folder..."; +static const char ESP32_MAIL_STR_98[] PROGMEM = "Finished"; +static const char ESP32_MAIL_STR_99[] PROGMEM = "Date: "; +static const char ESP32_MAIL_STR_100[] PROGMEM = "Messsage UID: "; +static const char ESP32_MAIL_STR_101[] PROGMEM = "Messsage ID: "; +static const char ESP32_MAIL_STR_102[] PROGMEM = "Accept Language: "; +static const char ESP32_MAIL_STR_103[] PROGMEM = "Content Language: "; +static const char ESP32_MAIL_STR_104[] PROGMEM = "From: "; +static const char ESP32_MAIL_STR_105[] PROGMEM = "From Charset: "; +static const char ESP32_MAIL_STR_106[] PROGMEM = "To: "; +static const char ESP32_MAIL_STR_107[] PROGMEM = "To Charset: "; +static const char ESP32_MAIL_STR_108[] PROGMEM = "CC: "; +static const char ESP32_MAIL_STR_109[] PROGMEM = "CC Charset: "; +static const char ESP32_MAIL_STR_110[] PROGMEM = "Subject: "; +static const char ESP32_MAIL_STR_111[] PROGMEM = "Subject Charset: "; +static const char ESP32_MAIL_STR_112[] PROGMEM = "Message Charset: "; +static const char ESP32_MAIL_STR_113[] PROGMEM = "Attachment: "; +static const char ESP32_MAIL_STR_114[] PROGMEM = "File Index: "; +static const char ESP32_MAIL_STR_115[] PROGMEM = "Filename: "; +static const char ESP32_MAIL_STR_116[] PROGMEM = "Name: "; +static const char ESP32_MAIL_STR_117[] PROGMEM = "Size: "; +static const char ESP32_MAIL_STR_118[] PROGMEM = "Type: "; +static const char ESP32_MAIL_STR_119[] PROGMEM = "Creation Date: "; +static const char ESP32_MAIL_STR_120[] PROGMEM = "Connecting to SMTP server..."; +static const char ESP32_MAIL_STR_121[] PROGMEM = "SMTP server connected, wait for response..."; +static const char ESP32_MAIL_STR_122[] PROGMEM = "Identification..."; +static const char ESP32_MAIL_STR_123[] PROGMEM = "Authentication..."; +static const char ESP32_MAIL_STR_124[] PROGMEM = "Sign in..."; +static const char ESP32_MAIL_STR_125[] PROGMEM = "Sending Email header..."; +static const char ESP32_MAIL_STR_126[] PROGMEM = "Sending Email body..."; +static const char ESP32_MAIL_STR_127[] PROGMEM = "Sending attachments..."; +static const char ESP32_MAIL_STR_128[] PROGMEM = "Finalize..."; +static const char ESP32_MAIL_STR_129[] PROGMEM = "Finished\r\nEmail sent successfully"; +static const char ESP32_MAIL_STR_130[] PROGMEM = "$ LOGIN "; +static const char ESP32_MAIL_STR_131[] PROGMEM = " "; +static const char ESP32_MAIL_STR_132[] PROGMEM = " OK "; +static const char ESP32_MAIL_STR_133[] PROGMEM = "$ LIST \"\" \"*\""; +static const char ESP32_MAIL_STR_134[] PROGMEM = "CAPABILITY"; +static const char ESP32_MAIL_STR_135[] PROGMEM = "$ EXAMINE \""; +static const char ESP32_MAIL_STR_136[] PROGMEM = "\""; +static const char ESP32_MAIL_STR_137[] PROGMEM = "UID "; +static const char ESP32_MAIL_STR_138[] PROGMEM = " UID"; +static const char ESP32_MAIL_STR_139[] PROGMEM = " SEARCH"; +static const char ESP32_MAIL_STR_140[] PROGMEM = "UID"; +static const char ESP32_MAIL_STR_141[] PROGMEM = "SEARCH"; +static const char ESP32_MAIL_STR_142[] PROGMEM = "$ UID FETCH "; +static const char ESP32_MAIL_STR_143[] PROGMEM = "$ FETCH "; +static const char ESP32_MAIL_STR_144[] PROGMEM = " BODY.PEEK[HEADER.FIELDS (SUBJECT FROM TO DATE Message-ID Accept-Language Content-Language)]"; +static const char ESP32_MAIL_STR_145[] PROGMEM = "IMAP"; +static const char ESP32_MAIL_STR_146[] PROGMEM = "$ LOGOUT"; +static const char ESP32_MAIL_STR_147[] PROGMEM = " BODY.PEEK["; +static const char ESP32_MAIL_STR_148[] PROGMEM = ".MIME]"; +static const char ESP32_MAIL_STR_149[] PROGMEM = "multipart/"; +static const char ESP32_MAIL_STR_150[] PROGMEM = "$ UID FETCH "; +static const char ESP32_MAIL_STR_151[] PROGMEM = " BODY.PEEK["; +static const char ESP32_MAIL_STR_152[] PROGMEM = "."; +static const char ESP32_MAIL_STR_153[] PROGMEM = "attachment"; +static const char ESP32_MAIL_STR_154[] PROGMEM = "text/html"; +static const char ESP32_MAIL_STR_155[] PROGMEM = "text/plain"; +static const char ESP32_MAIL_STR_156[] PROGMEM = "]"; +static const char ESP32_MAIL_STR_157[] PROGMEM = "* ESEARCH"; +static const char ESP32_MAIL_STR_158[] PROGMEM = "$ NO "; +static const char ESP32_MAIL_STR_159[] PROGMEM = "$ BAD "; +static const char ESP32_MAIL_STR_160[] PROGMEM = "base64"; +static const char ESP32_MAIL_STR_161[] PROGMEM = "/decoded_msg.txt"; +static const char ESP32_MAIL_STR_162[] PROGMEM = "/raw_msg.txt"; +static const char ESP32_MAIL_STR_163[] PROGMEM = "/decoded_msg.html"; +static const char ESP32_MAIL_STR_164[] PROGMEM = "/raw_msg.html"; +static const char ESP32_MAIL_STR_165[] PROGMEM = " FETCH "; +static const char ESP32_MAIL_STR_166[] PROGMEM = "* OK "; +static const char ESP32_MAIL_STR_167[] PROGMEM = "content-type: "; +static const char ESP32_MAIL_STR_168[] PROGMEM = "charset=\""; +static const char ESP32_MAIL_STR_169[] PROGMEM = "charset="; +static const char ESP32_MAIL_STR_170[] PROGMEM = "name=\""; +static const char ESP32_MAIL_STR_171[] PROGMEM = "name="; +static const char ESP32_MAIL_STR_172[] PROGMEM = "content-transfer-encoding: "; +static const char ESP32_MAIL_STR_173[] PROGMEM = "\r"; +static const char ESP32_MAIL_STR_174[] PROGMEM = "content-description: "; +static const char ESP32_MAIL_STR_175[] PROGMEM = "content-disposition: "; +static const char ESP32_MAIL_STR_176[] PROGMEM = "filename=\""; +static const char ESP32_MAIL_STR_177[] PROGMEM = "filename="; +static const char ESP32_MAIL_STR_178[] PROGMEM = "size="; +static const char ESP32_MAIL_STR_179[] PROGMEM = "creation-date=\""; +static const char ESP32_MAIL_STR_180[] PROGMEM = "creation-date="; +static const char ESP32_MAIL_STR_181[] PROGMEM = "modification-date=\""; +static const char ESP32_MAIL_STR_182[] PROGMEM = "modification-date="; +static const char ESP32_MAIL_STR_183[] PROGMEM = "*"; +static const char ESP32_MAIL_STR_184[] PROGMEM = "from: "; +static const char ESP32_MAIL_STR_185[] PROGMEM = "to: "; +static const char ESP32_MAIL_STR_186[] PROGMEM = "cc: "; +static const char ESP32_MAIL_STR_187[] PROGMEM = "subject: "; +static const char ESP32_MAIL_STR_188[] PROGMEM = "date: "; +static const char ESP32_MAIL_STR_189[] PROGMEM = "message-id: "; +static const char ESP32_MAIL_STR_190[] PROGMEM = "accept-language: "; +static const char ESP32_MAIL_STR_191[] PROGMEM = "content-language: "; +static const char ESP32_MAIL_STR_192[] PROGMEM = ")"; +static const char ESP32_MAIL_STR_193[] PROGMEM = "{"; +static const char ESP32_MAIL_STR_194[] PROGMEM = "}"; +static const char ESP32_MAIL_STR_195[] PROGMEM = " LIST "; +static const char ESP32_MAIL_STR_196[] PROGMEM = "\\Noselect"; +static const char ESP32_MAIL_STR_197[] PROGMEM = " FLAGS "; +static const char ESP32_MAIL_STR_198[] PROGMEM = "("; +static const char ESP32_MAIL_STR_199[] PROGMEM = " EXISTS"; +static const char ESP32_MAIL_STR_200[] PROGMEM = " [UIDNEXT "; +static const char ESP32_MAIL_STR_201[] PROGMEM = "]"; +static const char ESP32_MAIL_STR_202[] PROGMEM = "/"; +static const char ESP32_MAIL_STR_203[] PROGMEM = "/header.txt"; +static const char ESP32_MAIL_STR_204[] PROGMEM = "/esp.32"; +static const char ESP32_MAIL_STR_205[] PROGMEM = "high"; +static const char ESP32_MAIL_STR_206[] PROGMEM = "High"; +static const char ESP32_MAIL_STR_207[] PROGMEM = "normal"; +static const char ESP32_MAIL_STR_208[] PROGMEM = "Normal"; +static const char ESP32_MAIL_STR_209[] PROGMEM = "low"; +static const char ESP32_MAIL_STR_210[] PROGMEM = "Low"; +static const char ESP32_MAIL_STR_211[] PROGMEM = "$ OK "; +static const char ESP32_MAIL_STR_212[] PROGMEM = "FLAGS"; +static const char ESP32_MAIL_STR_213[] PROGMEM = "BODY"; +static const char ESP32_MAIL_STR_214[] PROGMEM = "PEEK"; +static const char ESP32_MAIL_STR_215[] PROGMEM = "TEXT"; +static const char ESP32_MAIL_STR_216[] PROGMEM = "HEADER"; +static const char ESP32_MAIL_STR_217[] PROGMEM = "FIELDS"; +static const char ESP32_MAIL_STR_218[] PROGMEM = "["; +static const char ESP32_MAIL_STR_219[] PROGMEM = "]"; +static const char ESP32_MAIL_STR_220[] PROGMEM = "MIME"; +static const char ESP32_MAIL_STR_221[] PROGMEM = "connection lost"; +static const char ESP32_MAIL_STR_222[] PROGMEM = "set recipient failed"; +static const char ESP32_MAIL_STR_223[] PROGMEM = " NEW"; +static const char ESP32_MAIL_STR_224[] PROGMEM = "ALL"; + +static const char ESP32_MAIL_STR_225[] PROGMEM = "INFO: connecting to IMAP server..."; +static const char ESP32_MAIL_STR_226[] PROGMEM = "ERROR: could not connect to internet"; +static const char ESP32_MAIL_STR_227[] PROGMEM = "ERROR: "; +static const char ESP32_MAIL_STR_228[] PROGMEM = "INFO: server connected"; +static const char ESP32_MAIL_STR_229[] PROGMEM = "INFO: send imap command LOGIN"; +static const char ESP32_MAIL_STR_230[] PROGMEM = "INFO: send imap command LIST"; +static const char ESP32_MAIL_STR_231[] PROGMEM = "INFO: send imap command EXAMINE"; +static const char ESP32_MAIL_STR_232[] PROGMEM = "INFO: search message"; +static const char ESP32_MAIL_STR_233[] PROGMEM = "INFO: fetch message"; +static const char ESP32_MAIL_STR_234[] PROGMEM = "INFO: send imap command LOGOUT"; +static const char ESP32_MAIL_STR_235[] PROGMEM = "INFO: message fetch completed"; +static const char ESP32_MAIL_STR_236[] PROGMEM = "INFO: connecting to SMTP server..."; +static const char ESP32_MAIL_STR_237[] PROGMEM = "ERROR: could not connect to internet"; +static const char ESP32_MAIL_STR_238[] PROGMEM = "INFO: smtp server connected"; +static const char ESP32_MAIL_STR_239[] PROGMEM = "INFO: send smtp HELO command"; +static const char ESP32_MAIL_STR_240[] PROGMEM = "INFO: send smtp AUTH LOGIN command"; +static const char ESP32_MAIL_STR_241[] PROGMEM = "INFO: log in with username and password"; +static const char ESP32_MAIL_STR_242[] PROGMEM = "INFO: send email header"; +static const char ESP32_MAIL_STR_243[] PROGMEM = "INFO: send email body"; +static const char ESP32_MAIL_STR_244[] PROGMEM = "INFO: send attachment..."; +static const char ESP32_MAIL_STR_245[] PROGMEM = "INFO: finalize..."; +static const char ESP32_MAIL_STR_246[] PROGMEM = "INFO: email sent successfully"; +static const char ESP32_MAIL_STR_247[] PROGMEM = "$ SELECT \""; +static const char ESP32_MAIL_STR_248[] PROGMEM = "INFO: send imap command SELECT"; +static const char ESP32_MAIL_STR_249[] PROGMEM = "$ UID STORE "; +static const char ESP32_MAIL_STR_250[] PROGMEM = " FLAGS ("; +static const char ESP32_MAIL_STR_251[] PROGMEM = " +FLAGS ("; +static const char ESP32_MAIL_STR_252[] PROGMEM = " -FLAGS ("; +static const char ESP32_MAIL_STR_253[] PROGMEM = "INFO: set FLAG"; +static const char ESP32_MAIL_STR_254[] PROGMEM = "INFO: add FLAG"; +static const char ESP32_MAIL_STR_255[] PROGMEM = "INFO: remove FLAG"; +static const char ESP32_MAIL_STR_256[] PROGMEM = "could not parse flag"; +static const char ESP32_MAIL_STR_257[] PROGMEM = "BAD"; + +__attribute__((used)) static bool compFunc(uint32_t i, uint32_t j) +{ + return (i > j); +} + +class ReadStatus +{ +public: + ReadStatus(); + ~ReadStatus(); + String status(); + String info(); + bool success(); + void empty(); + friend IMAPData; + + std::string _status = ""; + std::string _info = ""; + bool _success = false; +}; + +class SendStatus +{ +public: + SendStatus(); + ~SendStatus(); + String info(); + bool success(); + void empty(); + friend SMTPData; + + std::string _info = ""; + bool _success = false; +}; + +typedef void (*readStatusCallback)(ReadStatus); +typedef void (*sendStatusCallback)(SendStatus); + + + +class ESP32_MailClient +{ + +public: + /* + + Sending Email through SMTP server + + @param net - HTTPClientESP32 WiFi client. + + @return Boolean type status indicates the success of operation. + + */ + bool sendMail(SMTPData &smtpData); + + /* + + Reading Email through IMAP server. + + @param imapData - IMAP Data object to hold data and instances. + + @return Boolean type status indicates the success of operation. + + */ + bool readMail(IMAPData &imapData); + + /* + + Set the argument to the Flags for message. + + @param imapData - IMAP Data object to hold data and instances. + + @param msgUID - The UID of message. + + @param flags - The flag list. + + + @return Boolean type status indicates the success of operation. + + */ + bool setFlag(IMAPData &imapData, int msgUID, const String &flags); + + + + /* + + Add the argument to the Flags for message. + + @param imapData - IMAP Data object to hold data and instances. + + @param msgUID - The UID of message. + + @param flags - The flag list. + + + @return Boolean type status indicates the success of operation. + + */ + bool addFlag(IMAPData &imapData, int msgUID, const String &flags); + + /* + + Remove the argument from the Flags for message. + + @param imapData - IMAP Data object to hold data and instances. + + @param msgUID - The UID of message. + + @param flags - The flag list. + + + @return Boolean type status indicates the success of operation. + + */ + bool removeFlag(IMAPData &imapData, int msgUID, const String &flags); + + /* + + Get the Email sending error details. + + @return Error details string (String object). + + */ + String smtpErrorReason(); + + /* + + Get the Email reading error details. + + @return Error details string (String object). + + */ + String imapErrorReason(); + + /* + + Init SD card with GPIO pins. + + @param sck - SPI Clock pin. + @param miso - SPI MISO pin. + @param mosi - SPI MOSI pin. + @param ss - SPI Chip/Slave Select pin. + + @return Boolean type status indicates the success of operation. + + */ + bool sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss); + + /* + + Init SD card with default GPIO pins. + + @return Boolean type status indicates the success of operation. + + */ + bool sdBegin(void); + + struct IMAP_COMMAND_TYPE; + struct IMAP_HEADER_TYPE; + + +ESP32TimeHelper Time; + +private: + int _smtpStatus = 0; + int _imapStatus = 0; + bool _sdOk = false; + bool _sdConfigSet = false; + uint8_t _sck, _miso, _mosi, _ss; + unsigned long _lastReconnectMillis = 0; + uint16_t _reconnectTimeout = 10000; + + + std::string smtpErrorReasonStr(); + std::string imapErrorReasonStr(); + void ESP32MailDebugError(); + void ESP32MailDebugInfo(PGM_P info); + void set_message_header(string &header, std::string &message, bool htmlFormat); + void set_attachment_header(uint8_t index, std::string &header, attachmentData &attach); + void clientReadAll(WiFiClient *client); + double base64DecodeSize(std::string lastBase64String, int length); + unsigned char *base64_decode_char(const unsigned char *src, size_t len, size_t *out_len); + std::string base64_encode_string(const unsigned char *src, size_t len); + void send_base64_encode_mime_data(WiFiClient *client, const unsigned char *src, size_t len); + void send_base64_encode_mime_file(WiFiClient *client, File file); + int waitSMTPResponse(SMTPData &smtpData); + bool waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandType = 0, int maxChar = 0, int mailIndex = -1, int messageDataIndex = -1, std ::string part = ""); + bool _setFlag(IMAPData &imapData, int msgUID, const String &flags, uint8_t action); + bool getIMAPResponse(IMAPData &imapData); + void createDirs(std::string dirs); + bool smtpClientAvailable(SMTPData &smtpData, bool available); + bool imapClientAvailable(IMAPData &imapData, bool available); + bool sdTest(); +}; + +class messageBodyData +{ +public: + messageBodyData(); + ~messageBodyData(); + void empty(); + + friend ESP32_MailClient; + friend IMAPData; + +protected: + uint8_t _index = 0; + size_t _size = 0; + std::string _text = ""; + std::string _filename = ""; + std::string _savePath = ""; + std::string _name = ""; + std::string _disposition = ""; + std::string _contentType = ""; + std::string _descr = ""; + std::string _transfer_encoding = ""; + std::string _creation_date = ""; + std::string _modification_date = ""; + std::string _charset = ""; + std::string _part = ""; + std::string _downloadError = ""; + bool _sdFileOpenWrite = false; + bool _error = false; +}; + +class attachmentData +{ +public: + attachmentData(); + ~attachmentData(); + + friend ESP32_MailClient; + friend SMTPData; + +protected: + uint8_t _index = 0; + std::vector> _buf = std::vector>(); + std::vector _filename = std::vector(); + std::vector _id = std::vector(); + std::vector _type = std::vector(); + std::vector _size = std::vector(); + std::vector _mime_type = std::vector(); + + void add(const String &fileName, const String &mimeType, uint8_t *data, size_t size); + void remove(uint8_t index); + void free(); + String getFileName(uint8_t index); + String getMimeType(uint8_t index); + uint8_t *getData(uint8_t index); + uint16_t getSize(uint8_t index); + uint8_t getCount(); + uint8_t getType(uint8_t index); +}; + +class IMAPData +{ +public: + IMAPData(); + ~IMAPData(); + + /* + Set the IMAP server login credentials. + + @param host - IMAP server e.g. imap.gmail.com. + @param port - IMAP port e.g. 993 for gmail. + @param loginEmail - The Email address of account. + @param loginPassword - The account password. + @rootCA - Root CA certificate base64 string + + */ + void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA); + void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword); + + /* + + Set STARTTLS mode to enable STARTTLS protocol + + @param starttls - bool flag that enables STARTTLS mode + + */ + void setSTARTTLS(bool starttls); + + /* + + Set debug print to serial + + @param debug - bool flag to enable debug + + */ + void setDebug(bool debug); + + /* + + Set the mailbox folder to search or fetch. + + @param folderName - Known mailbox folder. Ddefault value is INBOX + + */ + void setFolder(const String &folderName); + + /* + + Set the maximum message buffer size for text/html result from search or fetch the message. + + @param size - The message size in byte. + + */ + void setMessageBufferSize(size_t size); + + /* + + Set the maximum attachment file size to be downloaded. + + @param size - The attachement file size in byte. + + */ + void setAttachmentSizeLimit(size_t size); + + /* + Set the search criteria used in selected mailbox search. + + In case of message UID was set through setFetchUID function, search operation will not process, + you need to clear message UID by calling imapData.setFetchUID("") to clear. + + @param criteria - Search criteria String. + + If folder is not set, the INBOX folder will be used + + Example: + + "SINCE 10-Feb-2019" will search all messages that received since 10 Feb 2019 + "UID SEARCH ALL" will seach all message which will return the message UID that can be use later for fetch one or more messages. + + + Search criteria can be consisted these keywords + + ALL - All messages in the mailbox; the default initial key for ANDing. + ANSWERED - Messages with the \Answered flag set. + BCC - Messages that contain the specified string in the envelope structure's BCC field. + BEFORE - Messages whose internal date (disregarding time and timezone) is earlier than the specified date. + BODY - Messages that contain the specified string in the body of the message. + CC - Messages that contain the specified string in the envelope structure's CC field. + DELETED - Messages with the \Deleted flag set. + DRAFT - Messages with the \Draft flag set. + FLAGGED - Messages with the \Flagged flag set. + FROM - Messages that contain the specified string in the envelope structure's FROM field. + HEADER - Messages that have a header with the specified field-name (as defined in [RFC-2822]) + and that contains the specified string in the text of the header (what comes after the colon). + + If the string to search is zero-length, this matches all messages that have a header line with + the specified field-name regardless of the contents. + + KEYWORD - Messages with the specified keyword flag set. + LARGER - Messages with an [RFC-2822] size larger than the specified number of octets. + NEW - Messages that have the \Recent flag set but not the \Seen flag. + This is functionally equivalent to "(RECENT UNSEEN)". + NOT - Messages that do not match the specified search key. + OLD - Messages that do not have the \Recent flag set. This is functionally equivalent to + "NOT RECENT" (as opposed to "NOT NEW"). + ON - Messages whose internal date (disregarding time and timezone) is within the specified date. + OR - Messages that match either search key. + RECENT - Messages that have the \Recent flag set. + SEEN - Messages that have the \Seen flag set. + SENTBEFORE - Messages whose [RFC-2822] Date: header (disregarding time and timezone) is earlier than the specified date. + SENTON - Messages whose [RFC-2822] Date: header (disregarding time and timezone) is within the specified date. + SENTSINCE - Messages whose [RFC-2822] Date: header (disregarding time and timezone) is within or later than the specified date. + SINCE - Messages whose internal date (disregarding time and timezone) is within or later than the specified date. + SMALLER - Messages with an [RFC-2822] size smaller than the specified number of octets. + SUBJECT - Messages that contain the specified string in the envelope structure's SUBJECT field. + TEXT - Messages that contain the specified string in the header or body of the message. + TO - Messages that contain the specified string in the envelope structure's TO field. + UID - Messages with unique identifiers corresponding to the specified unique identifier set. + Sequence set ranges are permitted. + UNANSWERED - Messages that do not have the \Answered flag set. + UNDELETED - Messages that do not have the \Deleted flag set. + UNDRAFT - Messages that do not have the \Draft flag set. + UNFLAGGED - Messages that do not have the \Flagged flag set. + UNKEYWORD - Messages that do not have the specified keyword flag set. + UNSEEN - Messages that do not have the \Seen flag set. + + */ + void setSearchCriteria(const String &criteria); + + /* + Set to search the unseen message. + + @param unseenSearch - Boolean flag to enable unseen message search. + + This function will be overridden (omitted) by setFetchUID as setSearchCriteria. + + */ + void setSearchUnseenMessage(bool unseenSearch); + + /* + Set the download folder. + + @param path - Path in SD card. + + All text/html message and attachemnts will be saved to message UID folder which created in defined path + e.g. "/{DEFINED_PATH}/{MESSAGE_UID}/{ATTACHMENT_FILE...}". + + */ + void setSaveFilePath(const String &path); + + /* + + Specify message UID to fetch or read. + + @param fetchUID - The message UID. + + Specify the message UID to fetch (read) only specific message instead of search. + + */ + void setFetchUID(const String &fetchUID); + + /* + + Set storage type to save download attached file or messages. + + @param storageType - The storage type to save file, MailClientStorageType::SD or MailClientStorageType::SPIFFS + + */ + void setFileStorageType(uint8_t storageType); + + /* + + Enable/disable attachment download. + + @param download - Boolean flag to enable/disable attachment download. + + */ + void setDownloadAttachment(bool download); + + /* + + Enable/disable html message result. + + @param htmlFormat - Boolean flag to enable/disable html message result. + + The default value is false. + + */ + void setHTMLMessage(bool htmlFormat); + + /* + + Enable/disable plain text message result. + + @param textFormat - Boolean flag to enable/disable plain text message result. + + The default value is true. + + */ + void setTextMessage(bool textFormat); + + /* + + Set the maximum message to search. + + @param limit - Any number from 0 to 65535. + + The default value is 20. + + */ + void setSearchLimit(uint16_t limit); + + /* + + Enable/disable recent sort result. + + @param recentSort - Boolean flag to enable/disable recent message sort result. + + The default value is true. + + */ + void setRecentSort(bool recentSort); + + /* + + Assign callback function that return status of message fetching or reading. + + @param readCallback - The function that accept readStatusCallback as parameter. + + */ + void setReadCallback(readStatusCallback readCallback); + + /* + + Enable/disable attachement download progress while fetching or receiving message. + + @param report - Boolean flag to enable/disable attachement download progress while fetching or receiving message. + + To get the download status, Callback function should be set through setReadCallback. + + */ + void setDownloadReport(bool report); + + /* + + Determine only message header is return when search. + + */ + bool isHeaderOnly(); + + /* + + Get the sender name/Email for selected message from search result. + + @param messageIndex - The index of message. + + @return Sender name/Email String. + + */ + String getFrom(uint16_t messageIndex); + + /* + + Get the sender name/Email charactor encoding. + + @param messageIndex - The index of message. + + @return Sender name/Email charactor encoding which use for decoding. + + */ + String getFromCharset(uint16_t messageIndex); + + /* + + Get the recipient name/Email for selected message index from search result. + + @param messageIndex - The index of message. + + @return Recipient name/Email String. + + */ + String getTo(uint16_t messageIndex); + + /* + + Get the recipient name/Email charactor encoding. + + @param messageIndex - The index of message. + + @return Recipient name/Email charactor encoding which use in decoding to local language. + + */ + String getToCharset(uint16_t messageIndex); + + /* + + Get the CC name/Email for selected message index of IMAPData result. + + @param messageIndex - The index of message. + + @return CC name/Email String. + + */ + String getCC(uint16_t messageIndex); + + /* + + Get the CC name/Email charactor encoding. + + @param messageIndex - The index of message. + + @return CC name/Email charactor encoding which use in decoding to local language. + + */ + String getCCCharset(uint16_t messageIndex); + + /* + + Get the message subject for selected message index from search result. + + @param messageIndex - The index of message. + + @return Message subject name/Email String. + + */ + String getSubject(uint16_t messageIndex); + + /* + + Get the message subject charactor encoding. + + @param messageIndex - The index of message. + + @return Message subject charactor encoding which use in decoding to local language. + + */ + String getSubjectCharset(uint16_t messageIndex); + + /* + + Get the html message for selected message index from search result. + + @param messageIndex - The index of message. + + @return The html message String or empty String upon the setHTMLMessage was set. + + */ + String getHTMLMessage(uint16_t messageIndex); + + /* + + Get the plain text message for selected message index from search result. + + @param messageIndex - The index of message. + + @return The plain text message String or empty String upon the setTextMessage was set. + + */ + String getTextMessage(uint16_t messageIndex); + + /* + + Get the html message charactor encoding. + + @param messageIndex - The index of message. + + @return Html message charactor encoding which use in decoding to local language. + + */ + String getHTMLMessgaeCharset(uint16_t messageIndex); + + /* + + Get the text message charactor encoding. + + @param messageIndex - The index of message. + + @return The text message charactor encoding which use in decoding to local language. + + */ + String getTextMessgaeCharset(uint16_t messageIndex); + + /* + + Get the date of received message for selected message index from search result. + + @param messageIndex - The index of message. + + @return The date String. + + */ + String getDate(uint16_t messageIndex); + + /* + + Get the message UID for selected message index from search result. + + @param messageIndex - The index of message. + + @return UID String that can be use in setFetchUID. + + */ + String getUID(uint16_t messageIndex); + + /* + + Get the message number for selected message index from search result. + + @param messageIndex - The index of message. + + @return The message number which vary upon search criteria and sorting. + + */ + String getNumber(uint16_t messageIndex); + + /* + + Get the message ID for selected message index from search result. + + @param messageIndex - The index of message. + + @return The message ID String. + + */ + String getMessageID(uint16_t messageIndex); + + /* + + Get the accept language for selected message index from search result. + + @param messageIndex - The index of message. + + @return The accept language String. + + */ + String getAcceptLanguage(uint16_t messageIndex); + + /* + + Get the content language of text or html for selected message index from search result. + + @param messageIndex - The index of message. + + @return The content language String. + + */ + String getContentLanguage(uint16_t messageIndex); + + /* + + Determine fetch error status for selected message index from search result. + + @param messageIndex - The index of message. + + @return Fetch error status. + + */ + bool isFetchMessageFailed(uint16_t messageIndex); + + /* + Get fetch error reason for selected message index from search result. + + @param messageIndex - The index of message. + + @return Fetch error reason String for selected message index. + + */ + String getFetchMessageFailedReason(uint16_t messageIndex); + + /* + Determine the attachment download error for selected message index from search result. + + @param messageIndex - The index of message. + + @return Fetch status. + + */ + bool isDownloadAttachmentFailed(uint16_t messageIndex, size_t attachmentIndex); + + /* + + Get the attachment download error reason for selected message index from search result. + + @param messageIndex - The index of message. + + @return Download error reason String for selected message index. + + */ + String getDownloadAttachmentFailedReason(uint16_t messageIndex, size_t attachmentIndex); + + /* + + Determine the downloaded/saved text message error status for selected message index from search result. + + @param messageIndex - The index of message. + + @return Text message download status. + + */ + bool isDownloadMessageFailed(uint16_t messageIndex); + + /* + + Get the attachment or message downloadeds error reason for selected message index from search result. + + @param messageIndex - The index of message. + + @return Downloaded error reason String for selected message index. + + */ + String getDownloadMessageFailedReason(uint16_t messageIndex); + + /* + + Assign the download and decode flags for html message download. + + @param download - Boolean flag to enable/disable message download. + + @param decoded - Boolean flag to enable/disable html message decoding (support utf8 and base64 encoding). + + */ + void saveHTMLMessage(bool download, bool decoded); + + /* + + Assign the download and decode flags for plain text message download. + + @param download - Boolean flag to enable/disable message download. + + @param decoded - Boolean flag to enable/disable plain text message decoding (support utf8 and base64 encoding). + + */ + void saveTextMessage(bool download, bool decoded); + + /* + + Determine the mailbox folder count. + + @return Folder count number. + + */ + uint16_t getFolderCount(); + + /* + Get the mailbox folder name at selected index. + + @param folderIndex - Index of folder. + + @return Folder name String. + + Use folder name from this function for fetch or search. + + */ + String getFolder(uint16_t folderIndex); + + /* + + Determin the number of supported flags count. + + @return Flag count number. + + */ + uint16_t getFlagCount(); + + /* + Get the flag name for selected index. + + @param folderIndex - Index of folder. + + @return Flag name String. + + Use flags from this function for fetch or search. + + */ + String getFlag(uint16_t flagIndex); + + /* + + Get the number of message in selected mailbox folder. + + @return Total message number. + + */ + size_t totalMessages(); + + /* + + Get the number of message from search result. + + @return Search result number. + + */ + size_t searchCount(); + + /* + + Get the number of message available from search result which less than search limit. + + @return Available message number. + + */ + size_t availableMessages(); + + /* + + Get the number of attachments for selected message index from search result. + + @param messageIndex - Index of message. + + @return Number of attachments + + */ + size_t getAttachmentCount(uint16_t messageIndex); + + /* + + Get file name of attachment for selected attachment index and message index from search result. + + @param messageIndex - Index of message. + @param attachmentIndex - Index of attachment. + + @return The attachment file name String at the selected index. + + */ + String getAttachmentFileName(size_t messageIndex, size_t attachmentIndex); + + /* + + Get the name of attachment for selected attachment index and message index from search result. + + @param messageIndex - Index of message. + @param attachmentIndex - Index of attachment. + + @return The attachment name String at the selected index. + + */ + String getAttachmentName(size_t messageIndex, size_t attachmentIndex); + + /* + + Get attachment file size for selected attachment index and message index from search result. + + @param messageIndex - Index of message. + @param attachmentIndex - Index of attachment. + + @return The attachment file size in byte at the selected index. + + */ + int getAttachmentFileSize(size_t messageIndex, size_t attachmentIndex); + + /* + + Get creation date of attachment for selected attachment index and message index from search result. + + @param messageIndex - Index of message. + @param attachmentIndex - Index of attachment. + + @return The attachment creation date String at the selected index. + + */ + String getAttachmentCreationDate(size_t messageIndex, size_t attachmentIndex); + + /* + Get attachment file type for selected attachment index and message index from search result. + + @param messageIndex - Index of message. + @param attachmentIndex - Index of attachment. + + @return File MIME String at the selected index e.g. image/jpeg. + + */ + String getAttachmentType(size_t messageIndex, size_t attachmentIndex); + + /* + + Clear all IMAPData object data. + + */ + void empty(); + + /* + + Clear IMAPData object message data. + + */ + void clearMessageData(); + + friend ESP32_MailClient; + +private: + String getMessage(uint16_t messageIndex, bool htmlFormat); + + + size_t _totalMessage = 0; + std::string _host = ""; + uint16_t _port = 993; + uint8_t _storageType = 1; + bool _unseen = false; + std::string _loginEmail = ""; + std::string _loginPassword = ""; + std::string _currentFolder = "INBOX"; + std::string _nextUID = ""; + std::string _searchCriteria = "ALL*"; + std::string _fetchUID = ""; + std::string _savePath = ""; + + bool _downloadAttachment = false; + bool _recentSort = true; + bool _htmlFormat = false; + bool _textFormat = false; + bool _headerOnly = true; + bool _uidSearch = false; + bool _saveHTMLMsg = false; + bool _saveTextMsg = false; + bool _saveDecodedHTML = false; + bool _saveDecodedText = false; + bool _downloadReport = false; + bool _headerSaved = false; + + size_t _message_buffer_size = 200; + size_t _attacement_max_size = 1024 * 1024; + uint16_t _emailNumMax = 20; + int _searchCount; + bool _starttls = false; + bool _debug = false; + readStatusCallback _readCallback = NULL; + + std::vector _date = std::vector(); + std::vector _subject = std::vector(); + std::vector _subject_charset = std::vector(); + std::vector _from = std::vector(); + std::vector _from_charset = std::vector(); + std::vector _to = std::vector(); + std::vector _to_charset = std::vector(); + std::vector _cc = std::vector(); + std::vector _cc_charset = std::vector(); + std::vector _msgNum = std::vector(); + std::vector _msgID = std::vector(); + std::vector _contentLanguage = std::vector(); + std::vector _acceptLanguage = std::vector(); + + std::vector _folders = std::vector(); + std::vector _flag = std::vector(); + std::vector _attachmentCount = std::vector(); + std::vector> _messageDataInfo = std::vector>(); + std::vector _totalAttachFileSize = std::vector(); + std::vector _downloadedByte = std::vector(); + std::vector _messageDataCount = std::vector(); + std::vector _errorMsg = std::vector(); + std::vector _error = std::vector(); + std::vector _rootCA = std::vector(); + + + std::unique_ptr _net = std::unique_ptr(new ESP32MailHTTPClient()); + + ReadStatus _cbData; +}; + +class SMTPData +{ +public: + SMTPData(); + ~SMTPData(); + + /* + + Set SMTP server login credentials + + @param host - SMTP server e.g. smtp.gmail.com + @param port - SMTP port. + @param loginEmail - The account Email. + @param loginPassword - The account password. + @rootCA - Root CA certificate base64 string + + */ + void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA); + void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword); + + /* + + Set STARTTLS mode to enable STARTTLS protocol + + @param starttls - bool flag that enables STARTTLS mode + + */ + void setSTARTTLS(bool starttls); + + /* + + Set debug print to serial + + @param debug - bool flag to enable debug + + */ + void setDebug(bool debug); + /* + + Set Sender info + + @param fromName - Sender's name + @param senderEmail - Sender's Email. + + */ + void setSender(const String &fromName, const String &senderEmail); + + /* + + Get Sender's name + + @return Sender's name String. + + */ + String getFromName(); + + /* + + Get Sender's Email + + @return Sender's Email String. + + */ + String getSenderEmail(); + + /* + + Set Email priority or importance + + @param priority - Number from 1 to 5, 1 for highest, 3 for normal and 5 for lowest priority + + */ + void setPriority(int priority); + + /* + + Set Email priority or importance + + @param priority - String (High, Normal or Low) + + */ + void setPriority(const String &priority); + + /* + + Get Email priority + + @return number represents Email priority (1 for highest, 3 for normal, 5 for low priority). + + */ + uint8_t getPriority(); + + /* + + Add one or more recipient + + @param email - Recipient Email String of one recipient. + + To add multiple recipients, call addRecipient for each recipient. + + */ + void addRecipient(const String &email); + + /* + + Remove recipient + + @param email - Recipient Email String. + + */ + void removeRecipient(const String &email); + + /* + + Remove recipient + + @param index - Index of recipients in Email object that previously added. + + */ + void removeRecipient(uint8_t index); + + /* + Clear all recipients. + */ + void clearRecipient(); + + /* + + Get one recipient + + @param index - Index of recipients. + + @return Recipient Email String at the index. + + */ + String getRecipient(uint8_t index); + + /* + + Get number of recipients + + @return Number of recipients. + + */ + uint8_t recipientCount(); + + /* + + Set the Email subject + + @param subject - The subject. + + */ + void setSubject(const String &subject); + + /* + + Get the Email subject + + @return Subject String. + + */ + String getSubject(); + + /* + + Set the Email message + + @param message - The message can be in normal text or html format. + @param htmlFormat - The html format flag, True for send the message as html format + + */ + void setMessage(const String &message, bool htmlFormat); + + void clrMessage(bool htmlFormat); + + void addMessage(const String &message); + + + /* + + Get the message + + @return Message String. + + */ + String getMessage(); + + /* + + Determine message is being send in html format + + @return Boolean status. + + */ + bool htmlFormat(); + + /* + + Add Carbon Copy (CC) Email + + @param email - The CC Email String. + + */ + void addCC(const String &email); + + /* + + Remove specified Carbon Copy (CC) Email + + @param email - The CC Email String to remove. + + */ + void removeCC(const String &email); + + /* + + Remove specified Carbon Copy (CC) Email + + @param index - The CC Email index to remove. + + */ + void removeCC(uint8_t index); + + /* + + Clear all Carbon Copy (CC) Emails + + */ + void clearCC(); + + /* + + Get Carbon Copy (CC) Email at specified index + + @param index - The CC Email index to get. + @return The CC Email string at the index. + + */ + String getCC(uint8_t index); + + /* + + Get the number of Carbon Copy (CC) Email + + @return Number of CC Emails. + + */ + uint8_t ccCount(); + + /* + Add Blind Carbon Copy (BCC) Email + + @param email - The BCC Email String. + + */ + void addBCC(const String &email); + + /* + + Remove specified Blind Carbon Copy (BCC) Email + + @param email - The BCC Email String to remove. + + */ + void removeBCC(const String &email); + + /* + + Remove specified Blind Carbon Copy (BCC) Email + + @param index - The BCC Email index to remove. + + */ + void removeBCC(uint8_t index); + + /* + + Clear all Blind Carbon Copy (BCC) Emails + + */ + void clearBCC(); + + /* + + Get Blind Carbon Copy (BCC) Email at specified index + + @param index - The BCC Email index to get. + + @return The BCC Email string at the index. + + */ + String getBCC(uint8_t index); + + /* + + Get the number of Blind Carbon Copy (BCC) Email + + @return Number of BCC Emails. + + */ + uint8_t bccCount(); + + /* + + Add attchement data (binary) from internal memory (flash or ram) + + @param fileName - The file name String that recipient can be saved. + @param mimeType - The MIME type of file (image/jpeg, image/png, text/plain...). Can be empty String. + @param data - The byte array of data (uint8_t) + @param size - The data length in byte. + + */ + void addAttachData(const String &fileName, const String &mimeType, uint8_t *data, size_t size); + + /* + + Remove specified attachment data + + @param fileName - The file name of the attachment data to remove. + + */ + void removeAttachData(const String &fileName); + + /* + + Remove specified attachment data + + @param index - The index of the attachment data (count only data type attachment) to remove. + + */ + void removeAttachData(uint8_t index); + + /* + + Get the number of attachment data + + @return Number of attach data. + + */ + uint8_t attachDataCount(); + + /* + + Add attchement file from SD card + + @param fileName - The file name String that recipient can be saved. + @param mimeType - The MIME type of file (image/jpeg, image/png, text/plain...). Can be omitted. + + */ + void addAttachFile(const String &filePath, const String &mimeType = ""); + + /* + + Remove specified attachment file from Email object + + @param fileName - The file name of the attachment file to remove. + + */ + void removeAttachFile(const String &filePath); + + /* + + Remove specified attachment file + + @param index - The index of the attachment file (count only file type attachment) to remove. + + */ + void removeAttachFile(uint8_t index); + + /* + + Set storage type for all attach files. + + @param storageType - The storage type to read attach file, MailClientStorageType::SD or MailClientStorageType::SPIFFS + + */ + void setFileStorageType(uint8_t storageType); + + /* + + Clear all attachment data + + */ + void clearAttachData(); + + /* + + Clear all attachment file. + + */ + void clearAttachFile(); + + /* + + Clear all attachments (both data and file type attachments). + + */ + void clearAttachment(); + + /* + + Get number of attachments (both data and file type attachments). + + @return Number of all attachemnts. + + */ + uint8_t attachFileCount(); + + /* + + Add one or more custom message header field. + + @param field - custom header String inform of FIELD: VALUE + + This header field will add to message header. + + + */ + void addCustomMessageHeader(const String &field); + + /* + + Remove one custom message header field that previously added. + + @param field - custom custom message header field String to remove. + + + */ + void removeCustomMessageHeader(const String &field); + + /* + + Remove one custom message header field that previously added by its index. + + @param index - custom message header field index (number) to remove. + + + */ + void removeCustomMessageHeader(uint8_t index); + + /* + + Clear all ccustom message header field that previously added. + + */ + void clearCustomMessageHeader(); + + /* + + Get the number of custom message header field that previously added. + + @return Number of custom message header field. + + */ + uint8_t CustomMessageHeaderCount(); + + /* + + Get custom message header field that previously added by index + + @param index - The custom message header field index to get. + + @return The custom message header field string at the index. + + */ + String getCustomMessageHeader(uint8_t index); + + + + /* + + Clear all data from Email object to free memory. + + */ + void empty(); + + /* + + Set the Email sending status callback function to Email object. + + @param sendCallback - The callback function that accept the sendStatusCallback param. + + */ + void setSendCallback(sendStatusCallback sendCallback); + + friend ESP32_MailClient; + friend attachmentData; + +protected: + int _priority = -1; + string _loginEmail = ""; + string _loginPassword = ""; + string _host = ""; + uint16_t _port = 0; + uint8_t _storageType = 1; + + string _fromName = ""; + string _senderEmail = ""; + string _subject = ""; + string _message = ""; + bool _htmlFormat = false; + bool _starttls = false; + bool _debug = false; + sendStatusCallback _sendCallback = NULL; + + std::vector _recipient = std::vector(); + std::vector _customMessageHeader = std::vector(); + std::vector _cc = std::vector(); + std::vector _bcc = std::vector(); + attachmentData _attach; + SendStatus _cbData; + std::vector _rootCA = std::vector(); + std::unique_ptr _net = std::unique_ptr(new ESP32MailHTTPClient()); + +}; + + +static void __attribute__((used)) ESP32MailDebug(const char *msg) +{ + + Serial.print(FPSTR("[DEBUG] - ")); + Serial.println(msg); + +} + +static void __attribute__((used)) ESP32MailDebugLine(const char *msg, bool newline) +{ + if (!newline) + Serial.print(FPSTR("[DEBUG] - ")); + + if (newline) + Serial.println(msg); + else + Serial.print(msg); +} + + +extern ESP32_MailClient MailClient; + +#endif //ESP32 + +#endif //ESP32_MailClient_H diff --git a/libesp32/ESP32-Mail-Client/src/RFC2047.cpp b/libesp32/ESP32-Mail-Client/src/RFC2047.cpp new file mode 100755 index 000000000..993c132e3 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/RFC2047.cpp @@ -0,0 +1,240 @@ +#ifndef RFC2047_CPP +#define RFC2047_CPP + +#ifdef ESP32 + +#include "RFC2047.h" + +RFC2047::RFC2047(){} + + +void RFC2047::rfc2047Decode(char *d, const char *s, size_t dlen){ + + const char *p, *q; + size_t n; + int found_encoded = 0; + + dlen--; /* save room for the terminal nul */ + + while (*s && dlen > 0) + { + if ((p = strstr (s, "=?")) == NULL || + (q = strchr (p + 2, '?')) == NULL || + (q = strchr (q + 1, '?')) == NULL || + (q = strstr (q + 1, "?=")) == NULL) + { + /* no encoded words */ + if (d != s) + strfcpy (d, s, dlen + 1); + return; + } + + if (p != s) + { + n = (size_t) (p - s); + /* ignore spaces between encoded words */ + if (!found_encoded || strspn (s, " \t\r\n") != n) + { + if (n > dlen) + n = dlen; + if (d != s) + memcpy (d, s, n); + d += n; + dlen -= n; + } + } + + rfc2047DecodeWord (d, p, dlen); + found_encoded = 1; + s = q + 2; + n = strlen (d); + dlen -= n; + d += n; + } + *d = 0; + +} + +void RFC2047::rfc2047DecodeWord(char *d, const char *s, size_t dlen){ + + char *p = safe_strdup (s); + char *pp = p; + char *pd = d; + size_t len = dlen; + int enc = 0, filter = 0, count = 0, c1, c2, c3, c4; + + while ((pp = strtok (pp, "?")) != NULL) + { + count++; + switch (count) + { + case 2: + if (strcasecmp (pp, Charset) != 0) + { + filter = 1; + } + break; + case 3: + if (toupper (*pp) == 'Q') + enc = ENCQUOTEDPRINTABLE; + else if (toupper (*pp) == 'B') + enc = ENCBASE64; + else + return; + break; + case 4: + if (enc == ENCQUOTEDPRINTABLE) + { + while (*pp && len > 0) + { + if (*pp == '_') + { + *pd++ = ' '; + len--; + } + else if (*pp == '=') + { + *pd++ = (hexval(pp[1]) << 4) | hexval(pp[2]); + len--; + pp += 2; + } + else + { + *pd++ = *pp; + len--; + } + pp++; + } + *pd = 0; + } + else if (enc == ENCBASE64) + { + while (*pp && len > 0) + { + c1 = base64val(pp[0]); + c2 = base64val(pp[1]); + *pd++ = (c1 << 2) | ((c2 >> 4) & 0x3); + if (--len == 0) break; + + if (pp[2] == '=') break; + + c3 = base64val(pp[2]); + *pd++ = ((c2 & 0xf) << 4) | ((c3 >> 2) & 0xf); + if (--len == 0) + break; + + if (pp[3] == '=') + break; + + c4 = base64val(pp[3]); + *pd++ = ((c3 & 0x3) << 6) | c4; + if (--len == 0) + break; + + pp += 4; + } + *pd = 0; + } + break; + } + pp = 0; + } + safe_free (&p); + if (filter) + { + + pd = d; + while (*pd) + { + if (!IsPrint (*pd)) + *pd = '?'; + pd++; + } + } + return; +} + + +void *RFC2047::safe_calloc (size_t nmemb, size_t size) +{ + void *p; + + if (!nmemb || !size) + return NULL; + if (!(p = calloc (nmemb, size))) + { + //out of memory + return NULL; + } + return p; +} + +void *RFC2047::safe_malloc (unsigned int siz) +{ + void *p; + + if (siz == 0) + return 0; + if ((p = (void *) malloc (siz)) == 0) + { + //out of memory + return NULL; + } + return (p); +} + +void RFC2047::safe_realloc (void **p, size_t siz) +{ + void *r; + + if (siz == 0) + { + if (*p) + { + free (*p); + *p = NULL; + } + return; + } + + if (*p) + r = (void *) realloc (*p, siz); + else + { + r = (void *) malloc (siz); + } + + if (!r) + { + //out of memory + return; + } + + *p = r; +} + +void RFC2047::safe_free (void *ptr) +{ + void **p = (void **)ptr; + if (*p) + { + free (*p); + *p = 0; + } +} + +char *RFC2047::safe_strdup (const char *s) +{ + char *p; + size_t l; + + if (!s || !*s) return 0; + l = strlen (s) + 1; + p = (char *)safe_malloc (l); + memcpy (p, s, l); + return (p); +} + +#endif //ESP32 + +#endif //RFC2047_CPP \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/src/RFC2047.h b/libesp32/ESP32-Mail-Client/src/RFC2047.h new file mode 100755 index 000000000..aeee0e12c --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/RFC2047.h @@ -0,0 +1,70 @@ + +#ifndef RFC2047_H +#define RFC2047_H + +#ifdef ESP32 + +#include + + +#define strfcpy(A,B,C) strncpy(A,B,C), *(A+(C)-1)=0 + +enum +{ + ENCOTHER, + ENC7BIT, + ENC8BIT, + ENCQUOTEDPRINTABLE, + ENCBASE64, + ENCBINARY +}; + +__attribute__((used)) static const char *Charset = "utf-8"; + +__attribute__((used)) static int Index_hex[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +__attribute__((used)) static int Index_64[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1}; + +#define IsPrint(c) (isprint((unsigned char)(c)) || \ + ((unsigned char)(c) >= 0xa0)) + +#define hexval(c) Index_hex[(unsigned int)(c)] +#define base64val(c) Index_64[(unsigned int)(c)] + +class RFC2047{ + + public: + RFC2047(); + void rfc2047Decode(char *d, const char *s, size_t dlen); + + + private: + void rfc2047DecodeWord(char *d, const char *s, size_t dlen); + void *safe_calloc (size_t nmemb, size_t size); + void *safe_malloc (unsigned int siz); + void safe_realloc (void **p, size_t siz); + void safe_free (void *ptr); + char *safe_strdup (const char *s); + + +}; + +#endif //ESP32 + +#endif //RFC2047_H \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.cpp b/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.cpp new file mode 100755 index 000000000..26460a604 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.cpp @@ -0,0 +1,397 @@ +/* + *Customized WiFiClientSecure.cpp to support STARTTLS protocol, version 1.0.1 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + WiFiClientSecureESP32.cpp - Client Secure class for ESP32 + Copyright (c) 2016 Hristo Gochkov All right reserved. + Additions Copyright (C) 2017 Evandro Luis Copercini. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef WiFiClientSecureESP32_CPP +#define WiFiClientSecureESP32_CPP + +#ifdef ESP32 + +#include "WiFiClientSecureESP32.h" +#include +#include +#include + +#undef connect +#undef write +#undef read + + +WiFiClientSecureESP32::WiFiClientSecureESP32() +{ + _connected = false; + + sslclient = new sslclient_context32; + ssl_init(sslclient); + sslclient->socket = -1; + sslclient->handshake_timeout = 120000; + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + next = NULL; +} + + +WiFiClientSecureESP32::WiFiClientSecureESP32(int sock) +{ + _connected = false; + _timeout = 0; + + sslclient = new sslclient_context32; + ssl_init(sslclient); + sslclient->socket = sock; + sslclient->handshake_timeout = 120000; + + if (sock >= 0) { + _connected = true; + } + + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + next = NULL; +} + +WiFiClientSecureESP32::WiFiClientSecureESP32(bool starttls) +{ + _connected = false; + + sslclient = new sslclient_context32; + ssl_init(sslclient); + sslclient->socket = -1; + sslclient->handshake_timeout = 120000; + sslclient->starttls = true; + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + next = NULL; +} + +WiFiClientSecureESP32::~WiFiClientSecureESP32() +{ + stop(); + delete sslclient; +} + +WiFiClientSecureESP32 &WiFiClientSecureESP32::operator=(const WiFiClientSecureESP32 &other) +{ + stop(); + sslclient->socket = other.sslclient->socket; + _connected = other._connected; + return *this; +} + +void WiFiClientSecureESP32::stop() +{ + if (sslclient->socket >= 0) { + close(sslclient->socket); + sslclient->socket = -1; + _connected = false; + _peek = -1; + } + stop_ssl_socket(sslclient, _CA_cert, _cert, _private_key); +} + +int WiFiClientSecureESP32::connect(IPAddress ip, uint16_t port) +{ + if (_pskIdent && _psKey) + return connect(ip, port, _pskIdent, _psKey); + return connect(ip, port, _CA_cert, _cert, _private_key); +} + +int WiFiClientSecureESP32::connect(IPAddress ip, uint16_t port, int32_t timeout){ + _timeout = timeout; + return connect(ip, port); +} + +int WiFiClientSecureESP32::connect(const char *host, uint16_t port) +{ + if (_pskIdent && _psKey) + return connect(host, port, _pskIdent, _psKey); + return connect(host, port, _CA_cert, _cert, _private_key); +} + +int WiFiClientSecureESP32::connect(const char *host, uint16_t port, int32_t timeout){ + _timeout = timeout; + return connect(host, port); +} + +int WiFiClientSecureESP32::connect(IPAddress ip, uint16_t port, const char *_CA_cert, const char *_cert, const char *_private_key) +{ + return connect(ip.toString().c_str(), port, _CA_cert, _cert, _private_key); +} + +int WiFiClientSecureESP32::connect(const char *host, uint16_t port, const char *_CA_cert, const char *_cert, const char *_private_key) +{ + if(_timeout > 0){ + sslclient->handshake_timeout = _timeout; + } + int ret = start_ssl_client(sslclient, host, port, _timeout, _CA_cert, _cert, _private_key, NULL, NULL); + _lastError = ret; + if (ret < 0) { + log_e("start_ssl_client: %d", ret); + stop(); + return 0; + } + _connected = true; + return 1; +} + +int WiFiClientSecureESP32::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey) { + return connect(ip.toString().c_str(), port,_pskIdent, _psKey); +} + +int WiFiClientSecureESP32::connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey) { + log_v("start_ssl_client with PSK"); + if(_timeout > 0){ + sslclient->handshake_timeout = _timeout; + } + int ret = start_ssl_client(sslclient, host, port, _timeout, NULL, NULL, NULL, _pskIdent, _psKey); + _lastError = ret; + if (ret < 0) { + log_e("start_ssl_client: %d", ret); + stop(); + return 0; + } + _connected = true; + return 1; +} + +int WiFiClientSecureESP32::peek(){ + if(_peek >= 0){ + return _peek; + } + _peek = timedRead(); + return _peek; +} + +size_t WiFiClientSecureESP32::write(uint8_t data) +{ + return write(&data, 1); +} + +int WiFiClientSecureESP32::read() +{ + uint8_t data = -1; + int res = read(&data, 1); + if (res < 0) { + return res; + } + return data; +} + +size_t WiFiClientSecureESP32::write(const uint8_t *buf, size_t size) +{ + if (!_connected) { + return 0; + } + int res = send_ssl_data(sslclient, buf, size); + if (res < 0) { + stop(); + res = 0; + } + return res; +} + +int WiFiClientSecureESP32::read(uint8_t *buf, size_t size) +{ + int peeked = 0; + int avail = available(); + if ((!buf && size) || avail <= 0) { + return -1; + } + if(!size){ + return 0; + } + if(_peek >= 0){ + buf[0] = _peek; + _peek = -1; + size--; + avail--; + if(!size || !avail){ + return 1; + } + buf++; + peeked = 1; + } + + int res = get_ssl_receive(sslclient, buf, size); + if (res < 0) { + stop(); + return peeked?peeked:res; + } + return res + peeked; +} + +int WiFiClientSecureESP32::available() +{ + int peeked = (_peek >= 0); + if (!_connected) { + return peeked; + } + int res = data_to_read(sslclient); + if (res < 0) { + stop(); + return peeked?peeked:res; + } + return res+peeked; +} + +uint8_t WiFiClientSecureESP32::connected() +{ + uint8_t dummy = 0; + read(&dummy, 0); + + return _connected; +} + +void WiFiClientSecureESP32::setCACert (const char *rootCA) +{ + _CA_cert = rootCA; +} + +void WiFiClientSecureESP32::setCertificate (const char *client_ca) +{ + _cert = client_ca; +} + +void WiFiClientSecureESP32::setPrivateKey (const char *private_key) +{ + _private_key = private_key; +} + +void WiFiClientSecureESP32::setPreSharedKey(const char *pskIdent, const char *psKey) { + _pskIdent = pskIdent; + _psKey = psKey; +} + +bool WiFiClientSecureESP32::verify(const char* fp, const char* domain_name) +{ + if (!sslclient) + return false; + + return verify_ssl_fingerprint(sslclient, fp, domain_name); +} + +char *WiFiClientSecureESP32::_streamLoad(Stream& stream, size_t size) { + static char *dest = nullptr; + if(dest) { + free(dest); + } + dest = (char*)malloc(size); + if (!dest) { + return nullptr; + } + if (size != stream.readBytes(dest, size)) { + free(dest); + dest = nullptr; + } + return dest; +} + +bool WiFiClientSecureESP32::loadCACert(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setCACert(dest); + ret = true; + } + return ret; +} + +bool WiFiClientSecureESP32::loadCertificate(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setCertificate(dest); + ret = true; + } + return ret; +} + +bool WiFiClientSecureESP32::loadPrivateKey(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setPrivateKey(dest); + ret = true; + } + return ret; +} + +int WiFiClientSecureESP32::lastError(char *buf, const size_t size) +{ + if (!_lastError) { + return 0; + } + char error_buf[100]; + mbedtls_strerror(_lastError, error_buf, 100); + snprintf(buf, size, "%s", error_buf); + return _lastError; +} + +void WiFiClientSecureESP32::setHandshakeTimeout(unsigned long handshake_timeout) +{ + sslclient->handshake_timeout = handshake_timeout * 1000; +} + +void WiFiClientSecureESP32::setSTARTTLS(bool starttls) +{ + sslclient->starttls = starttls; +} + +void WiFiClientSecureESP32::setDebugCB(DebugMsgCallback cb) +{ + sslclient->_debugCallback = std::move(cb); +} + +#endif //ESP32 + +#endif //WiFiClientSecureESP32_CPP \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.h b/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.h new file mode 100755 index 000000000..2a940c391 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.h @@ -0,0 +1,145 @@ + +/* + *Customized WiFiClientSecure.h to support STARTTLS protocol, version 1.0.1 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + WiFiClientSecureESP32.h - Base class that provides Client SSL to ESP32 + Copyright (c) 2011 Adrian McEwen. All right reserved. + Additions Copyright (C) 2017 Evandro Luis Copercini. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef WiFiClientSecureESP32_H +#define WiFiClientSecureESP32_H + +#ifdef ESP32 + +#include "Arduino.h" +#include "IPAddress.h" +#include +#include "ssl_client32.h" + +typedef void (*DebugMsgCallback)(const char* msg); + +class WiFiClientSecureESP32 : public WiFiClient +{ +protected: + sslclient_context32 *sslclient; + + int _lastError = 0; + int _peek = -1; + int _timeout = 0; + const char *_CA_cert; + const char *_cert; + const char *_private_key; + const char *_pskIdent; // identity for PSK cipher suites + const char *_psKey; // key in hex for PSK cipher suites + DebugMsgCallback _debugCallback = NULL; + +public: + WiFiClientSecureESP32 *next; + WiFiClientSecureESP32(); + WiFiClientSecureESP32(int socket); + WiFiClientSecureESP32(bool starttls); + ~WiFiClientSecureESP32(); + int connect(IPAddress ip, uint16_t port); + int connect(IPAddress ip, uint16_t port, int32_t timeout); + int connect(const char *host, uint16_t port); + int connect(const char *host, uint16_t port, int32_t timeout); + int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); + int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); + int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey); + int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey); + int peek(); + size_t write(uint8_t data); + size_t write(const uint8_t *buf, size_t size); + int available(); + int read(); + int read(uint8_t *buf, size_t size); + void flush() {} + void stop(); + uint8_t connected(); + int lastError(char *buf, const size_t size); + void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex + void setCACert(const char *rootCA); + void setCertificate(const char *client_ca); + void setPrivateKey (const char *private_key); + bool loadCACert(Stream& stream, size_t size); + bool loadCertificate(Stream& stream, size_t size); + bool loadPrivateKey(Stream& stream, size_t size); + bool verify(const char* fingerprint, const char* domain_name); + void setHandshakeTimeout(unsigned long handshake_timeout); + void setSTARTTLS(bool starttls); + void setDebugCB(DebugMsgCallback cb); + + operator bool() + { + return connected(); + } + WiFiClientSecureESP32 &operator=(const WiFiClientSecureESP32 &other); + bool operator==(const bool value) + { + return bool() == value; + } + bool operator!=(const bool value) + { + return bool() != value; + } + bool operator==(const WiFiClientSecureESP32 &); + bool operator!=(const WiFiClientSecureESP32 &rhs) + { + return !this->operator==(rhs); + }; + + int socket() + { + return sslclient->socket = -1; + } + +private: + char *_streamLoad(Stream& stream, size_t size); + + //friend class WiFiServer; + using Print::write; +}; + +#endif //ESP32 + +#endif //WiFiClientSecureESP32_H + + diff --git a/libesp32/ESP32-Mail-Client/src/ssl_client32.cpp b/libesp32/ESP32-Mail-Client/src/ssl_client32.cpp new file mode 100755 index 000000000..06e3f11b1 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ssl_client32.cpp @@ -0,0 +1,853 @@ +/* + *Customized ssl_client.cpp to support STARTTLS protocol, version 1.0.3 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* Provide SSL/TLS functions to ESP32 with Arduino IDE +* +* Adapted from the ssl_client1 example of mbedtls. +* +* Original Copyright (C) 2006-2015, ARM Limited, All Rights Reserved, Apache 2.0 License. +* Additions Copyright (C) 2017 Evandro Luis Copercini, Apache 2.0 License. +*/ + +#ifndef SSL_CLIENT32_CPP +#define SSL_CLIENT32_CPP + +#ifdef ESP32 + +#include "Arduino.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ssl_client32.h" +#include "WiFi.h" + +const char *pers32 = "esp32-tls"; + +static int handle_error(int err) +{ + if (err == -30848) + { + return err; + } +#ifdef MBEDTLS_ERROR_C + char error_buf[100]; + mbedtls_strerror(err, error_buf, 100); + log_e("%s", error_buf); +#endif + log_e("MbedTLS message code: %d", err); + return err; +} + +void ssl_init(sslclient_context32 *ssl_client) +{ + mbedtls_ssl_init(&ssl_client->ssl_ctx); + mbedtls_ssl_config_init(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_init(&ssl_client->drbg_ctx); + mbedtls_net_init(&ssl_client->server_fd); +} + +int start_ssl_client(sslclient_context32 *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey) +{ + char buf[512]; + int ret, flags; + int enable = 1; + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_2, ssl_client); + + log_v("Free internal heap before TLS %u", ESP.getFreeHeap()); + + log_v("Starting socket"); + ssl_client->socket = -1; + + ssl_client->socket = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (ssl_client->socket < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_3, ssl_client); + log_e("ERROR opening socket"); + return ssl_client->socket; + } + + IPAddress srv((uint32_t)0); + if (!WiFiGenericClass::hostByName(host, srv)) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_4, ssl_client); + return -1; + } + + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = srv; + serv_addr.sin_port = htons(port); + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_5, ssl_client); + + if (lwip_connect(ssl_client->socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) + { + if (timeout <= 0) + { + timeout = 30000; + } + lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)); + lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)); + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_6, ssl_client); + } + else + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_7, ssl_client); + log_e("Connect to Server failed!"); + return -1; + } + + fcntl(ssl_client->socket, F_SETFL, fcntl(ssl_client->socket, F_GETFL, 0) | O_NONBLOCK); + + if (ssl_client->starttls && (port == 25 || port == 587 || port == 143)) + { + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_8, ssl_client); + + if ((ret = starttlsHandshake(ssl_client, port)) != 0) + { + log_e("STARTTLS failed!"); + return -1; + } + } + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_9, ssl_client); + + log_v("Seeding the random number generator"); + mbedtls_entropy_init(&ssl_client->entropy_ctx); + + ret = mbedtls_ctr_drbg_seed(&ssl_client->drbg_ctx, mbedtls_entropy_func, + &ssl_client->entropy_ctx, (const unsigned char *)pers32, strlen(pers32)); + if (ret < 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + + return handle_error(ret); + } + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_10, ssl_client); + + log_v("Setting up the SSL/TLS structure..."); + + if ((ret = mbedtls_ssl_config_defaults(&ssl_client->ssl_conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + return handle_error(ret); + } + + // MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and + // MBEDTLS_SSL_VERIFY_NONE if not. + + if (rootCABuff != NULL) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_11, ssl_client); + log_v("Loading CA cert"); + mbedtls_x509_crt_init(&ssl_client->ca_cert); + mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); + ret = mbedtls_x509_crt_parse(&ssl_client->ca_cert, (const unsigned char *)rootCABuff, strlen(rootCABuff) + 1); + mbedtls_ssl_conf_ca_chain(&ssl_client->ssl_conf, &ssl_client->ca_cert, NULL); + //mbedtls_ssl_conf_verify(&ssl_client->ssl_ctx, my_verify, NULL ); + if (ret < 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + return handle_error(ret); + } + } + else if (pskIdent != NULL && psKey != NULL) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_12, ssl_client); + log_v("Setting up PSK"); + // convert PSK from hex to binary + if ((strlen(psKey) & 1) != 0 || strlen(psKey) > 2 * MBEDTLS_PSK_MAX_LEN) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_13, ssl_client); + log_e("pre-shared key not valid hex or too long"); + return -1; + } + unsigned char psk[MBEDTLS_PSK_MAX_LEN]; + size_t psk_len = strlen(psKey) / 2; + for (int j = 0; j < strlen(psKey); j += 2) + { + char c = psKey[j]; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + c -= 'a' - 10; + else + return -1; + psk[j / 2] = c << 4; + c = psKey[j + 1]; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + c -= 'a' - 10; + else + return -1; + psk[j / 2] |= c; + } + // set mbedtls config + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_14, ssl_client); + ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, psk, psk_len, + (const unsigned char *)pskIdent, strlen(pskIdent)); + if (ret != 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + log_e("mbedtls_ssl_conf_psk returned %d", ret); + return handle_error(ret); + } + } + else + { + + mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE); + log_i("WARNING: Use certificates for a more secure communication!"); + } + + if (cli_cert != NULL && cli_key != NULL) + { + + mbedtls_x509_crt_init(&ssl_client->client_cert); + mbedtls_pk_init(&ssl_client->client_key); + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_15, ssl_client); + + log_v("Loading CRT cert"); + + ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1); + if (ret < 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + return handle_error(ret); + } + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_16, ssl_client); + + log_v("Loading private key"); + ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen(cli_key) + 1, NULL, 0); + + if (ret != 0) + { + return handle_error(ret); + } + + mbedtls_ssl_conf_own_cert(&ssl_client->ssl_conf, &ssl_client->client_cert, &ssl_client->client_key); + } + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_17, ssl_client); + + log_v("Setting hostname for TLS session..."); + + // Hostname set here should match CN in server certificate + if ((ret = mbedtls_ssl_set_hostname(&ssl_client->ssl_ctx, host)) != 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + return handle_error(ret); + } + + mbedtls_ssl_conf_rng(&ssl_client->ssl_conf, mbedtls_ctr_drbg_random, &ssl_client->drbg_ctx); + + if ((ret = mbedtls_ssl_setup(&ssl_client->ssl_ctx, &ssl_client->ssl_conf)) != 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + + return handle_error(ret); + } + + mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL); + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_18, ssl_client); + + log_v("Performing the SSL/TLS handshake..."); + unsigned long handshake_start_time = millis(); + while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) + { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + return handle_error(ret); + } + if ((millis() - handshake_start_time) > ssl_client->handshake_timeout) + return -1; + vTaskDelay(10 / portTICK_PERIOD_MS); + } + + if (cli_cert != NULL && cli_key != NULL) + { + log_d("Protocol is %s Ciphersuite is %s", mbedtls_ssl_get_version(&ssl_client->ssl_ctx), mbedtls_ssl_get_ciphersuite(&ssl_client->ssl_ctx)); + if ((ret = mbedtls_ssl_get_record_expansion(&ssl_client->ssl_ctx)) >= 0) + { + + log_d("Record expansion is %d", ret); + } + else + { + log_w("Record expansion is unknown (compression)"); + } + } + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_19, ssl_client); + + log_v("Verifying peer X.509 certificate..."); + + if ((flags = mbedtls_ssl_get_verify_result(&ssl_client->ssl_ctx)) != 0) + { + bzero(buf, sizeof(buf)); + mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags); + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_20, ssl_client); + log_e("Failed to verify peer certificate! verification info: %s", buf); + stop_ssl_socket(ssl_client, rootCABuff, cli_cert, cli_key); //It's not safe continue. + return handle_error(ret); + } + else + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_21, ssl_client); + log_v("Certificate verified."); + } + + if (rootCABuff != NULL) + { + mbedtls_x509_crt_free(&ssl_client->ca_cert); + } + + if (cli_cert != NULL) + { + mbedtls_x509_crt_free(&ssl_client->client_cert); + } + + if (cli_key != NULL) + { + mbedtls_pk_free(&ssl_client->client_key); + } + + log_v("Free internal heap after TLS %u", ESP.getFreeHeap()); + + return ssl_client->socket; +} + +void stop_ssl_socket(sslclient_context32 *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key) +{ + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_22, ssl_client); + log_v("Cleaning SSL connection."); + + if (ssl_client->socket >= 0) + { + close(ssl_client->socket); + ssl_client->socket = -1; + } + + mbedtls_ssl_free(&ssl_client->ssl_ctx); + mbedtls_ssl_config_free(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx); + mbedtls_entropy_free(&ssl_client->entropy_ctx); +} + +int data_to_read(sslclient_context32 *ssl_client) +{ + int ret, res; + ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, NULL, 0); + //log_e("RET: %i",ret); //for low level debug + res = mbedtls_ssl_get_bytes_avail(&ssl_client->ssl_ctx); + //log_e("RES: %i",res); //for low level debug + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) + { + if (ssl_client->_debugCallback) + { + char *buf = new char[512]; + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + delete[] buf; + } + return handle_error(ret); + } + + return res; +} + +int send_ssl_data(sslclient_context32 *ssl_client, const uint8_t *data, uint16_t len) +{ + + log_v("Writing HTTP request..."); //for low level debug + int ret = -1; + + while ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0) + { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) + { + return handle_error(ret); + } + } + + len = ret; + //log_v("%d bytes written", len); //for low level debug + return ret; +} + +int get_ssl_receive(sslclient_context32 *ssl_client, uint8_t *data, int length) +{ + + //log_d( "Reading HTTP response..."); //for low level debug + int ret = -1; + + ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, data, length); + + //log_v( "%d bytes read", ret); //for low level debug + return ret; +} + +static bool parseHexNibble(char pb, uint8_t *res) +{ + if (pb >= '0' && pb <= '9') + { + *res = (uint8_t)(pb - '0'); + return true; + } + else if (pb >= 'a' && pb <= 'f') + { + *res = (uint8_t)(pb - 'a' + 10); + return true; + } + else if (pb >= 'A' && pb <= 'F') + { + *res = (uint8_t)(pb - 'A' + 10); + return true; + } + return false; +} + +// Compare a name from certificate and domain name, return true if they match +static bool matchName(const std::string &name, const std::string &domainName) +{ + size_t wildcardPos = name.find('*'); + if (wildcardPos == std::string::npos) + { + // Not a wildcard, expect an exact match + return name == domainName; + } + + size_t firstDotPos = name.find('.'); + if (wildcardPos > firstDotPos) + { + // Wildcard is not part of leftmost component of domain name + // Do not attempt to match (rfc6125 6.4.3.1) + return false; + } + if (wildcardPos != 0 || firstDotPos != 1) + { + // Matching of wildcards such as baz*.example.com and b*z.example.com + // is optional. Maybe implement this in the future? + return false; + } + size_t domainNameFirstDotPos = domainName.find('.'); + if (domainNameFirstDotPos == std::string::npos) + { + return false; + } + return domainName.substr(domainNameFirstDotPos) == name.substr(firstDotPos); +} + +// Verifies certificate provided by the peer to match specified SHA256 fingerprint +bool verify_ssl_fingerprint(sslclient_context32 *ssl_client, const char *fp, const char *domain_name) +{ + // Convert hex string to byte array + uint8_t fingerprint_local[32]; + int len = strlen(fp); + int pos = 0; + for (size_t i = 0; i < sizeof(fingerprint_local); ++i) + { + while (pos < len && ((fp[pos] == ' ') || (fp[pos] == ':'))) + { + ++pos; + } + if (pos > len - 2) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_23, ssl_client); + log_d("pos:%d len:%d fingerprint too short", pos, len); + return false; + } + uint8_t high, low; + if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos + 1], &low)) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_24, ssl_client); + log_d("pos:%d len:%d invalid hex sequence: %c%c", pos, len, fp[pos], fp[pos + 1]); + return false; + } + pos += 2; + fingerprint_local[i] = low | (high << 4); + } + + // Get certificate provided by the peer + const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); + + if (!crt) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_25, ssl_client); + log_d("could not fetch peer certificate"); + return false; + } + + // Calculate certificate's SHA256 fingerprint + uint8_t fingerprint_remote[32]; + mbedtls_sha256_context sha256_ctx; + mbedtls_sha256_init(&sha256_ctx); + mbedtls_sha256_starts(&sha256_ctx, false); + mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len); + mbedtls_sha256_finish(&sha256_ctx, fingerprint_remote); + + // Check if fingerprints match + if (memcmp(fingerprint_local, fingerprint_remote, 32)) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_26, ssl_client); + log_d("fingerprint doesn't match"); + return false; + } + + // Additionally check if certificate has domain name if provided + if (domain_name) + return verify_ssl_dn(ssl_client, domain_name); + else + return true; +} + +// Checks if peer certificate has specified domain in CN or SANs +bool verify_ssl_dn(sslclient_context32 *ssl_client, const char *domain_name) +{ + log_d("domain name: '%s'", (domain_name) ? domain_name : "(null)"); + std::string domain_name_str(domain_name); + std::transform(domain_name_str.begin(), domain_name_str.end(), domain_name_str.begin(), ::tolower); + + // Get certificate provided by the peer + const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); + + // Check for domain name in SANs + const mbedtls_x509_sequence *san = &crt->subject_alt_names; + while (san != nullptr) + { + std::string san_str((const char *)san->buf.p, san->buf.len); + std::transform(san_str.begin(), san_str.end(), san_str.begin(), ::tolower); + + if (matchName(san_str, domain_name_str)) + return true; + + log_d("SAN '%s': no match", san_str.c_str()); + + // Fetch next SAN + san = san->next; + } + + // Check for domain name in CN + const mbedtls_asn1_named_data *common_name = &crt->subject; + while (common_name != nullptr) + { + // While iterating through DN objects, check for CN object + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid)) + { + std::string common_name_str((const char *)common_name->val.p, common_name->val.len); + + if (matchName(common_name_str, domain_name_str)) + return true; + + log_d("CN '%s': not match", common_name_str.c_str()); + } + + // Fetch next DN object + common_name = common_name->next; + } + + return false; +} + +int starttlsHandshake(sslclient_context32 *ssl_client, int port) +{ + + int ret = 0; + size_t msgLen = 100; + size_t bufLen = 512; + char *buf = new char[bufLen]; + char *hMsg = new char[msgLen]; + + fd_set readset; + fd_set writeset; + fd_set errset; + + struct timeval tv; + + FD_ZERO(&readset); + FD_SET(ssl_client->socket, &readset); + FD_ZERO(&writeset); + FD_SET(ssl_client->socket, &writeset); + + FD_ZERO(&errset); + FD_SET(ssl_client->socket, &errset); + + tv.tv_sec = 1; + tv.tv_usec = 0; + + ret = lwip_select(ssl_client->socket, &readset, &writeset, &errset, &tv); + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_27, ssl_client); + + goto starttls_exit; + } + + ret = read(ssl_client->socket, buf, bufLen); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_28, ssl_client); + goto starttls_exit; + } + else + { + if (ssl_client->_debugCallback) + ssl_client->_debugCallback(buf); + } + + if (port == 587 || port == 25) + { + + memset(hMsg, 0, msgLen); + strcpy_P(hMsg, ESP32_SSL_CLIENT_STR_29); + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_30, ssl_client); + ret = lwip_write(ssl_client->socket, hMsg, strlen(hMsg)); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_31, ssl_client); + goto starttls_exit; + } + + ret = lwip_select(ssl_client->socket, &readset, &writeset, &errset, &tv); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_32, ssl_client); + goto starttls_exit; + } + + memset(buf, 0, bufLen); + ret = lwip_read(ssl_client->socket, buf, bufLen); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_33, ssl_client); + goto starttls_exit; + } + else + { + if (ssl_client->_debugCallback) + ssl_client->_debugCallback(buf); + } + } + + memset(hMsg, 0, msgLen); + strcpy_P(hMsg, ESP32_SSL_CLIENT_STR_34); + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_35, ssl_client); + ret = lwip_write(ssl_client->socket, hMsg, strlen(hMsg)); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_36, ssl_client); + goto starttls_exit; + } + + ret = lwip_select(ssl_client->socket, &readset, &writeset, &errset, &tv); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_37, ssl_client); + goto starttls_exit; + } + + memset(buf, 0, bufLen); + ret = lwip_read(ssl_client->socket, buf, bufLen); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_38, ssl_client); + goto starttls_exit; + } + else + { + if (ssl_client->_debugCallback) + ssl_client->_debugCallback(buf); + } + + delete[] buf; + delete[] hMsg; + + return 0; + +starttls_exit: + + delete[] buf; + delete[] hMsg; + + return -1; +} + +void ESP32SSLClientDebugInfo(PGM_P info, sslclient_context32 *ssl_client) +{ + size_t dbgInfoLen = strlen_P(info) + 1; + char *dbgInfo = new char[dbgInfoLen]; + memset(dbgInfo, 0, dbgInfoLen); + strcpy_P(dbgInfo, info); + ssl_client->_debugCallback(dbgInfo); + delete[] dbgInfo; +} + +#endif //ESP32 + +#endif //SSL_CLIENT32_CPP \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/src/ssl_client32.h b/libesp32/ESP32-Mail-Client/src/ssl_client32.h new file mode 100755 index 000000000..3e2d3b09a --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ssl_client32.h @@ -0,0 +1,116 @@ +/* + *Customized ssl_client.h to support STARTTLS protocol, version 1.0.3 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* Provide SSL/TLS functions to ESP32 with Arduino IDE + * by Evandro Copercini - 2017 - Apache 2.0 License + */ + +#ifndef SSL_CLIENT32_H +#define SSL_CLIENT32_H + +#ifdef ESP32 + +#include "mbedtls/platform.h" +#include "mbedtls/net.h" +#include "mbedtls/debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" + +static const char ESP32_SSL_CLIENT_STR_1[] PROGMEM = "ERROR: "; +static const char ESP32_SSL_CLIENT_STR_2[] PROGMEM = "INFO: starting socket"; +static const char ESP32_SSL_CLIENT_STR_3[] PROGMEM = "ERROR: opening socket"; +static const char ESP32_SSL_CLIENT_STR_4[] PROGMEM = "ERROR: could not get ip from host"; +static const char ESP32_SSL_CLIENT_STR_5[] PROGMEM = "INFO: connecting to Server..."; +static const char ESP32_SSL_CLIENT_STR_6[] PROGMEM = "INFO: server connected"; +static const char ESP32_SSL_CLIENT_STR_7[] PROGMEM = "ERROR: connect to Server failed!"; +static const char ESP32_SSL_CLIENT_STR_8[] PROGMEM = "INFO: begin STARTTLS handshake"; +static const char ESP32_SSL_CLIENT_STR_9[] PROGMEM = "INFO: seeding the random number generator"; +static const char ESP32_SSL_CLIENT_STR_10[] PROGMEM = "INFO: setting up the SSL/TLS structure..."; +static const char ESP32_SSL_CLIENT_STR_11[] PROGMEM = "INFO: loading CA cert"; +static const char ESP32_SSL_CLIENT_STR_12[] PROGMEM = "INFO: setting up PSK"; +static const char ESP32_SSL_CLIENT_STR_13[] PROGMEM = "ERROR: pre-shared key not valid hex or too long"; +static const char ESP32_SSL_CLIENT_STR_14[] PROGMEM = "INFO: set mbedtls config"; +static const char ESP32_SSL_CLIENT_STR_15[] PROGMEM = "INFO: loading CRT cert"; +static const char ESP32_SSL_CLIENT_STR_16[] PROGMEM = "INFO: loading private key"; +static const char ESP32_SSL_CLIENT_STR_17[] PROGMEM = "INFO: setting hostname for TLS session..."; +static const char ESP32_SSL_CLIENT_STR_18[] PROGMEM = "INFO: performing the SSL/TLS handshake..."; +static const char ESP32_SSL_CLIENT_STR_19[] PROGMEM = "INFO: verifying peer X.509 certificate..."; +static const char ESP32_SSL_CLIENT_STR_20[] PROGMEM = "ERROR: failed to verify peer certificate!"; +static const char ESP32_SSL_CLIENT_STR_21[] PROGMEM = "INFO: certificate verified"; +static const char ESP32_SSL_CLIENT_STR_22[] PROGMEM = "INFO: cleaning SSL connection"; +static const char ESP32_SSL_CLIENT_STR_23[] PROGMEM = "ERROR: fingerprint too short"; +static const char ESP32_SSL_CLIENT_STR_24[] PROGMEM = "ERROR: invalid hex sequence"; +static const char ESP32_SSL_CLIENT_STR_25[] PROGMEM = "ERROR: could not fetch peer certificate"; +static const char ESP32_SSL_CLIENT_STR_26[] PROGMEM = "ERROR: fingerprint doesn't match"; +static const char ESP32_SSL_CLIENT_STR_27[] PROGMEM = "ERROR: waiting incoming data failed!"; +static const char ESP32_SSL_CLIENT_STR_28[] PROGMEM = "ERROR: reading incoming data failed!"; +static const char ESP32_SSL_CLIENT_STR_29[] PROGMEM = "EHLO DUDE\r\n"; +static const char ESP32_SSL_CLIENT_STR_30[] PROGMEM = "INFO: send SMTP command extended HELO"; +static const char ESP32_SSL_CLIENT_STR_31[] PROGMEM = "ERROR: send SMTP command failed!"; +static const char ESP32_SSL_CLIENT_STR_32[] PROGMEM = "ERROR: waiting incoming data failed!"; +static const char ESP32_SSL_CLIENT_STR_33[] PROGMEM = "ERROR: reading incoming data failed!"; +static const char ESP32_SSL_CLIENT_STR_34[] PROGMEM = "STARTTLS\r\n"; +static const char ESP32_SSL_CLIENT_STR_35[] PROGMEM = "INFO: send STARTTLS protocol command"; +static const char ESP32_SSL_CLIENT_STR_36[] PROGMEM = "ERROR: send STARTTLS protocol command failed!"; +static const char ESP32_SSL_CLIENT_STR_37[] PROGMEM = "ERROR: waiting incoming data failed!"; +static const char ESP32_SSL_CLIENT_STR_38[] PROGMEM = "ERROR: reading incoming data failed!"; + +typedef void (*DebugMsgCallback)(const char *msg); + +typedef struct sslclient_context32 { + int socket; + bool starttls; + mbedtls_ssl_context ssl_ctx; + mbedtls_ssl_config ssl_conf; + mbedtls_net_context server_fd; + + mbedtls_ctr_drbg_context drbg_ctx; + mbedtls_entropy_context entropy_ctx; + + mbedtls_x509_crt ca_cert; + mbedtls_x509_crt client_cert; + mbedtls_pk_context client_key; + DebugMsgCallback _debugCallback; + + unsigned long handshake_timeout; +} sslclient_context32; + + +void ssl_init(sslclient_context32 *ssl_client); +int start_ssl_client(sslclient_context32 *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey); +void stop_ssl_socket(sslclient_context32 *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key); +int data_to_read(sslclient_context32 *ssl_client); +int send_ssl_data(sslclient_context32 *ssl_client, const uint8_t *data, uint16_t len); +int get_ssl_receive(sslclient_context32 *ssl_client, uint8_t *data, int length); +bool verify_ssl_fingerprint(sslclient_context32 *ssl_client, const char* fp, const char* domain_name); +bool verify_ssl_dn(sslclient_context32 *ssl_client, const char* domain_name); +int starttlsHandshake(sslclient_context32 *ssl_client, int port); +void ESP32SSLClientDebugInfo(PGM_P info, sslclient_context32 *ssl_client); + +#endif //ESP32 + +#endif //SSL_CLIENT32_H diff --git a/libesp32/ESP32-to-ESP8266-compat/README.adoc b/libesp32/ESP32-to-ESP8266-compat/README.adoc new file mode 100644 index 000000000..abf17a277 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/README.adoc @@ -0,0 +1,19 @@ +Library for ESP32 with Tasmota + +This Class is for compatibility with esp8266 code + +== License == + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/libesp32/ESP32-to-ESP8266-compat/keywords.txt b/libesp32/ESP32-to-ESP8266-compat/keywords.txt new file mode 100644 index 000000000..3422d1305 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/keywords.txt @@ -0,0 +1,24 @@ +####################################### +# Syntax Coloring Map For Test +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +A4988_ESP32Compat KEYWORD1 A4988_ESP32Compat + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +doMove KEYWORD2 +doRotate KEYWORD2 +setRPM KEYWORD2 +setSPR KEYWORD2 +setMIS KEYWORD2 +version KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libesp32/ESP32-to-ESP8266-compat/library.properties b/libesp32/ESP32-to-ESP8266-compat/library.properties new file mode 100644 index 000000000..5d5e39166 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/library.properties @@ -0,0 +1,9 @@ +name=ESP32-to-ESP8266-compat +version=0.0.2 +author=Jörg Schüler-Maroldt +maintainer=Jörg Schüler-Maroldt +sentence=Allows Tasmota to compile for esp32 +paragraph=Allows Tasmota to compile for esp32 +category=ESP32 +url= +architectures=* diff --git a/libesp32/ESP32-to-ESP8266-compat/src/AddrList.h b/libesp32/ESP32-to-ESP8266-compat/src/AddrList.h new file mode 100644 index 000000000..cc32ea232 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/AddrList.h @@ -0,0 +1,233 @@ +/* + AddrList.h - cycle through lwIP netif's ip addresses like a c++ list + Copyright (c) 2018 david gauchard. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + This class allows to explore all configured IP addresses + in lwIP netifs, with that kind of c++ loop: + + for (auto a: addrList) + out.printf("IF='%s' index=%d legacy=%d IPv4=%d local=%d hostname='%s' addr= %s\n", + a.iface().c_str(), + a.ifnumber(), + a.addr().isLegacy(), + a.addr().isV4(), + a.addr().isLocal(), + a.hostname().c_str(), + a.addr().toString().c_str()); + + This loop: + + while (WiFi.status() != WL_CONNECTED()) { + Serial.print('.'); + delay(500); + } + + can be replaced by: + + for (bool configured = false; !configured; ) { + for (auto iface: addrList) + if ((configured = !iface.addr().isLocal()) + break; + Serial.print('.'); + delay(500); + } + + waiting for an IPv6 global address: + + for (bool configured = false; !configured; ) { + for (auto iface: addrList) + if ((configured = ( !iface.addr().isV4() + && !iface.addr().isLocal()))) + break; + Serial.print('.'); + delay(500); + } + + waiting for an IPv6 global address, on a specific interface: + + for (bool configured = false; !configured; ) { + for (auto iface: addrList) + if ((configured = ( !iface.addr().isV4() + && !iface.addr().isLocal() + && iface.ifnumber() == STATION_IF))) + break; + Serial.print('.'); + delay(500); + } +*/ + +#ifndef __ADDRLIST_H +#define __ADDRLIST_H + +#include +#include + +#if LWIP_IPV6 +#define IF_NUM_ADDRESSES (1 + LWIP_IPV6_NUM_ADDRESSES) +#else +#define IF_NUM_ADDRESSES (1) +#endif + + +namespace esp8266 +{ + +namespace AddressListImplementation +{ + + +struct netifWrapper +{ + netifWrapper (netif* netif) : _netif(netif), _num(-1) {} + netifWrapper (const netifWrapper& o) : _netif(o._netif), _num(o._num) {} + + netifWrapper& operator= (const netifWrapper& o) + { + _netif = o._netif; + _num = o._num; + return *this; + } + + bool equal(const netifWrapper& o) + { + return _netif == o._netif && (!_netif || _num == o._num); + } + + // address properties + class IPAddress4 : public IPAddress + { + public: + bool isV6() const + { + return false; + } + bool isLocal() const + { + return false; + } + }; + IPAddress4 addr () const { return ipFromNetifNum(); } + bool isLegacy () const { return _num == 0; } + //bool isLocal () const { return addr().isLocal(); } + bool isV4 () const { return addr().isV4(); } + bool isV6 () const { return !addr().isV4(); } + String toString() const { return addr().toString(); } + + // related to legacy address (_num=0, ipv4) + IPAddress ipv4 () const { return _netif->ip_addr; } + IPAddress netmask () const { return _netif->netmask; } + IPAddress gw () const { return _netif->gw; } + + // common to all addresses of this interface + String ifname () const { return String(_netif->name[0]) + _netif->name[1]; } + const char* ifhostname () const { return _netif->hostname?: emptyString.c_str(); } + const char* ifmac () const { return (const char*)_netif->hwaddr; } + int ifnumber () const { return _netif->num; } + bool ifUp () const { return !!(_netif->flags & NETIF_FLAG_UP); } + const netif* interface () const { return _netif; } + + const ip_addr_t* ipFromNetifNum () const + { +#if LWIP_IPV6 + return _num ? &_netif->ip6_addr[_num - 1] : &_netif->ip_addr; +#else + return &_netif->ip_addr; +#endif + } + + // lwIP interface + netif* _netif; + + // address index within interface + // 0: legacy address (IPv4) + // n>0: (_num-1) is IPv6 index for netif->ip6_addr[] + int _num; +}; + + +class AddressListIterator +{ +public: + AddressListIterator (const netifWrapper& o) : netIf(o) {} + AddressListIterator (netif* netif) : netIf(netif) + { + // This constructor is called with lwIP's global netif_list, or + // nullptr. operator++() is designed to loop through _configured_ + // addresses. That's why netIf's _num is initialized to -1 to allow + // returning the first usable address to AddressList::begin(). + (void)operator++(); + } + + const netifWrapper& operator* () const { return netIf; } + const netifWrapper* operator-> () const { return &netIf; } + + bool operator== (AddressListIterator& o) { return netIf.equal(*o); } + bool operator!= (AddressListIterator& o) { return !netIf.equal(*o); } + + AddressListIterator operator++ (int) + { + AddressListIterator ret = *this; + (void)operator++(); + return ret; + } + + AddressListIterator& operator++ () + { + while (netIf._netif) + { + if (++netIf._num == IF_NUM_ADDRESSES) + { + // all addresses from current interface were iterated, + // switching to next interface + netIf = netifWrapper(netIf._netif->next); + continue; + } + if (!ip_addr_isany(netIf.ipFromNetifNum())) + // found an initialized address + break; + } + return *this; + } + + netifWrapper netIf; +}; + + +class AddressList +{ +public: + using const_iterator = const AddressListIterator; + + const_iterator begin () const { return const_iterator(netif_list); } + const_iterator end () const { return const_iterator(nullptr); } + +}; + +inline AddressList::const_iterator begin (const AddressList& a) { return a.begin(); } +inline AddressList::const_iterator end (const AddressList& a) { return a.end(); } + + +} // AddressListImplementation + +} // esp8266 + +extern AddressList addrList; + + +#endif diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp b/libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp new file mode 100644 index 000000000..9ce191cde --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp @@ -0,0 +1,93 @@ +/* + WiFi compat with ESP32 + + Copyright (C) 2020 Theo Arends / Jörg Schüler-Maroldt + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +// +#include "Arduino.h" +#include + +// +// Wifi +// +#ifdef WiFi +#undef WiFi +#endif + +void WiFiClass32::setSleepMode(int iSleepMode) +{ + // WIFI_MODEM_SLEEP + WiFi.setSleep(iSleepMode != WIFI_MODEM_SLEEP); +} + +int WiFiClass32::getPhyMode() +{ + return 0; // " BGN" +} + +void WiFiClass32::wps_disable() +{ +} + +void WiFiClass32::setOutputPower(int n) +{ + wifi_power_t p = WIFI_POWER_2dBm; + if (n > 19) + p = WIFI_POWER_19_5dBm; + else if (n > 18) + p = WIFI_POWER_18_5dBm; + else if (n >= 17) + p = WIFI_POWER_17dBm; + else if (n >= 15) + p = WIFI_POWER_15dBm; + else if (n >= 13) + p = WIFI_POWER_13dBm; + else if (n >= 11) + p = WIFI_POWER_11dBm; + else if (n >= 8) + p = WIFI_POWER_8_5dBm; + else if (n >= 7) + p = WIFI_POWER_7dBm; + else if (n >= 5) + p = WIFI_POWER_5dBm; + WiFi.setTxPower(p); +} + +void WiFiClass32::forceSleepBegin() +{ +} + +void WiFiClass32::forceSleepWake() +{ +} + +bool WiFiClass32::getNetworkInfo(uint8_t i, String &ssid, uint8_t &encType, int32_t &rssi, uint8_t *&bssid, int32_t &channel, bool &hidden_scan) +{ + hidden_scan = false; + return WiFi.getNetworkInfo(i, ssid, encType, rssi, bssid, channel); +} + +void wifi_station_disconnect() +{ + // erase ap: empty ssid, ... + WiFi.disconnect(true, true); +} + +void wifi_station_dhcpc_start() +{ +} + +WiFiClass32 WiFi32; diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP8266HTTPClient.h b/libesp32/ESP32-to-ESP8266-compat/src/ESP8266HTTPClient.h new file mode 100644 index 000000000..178911e3c --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/ESP8266HTTPClient.h @@ -0,0 +1,5 @@ +// +// Compat with ESP32 +// +#include + diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WebServer.h b/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WebServer.h new file mode 100644 index 000000000..f00be7b64 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WebServer.h @@ -0,0 +1,19 @@ +// +// Compat with ESP32 +// +#pragma once +#include + +//#define ESP8266WebServer WebServer + + +class ESP8266WebServer : public WebServer +{ +public: + ESP8266WebServer(int port) + :WebServer(port) + { + } +}; + +//#define ENC_TYPE_AUTO 0 diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h b/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h new file mode 100644 index 000000000..09d404e72 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h @@ -0,0 +1,85 @@ +/* + WiFi compat with ESP32 + + Copyright (C) 2020 Theo Arends / Jörg Schüler-Maroldt + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#pragma once +#include + +// sorry, no +#undef LWIP_IPV6 + +#define ENC_TYPE_NONE WIFI_AUTH_OPEN +#define ENC_TYPE_WEP WIFI_AUTH_WEP +#define ENC_TYPE_CCMP WIFI_AUTH_WPA2_PSK +#define ENC_TYPE_TKIP WIFI_AUTH_WPA_WPA2_PSK +#define ENC_TYPE_AUTO WIFI_AUTH_MAX + 1 + +#define WIFI_LIGHT_SLEEP 1 +#define WIFI_MODEM_SLEEP 2 + +class WiFiClass32 : public WiFiClass +{ +public: + static void hostname(const char* aHostname) + { + WiFi.setHostname(aHostname); + } + static void setSleepMode(int iSleepMode); + static int getPhyMode(); + + static void wps_disable(); + static void setOutputPower(int n); + static void forceSleepBegin(); + static void forceSleepWake(); + static bool getNetworkInfo(uint8_t i, String &ssid, uint8_t &encType, int32_t &rssi, uint8_t* &bssid, int32_t &channel, bool &hidden_scan); +}; + +void wifi_station_disconnect(); +void wifi_station_dhcpc_start(); +extern WiFiClass32 WiFi32; +#define WiFi WiFi32 + +class WiFiUDP32 : public WiFiUDP +{ + public: + size_t write(const char*s) + { + return WiFiUDP::write((const uint8_t *)s, strlen(s)); + } + size_t write(const uint8_t *buf, size_t n) + { + return WiFiUDP::write(buf, n); + } + static void stopAll() + { + + } + static void forceSleepWake() + { + + } + uint8_t beginMulticast(IPAddress interfaceAddr, IPAddress multicast, uint16_t port) + { + return WiFiUDP::beginMulticast(multicast, port); + } + void beginPacketMulticast(IPAddress multicast, uint16_t port, IPAddress interfaceAddr) + { + + } +}; + +#define WiFiUDP WiFiUDP32 diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP8266httpUpdate.h b/libesp32/ESP32-to-ESP8266-compat/src/ESP8266httpUpdate.h new file mode 100644 index 000000000..0ef8fbbb0 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/ESP8266httpUpdate.h @@ -0,0 +1,10 @@ +// +// Compat with ESP32 +// +#include +#define ESPhttpUpdate httpUpdate + +inline HTTPUpdateResult ESPhttpUpdate_update(const String& url, const String& currentVersion = "") +{ + return HTTP_UPDATE_OK; +} diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP8266mDNS.h b/libesp32/ESP32-to-ESP8266-compat/src/ESP8266mDNS.h new file mode 100644 index 000000000..f679ec5fa --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/ESP8266mDNS.h @@ -0,0 +1,4 @@ +// +// Compat with ESP32 +// +#include diff --git a/libesp32/ESP32-to-ESP8266-compat/src/c_types.h b/libesp32/ESP32-to-ESP8266-compat/src/c_types.h new file mode 100644 index 000000000..22f551391 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/c_types.h @@ -0,0 +1,6 @@ +#pragma once +/**/ +#include +#ifndef ICACHE_FLASH_ATTR +#define ICACHE_FLASH_ATTR +#endif diff --git a/libesp32/ESP32-to-ESP8266-compat/src/eboot_command.h b/libesp32/ESP32-to-ESP8266-compat/src/eboot_command.h new file mode 100644 index 000000000..992d014ea --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/eboot_command.h @@ -0,0 +1,3 @@ +// +// Compat with ESP32 +// diff --git a/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp new file mode 100644 index 000000000..e15770022 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp @@ -0,0 +1,24 @@ +/* + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + */ +// +#include "Arduino.h" +#include "lwip/apps/sntp.h" +#include +#include +#include +#include "esp8266toEsp32.h" + +// ESP Stuff diff --git a/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h new file mode 100644 index 000000000..90e2460b3 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h @@ -0,0 +1,142 @@ +/* + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + */ +#pragma once +#ifdef ESP32 +// my debug Stuff +#define Serial_Debug1(p) Serial.printf p +#define Serial_DebugX(p) + +// +// basics +// +// dummy defines +#define SPIFFS_END (SPI_FLASH_SEC_SIZE * 200) +#define SETTINGS_LOCATION SPIFFS_END + +#include + + +// webcam uses channel 0, so we offset standard PWM +#define PWM_CHANNEL_OFFSET 2 +// Analog + +uint8_t pwm_channel[8]={99,99,99,99,99,99,99,99}; + +inline uint32_t pin2chan(uint32_t pin) { + for (uint32_t cnt=0;cnt<8;cnt++) { + if ((pwm_channel[cnt]<99) && (pwm_channel[cnt]==pin)) { + return cnt; + } + } + return 0; +} + +inline void analogWrite(uint8_t pin, int val) +{ + uint32_t channel=pin2chan(pin); + ledcWrite(channel+PWM_CHANNEL_OFFSET,val); + //Serial.printf("write %d - %d\n",channel,val); +} + +inline void analogWriteFreq(uint32_t freq) +{ +} +inline void analogWriteRange(uint32_t range) +{ +} + +inline void analogAttach(uint32_t pin, uint32_t channel) { + pwm_channel[channel&7]=pin; + ledcAttachPin(pin,channel+PWM_CHANNEL_OFFSET); + //Serial.printf("attach %d - %d\n",channel,pin); +} + +inline uint32_t pow2(uint32_t x) { +uint32_t power = 1,bits=0; + while (power < x) { + power*=2; + bits++; + } + return bits-1; +} +// input range is in full range, ledc needs bits +inline void analogWriteFreqRange(uint32_t channel,uint32_t freq, uint32_t irange) { + uint32_t range=pow2(irange); + for (uint32_t cnt=0;cnt<8;cnt++) { + if (pwm_channel[cnt]<99) { + ledcSetup(cnt+PWM_CHANNEL_OFFSET,freq,range); + } + } + //Serial.printf("freq - range %d - %d\n",freq,range); +} + +#define INPUT_PULLDOWN_16 INPUT_PULLUP + +typedef double real64_t; + +// +// Time and Timer +// +#define ETS_UART_INTR_DISABLE() +#define ETS_UART_INTR_ENABLE() + +#define ESPhttpUpdate httpUpdate +#define getFlashChipRealSize() getFlashChipSize() + +#define os_delay_us ets_delay_us +// Serial minimal type to hold the config +typedef int SerConfu8; +typedef int SerialConfig; +//#define analogWrite(a, b) + +// +// WS2812 +// +#define NeoEsp8266BitBang800KbpsMethod NeoEsp32BitBang800KbpsMethod +// +// UDP +// +//#define PortUdp_writestr(log_data) PortUdp.write((const uint8_t *)(log_data), strlen(log_data)) +#define PortUdp_write(log_data, n) PortUdp.write((const uint8_t *)(log_data), n) + +// +#define wifi_forceSleepBegin() + +#undef LWIP_IPV6 + +#define REASON_DEFAULT_RST 0 // "Power on" normal startup by power on +#define REASON_WDT_RST 1 // "Hardware Watchdog" hardware watch dog reset +#define REASON_EXCEPTION_RST 2 // "Exception" exception reset, GPIO status won’t change +#define REASON_SOFT_WDT_RST 3 // "Software Watchdog" software watch dog reset, GPIO status won’t change +#define REASON_SOFT_RESTART 4 // "Software/System restart" software restart ,system_restart , GPIO status won’t change +#define REASON_DEEP_SLEEP_AWAKE 5 // "Deep-Sleep Wake" wake up from deep-sleep +#define REASON_EXT_SYS_RST 6 // "External System" external system reset + +// memmove ... +#define memcpy_P memcpy +#define memmove_P memmove +#define strncpy_P strncpy +#define strcmp_P strcmp +#define memccpy_P memccpy +#define snprintf_P snprintf +#define sprintf_P sprintf +#define strncmp_P strncmp + +// LWIP STuff + +#define STATION_IF 0 + +#endif diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ets_sys.h b/libesp32/ESP32-to-ESP8266-compat/src/ets_sys.h new file mode 100644 index 000000000..30a7e7733 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/ets_sys.h @@ -0,0 +1,3 @@ +#pragma once +#define timercallback void* +#define ets_printf(...) diff --git a/libesp32/ESP32-to-ESP8266-compat/src/gpio.h b/libesp32/ESP32-to-ESP8266-compat/src/gpio.h new file mode 100644 index 000000000..0ff47d6a2 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/gpio.h @@ -0,0 +1,2 @@ +#pragma once +#define GPIO_STATUS_W1TC_ADDRESS 0x24 diff --git a/libesp32/ESP32-to-ESP8266-compat/src/os_type.h b/libesp32/ESP32-to-ESP8266-compat/src/os_type.h new file mode 100644 index 000000000..a9e1558f5 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/os_type.h @@ -0,0 +1,6 @@ +#pragma once +#include "esp8266-compat.h" +#include +#include +typedef uint16 uint16_t; +typedef double real64_t; diff --git a/libesp32/ESP32-to-ESP8266-compat/src/osapi.h b/libesp32/ESP32-to-ESP8266-compat/src/osapi.h new file mode 100644 index 000000000..947de57cc --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/osapi.h @@ -0,0 +1,8 @@ +#pragma once +/**/ +#include +/* +#ifndef ICACHE_FLASH_ATTR +#define ICACHE_FLASH_ATTR +#endif +*/ diff --git a/libesp32/ESP32-to-ESP8266-compat/src/sntp.h b/libesp32/ESP32-to-ESP8266-compat/src/sntp.h new file mode 100644 index 000000000..2d92438d5 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/sntp.h @@ -0,0 +1,7 @@ +#pragma once +#define sntp_get_current_timestamp() SntpGetCurrentTimestamp() +#define sntp_init() SntpInit() +#define sntp_set_timezone(tz) +#define sntp_setservername(idx, name) +#define sntp_stop() + diff --git a/libesp32/ESP32-to-ESP8266-compat/src/spi_flash.h b/libesp32/ESP32-to-ESP8266-compat/src/spi_flash.h new file mode 100644 index 000000000..fffa3c3a0 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/spi_flash.h @@ -0,0 +1,4 @@ +// +// Compat with ESP32 +// +// TODO: Port it to ESP32 diff --git a/libesp32/ESP32-to-ESP8266-compat/src/twi.h b/libesp32/ESP32-to-ESP8266-compat/src/twi.h new file mode 100644 index 000000000..eaf51d122 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/twi.h @@ -0,0 +1,2 @@ +#pragma once +/**/ \ No newline at end of file diff --git a/libesp32/ESP32-to-ESP8266-compat/src/user_interface.h b/libesp32/ESP32-to-ESP8266-compat/src/user_interface.h new file mode 100644 index 000000000..ee38dfb21 --- /dev/null +++ b/libesp32/ESP32-to-ESP8266-compat/src/user_interface.h @@ -0,0 +1,24 @@ +/* + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef user_interface_h +#define user_interface_h +enum wps_cb_status { + WPS_CB_ST_SUCCESS = 0, + WPS_CB_ST_FAILED, + WPS_CB_ST_TIMEOUT, + WPS_CB_ST_WEP, + WPS_CB_ST_UNK, +}; +#endif diff --git a/libesp32/ESP32README.md b/libesp32/ESP32README.md new file mode 100644 index 000000000..1cb4e3c42 --- /dev/null +++ b/libesp32/ESP32README.md @@ -0,0 +1,46 @@ +![Tasmota logo](/tools/logo/TASMOTA_FullLogo_Vector.svg) + +## ESP32 port with minimal changes +## Description: +This is my esp32 port, i try to make only minimal changes to the original source code +from Theo Arends, now again for development branch. + +## Checklist: + - [x] The pull request is done against the latest dev branch + - [x] Only relevant files were touched + - [x] Only one feature/fix was added per PR. + - [x] The code change is tested and works on core Tasmota_core_stage + - [ ] The code change pass travis tests. **Your PR cannot be merged unless tests pass** + - [x] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). + - [x] i checked binary "tasmota.bin" to be the same in development and development_esp32 branch + +Here are the main things i have done + +- in "lib_extra_dirs" i have libesp32 directory for things missing in ESP32 framework + my "ESP32-to-ESP8266-compat" has all files that are not available in ESP32 + so you dont have to change the source code and i write code to get the informations from ESP32 +- all librarys that are not compatibel i add to lib_ignore +- all code that is not for ESP32 i put in "#ifdef ESP8266" the define is from espessif platform +- all code for ESP32 is in "#ifdef ESP32" +- SerConfu8 type uint8_t for SerialConfig list +- changed "HTTP_HEADER" to "HTTP_HEADER1", in ESP32 its an enum +- for ip adress 0 i used IPAddress(0,0,0,0) +- Special ESP.xxx call i change to ESP_xxx (ESP_rtcUserMemoryWrite, ...) and write macros for ESP8266 +- because ESP32 has only WEMOS 32 modul, i exclude all code like this: + "if (SONOFF_xxx == Settings.module)" in "#ifdef ESP8266" +- variable "sleep" i changed to "ssleep" because of standard library sleep(..) function +- all esp32 stuff is in support_esp32.ino +- in tasmota.ino i include "tasmota_compat.h" +- in tasmota_template.h i use ifdef and tasmota_templESP32.h +- defines for sensors that currently don't work, i undef in tasmota_templESP32.h +- comment fo "no warnig" in "xdrv_20_hue.ino" thats the only warning i had + +Build info + +Copy platformio_override_esp32.ini to platformio_override.ini an select your imagetype. +You can build tasmota and tasmota32 Version with one build. +If you need other versions change platformio_override.ini + +stay at home, have fun and keep good + +Jörg \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/API_DIFFERENCES.md b/libesp32/NimBLE-Arduino/API_DIFFERENCES.md new file mode 100644 index 000000000..d50356924 --- /dev/null +++ b/libesp32/NimBLE-Arduino/API_DIFFERENCES.md @@ -0,0 +1,207 @@ +# Server API differnces: + +### Characteristics: +When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`. + +#### Previous: +``` +BLECharacteristic::PROPERTY_READ | +BLECharacteristic::PROPERTY_WRITE +``` + +#### Changed to: +``` +NIMBLE_PROPERTY::READ | +NIMBLE_PROPERTY::WRITE +``` + +#### The full list of properties: +``` +NIMBLE_PROPERTY::READ +NIMBLE_PROPERTY::READ_ENC +NIMBLE_PROPERTY::READ_AUTHEN +NIMBLE_PROPERTY::READ_AUTHOR +NIMBLE_PROPERTY::WRITE +NIMBLE_PROPERTY::WRITE_NR +NIMBLE_PROPERTY::WRITE_ENC +NIMBLE_PROPERTY::WRITE_AUTHEN +NIMBLE_PROPERTY::WRITE_AUTHOR +NIMBLE_PROPERTY::BROADCAST +NIMBLE_PROPERTY::NOTIFY +NIMBLE_PROPERTY::INDICATE +``` + +### Descriptors: +Descriptors are now created using the NimBLEcharacteristic method `createDescriptor()`. + +The previous method `addDescriptor()` is now a private function in the library. + +This was done because the NimBLE host automatically creates a 0x2902 descriptor if a characteristic has notify or indicate properties applied. +Due to this fact, this library also creates one automatically for your application. +The only reason to manually create this descriptor now is to assign callback functions. +If you do not require this functionality you can safely exclude the manual creation of that descriptor. + + +For any other descriptor, (except 0x2904, see below) it should now be created just as characteristics are +by invoking the `NimBLECharacteristic::createDescriptor` methods. +Which are defined as: +``` +NimBLEDescriptor* createDescriptor(const char* uuid, + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = 100); + +NimBLEDescriptor* createDescriptor(NimBLEUUID uuid, + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = 100); +``` +##### Example: +``` +pDescriptor = pCharacteristic->createDescriptor("ABCD", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::WRITE_ENC, + 25);` +``` +Would create a descriptor with the UUID 0xABCD, publicly readable but only writable if paired/bonded (encrypted) and has a max value length of 25 bytes. + +For the 0x2904 descriptor, there is a special class that is created when you call `createDescriptor("2904")`. + +The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` so you must cast the returned pointer to `NimBLE2904*` to access the specific class methods. + +##### Example: +``` +p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904"); +``` + +#### Server Security: +Security is set on the characteristic or descriptor properties by applying one of the following: +``` +NIMBLE_PROPERTY::READ_ENC +NIMBLE_PROPERTY::READ_AUTHEN +NIMBLE_PROPERTY::READ_AUTHOR +NIMBLE_PROPERTY::WRITE_ENC +NIMBLE_PROPERTY::WRITE_AUTHEN +NIMBLE_PROPERTY::WRITE_AUTHOR +``` +When a peer wants to read or write a characteristic or descriptor with any of these properties applied +it will trigger the pairing process. By default the "just-works" pairing will be performed automatically. +This can be changed to use passkey authentication or numeric confirmation. See below for details. + + +# Client API Differences: +The `BLEAdvertisedDeviceCallbacks` class `onResult()` method now receives a pointer to the +`NimBLEAdvertisedDevice` object instead of a copy. + +`NimBLEClient::connect()` now takes an extra parameter to indicate if the client should download the services + database from the peripheral, default value is true. + +Defined as: +``` +bool connect(NimBLEAdvertisedDevice* device, bool refreshServices = true); +bool connect(NimBLEAddress address, uint8_t type = BLE_ADDR_TYPE_PUBLIC, bool refreshServices = true); +``` +If set to false the client will use the services database it retrieved from the peripheral last time it connected. +This allows for faster connections and power saving if the devices just dropped connection and want to reconnect. + +``` +NimBLERemoteCharacteristic::writeValue(); +NimBLERemoteCharacteristic::registerForNotify(); +``` +Now return true or false to indicate success or failure so you can choose to disconnect or try again. + +#### Client Security: +The client will automatically initiate security when the peripheral responds that it's required. +The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below. + + +# Security: +Security callback functions are now incorporated in the client/server Callbacks class. +However backward compatibility with the `BLESecurity` class is retained to minimize app code changes. + +The relevant server callbacks are defined as: +``` +bool onConfirmPIN(uint32_t pin); // accept or reject the passkey +void onAuthenticationComplete(ble_gap_conn_desc* desc); // auth complete - details in desc +bool onPassKeyNotify(uint32_t pass_key); // receive the passkey sent by the client, accept or reject +``` +The relevant client callbacks are defined as: +``` +bool onConfirmPIN(uint32_t pin); // accept or reject the passkey +void onAuthenticationComplete(ble_gap_conn_desc* desc); // auth complete - details in desc +uint32_t onPassKeyRequest(); // return the passkey to send to the server +``` + +Security settings and IO capabilities are now set by the corresponding method of `NimBLEDevice::`. +``` +static void setSecurityAuth(bool bonding, bool mitm, bool sc); +static void setSecurityAuth(uint8_t auth_req); +static void setSecurityIOCap(uint8_t iocap); +static void setSecurityInitKey(uint8_t init_key); +static void setSecurityRespKey(uint8_t init_key); + + +/** + * @brief Set the authorization mode for this device. + * @param bonding, if true we allow bonding, false no bonding will be performed. + * @param mitm, if true we are capable of man in the middle protection, false if not. + * @param sc, if true we will perform secure connection pairing, false we will use legacy pairing. + */ +void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc) + + + +/** + * @brief Set the authorization mode for this device. + * @param A bitmap indicating what modes are supported. + * The bits are defined as follows: + ** 0x01 BLE_SM_PAIR_AUTHREQ_BOND + ** 0x04 BLE_SM_PAIR_AUTHREQ_MITM + ** 0x08 BLE_SM_PAIR_AUTHREQ_SC + ** 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported. + ** 0xe2 BLE_SM_PAIR_AUTHREQ_RESERVED - for reference only. + */ +void NimBLEDevice::setSecurityAuth(uint8_t auth_req) + + + +/** + * @brief Set the Input/Output capabilities of this device. + * @param One of the following: + ** 0x00 BLE_HS_IO_DISPLAY_ONLY DisplayOnly IO capability + ** 0x01 BLE_HS_IO_DISPLAY_YESNO DisplayYesNo IO capability + ** 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability + ** 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability + ** 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability + */ +void NimBLEDevice::setSecurityIOCap(uint8_t iocap) + + + +/** + * @brief If we are the initiator of the security procedure this sets the keys we will distribute. + * @param A bitmap indicating which keys to distribute during pairing. + * The bits are defined as follows: + ** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key. + ** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK). + ** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN + ** 0x08: BLE_SM_PAIR_KEY_DIST_LINK + */ +void NimBLEDevice::setSecurityInitKey(uint8_t init_key) + + +/** + * @brief Set the keys we are willing to accept during pairing. + * @param A bitmap indicating which keys to accept during pairing. + * The bits are defined as follows: + ** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key. + ** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK). + ** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN + ** 0x08: BLE_SM_PAIR_KEY_DIST_LINK + */ +void NimBLEDevice::setSecurityRespKey(uint8_t init_key) +``` + + I'm sure there are more things I have forgotten but this is all the majors. + I will update this document as necessary. diff --git a/libesp32/NimBLE-Arduino/LICENSE b/libesp32/NimBLE-Arduino/LICENSE new file mode 100644 index 000000000..4abe69699 --- /dev/null +++ b/libesp32/NimBLE-Arduino/LICENSE @@ -0,0 +1,219 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {2020} {Ryan Powell} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +This product bundles queue.h 8.5, which is available under the "3-clause BSD" +license. For details, see porting/nimble/include/os/queue.h + +This product partly derives from FreeBSD, which is available under the +"3-clause BSD" license. For details, see: + * porting/nimble/src/os_mbuf.c + +This product bundles Gary S. Brown's CRC32 implementation, which is available +under the following license: + COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction. + +This product bundles tinycrypt, which is available under the "3-clause BSD" +license. For details, and bundled files see: + * ext/tinycrypt/LICENSE + +This product partly derives from esp32-snippets; Copyright 2017 Neil Kolban. \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/README.md b/libesp32/NimBLE-Arduino/README.md new file mode 100644 index 000000000..5271d12a3 --- /dev/null +++ b/libesp32/NimBLE-Arduino/README.md @@ -0,0 +1,79 @@ +# *** UPDATE *** +Server now handles long reads and writes, still work to do on client. + +NEW Client callback created - ```bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params)``` +Called when the server wants to change the connection parameters, return true to accept them or false if not. +Check NimBLE_Client.ino example for a demonstration. + + + +# NimBLE-Arduino +A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with a CPP library for use with ESP32. + +Why? Because the Bluedroid library is too bulky. + +Initial client code testing has resulted in code size reduction of ~115k and reduced ram consumption of ~37k. + +Server code testing results from @beegee-toyo [from the project here](https://github.com/beegee-tokyo/ESP32WiFiBLE-NimBLE): + + +### Memory usage (compilation output) +#### Arduino BLE library +```log +RAM: [== ] 17.7% (used 58156 bytes from 327680 bytes) +Flash: [======== ] 76.0% (used 1345630 bytes from 1769472 bytes) +``` +#### NimBLE-Arduino library +```log +RAM: [= ] 14.5% (used 47476 bytes from 327680 bytes) +Flash: [======= ] 69.5% (used 911378 bytes from 1310720 bytes) +``` +### Memory usage after **`setup()`** function +#### Arduino BLE library +**`Internal Total heap 259104, internal Free Heap 91660`** +#### NimBLE-Arduino library +**`Internal Total heap 290288, internal Free Heap 182344`** + + +# Installation: + +Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library. + +`#include "NimBLEDevice.h"` at the beginning of your sketch. + +Tested and working with esp32-arduino v1.0.2 and 1.0.4 in Arduino IDE v1.8.12 and platform IO. + + +# Usage: + +This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes. + +Check the Refactored_original_examples in the examples folder for highlights of the differences with the original library. + +More advanced examples highlighting many available features are in examples/ NimBLE_Server, NimBLE_Client. + +Beacon examples provided by @beegee-tokyo are in examples/ BLE_Beacon_Scanner, BLE_EddystoneTLM_Beacon, BLE_EddystoneURL_Beacon. + +Change the settings in the `nimconfig.h` file to customize NimBLE to your project, such as increasing max connections, default is 3. + + +# Continuing development: + +This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@0a1604a.](https://github.com/espressif/esp-nimble) + +Also tracking the NimBLE related changes in esp-idf, master branch, currently [@48bd2d7.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble) + + +# Acknowledgments: + +* @nkolban and @chegewara for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets) this project was derived from. +* @beegee-tokyo for contributing your time to test/debug and contributing the beacon examples. + + +# Todo: + +1. Code cleanup. +2. Create documentation. +3. Expose more NimBLE features. +4. Add BLE Mesh code. + diff --git a/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino b/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino new file mode 100644 index 000000000..fea6b7d50 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino @@ -0,0 +1,164 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** + #include + #include + #include + #include + #include "BLEEddystoneURL.h" + #include "BLEEddystoneTLM.h" + #include "BLEBeacon.h" +***********************/ + +#include + +#include +#include +#include "NimBLEEddystoneURL.h" +#include "NimBLEEddystoneTLM.h" +#include "NimBLEBeacon.h" + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8)) + +int scanTime = 5; //In seconds +BLEScan *pBLEScan; + +class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks +{ + /*** Only a reference to the advertised device is passed now + void onResult(BLEAdvertisedDevice advertisedDevice) { **/ + void onResult(BLEAdvertisedDevice *advertisedDevice) + { + if (advertisedDevice->haveName()) + { + Serial.print("Device name: "); + Serial.println(advertisedDevice->getName().c_str()); + Serial.println(""); + } + + if (advertisedDevice->haveServiceUUID()) + { + BLEUUID devUUID = advertisedDevice->getServiceUUID(); + Serial.print("Found ServiceUUID: "); + Serial.println(devUUID.toString().c_str()); + Serial.println(""); + } + else + { + if (advertisedDevice->haveManufacturerData() == true) + { + std::string strManufacturerData = advertisedDevice->getManufacturerData(); + + uint8_t cManufacturerData[100]; + strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0); + + if (strManufacturerData.length() == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00) + { + Serial.println("Found an iBeacon!"); + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setData(strManufacturerData); + Serial.printf("iBeacon Frame\n"); + Serial.printf("ID: %04X Major: %d Minor: %d UUID: %s Power: %d\n", oBeacon.getManufacturerId(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor()), oBeacon.getProximityUUID().toString().c_str(), oBeacon.getSignalPower()); + } + else + { + Serial.println("Found another manufacturers beacon!"); + Serial.printf("strManufacturerData: %d ", strManufacturerData.length()); + for (int i = 0; i < strManufacturerData.length(); i++) + { + Serial.printf("[%X]", cManufacturerData[i]); + } + Serial.printf("\n"); + } + } + return; + } + + uint8_t *payLoad = advertisedDevice->getPayload(); + + BLEUUID checkUrlUUID = (uint16_t)0xfeaa; + + if (advertisedDevice->getServiceUUID().equals(checkUrlUUID)) + { + if (payLoad[11] == 0x10) + { + Serial.println("Found an EddystoneURL beacon!"); + BLEEddystoneURL foundEddyURL = BLEEddystoneURL(); + std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct! + + foundEddyURL.setData(eddyContent); + std::string bareURL = foundEddyURL.getURL(); + if (bareURL[0] == 0x00) + { + size_t payLoadLen = advertisedDevice->getPayloadLength(); + Serial.println("DATA-->"); + for (int idx = 0; idx < payLoadLen; idx++) + { + Serial.printf("0x%08X ", payLoad[idx]); + } + Serial.println("\nInvalid Data"); + return; + } + + Serial.printf("Found URL: %s\n", foundEddyURL.getURL().c_str()); + Serial.printf("Decoded URL: %s\n", foundEddyURL.getDecodedURL().c_str()); + Serial.printf("TX power %d\n", foundEddyURL.getPower()); + Serial.println("\n"); + } + else if (payLoad[11] == 0x20) + { + Serial.println("Found an EddystoneTLM beacon!"); + BLEEddystoneTLM foundEddyURL = BLEEddystoneTLM(); + std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct! + + eddyContent = "01234567890123"; + + for (int idx = 0; idx < 14; idx++) + { + eddyContent[idx] = payLoad[idx + 11]; + } + + foundEddyURL.setData(eddyContent); + Serial.printf("Reported battery voltage: %dmV\n", foundEddyURL.getVolt()); + Serial.printf("Reported temperature from TLM class: %.2fC\n", (double)foundEddyURL.getTemp()); + int temp = (int)payLoad[16] + (int)(payLoad[15] << 8); + float calcTemp = temp / 256.0f; + Serial.printf("Reported temperature from data: %.2fC\n", calcTemp); + Serial.printf("Reported advertise count: %d\n", foundEddyURL.getCount()); + Serial.printf("Reported time since last reboot: %ds\n", foundEddyURL.getTime()); + Serial.println("\n"); + Serial.print(foundEddyURL.toString().c_str()); + Serial.println("\n"); + } + } + } +}; + +void setup() +{ + Serial.begin(115200); + Serial.println("Scanning..."); + + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); //create new scan + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); // less or equal setInterval value +} + +void loop() +{ + // put your main code here, to run repeatedly: + BLEScanResults foundDevices = pBLEScan->start(scanTime, false); + Serial.print("Devices found: "); + Serial.println(foundDevices.getCount()); + Serial.println("Scan done!"); + pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + delay(2000); +} diff --git a/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md b/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md new file mode 100644 index 000000000..558c3e7ae --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md @@ -0,0 +1,9 @@ +## BLE Beacon Scanner + +Initiates a BLE device scan. +Checks if the discovered devices are +- an iBeacon +- an Eddystone TLM beacon +- an Eddystone URL beacon + +and sends the decoded beacon information over Serial log \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino b/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino new file mode 100644 index 000000000..32e0b1e99 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino @@ -0,0 +1,113 @@ +/* + EddystoneTLM beacon for NimBLE by BeeGee based on https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino + EddystoneTLM frame specification https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md +*/ + +/* + Create a BLE server that will send periodic Eddystone URL frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ + +#include "NimBLEDevice.h" +#include "NimBLEBeacon.h" +#include "NimBLEAdvertising.h" +#include "NimBLEEddystoneURL.h" + +#include "sys/time.h" +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up + +// UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" + +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +BLEAdvertising *pAdvertising; +struct timeval nowTimeStruct; + +time_t lastTenth; + +// Check +// https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md +// and http://www.hugi.scene.org/online/coding/hugi%2015%20-%20cmtadfix.htm +// for the temperature value. It is a 8.8 fixed-point notation +void setBeacon() +{ + char beacon_data[25]; + uint16_t beconUUID = 0xFEAA; + uint16_t volt = random(2800, 3700); // 3300mV = 3.3V + float tempFloat = random(2000, 3100) / 100.0f; + Serial.printf("Random temperature is %.2fC\n", tempFloat); + int temp = (int)(tempFloat * 256); //(uint16_t)((float)23.00); + Serial.printf("Converted to 8.8 format %0X%0X\n", (temp >> 8), (temp & 0xFF)); + + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + oScanResponseData.setFlags(0x06); // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04 + oScanResponseData.setCompleteServices(BLEUUID(beconUUID)); + + beacon_data[0] = 0x20; // Eddystone Frame Type (Unencrypted Eddystone-TLM) + beacon_data[1] = 0x00; // TLM version + beacon_data[2] = (volt >> 8); // Battery voltage, 1 mV/bit i.e. 0xCE4 = 3300mV = 3.3V + beacon_data[3] = (volt & 0xFF); // + beacon_data[4] = (temp >> 8); // Beacon temperature + beacon_data[5] = (temp & 0xFF); // + beacon_data[6] = ((bootcount & 0xFF000000) >> 24); // Advertising PDU count + beacon_data[7] = ((bootcount & 0xFF0000) >> 16); // + beacon_data[8] = ((bootcount & 0xFF00) >> 8); // + beacon_data[9] = (bootcount & 0xFF); // + beacon_data[10] = ((lastTenth & 0xFF000000) >> 24); // Time since power-on or reboot as 0.1 second resolution counter + beacon_data[11] = ((lastTenth & 0xFF0000) >> 16); // + beacon_data[12] = ((lastTenth & 0xFF00) >> 8); // + beacon_data[13] = (lastTenth & 0xFF); // + + oScanResponseData.setServiceData(BLEUUID(beconUUID), std::string(beacon_data, 14)); + oAdvertisementData.setName("TLMBeacon"); + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); +} + +void setup() +{ + + Serial.begin(115200); + gettimeofday(&nowTimeStruct, NULL); + + Serial.printf("start ESP32 %d\n", bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", nowTimeStruct.tv_sec, nowTimeStruct.tv_sec - last); + + last = nowTimeStruct.tv_sec; + lastTenth = nowTimeStruct.tv_sec * 10; // Time since last reset as 0.1 second resolution counter + + // Create the BLE Device + BLEDevice::init("TLMBeacon"); + + BLEDevice::setPower(ESP_PWR_LVL_N12); + + pAdvertising = BLEDevice::getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started for 10s ..."); + delay(10000); + pAdvertising->stop(); + Serial.printf("enter deep sleep for 10s\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() +{ +} diff --git a/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md b/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md new file mode 100644 index 000000000..2e34029d1 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md @@ -0,0 +1,14 @@ +## Eddystone TLM beacon +EddystoneTLM beacon by BeeGee based on +[pcbreflux ESP32 Eddystone TLM deepsleep](https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino) + +[EddystoneTLM frame specification](https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md) + + Create a BLE server that will send periodic Eddystone TLM frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep diff --git a/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino b/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino new file mode 100644 index 000000000..07879b257 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino @@ -0,0 +1,185 @@ +/* + EddystoneURL beacon for NimBLE by BeeGee + EddystoneURL frame specification https://github.com/google/eddystone/blob/master/eddystone-url/README.md + +*/ + +/* + Create a BLE server that will send periodic Eddystone URL frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ + +#include "NimBLEDevice.h" +#include "NimBLEBeacon.h" +#include "NimBLEEddystoneURL.h" + +#include "sys/time.h" +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up + +// UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" + +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +BLEAdvertising *pAdvertising; +struct timeval now; + +static const char *eddystone_url_prefix_subs[] = { + "http://www.", + "https://www.", + "http://", + "https://", + "urn:uuid:", + NULL +}; + +static const char *eddystone_url_suffix_subs[] = { + ".com/", + ".org/", + ".edu/", + ".net/", + ".info/", + ".biz/", + ".gov/", + ".com", + ".org", + ".edu", + ".net", + ".info", + ".biz", + ".gov", + NULL +}; + +static int string_begin_with(const char *str, const char *prefix) +{ + int prefix_len = strlen(prefix); + if (strncmp(prefix, str, prefix_len) == 0) + { + return prefix_len; + } + return 0; +} + +void setBeacon() +{ + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + const char url[] = "https://d.giesecke.tk"; + + int scheme_len, ext_len = 1, i, idx, url_idx; + char *ret_data; + int url_len = strlen(url); + + ret_data = (char *)calloc(1, url_len + 13); + + ret_data[0] = 2; // Len + ret_data[1] = 0x01; // Type Flags + ret_data[2] = 0x06; // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04 + ret_data[3] = 3; // Len + ret_data[4] = 0x03; // Type 16-Bit UUID + ret_data[5] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB + ret_data[6] = 0xFE; // Eddystone UUID 1 MSB + ret_data[7] = 19; // Length of Beacon Data + ret_data[8] = 0x16; // Type Service Data + ret_data[9] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB + ret_data[10] = 0xFE; // Eddystone UUID 1 MSB + ret_data[11] = 0x10; // Eddystone Frame Type + ret_data[12] = 0xF4; // Beacons TX power at 0m + + i = 0, idx = 13, url_idx = 0; + + //replace prefix + scheme_len = 0; + while (eddystone_url_prefix_subs[i] != NULL) + { + if ((scheme_len = string_begin_with(url, eddystone_url_prefix_subs[i])) > 0) + { + ret_data[idx] = i; + idx++; + url_idx += scheme_len; + break; + } + i++; + } + while (url_idx < url_len) + { + i = 0; + ret_data[idx] = url[url_idx]; + ext_len = 1; + while (eddystone_url_suffix_subs[i] != NULL) + { + if ((ext_len = string_begin_with(&url[url_idx], eddystone_url_suffix_subs[i])) > 0) + { + ret_data[idx] = i; + break; + } + else + { + ext_len = 1; //inc 1 + } + i++; + } + url_idx += ext_len; + idx++; + } + ret_data[7] = idx - 8; + + Serial.printf("struct size %d url size %d reported len %d\n", + url_len + 13, + url_len, ret_data[7]); + + Serial.printf("URL in data %s\n", &ret_data[13]); + + std::string eddyStoneData(ret_data); + + oAdvertisementData.addData(eddyStoneData); + oScanResponseData.setName("MeBeacon"); + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); +} + +void setup() +{ + + Serial.begin(115200); + gettimeofday(&now, NULL); + + Serial.printf("start ESP32 %d\n", bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", now.tv_sec, now.tv_sec - last); + + last = now.tv_sec; + + // Create the BLE Device + BLEDevice::init("MeBeacon"); + + BLEDevice::setPower(ESP_PWR_LVL_N12); + + pAdvertising = BLEDevice::getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started..."); + delay(10000); + pAdvertising->stop(); + Serial.printf("enter deep sleep\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() +{ +} diff --git a/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md b/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md new file mode 100644 index 000000000..2baf1cc52 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md @@ -0,0 +1,14 @@ +## Eddystone URL beacon +EddystoneURL beacon by BeeGee based on +[pcbreflux ESP32 Eddystone URL deepsleep](https://github.com/pcbreflux/espressif/tree/master/esp32/arduino/sketchbook/ESP32_Eddystone_URL_deepsleep) + +[EddystoneURL frame specification](https://github.com/google/eddystone/blob/master/eddystone-url/README.md) + + Create a BLE server that will send periodic Eddystone URL frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep diff --git a/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino b/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino new file mode 100644 index 000000000..30eeb9fe3 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino @@ -0,0 +1,383 @@ + +/** NimBLE_Server Demo: + * + * Demonstrates many of the available features of the NimBLE client library. + * + * Created: on March 24 2020 + * Author: H2zero + * +*/ + +#include + +void scanEndedCB(NimBLEScanResults results); + +static NimBLEAdvertisedDevice* advDevice; + +static bool doConnect = false; +static uint32_t scanTime = 0; /** 0 = scan forever */ + + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class ClientCallbacks : public NimBLEClientCallbacks { + void onConnect(NimBLEClient* pClient) { + Serial.println("Connected"); + /** After connection we should change the parameters if we don't need fast response times. + * These settings are 150ms interval, 0 latency, 450ms timout. + * Timeout should be a multiple of the interval, minimum is 100ms. + * I find a multiple of 3-5 * the interval works best for quick response/reconnect. + * Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout + */ + pClient->updateConnParams(120,120,0,60); + }; + + void onDisconnect(NimBLEClient* pClient) { + Serial.print(pClient->getPeerAddress().toString().c_str()); + Serial.println(" Disconnected - Starting scan"); + NimBLEDevice::getScan()->start(scanTime, scanEndedCB); + }; + + /** Called when the peripheral requests a change to the connection parameters. + * Return true to accept and apply them or false to reject and keep + * the currently used parameters. Default will return true. + */ + bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) { + if(params->itvl_min < 24) { /** 1.25ms units */ + return false; + } else if(params->itvl_max > 40) { /** 1.25ms units */ + return false; + } else if(params->latency > 2) { /** Number of intervals allowed to skip */ + return false; + } else if(params->supervision_timeout > 100) { /** 10ms units */ + return false; + } + + return true; + }; + + /********************* Security handled here ********************** + ****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Client Passkey Request"); + /** return the passkey to send to the server */ + return 123456; + }; + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: "); + Serial.println(pass_key); + /** Return false if passkeys don't match. */ + return true; + }; + + /** Pairing process complete, we can check the results in ble_gap_conn_desc */ + void onAuthenticationComplete(ble_gap_conn_desc* desc){ + if(!desc->sec_state.encrypted) { + Serial.println("Encrypt connection failed - disconnecting"); + /** Find the client with the connection handle provided in desc */ + NimBLEDevice::getClientByID(desc->conn_handle)->disconnect(); + return; + } + }; +}; + + +/** Define a class to handle the callbacks when advertisments are received */ +class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { + + void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + Serial.print("Advertised Device found: "); + Serial.println(advertisedDevice->toString().c_str()); + if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) + { + Serial.println("Found Our Service"); + /** stop scan before connecting */ + NimBLEDevice::getScan()->stop(); + /** Save the device reference in a global for the client to use*/ + advDevice = advertisedDevice; + /** Ready to connect now */ + doConnect = true; + } + }; +}; + + +/** Notification / Indication receiving handler callback */ +void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ + std::string str = (isNotify == true) ? "Notification" : "Indication"; + str += " from "; + str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString(); + str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString(); + str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString(); + str += ", Value = " + std::string((char*)pData, length); + Serial.println(str.c_str()); +} + +/** Callback to process the results of the last scan or restart it */ +void scanEndedCB(NimBLEScanResults results){ + Serial.println("Scan Ended"); +} + + +/** Create a single global instance of the callback class to be used by all clients */ +static ClientCallbacks clientCB; + + +/** Handles the provisioning of clients and connects / interfaces with the server */ +bool connectToServer() { + NimBLEClient* pClient = nullptr; + + /** Check if we have a client we should reuse first **/ + if(NimBLEDevice::getClientListSize()) { + /** Special case when we already know this device, we send false as the + * second argument in connect() to prevent refreshing the service database. + * This saves considerable time and power. + */ + pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress()); + if(pClient){ + if(!pClient->connect(advDevice, false)) { + Serial.println("Reconnect failed"); + return false; + } + Serial.println("Reconnected client"); + } + /** We don't already have a client that knows this device, + * we will check for a client that is disconnected that we can use. + */ + else { + pClient = NimBLEDevice::getDisconnectedClient(); + } + } + + /** No client to reuse? Create a new one. */ + if(!pClient) { + if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) { + Serial.println("Max clients reached - no more connections available"); + return false; + } + + pClient = NimBLEDevice::createClient(); + + Serial.println("New client created"); + + pClient->setClientCallbacks(&clientCB, false); + /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout. + * These settings are safe for 3 clients to connect reliably, can go faster if you have less + * connections. Timeout should be a multiple of the interval, minimum is 100ms. + * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout + */ + pClient->setConnectionParams(12,12,0,51); + /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */ + pClient->setConnectTimeout(5); + + + if (!pClient->connect(advDevice)) { + /** Created a client but failed to connect, don't need to keep it as it has no data */ + NimBLEDevice::deleteClient(pClient); + Serial.println("Failed to connect, deleted client"); + return false; + } + } + + if(!pClient->isConnected()) { + if (!pClient->connect(advDevice)) { + Serial.println("Failed to connect"); + return false; + } + } + + Serial.print("Connected to: "); + Serial.println(pClient->getPeerAddress().toString().c_str()); + Serial.print("RSSI: "); + Serial.println(pClient->getRssi()); + + /** Now we can read/write/subscribe the charateristics of the services we are interested in */ + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + NimBLERemoteDescriptor* pDsc = nullptr; + + pSvc = pClient->getService("DEAD"); + if(pSvc) { /** make sure it's not null */ + pChr = pSvc->getCharacteristic("BEEF"); + } + + if(pChr) { /** make sure it's not null */ + if(pChr->canRead()) { + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pChr->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("Tasty")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + /** Disconnect if write failed */ + pClient->disconnect(); + return false; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + if(pChr->canNotify()) { + /** Must send a callback to subscribe, if nullptr it will unsubscribe */ + if(!pChr->registerForNotify(notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + else if(pChr->canIndicate()) { + /** Send false as second argument to subscribe to indications instead of notifications */ + if(!pChr->registerForNotify(notifyCB, false)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + } + + else{ + Serial.println("DEAD service not found."); + } + + pSvc = pClient->getService("BAAD"); + if(pSvc) { /** make sure it's not null */ + pChr = pSvc->getCharacteristic("F00D"); + } + + if(pChr) { /** make sure it's not null */ + if(pChr->canRead()) { + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pChr->readValue().c_str()); + } + + pDsc = pChr->getDescriptor(NimBLEUUID("C01D")); + if(pDsc) { /** make sure it's not null */ + Serial.print("Descriptor: "); + Serial.print(pDsc->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pDsc->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("No tip!")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + /** Disconnect if write failed */ + pClient->disconnect(); + return false; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + if(pChr->canNotify()) { + /** Must send a callback to subscribe, if nullptr it will unsubscribe */ + if(!pChr->registerForNotify(notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + else if(pChr->canIndicate()) { + /** Send false as second argument to subscribe to indications instead of notifications */ + if(!pChr->registerForNotify(notifyCB, false)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + } + + else{ + Serial.println("BAAD service not found."); + } + + Serial.println("Done with this device!"); + return true; +} + +void setup (){ + Serial.begin(115200); + Serial.println("Starting NimBLE Client"); + /** Initialize NimBLE, no device name spcified as we are not advertising */ + NimBLEDevice::init(""); + + /** Set the IO capabilities of the device, each option will trigger a different pairing method. + * BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing + * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing + * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing + */ + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison + + /** 2 different ways to set security - both calls achieve the same result. + * no bonding, no man in the middle protection, secure connections. + * + * These are the default values, only shown here for demonstration. + */ + //NimBLEDevice::setSecurityAuth(false, false, true); + NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); + + /** Optional: set the transmit power, default is 3db */ + NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */ + + /** Optional: set any devices you don't want to get advertisments from */ + // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff")); + + /** create new scan */ + NimBLEScan* pScan = NimBLEDevice::getScan(); + + /** create a callback that gets called when advertisers are found */ + pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks()); + + /** Set scan interval (how often) and window (how long) in milliseconds */ + pScan->setInterval(45); + pScan->setWindow(15); + + /** Active scan will gather scan response data from advertisers + * but will use more energy from both devices + */ + pScan->setActiveScan(true); + /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever + * Optional callback for when scanning stops. + */ + pScan->start(scanTime, scanEndedCB); +} + + +void loop (){ + /** Loop here until we find a device we want to connect to */ + while(!doConnect){ + delay(1); + } + + doConnect = false; + + /** Found a device we want to connect to, do it now */ + if(connectToServer()) { + Serial.println("Success! we should now be getting notifications, scanning for more!"); + } else { + Serial.println("Failed to connect, starting scan"); + } + + NimBLEDevice::getScan()->start(scanTime,scanEndedCB); +} diff --git a/libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino b/libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino new file mode 100644 index 000000000..cd48cb348 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino @@ -0,0 +1,251 @@ + +/** NimBLE_Server Demo: + * + * Demonstrates many of the available features of the NimBLE server library. + * + * Created: on March 22 2020 + * Author: H2zero + * +*/ + +#include +#include +#include + +static NimBLEServer* pServer; + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class ServerCallbacks: public NimBLEServerCallbacks { + void onConnect(NimBLEServer* pServer) { + Serial.println("Client connected"); + Serial.println("Multi-connect support: start advertising"); + NimBLEDevice::startAdvertising(); + }; + /** Alternative onConnect() method to extract details of the connection. + * See: src/ble_gap.h for the details of the ble_gap_conn_desc struct. + */ + void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { + Serial.print("Client address: "); + Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str()); + /** We can use the connection handle here to ask for different connection parameters. + * Args: connection handle, min connection interval, max connection interval + * latency, supervision timeout. + * Units; Min/Max Intervals: 1.25 millisecond increments. + * Latency: number of intervals allowed to skip. + * Timeout: 10 millisecond increments, try for 5x interval time for best results. + */ + pServer->updateConnParams(desc->conn_handle, 24, 48, 0, 60); + }; + void onDisconnect(NimBLEServer* pServer) { + Serial.println("Client disconnected - start advertising"); + NimBLEDevice::startAdvertising(); + }; + +/********************* Security handled here ********************** +****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server Passkey Request"); + /** This should return a random 6 digit number for security + * or make your own static passkey as done here. + */ + return 123456; + }; + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + /** Return false if passkeys don't match. */ + return true; + }; + + void onAuthenticationComplete(ble_gap_conn_desc* desc){ + /** Check that encryption was successful, if not we disconnect the client */ + if(!desc->sec_state.encrypted) { + /** NOTE: createServer returns the current server reference unless one is not already created */ + NimBLEDevice::createServer()->disconnect(desc->conn_handle); + Serial.println("Encrypt connection failed - disconnecting client"); + return; + } + Serial.println("Starting BLE work!"); + }; +}; + +/** Handler class for characteristic actions */ +class CharacteristicCallbacks: public NimBLECharacteristicCallbacks { + void onRead(NimBLECharacteristic* pCharacteristic){ + Serial.print(pCharacteristic->getUUID().toString().c_str()); + Serial.print(": onRead(), value: "); + Serial.println(pCharacteristic->getValue().c_str()); + }; + + void onWrite(NimBLECharacteristic* pCharacteristic) { + Serial.print(pCharacteristic->getUUID().toString().c_str()); + Serial.print(": onWrite(), value: "); + Serial.println(pCharacteristic->getValue().c_str()); + }; + /** Called before notification or indication is sent, + * the value can be changed here before sending if desired. + */ + void onNotify(NimBLECharacteristic* pCharacteristic) { + Serial.println("Sending notification to clients"); + }; + + + /** The status returned in status is defined in NimBLECharacteristic.h. + * The value returned in code is the NimBLE host return code. + */ + void onStatus(NimBLECharacteristic* pCharacteristic, Status status, int code) { + String str = ("Notification/Indication status code: "); + str += status; + str += ", return code: "; + str += code; + str += ", "; + str += NimBLEUtils::returnCodeToString(code); + Serial.println(str); + }; +}; + +/** Handler class for descriptor actions */ +class DescriptorCallbacks : public NimBLEDescriptorCallbacks { + void onWrite(NimBLEDescriptor* pDescriptor) { + if(pDescriptor->getUUID().equals(NimBLEUUID("2902"))) { + /** Cast to NimBLE2902 to use the class specific functions. **/ + NimBLE2902* p2902 = (NimBLE2902*)pDescriptor; + if(p2902->getNotifications()) { + Serial.println("Client Subscribed to notfications"); + } else { + Serial.println("Client Unubscribed to notfications"); + } + } else { + std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength()); + Serial.print("Descriptor witten value:"); + Serial.println(dscVal.c_str()); + } + }; + + void onRead(NimBLEDescriptor* pDescriptor) { + Serial.print(pDescriptor->getUUID().toString().c_str()); + Serial.println(" Descriptor read"); + }; +}; + + +/** Define callback instances globally to use for multiple Charateristics \ Descriptors */ +static DescriptorCallbacks dscCallbacks; +static CharacteristicCallbacks chrCallbacks; + + +void setup() { + Serial.begin(115200); + Serial.println("Starting NimBLE Server"); + + /** sets device name */ + NimBLEDevice::init("NimBLE-Arduino"); + + /** Optional: set the transmit power, default is 3db */ + NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */ + + /** Set the IO capabilities of the device, each option will trigger a different pairing method. + * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing + * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing + * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing + */ + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison + + /** 2 different ways to set security - both calls achieve the same result. + * no bonding, no man in the middle protection, secure connections. + * + * These are the default values, only shown here for demonstration. + */ + //NimBLEDevice::setSecurityAuth(false, false, true); + NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); + + pServer = NimBLEDevice::createServer(); + pServer->setCallbacks(new ServerCallbacks()); + + NimBLEService* pDeadService = pServer->createService("DEAD"); + NimBLECharacteristic* pBeefCharacteristic = pDeadService->createCharacteristic( + "BEEF", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + /** Require a secure connection for read and write access */ + NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted + NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted + ); + + pBeefCharacteristic->setValue("Burger"); + pBeefCharacteristic->setCallbacks(&chrCallbacks); + + /** 2902 and 2904 descriptors are a special case, when createDescriptor is called with + * either of those uuid's it will create the associated class with the correct properties + * and sizes. However we must cast the returned reference to the correct type as the method + * only returns a pointer to the base NimBLEDescriptor class. + */ + NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904"); + pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8); + pBeef2904->setCallbacks(&dscCallbacks); + + + NimBLEService* pBaadService = pServer->createService("BAAD"); + NimBLECharacteristic* pFoodCharacteristic = pBaadService->createCharacteristic( + "F00D", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY + ); + + pFoodCharacteristic->setValue("Fries"); + pFoodCharacteristic->setCallbacks(&chrCallbacks); + + /** Custom descriptor: Arguments are UUID, Properties, max length in bytes of the value */ + NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor( + "C01D", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE| + NIMBLE_PROPERTY::WRITE_ENC, // only allow writing if paired / encrypted + 20 + ); + pC01Ddsc->setValue("Send it back!"); + pC01Ddsc->setCallbacks(&dscCallbacks); + + /** Note a 2902 descriptor does NOT need to be created as any chactateristic with + * notification or indication properties will have one created autmatically. + * Manually creating it is only useful if you wish to handle callback functions + * as shown here. Otherwise this can be removed without loss of functionality. + */ + NimBLE2902* pFood2902 = (NimBLE2902*)pFoodCharacteristic->createDescriptor("2902"); + pFood2902->setCallbacks(&dscCallbacks); + + /** Start the services when finished creating all Characteristics and Descriptors */ + pDeadService->start(); + pBaadService->start(); + + NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); + /** Add the services to the advertisment data **/ + pAdvertising->addServiceUUID(pDeadService->getUUID()); + pAdvertising->addServiceUUID(pBaadService->getUUID()); + /** If your device is battery powered you may consider setting scan response + * to false as it will extend battery life at the expense of less data sent. + */ + pAdvertising->setScanResponse(true); + pAdvertising->start(); + + Serial.println("Advertising Started"); +} + + +void loop() { + /** Do your thing here, this just spams notifications to all connected clients */ + if(pServer->getConnectedCount()) { + NimBLEService* pSvc = pServer->getServiceByUUID("BAAD"); + if(pSvc) { + NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D"); + if(pChr) { + pChr->notify(true); + } + } + } + + delay(2000); +} diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino new file mode 100644 index 000000000..0d4ab94b4 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino @@ -0,0 +1,194 @@ +/** + * A BLE client example that is rich in capabilities. + * There is a lot new capabilities implemented. + * author unknown + * updated by chegewara + * updated for NimBLE by H2zero + */ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include "BLEDevice.h" +***********************/ +#include "NimBLEDevice.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8"); + +static boolean doConnect = false; +static boolean connected = false; +static boolean doScan = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; +static BLEAdvertisedDevice* myDevice; + +static void notifyCallback( + BLERemoteCharacteristic* pBLERemoteCharacteristic, + uint8_t* pData, + size_t length, + bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); + Serial.print("data: "); + Serial.println((char*)pData); +} + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class MyClientCallback : public BLEClientCallbacks { + void onConnect(BLEClient* pclient) { + } + + void onDisconnect(BLEClient* pclient) { + connected = false; + Serial.println("onDisconnect"); + } +/***************** New - Security handled here ******************** +****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Client PassKeyRequest"); + return 123456; + } + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } +/*******************************************************************/ +}; + +bool connectToServer() { + Serial.print("Forming a connection to "); + Serial.println(myDevice->getAddress().toString().c_str()); + + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + pClient->setClientCallbacks(new MyClientCallback()); + + // Connect to the remove BLE Server. + pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private) + Serial.println(" - Connected to server"); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found our service"); + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + Serial.print("Failed to find our characteristic UUID: "); + Serial.println(charUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + if(pRemoteCharacteristic->canRead()) { + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + } + + if(pRemoteCharacteristic->canNotify()) + pRemoteCharacteristic->registerForNotify(notifyCallback); + + connected = true; + return true; +} + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + +/*** Only a reference to the advertised device is passed now + void onResult(BLEAdvertisedDevice advertisedDevice) { **/ + void onResult(BLEAdvertisedDevice* advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice->toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. +/******************************************************************************** + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { +********************************************************************************/ + if (advertisedDevice->haveServiceUUID() && advertisedDevice->isAdvertisingService(serviceUUID)) { + + BLEDevice::getScan()->stop(); +/******************************************************************* + myDevice = new BLEAdvertisedDevice(advertisedDevice); +*******************************************************************/ + myDevice = advertisedDevice; /** Just save the reference now, no need to copy the object */ + doConnect = true; + doScan = true; + + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +void setup() { + Serial.begin(115200); + Serial.println("Starting Arduino BLE Client application..."); + BLEDevice::init(""); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 5 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setInterval(1349); + pBLEScan->setWindow(449); + pBLEScan->setActiveScan(true); + pBLEScan->start(5, false); +} // End of setup. + + +// This is the Arduino main loop function. +void loop() { + + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. Once we are + // connected we set the connected flag to be true. + if (doConnect == true) { + if (connectToServer()) { + Serial.println("We are now connected to the BLE Server."); + } else { + Serial.println("We have failed to connect to the server; there is nothin more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, update the characteristic each time we are reached + // with the current time since boot. + if (connected) { + String newValue = "Time since boot: " + String(millis()/1000); + Serial.println("Setting new characteristic value to \"" + newValue + "\""); + + // Set the characteristic's value to be the array of bytes that is actually a string. + /*** Note: write / read value now returns true if successful, false otherwise - try again or disconnect ***/ + pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); + }else if(doScan){ + BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino new file mode 100644 index 000000000..86b97def3 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino @@ -0,0 +1,118 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by pcbreflux +*/ + + +/* + Create a BLE server that will send periodic iBeacon frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ + + +/** NimBLE differences highlighted in comment blocks **/ + + +#include "sys/time.h" +/*******original******** +#include "BLEDevice.h" +#include "BLEUtils.h" +#include "BLEBeacon.h" +***********************/ +#include "NimBLEDevice.h" +#include "NimBLEBeacon.h" +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t temprature_sens_read(); +//uint8_t g_phyFuns; + +#ifdef __cplusplus +} +#endif + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +BLEAdvertising *pAdvertising; +struct timeval now; + +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) + +void setBeacon() { + + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!) + oBeacon.setProximityUUID(BLEUUID(BEACON_UUID)); + oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16); + oBeacon.setMinor(bootcount&0xFFFF); + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04 + + std::string strServiceData = ""; + + strServiceData += (char)26; // Len + strServiceData += (char)0xFF; // Type + strServiceData += oBeacon.getData(); + oAdvertisementData.addData(strServiceData); + + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); + /** pAdvertising->setAdvertisementType(ADV_TYPE_NONCONN_IND); + * Advertising mode. Can be one of following constants: + * - BLE_GAP_CONN_MODE_NON (non-connectable; 3.C.9.3.2). + * - BLE_GAP_CONN_MODE_DIR (directed-connectable; 3.C.9.3.3). + * - BLE_GAP_CONN_MODE_UND (undirected-connectable; 3.C.9.3.4). + */ + pAdvertising->setAdvertisementType(BLE_GAP_CONN_MODE_NON); + +} + +void setup() { + + + Serial.begin(115200); + gettimeofday(&now, NULL); + + Serial.printf("start ESP32 %d\n",bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last); + + last = now.tv_sec; + + // Create the BLE Device + BLEDevice::init(""); + + // Create the BLE Server + // BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage + + pAdvertising = BLEDevice::getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started..."); + delay(100); + pAdvertising->stop(); + Serial.printf("enter deep sleep\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() { +} diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino new file mode 100644 index 000000000..f57c52e2b --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino @@ -0,0 +1,147 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + updated by chegewara + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b + And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + A connect hander associated with the server starts a background task that performs notification + every couple of seconds. +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ +#include + +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint32_t value = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } +/***************** New - Security handled here ******************** +****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server PassKeyRequest"); + return 123456; + } + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } +/*******************************************************************/ +}; + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("ESP32"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + /******* Enum Type NIMBLE_PROPERTY now ******* + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + **********************************************/ + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY | + NIMBLE_PROPERTY::INDICATE + ); + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + // Create a BLE Descriptor + /*********** New createDescriptor method ************ + NOTE: There is no need to create the 2902 descriptor + as it will be created automatically if notifications + or indications are enabled on a characteristic. + + pCharacteristic->addDescriptor(new BLE2902()); + ****************************************************/ + /** Add properties the same way as characteristics now **/ + + pCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/); + // Start the service + pService->start(); + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter + BLEDevice::startAdvertising(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue((uint8_t*)&value, 4); + pCharacteristic->notify(); + value++; + delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms + } + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino new file mode 100644 index 000000000..86cdaf46f --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino @@ -0,0 +1,49 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ + +#include + +int scanTime = 5; //In seconds +BLEScan* pBLEScan; + +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /*** Only a reference to the advertised device is passed now + void onResult(BLEAdvertisedDevice advertisedDevice) { **/ + void onResult(BLEAdvertisedDevice* advertisedDevice) { + /** Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str()); **/ + Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str()); + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Scanning..."); + + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); //create new scan + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); // less or equal setInterval value +} + +void loop() { + // put your main code here, to run repeatedly: + BLEScanResults foundDevices = pBLEScan->start(scanTime, false); + Serial.print("Devices found: "); + Serial.println(foundDevices.getCount()); + Serial.println("Scan done!"); + pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + delay(2000); +} \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino new file mode 100644 index 000000000..82aa70aaa --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino @@ -0,0 +1,56 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini + updates by chegewara +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +***********************/ + +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("Long name works now"); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + /***** Enum Type NIMBLE_PROPERTY now ***** + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + *****************************************/ + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + // BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue + pAdvertising->setMinPreferred(0x12); + BLEDevice::startAdvertising(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino new file mode 100644 index 000000000..025266650 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino @@ -0,0 +1,151 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + updated by chegewara + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b + And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + A connect hander associated with the server starts a background task that performs notification + every couple of seconds. +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ +#include + +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint32_t value = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + BLEDevice::startAdvertising(); + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } + /***************** New - Security handled here ******************** + ****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server PassKeyRequest"); + return 123456; + } + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } + /*******************************************************************/ +}; + + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("ESP32"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + /******* Enum Type NIMBLE_PROPERTY now ******* + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + **********************************************/ + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY | + NIMBLE_PROPERTY::INDICATE + ); + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + // Create a BLE Descriptor + /*********** New createDescriptor method ************ + NOTE: There is no need to create the 2902 descriptor + as it will be created automatically if notifications + or indications are enabled on a characteristic. + + pCharacteristic->addDescriptor(new BLE2902()); + ****************************************************/ + /** Add properties the same way as characteristics now **/ + + pCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/); + + // Start the service + pService->start(); + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter + BLEDevice::startAdvertising(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue((uint8_t*)&value, 4); + pCharacteristic->notify(); + value++; + delay(10); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms + } + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino new file mode 100644 index 000000000..b83470cf6 --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino @@ -0,0 +1,165 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E + Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE" + Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY" + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + In this example rxValue is the data received (only accessible inside that function). + And txValue is the data to be sent, in this example just a byte incremented every second. +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ +#include + +BLEServer *pServer = NULL; +BLECharacteristic * pTxCharacteristic; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint8_t txValue = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID +#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" +#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" + + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } + /***************** New - Security handled here ******************** + ****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server PassKeyRequest"); + return 123456; + } + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } + /*******************************************************************/ +}; + +class MyCallbacks: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic *pCharacteristic) { + std::string rxValue = pCharacteristic->getValue(); + + if (rxValue.length() > 0) { + Serial.println("*********"); + Serial.print("Received Value: "); + for (int i = 0; i < rxValue.length(); i++) + Serial.print(rxValue[i]); + + Serial.println(); + Serial.println("*********"); + } + } +}; + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("UART Service"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pTxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_TX, + /******* Enum Type NIMBLE_PROPERTY now ******* + BLECharacteristic::PROPERTY_NOTIFY + ); + **********************************************/ + NIMBLE_PROPERTY::NOTIFY + ); + + /******* New createDescriptor method ******** + NOTE: There is no need to create the 2902 descriptor + as it will be created automatically if notifications or + indications are enabled on a characteristic. + + pCharacteristic->addDescriptor(new BLE2902()); + ********************************************/ + /** Add properties the same way as characteristics now **/ + pTxCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/); + + BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_RX, + /******* Enum Type NIMBLE_PROPERTY now ******* + BLECharacteristic::PROPERTY_WRITE + ); + *********************************************/ + NIMBLE_PROPERTY::WRITE + ); + + pRxCharacteristic->setCallbacks(new MyCallbacks()); + + // Start the service + pService->start(); + + // Start advertising + pServer->getAdvertising()->start(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + + if (deviceConnected) { + pTxCharacteristic->setValue(&txValue, 1); + pTxCharacteristic->notify(); + txValue++; + delay(10); // bluetooth stack will go into congestion, if too many packets are sent + } + + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino new file mode 100644 index 000000000..b1eb0f83e --- /dev/null +++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino @@ -0,0 +1,75 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +***********************/ +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +class MyCallbacks: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic *pCharacteristic) { + std::string value = pCharacteristic->getValue(); + + if (value.length() > 0) { + Serial.println("*********"); + Serial.print("New value: "); + for (int i = 0; i < value.length(); i++) + Serial.print(value[i]); + + Serial.println(); + Serial.println("*********"); + } + } +}; + +void setup() { + Serial.begin(115200); + + Serial.println("1- Download and install an BLE scanner app in your phone"); + Serial.println("2- Scan for BLE devices in the app"); + Serial.println("3- Connect to MyESP32"); + Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something"); + Serial.println("5- See the magic =)"); + + BLEDevice::init("MyESP32"); + BLEServer *pServer = BLEDevice::createServer(); + + BLEService *pService = pServer->createService(SERVICE_UUID); + + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + /***** Enum Type NIMBLE_PROPERTY now ***** + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + *****************************************/ + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE + ); + + pCharacteristic->setCallbacks(new MyCallbacks()); + + pCharacteristic->setValue("Hello World"); + pService->start(); + + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/library.properties b/libesp32/NimBLE-Arduino/library.properties new file mode 100644 index 000000000..236fcd606 --- /dev/null +++ b/libesp32/NimBLE-Arduino/library.properties @@ -0,0 +1,10 @@ +name=NimBLE-Arduino +version=0.9.0 +author=H2zero +maintainer=h2zero +sentence=NimBLE library for Arduino +paragraph=A lighter-weight alternative to the bluedroid library for esp32. +url=https://github.com/h2zero/NimBLE-Arduino +category=Communication +architectures=esp32 +includes=NimBLEDevice.h \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/CODING_STANDARDS.md b/libesp32/NimBLE-Arduino/src/CODING_STANDARDS.md new file mode 100644 index 000000000..d14b9fdb1 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/CODING_STANDARDS.md @@ -0,0 +1,267 @@ +# Coding Style for Apache NimBLE + +Apache NimBLE project is part of Apache Mynewt projct and follows its coding +style. + +# Coding Style for Apache Mynewt Core + +This document is meant to define the coding style for Apache Mynewt, and +all subprojects of Apache Mynewt. This covers C and Assembly coding +conventions, *only*. Other languages (such as Go), have their own +coding conventions. + +## Headers + +* All files that are newly written, should have the Apache License clause +at the top of them. + +* For files that are copied from another source, but contain an Apache +compatible license, the original license header shall be maintained. + +* For more information on applying the Apache license, the definitive +source is here: http://www.apache.org/dev/apply-license.html + +* The Apache License clause for the top of files is as follows: + +```no-highlight +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +``` + +## Whitespace and Braces + +* Code must be indented to 4 spaces, tabs should not be used. + +* Do not add whitespace at the end of a line. + +* Put space after keywords (for, if, return, switch, while). + +* for, else, if, while statements must have braces around their +code blocks, i.e., do: + +``` + if (x) { + assert(0); + } else { + assert(0); + } +``` + +Not: + +``` + if (x) + assert(0); + else + assert(0); +``` + +* Braces for statements must be on the same line as the statement. Good: + +``` + for (i = 0; i < 10; i++) { + if (i == 5) { + break; + } else { + continue; + } + } +``` + +Not: + +``` + for (i = 0; i < 10; i++) + { <-- brace must be on same line as for + if (i == 5) { + break; + } <-- no new line between else + else { + continue; + } + } +``` + +* After a function declaration, the braces should be on a newline, i.e. do: + +``` + static void * + function(int var1, int var2) + { +``` + +not: + +``` + static void * + function(int var1, int var2) { +``` + +## Line Length and Wrap + +* Line length should never exceed 79 columns. + +* When you have to wrap a long statement, put the operator at the end of the + line. i.e.: + +``` + if (x && + y == 10 && + b) +``` + +Not: + +``` + if (x + && y == 10 + && b) +``` + +## Comments + +* No C++ style comments allowed. + +* When using a single line comment, put it above the line of code that you +intend to comment, i.e., do: + +``` + /* check variable */ + if (a) { +``` + +Not: + +``` + if (a) { /* check variable */ +``` + + +* All public APIs should be commented with Doxygen style comments describing +purpose, parameters and return values. Private APIs need not be documented. + + +## Header files + +* Header files must contain the following structure: + * Apache License (see above) + * ```#ifdef``` aliasing, to prevent multiple includes + * ```#include``` directives for other required header files + * ```#ifdef __cplusplus``` wrappers to maintain C++ friendly APIs + * Contents of the header file + +* ```#ifdef``` aliasing, shall be in the following format, where +the package name is "os" and the file name is "callout.h": + +```no-highlight +#ifndef _OS_CALLOUT_H +#define _OS_CALLOUT_H +``` + +* ```#include``` directives must happen prior to the cplusplus +wrapper. + +* The cplusplus wrapper must have the following format, and precedes +any contents of the header file: + +```no-highlight +#ifdef __cplusplus +#extern "C" { +##endif +``` + +## Naming + +* Names of functions, structures and variables must be in all lowercase. + +* Names should be as short as possible, but no shorter. + +* Globally visible names must be prefixed with the name of the module, +followed by the '_' character, i.e.: + +``` + os_callout_init(&c) +``` + +Not: + +``` + callout_init(c) +``` + +## Functions + +* No spaces after function names when calling a function, i.e, do: + +``` + rc = function(a) +``` + +Not: + +``` + rc = function (a) +``` + + +* Arguments to function calls should have spaces between the comma, i.e. do: + +``` + rc = function(a, b) +``` + +Not: + +``` + rc = function(a,b) +``` + +* The function type must be on a line by itself preceding the function, i.e. do: + +``` + static void * + function(int var1, int var2) + { +``` + +Not: + +``` + static void *function(int var1, int var2) + { +``` + +* In general, for functions that return values that denote success or error, 0 +shall be success, and non-zero shall be the failure code. + +## Variables and Macros + +* Do not use typedefs for structures. This makes it impossible for +applications to use pointers to those structures opaquely. + +* typedef may be used for non-structure types, where it is beneficial to +hide or alias the underlying type used (e.g. ```os_time_t```.) Indicate +typedefs by applying the ```_t``` marker to them. + +* Place all function-local variable definitions at the top of the function body, before any statements. + +## Compiler Directives + +* Code must compile cleanly with -Wall enabled. + diff --git a/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp b/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp new file mode 100644 index 000000000..2cc7a988f --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp @@ -0,0 +1,309 @@ +/* + * FreeRTOS.cpp + * + * Created on: Feb 24, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#include "FreeRTOS.h" +#include "NimBLELog.h" + +#include // Include the base FreeRTOS definitions +#include // Include the task definitions +#include // Include the semaphore definitions +#include + +static const char* LOG_TAG = "FreeRTOS"; + + +/** + * Sleep for the specified number of milliseconds. + * @param[in] ms The period in milliseconds for which to sleep. + */ +void FreeRTOS::sleep(uint32_t ms) { + ::vTaskDelay(ms / portTICK_PERIOD_MS); +} // sleep + + +/** + * Start a new task. + * @param[in] task The function pointer to the function to be run in the task. + * @param[in] taskName A string identifier for the task. + * @param[in] param An optional parameter to be passed to the started task. + * @param[in] stackSize An optional paremeter supplying the size of the stack in which to run the task. + */ +void FreeRTOS::startTask(void task(void*), std::string taskName, void* param, uint32_t stackSize) { + ::xTaskCreate(task, taskName.data(), stackSize, param, 5, NULL); +} // startTask + + +/** + * Delete the task. + * @param[in] pTask An optional handle to the task to be deleted. If not supplied the calling task will be deleted. + */ +void FreeRTOS::deleteTask(TaskHandle_t pTask) { + ::vTaskDelete(pTask); +} // deleteTask + + +/** + * Get the time in milliseconds since the %FreeRTOS scheduler started. + * @return The time in milliseconds since the %FreeRTOS scheduler started. + */ +uint32_t FreeRTOS::getTimeSinceStart() { + return (uint32_t) (xTaskGetTickCount() * portTICK_PERIOD_MS); +} // getTimeSinceStart + + +/** + * @brief Wait for a semaphore to be released by trying to take it and + * then releasing it again. + * @param [in] owner A debug tag. + * @return The value associated with the semaphore. + */ +uint32_t FreeRTOS::Semaphore::wait(std::string owner) { + NIMBLE_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); + + if (m_usePthreads) { + pthread_mutex_lock(&m_pthread_mutex); + } else { + xSemaphoreTake(m_semaphore, portMAX_DELAY); + } + + if (m_usePthreads) { + pthread_mutex_unlock(&m_pthread_mutex); + } else { + xSemaphoreGive(m_semaphore); + } + + NIMBLE_LOGD(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str()); + return m_value; +} // wait + + +/** + * @brief Wait for a semaphore to be released in a given period of time by trying to take it and + * then releasing it again. The value associated with the semaphore can be taken by value() call after return + * @param [in] owner A debug tag. + * @param [in] timeoutMs timeout to wait in ms. + * @return True if we took the semaphore within timeframe. + */ +bool FreeRTOS::Semaphore::timedWait(std::string owner, uint32_t timeoutMs) { + NIMBLE_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); + + if (m_usePthreads && timeoutMs != portMAX_DELAY) { + assert(false); // We apparently don't have a timed wait for pthreads. + } + + auto ret = pdTRUE; + + if (m_usePthreads) { + pthread_mutex_lock(&m_pthread_mutex); + } else { + ret = xSemaphoreTake(m_semaphore, timeoutMs); + } + + if (m_usePthreads) { + pthread_mutex_unlock(&m_pthread_mutex); + } else { + xSemaphoreGive(m_semaphore); + } + + NIMBLE_LOGD(LOG_TAG, "<< wait: Semaphore %s released: %d", toString().c_str(), ret); + return ret; +} // wait + + +FreeRTOS::Semaphore::Semaphore(std::string name) { + m_usePthreads = false; // Are we using pThreads or FreeRTOS? + if (m_usePthreads) { + pthread_mutex_init(&m_pthread_mutex, nullptr); + } else { + //m_semaphore = xSemaphoreCreateMutex(); + m_semaphore = xSemaphoreCreateBinary(); + xSemaphoreGive(m_semaphore); + } + + m_name = name; + m_owner = std::string(""); + m_value = 0; +} + + +FreeRTOS::Semaphore::~Semaphore() { + if (m_usePthreads) { + pthread_mutex_destroy(&m_pthread_mutex); + } else { + vSemaphoreDelete(m_semaphore); + } +} + + +/** + * @brief Give a semaphore. + * The Semaphore is given. + */ +void FreeRTOS::Semaphore::give() { + NIMBLE_LOGD(LOG_TAG, "Semaphore giving: %s", toString().c_str()); + m_owner = std::string(""); + + if (m_usePthreads) { + pthread_mutex_unlock(&m_pthread_mutex); + } else { + xSemaphoreGive(m_semaphore); + } +// #ifdef ARDUINO_ARCH_ESP32 +// FreeRTOS::sleep(10); +// #endif + +} // Semaphore::give + + +/** + * @brief Give a semaphore. + * The Semaphore is given with an associated value. + * @param [in] value The value to associate with the semaphore. + */ +void FreeRTOS::Semaphore::give(uint32_t value) { + m_value = value; + give(); +} // give + + +/** + * @brief Give a semaphore from an ISR. + */ +void FreeRTOS::Semaphore::giveFromISR() { + BaseType_t higherPriorityTaskWoken; + if (m_usePthreads) { + assert(false); + } else { + xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken); + } +} // giveFromISR + + +/** + * @brief Take a semaphore. + * Take a semaphore and wait indefinitely. + * @param [in] owner The new owner (for debugging) + * @return True if we took the semaphore. + */ +bool FreeRTOS::Semaphore::take(std::string owner) { + NIMBLE_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); + bool rc = false; + if (m_usePthreads) { + pthread_mutex_lock(&m_pthread_mutex); + } else { + rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY) == pdTRUE; + } + m_owner = owner; + if (rc) { + NIMBLE_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + } else { + NIMBLE_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + } + return rc; +} // Semaphore::take + + +/** + * @brief Take a semaphore. + * Take a semaphore but return if we haven't obtained it in the given period of milliseconds. + * @param [in] timeoutMs Timeout in milliseconds. + * @param [in] owner The new owner (for debugging) + * @return True if we took the semaphore. + */ +bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { + NIMBLE_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); + bool rc = false; + if (m_usePthreads) { + assert(false); // We apparently don't have a timed wait for pthreads. + } else { + rc = ::xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS) == pdTRUE; + } + m_owner = owner; + if (rc) { + NIMBLE_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + } else { + NIMBLE_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + } + return rc; +} // Semaphore::take + + + +/** + * @brief Create a string representation of the semaphore. + * @return A string representation of the semaphore. + */ +std::string FreeRTOS::Semaphore::toString() { + char hex[9]; + std::string res = "name: " + m_name + " (0x"; + snprintf(hex, sizeof(hex), "%08x", (uint32_t)m_semaphore); + res += hex; + res += "), owner: " + m_owner; + return res; +} // toString + + +/** + * @brief Set the name of the semaphore. + * @param [in] name The name of the semaphore. + */ +void FreeRTOS::Semaphore::setName(std::string name) { + m_name = name; +} // setName + + +/** + * @brief Create a ring buffer. + * @param [in] length The amount of storage to allocate for the ring buffer. + * @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF. + */ +#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t +Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) { +#else +Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) { +#endif + m_handle = ::xRingbufferCreate(length, type); +} // Ringbuffer + + +Ringbuffer::~Ringbuffer() { + ::vRingbufferDelete(m_handle); +} // ~Ringbuffer + + +/** + * @brief Receive data from the buffer. + * @param [out] size On return, the size of data returned. + * @param [in] wait How long to wait. + * @return A pointer to the storage retrieved. + */ +void* Ringbuffer::receive(size_t* size, TickType_t wait) { + return ::xRingbufferReceive(m_handle, size, wait); +} // receive + + +/** + * @brief Return an item. + * @param [in] item The item to be returned/released. + */ +void Ringbuffer::returnItem(void* item) { + ::vRingbufferReturnItem(m_handle, item); +} // returnItem + + +/** + * @brief Send data to the buffer. + * @param [in] data The data to place into the buffer. + * @param [in] length The length of data to place into the buffer. + * @param [in] wait How long to wait before giving up. The default is to wait indefinitely. + * @return + */ +bool Ringbuffer::send(void* data, size_t length, TickType_t wait) { + return ::xRingbufferSend(m_handle, data, length, wait) == pdTRUE; +} // send + + diff --git a/libesp32/NimBLE-Arduino/src/FreeRTOS.h b/libesp32/NimBLE-Arduino/src/FreeRTOS.h new file mode 100644 index 000000000..07877275f --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/FreeRTOS.h @@ -0,0 +1,78 @@ +/* + * FreeRTOS.h + * + * Created on: Feb 24, 2017 + * Author: kolban + */ + +#ifndef MAIN_FREERTOS_H_ +#define MAIN_FREERTOS_H_ + +#include // Include the base FreeRTOS definitions. +#include // Include the task definitions. +#include // Include the semaphore definitions. +#include // Include the ringbuffer definitions. + +#include +#include +#include + + +/** + * @brief Interface to %FreeRTOS functions. + */ +class FreeRTOS { +public: + static void sleep(uint32_t ms); + static void startTask(void task(void*), std::string taskName, void* param = nullptr, uint32_t stackSize = 2048); + static void deleteTask(TaskHandle_t pTask = nullptr); + + static uint32_t getTimeSinceStart(); + + class Semaphore { + public: + Semaphore(std::string owner = ""); + ~Semaphore(); + void give(); + void give(uint32_t value); + void giveFromISR(); + void setName(std::string name); + bool take(std::string owner = ""); + bool take(uint32_t timeoutMs, std::string owner = ""); + std::string toString(); + bool timedWait(std::string owner = "", uint32_t timeoutMs = portMAX_DELAY); + uint32_t wait(std::string owner = ""); + uint32_t value(){ return m_value; }; + + private: + SemaphoreHandle_t m_semaphore; + pthread_mutex_t m_pthread_mutex; + std::string m_name; + std::string m_owner; + uint32_t m_value; + bool m_usePthreads; + + }; +}; + + +/** + * @brief Ringbuffer. + */ +class Ringbuffer { +public: +#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t + Ringbuffer(size_t length, RingbufferType_t type = RINGBUF_TYPE_NOSPLIT); +#else + Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT); +#endif + ~Ringbuffer(); + + void* receive(size_t* size, TickType_t wait = portMAX_DELAY); + void returnItem(void* item); + bool send(void* data, size_t length, TickType_t wait = portMAX_DELAY); +private: + RingbufHandle_t m_handle; +}; + +#endif /* MAIN_FREERTOS_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/HIDKeyboardTypes.h b/libesp32/NimBLE-Arduino/src/HIDKeyboardTypes.h new file mode 100644 index 000000000..4e221d57f --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/HIDKeyboardTypes.h @@ -0,0 +1,402 @@ +/* Copyright (c) 2015 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: this file was pulled from different parts of the USBHID library, in mbed SDK + */ + +#ifndef KEYBOARD_DEFS_H +#define KEYBOARD_DEFS_H + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_VOLUME 3 + +/* Modifiers */ +enum MODIFIER_KEY { + KEY_CTRL = 1, + KEY_SHIFT = 2, + KEY_ALT = 4, +}; + + +enum MEDIA_KEY { + KEY_NEXT_TRACK, /*!< next Track Button */ + KEY_PREVIOUS_TRACK, /*!< Previous track Button */ + KEY_STOP, /*!< Stop Button */ + KEY_PLAY_PAUSE, /*!< Play/Pause Button */ + KEY_MUTE, /*!< Mute Button */ + KEY_VOLUME_UP, /*!< Volume Up Button */ + KEY_VOLUME_DOWN, /*!< Volume Down Button */ +}; + +enum FUNCTION_KEY { + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ + + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ + + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ +}; + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/HIDTypes.h b/libesp32/NimBLE-Arduino/src/HIDTypes.h new file mode 100644 index 000000000..64850ef8a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/HIDTypes.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USBCLASS_HID_TYPES +#define USBCLASS_HID_TYPES + +#include + +/* */ +#define HID_VERSION_1_11 (0x0111) + +/* HID Class */ +#define HID_CLASS (3) +#define HID_SUBCLASS_NONE (0) +#define HID_PROTOCOL_NONE (0) + +/* Descriptors */ +#define HID_DESCRIPTOR (33) +#define HID_DESCRIPTOR_LENGTH (0x09) +#define REPORT_DESCRIPTOR (34) + +/* Class requests */ +#define GET_REPORT (0x1) +#define GET_IDLE (0x2) +#define SET_REPORT (0x9) +#define SET_IDLE (0xa) + +/* HID Class Report Descriptor */ +/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ +/* of data as per HID Class standard */ + +/* Main items */ +#ifdef ARDUINO_ARCH_ESP32 +#define HIDINPUT(size) (0x80 | size) +#define HIDOUTPUT(size) (0x90 | size) +#else +#define INPUT(size) (0x80 | size) +#define OUTPUT(size) (0x90 | size) +#endif +#define FEATURE(size) (0xb0 | size) +#define COLLECTION(size) (0xa0 | size) +#define END_COLLECTION(size) (0xc0 | size) + +/* Global items */ +#define USAGE_PAGE(size) (0x04 | size) +#define LOGICAL_MINIMUM(size) (0x14 | size) +#define LOGICAL_MAXIMUM(size) (0x24 | size) +#define PHYSICAL_MINIMUM(size) (0x34 | size) +#define PHYSICAL_MAXIMUM(size) (0x44 | size) +#define UNIT_EXPONENT(size) (0x54 | size) +#define UNIT(size) (0x64 | size) +#define REPORT_SIZE(size) (0x74 | size) //bits +#define REPORT_ID(size) (0x84 | size) +#define REPORT_COUNT(size) (0x94 | size) //bytes +#define PUSH(size) (0xa4 | size) +#define POP(size) (0xb4 | size) + +/* Local items */ +#define USAGE(size) (0x08 | size) +#define USAGE_MINIMUM(size) (0x18 | size) +#define USAGE_MAXIMUM(size) (0x28 | size) +#define DESIGNATOR_INDEX(size) (0x38 | size) +#define DESIGNATOR_MINIMUM(size) (0x48 | size) +#define DESIGNATOR_MAXIMUM(size) (0x58 | size) +#define STRING_INDEX(size) (0x78 | size) +#define STRING_MINIMUM(size) (0x88 | size) +#define STRING_MAXIMUM(size) (0x98 | size) +#define DELIMITER(size) (0xa8 | size) + +/* HID Report */ +/* Where report IDs are used the first byte of 'data' will be the */ +/* report ID and 'length' will include this report ID byte. */ + +#define MAX_HID_REPORT_SIZE (64) + +typedef struct { + uint32_t length; + uint8_t data[MAX_HID_REPORT_SIZE]; +} HID_REPORT; + +#endif diff --git a/libesp32/NimBLE-Arduino/src/NOTICE b/libesp32/NimBLE-Arduino/src/NOTICE new file mode 100644 index 000000000..fc24c6abc --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NOTICE @@ -0,0 +1,8 @@ +Apache Mynewt NimBLE +Copyright 2015-2018 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Portions of this software were developed at +Runtime Inc, copyright 2015. diff --git a/libesp32/NimBLE-Arduino/src/NimBLE2902.cpp b/libesp32/NimBLE-Arduino/src/NimBLE2902.cpp new file mode 100644 index 000000000..b23206032 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLE2902.cpp @@ -0,0 +1,75 @@ +/* + * NimBLE2902.cpp + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLE2902.cpp + * + * Created on: Jun 25, 2017 + * Author: kolban + */ + + +/* + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLE2902.h" + +NimBLE2902::NimBLE2902(NimBLECharacteristic* pCharacterisitic) +: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2902), + BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE, + 2, pCharacterisitic) +{ + uint8_t data[2] = { 0, 0 }; + setValue(data, 2); +} // NimBLE2902 + + +/** + * @brief Get the notifications value. + * @return The notifications value. True if notifications are enabled and false if not. + */ +bool NimBLE2902::getNotifications() { + return (getValue()[0] & (1 << 0)) != 0; +} // getNotifications + + +/** + * @brief Get the indications value. + * @return The indications value. True if indications are enabled and false if not. + */ +bool NimBLE2902::getIndications() { + return (getValue()[0] & (1 << 1)) != 0; +} // getIndications + + +/** + * @brief Set the indications flag. + * @param [in] flag The indications flag. + */ +void NimBLE2902::setIndications(bool flag) { + uint8_t *pValue = getValue(); + if (flag) pValue[0] |= 1 << 1; + else pValue[0] &= ~(1 << 1); +} // setIndications + + +/** + * @brief Set the notifications flag. + * @param [in] flag The notifications flag. + */ +void NimBLE2902::setNotifications(bool flag) { + uint8_t *pValue = getValue(); + if (flag) pValue[0] |= 1 << 0; + else pValue[0] &= ~(1 << 0); +} // setNotifications + +#endif \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLE2902.h b/libesp32/NimBLE-Arduino/src/NimBLE2902.h new file mode 100644 index 000000000..dcecbaa28 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLE2902.h @@ -0,0 +1,50 @@ +/* + * NimBLE2902.h + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLE2902.h + * + * Created on: Jun 25, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLE2902_H_ +#define MAIN_NIMBLE2902_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEDescriptor.h" + +#include + +#define NIMBLE_DESC_FLAG_NOTIFY 0x0001 +#define NIMBLE_DESC_FLAG_INDICATE 0x0002 + + +/** + * @brief Descriptor for Client Characteristic Configuration. + * + * This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + */ +class NimBLE2902: public NimBLEDescriptor { +public: + bool getNotifications(); + bool getIndications(); + void setNotifications(bool flag); + void setIndications(bool flag); +private: + NimBLE2902(NimBLECharacteristic* pCharacterisitic); + friend class NimBLECharacteristic; + std::map m_subscribedMap; + +}; // NimBLE2902 + +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_NIMBLE2902_H_ */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp b/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp new file mode 100644 index 000000000..59ef00580 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp @@ -0,0 +1,86 @@ +/* + * NimBLE2904.cpp + * + * Created: on March 13, 2020 + * Author H2zero + * + * Originally: + * + * BLE2904.cpp + * + * Created on: Dec 23, 2017 + * Author: kolban + */ + +/* + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLE2904.h" + + +NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacterisitic) +: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2904), + BLE_GATT_CHR_F_READ, + sizeof(BLE2904_Data), + pCharacterisitic) +{ + m_data.m_format = 0; + m_data.m_exponent = 0; + m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers + m_data.m_unit = 0; + m_data.m_description = 0; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // BLE2902 + + +/** + * @brief Set the description. + */ +void NimBLE2904::setDescription(uint16_t description) { + m_data.m_description = description; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} + + +/** + * @brief Set the exponent. + */ +void NimBLE2904::setExponent(int8_t exponent) { + m_data.m_exponent = exponent; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setExponent + + +/** + * @brief Set the format. + */ +void NimBLE2904::setFormat(uint8_t format) { + m_data.m_format = format; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setFormat + + +/** + * @brief Set the namespace. + */ +void NimBLE2904::setNamespace(uint8_t namespace_value) { + m_data.m_namespace = namespace_value; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setNamespace + + +/** + * @brief Set the units for this value. It should be one of the encoded values defined here: + * https://www.bluetooth.com/specifications/assigned-numbers/units + * @param [in] unit The type of units of this characteristic as defined by assigned numbers. + */ +void NimBLE2904::setUnit(uint16_t unit) { + m_data.m_unit = unit; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setUnit + +#endif \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLE2904.h b/libesp32/NimBLE-Arduino/src/NimBLE2904.h new file mode 100644 index 000000000..b2aafb0c6 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLE2904.h @@ -0,0 +1,82 @@ +/* + * NimBLE2904.h + * + * Created: on March 13, 2020 + * Author H2zero + * + * Originally: + * + * BLE2904.h + * + * Created on: Dec 23, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLE2904_H_ +#define MAIN_NIMBLE2904_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEDescriptor.h" + +struct BLE2904_Data { + uint8_t m_format; + int8_t m_exponent; + uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units + uint8_t m_namespace; + uint16_t m_description; + +} __attribute__((packed)); + +/** + * @brief Descriptor for Characteristic Presentation Format. + * + * This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + */ +class NimBLE2904: public NimBLEDescriptor { +public: + static const uint8_t FORMAT_BOOLEAN = 1; + static const uint8_t FORMAT_UINT2 = 2; + static const uint8_t FORMAT_UINT4 = 3; + static const uint8_t FORMAT_UINT8 = 4; + static const uint8_t FORMAT_UINT12 = 5; + static const uint8_t FORMAT_UINT16 = 6; + static const uint8_t FORMAT_UINT24 = 7; + static const uint8_t FORMAT_UINT32 = 8; + static const uint8_t FORMAT_UINT48 = 9; + static const uint8_t FORMAT_UINT64 = 10; + static const uint8_t FORMAT_UINT128 = 11; + static const uint8_t FORMAT_SINT8 = 12; + static const uint8_t FORMAT_SINT12 = 13; + static const uint8_t FORMAT_SINT16 = 14; + static const uint8_t FORMAT_SINT24 = 15; + static const uint8_t FORMAT_SINT32 = 16; + static const uint8_t FORMAT_SINT48 = 17; + static const uint8_t FORMAT_SINT64 = 18; + static const uint8_t FORMAT_SINT128 = 19; + static const uint8_t FORMAT_FLOAT32 = 20; + static const uint8_t FORMAT_FLOAT64 = 21; + static const uint8_t FORMAT_SFLOAT16 = 22; + static const uint8_t FORMAT_SFLOAT32 = 23; + static const uint8_t FORMAT_IEEE20601 = 24; + static const uint8_t FORMAT_UTF8 = 25; + static const uint8_t FORMAT_UTF16 = 26; + static const uint8_t FORMAT_OPAQUE = 27; + + void setDescription(uint16_t); + void setExponent(int8_t exponent); + void setFormat(uint8_t format); + void setNamespace(uint8_t namespace_value); + void setUnit(uint16_t unit); + +private: + NimBLE2904(NimBLECharacteristic* pCharacterisitic); + friend class NimBLECharacteristic; + BLE2904_Data m_data; +}; // BLE2904 + +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_NIMBLE2904_H_ */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp b/libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp new file mode 100644 index 000000000..074ce7e84 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp @@ -0,0 +1,108 @@ +/* + * NimBLEAddress.cpp + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEAddress.cpp + * + * Created on: Jul 2, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEAddress.h" +#include "NimBLEUtils.h" + + +/************************************************* +NOTE: NimBLE addresses are in INVERSE ORDER! +We will accomodate that fact in these methods. +*************************************************/ + +/** + * @brief Create an address from the native ESP32 representation. + * @param [in] address The native representation. + */ +NimBLEAddress::NimBLEAddress(ble_addr_t address) { + memcpy(m_address, address.val, 6); +} // BLEAddress + + +/** + * @brief Create an address from a hex string + * + * A hex string is of the format: + * ``` + * 00:00:00:00:00:00 + * ``` + * which is 17 characters in length. + * + * @param [in] stringAddress The hex representation of the address. + */ +NimBLEAddress::NimBLEAddress(std::string stringAddress) { + if (stringAddress.length() != 17) return; + + int data[6]; + sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[5], &data[4], &data[3], &data[2], &data[1], &data[0]); + m_address[0] = (uint8_t) data[0]; + m_address[1] = (uint8_t) data[1]; + m_address[2] = (uint8_t) data[2]; + m_address[3] = (uint8_t) data[3]; + m_address[4] = (uint8_t) data[4]; + m_address[5] = (uint8_t) data[5]; +} // BLEAddress + + +/** + * @brief Constructor for compatibility with bluedrioid esp library. + * @param [in] esp_bd_addr_t struct containing the address. + */ +NimBLEAddress::NimBLEAddress(esp_bd_addr_t address) { + NimBLEUtils::memrcpy(m_address, address, 6); +} // NimBLEAddress + + +/** + * @brief Determine if this address equals another. + * @param [in] otherAddress The other address to compare against. + * @return True if the addresses are equal. + */ +bool NimBLEAddress::equals(NimBLEAddress otherAddress) { + return memcmp(otherAddress.getNative(), m_address, 6) == 0; +} // equals + + +/** + * @brief Return the native representation of the address. + * @return The native representation of the address. + */ +uint8_t *NimBLEAddress::getNative() { + return m_address; +} // getNative + + +/** + * @brief Convert a BLE address to a string. + * + * A string representation of an address is in the format: + * + * ``` + * xx:xx:xx:xx:xx:xx + * ``` + * + * @return The string representation of the address. + */ +std::string NimBLEAddress::toString() { + auto size = 18; + char *res = (char*)malloc(size); + snprintf(res, size, "%02x:%02x:%02x:%02x:%02x:%02x", m_address[5], m_address[4], m_address[3], m_address[2], m_address[1], m_address[0]); + std::string ret(res); + free(res); + return ret; + +} // toString +#endif diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAddress.h b/libesp32/NimBLE-Arduino/src/NimBLEAddress.h new file mode 100644 index 000000000..ac1f26ca6 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEAddress.h @@ -0,0 +1,63 @@ +/* + * NimBLEAddress.h + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEAddress.h + * + * Created on: Jul 2, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEADDRESS_H_ +#define COMPONENTS_NIMBLEADDRESS_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "nimble/ble.h" +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include + +typedef enum { + BLE_ADDR_TYPE_PUBLIC = 0x00, + BLE_ADDR_TYPE_RANDOM = 0x01, + BLE_ADDR_TYPE_RPA_PUBLIC = 0x02, + BLE_ADDR_TYPE_RPA_RANDOM = 0x03, +} esp_nimble_addr_type_t; + +typedef uint8_t esp_ble_addr_type_t ; + +/// Bluetooth address length +#define ESP_BD_ADDR_LEN 6 + +/// Bluetooth device address +typedef uint8_t esp_bd_addr_t[ESP_BD_ADDR_LEN]; + + +/** + * @brief A %BLE device address. + * + * Every %BLE device has a unique address which can be used to identify it and form connections. + */ +class NimBLEAddress { +public: + NimBLEAddress(ble_addr_t address); + NimBLEAddress(esp_bd_addr_t address); + NimBLEAddress(std::string stringAddress); + bool equals(NimBLEAddress otherAddress); + uint8_t* getNative(); + std::string toString(); + +private: + uint8_t m_address[6]; +}; + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_NIMBLEADDRESS_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp b/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp new file mode 100644 index 000000000..ff0097a25 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp @@ -0,0 +1,546 @@ +/* + * NimBLEAdvertisedDevice.cpp + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEAdvertisedDevice.cpp + * + * Created on: Jul 3, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEAdvertisedDevice.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEAdvertisedDevice"; + + +/** + * @brief Constructor + */ +NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() { + m_advType = 0; + m_appearance = 0; + m_deviceType = 0; + m_manufacturerData = ""; + m_name = ""; + m_rssi = -9999; + m_serviceData = ""; + m_txPower = 0; + m_pScan = nullptr; + + m_haveAppearance = false; + m_haveManufacturerData = false; + m_haveName = false; + m_haveRSSI = false; + m_haveServiceData = false; + m_haveServiceUUID = false; + m_haveTXPower = false; + +} // NimBLEAdvertisedDevice + + +/** + * @brief Get the address. + * + * Every %BLE device exposes an address that is used to identify it and subsequently connect to it. + * Call this function to obtain the address of the advertised device. + * + * @return The address of the advertised device. + */ +NimBLEAddress NimBLEAdvertisedDevice::getAddress() { + return m_address; +} // getAddress + + +/** + * @brief Get the appearance. + * + * A %BLE device can declare its own appearance. The appearance is how it would like to be shown to an end user + * typcially in the form of an icon. + * + * @return The appearance of the advertised device. + */ +uint16_t NimBLEAdvertisedDevice::getAppearance() { + return m_appearance; +} // getAppearance + + +/** + * @brief Get the manufacturer data. + * @return The manufacturer data of the advertised device. + */ +std::string NimBLEAdvertisedDevice::getManufacturerData() { + return m_manufacturerData; +} // getManufacturerData + + +/** + * @brief Get the name. + * @return The name of the advertised device. + */ +std::string NimBLEAdvertisedDevice::getName() { + return m_name; +} // getName + + +/** + * @brief Get the RSSI. + * @return The RSSI of the advertised device. + */ +int NimBLEAdvertisedDevice::getRSSI() { + return m_rssi; +} // getRSSI + + +/** + * @brief Get the scan object that created this advertisement. + * @return The scan object. + */ +NimBLEScan* NimBLEAdvertisedDevice::getScan() { + return m_pScan; +} // getScan + + +/** + * @brief Get the service data. + * @return The ServiceData of the advertised device. + */ +std::string NimBLEAdvertisedDevice::getServiceData() { + return m_serviceData; +} //getServiceData + + +/** + * @brief Get the service data UUID. + * @return The service data UUID. + */ + +NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID() { + return m_serviceDataUUID; +} // getServiceDataUUID + + +/** + * @brief Get the Service UUID. + * @return The Service UUID of the advertised device. + */ + +NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful + return m_serviceUUIDs[0]; +} // getServiceUUID + + +/** + * @brief Check advertised serviced for existence required UUID + * @return Return true if service is advertised + */ +bool NimBLEAdvertisedDevice::isAdvertisingService(NimBLEUUID uuid){ + for (int i = 0; i < m_serviceUUIDs.size(); i++) { + NIMBLE_LOGI(LOG_TAG, "Comparing UUIDS: %s %s", m_serviceUUIDs[i].toString().c_str(), uuid.toString().c_str()); + if (m_serviceUUIDs[i].equals(uuid)) return true; + } + return false; +} + + +/** + * @brief Get the TX Power. + * @return The TX Power of the advertised device. + */ +int8_t NimBLEAdvertisedDevice::getTXPower() { + return m_txPower; +} // getTXPower + + +/** + * @brief Does this advertisement have an appearance value? + * @return True if there is an appearance value present. + */ +bool NimBLEAdvertisedDevice::haveAppearance() { + return m_haveAppearance; +} // haveAppearance + + +/** + * @brief Does this advertisement have manufacturer data? + * @return True if there is manufacturer data present. + */ +bool NimBLEAdvertisedDevice::haveManufacturerData() { + return m_haveManufacturerData; +} // haveManufacturerData + + +/** + * @brief Does this advertisement have a name value? + * @return True if there is a name value present. + */ +bool NimBLEAdvertisedDevice::haveName() { + return m_haveName; +} // haveName + + +/** + * @brief Does this advertisement have a signal strength value? + * @return True if there is a signal strength value present. + */ +bool NimBLEAdvertisedDevice::haveRSSI() { + return m_haveRSSI; +} // haveRSSI + + +/** + * @brief Does this advertisement have a service data value? + * @return True if there is a service data value present. + */ +bool NimBLEAdvertisedDevice::haveServiceData() { + return m_haveServiceData; +} // haveServiceData + + +/** + * @brief Does this advertisement have a service UUID value? + * @return True if there is a service UUID value present. + */ +bool NimBLEAdvertisedDevice::haveServiceUUID() { + return m_haveServiceUUID; +} // haveServiceUUID + + +/** + * @brief Does this advertisement have a transmission power value? + * @return True if there is a transmission power value present. + */ +bool NimBLEAdvertisedDevice::haveTXPower() { + return m_haveTXPower; +} // haveTXPower + + +/** + * @brief Parse the advertising pay load. + * + * The pay load is a buffer of bytes that is either 31 bytes long or terminated by + * a 0 length value. Each entry in the buffer has the format: + * [length][type][data...] + * + * The length does not include itself but does include everything after it until the next record. A record + * with a length value of 0 indicates a terminator. + * + * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile + */ + void NimBLEAdvertisedDevice::parseAdvertisement(ble_hs_adv_fields *fields) { + //char s[BLE_HS_ADV_MAX_SZ]; + uint8_t *u8p; + uint8_t length; + int i; + + if (fields->uuids16 != NULL) { + for (i = 0; i < fields->num_uuids16; i++) { + setServiceUUID(NimBLEUUID(fields->uuids16[i].value)); + } + } + + if (fields->uuids32 != NULL) { + for (i = 0; i < fields->num_uuids32; i++) { + setServiceUUID(NimBLEUUID(fields->uuids32[i].value)); + } + } + + if (fields->uuids128 != NULL) { + for (i = 0; i < fields->num_uuids128; i++) { + setServiceUUID(NimBLEUUID(&fields->uuids128[i])); + } + } + + if (fields->name != NULL) { + setName(std::string(reinterpret_cast(fields->name), fields->name_len)); + } + + if (fields->tx_pwr_lvl_is_present) { + setTXPower(fields->tx_pwr_lvl); + } + + if (fields->svc_data_uuid16 != NULL) { + + u8p = fields->svc_data_uuid16; + length = fields->svc_data_uuid16_len; + + if (length < 2) { + NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); + } + else{ + uint16_t uuid = *(uint16_t*)u8p; + setServiceDataUUID(NimBLEUUID(uuid)); + if (length > 2) { + setServiceData(std::string(reinterpret_cast(u8p + 2), length - 2)); + } + } + } + + if (fields->svc_data_uuid32 != NULL) { + + u8p = fields->svc_data_uuid16; + length = fields->svc_data_uuid16_len; + + if (length < 4) { + NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA"); + } + + uint32_t uuid = *(uint32_t*) u8p; + setServiceDataUUID(NimBLEUUID(uuid)); + if (length > 4) { + setServiceData(std::string(reinterpret_cast(u8p + 4), length - 4)); + } + } + + if (fields->svc_data_uuid128 != NULL) { + + u8p = fields->svc_data_uuid16; + length = fields->svc_data_uuid16_len; + + if (length < 16) { + NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA"); + } + + setServiceDataUUID(NimBLEUUID(u8p, (size_t)16, false)); + if (length > 16) { + setServiceData(std::string(reinterpret_cast(u8p + 16), length - 16)); + } + } + + if (fields->appearance_is_present) { + NIMBLE_LOGD(LOG_TAG, " appearance=0x%04x", fields->appearance); + setAppearance(fields->appearance); + } + +/**** TODO: create storage and fucntions for these parameters + if (fields->public_tgt_addr != NULL) { + NIMBLE_LOGD(LOG_TAG, " public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + NIMBLE_LOGD(LOG_TAG, "public_tgt_addr=%s ", addr_str(u8p)); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + NIMBLE_LOGD(LOG_TAG, "\n"); + } + + if (fields->slave_itvl_range != NULL) { + NIMBLE_LOGD(LOG_TAG, " slave_itvl_range="); + print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + NIMBLE_LOGD(LOG_TAG, "\n"); + } + + if (fields->adv_itvl_is_present) { + NIMBLE_LOGD(LOG_TAG, " adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->uri != NULL) { + NIMBLE_LOGD(LOG_TAG, " uri="); + print_bytes(fields->uri, fields->uri_len); + NIMBLE_LOGD(LOG_TAG, "\n"); + } +*/ + if (fields->mfg_data != NULL) { + setManufacturerData(std::string(reinterpret_cast(fields->mfg_data), fields->mfg_data_len)); + } + } //parseAdvertisement + + +/** + * @brief Set the address of the advertised device. + * @param [in] address The address of the advertised device. + */ +void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) { + m_address = address; +} // setAddress + + +/** + * @brief Set the adFlag for this device. + * @param [in] The discovered adFlag. + */ +void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) { + m_advType = advType; +} // setAdvType + + +/** + * @brief Set the appearance for this device. + * @param [in] The discovered appearance. + */ +void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) { + m_appearance = appearance; + m_haveAppearance = true; + NIMBLE_LOGD(LOG_TAG,"- appearance: %d", m_appearance); +} // setAppearance + + +/** + * @brief Set the manufacturer data for this device. + * @param [in] The discovered manufacturer data. + */ +void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) { + m_manufacturerData = manufacturerData; + m_haveManufacturerData = true; + + char* pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*) m_manufacturerData.data(), (uint8_t) m_manufacturerData.length()); + NIMBLE_LOGD(LOG_TAG,"- manufacturer data: %s", pHex); + free(pHex); +} // setManufacturerData + + +/** + * @brief Set the name for this device. + * @param [in] name The discovered name. + */ +void NimBLEAdvertisedDevice::setName(std::string name) { + m_name = name; + m_haveName = true; + NIMBLE_LOGD(LOG_TAG,"- setName(): name: %s", m_name.c_str()); +} // setName + + +/** + * @brief Set the RSSI for this device. + * @param [in] rssi The discovered RSSI. + */ +void NimBLEAdvertisedDevice::setRSSI(int rssi) { + m_rssi = rssi; + m_haveRSSI = true; + NIMBLE_LOGD(LOG_TAG,"- setRSSI(): rssi: %d", m_rssi); +} // setRSSI + + +/** + * @brief Set the Scan that created this advertised device. + * @param pScan The Scan that created this advertised device. + */ +void NimBLEAdvertisedDevice::setScan(NimBLEScan* pScan) { + m_pScan = pScan; +} // setScan + + +/** + * @brief Set the Service UUID for this device. + * @param [in] serviceUUID The discovered serviceUUID + */ + +void NimBLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) { + return setServiceUUID(NimBLEUUID(serviceUUID)); +} // setServiceUUID + + +/** + * @brief Set the Service UUID for this device. + * @param [in] serviceUUID The discovered serviceUUID + */ +void NimBLEAdvertisedDevice::setServiceUUID(NimBLEUUID serviceUUID) { + m_serviceUUIDs.push_back(serviceUUID); + m_haveServiceUUID = true; + NIMBLE_LOGD(LOG_TAG,"- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str()); +} // setServiceUUID + + +/** + * @brief Set the ServiceData value. + * @param [in] data ServiceData value. + */ +void NimBLEAdvertisedDevice::setServiceData(std::string serviceData) { + m_haveServiceData = true; // Set the flag that indicates we have service data. + m_serviceData = serviceData; // Save the service data that we received. +} //setServiceData + + +/** + * @brief Set the ServiceDataUUID value. + * @param [in] data ServiceDataUUID value. + */ +void NimBLEAdvertisedDevice::setServiceDataUUID(NimBLEUUID uuid) { + m_haveServiceData = true; // Set the flag that indicates we have service data. + m_serviceDataUUID = uuid; +} // setServiceDataUUID + + +/** + * @brief Set the power level for this device. + * @param [in] txPower The discovered power level. + */ +void NimBLEAdvertisedDevice::setTXPower(int8_t txPower) { + m_txPower = txPower; + m_haveTXPower = true; + NIMBLE_LOGD(LOG_TAG,"- txPower: %d", m_txPower); +} // setTXPower + + +/** + * @brief Create a string representation of this device. + * @return A string representation of this device. + */ +std::string NimBLEAdvertisedDevice::toString() { + std::string res = "Name: " + getName() + ", Address: " + getAddress().toString(); + + if (haveAppearance()) { + char val[6]; + snprintf(val, sizeof(val), "%d", getAppearance()); + res += ", appearance: "; + res += val; + } + + if (haveManufacturerData()) { + char *pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length()); + res += ", manufacturer data: "; + res += pHex; + free(pHex); + } + + if (haveServiceUUID()) { + res += ", serviceUUID: " + getServiceUUID().toString(); + } + + if (haveTXPower()) { + char val[5]; + snprintf(val, sizeof(val), "%d", getTXPower()); + res += ", txPower: "; + res += val; + } + + res += ", advType: " + std::string(NimBLEUtils::advTypeToString(m_advType)); + + return res; + +} // toString + + +uint8_t* NimBLEAdvertisedDevice::getPayload() { + return m_payload; +} + + +uint8_t NimBLEAdvertisedDevice::getAddressType() { + return m_addressType; +} + + +void NimBLEAdvertisedDevice::setAddressType(uint8_t type) { + m_addressType = type; +} + + +size_t NimBLEAdvertisedDevice::getPayloadLength() { + return m_payloadLength; +} + + +void NimBLEAdvertisedDevice::setAdvertisementResult(uint8_t* payload, uint8_t length){ + m_payload = payload; + m_payloadLength = length; +} + +#endif /* CONFIG_BT_ENABLED */ + diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h b/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h new file mode 100644 index 000000000..b6b2f706b --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h @@ -0,0 +1,133 @@ +/* + * NimBLEAdvertisedDevice.h + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEAdvertisedDevice.h + * + * Created on: Jul 3, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ +#define COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEAddress.h" +#include "NimBLEScan.h" +#include "NimBLEUUID.h" + +#include "host/ble_hs_adv.h" + +#include +#include + + +class NimBLEScan; +/** + * @brief A representation of a %BLE advertised device found by a scan. + * + * When we perform a %BLE scan, the result will be a set of devices that are advertising. This + * class provides a model of a detected device. + */ +class NimBLEAdvertisedDevice { +public: + NimBLEAdvertisedDevice(); + + NimBLEAddress getAddress(); + uint16_t getAppearance(); + std::string getManufacturerData(); + std::string getName(); + int getRSSI(); + NimBLEScan* getScan(); + std::string getServiceData(); + NimBLEUUID getServiceDataUUID(); + NimBLEUUID getServiceUUID(); + int8_t getTXPower(); + uint8_t* getPayload(); + size_t getPayloadLength(); + uint8_t getAddressType(); + void setAddressType(uint8_t type); + + + bool isAdvertisingService(NimBLEUUID uuid); + bool haveAppearance(); + bool haveManufacturerData(); + bool haveName(); + bool haveRSSI(); + bool haveServiceData(); + bool haveServiceUUID(); + bool haveTXPower(); + + std::string toString(); + +private: + friend class NimBLEScan; + + void parseAdvertisement(ble_hs_adv_fields *fields); + void setAddress(NimBLEAddress address); + void setAdvType(uint8_t advType); + void setAdvertisementResult(uint8_t* payload, uint8_t length); + void setAppearance(uint16_t appearance); + void setManufacturerData(std::string manufacturerData); + void setName(std::string name); + void setRSSI(int rssi); + void setScan(NimBLEScan* pScan); + void setServiceData(std::string data); + void setServiceDataUUID(NimBLEUUID uuid); + void setServiceUUID(const char* serviceUUID); + void setServiceUUID(NimBLEUUID serviceUUID); + void setTXPower(int8_t txPower); + + bool m_haveAppearance; + bool m_haveManufacturerData; + bool m_haveName; + bool m_haveRSSI; + bool m_haveServiceData; + bool m_haveServiceUUID; + bool m_haveTXPower; + + + NimBLEAddress m_address = NimBLEAddress("\0\0\0\0\0\0"); + uint8_t m_advType; + uint16_t m_appearance; + int m_deviceType; + std::string m_manufacturerData; + std::string m_name; + NimBLEScan* m_pScan; + int m_rssi; + std::vector m_serviceUUIDs; + int8_t m_txPower; + std::string m_serviceData; + NimBLEUUID m_serviceDataUUID; + uint8_t* m_payload; + size_t m_payloadLength = 0; + uint8_t m_addressType; +}; + +/** + * @brief A callback handler for callbacks associated device scanning. + * + * When we are performing a scan as a %BLE client, we may wish to know when a new device that is advertising + * has been found. This class can be sub-classed and registered such that when a scan is performed and + * a new advertised device has been found, we will be called back to be notified. + */ +class NimBLEAdvertisedDeviceCallbacks { +public: + virtual ~NimBLEAdvertisedDeviceCallbacks() {} + /** + * @brief Called when a new scan result is detected. + * + * As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the + * device that was found. During any individual scan, a device will only be detected one time. + */ + //virtual void onResult(NimBLEAdvertisedDevice advertisedDevice) = 0; + virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0; +}; + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp b/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp new file mode 100644 index 000000000..bfdf49dfe --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp @@ -0,0 +1,606 @@ +/* + * NimBLEAdvertising.cpp + * + * Created: on March 3, 2020 + * Author H2zero + * + * Originally: + * + * BLEAdvertising.cpp + * + * This class encapsulates advertising a BLE Server. + * Created on: Jun 21, 2017 + * Author: kolban + * + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "services/gap/ble_svc_gap.h" +#include "NimBLEAdvertising.h" +#include "NimBLEDevice.h" +#include "NimBLEServer.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEAdvertising"; + + +/** + * @brief Construct a default advertising object. + * + */ +NimBLEAdvertising::NimBLEAdvertising() { + memset(&m_advData, 0, sizeof m_advData); + memset(&m_scanData, 0, sizeof m_scanData); + memset(&m_advParams, 0, sizeof m_advParams); + const char *name = ble_svc_gap_device_name(); + + m_advData.name = (uint8_t *)name; + m_advData.name_len = strlen(name); + m_advData.name_is_complete = 1; + m_scanData.tx_pwr_lvl_is_present = 1; + m_scanData.tx_pwr_lvl = NimBLEDevice::getPower(); + m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); + m_advData.appearance = 0; + m_advData.appearance_is_present = 0; + m_advData.mfg_data_len = 0; + m_advData.mfg_data = nullptr; + + m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND; + m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; + m_advParams.itvl_min = 0; + m_advParams.itvl_max = 0; + +} // NimBLEAdvertising + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The UUID of the service to expose. + */ +void NimBLEAdvertising::addServiceUUID(NimBLEUUID serviceUUID) { + m_serviceUUIDs.push_back(serviceUUID); +} // addServiceUUID + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The string representation of the service to expose. + */ +void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) { + addServiceUUID(NimBLEUUID(serviceUUID)); +} // addServiceUUID + + +/** + * @brief Set the device appearance in the advertising data. + * The appearance attribute is of type 0x19. The codes for distinct appearances can be found here: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml. + * @param [in] appearance The appearance of the device in the advertising data. + * @return N/A. + */ +void NimBLEAdvertising::setAppearance(uint16_t appearance) { + m_advData.appearance = appearance; + m_advData.appearance_is_present = 1; +} // setAppearance + +void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){ + m_advParams.conn_mode = adv_type; +} // setAdvertisementType + +void NimBLEAdvertising::setMinInterval(uint16_t mininterval) { + m_advParams.itvl_min = mininterval; +} // setMinInterval + +void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) { + m_advParams.itvl_max = maxinterval; +} // setMaxInterval + +// These are dummy functions for now for compatibility +void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) { + //m_advData.min_interval = mininterval; +} // + +void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) { + //m_advData.max_interval = maxinterval; +} // +////////////////////////////////////////////////////////// + +void NimBLEAdvertising::setScanResponse(bool set) { + m_scanResp = set; +} + +/** + * @brief Set the filtering for the scan filter. + * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. + * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. + */ +void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { + NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); + if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { + m_advParams.filter_policy = BLE_HCI_ADV_FILT_NONE; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (scanRequestWhitelistOnly && !connectWhitelistOnly) { + m_advParams.filter_policy = BLE_HCI_ADV_FILT_SCAN; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (!scanRequestWhitelistOnly && connectWhitelistOnly) { + m_advParams.filter_policy = BLE_HCI_ADV_FILT_CONN; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (scanRequestWhitelistOnly && connectWhitelistOnly) { + m_advParams.filter_policy = BLE_HCI_ADV_FILT_BOTH; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } +} // setScanFilter + +/** + * @brief Set the advertisement data that is to be published in a regular advertisement. + * @param [in] advertisementData The data to be advertised. + */ + +void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisementData) { + NIMBLE_LOGD(LOG_TAG, ">> setAdvertisementData"); + int rc = ble_gap_adv_set_data( + (uint8_t*)advertisementData.getPayload().data(), + advertisementData.getPayload().length()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + m_customAdvData = true; // Set the flag that indicates we are using custom advertising data. + NIMBLE_LOGD(LOG_TAG, "<< setAdvertisementData"); +} // setAdvertisementData + + +/** + * @brief Set the advertisement data that is to be published in a scan response. + * @param [in] advertisementData The data to be advertised. + */ +void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertisementData) { + NIMBLE_LOGD(LOG_TAG, ">> setScanResponseData"); + int rc = ble_gap_adv_rsp_set_data( + (uint8_t*)advertisementData.getPayload().data(), + advertisementData.getPayload().length()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. + NIMBLE_LOGD(LOG_TAG, "<< setScanResponseData"); +} // setScanResponseData + + +/** + * @brief Start advertising. + * Start advertising. + * @return N/A. + */ +void NimBLEAdvertising::start() { + NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); + + // If Host is not synced we cannot start advertising. + if(!NimBLEDevice::m_synced) { + NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); + return; + } + + if(NimBLEDevice::createServer()->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) { + NIMBLE_LOGW(LOG_TAG, "Max connections reached - not advertising"); + return; + } + + int numServices = m_serviceUUIDs.size(); + int rc = 0; + uint8_t addressType; + uint8_t payloadLen = 3; //start with 3 bytes for the flags data + + // If already advertising just return + if(ble_gap_adv_active()) { + return; + } + + NimBLEServer* pServer = NimBLEDevice::createServer(); + if(!pServer->m_gattsStarted){ + pServer->start(); + } + + if (!m_customAdvData && !m_advSvcsSet && numServices > 0) { + for (int i = 0; i < numServices; i++) { + if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_16) { + int add = (m_advData.num_uuids16 > 0) ? 2 : 4; + if((payloadLen + add) > 31){ + m_advData.uuids16_is_complete = 0; + continue; + } + payloadLen += add; + + if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16, + (m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t)))) + { + NIMBLE_LOGE(LOG_TAG, "Error, no mem"); + abort(); + } + memcpy(&m_advData.uuids16[m_advData.num_uuids16].value, + &m_serviceUUIDs[i].getNative()->u16.value, sizeof(uint16_t)); + + m_advData.uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16; + m_advData.uuids16_is_complete = 1; + m_advData.num_uuids16++; + } + if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_32) { + int add = (m_advData.num_uuids32 > 0) ? 4 : 6; + if((payloadLen + add) > 31){ + m_advData.uuids32_is_complete = 0; + continue; + } + payloadLen += add; + + if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32, + (m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t)))) + { + NIMBLE_LOGE(LOG_TAG, "Error, no mem"); + abort(); + } + memcpy(&m_advData.uuids32[m_advData.num_uuids32].value, + &m_serviceUUIDs[i].getNative()->u32.value, sizeof(uint32_t)); + + m_advData.uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32; + m_advData.uuids32_is_complete = 1; + m_advData.num_uuids32++; + } + if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_128){ + int add = (m_advData.num_uuids128 > 0) ? 16 : 18; + if((payloadLen + add) > 31){ + m_advData.uuids128_is_complete = 0; + continue; + } + payloadLen += add; + + if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128, + (m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))) { + NIMBLE_LOGE(LOG_TAG, "Error, no mem"); + abort(); + } + memcpy(&m_advData.uuids128[m_advData.num_uuids128].value, + &m_serviceUUIDs[i].getNative()->u128.value, 16); + + m_advData.uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128; + m_advData.uuids128_is_complete = 1; + m_advData.num_uuids128++; + } + } + + // check if there is room for the name, if not put it in scan data + if((payloadLen + m_advData.name_len) > 29) { + if(m_scanResp){ + m_scanData.name = m_advData.name; + m_scanData.name_len = m_advData.name_len; + m_scanData.name_is_complete = m_advData.name_is_complete; + m_advData.name = nullptr; + m_advData.name_len = 0; + } else { + // if not using scan response just cut the name down + // leaving 2 bytes for the data specifier. + m_advData.name_len = (29 - payloadLen); + } + m_advData.name_is_complete = 0; + } + + if(m_advData.name_len > 0) { + payloadLen += (m_advData.name_len + 2); + } + + if(m_scanResp) { + // name length + type byte + length byte + tx power type + length + data + if((m_scanData.name_len + 5) > 31) { + // prioritize name data over tx power + m_scanData.tx_pwr_lvl_is_present = 0; + m_scanData.tx_pwr_lvl = 0; + // limit name to 29 to leave room for the data specifiers + if(m_scanData.name_len > 29) { + m_scanData.name_len = 29; + m_scanData.name_is_complete = false; + } + } + + rc = ble_gap_adv_rsp_set_fields(&m_scanData); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + // if not using scan response and there is room, + // throw the tx power data into the advertisment + } else if (payloadLen < 29) { + m_advData.tx_pwr_lvl_is_present = 1; + m_advData.tx_pwr_lvl = NimBLEDevice::getPower(); + } + + rc = ble_gap_adv_set_fields(&m_advData); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + + if(m_advData.num_uuids128 > 0) { + free(m_advData.uuids128); + m_advData.uuids128 = nullptr; + m_advData.num_uuids128 = 0; + } + + if(m_advData.num_uuids32 > 0) { + free(m_advData.uuids32); + m_advData.uuids32 = nullptr; + m_advData.num_uuids32 = 0; + } + + if(m_advData.num_uuids16 > 0) { + free(m_advData.uuids16); + m_advData.uuids16 = nullptr; + m_advData.num_uuids16 = 0; + } + + m_advSvcsSet = true; + } + + rc = ble_hs_id_infer_auto(0, &addressType); + if (rc != 0) { + NIMBLE_LOGC(LOG_TAG, "Error determining address type; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + + rc = ble_gap_adv_start(addressType, NULL, BLE_HS_FOREVER, + &m_advParams, NimBLEServer::handleGapEvent, NimBLEDevice::createServer()); //get a reference to the server (does not create a new one) + if (rc != 0) { + NIMBLE_LOGC(LOG_TAG, "Error enabling advertising; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + + NIMBLE_LOGD(LOG_TAG, "<< Advertising start"); +} // start + + +/** + * @brief Stop advertising. + * Stop advertising. + * @return N/A. + */ +void NimBLEAdvertising::stop() { + NIMBLE_LOGD(LOG_TAG, ">> stop"); + int rc = ble_gap_adv_stop(); + if (rc != 0 && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return; + } + + NIMBLE_LOGD(LOG_TAG, "<< stop"); +} // stop + + +/** + * Host reset seems to clear advertising data, + * we need clear the flag so it reloads it. + */ +void NimBLEAdvertising::onHostReset() { + m_advSvcsSet = false; +} + + +/** + * @brief Add data to the payload to be advertised. + * @param [in] data The data to be added to the payload. + */ +void NimBLEAdvertisementData::addData(std::string data) { + if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) { + return; + } + m_payload.append(data); +} // addData + + +/** + * @brief Set the appearance. + * @param [in] appearance The appearance code value. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml + */ +void NimBLEAdvertisementData::setAppearance(uint16_t appearance) { + char cdata[2]; + cdata[0] = 3; + cdata[1] = BLE_HS_ADV_TYPE_APPEARANCE; // 0x19 + addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); +} // setAppearance + + +/** + * @brief Set the complete services. + * @param [in] uuid The single service to advertise. + */ +void NimBLEAdvertisementData::setCompleteServices(NimBLEUUID uuid) { + char cdata[2]; + switch (uuid.bitSize()) { + case 16: { + // [Len] [0x02] [LL] [HH] + cdata[0] = 3; + cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS16; // 0x03 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2)); + break; + } + + case 32: { + // [Len] [0x04] [LL] [LL] [HH] [HH] + cdata[0] = 5; + cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS32; // 0x05 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4)); + break; + } + + case 128: { + // [Len] [0x04] [0] [1] ... [15] + cdata[0] = 17; + cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS128; // 0x07 + addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->u128.value, 16)); + break; + } + + default: + return; + } +} // setCompleteServices + + +/** + * @brief Set the advertisement flags. + * @param [in] The flags to be set in the advertisement. + * * ****DO NOT USE THESE**** + * * ESP_BLE_ADV_FLAG_LIMIT_DISC + * * ESP_BLE_ADV_FLAG_GEN_DISC + * * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT + * * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT + * * ESP_BLE_ADV_FLAG_DMT_HOST_SPT + * * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC + * * + * * ****THESE ARE SUPPORTED**** + * * BLE_HS_ADV_F_DISC_LTD + * * BLE_HS_ADV_F_DISC_GEN + * * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE + */ +void NimBLEAdvertisementData::setFlags(uint8_t flag) { + char cdata[3]; + cdata[0] = 2; + cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01 + cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP; + addData(std::string(cdata, 3)); +} // setFlag + + +/** + * @brief Set manufacturer specific data. + * @param [in] data Manufacturer data. + */ +void NimBLEAdvertisementData::setManufacturerData(std::string data) { + NIMBLE_LOGD("NimBLEAdvertisementData", ">> setManufacturerData"); + char cdata[2]; + cdata[0] = data.length() + 1; + cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff + addData(std::string(cdata, 2) + data); + NIMBLE_LOGD("NimBLEAdvertisementData", "<< setManufacturerData"); +} // setManufacturerData + + +/** + * @brief Set the name. + * @param [in] The complete name of the device. + */ +void NimBLEAdvertisementData::setName(std::string name) { + NIMBLE_LOGD("NimBLEAdvertisementData", ">> setName: %s", name.c_str()); + char cdata[2]; + cdata[0] = name.length() + 1; + cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09 + addData(std::string(cdata, 2) + name); + NIMBLE_LOGD("NimBLEAdvertisementData", "<< setName"); +} // setName + + +/** + * @brief Set the partial services. + * @param [in] uuid The single service to advertise. + */ +void NimBLEAdvertisementData::setPartialServices(NimBLEUUID uuid) { + char cdata[2]; + switch (uuid.bitSize()) { + case 16: { + // [Len] [0x02] [LL] [HH] + cdata[0] = 3; + cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS16; // 0x02 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u16.value, 2)); + break; + } + + case 32: { + // [Len] [0x04] [LL] [LL] [HH] [HH] + cdata[0] = 5; + cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS32; // 0x04 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u32.value, 4)); + break; + } + + case 128: { + // [Len] [0x04] [0] [1] ... [15] + cdata[0] = 17; + cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS128; // 0x06 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u128.value, 16)); + break; + } + + default: + return; + } +} // setPartialServices + + +/** + * @brief Set the service data (UUID + data) + * @param [in] uuid The UUID to set with the service data. Size of UUID will be used. + * @param [in] data The data to be associated with the service data advert. + */ +void NimBLEAdvertisementData::setServiceData(NimBLEUUID uuid, std::string data) { + char cdata[2]; + switch (uuid.bitSize()) { + case 16: { + // [Len] [0x16] [UUID16] data + cdata[0] = data.length() + 3; + cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data); + break; + } + + case 32: { + // [Len] [0x20] [UUID32] data + cdata[0] = data.length() + 5; + cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data); + break; + } + + case 128: { + // [Len] [0x21] [UUID128] data + cdata[0] = data.length() + 17; + cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data); + break; + } + + default: + return; + } +} // setServiceData + + +/** + * @brief Set the short name. + * @param [in] The short name of the device. + */ +void NimBLEAdvertisementData::setShortName(std::string name) { + NIMBLE_LOGD("NimBLEAdvertisementData", ">> setShortName: %s", name.c_str()); + char cdata[2]; + cdata[0] = name.length() + 1; + cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08 + addData(std::string(cdata, 2) + name); + NIMBLE_LOGD("NimBLEAdvertisementData", "<< setShortName"); +} // setShortName + + +/** + * @brief Retrieve the payload that is to be advertised. + * @return The payload that is to be advertised. + */ +std::string NimBLEAdvertisementData::getPayload() { + return m_payload; +} // getPayload + +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h b/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h new file mode 100644 index 000000000..5e4d58a9a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h @@ -0,0 +1,105 @@ +/* + * NimBLEAdvertising.h + * + * Created: on March 3, 2020 + * Author H2zero + * + * Originally: + * + * BLEAdvertising.h + * + * Created on: Jun 21, 2017 + * Author: kolban + */ + +#ifndef MAIN_BLEADVERTISING_H_ +#define MAIN_BLEADVERTISING_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "host/ble_gap.h" +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include "NimBLEUUID.h" +#include "FreeRTOS.h" + +#include + +/* COMPATIBILITY - DO NOT USE */ +#define ESP_BLE_ADV_FLAG_LIMIT_DISC (0x01 << 0) +#define ESP_BLE_ADV_FLAG_GEN_DISC (0x01 << 1) +#define ESP_BLE_ADV_FLAG_BREDR_NOT_SPT (0x01 << 2) +#define ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT (0x01 << 3) +#define ESP_BLE_ADV_FLAG_DMT_HOST_SPT (0x01 << 4) +#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) + /* ************************* */ + + +/** + * @brief Advertisement data set by the programmer to be published by the %BLE server. + */ +class NimBLEAdvertisementData { + // Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will + // be exposed on demand/request or as time permits. + // +public: + void setAppearance(uint16_t appearance); + void setCompleteServices(NimBLEUUID uuid); + void setFlags(uint8_t); + void setManufacturerData(std::string data); + void setName(std::string name); + void setPartialServices(NimBLEUUID uuid); + void setServiceData(NimBLEUUID uuid, std::string data); + void setShortName(std::string name); + void addData(std::string data); // Add data to the payload. + std::string getPayload(); // Retrieve the current advert payload. + +private: + friend class NimBLEAdvertising; + std::string m_payload; // The payload of the advertisement. +}; // NimBLEAdvertisementData + + +/** + * @brief Perform and manage %BLE advertising. + * + * A %BLE server will want to perform advertising in order to make itself known to %BLE clients. + */ +class NimBLEAdvertising { +public: + NimBLEAdvertising(); + void addServiceUUID(NimBLEUUID serviceUUID); + void addServiceUUID(const char* serviceUUID); + void start(); + void stop(); + void setAppearance(uint16_t appearance); + void setAdvertisementType(uint8_t adv_type); + void setMaxInterval(uint16_t maxinterval); + void setMinInterval(uint16_t mininterval); + void setAdvertisementData(NimBLEAdvertisementData& advertisementData); + void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); + void setScanResponseData(NimBLEAdvertisementData& advertisementData); + void setPrivateAddress(uint8_t type = BLE_ADDR_RANDOM); + + void setMinPreferred(uint16_t); + void setMaxPreferred(uint16_t); + void setScanResponse(bool); + +private: + friend class NimBLEDevice; + void onHostReset(); + ble_hs_adv_fields m_advData; + ble_hs_adv_fields m_scanData; + ble_gap_adv_params m_advParams; + std::vector m_serviceUUIDs; + bool m_customAdvData = false; // Are we using custom advertising data? + bool m_customScanResponseData = false; // Are we using custom scan response data? + bool m_scanResp = true; + bool m_advSvcsSet = false; + +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_BLEADVERTISING_H_ */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp b/libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp new file mode 100644 index 000000000..d9f32aee6 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp @@ -0,0 +1,92 @@ +/* + * NimBLEBeacon2.cpp + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEBeacon.cpp + * + * Created on: Jan 4, 2018 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include "NimBLEBeacon.h" +#include "NimBLELog.h" + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) + +static const char* LOG_TAG = "NimBLEBeacon"; + +NimBLEBeacon::NimBLEBeacon() { + m_beaconData.manufacturerId = 0x4c00; + m_beaconData.subType = 0x02; + m_beaconData.subTypeLength = 0x15; + m_beaconData.major = 0; + m_beaconData.minor = 0; + m_beaconData.signalPower = 0; + memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); +} // NimBLEBeacon + +std::string NimBLEBeacon::getData() { + return std::string((char*) &m_beaconData, sizeof(m_beaconData)); +} // getData + +uint16_t NimBLEBeacon::getMajor() { + return m_beaconData.major; +} + +uint16_t NimBLEBeacon::getManufacturerId() { + return m_beaconData.manufacturerId; +} + +uint16_t NimBLEBeacon::getMinor() { + return m_beaconData.minor; +} + +NimBLEUUID NimBLEBeacon::getProximityUUID() { + return NimBLEUUID(m_beaconData.proximityUUID, 16, false); +} + +int8_t NimBLEBeacon::getSignalPower() { + return m_beaconData.signalPower; +} + +/** + * Set the raw data for the beacon record. + */ +void NimBLEBeacon::setData(std::string data) { + if (data.length() != sizeof(m_beaconData)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", + data.length(), sizeof(m_beaconData)); + return; + } + memcpy(&m_beaconData, data.data(), sizeof(m_beaconData)); +} // setData + +void NimBLEBeacon::setMajor(uint16_t major) { + m_beaconData.major = ENDIAN_CHANGE_U16(major); +} // setMajor + +void NimBLEBeacon::setManufacturerId(uint16_t manufacturerId) { + m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId); +} // setManufacturerId + +void NimBLEBeacon::setMinor(uint16_t minor) { + m_beaconData.minor = ENDIAN_CHANGE_U16(minor); +} // setMinior + +void NimBLEBeacon::setProximityUUID(NimBLEUUID uuid) { + uuid = uuid.to128(); + memcpy(m_beaconData.proximityUUID, uuid.getNative()->u128.value, 16); +} // setProximityUUID + +void NimBLEBeacon::setSignalPower(int8_t signalPower) { + m_beaconData.signalPower = signalPower; +} // setSignalPower + + +#endif diff --git a/libesp32/NimBLE-Arduino/src/NimBLEBeacon.h b/libesp32/NimBLE-Arduino/src/NimBLEBeacon.h new file mode 100644 index 000000000..c0c15c8eb --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEBeacon.h @@ -0,0 +1,50 @@ +/* + * NimBLEBeacon2.h + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEBeacon2.h + * + * Created on: Jan 4, 2018 + * Author: kolban + */ + +#ifndef MAIN_NIMBLEBEACON_H_ +#define MAIN_NIMBLEBEACON_H_ +#include "NimBLEUUID.h" +/** + * @brief Representation of a beacon. + * See: + * * https://en.wikipedia.org/wiki/IBeacon + */ +class NimBLEBeacon { +private: + struct { + uint16_t manufacturerId; + uint8_t subType; + uint8_t subTypeLength; + uint8_t proximityUUID[16]; + uint16_t major; + uint16_t minor; + int8_t signalPower; + } __attribute__((packed)) m_beaconData; +public: + NimBLEBeacon(); + std::string getData(); + uint16_t getMajor(); + uint16_t getMinor(); + uint16_t getManufacturerId(); + NimBLEUUID getProximityUUID(); + int8_t getSignalPower(); + void setData(std::string data); + void setMajor(uint16_t major); + void setMinor(uint16_t minor); + void setManufacturerId(uint16_t manufacturerId); + void setProximityUUID(NimBLEUUID uuid); + void setSignalPower(int8_t signalPower); +}; // NimBLEBeacon + +#endif /* MAIN_NIMBLEBEACON_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp b/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp new file mode 100644 index 000000000..d69fee8cd --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp @@ -0,0 +1,640 @@ +/* + * NimBLECharacteristic.cpp + * + * Created: on March 3, 2020 + * Author H2zero + * + * BLECharacteristic.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLECharacteristic.h" +#include "NimBLE2902.h" +#include "NimBLE2904.h" +#include "NimBLEDevice.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +#include + +#define NULL_HANDLE (0xffff) + +static NimBLECharacteristicCallbacks defaultCallback; + +static const char* LOG_TAG = "NimBLECharacteristic"; + +/** + * @brief Construct a characteristic + * @param [in] uuid - UUID (const char*) for the characteristic. + * @param [in] properties - Properties for the characteristic. + */ +NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, NimBLEService* pService) +: NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) { +} + +/** + * @brief Construct a characteristic + * @param [in] uuid - UUID for the characteristic. + * @param [in] properties - Properties for the characteristic. + */ +NimBLECharacteristic::NimBLECharacteristic(NimBLEUUID uuid, uint16_t properties, NimBLEService* pService) { + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_properties = properties; + m_pCallbacks = &defaultCallback; + m_pService = pService; +// Backward Compatibility - to be removed +/* setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); + setReadProperty((properties & PROPERTY_READ) != 0); + setWriteProperty((properties & PROPERTY_WRITE) != 0); + setNotifyProperty((properties & PROPERTY_NOTIFY) != 0); + setIndicateProperty((properties & PROPERTY_INDICATE) != 0); + setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0); +*/ +/////////////////////////////////////////// +} // NimBLECharacteristic + +/** + * @brief Destructor. + */ +NimBLECharacteristic::~NimBLECharacteristic() { +} // ~NimBLECharacteristic + + +/** + * @brief Associate a descriptor with this characteristic. + * @param [in] pDescriptor + * @return N/A. + */ +void NimBLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) { + NIMBLE_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str()); + // Check that we don't add the same descriptor twice. + if (m_descriptorMap.getByUUID(pDescriptor->getUUID()) != nullptr) { + NIMBLE_LOGW(LOG_TAG, "<< Adding a new descriptor with the same UUID as a previous one"); + //return; + } + m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor); + NIMBLE_LOGD(LOG_TAG, "<< addDescriptor()"); +} // addDescriptor + + +/** + * @brief Create a new BLE Descriptor associated with this characteristic. + * @param [in] uuid - The UUID of the descriptor. + * @param [in] properties - The properties of the descriptor. + * @return The new BLE descriptor. + */ +NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint32_t properties, uint16_t max_len) { + return createDescriptor(NimBLEUUID(uuid), properties, max_len); +} + + +/** + * @brief Create a new BLE Descriptor associated with this characteristic. + * @param [in] uuid - The UUID of the descriptor. + * @param [in] properties - The properties of the descriptor. + * @return The new BLE descriptor. + */ +NimBLEDescriptor* NimBLECharacteristic::createDescriptor(NimBLEUUID uuid, uint32_t properties, uint16_t max_len) { + NimBLEDescriptor* pDescriptor = nullptr; + if(uuid.equals(NimBLEUUID((uint16_t)0x2902))) { + if(!(m_properties & BLE_GATT_CHR_F_NOTIFY) && !(m_properties & BLE_GATT_CHR_F_INDICATE)) { + assert(0 && "Cannot create 2902 descriptior without characteristic notification or indication property set"); + } + // We cannot have more than one 2902 descriptor, if it's already been created just return a pointer to it. + pDescriptor = m_descriptorMap.getByUUID(uuid); + if(pDescriptor == nullptr) { + pDescriptor = new NimBLE2902(this); + } else { + return pDescriptor; + } + + } else if (uuid.equals(NimBLEUUID((uint16_t)0x2904))) { + pDescriptor = new NimBLE2904(this); + + } else { + pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this); + } + addDescriptor(pDescriptor); + return pDescriptor; +} // createCharacteristic + + +/** + * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. + * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. + * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. + */ +NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) { + return m_descriptorMap.getByUUID(NimBLEUUID(descriptorUUID)); +} // getDescriptorByUUID + + +/** + * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. + * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. + * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. + */ +NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(NimBLEUUID descriptorUUID) { + return m_descriptorMap.getByUUID(descriptorUUID); +} // getDescriptorByUUID + + +/** + * @brief Get the handle of the characteristic. + * @return The handle of the characteristic. + */ +uint16_t NimBLECharacteristic::getHandle() { + return m_handle; +} // getHandle + +/* +void NimBLECharacteristic::setAccessPermissions(uint16_t perm) { + m_permissions = perm; +} +*/ + +uint8_t NimBLECharacteristic::getProperties() { + return m_properties; +} // getProperties + + +/** + * @brief Get the service associated with this characteristic. + */ +NimBLEService* NimBLECharacteristic::getService() { + return m_pService; +} // getService + + +/** + * @brief Get the UUID of the characteristic. + * @return The UUID of the characteristic. + */ +NimBLEUUID NimBLECharacteristic::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Retrieve the current value of the characteristic. + * @return A pointer to storage containing the current characteristic value. + */ +std::string NimBLECharacteristic::getValue() { + return m_value.getValue(); +} // getValue + + +/** + * @brief Retrieve the current raw data of the characteristic. + * @return A pointer to storage containing the current characteristic data. + */ +uint8_t* NimBLECharacteristic::getData() { + return m_value.getData(); +} // getData + + +int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rc; + NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg; + + NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(), + ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write"); + + uuid = ctxt->chr->uuid; + if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){ + switch(ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: { + //NIMBLE_LOGD(LOG_TAG, "read char pkthdr len:%d flags:%d", ctxt->om->om_pkthdr_len, ctxt->om->om_flags); + // If the packet header is only 8 bytes this is a follow up of a long read + // so we don't want to call the onRead() callback again. + if(ctxt->om->om_pkthdr_len > 8) { + pCharacteristic->m_pCallbacks->onRead(pCharacteristic); + } + rc = os_mbuf_append(ctxt->om, pCharacteristic->getData(), pCharacteristic->m_value.getLength()); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + case BLE_GATT_ACCESS_OP_WRITE_CHR: { + //NIMBLE_LOGD(LOG_TAG, "write char pkthdr len:%d datalen:%d", ctxt->om->om_pkthdr_len, ctxt->om->om_len); + if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + //pCharacteristic->setValue(ctxt->om->om_data, ctxt->om->om_len); + pCharacteristic->m_value.addPart(ctxt->om->om_data, ctxt->om->om_len); + os_mbuf *next; + next = SLIST_NEXT(ctxt->om, om_next); + while(next != NULL){ + //NIMBLE_LOGD(LOG_TAG, "Found long write data, len:%d", next->om_len); + pCharacteristic->m_value.addPart(next->om_data, next->om_len); + next = SLIST_NEXT(next, om_next); + } + pCharacteristic->m_value.commit(); + pCharacteristic->m_pCallbacks->onWrite(pCharacteristic); + + return 0; + } + default: + break; + } + } + + return BLE_ATT_ERR_UNLIKELY; +} + + +/** + * @brief Set the subscribe status for this characteristic. + * This will maintain a map of subscribed clients and their indicate/notify status. + * @return N/A + */ +void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) { + uint16_t subVal = 0; + if(event->subscribe.cur_notify) { + subVal |= NIMBLE_DESC_FLAG_NOTIFY; + } + if(event->subscribe.cur_indicate) { + subVal |= NIMBLE_DESC_FLAG_INDICATE; + } + + m_semaphoreConfEvt.give((subVal | NIMBLE_DESC_FLAG_INDICATE) ? 0 : + NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED); + + NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", event->subscribe.conn_handle, subVal); + + NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902); + if(p2902 == nullptr){ + ESP_LOGE(LOG_TAG, "No 2902 descriptor found for %s", getUUID().toString().c_str()); + return; + } + + p2902->setNotifications(subVal & NIMBLE_DESC_FLAG_NOTIFY); + p2902->setIndications(subVal & NIMBLE_DESC_FLAG_INDICATE); + p2902->m_pCallbacks->onWrite(p2902); + + + auto it = p2902->m_subscribedMap.find(event->subscribe.conn_handle); + if(subVal > 0 && it == p2902->m_subscribedMap.cend()) { + p2902->m_subscribedMap.insert(std::pair(event->subscribe.conn_handle, subVal)); + return; + } else if(it != p2902->m_subscribedMap.cend()) { + p2902->m_subscribedMap.erase(event->subscribe.conn_handle); + return; + } +/* + if(event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) { + p2902->m_subscribedMap.erase(event->subscribe.conn_handle); + return; + } +*/ + (*it).second = subVal; +} + + +/** + * @brief Send an indication. + * An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication + * will block waiting a positive confirmation from the client. + * @return N/A + */ +void NimBLECharacteristic::indicate() { + NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length()); + notify(false); + NIMBLE_LOGD(LOG_TAG, "<< indicate"); +} // indicate + +/** + * @brief Send a notify. + * A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification + * will not block; it is a fire and forget. + * @return N/A. + */ +void NimBLECharacteristic::notify(bool is_notification) { + NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length()); + + assert(getService() != nullptr); + assert(getService()->getServer() != nullptr); + + + if (getService()->getServer()->getConnectedCount() == 0) { + NIMBLE_LOGD(LOG_TAG, "<< notify: No connected clients."); + return; + } + + m_pCallbacks->onNotify(this); + + int rc = 0; + NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902); + + for (auto it = p2902->m_subscribedMap.cbegin(); it != p2902->m_subscribedMap.cend(); ++it) { + uint16_t _mtu = getService()->getServer()->getPeerMTU((*it).first); + // Must rebuild the data on each loop iteration as NimBLE will release it. + size_t length = m_value.getValue().length(); + uint8_t* data = (uint8_t*)m_value.getValue().data(); + os_mbuf *om; + + if(_mtu == 0) { + //NIMBLE_LOGD(LOG_TAG, "peer not connected, removing from map"); + p2902->m_subscribedMap.erase((*it).first); + it = p2902->m_subscribedMap.cbegin(); + if(it == p2902->m_subscribedMap.cend()) { + return; + } + continue; + } + + if (length > _mtu - 3) { + NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3); + } + + if((*it).second == 0) { + //NIMBLE_LOGI(LOG_TAG, "Skipping unsubscribed client"); + continue; + } + + if(is_notification && (!((*it).second & NIMBLE_DESC_FLAG_NOTIFY))) { + NIMBLE_LOGW(LOG_TAG, + "Sending notification to client subscribed to indications, sending indication instead"); + is_notification = false; + } + + if(!is_notification && (!((*it).second & NIMBLE_DESC_FLAG_INDICATE))) { + NIMBLE_LOGW(LOG_TAG, + "Sending indication to client subscribed to notifications, sending notifications instead"); + is_notification = true; + } + + // don't create the m_buf until we are sure to send the data or else + // we could be allocating a buffer that doesn't get released. + // We also must create it in each loop iteration because it is consumed with each host call. + om = ble_hs_mbuf_from_flat(data, length); + + if(!is_notification) { + m_semaphoreConfEvt.take("indicate"); + rc = ble_gattc_indicate_custom((*it).first, m_handle, om); + if(rc != 0){ + m_semaphoreConfEvt.give(); + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_GATT, rc); + return; + } + + rc = m_semaphoreConfEvt.wait(); + + if(rc == BLE_HS_ETIMEOUT) { + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, rc); + } else if(rc == BLE_HS_EDONE) { + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE, rc); + } else { + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE, rc); + } + } else { + rc = ble_gattc_notify_custom((*it).first, m_handle, om); + if(rc == 0) { + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY, 0); + } else { + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_GATT, rc); + } + } + } + + NIMBLE_LOGD(LOG_TAG, "<< notify"); +} // Notify + + +/** + * @brief Set the callback handlers for this characteristic. + * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic. + */ +void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) { + if (pCallbacks != nullptr){ + m_pCallbacks = pCallbacks; + } else { + m_pCallbacks = &defaultCallback; + } +} // setCallbacks + +// Backward compatibility - to be removed //////////////////////////////// +/** + * @brief Set the permission to broadcast. + * A characteristics has properties associated with it which define what it is capable of doing. + * One of these is the broadcast flag. + * @param [in] value The flag value of the property. + * @return N/A + */ +void NimBLECharacteristic::setBroadcastProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_F_BROADCAST); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_F_BROADCAST); + } +} // setBroadcastProperty + + +/** + * @brief Set the Indicate property value. + * @param [in] value Set to true if we are to allow indicate messages. + */ +void NimBLECharacteristic::setIndicateProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_F_INDICATE); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_F_INDICATE); + } +} // setIndicateProperty + + +/** + * @brief Set the Notify property value. + * @param [in] value Set to true if we are to allow notification messages. + */ +void NimBLECharacteristic::setNotifyProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_F_NOTIFY); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_F_NOTIFY); + } +} // setNotifyProperty + + +/** + * @brief Set the Read property value. + * @param [in] value Set to true if we are to allow reads. + */ +void NimBLECharacteristic::setReadProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_F_READ); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_F_READ); + } +} // setReadProperty + + +/** + * @brief Set the Write No Response property value. + * @param [in] value Set to true if we are to allow writes with no response. + */ +void NimBLECharacteristic::setWriteNoResponseProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_F_WRITE_NO_RSP); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE_NO_RSP); + } +} // setWriteNoResponseProperty + + +/** + * @brief Set the Write property value. + * @param [in] value Set to true if we are to allow writes. + */ +void NimBLECharacteristic::setWriteProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_F_WRITE ); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE ); + } +} // setWriteProperty +////////////////////////////////////////////////////////////////////////////////// + +/** + * @brief Set the value of the characteristic. + * @param [in] data The data to set for the characteristic. + * @param [in] length The length of the data in bytes. + */ +void NimBLECharacteristic::setValue(uint8_t* data, size_t length) { + char* pHex = NimBLEUtils::buildHexData(nullptr, data, length); + NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str()); + free(pHex); + + if (length > BLE_ATT_ATTR_MAX_LEN) { + NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN); + return; + } + + m_value.setValue(data, length); + + // if(m_handle != NULL_HANDLE) { + //ble_gatts_chr_updated(m_handle); + // ble_gattc_notify(getService()->getServer()->m_connId, m_handle); + // } + + NIMBLE_LOGD(LOG_TAG, "<< setValue"); +} // setValue + + +/** + * @brief Set the value of the characteristic from string data. + * We set the value of the characteristic from the bytes contained in the + * string. + * @param [in] Set the value of the characteristic. + * @return N/A. + */ +void NimBLECharacteristic::setValue(std::string value) { + setValue((uint8_t*)(value.data()), value.length()); +} // setValue + +void NimBLECharacteristic::setValue(uint16_t& data16) { + uint8_t temp[2]; + temp[0] = data16; + temp[1] = data16 >> 8; + setValue(temp, 2); +} // setValue + +void NimBLECharacteristic::setValue(uint32_t& data32) { + uint8_t temp[4]; + temp[0] = data32; + temp[1] = data32 >> 8; + temp[2] = data32 >> 16; + temp[3] = data32 >> 24; + setValue(temp, 4); +} // setValue + +void NimBLECharacteristic::setValue(int& data32) { + uint8_t temp[4]; + temp[0] = data32; + temp[1] = data32 >> 8; + temp[2] = data32 >> 16; + temp[3] = data32 >> 24; + setValue(temp, 4); +} // setValue + +void NimBLECharacteristic::setValue(float& data32) { + float temp = data32; + setValue((uint8_t*)&temp, 4); +} // setValue + +void NimBLECharacteristic::setValue(double& data64) { + double temp = data64; + setValue((uint8_t*)&temp, 8); +} // setValue + + +/** + * @brief Return a string representation of the characteristic. + * @return A string representation of the characteristic. + */ +std::string NimBLECharacteristic::toString() { + std::string res = "UUID: " + m_uuid.toString() + ", handle : 0x"; + char hex[5]; + snprintf(hex, sizeof(hex), "%04x", m_handle); + res += hex; + res += " "; + if (m_properties & BLE_GATT_CHR_PROP_READ ) res += "Read "; + if (m_properties & BLE_GATT_CHR_PROP_WRITE) res += "Write "; + if (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) res += "WriteNoResponse "; + if (m_properties & BLE_GATT_CHR_PROP_BROADCAST) res += "Broadcast "; + if (m_properties & BLE_GATT_CHR_PROP_NOTIFY) res += "Notify "; + if (m_properties & BLE_GATT_CHR_PROP_INDICATE) res += "Indicate "; + return res; +} // toString + + +NimBLECharacteristicCallbacks::~NimBLECharacteristicCallbacks() {} + + +/** + * @brief Callback function to support a read request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default"); +} // onRead + + +/** + * @brief Callback function to support a write request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default"); +} // onWrite + + +/** + * @brief Callback function to support a Notify request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacteristic) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onNotify: default"); +} // onNotify + + +/** + * @brief Callback function to support a Notify/Indicate Status report. + * @param [in] pCharacteristic The characteristic that is the source of the event. + * @param [in] s Status of the notification/indication + * @param [in] code Additional code of underlying errors + */ +void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default"); +} // onStatus + +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h b/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h new file mode 100644 index 000000000..1787937bd --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h @@ -0,0 +1,194 @@ +/* + * NimBLECharacteristic.h + * + * Created: on March 3, 2020 + * Author H2zero + * + * Originally: + * BLECharacteristic.h + * + * Created on: Jun 22, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLECHARACTERISTIC_H_ +#define MAIN_NIMBLECHARACTERISTIC_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "host/ble_hs.h" +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +typedef enum { + READ = BLE_GATT_CHR_F_READ, + READ_ENC = BLE_GATT_CHR_F_READ_ENC, + READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN, + READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR, + WRITE = BLE_GATT_CHR_F_WRITE, + WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP, + WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC, + WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN, + WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR, + BROADCAST = BLE_GATT_CHR_F_BROADCAST, + NOTIFY = BLE_GATT_CHR_F_NOTIFY, + INDICATE = BLE_GATT_CHR_F_INDICATE +} NIMBLE_PROPERTY; + +#include "NimBLEService.h" +#include "NimBLEDescriptor.h" +#include "NimBLEUUID.h" +#include "NimBLEValue.h" +#include "FreeRTOS.h" + +#include +#include + + +class NimBLEService; +class NimBLEDescriptor; +class NimBLECharacteristicCallbacks; + + +/** + * @brief A management structure for %BLE descriptors. + */ +class NimBLEDescriptorMap { +public: + void setByUUID(const char* uuid, NimBLEDescriptor* pDescriptor); + void setByUUID(NimBLEUUID uuid, NimBLEDescriptor* pDescriptor); +// void setByHandle(uint16_t handle, NimBLEDescriptor* pDescriptor); + NimBLEDescriptor* getByUUID(const char* uuid); + NimBLEDescriptor* getByUUID(NimBLEUUID uuid); +// NimBLEDescriptor* getByHandle(uint16_t handle); + std::string toString(); + NimBLEDescriptor* getFirst(); + NimBLEDescriptor* getNext(); + uint8_t getSize(); + +private: + std::map m_uuidMap; +// std::map m_handleMap; + std::map::iterator m_iterator; +}; + + +/** + * @brief The model of a %BLE Characteristic. + * + * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and + * can be read and written to by a %BLE client. + */ +class NimBLECharacteristic { +public: + NimBLEDescriptor* createDescriptor(const char* uuid, + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = 100); + NimBLEDescriptor* createDescriptor(NimBLEUUID uuid, + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = 100); + + NimBLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); + NimBLEDescriptor* getDescriptorByUUID(NimBLEUUID descriptorUUID); + NimBLEUUID getUUID(); + std::string getValue(); + uint8_t* getData(); + + void indicate(); + void notify(bool is_notification = true); + void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); +// Backward Compatibility - to be removed + void setBroadcastProperty(bool value); + void setIndicateProperty(bool value); + void setNotifyProperty(bool value); + void setReadProperty(bool value); + void setWriteProperty(bool value); + void setWriteNoResponseProperty(bool value); +////////////////////////////////////////////////////// + void setValue(uint8_t* data, size_t size); + void setValue(std::string value); + void setValue(uint16_t& data16); + void setValue(uint32_t& data32); + void setValue(int& data32); + void setValue(float& data32); + void setValue(double& data64); + + std::string toString(); + uint16_t getHandle(); +// void setAccessPermissions(uint16_t perm); + +// Backward Compatibility - to be removed +/* static const uint32_t PROPERTY_READ = 1<<0; + static const uint32_t PROPERTY_WRITE = 1<<1; + static const uint32_t PROPERTY_NOTIFY = 1<<2; + static const uint32_t PROPERTY_BROADCAST = 1<<3; + static const uint32_t PROPERTY_INDICATE = 1<<4; + static const uint32_t PROPERTY_WRITE_NR = 1<<5; +*/ +////////////////////////////////////////////////////// + +private: + + friend class NimBLEServer; + friend class NimBLEService; +// friend class NimBLEDescriptor; +// friend class NimBLECharacteristicMap; + + NimBLECharacteristic(const char* uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, + NimBLEService* pService = nullptr); + NimBLECharacteristic(NimBLEUUID uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, + NimBLEService* pService = nullptr); + virtual ~NimBLECharacteristic(); + + NimBLEUUID m_uuid; + NimBLEDescriptorMap m_descriptorMap; + uint16_t m_handle; + uint16_t m_properties; + NimBLECharacteristicCallbacks* m_pCallbacks; + NimBLEService* m_pService; + NimBLEValue m_value; +// uint16_t m_permissions; + + void addDescriptor(NimBLEDescriptor* pDescriptor); + NimBLEService* getService(); + uint8_t getProperties(); + void setSubscribe(struct ble_gap_event *event); + static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + + FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); +}; // NimBLECharacteristic + + +/** + * @brief Callbacks that can be associated with a %BLE characteristic to inform of events. + * + * When a server application creates a %BLE characteristic, we may wish to be informed when there is either + * a read or write request to the characteristic's value. An application can register a + * sub-classed instance of this class and will be notified when such an event happens. + */ +class NimBLECharacteristicCallbacks { +public: + typedef enum { + SUCCESS_INDICATE, + SUCCESS_NOTIFY, + ERROR_INDICATE_DISABLED, + ERROR_NOTIFY_DISABLED, + ERROR_GATT, + ERROR_NO_CLIENT, + ERROR_INDICATE_TIMEOUT, + ERROR_INDICATE_FAILURE + }Status; + + virtual ~NimBLECharacteristicCallbacks(); + virtual void onRead(NimBLECharacteristic* pCharacteristic); + virtual void onWrite(NimBLECharacteristic* pCharacteristic); + virtual void onNotify(NimBLECharacteristic* pCharacteristic); + virtual void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code); +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /*MAIN_NIMBLECHARACTERISTIC_H_*/ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLECharacteristicMap.cpp b/libesp32/NimBLE-Arduino/src/NimBLECharacteristicMap.cpp new file mode 100644 index 000000000..9ee741bc0 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLECharacteristicMap.cpp @@ -0,0 +1,128 @@ +/* + * NimBLECharacteristicMap.cpp + * + * Created: on March 3, 2020 + * Author H2zero + * + * BLECharacteristicMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEService.h" +#include "NimBLELog.h" + + +/** + * @brief Return the characteristic by handle. + * @param [in] handle The handle to look up the characteristic. + * @return The characteristic. + */ +NimBLECharacteristic* NimBLECharacteristicMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle + + +/** + * @brief Return the characteristic by UUID. + * @param [in] UUID The UUID to look up the characteristic. + * @return The characteristic. + */ +NimBLECharacteristic* NimBLECharacteristicMap::getByUUID(const char* uuid) { + return getByUUID(NimBLEUUID(uuid)); +} + + +/** + * @brief Return the characteristic by UUID. + * @param [in] UUID The UUID to look up the characteristic. + * @return The characteristic. + */ +NimBLECharacteristic* NimBLECharacteristicMap::getByUUID(NimBLEUUID uuid) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + + return nullptr; +} // getByUUID + +/** + * @brief Get the number of characteristics in the map. + */ +uint8_t NimBLECharacteristicMap::getSize() { + return (uint8_t)m_uuidMap.size(); +} // getSize + +/** + * @brief Get the first characteristic in the map. + * @return The first characteristic in the map. + */ +NimBLECharacteristic* NimBLECharacteristicMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLECharacteristic* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + + +/** + * @brief Get the next characteristic in the map. + * @return The next characteristic in the map. + */ +NimBLECharacteristic* NimBLECharacteristicMap::getNext() { + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLECharacteristic* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext + + +/** + * @brief Set the characteristic by handle. + * @param [in] handle The handle of the characteristic. + * @param [in] characteristic The characteristic to cache. + * @return N/A. + */ +void NimBLECharacteristicMap::setByHandle(uint16_t handle, NimBLECharacteristic* characteristic) { + m_handleMap.insert(std::pair(handle, characteristic)); +} // setByHandle + + +/** + * @brief Set the characteristic by UUID. + * @param [in] uuid The uuid of the characteristic. + * @param [in] characteristic The characteristic to cache. + * @return N/A. + */ +void NimBLECharacteristicMap::setByUUID(NimBLECharacteristic* pCharacteristic, NimBLEUUID uuid) { + m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); +} // setByUUID + + +/** + * @brief Return a string representation of the characteristic map. + * @return A string representation of the characteristic map. + */ +std::string NimBLECharacteristicMap::toString() { + std::string res; + int count = 0; + char hex[5]; + for (auto &myPair: m_uuidMap) { + if (count > 0) {res += "\n";} + snprintf(hex, sizeof(hex), "%04x", myPair.first->getHandle()); + count++; + res += "handle: 0x"; + res += hex; + res += ", uuid: " + myPair.first->getUUID().toString(); + } + return res; +} // toString + + +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp b/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp new file mode 100644 index 000000000..3930acf88 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp @@ -0,0 +1,893 @@ +/* + * NimBLEClient.cpp + * + * Created: on Jan 26 2020 + * Author H2zero + * + * Originally: + * BLEClient.cpp + * + * Created on: Mar 22, 2017 + * Author: kolban + */ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEClient.h" +#include "NimBLEUtils.h" +#include "NimBLEDevice.h" +#include "NimBLELog.h" + +#include +#include + +static const char* LOG_TAG = "NimBLEClient"; +static NimBLEClientCallbacks defaultCallbacks; + +/* + * Design + * ------ + * When we perform a getService() request, we are asking the BLE server to return each of the services + * that it exposes. For each service, we receive a callback which contains details + * of the exposed service including its UUID. + * + * The objects we will invent for a NimBLEClient will be as follows: + * * NimBLERemoteService - A model of a remote service. + * * NimBLERemoteCharacteristic - A model of a remote characteristic + * * NimBLERemoteDescriptor - A model of a remote descriptor. + * + * Since there is a hierarchical relationship here, we will have the idea that from a NimBLERemoteService will own + * zero or more remote characteristics and a NimBLERemoteCharacteristic will own zero or more remote NimBLEDescriptors. + * + * We will assume that a NimBLERemoteService contains a map that maps NimBLEUUIDs to the set of owned characteristics + * and that a NimBLECharacteristic contains a map that maps NimBLEUUIDs to the set of owned descriptors. + * + * + */ + +NimBLEClient::NimBLEClient() +{ + m_pClientCallbacks = &defaultCallbacks; + m_conn_id = BLE_HS_CONN_HANDLE_NONE; + m_haveServices = false; + m_isConnected = false; + m_connectTimeout = 30000; + + m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default) + m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default) + m_pConnParams.itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN; // min_int = 0x10*1.25ms = 20ms + m_pConnParams.itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX; // max_int = 0x20*1.25ms = 40ms + m_pConnParams.latency = BLE_GAP_INITIAL_CONN_LATENCY; // number of packets allowed to skip (extends max interval) + m_pConnParams.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT; // timeout = 400*10ms = 4000ms + m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units + m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units +} // NimBLEClient + + +/** + * @brief Destructor, private - only callable by NimBLEDevice::deleteClient + * to ensure proper disconnect and removal from device list. + */ +NimBLEClient::~NimBLEClient() { + // We may have allocated service references associated with this client. + // Before we are finished with the client, we must release resources. + clearServices(); + + if(m_deleteCallbacks) { + delete m_pClientCallbacks; + } + +} // ~NimBLEClient + + +/** + * @brief Clear any existing services. + */ +void NimBLEClient::clearServices() { + NIMBLE_LOGD(LOG_TAG, ">> clearServices"); + // Delete all the services. + for (auto &myPair : m_servicesMap) { + delete myPair.second; + } + m_servicesMap.clear(); + m_haveServices = false; + NIMBLE_LOGD(LOG_TAG, "<< clearServices"); +} // clearServices + + +/** + * NOT NEEDED + */ + /* +void NimBLEClient::onHostReset() { + +} + */ + +/** + * Add overloaded function to ease connect to peer device with not public address + */ +bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool refreshServices) { + NimBLEAddress address(device->getAddress()); + uint8_t type = device->getAddressType(); + return connect(address, type, refreshServices); +} + + +/** + * @brief Connect to the partner (BLE Server). + * @param [in] address The address of the partner. + * @return True on success. + */ +bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServices) { + NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); + + if(!NimBLEDevice::m_synced) { + NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync."); + return false; + } + + if(ble_gap_conn_active()) { + NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait."); + return false; + } + + int rc = 0; + m_peerAddress = address; + + ble_addr_t peerAddrt; + memcpy(&peerAddrt.val, address.getNative(),6); + peerAddrt.type = type; + + m_semaphoreOpenEvt.take("connect"); + + /** Try to connect the the advertiser. Allow 30 seconds (30000 ms) for + * timeout (default value of m_connectTimeout). + * Loop on BLE_HS_EBUSY if the scan hasn't stopped yet. + */ + do{ + rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, &m_pConnParams, + NimBLEClient::handleGapEvent, this); + if(rc == BLE_HS_EBUSY) { + vTaskDelay(1); + } + }while(rc == BLE_HS_EBUSY); + + if (rc != 0 && rc != BLE_HS_EDONE) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; addr_type=%d " + "addr=%s, rc=%d; %s", + type, + m_peerAddress.toString().c_str(), + rc, NimBLEUtils::returnCodeToString(rc)); + + m_semaphoreOpenEvt.give(); + m_waitingToConnect = false; + return false; + } + + m_waitingToConnect = true; + + rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete. + + if(rc != 0){ + return false; + } + + if(refreshServices) { + NIMBLE_LOGD(LOG_TAG, "Refreshing Services for: (%s)", address.toString().c_str()); + clearServices(); + } + + if (!m_haveServices) { + if (!retrieveServices()) { + // error getting services, make sure we disconnect and release any resources before returning + disconnect(); + clearServices(); + return false; + } + else{ + NIMBLE_LOGD(LOG_TAG, "Found %d services", getServices()->size()); + } + } + + m_pClientCallbacks->onConnect(this); + + NIMBLE_LOGD(LOG_TAG, "<< connect()"); + return true; +} // connect + + +/** + * @brief Called when a characteristic or descriptor requires encryption or authentication to access it. + * This will pair with the device and bond if enabled. + * @return True on success. + */ +bool NimBLEClient::secureConnection() { + + m_semeaphoreSecEvt.take("secureConnection"); + + int rc = NimBLEDevice::startSecurity(m_conn_id); + if(rc != 0){ + m_semeaphoreSecEvt.give(); + return false; + } + + rc = m_semeaphoreSecEvt.wait("secureConnection"); + if(rc != 0){ + return false; + } + + return true; +} + + +/** + * @brief Disconnect from the peer. + * @return N/A. + */ +int NimBLEClient::disconnect(uint8_t reason) { + NIMBLE_LOGD(LOG_TAG, ">> disconnect()"); + int rc = 0; + if(m_isConnected){ + m_isConnected = false; // flag the disconnect now so no calls are performed after + rc = ble_gap_terminate(m_conn_id, reason); + if(rc != 0){ + NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, + NimBLEUtils::returnCodeToString(rc)); + } + // Sometimes a disconnect event is not sent so we need to make sure + // the device can be found again. + NimBLEDevice::removeIgnored(m_peerAddress); + } + + NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); + return rc; +} // disconnect + + +/** + * @brief Set the connection paramaters to use when connecting to a server. + */ +void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t scanInterval, uint16_t scanWindow)/*, + uint16_t minConnTime, uint16_t maxConnTime)*/ +{ + + m_pConnParams.scan_itvl = scanInterval; // Scan interval in 0.625ms units + m_pConnParams.scan_window = scanWindow; // Scan window in 0.625ms units + m_pConnParams.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms + m_pConnParams.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms + m_pConnParams.latency = latency; // number of packets allowed to skip (extends max interval) + m_pConnParams.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms + + // These are not used by NimBLE at this time - Must leave at defaults + //m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units + //m_pConnParams->max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units + + int rc = NimBLEUtils::checkConnParams(&m_pConnParams); + assert(rc == 0 && "Invalid Connection parameters"); +} + + +/** + * Update connection parameters can be called only after connection has been established + */ +void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout) +{ + ble_gap_upd_params params; + + params.latency = latency; + params.itvl_max = maxInterval; + params.itvl_min = minInterval; + params.supervision_timeout = timeout; + // These are not used by NimBLE at this time - Must leave at defaults + params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; + params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; + + int rc = ble_gap_update_params(m_conn_id, ¶ms); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + } +} + + +/** + * @brief Set the timeout to wait for connection attempt to complete + * @params[in] Time to wait in seconds. + */ +void NimBLEClient::setConnectTimeout(uint8_t time) { + m_connectTimeout = (uint32_t)(time * 1000); +} + + +/** + * @brief Get the connection id for this client. + * @return The connection id. + */ +uint16_t NimBLEClient::getConnId() { + return m_conn_id; +} // getConnId + + +/** + * @brief Retrieve the address of the peer. + */ +NimBLEAddress NimBLEClient::getPeerAddress() { + return m_peerAddress; +} // getAddress + + +/** + * @brief Ask the BLE server for the RSSI value. + * @return The RSSI value. + */ +int NimBLEClient::getRssi() { + NIMBLE_LOGD(LOG_TAG, ">> getRssi()"); + if (!isConnected()) { + NIMBLE_LOGE(LOG_TAG, "<< getRssi(): Not connected"); + return 0; + } + + int8_t rssiValue = 0; + int rc = ble_gap_conn_rssi(m_conn_id, &rssiValue); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to read RSSI error code: %d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + return 0; + } + + return rssiValue; +} // getRssi + + +/** + * @brief Get the service BLE Remote Service instance corresponding to the uuid. + * @param [in] uuid The UUID of the service being sought. + * @return A reference to the Service or nullptr if don't know about it. + */ +NimBLERemoteService* NimBLEClient::getService(const char* uuid) { + return getService(NimBLEUUID(uuid)); +} // getService + + +/** + * @brief Get the service object corresponding to the uuid. + * @param [in] uuid The UUID of the service being sought. + * @return A reference to the Service or nullptr if don't know about it. + */ +NimBLERemoteService* NimBLEClient::getService(NimBLEUUID uuid) { + NIMBLE_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str()); + + if (!m_haveServices) { + return nullptr; + } + std::string uuidStr = uuid.toString(); + for (auto &myPair : m_servicesMap) { + if (myPair.first == uuidStr) { + NIMBLE_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str()); + return myPair.second; + } + } + NIMBLE_LOGD(LOG_TAG, "<< getService: not found"); + return nullptr; +} // getService + + +/** + * @Get a pointer to the map of found services. + */ +std::map* NimBLEClient::getServices() { + return &m_servicesMap; +} + + +/** + * @brief Ask the remote %BLE server for its services. + * A %BLE Server exposes a set of services for its partners. Here we ask the server for its set of + * services and wait until we have received them all. + * We then ask for the characteristics for each service found and their desciptors. + * @return true on success otherwise false if an error occurred + */ +bool NimBLEClient::retrieveServices() { +/** + * Design + * ------ + * We invoke ble_gattc_disc_all_svcs. This will request a list of the services exposed by the + * peer BLE partner to be returned in the callback function provided. + */ + + NIMBLE_LOGD(LOG_TAG, ">> retrieveServices"); + + if(!m_isConnected){ + NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting"); + return false; + } + + m_semaphoreSearchCmplEvt.take("retrieveServices"); + + int rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, this); + + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_svcs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + m_haveServices = false; + m_semaphoreSearchCmplEvt.give(); + return false; + } + + // wait until we have all the services + // If sucessful, remember that we now have services. + m_haveServices = (m_semaphoreSearchCmplEvt.wait("retrieveServices") == 0); + if(m_haveServices){ + for (auto &myPair : m_servicesMap) { + if(!m_isConnected || !myPair.second->retrieveCharacteristics()) { + NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve characteristics -aborting"); + return false; + } + } + + NIMBLE_LOGD(LOG_TAG, "<< retrieveServices"); + return true; + } + else { + NIMBLE_LOGE(LOG_TAG, "Could not retrieve services"); + return false; + } +} // getServices + + +/** + * @brief STATIC Callback for the service discovery API function. + * When a service is found or there is none left or there was an error + * the API will call this and report findings. + */ +int NimBLEClient::serviceDiscoveredCB( + uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + NIMBLE_LOGD(LOG_TAG,"Service Discovered >> status: %d handle: %d", error->status, conn_handle); + NimBLEClient *peer = (NimBLEClient*)arg; + int rc=0; + + // Make sure the service discovery is for this device + if(peer->getConnId() != conn_handle){ + return 0; + } + + switch (error->status) { + case 0: { + // Found a service - add it to the map + NimBLERemoteService* pRemoteService = new NimBLERemoteService(peer, service); + peer->m_servicesMap.insert(std::pair(pRemoteService->getUUID().toString(), pRemoteService)); + break; + } + case BLE_HS_EDONE:{ + // All services discovered; start discovering characteristics. + + //NIMBLE_LOGD(LOG_TAG,"Giving search semaphore - completed"); + peer->m_semaphoreSearchCmplEvt.give(0); + rc = 0; + break; + } + default: + // Error; abort discovery. + rc = error->status; + break; + } + + if (rc != 0) { + // pass non-zero to semaphore on error to indicate an error finding services + peer->m_semaphoreSearchCmplEvt.give(1); + } + NIMBLE_LOGD(LOG_TAG,"<< Service Discovered. status: %d", rc); + return rc; +} + + +/** + * @brief Get the value of a specific characteristic associated with a specific service. + * @param [in] serviceUUID The service that owns the characteristic. + * @param [in] characteristicUUID The characteristic whose value we wish to read. + * @returns characteristic value or an empty string if not found + */ +std::string NimBLEClient::getValue(NimBLEUUID serviceUUID, NimBLEUUID characteristicUUID) { + NIMBLE_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + + std::string ret = ""; + NimBLERemoteService* pService = getService(serviceUUID); + + if(pService != nullptr) { + NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID); + if(pChar != nullptr) { + ret = pChar->readValue(); + } + } + + NIMBLE_LOGD(LOG_TAG, "<> setValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + + bool ret = false; + NimBLERemoteService* pService = getService(serviceUUID); + + if(pService != nullptr) { + NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID); + if(pChar != nullptr) { + ret = pChar->writeValue(value); + } + } + + NIMBLE_LOGD(LOG_TAG, "<< setValue"); + return ret; +} // setValue + + + +/** + * @brief Get the current mtu of this connection. + */ +uint16_t NimBLEClient::getMTU() { + return ble_att_mtu(m_conn_id); +} + + +/** + * @brief Handle a received GAP event. + * + * @param [in] event + * @param [in] arg = pointer to the client instance + */ + /*STATIC*/ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { + + NimBLEClient* client = (NimBLEClient*)arg; + //struct ble_gap_conn_desc desc; + //struct ble_hs_adv_fields fields; + int rc; + + NIMBLE_LOGD(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type)); + + // Execute handler code based on the type of event received. + switch(event->type) { + + case BLE_GAP_EVENT_DISCONNECT: { + if(!client->m_isConnected) + return 0; + + if(client->m_conn_id != event->disconnect.conn.conn_handle) + return 0; + + client->m_isConnected = false; + client->m_waitingToConnect=false; + // Remove the device from ignore list so we will scan it again + NimBLEDevice::removeIgnored(client->m_peerAddress); + + NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason, + NimBLEUtils::returnCodeToString(event->disconnect.reason)); + //print_conn_desc(&event->disconnect.conn); + //MODLOG_DFLT(INFO, "\n"); + + + // If Host reset tell the device now before returning to prevent + // any errors caused by calling host functions before resyncing. + switch(event->disconnect.reason) { + case BLE_HS_ETIMEOUT_HCI: + case BLE_HS_EOS: + case BLE_HS_ECONTROLLER: + case BLE_HS_ENOTSYNCED: + NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason); + NimBLEDevice::onReset(event->disconnect.reason); + break; + default: + break; + } + + //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; + + // Indicate a non-success return value to any semaphores waiting + client->m_semaphoreOpenEvt.give(1); + client->m_semaphoreSearchCmplEvt.give(1); + client->m_semeaphoreSecEvt.give(1); + + client->m_pClientCallbacks->onDisconnect(client); + + return 0; + } // BLE_GAP_EVENT_DISCONNECT + + case BLE_GAP_EVENT_CONNECT: { + + if(!client->m_waitingToConnect) + return 0; + + //if(client->m_conn_id != BLE_HS_CONN_HANDLE_NONE) + // return 0; + + client->m_waitingToConnect=false; + + if (event->connect.status == 0) { + client->m_isConnected = true; + + NIMBLE_LOGD(LOG_TAG, "Connection established"); + + client->m_conn_id = event->connect.conn_handle; + + // rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + // assert(rc == 0); + // print_conn_desc(&desc); + // MODLOG_DFLT(INFO, "\n"); + + + // In the case of a multiconnecting device we ignore this device when + // scanning since we are already connected to it + NimBLEDevice::addIgnored(client->m_peerAddress); + + rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc, + NimBLEUtils::returnCodeToString(rc)); + // if error getting mtu indicate a connection error. + client->m_semaphoreOpenEvt.give(rc); + } + } else { + // Connection attempt failed + NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s", + event->connect.status, + NimBLEUtils::returnCodeToString(event->connect.status)); + } + client->m_semaphoreOpenEvt.give(event->connect.status); + return 0; + } // BLE_GAP_EVENT_CONNECT + + case BLE_GAP_EVENT_NOTIFY_RX: { + if(client->m_conn_id != event->notify_rx.conn_handle) + return 0; + + NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",event->notify_rx.attr_handle); + if(!client->m_haveServices) + return 0; + + for(auto &sPair : client->m_servicesMap){ + // Dont waste cycles searching services without this handle in their range + if(sPair.second->getEndHandle() < event->notify_rx.attr_handle) { + continue; + } + auto cMap = sPair.second->getCharacteristicsByHandle(); + NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d", sPair.second->getUUID().toString().c_str(),event->notify_rx.attr_handle); + auto characteristic = cMap->find(event->notify_rx.attr_handle); + if(characteristic != cMap->end()) { + NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", characteristic->second->toString().c_str()); + + if (characteristic->second->m_notifyCallback != nullptr) { + NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", characteristic->second->toString().c_str()); + characteristic->second->m_notifyCallback(characteristic->second, event->notify_rx.om->om_data, event->notify_rx.om->om_len, !event->notify_rx.indication); + } + + break; + } + } + + return 0; + } // BLE_GAP_EVENT_NOTIFY_RX + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: { + if(client->m_conn_id != event->conn_update_req.conn_handle){ + return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE + } + NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters"); + NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d", + event->conn_update_req.peer_params->itvl_min, + event->conn_update_req.peer_params->itvl_max, + event->conn_update_req.peer_params->latency, + event->conn_update_req.peer_params->supervision_timeout); + + rc = client->m_pClientCallbacks->onConnParamsUpdateRequest(client, + event->conn_update_req.peer_params) ? 0 : BLE_ERR_CONN_PARMS; + + + if(!rc && event->type == BLE_GAP_EVENT_CONN_UPDATE_REQ ) { + event->conn_update_req.self_params->itvl_min = client->m_pConnParams.itvl_min; + event->conn_update_req.self_params->itvl_max = client->m_pConnParams.itvl_max; + event->conn_update_req.self_params->latency = client->m_pConnParams.latency; + event->conn_update_req.self_params->supervision_timeout = client->m_pConnParams.supervision_timeout; + } + + NIMBLE_LOGD(LOG_TAG, "%s peer params", (rc == 0) ? "Accepted" : "Rejected"); + return rc; + } // BLE_GAP_EVENT_CONN_UPDATE_REQ, BLE_GAP_EVENT_L2CAP_UPDATE_REQ + + case BLE_GAP_EVENT_CONN_UPDATE: { + if(client->m_conn_id != event->conn_update.conn_handle){ + return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE + } + if(event->conn_update.status == 0) { + NIMBLE_LOGI(LOG_TAG, "Connection parameters updated."); + } else { + NIMBLE_LOGE(LOG_TAG, "Update connection parameters failed."); + } + return 0; + } // BLE_GAP_EVENT_CONN_UPDATE + + case BLE_GAP_EVENT_ENC_CHANGE: { + if(client->m_conn_id != event->enc_change.conn_handle){ + return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE + } + + if(event->enc_change.status == 0) { + struct ble_gap_conn_desc desc; + rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + assert(rc == 0); + + if(NimBLEDevice::m_securityCallbacks != nullptr) { + NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); + } else { + client->m_pClientCallbacks->onAuthenticationComplete(&desc); + } + } + + client->m_semeaphoreSecEvt.give(event->enc_change.status); + return 0; + } //BLE_GAP_EVENT_ENC_CHANGE + + case BLE_GAP_EVENT_MTU: { + if(client->m_conn_id != event->mtu.conn_handle){ + return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE + } + NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", + event->mtu.conn_handle, + event->mtu.value); + client->m_semaphoreOpenEvt.give(0); + //client->m_mtu = event->mtu.value; + return 0; + } // BLE_GAP_EVENT_MTU + + case BLE_GAP_EVENT_PASSKEY_ACTION: { + struct ble_sm_io pkey = {0}; + + if(client->m_conn_id != event->passkey.conn_handle) + return 0; + + if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + pkey.action = event->passkey.params.action; + pkey.passkey = NimBLEDevice::m_passkey; // This is the passkey to be entered on peer + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); + + } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %d", event->passkey.params.numcmp); + pkey.action = event->passkey.params.action; + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + pkey.numcmp_accept = NimBLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp); + //////////////////////////////////////////////////// + } else { + pkey.numcmp_accept = client->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp); + } + + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); + + //TODO: Handle out of band pairing + } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { + static uint8_t tem_oob[16] = {0}; + pkey.action = event->passkey.params.action; + for (int i = 0; i < 16; i++) { + pkey.oob[i] = tem_oob[i]; + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); + //////// + } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { + NIMBLE_LOGD(LOG_TAG, "Enter the passkey"); + pkey.action = event->passkey.params.action; + + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + pkey.passkey = NimBLEDevice::m_securityCallbacks->onPassKeyRequest(); + ///////////////////////////////////////////// + } else { + client->m_pClientCallbacks->onPassKeyRequest(); + } + + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); + + } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { + NIMBLE_LOGD(LOG_TAG, "No passkey action required"); + } + + return 0; + } // BLE_GAP_EVENT_PASSKEY_ACTION + + default: { + return 0; + } + } // Switch +} // handleGapEvent + + +/** + * @brief Are we connected to a server? + * @return True if we are connected and false if we are not connected. + */ +bool NimBLEClient::isConnected() { + return m_isConnected; +} // isConnected + + +/** + * @brief Set the callbacks that will be invoked. + */ +void NimBLEClient::setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) { + if (pClientCallbacks != nullptr){ + m_pClientCallbacks = pClientCallbacks; + } else { + m_pClientCallbacks = &defaultCallbacks; + } + m_deleteCallbacks = deleteCallbacks; +} // setClientCallbacks + + +/** + * @brief Return a string representation of this client. + * @return A string representation of this client. + */ +std::string NimBLEClient::toString() { + std::string res = "peer address: " + m_peerAddress.toString(); + res += "\nServices:\n"; + for (auto &myPair : m_servicesMap) { + res += myPair.second->toString() + "\n"; + } + + return res; +} // toString + + +void NimBLEClientCallbacks::onConnect(NimBLEClient* pClient) { + NIMBLE_LOGD("NimBLEClientCallbacks", "onConnect: default"); +} + +void NimBLEClientCallbacks::onDisconnect(NimBLEClient* pClient) { + NIMBLE_LOGD("NimBLEClientCallbacks", "onDisconnect: default"); +} + +bool NimBLEClientCallbacks::onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) { + NIMBLE_LOGD("NimBLEClientCallbacks", "onConnParamsUpdateRequest: default"); + return true; +} + +uint32_t NimBLEClientCallbacks::onPassKeyRequest(){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyRequest: default: 123456"); + return 123456; +} + +void NimBLEClientCallbacks::onPassKeyNotify(uint32_t pass_key){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyNotify: default: %d", pass_key); +} + +bool NimBLEClientCallbacks::onSecurityRequest(){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onSecurityRequest: default: true"); + return true; +} +void NimBLEClientCallbacks::onAuthenticationComplete(ble_gap_conn_desc* desc){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default"); +} +bool NimBLEClientCallbacks::onConfirmPIN(uint32_t pin){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onConfirmPIN: default: true"); + return true; +} + +#endif // CONFIG_BT_ENABLED diff --git a/libesp32/NimBLE-Arduino/src/NimBLEClient.h b/libesp32/NimBLE-Arduino/src/NimBLEClient.h new file mode 100644 index 000000000..3d3613c0f --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEClient.h @@ -0,0 +1,113 @@ +/* + * NimBLEClient.h + * + * Created: on Jan 26 2020 + * Author H2zero + * + * Originally: + * BLEClient.h + * + * Created on: Mar 22, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLECLIENT_H_ +#define MAIN_NIMBLECLIENT_H_ + +#if defined(CONFIG_BT_ENABLED) +#include "sdkconfig.h" + +#include "NimBLEAddress.h" +#include "NimBLEAdvertisedDevice.h" +#include "NimBLERemoteService.h" + +#include +#include + +class NimBLERemoteService; +class NimBLEClientCallbacks; +class NimBLEAdvertisedDevice; + +/** + * @brief A model of a %BLE client. + */ +class NimBLEClient { +public: + bool connect(NimBLEAdvertisedDevice* device, bool refreshServices = true); + bool connect(NimBLEAddress address, uint8_t type = BLE_ADDR_TYPE_PUBLIC, bool refreshServices = true); // Connect to the remote BLE Server + int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); // Disconnect from the remote BLE Server + NimBLEAddress getPeerAddress(); // Get the address of the remote BLE Server + int getRssi(); // Get the RSSI of the remote BLE Server + std::map* getServices(); // Get a map of the services offered by the remote BLE Server + NimBLERemoteService* getService(const char* uuid); // Get a reference to a specified service offered by the remote BLE server. + NimBLERemoteService* getService(NimBLEUUID uuid); // Get a reference to a specified service offered by the remote BLE server. + std::string getValue(NimBLEUUID serviceUUID, NimBLEUUID characteristicUUID); // Get the value of a given characteristic at a given service. + bool setValue(NimBLEUUID serviceUUID, NimBLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service. + bool isConnected(); // Return true if we are connected. + void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks, bool deleteCallbacks = true); + std::string toString(); // Return a string representation of this client. + uint16_t getConnId(); + uint16_t getMTU(); + bool secureConnection(); + void setConnectTimeout(uint8_t timeout); + void setConnectionParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t scanInterval=16, uint16_t scanWindow=16); // NimBLE default scan settings + void updateConnParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout); + + +private: + NimBLEClient(); + ~NimBLEClient(); + friend class NimBLEDevice; + friend class NimBLERemoteService; + + static int handleGapEvent(struct ble_gap_event *event, void *arg); + static int serviceDiscoveredCB(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg); + void clearServices(); // Clear any existing services. + bool retrieveServices(); //Retrieve services from the server +// void onHostReset(); + + NimBLEAddress m_peerAddress = NimBLEAddress("\0\0\0\0\0\0"); // The BD address of the remote server. + uint16_t m_conn_id; + bool m_haveServices = false; // Have we previously obtain the set of services from the remote server. + bool m_isConnected = false; // Are we currently connected. + bool m_waitingToConnect =false; + bool m_deleteCallbacks = true; + int32_t m_connectTimeout; + //uint16_t m_mtu = 23; + + NimBLEClientCallbacks* m_pClientCallbacks = nullptr; + + FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); + FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt"); + FreeRTOS::Semaphore m_semeaphoreSecEvt = FreeRTOS::Semaphore("Security"); + + std::map m_servicesMap; + +private: + friend class NimBLEClientCallbacks; + ble_gap_conn_params m_pConnParams; + +}; // class NimBLEClient + + +/** + * @brief Callbacks associated with a %BLE client. + */ +class NimBLEClientCallbacks { +public: + virtual ~NimBLEClientCallbacks() {}; + virtual void onConnect(NimBLEClient* pClient); + virtual void onDisconnect(NimBLEClient* pClient); + virtual bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params); + virtual uint32_t onPassKeyRequest(); + virtual void onPassKeyNotify(uint32_t pass_key); + virtual bool onSecurityRequest(); + virtual void onAuthenticationComplete(ble_gap_conn_desc* desc); + virtual bool onConfirmPIN(uint32_t pin); +}; + +#endif // CONFIG_BT_ENABLED +#endif /* MAIN_NIMBLECLIENT_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp b/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp new file mode 100644 index 000000000..828334505 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp @@ -0,0 +1,248 @@ +/* + * NimBLEDescriptor.cpp + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLEDescriptor.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEService.h" +#include "NimBLEDescriptor.h" +#include "NimBLELog.h" + +#include + +#define NULL_HANDLE (0xffff) + +static const char* LOG_TAG = "NimBLEDescriptor"; +static NimBLEDescriptorCallbacks defaultCallbacks; + + +/** + * @brief NimBLEDescriptor constructor. + */ +NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, + NimBLECharacteristic* pCharacteristic) +: NimBLEDescriptor(NimBLEUUID(uuid), max_len, properties, pCharacteristic) { +} + +/** + * @brief NimBLEDescriptor constructor. + */ +NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_t max_len, + NimBLECharacteristic* pCharacteristic) +{ + m_uuid = uuid; + m_value.attr_len = 0; // Initial length is 0. + m_value.attr_max_len = max_len; // Maximum length of the data. + m_handle = NULL_HANDLE; // Handle is initially unknown. + m_pCharacteristic = nullptr; // No initial characteristic. + m_pCallbacks = &defaultCallbacks; // No initial callback. + m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value. + m_properties = 0; + + if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t + m_properties |= BLE_ATT_F_READ; + } + if (properties & (BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE)) { + m_properties |= BLE_ATT_F_WRITE; + } + if (properties & BLE_GATT_CHR_F_READ_ENC) { + m_properties |= BLE_ATT_F_READ_ENC; + } + if (properties & BLE_GATT_CHR_F_READ_AUTHEN) { + m_properties |= BLE_ATT_F_READ_AUTHEN; + } + if (properties & BLE_GATT_CHR_F_READ_AUTHOR) { + m_properties |= BLE_ATT_F_READ_AUTHOR; + } + if (properties & BLE_GATT_CHR_F_WRITE_ENC) { + m_properties |= BLE_ATT_F_WRITE_ENC; + } + if (properties & BLE_GATT_CHR_F_WRITE_AUTHEN) { + m_properties |= BLE_ATT_F_WRITE_AUTHEN; + } + if (properties & BLE_GATT_CHR_F_WRITE_AUTHOR) { + m_properties |= BLE_ATT_F_WRITE_AUTHOR; + } + +} // NimBLEDescriptor + + +/** + * @brief NimBLEDescriptor destructor. + */ +NimBLEDescriptor::~NimBLEDescriptor() { + free(m_value.attr_value); // Release the storage we created in the constructor. +} // ~NimBLEDescriptor + +/** + * @brief Get the BLE handle for this descriptor. + * @return The handle for this descriptor. + */ +uint16_t NimBLEDescriptor::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Get the length of the value of this descriptor. + * @return The length (in bytes) of the value of this descriptor. + */ +size_t NimBLEDescriptor::getLength() { + return m_value.attr_len; +} // getLength + + +/** + * @brief Get the UUID of the descriptor. + */ +NimBLEUUID NimBLEDescriptor::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Get the value of this descriptor. + * @return A pointer to the value of this descriptor. + */ +uint8_t* NimBLEDescriptor::getValue() { + return m_value.attr_value; +} // getValue + + +int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rc; + NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg; + + NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(), + ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC ? "Read" : "Write"); + + uuid = ctxt->chr->uuid; + if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){ + switch(ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_DSC: { + pDescriptor->m_pCallbacks->onRead(pDescriptor); + rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength()); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + case BLE_GATT_ACCESS_OP_WRITE_DSC: { + if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + pDescriptor->setValue(ctxt->om->om_data, ctxt->om->om_len); + pDescriptor->m_pCallbacks->onWrite(pDescriptor); + return 0; + } + default: + break; + } + } + + return BLE_ATT_ERR_UNLIKELY; +} + +/** + * @brief Set the callback handlers for this descriptor. + * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. + */ +void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallbacks) { + if (pCallbacks != nullptr){ + m_pCallbacks = pCallbacks; + } else { + m_pCallbacks = &defaultCallbacks; + } +} // setCallbacks + + +/** + * @brief Set the handle of this descriptor. + * Set the handle of this descriptor to be the supplied value. + * @param [in] handle The handle to be associated with this descriptor. + * @return N/A. + */ +void NimBLEDescriptor::setHandle(uint16_t handle) { + NIMBLE_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle); + m_handle = handle; + NIMBLE_LOGD(LOG_TAG, "<< setHandle()"); +} // setHandle + + +/** + * @brief Set the value of the descriptor. + * @param [in] data The data to set for the descriptor. + * @param [in] length The length of the data in bytes. + */ +void NimBLEDescriptor::setValue(uint8_t* data, size_t length) { + if (length > BLE_ATT_ATTR_MAX_LEN) { + NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN); + return; + } + m_value.attr_len = length; + memcpy(m_value.attr_value, data, length); +} // setValue + + +/** + * @brief Set the value of the descriptor. + * @param [in] value The value of the descriptor in string form. + */ +void NimBLEDescriptor::setValue(std::string value) { + setValue((uint8_t*) value.data(), value.length()); +} // setValue + + +/* +void NimBLEDescriptor::setAccessPermissions(uint8_t perm) { + m_permissions = perm; +} +*/ + + +/** + * @brief Return a string representation of the descriptor. + * @return A string representation of the descriptor. + */ +std::string NimBLEDescriptor::toString() { + char hex[5]; + snprintf(hex, sizeof(hex), "%04x", m_handle); + std::string res = "UUID: " + m_uuid.toString() + ", handle: 0x" + hex; + return res; +} // toString + + +NimBLEDescriptorCallbacks::~NimBLEDescriptorCallbacks() {} + +/** + * @brief Callback function to support a read request. + * @param [in] pDescriptor The descriptor that is the source of the event. + */ +void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor) { + NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default"); +} // onRead + + +/** + * @brief Callback function to support a write request. + * @param [in] pDescriptor The descriptor that is the source of the event. + */ +void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor) { + NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default"); +} // onWrite + + +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h b/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h new file mode 100644 index 000000000..4f376a99b --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h @@ -0,0 +1,101 @@ +/* + * NimBLEDescriptor.h + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLEDescriptor.h + * + * Created on: Jun 22, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLEDESCRIPTOR_H_ +#define MAIN_NIMBLEDESCRIPTOR_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLECharacteristic.h" +#include "NimBLEUUID.h" +#include "FreeRTOS.h" + +#include + + +typedef struct +{ + uint16_t attr_max_len; /*!< attribute max value length */ + uint16_t attr_len; /*!< attribute current value length */ + uint8_t *attr_value; /*!< the pointer to attribute value */ +} attr_value_t; + +typedef attr_value_t esp_attr_value_t; /*!< compatibility for esp32 */ + +class NimBLEService; +class NimBLECharacteristic; +class NimBLEDescriptorCallbacks; + + +/** + * @brief A model of a %BLE descriptor. + */ +class NimBLEDescriptor { +public: + virtual ~NimBLEDescriptor(); + uint16_t getHandle(); // Get the handle of the descriptor. + size_t getLength(); // Get the length of the value of the descriptor. + NimBLEUUID getUUID(); // Get the UUID of the descriptor. + uint8_t* getValue(); // Get a pointer to the value of the descriptor. +// void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor. + void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor. + void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data. + void setValue(std::string value); // Set the value of the descriptor as a data buffer. + + std::string toString(); // Convert the descriptor to a string representation. + +private: + friend class NimBLEDescriptorMap; + friend class NimBLECharacteristic; + friend class NimBLEService; + friend class NimBLE2902; + friend class NimBLE2904; + + NimBLEDescriptor(const char* uuid, uint16_t properties, + uint16_t max_len, + NimBLECharacteristic* pCharacteristic); + + NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, + uint16_t max_len, + NimBLECharacteristic* pCharacteristic); + + NimBLEUUID m_uuid; + uint16_t m_handle; + NimBLEDescriptorCallbacks* m_pCallbacks; + NimBLECharacteristic* m_pCharacteristic; + uint8_t m_properties; + attr_value_t m_value; + + static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + + void setHandle(uint16_t handle); +}; // BLEDescriptor + + +/** + * @brief Callbacks that can be associated with a %BLE descriptors to inform of events. + * + * When a server application creates a %BLE descriptor, we may wish to be informed when there is either + * a read or write request to the descriptors value. An application can register a + * sub-classed instance of this class and will be notified when such an event happens. + */ +class NimBLEDescriptorCallbacks { +public: + virtual ~NimBLEDescriptorCallbacks(); + virtual void onRead(NimBLEDescriptor* pDescriptor); + virtual void onWrite(NimBLEDescriptor* pDescriptor); +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDescriptorMap.cpp b/libesp32/NimBLE-Arduino/src/NimBLEDescriptorMap.cpp new file mode 100644 index 000000000..8d0a13d29 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEDescriptorMap.cpp @@ -0,0 +1,143 @@ +/* + * NimBLEDescriptorMap.cpp + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLEDescriptorMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "NimBLECharacteristic.h" +#include "NimBLEDescriptor.h" + + +/** + * @brief Return the descriptor by UUID. + * @param [in] UUID The UUID to look up the descriptor. + * @return The descriptor. If not present, then nullptr is returned. + */ +NimBLEDescriptor* NimBLEDescriptorMap::getByUUID(const char* uuid) { + return getByUUID(NimBLEUUID(uuid)); +} + + +/** + * @brief Return the descriptor by UUID. + * @param [in] UUID The UUID to look up the descriptor. + * @return The descriptor. If not present, then nullptr is returned. + */ +NimBLEDescriptor* NimBLEDescriptorMap::getByUUID(NimBLEUUID uuid) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + return nullptr; +} // getByUUID + + +/** + * @brief Return the descriptor by handle. + * @param [in] handle The handle to look up the descriptor. + * @return The descriptor. + */ + /* +NimBLEDescriptor* NimBLEDescriptorMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle +*/ + +/** + * @brief Set the descriptor by UUID. + * @param [in] uuid The uuid of the descriptor. + * @param [in] characteristic The descriptor to cache. + * @return N/A. + */ +void NimBLEDescriptorMap::setByUUID(const char* uuid, NimBLEDescriptor* pDescriptor){ + m_uuidMap.insert(std::pair(pDescriptor, uuid)); +} // setByUUID + + + +/** + * @brief Set the descriptor by UUID. + * @param [in] uuid The uuid of the descriptor. + * @param [in] characteristic The descriptor to cache. + * @return N/A. + */ +void NimBLEDescriptorMap::setByUUID(NimBLEUUID uuid, NimBLEDescriptor* pDescriptor) { + m_uuidMap.insert(std::pair(pDescriptor, uuid.toString())); +} // setByUUID + + +/** + * @brief Set the descriptor by handle. + * @param [in] handle The handle of the descriptor. + * @param [in] descriptor The descriptor to cache. + * @return N/A. + */ + /* +void NimBLEDescriptorMap::setByHandle(uint16_t handle, NimBLEDescriptor* pDescriptor) { + m_handleMap.insert(std::pair(handle, pDescriptor)); +} // setByHandle +*/ + + +/** + * @brief Get the number of descriptors in the map. + */ +uint8_t NimBLEDescriptorMap::getSize() { + return (uint8_t)m_uuidMap.size(); +} // getSize + + +/** + * @brief Return a string representation of the descriptor map. + * @return A string representation of the descriptor map. + */ +std::string NimBLEDescriptorMap::toString() { + std::string res; + char hex[5]; + int count = 0; + for (auto &myPair : m_uuidMap) { + if (count > 0) {res += "\n";} + snprintf(hex, sizeof(hex), "%04x", myPair.first->getHandle()); + count++; + res += "handle: 0x"; + res += hex; + res += ", uuid: " + myPair.first->getUUID().toString(); + } + return res; +} // toString + + +/** + * @brief Get the first descriptor in the map. + * @return The first descriptor in the map. + */ +NimBLEDescriptor* NimBLEDescriptorMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLEDescriptor* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + + +/** + * @brief Get the next descriptor in the map. + * @return The next descriptor in the map. + */ +NimBLEDescriptor* NimBLEDescriptorMap::getNext() { + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLEDescriptor* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp b/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp new file mode 100644 index 000000000..a8de6718c --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp @@ -0,0 +1,678 @@ +/* + * NimBLEDevice.cpp + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEDevice.cpp + * + * Created on: Mar 16, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEDevice.h" +#include "NimBLEUtils.h" + +#include "esp_err.h" +#include "esp_bt.h" +#include "nvs_flash.h" +#include "esp_nimble_hci.h" +#include "nimble/nimble_port.h" +#include "nimble/nimble_port_freertos.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" + +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-bt.h" +#endif + +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEDevice"; + +/** + * Singletons for the NimBLEDevice. + */ +bool NimBLEDevice_initialized = false; +NimBLEScan* NimBLEDevice::m_pScan = nullptr; +NimBLEServer* NimBLEDevice::m_pServer = nullptr; +uint32_t NimBLEDevice::m_passkey = 123456; +bool NimBLEDevice::m_synced = false; +NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr; + +gap_event_handler NimBLEDevice::m_customGapHandler = nullptr; +ble_gap_event_listener NimBLEDevice::m_listener; +std::list NimBLEDevice::m_cList; +std::list NimBLEDevice::m_ignoreList; +NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr; + +//std::map BLEDevice::m_connectedClientsMap; + +//gattc_event_handler BLEDevice::m_customGattcHandler = nullptr; +//gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; + + +/** + * @brief Create a new instance of a server. + * @return A new instance of the server. + */ +/* STATIC */ NimBLEServer* NimBLEDevice::createServer() { +/*#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig + NIMBLE_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); + abort(); +#endif // CONFIG_GATTS_ENABLE +*/ + if(NimBLEDevice::m_pServer == nullptr) { + NimBLEDevice::m_pServer = new NimBLEServer(); + ble_gatts_reset(); + ble_svc_gap_init(); + ble_svc_gatt_init(); + } + + return m_pServer; +} // createServer + + +NimBLEAdvertising* NimBLEDevice::getAdvertising() { + if(m_bleAdvertising == nullptr) { + m_bleAdvertising = new NimBLEAdvertising(); + } + return m_bleAdvertising; +} + + +void NimBLEDevice::startAdvertising() { + getAdvertising()->start(); +} // startAdvertising + + +void NimBLEDevice::stopAdvertising() { + getAdvertising()->stop(); +} // stopAdvertising + + +/** + * @brief Retrieve the Scan object that we use for scanning. + * @return The scanning object reference. This is a singleton object. The caller should not + * try and release/delete it. + */ +/* STATIC */ NimBLEScan* NimBLEDevice::getScan() { + if (m_pScan == nullptr) { + m_pScan = new NimBLEScan(); + } + return m_pScan; +} // getScan + + +/** + * @brief Creates a new client object and maintains a list of all client objects + * each client can connect to 1 peripheral device. + * @return A reference to the new client object. + */ +/* STATIC */ NimBLEClient* NimBLEDevice::createClient() { + if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) { + NIMBLE_LOGW("Number of clients exceeds Max connections. Max=(%d)", + NIMBLE_MAX_CONNECTIONS); + } + + NimBLEClient* pClient = new NimBLEClient(); + m_cList.push_back(pClient); + + return pClient; +} // createClient + + +/** + * @brief Delete the client object and remove it from the list. + * Check if it is connected or trying to connect and close/stop it first. + * @param [in] Pointer to the client object. + */ +/* STATIC */ bool NimBLEDevice::deleteClient(NimBLEClient* pClient) { + if(pClient == nullptr) { + return false; + } + + if(pClient->m_isConnected) { + if (pClient->disconnect() != 0) { + return false; + } + while(pClient->m_isConnected) { + vTaskDelay(1); + } + } + + if(pClient->m_waitingToConnect) { + if(ble_gap_conn_cancel() != 0){ + return false; + } + while(pClient->m_waitingToConnect) { + vTaskDelay(1); + } + } + + m_cList.remove(pClient); + delete pClient; + + return true; +} // deleteClient + + +/** + * @brief get the list of clients. + * @return a pointer to the list of clients. + */ +/* STATIC */std::list* NimBLEDevice::getClientList() { + return &m_cList; +} // getClientList + + +/** + * @brief get the size of the list of clients. + * @return a pointer to the list of clients. + */ +/* STATIC */size_t NimBLEDevice::getClientListSize() { + return m_cList.size(); +} // getClientList + + +/** + * @brief Get a reference to a client by connection ID. + * @param [in] The client connection ID to search for. + * @return A reference pointer to the client with the spcified connection ID. + */ +/* STATIC */NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) { + for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { + if((*it)->getConnId() == conn_id) { + return (*it); + } + } + assert(0); + return nullptr; +} // getClientByID + + +/** + * @brief Get a reference to a client by peer address. + * @param [in] a NimBLEAddress of the peer to search for. + * @return A reference pointer to the client with the peer address. + */ +/* STATIC */NimBLEClient* NimBLEDevice::getClientByPeerAddress(NimBLEAddress peer_addr) { + for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { + if((*it)->getPeerAddress().equals(peer_addr)) { + return (*it); + } + } + return nullptr; +} // getClientPeerAddress + + +/** + * @brief Finds the first disconnected client in the list. + * @return A reference pointer to the first client that is not connected to a peer. + */ +/* STATIC */NimBLEClient* NimBLEDevice::getDisconnectedClient() { + for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { + if(!(*it)->isConnected()) { + return (*it); + } + } + return nullptr; +} // getDisconnectedClient + + +/** + * @brief Set the transmission power. + * The power level can be one of: + * * ESP_PWR_LVL_N12 = 0, !< Corresponding to -12dbm + * * ESP_PWR_LVL_N9 = 1, !< Corresponding to -9dbm + * * ESP_PWR_LVL_N6 = 2, !< Corresponding to -6dbm + * * ESP_PWR_LVL_N3 = 3, !< Corresponding to -3dbm + * * ESP_PWR_LVL_N0 = 4, !< Corresponding to 0dbm + * * ESP_PWR_LVL_P3 = 5, !< Corresponding to +3dbm + * * ESP_PWR_LVL_P6 = 6, !< Corresponding to +6dbm + * * ESP_PWR_LVL_P9 = 7, !< Corresponding to +9dbm + * @param [in] powerLevel. + */ +/* STATIC */ void NimBLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) { + NIMBLE_LOGD(LOG_TAG, ">> setPower: %d (type: %d)", powerLevel, powerType); + esp_err_t errRc = esp_ble_tx_power_set(powerType, powerLevel); + if (errRc != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d", errRc); + } + NIMBLE_LOGD(LOG_TAG, "<< setPower"); +} // setPower + + +/* STATIC */ int NimBLEDevice::getPower(esp_ble_power_type_t powerType) { + + switch(esp_ble_tx_power_get(powerType)) { + case ESP_PWR_LVL_N12: + return -12; + case ESP_PWR_LVL_N9: + return -9; + case ESP_PWR_LVL_N6: + return -6; + case ESP_PWR_LVL_N3: + return -6; + case ESP_PWR_LVL_N0: + return 0; + case ESP_PWR_LVL_P3: + return 3; + case ESP_PWR_LVL_P6: + return 6; + case ESP_PWR_LVL_P9: + return 9; + default: + return BLE_HS_ADV_TX_PWR_LVL_AUTO; + } +} // setPower + + +/** + * @brief Get our device address. + * @return A NimBLEAddress object of our public address if we have one, + * if not then our current random address. + */ +/* STATIC*/ NimBLEAddress NimBLEDevice::getAddress() { + ble_addr_t addr = {BLE_ADDR_PUBLIC, 0}; + //ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL) + + if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)) { + NIMBLE_LOGD(LOG_TAG, "Public address not found, checking random"); + addr.type = BLE_ADDR_RANDOM; + ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr.val, NULL); + } + + return NimBLEAddress(addr); +} // getAddress + + +/** + * @brief Return a string representation of the address of this device. + * @return A string representation of this device address. + */ +/* STATIC */ std::string NimBLEDevice::toString() { + return getAddress().toString(); +} // toString + + +/** + * @brief Setup local mtu that will be used to negotiate mtu during request from client peer + * @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to + * BLE_ATT_MTU_MAX = 527 + */ +/* STATIC */int NimBLEDevice::setMTU(uint16_t mtu) { + NIMBLE_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); + + int rc = ble_att_set_preferred_mtu(mtu); + + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Could not set local mtu value to: %d", mtu); + } + + NIMBLE_LOGD(LOG_TAG, "<< setLocalMTU"); + return rc; +} // setMTU + + +/** + * @brief Get local MTU value set. + */ +/* STATIC */uint16_t NimBLEDevice::getMTU() { + return ble_att_preferred_mtu(); +} + + +/** + * @brief Host reset, we pass the message so we don't make calls until resynced. + */ +/* STATIC */ void NimBLEDevice::onReset(int reason) +{ + if(!m_synced) { + return; + } + + m_synced = false; + + if(m_pScan != nullptr) { + m_pScan->onHostReset(); + } +/* Not needed + if(m_pServer != nullptr) { + m_pServer->onHostReset(); + } + + for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { + (*it)->onHostReset(); + } +*/ + if(m_bleAdvertising != nullptr) { + m_bleAdvertising->onHostReset(); + } + + NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason, + NimBLEUtils::returnCodeToString(reason)); +} // onReset + + +/** + * @brief Host resynced with controller, all clear to make calls. + */ +/* STATIC */ void NimBLEDevice::onSync(void) +{ + NIMBLE_LOGI(LOG_TAG, "NimBle host synced."); + // This check is needed due to potentially being called multiple times in succession + // If this happens, the call to scan start may get stuck or cause an advertising fault. + if(m_synced) { + return; + } + + /* Make sure we have proper identity address set (public preferred) */ + int rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + m_synced = true; + + if(m_pScan != nullptr) { + // Restart scanning with the last values sent, allow to clear results. + m_pScan->start(m_pScan->m_duration, m_pScan->m_scanCompleteCB); + } + + if(m_bleAdvertising != nullptr) { + // Restart advertisng, parameters should already be set. + m_bleAdvertising->start(); + } +} // onSync + + +/** + * @brief The main host task. + */ +/* STATIC */ void NimBLEDevice::host_task(void *param) +{ + NIMBLE_LOGI(LOG_TAG, "BLE Host Task Started"); + /* This function will return only when nimble_port_stop() is executed */ + nimble_port_run(); + + nimble_port_freertos_deinit(); +} // host_task + + +/** + * @brief Initialize the %BLE environment. + * @param deviceName The device name of the device. + */ +/* STATIC */ void NimBLEDevice::init(std::string deviceName) { + if(!NimBLEDevice_initialized){ + NimBLEDevice_initialized = true; // Set the initialization flag to ensure we are only initialized once. + + int rc=0; + esp_err_t errRc = ESP_OK; + +#ifdef ARDUINO_ARCH_ESP32 + // make sure the linker includes esp32-hal-bt.c so ardruino init doesn't release BLE memory. + btStarted(); +#endif + + errRc = nvs_flash_init(); + + if (errRc == ESP_ERR_NVS_NO_FREE_PAGES || errRc == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + errRc = nvs_flash_init(); + } + + ESP_ERROR_CHECK(errRc); + + ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init()); + + nimble_port_init(); + + // Setup callbacks for host events + ble_hs_cfg.reset_cb = NimBLEDevice::onReset; + ble_hs_cfg.sync_cb = NimBLEDevice::onSync; + + // Set initial security capabilities + ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT; + ble_hs_cfg.sm_bonding = 0; + ble_hs_cfg.sm_mitm = 0; + ble_hs_cfg.sm_sc = 1; + ble_hs_cfg.sm_our_key_dist = 1; + ble_hs_cfg.sm_their_key_dist = 3; + + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /*TODO: Implement handler for this*/ + + // Set the device name. + rc = ble_svc_gap_device_name_set(deviceName.c_str()); + assert(rc == 0); + + ble_store_config_init(); + + nimble_port_freertos_init(NimBLEDevice::host_task); + } + // Wait for host and controller to sync before returning and accepting new tasks + while(!m_synced){ + vTaskDelay(1 / portTICK_PERIOD_MS); + } + //vTaskDelay(200 / portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. +} // init + + +/** + * @brief Shutdown the NimBLE stack/controller. + */ +/* STATIC */ void NimBLEDevice::deinit() { + int ret = nimble_port_stop(); + if (ret == 0) { + nimble_port_deinit(); + + ret = esp_nimble_hci_and_controller_deinit(); + if (ret != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret); + } + + NimBLEDevice_initialized = false; + } +} // deinit + + +/** + * @brief Check if the initialization is complete. + */ +bool NimBLEDevice::getInitialized() { + return NimBLEDevice_initialized; +} // getInitialized + + +/** + * @brief Set the authorization mode for this device. + * @param bonding, if true we allow bonding, false no bonding will be performed. + * @param mitm, if true we are capable of man in the middle protection, false if not. + * @param sc, if true we will perform secure connection pairing, false we will use legacy pairing. + */ +/*STATIC*/ void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc) { + NIMBLE_LOGD(LOG_TAG, "Setting bonding: %d, mitm: %d, sc: %d",bonding,mitm,sc); + ble_hs_cfg.sm_bonding = bonding; + ble_hs_cfg.sm_mitm = mitm; + ble_hs_cfg.sm_sc = sc; +} // setSecurityAuth + + +/** + * @brief Set the authorization mode for this device. + * @param A bitmap indicating what modes are supported. + * The bits are defined as follows: + ** 0x01 BLE_SM_PAIR_AUTHREQ_BOND + ** 0x04 BLE_SM_PAIR_AUTHREQ_MITM + ** 0x08 BLE_SM_PAIR_AUTHREQ_SC + ** 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported. + ** 0xe2 BLE_SM_PAIR_AUTHREQ_RESERVED - for reference only. + */ +/*STATIC*/void NimBLEDevice::setSecurityAuth(uint8_t auth_req) { + NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0, + (auth_req & BLE_SM_PAIR_AUTHREQ_MITM)>0, + (auth_req & BLE_SM_PAIR_AUTHREQ_SC)>0); +} // setSecurityAuth + + +/** + * @brief Set the Input/Output capabilities of this device. + * @param One of the following: + ** 0x00 BLE_HS_IO_DISPLAY_ONLY DisplayOnly IO capability + ** 0x01 BLE_HS_IO_DISPLAY_YESNO DisplayYesNo IO capability + ** 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability + ** 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability + ** 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability + */ +/*STATIC*/ void NimBLEDevice::setSecurityIOCap(uint8_t iocap) { + ble_hs_cfg.sm_io_cap = iocap; +} // setSecurityIOCap + + +/** + * @brief If we are the initiator of the security procedure this sets the keys we will distribute. + * @param A bitmap indicating which keys to distribute during pairing. + * The bits are defined as follows: + ** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key. + ** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK). + ** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN + ** 0x08: BLE_SM_PAIR_KEY_DIST_LINK + */ +/*STATIC*/void NimBLEDevice::setSecurityInitKey(uint8_t init_key) { + ble_hs_cfg.sm_our_key_dist = init_key; +} // setsSecurityInitKey + + +/** + * @brief Set the keys we are willing to accept during pairing. + * @param A bitmap indicating which keys to accept during pairing. + * The bits are defined as follows: + ** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key. + ** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK). + ** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN + ** 0x08: BLE_SM_PAIR_KEY_DIST_LINK + */ +/*STATIC*/void NimBLEDevice::setSecurityRespKey(uint8_t init_key) { + ble_hs_cfg.sm_their_key_dist = init_key; +} // setsSecurityRespKey + + +/** + * @brief Set the passkey for pairing. + */ +/*STATIC*/void NimBLEDevice::setSecurityPasskey(uint32_t pin) { + m_passkey = pin; +} // setSecurityPasskey + + +/** + * @brief Get the passkey for pairing. + */ +/*STATIC*/uint32_t NimBLEDevice::getSecurityPasskey() { + return m_passkey; +} // getSecurityPasskey + + +/** + * @brief Set callbacks that will be used to handle encryption negotiation events and authentication events + * @param [in] cllbacks Pointer to NimBLESecurityCallbacks class + */ +void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) { + NimBLEDevice::m_securityCallbacks = callbacks; +} // setSecurityCallbacks + + +/** + * @brief Start the connection securing and authorization for this connection. + * @param Connection id of the client. + * @returns host return code 0 if success. + */ +/* STATIC */int NimBLEDevice::startSecurity(uint16_t conn_id) { + /* if(m_securityCallbacks != nullptr) { + m_securityCallbacks->onSecurityRequest(); + } + */ + int rc = ble_gap_security_initiate(conn_id); + if(rc != 0){ + NIMBLE_LOGE(LOG_TAG, "ble_gap_security_initiate: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + + return rc; +} // startSecurity + + +/** + * @brief Check if the device address is on our ignore list. + * @return True if ignoring. + */ +/*STATIC*/ bool NimBLEDevice::isIgnored(NimBLEAddress address) { + for(auto &it : m_ignoreList) { + if(it.equals(address)){ + return true; + } + } + + return false; +} + + +/** + * @brief Add a device to the ignore list. + * @param Address of the device we want to ignore. + */ +/*STATIC*/ void NimBLEDevice::addIgnored(NimBLEAddress address) { + m_ignoreList.push_back(address); +} + + +/** + * @brief Remove a device from the ignore list. + * @param Address of the device we want to remove from the list. + */ +/*STATIC*/void NimBLEDevice::removeIgnored(NimBLEAddress address) { + for(auto it = m_ignoreList.begin(); it != m_ignoreList.end(); ++it) { + if((*it).equals(address)){ + m_ignoreList.erase(it); + return; + } + } +} + + +/** + * @brief Set a custom callback for gap events. + */ +void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) { + m_customGapHandler = handler; + int rc = ble_gap_event_listener_register(&m_listener, m_customGapHandler, NULL); + if(rc == BLE_HS_EALREADY){ + NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events."); + } + else{ + assert(rc == 0); + } +} // setCustomGapHandler + + +/** + * @brief Backward compatibility for bluedroid gatt events. + * NimBLe does not send GATT events + */ + /* +void BLEDevice::setCustomGattcHandler(gattc_event_handler handler) { + setCustomGapHandler(handler); +} + +void BLEDevice::setCustomGattsHandler(gatts_event_handler handler) { + setCustomGapHandler(handler); +} +*/ +/**********************************************************/ + + +#endif // CONFIG_BT_ENABLED diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDevice.h b/libesp32/NimBLE-Arduino/src/NimBLEDevice.h new file mode 100644 index 000000000..b2731c23f --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEDevice.h @@ -0,0 +1,141 @@ +/* + * NimBLEDevice.h + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEDevice.h + * + * Created on: Mar 16, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLEDEVICE_H_ +#define MAIN_NIMBLEDEVICE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEScan.h" +#include "NimBLEUtils.h" +#include "NimBLEClient.h" +#include "NimBLEServer.h" +#include "NimBLESecurity.h" + +#include "esp_bt.h" + +#include +#include +#include + +#define BLEDevice NimBLEDevice +#define BLEClient NimBLEClient +#define BLERemoteService NimBLERemoteService +#define BLERemoteCharacteristic NimBLERemoteCharacteristic +#define BLERemoteDescriptor NimBLERemoteDescriptor +#define BLEAdvertisedDevice NimBLEAdvertisedDevice +#define BLEScan NimBLEScan +#define BLEUUID NimBLEUUID +#define BLESecurity NimBLESecurity +#define BLESecurityCallbacks NimBLESecurityCallbacks +#define BLEAddress NimBLEAddress +#define BLEUtils NimBLEUtils +#define BLEClientCallbacks NimBLEClientCallbacks +#define BLEAdvertisedDeviceCallbacks NimBLEAdvertisedDeviceCallbacks +#define BLEScanResults NimBLEScanResults +#define BLEServer NimBLEServer +#define BLEService NimBLEService +#define BLECharacteristic NimBLECharacteristic +#define BLEAdvertising NimBLEAdvertising +#define BLEServerCallbacks NimBLEServerCallbacks +#define BLECharacteristicCallbacks NimBLECharacteristicCallbacks +#define BLEAdvertisementData NimBLEAdvertisementData +#define BLEDescriptor NimBLEDescriptor +#define BLE2902 NimBLE2902 +#define BLE2904 NimBLE2904 +#define BLEDescriptorCallbacks NimBLEDescriptorCallbacks +#define BLEBeacon NimBLEBeacon +#define BLEEddystoneTLM NimBLEEddystoneTLM +#define BLEEddystoneURL NimBLEEddystoneURL + +#ifdef CONFIG_BT_NIMBLE_MAX_CONNECTIONS +#define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS +#else +#define NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS +#endif + +/** + * @brief BLE functions. + */ + typedef int (*gap_event_handler)(ble_gap_event *event, void *arg); +//typedef void (*gattc_event_handler)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param); +//typedef void (*gatts_event_handler)(esp_gatts_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gatts_cb_param_t* param); + +extern "C" void ble_store_config_init(void); + +class NimBLEDevice { +public: + static void init(std::string deviceName); // Initialize the local BLE environment. + static void deinit(); + static bool getInitialized(); + static NimBLEAddress getAddress(); + static std::string toString(); + static NimBLEScan* getScan(); // Get the scan object + static NimBLEClient* createClient(); + static NimBLEServer* createServer(); + static bool deleteClient(NimBLEClient* pClient); + static void setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); + static int getPower(esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); + static void setCustomGapHandler(gap_event_handler handler); + static void setSecurityAuth(bool bonding, bool mitm, bool sc); + static void setSecurityAuth(uint8_t auth_req); + static void setSecurityIOCap(uint8_t iocap); + static void setSecurityInitKey(uint8_t init_key); + static void setSecurityRespKey(uint8_t init_key); + static void setSecurityPasskey(uint32_t pin); + static uint32_t getSecurityPasskey(); + static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks); + static int setMTU(uint16_t mtu); + static uint16_t getMTU(); + static bool isIgnored(NimBLEAddress address); + static void addIgnored(NimBLEAddress address); + static void removeIgnored(NimBLEAddress address); + static NimBLEAdvertising* getAdvertising(); + static void startAdvertising(); + static void stopAdvertising(); + static NimBLEClient* getClientByID(uint16_t conn_id); + static NimBLEClient* getClientByPeerAddress(NimBLEAddress peer_addr); + static NimBLEClient* getDisconnectedClient(); + static size_t getClientListSize(); + static std::list* getClientList(); + +private: + friend class NimBLEServer; + friend class NimBLEClient; + friend class NimBLEScan; + friend class NimBLEAdvertising; + friend class NimBLECharacteristic; + + static void onReset(int reason); + static void onSync(void); + static void host_task(void *param); + static int startSecurity( uint16_t conn_id); + + static bool m_synced; + static NimBLEScan* m_pScan; + static NimBLEServer* m_pServer; + static NimBLEAdvertising* m_bleAdvertising; + static ble_gap_event_listener m_listener; + static uint32_t m_passkey; + static std::list m_cList; + static std::list m_ignoreList; + static NimBLESecurityCallbacks* m_securityCallbacks; + +public: + static gap_event_handler m_customGapHandler; +}; + + +#endif // CONFIG_BT_ENABLED +#endif // MAIN_NIMBLEDEVICE_H_ diff --git a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp new file mode 100644 index 000000000..b601d8de0 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp @@ -0,0 +1,152 @@ +/* + * NimBLEEddystoneTLM.cpp + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneTLM.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEEddystoneTLM.h" +#include "NimBLELog.h" + +#include + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) +#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) + +static const char LOG_TAG[] = "NimBLEEddystoneTLM"; + + +NimBLEEddystoneTLM::NimBLEEddystoneTLM() { + beaconUUID = 0xFEAA; + m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE; + m_eddystoneData.version = 0; + m_eddystoneData.volt = 3300; // 3300mV = 3.3V + m_eddystoneData.temp = (uint16_t) ((float) 23.00 * 256); // 8.8 fixed format + m_eddystoneData.advCount = 0; + m_eddystoneData.tmil = 0; +} // NimBLEEddystoneTLM + +std::string NimBLEEddystoneTLM::getData() { + return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); +} // getData + +NimBLEUUID NimBLEEddystoneTLM::getUUID() { + return NimBLEUUID(beaconUUID); +} // getUUID + +uint8_t NimBLEEddystoneTLM::getVersion() { + return m_eddystoneData.version; +} // getVersion + +uint16_t NimBLEEddystoneTLM::getVolt() { + return ENDIAN_CHANGE_U16(m_eddystoneData.volt); +} // getVolt + +float NimBLEEddystoneTLM::getTemp() { + return ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f; +} // getTemp + +uint32_t NimBLEEddystoneTLM::getCount() { + return ENDIAN_CHANGE_U32(m_eddystoneData.advCount); +} // getCount + +uint32_t NimBLEEddystoneTLM::getTime() { + return (ENDIAN_CHANGE_U32(m_eddystoneData.tmil)) / 10; +} // getTime + +std::string NimBLEEddystoneTLM::toString() { + std::string out = ""; + uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); + char val[12]; + + out += "Version "; // + std::string(m_eddystoneData.version); + snprintf(val, sizeof(val), "%d", m_eddystoneData.version); + out += val; + out += "\n"; + out += "Battery Voltage "; // + ENDIAN_CHANGE_U16(m_eddystoneData.volt); + snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U16(m_eddystoneData.volt)); + out += val; + out += " mV\n"; + + out += "Temperature "; + snprintf(val, sizeof(val), "%.2f", ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f); + out += val; + out += " C\n"; + + out += "Adv. Count "; + snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U32(m_eddystoneData.advCount)); + out += val; + out += "\n"; + + out += "Time in seconds "; + snprintf(val, sizeof(val), "%d", rawsec/10); + out += val; + out += "\n"; + + out += "Time "; + + snprintf(val, sizeof(val), "%04d", rawsec / 864000); + out += val; + out += "."; + + snprintf(val, sizeof(val), "%02d", (rawsec / 36000) % 24); + out += val; + out += ":"; + + snprintf(val, sizeof(val), "%02d", (rawsec / 600) % 60); + out += val; + out += ":"; + + snprintf(val, sizeof(val), "%02d", (rawsec / 10) % 60); + out += val; + out += "\n"; + + return out; +} // toString + +/** + * Set the raw data for the beacon record. + */ +void NimBLEEddystoneTLM::setData(std::string data) { + if (data.length() != sizeof(m_eddystoneData)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", + data.length(), sizeof(m_eddystoneData)); + return; + } + memcpy(&m_eddystoneData, data.data(), data.length()); +} // setData + +void NimBLEEddystoneTLM::setUUID(NimBLEUUID l_uuid) { + beaconUUID = l_uuid.getNative()->u16.value; +} // setUUID + +void NimBLEEddystoneTLM::setVersion(uint8_t version) { + m_eddystoneData.version = version; +} // setVersion + +void NimBLEEddystoneTLM::setVolt(uint16_t volt) { + m_eddystoneData.volt = volt; +} // setVolt + +void NimBLEEddystoneTLM::setTemp(float temp) { + m_eddystoneData.temp = (uint16_t)temp; +} // setTemp + +void NimBLEEddystoneTLM::setCount(uint32_t advCount) { + m_eddystoneData.advCount = advCount; +} // setCount + +void NimBLEEddystoneTLM::setTime(uint32_t tmil) { + m_eddystoneData.tmil = tmil; +} // setTime + +#endif diff --git a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.h b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.h new file mode 100644 index 000000000..3c7219eb6 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.h @@ -0,0 +1,60 @@ +/* + * NimBLEEddystoneTLM.h + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneTLM.h + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#ifndef _NimBLEEddystoneTLM_H_ +#define _NimBLEEddystoneTLM_H_ +#include "NimBLEUUID.h" + +#include + +#define EDDYSTONE_TLM_FRAME_TYPE 0x20 + +/** + * @brief Representation of a beacon. + * See: + * * https://github.com/google/eddystone + */ +class NimBLEEddystoneTLM { +public: + NimBLEEddystoneTLM(); + std::string getData(); + NimBLEUUID getUUID(); + uint8_t getVersion(); + uint16_t getVolt(); + float getTemp(); + uint32_t getCount(); + uint32_t getTime(); + std::string toString(); + void setData(std::string data); + void setUUID(NimBLEUUID l_uuid); + void setVersion(uint8_t version); + void setVolt(uint16_t volt); + void setTemp(float temp); + void setCount(uint32_t advCount); + void setTime(uint32_t tmil); + +private: + uint16_t beaconUUID; + struct { + uint8_t frameType; + uint8_t version; + uint16_t volt; + uint16_t temp; + uint32_t advCount; + uint32_t tmil; + } __attribute__((packed)) m_eddystoneData; + +}; // NimBLEEddystoneTLM + +#endif /* _NimBLEEddystoneTLM_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp new file mode 100644 index 000000000..c1d2aff6f --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp @@ -0,0 +1,159 @@ +/* + * NimBLEEddystoneURL.cpp + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneURL.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEEddystoneURL.h" +#include "NimBLELog.h" + +#include + +static const char LOG_TAG[] = "NimBLEEddystoneURL"; + +NimBLEEddystoneURL::NimBLEEddystoneURL() { + beaconUUID = 0xFEAA; + lengthURL = 0; + m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE; + m_eddystoneData.advertisedTxPower = 0; + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); +} // BLEEddystoneURL + +std::string NimBLEEddystoneURL::getData() { + return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); +} // getData + +NimBLEUUID NimBLEEddystoneURL::getUUID() { + return NimBLEUUID(beaconUUID); +} // getUUID + +int8_t NimBLEEddystoneURL::getPower() { + return m_eddystoneData.advertisedTxPower; +} // getPower + +std::string NimBLEEddystoneURL::getURL() { + return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url)); +} // getURL + +std::string NimBLEEddystoneURL::getDecodedURL() { + std::string decodedURL = ""; + + switch (m_eddystoneData.url[0]) { + case 0x00: + decodedURL += "http://www."; + break; + case 0x01: + decodedURL += "https://www."; + break; + case 0x02: + decodedURL += "http://"; + break; + case 0x03: + decodedURL += "https://"; + break; + default: + decodedURL += m_eddystoneData.url[0]; + } + + for (int i = 1; i < lengthURL; i++) { + if (m_eddystoneData.url[i] > 33 && m_eddystoneData.url[i] < 127) { + decodedURL += m_eddystoneData.url[i]; + } else { + switch (m_eddystoneData.url[i]) { + case 0x00: + decodedURL += ".com/"; + break; + case 0x01: + decodedURL += ".org/"; + break; + case 0x02: + decodedURL += ".edu/"; + break; + case 0x03: + decodedURL += ".net/"; + break; + case 0x04: + decodedURL += ".info/"; + break; + case 0x05: + decodedURL += ".biz/"; + break; + case 0x06: + decodedURL += ".gov/"; + break; + case 0x07: + decodedURL += ".com"; + break; + case 0x08: + decodedURL += ".org"; + break; + case 0x09: + decodedURL += ".edu"; + break; + case 0x0A: + decodedURL += ".net"; + break; + case 0x0B: + decodedURL += ".info"; + break; + case 0x0C: + decodedURL += ".biz"; + break; + case 0x0D: + decodedURL += ".gov"; + break; + default: + break; + } + } + } + return decodedURL; +} // getDecodedURL + + + +/** + * Set the raw data for the beacon record. + */ +void NimBLEEddystoneURL::setData(std::string data) { + if (data.length() > sizeof(m_eddystoneData)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and max expected %d", + data.length(), sizeof(m_eddystoneData)); + return; + } + memset(&m_eddystoneData, 0, sizeof(m_eddystoneData)); + memcpy(&m_eddystoneData, data.data(), data.length()); + lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url)); +} // setData + +void NimBLEEddystoneURL::setUUID(NimBLEUUID l_uuid) { + beaconUUID = l_uuid.getNative()->u16.value; +} // setUUID + +void NimBLEEddystoneURL::setPower(int8_t advertisedTxPower) { + m_eddystoneData.advertisedTxPower = advertisedTxPower; +} // setPower + +void NimBLEEddystoneURL::setURL(std::string url) { + if (url.length() > sizeof(m_eddystoneData.url)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", + url.length(), sizeof(m_eddystoneData.url)); + return; + } + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); + memcpy(m_eddystoneData.url, url.data(), url.length()); + lengthURL = url.length(); +} // setURL + + +#endif diff --git a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.h b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.h new file mode 100644 index 000000000..2e135886a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.h @@ -0,0 +1,52 @@ +/* + * NimBLEEddystoneURL.h + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneURL.h + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#ifndef _NIMBLEEddystoneURL_H_ +#define _NIMBLEEddystoneURL_H_ +#include "NimBLEUUID.h" + +#include + +#define EDDYSTONE_URL_FRAME_TYPE 0x10 + +/** + * @brief Representation of a beacon. + * See: + * * https://github.com/google/eddystone + */ +class NimBLEEddystoneURL { +public: + NimBLEEddystoneURL(); + std::string getData(); + NimBLEUUID getUUID(); + int8_t getPower(); + std::string getURL(); + std::string getDecodedURL(); + void setData(std::string data); + void setUUID(NimBLEUUID l_uuid); + void setPower(int8_t advertisedTxPower); + void setURL(std::string url); + +private: + uint16_t beaconUUID; + uint8_t lengthURL; + struct { + uint8_t frameType; + int8_t advertisedTxPower; + uint8_t url[16]; + } __attribute__((packed)) m_eddystoneData; + +}; // NIMBLEEddystoneURL + +#endif /* _NIMBLEEddystoneURL_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLELog.h b/libesp32/NimBLE-Arduino/src/NimBLELog.h new file mode 100644 index 000000000..d223a046e --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLELog.h @@ -0,0 +1,60 @@ +/* + * NimBLELog.h + * + * Created: on Feb 24 2020 + * Author H2zero + * + */ +#ifndef MAIN_NIMBLELOG_H_ +#define MAIN_NIMBLELOG_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "syscfg/syscfg.h" +#include "modlog/modlog.h" + + +// If Arduino is being used, strip out the colors and ignore log printing below ui setting. +// Note: because CONFIG_LOG_DEFAULT_LEVEL is set at ERROR in Arduino we must use MODLOG_DFLT(ERROR +// otherwise no messages will be printed above that level. +#ifdef ARDUINO_ARCH_ESP32 +#ifndef CORE_DEBUG_LEVEL +#define CORE_DEBUG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL +#endif + +#if CORE_DEBUG_LEVEL >= 4 +#define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(ERROR, "D %s: "#format"\n",tag,##__VA_ARGS__) +#else +#define NIMBLE_LOGD( tag, format, ... ) +#endif + +#if CORE_DEBUG_LEVEL >= 3 +#define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(ERROR, "I %s: "#format"\n",tag,##__VA_ARGS__) +#else +#define NIMBLE_LOGI( tag, format, ... ) +#endif + +#if CORE_DEBUG_LEVEL >= 2 +#define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(ERROR, "W %s: "#format"\n",tag,##__VA_ARGS__) +#else +#define NIMBLE_LOGW( tag, format, ... ) +#endif + +#if CORE_DEBUG_LEVEL >= 1 +#define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "E %s: "#format"\n",tag,##__VA_ARGS__) +#else +#define NIMBLE_LOGE( tag, format, ... ) +#endif + +#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "CRIT %s: "#format"\n",tag,##__VA_ARGS__) + +#else +#define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "\033[0;31mE %s: "#format"\033[0m\n",tag,##__VA_ARGS__) +#define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(WARN, "\033[0;33mW %s: "#format"\033[0m\n",tag,##__VA_ARGS__) +#define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(INFO, "\033[0;32mI %s: "#format"\033[0m\n",tag,##__VA_ARGS__) +#define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(DEBUG, "D %s: "#format"\n",tag,##__VA_ARGS__) +#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "\033[1;31mCRIT %s: "#format"\033[0m\n",tag,##__VA_ARGS__) +#endif /*ARDUINO_ARCH_ESP32*/ + +#endif /*CONFIG_BT_ENABLED*/ +#endif /*MAIN_NIMBLELOG_H_*/ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp b/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp new file mode 100644 index 000000000..a48354c57 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp @@ -0,0 +1,631 @@ +/* + * NimBLERemoteCharacteristic.cpp + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteCharacteristic.cpp + * + * Created on: Mar 16, 2017 + * Author: kolban + */ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLERemoteCharacteristic.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLERemoteCharacteristic"; + +/** + * @brief Constructor. + * @param [in] reference to the service this characteristic belongs to. + * @param [in] ble_gatt_chr struct defined as: + * struct ble_gatt_chr { + * uint16_t def_handle; + * uint16_t val_handle; + * uint8_t properties; + * ble_uuid_any_t uuid; + * }; + */ + NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService, const struct ble_gatt_chr *chr) { + + switch (chr->uuid.u.type) { + case BLE_UUID_TYPE_16: + m_uuid = NimBLEUUID(chr->uuid.u16.value); + break; + case BLE_UUID_TYPE_32: + m_uuid = NimBLEUUID(chr->uuid.u32.value); + break; + case BLE_UUID_TYPE_128: + m_uuid = NimBLEUUID(const_cast(&chr->uuid.u128)); + break; + default: + m_uuid = nullptr; + break; + } + m_handle = chr->val_handle; + m_defHandle = chr->def_handle; + m_charProp = chr->properties; + m_pRemoteService = pRemoteService; + m_notifyCallback = nullptr; + } // NimBLERemoteCharacteristic + + +/** + *@brief Destructor. + */ +NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() { + removeDescriptors(); // Release resources for any descriptor information we may have allocated. + if(m_rawData != nullptr) free(m_rawData); +} // ~NimBLERemoteCharacteristic + +/* +#define BLE_GATT_CHR_PROP_BROADCAST 0x01 +#define BLE_GATT_CHR_PROP_READ 0x02 +#define BLE_GATT_CHR_PROP_WRITE_NO_RSP 0x04 +#define BLE_GATT_CHR_PROP_WRITE 0x08 +#define BLE_GATT_CHR_PROP_NOTIFY 0x10 +#define BLE_GATT_CHR_PROP_INDICATE 0x20 +#define BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE 0x40 +#define BLE_GATT_CHR_PROP_EXTENDED 0x80 +*/ + +/** + * @brief Does the characteristic support broadcasting? + * @return True if the characteristic supports broadcasting. + */ +bool NimBLERemoteCharacteristic::canBroadcast() { + return (m_charProp & BLE_GATT_CHR_PROP_BROADCAST) != 0; +} // canBroadcast + + +/** + * @brief Does the characteristic support indications? + * @return True if the characteristic supports indications. + */ +bool NimBLERemoteCharacteristic::canIndicate() { + return (m_charProp & BLE_GATT_CHR_PROP_INDICATE) != 0; +} // canIndicate + + +/** + * @brief Does the characteristic support notifications? + * @return True if the characteristic supports notifications. + */ +bool NimBLERemoteCharacteristic::canNotify() { + return (m_charProp & BLE_GATT_CHR_PROP_NOTIFY) != 0; +} // canNotify + + +/** + * @brief Does the characteristic support reading? + * @return True if the characteristic supports reading. + */ +bool NimBLERemoteCharacteristic::canRead() { + return (m_charProp & BLE_GATT_CHR_PROP_READ) != 0; +} // canRead + + +/** + * @brief Does the characteristic support writing? + * @return True if the characteristic supports writing. + */ +bool NimBLERemoteCharacteristic::canWrite() { + return (m_charProp & BLE_GATT_CHR_PROP_WRITE) != 0; +} // canWrite + + +/** + * @brief Does the characteristic support writing with no response? + * @return True if the characteristic supports writing with no response. + */ +bool NimBLERemoteCharacteristic::canWriteNoResponse() { + return (m_charProp & BLE_GATT_CHR_PROP_WRITE_NO_RSP) != 0; +} // canWriteNoResponse + + +/** + * @brief Callback used by the API when a descriptor is discovered or search complete. + */ +int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *dsc, + void *arg) +{ + NIMBLE_LOGD(LOG_TAG,"Descriptor Discovered >> status: %d handle: %d", error->status, conn_handle); + + NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)arg; + int rc=0; + + // Make sure the discovery is for this device + if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){ + return 0; + } + + switch (error->status) { + case 0: { + // Found a descriptor - add it to the map + NimBLERemoteDescriptor* pNewRemoteDescriptor = new NimBLERemoteDescriptor(characteristic, dsc); + characteristic->m_descriptorMap.insert(std::pair(pNewRemoteDescriptor->getUUID().toString(), pNewRemoteDescriptor)); + + break; + } + case BLE_HS_EDONE:{ + /* All descriptors in this characteristic discovered; */ + characteristic->m_semaphoreGetDescEvt.give(0); + rc = 0; + break; + } + default: + rc = error->status; + break; + } + if (rc != 0) { + /* Error; abort discovery. */ + // pass non-zero to semaphore on error to indicate an error finding descriptors + characteristic->m_semaphoreGetDescEvt.give(1); + } + NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", rc); + return rc; +} + +/** + * @brief Populate the descriptors (if any) for this characteristic. + * @param [in] the end handle of the characteristic, or the service, whichever comes first. + */ +bool NimBLERemoteCharacteristic::retrieveDescriptors(uint16_t endHdl) { + NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); + + int rc = 0; + //removeDescriptors(); // Remove any existing descriptors. + + m_semaphoreGetDescEvt.take("retrieveDescriptors"); + + rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(), + m_handle, + endHdl, + NimBLERemoteCharacteristic::descriptorDiscCB, + this); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + m_semaphoreGetDescEvt.give(); + return false; + } + + if(m_semaphoreGetDescEvt.wait("retrieveCharacteristics") != 0) { + // if there was an error release the resources + //removeDescriptors(); + return false; + } + + return true; + NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorMap.size()); +} // getDescriptors + + +/** + * @brief Retrieve the map of descriptors keyed by UUID. + */ +std::map* NimBLERemoteCharacteristic::getDescriptors() { + return &m_descriptorMap; +} // getDescriptors + + +/** + * @brief Get the handle for this characteristic. + * @return The handle for this characteristic. + */ +uint16_t NimBLERemoteCharacteristic::getHandle() { + return m_handle; +} // getHandle + +/** + * @brief Get the handle for this characteristics definition. + * @return The handle for this characteristic definition. + */ +uint16_t NimBLERemoteCharacteristic::getDefHandle() { + return m_defHandle; +} // getDefHandle + + +/** + * @brief Get the descriptor instance with the given UUID that belongs to this characteristic. + * @param [in] uuid The UUID of the descriptor to find. + * @return The Remote descriptor (if present) or null if not present. + */ +NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(NimBLEUUID uuid) { + NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str()); + std::string v = uuid.toString(); + for (auto &myPair : m_descriptorMap) { + if (myPair.first == v) { + NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found"); + return myPair.second; + } + } + NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found"); + return nullptr; +} // getDescriptor + + +/** + * @brief Get the remote service associated with this characteristic. + * @return The remote service associated with this characteristic. + */ +NimBLERemoteService* NimBLERemoteCharacteristic::getRemoteService() { + return m_pRemoteService; +} // getRemoteService + + +/** + * @brief Get the UUID for this characteristic. + * @return The UUID for this characteristic. + */ +NimBLEUUID NimBLERemoteCharacteristic::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Read an unsigned 16 bit value + * @return The unsigned 16 bit value. + */ +uint16_t NimBLERemoteCharacteristic::readUInt16() { + std::string value = readValue(); + if (value.length() >= 2) { + return *(uint16_t*)(value.data()); + } + return 0; +} // readUInt16 + + +/** + * @brief Read an unsigned 32 bit value. + * @return the unsigned 32 bit value. + */ +uint32_t NimBLERemoteCharacteristic::readUInt32() { + std::string value = readValue(); + if (value.length() >= 4) { + return *(uint32_t*)(value.data()); + } + return 0; +} // readUInt32 + + +/** + * @brief Read a byte value + * @return The value as a byte + */ +uint8_t NimBLERemoteCharacteristic::readUInt8() { + std::string value = readValue(); + if (value.length() >= 1) { + return (uint8_t)value[0]; + } + return 0; +} // readUInt8 + + +/** + * @brief Read the value of the remote characteristic. + * @return The value of the remote characteristic. + */ +std::string NimBLERemoteCharacteristic::readValue() { + NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", getUUID().toString().c_str(), getHandle(), getHandle()); + + int rc = 0; + int retryCount = 1; + + NimBLEClient* pClient = getRemoteService()->getClient(); + + // Check to see that we are connected. + if (!pClient->isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Disconnected"); + return ""; + } + + do { + m_semaphoreReadCharEvt.take("readValue"); + + rc = ble_gattc_read(pClient->getConnId(), m_handle, + NimBLERemoteCharacteristic::onReadCB, this); + +// long read experiment +/* rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, + NimBLERemoteCharacteristic::onReadCB, this); +*/ + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d", rc); + m_semaphoreReadCharEvt.give(); + return ""; + } + + rc = m_semaphoreReadCharEvt.wait("readValue"); + switch(rc){ + case 0: + break; + + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + if (retryCount && pClient->secureConnection()) + break; + /* Else falls through. */ + default: + return ""; + } + } while(rc != 0 && retryCount--); + + NIMBLE_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); + return (rc == 0) ? m_value : ""; +} // readValue + + +/** + * @brief Callback for characteristic read operation. + * @return 0 or error code. + */ +int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + NimBLERemoteCharacteristic* characteristic = (NimBLERemoteCharacteristic*)arg; + + // Make sure the discovery is for this device + if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){ + return 0; + } + NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); +// long read experiment +/* if(attr && (attr->om->om_len >= (ble_att_mtu(characteristic->getRemoteService()->getClient()->getConnId()) - 1))){ + + return 0; + } +*/ + + if(characteristic->m_rawData != nullptr) { + free(characteristic->m_rawData); + } + + if (error->status == 0) { + characteristic->m_value = std::string((char*) attr->om->om_data, attr->om->om_len); + characteristic->m_rawData = (uint8_t*) calloc(attr->om->om_len, sizeof(uint8_t)); + memcpy(characteristic->m_rawData, attr->om->om_data, attr->om->om_len); + characteristic->m_semaphoreReadCharEvt.give(0); + } else { + characteristic->m_rawData = nullptr; + characteristic->m_value = ""; + characteristic->m_semaphoreReadCharEvt.give(error->status); + } + +// characteristic->m_semaphoreReadCharEvt.give(error->status); + return 0; //1 +} + + +/** + * @brief Register for notifications. + * @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we are + * unregistering for notifications. + * @param [in] bool if true, register for notifications, false register for indications. + * @param [in] bool if true, require a write response from the descriptor write operation. + * @return true if successful. + */ +bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications, bool response) { + NIMBLE_LOGD(LOG_TAG, ">> registerForNotify(): %s", toString().c_str()); + + m_notifyCallback = notifyCallback; // Save the notification callback. + + uint8_t val[] = {0x01, 0x00}; + + NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902)); + if(desc == nullptr) + return false; + + if(notifyCallback != nullptr){ + if(!notifications){ + val[0] = 0x02; + } + } + + else if (notifyCallback == nullptr){ + val[0] = 0x00; + } + + NIMBLE_LOGD(LOG_TAG, "<< registerForNotify()"); + + return desc->writeValue(val, 2, response); +} // registerForNotify + + +/** + * @brief Delete the descriptors in the descriptor map. + * We maintain a map called m_descriptorMap that contains pointers to BLERemoteDescriptors + * object references. Since we allocated these in this class, we are also responsible for deleteing + * them. This method does just that. + * @return N/A. + */ +void NimBLERemoteCharacteristic::removeDescriptors() { + // Iterate through all the descriptors releasing their storage and erasing them from the map. + for (auto &myPair : m_descriptorMap) { + m_descriptorMap.erase(myPair.first); + delete myPair.second; + } + m_descriptorMap.clear(); // Technically not neeeded, but just to be sure. +} // removeCharacteristics + + +/** + * @brief Convert a BLERemoteCharacteristic to a string representation; + * @return a String representation. + */ +std::string NimBLERemoteCharacteristic::toString() { + std::string res = "Characteristic: uuid: " + m_uuid.toString(); + char val[6]; + res += ", handle: "; + snprintf(val, sizeof(val), "%d", getHandle()); + res += val; + res += " 0x"; + snprintf(val, sizeof(val), "%04x", getHandle()); + res += val; + res += ", props: "; + res += " 0x"; + snprintf(val, sizeof(val), "%02x", m_charProp); + res += val; + + for (auto &myPair : m_descriptorMap) { + res += "\n" + myPair.second->toString(); + } + + return res; +} // toString + + +/** + * @brief Write the new value for the characteristic. + * @param [in] newValue The new value to write. + * @param [in] response Do we expect a response? + * @return false if not connected or cant perform write for some reason. + */ +bool NimBLERemoteCharacteristic::writeValue(std::string newValue, bool response) { + return writeValue((uint8_t*)newValue.c_str(), strlen(newValue.c_str()), response); +} // writeValue + + +/** + * @brief Write the new value for the characteristic. + * + * This is a convenience function. Many BLE characteristics are a single byte of data. + * @param [in] newValue The new byte value to write. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or cant perform write for some reason. + */ +bool NimBLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { + return writeValue(&newValue, 1, response); +} // writeValue + + +/** + * @brief Write the new value for the characteristic from a data buffer. + * @param [in] data A pointer to a data buffer. + * @param [in] length The length of the data in the data buffer. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or cant perform write for some reason. + */ +bool NimBLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) { + + NIMBLE_LOGD(LOG_TAG, ">> writeValue(), length: %d", length); + + NimBLEClient* pClient = getRemoteService()->getClient(); + int rc = 0; + int retryCount = 1; +// uint16_t mtu; + + // Check to see that we are connected. + if (!pClient->isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Disconnected"); + return false; + } + +// mtu = ble_att_mtu(pClient->getConnId()) - 3; + + if(/*!length > mtu &&*/ !response) { + rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length); + return (rc==0); + } + + do { + m_semaphoreWriteCharEvt.take("writeValue"); +// long write experiment +/* if(length > mtu) { + NIMBLE_LOGD(LOG_TAG,"long write"); + os_mbuf *om = ble_hs_mbuf_from_flat(data, length); + rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om, + NimBLERemoteCharacteristic::onWriteCB, + this); + } else { +*/ + rc = ble_gattc_write_flat(pClient->getConnId(), m_handle, + data, length, + NimBLERemoteCharacteristic::onWriteCB, + this); +// } + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc); + m_semaphoreWriteCharEvt.give(); + return false; + } + + rc = m_semaphoreWriteCharEvt.wait("writeValue"); + + switch(rc){ + case 0: + break; + + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + if (retryCount && pClient->secureConnection()) + break; + /* Else falls through. */ + default: + return false; + } + } while(rc != 0 && retryCount--); + + NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d",rc); + return (rc == 0); +} // writeValue + + +/** + * @brief Callback for characteristic write operation. + * @return 0 or error code. + */ +int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + NimBLERemoteCharacteristic* characteristic = (NimBLERemoteCharacteristic*)arg; + + // Make sure the discovery is for this device + if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){ + return 0; + } + + NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); + + if (error->status == 0) { + + characteristic->m_semaphoreWriteCharEvt.give(0); + } else { + + characteristic->m_semaphoreWriteCharEvt.give(error->status); + } + + return 0; +} + + +/** + * @brief Read raw data from remote characteristic as hex bytes + * @return return pointer data read + */ +uint8_t* NimBLERemoteCharacteristic::readRawData() { + return m_rawData; +} + + +void NimBLERemoteCharacteristic::releaseSemaphores() { + for (auto &dPair : m_descriptorMap) { + dPair.second->releaseSemaphores(); + } + m_semaphoreWriteCharEvt.give(1); + m_semaphoreGetDescEvt.give(1); + m_semaphoreReadCharEvt.give(1); +} +#endif /* CONFIG_BT_ENABLED */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h b/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h new file mode 100644 index 000000000..c107be79d --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h @@ -0,0 +1,100 @@ +/* + * NimBLERemoteCharacteristic.h + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteCharacteristic.h + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ +#define COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +//#include "NimBLEUUID.h" +//#include "FreeRTOS.h" +#include "NimBLERemoteService.h" +#include "NimBLERemoteDescriptor.h" + +//#include +#include + +class NimBLERemoteService; +class NimBLERemoteDescriptor; + + +typedef void (*notify_callback)(NimBLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); + +/** + * @brief A model of a remote %BLE characteristic. + */ +class NimBLERemoteCharacteristic { +public: + ~NimBLERemoteCharacteristic(); + + // Public member functions + bool canBroadcast(); + bool canIndicate(); + bool canNotify(); + bool canRead(); + bool canWrite(); + bool canWriteNoResponse(); + NimBLERemoteDescriptor* getDescriptor(NimBLEUUID uuid); + std::map* getDescriptors(); + uint16_t getHandle(); + uint16_t getDefHandle(); + NimBLEUUID getUUID(); + std::string readValue(); + uint8_t readUInt8(); + uint16_t readUInt16(); + uint32_t readUInt32(); + bool registerForNotify(notify_callback _callback, bool notifications = true, bool response = true); + bool writeValue(uint8_t* data, size_t length, bool response = false); + bool writeValue(std::string newValue, bool response = false); + bool writeValue(uint8_t newValue, bool response = false); + std::string toString(); + uint8_t* readRawData(); + NimBLERemoteService* getRemoteService(); + +private: + + NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteservice, const struct ble_gatt_chr *chr); + + friend class NimBLEClient; + friend class NimBLERemoteService; + friend class NimBLERemoteDescriptor; + + // Private member functions + void removeDescriptors(); + bool retrieveDescriptors(uint16_t endHdl); + static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg); + static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg); + void releaseSemaphores(); + static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, + void *arg); + + // Private properties + NimBLEUUID m_uuid; + uint8_t m_charProp; + uint16_t m_handle; + uint16_t m_defHandle; + NimBLERemoteService* m_pRemoteService; + FreeRTOS::Semaphore m_semaphoreGetDescEvt = FreeRTOS::Semaphore("GetDescEvt"); + FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); + FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); + std::string m_value; + uint8_t* m_rawData = nullptr; + notify_callback m_notifyCallback; + + // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. + std::map m_descriptorMap; +}; // BLERemoteCharacteristic +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp b/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp new file mode 100644 index 000000000..d862a8cd0 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp @@ -0,0 +1,312 @@ +/* + * NimBLERemoteDescriptor.cpp + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteDescriptor.cpp + * + * Created on: Jul 8, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLERemoteDescriptor.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLERemoteDescriptor"; + +/** + * @brief Remote descriptor constructor. + * @param [in] Reference to the Characteristic that this belongs to. + * @param [in] Reference to the struct that contains the descriptor information. + */ +NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic, + const struct ble_gatt_dsc *dsc) +{ + switch (dsc->uuid.u.type) { + case BLE_UUID_TYPE_16: + m_uuid = NimBLEUUID(dsc->uuid.u16.value); + break; + case BLE_UUID_TYPE_32: + m_uuid = NimBLEUUID(dsc->uuid.u32.value); + break; + case BLE_UUID_TYPE_128: + m_uuid = NimBLEUUID(const_cast(&dsc->uuid.u128)); + break; + default: + m_uuid = nullptr; + break; + } + m_handle = dsc->handle; + m_pRemoteCharacteristic = pRemoteCharacteristic; + +} + + +/** + * @brief Retrieve the handle associated with this remote descriptor. + * @return The handle associated with this remote descriptor. + */ +uint16_t NimBLERemoteDescriptor::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Get the characteristic that owns this descriptor. + * @return The characteristic that owns this descriptor. + */ +NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() { + return m_pRemoteCharacteristic; +} // getRemoteCharacteristic + + +/** + * @brief Retrieve the UUID associated this remote descriptor. + * @return The UUID associated this remote descriptor. + */ +NimBLEUUID NimBLERemoteDescriptor::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Callback for Descriptor read operation. + * @return 0 or error code. + */ +int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + NimBLERemoteDescriptor* desc = (NimBLERemoteDescriptor*)arg; + + // Make sure the discovery is for this device + if(desc->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle){ + return 0; + } + + NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); + + if (error->status == 0) { + desc->m_value = std::string((char*) attr->om->om_data, attr->om->om_len); + desc->m_semaphoreReadDescrEvt.give(0); + } else { + desc->m_value = ""; + desc->m_semaphoreReadDescrEvt.give(error->status); + } + + return 0; +} + + +std::string NimBLERemoteDescriptor::readValue() { + NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str()); + + NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient(); + + int rc = 0; + int retryCount = 1; + + // Check to see that we are connected. + if (!pClient->isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Disconnected"); + return ""; + } + + do { + m_semaphoreReadDescrEvt.take("ReadDescriptor"); + + rc = ble_gattc_read(pClient->getConnId(), m_handle, + NimBLERemoteDescriptor::onReadCB, this); + + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Descriptor read failed, code: %d", rc); + m_semaphoreReadDescrEvt.give(); + return ""; + } + + rc = m_semaphoreReadDescrEvt.wait("ReadDescriptor"); + + switch(rc){ + case 0: + break; + + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + if (retryCount && pClient->secureConnection()) + break; + /* Else falls through. */ + default: + return ""; + } + } while(rc != 0 && retryCount--); + + NIMBLE_LOGD(LOG_TAG, "<< Descriptor readValue(): length: %d, rc: %d", m_value.length(), rc); + + return (rc == 0) ? m_value : ""; +} // readValue + + +uint8_t NimBLERemoteDescriptor::readUInt8() { + std::string value = readValue(); + if (value.length() >= 1) { + return (uint8_t) value[0]; + } + return 0; +} // readUInt8 + + +uint16_t NimBLERemoteDescriptor::readUInt16() { + std::string value = readValue(); + if (value.length() >= 2) { + return *(uint16_t*) value.data(); + } + return 0; +} // readUInt16 + + +uint32_t NimBLERemoteDescriptor::readUInt32() { + std::string value = readValue(); + if (value.length() >= 4) { + return *(uint32_t*) value.data(); + } + return 0; +} // readUInt32 + + +/** + * @brief Return a string representation of this BLE Remote Descriptor. + * @retun A string representation of this BLE Remote Descriptor. + */ +std::string NimBLERemoteDescriptor::toString() { + std::string res = "Descriptor: uuid: " + getUUID().toString(); + char val[6]; + res += ", handle: "; + snprintf(val, sizeof(val), "%d", getHandle()); + res += val; + + return res; +} // toString + + +/** + * @brief Callback for descriptor write operation. + * @return 0 or error code. + */ +int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + NimBLERemoteDescriptor* descriptor = (NimBLERemoteDescriptor*)arg; + + // Make sure the discovery is for this device + if(descriptor->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle){ + return 0; + } + + NIMBLE_LOGD(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); + + if (error->status == 0) { + descriptor->m_semaphoreDescWrite.give(0); + } else { + descriptor->m_semaphoreDescWrite.give(error->status); + } + + return 0; +} + + +/** + * @brief Write data to the BLE Remote Descriptor. + * @param [in] data The data to send to the remote descriptor. + * @param [in] length The length of the data to send. + * @param [in] response True if we expect a response. + */ +bool NimBLERemoteDescriptor::writeValue(uint8_t* data, size_t length, bool response) { + + NIMBLE_LOGD(LOG_TAG, ">> Descriptor writeValue: %s", toString().c_str()); + + NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient(); + + int rc = 0; + int retryCount = 1; + + // Check to see that we are connected. + if (!pClient->isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Disconnected"); + return false; + } + + if(!response) { + rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length); + return (rc==0); + } + + do { + m_semaphoreDescWrite.take("WriteDescriptor"); + + rc = ble_gattc_write_flat(pClient->getConnId(), m_handle, + data, length, + NimBLERemoteDescriptor::onWriteCB, + this); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to write descriptor; rc=%d", rc); + m_semaphoreDescWrite.give(); + return false; + } + + rc = m_semaphoreDescWrite.wait("WriteDescriptor"); + + switch(rc){ + case 0: + break; + + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + if (retryCount && pClient->secureConnection()) + break; + /* Else falls through. */ + default: + return false; + } + } while(rc != 0 && retryCount--); + + NIMBLE_LOGD(LOG_TAG, "<< Descriptor writeValue, rc: %d",rc); + return (rc == 0); //true; +} // writeValue + + +/** + * @brief Write data represented as a string to the BLE Remote Descriptor. + * @param [in] newValue The data to send to the remote descriptor. + * @param [in] response True if we expect a response. + */ +bool NimBLERemoteDescriptor::writeValue(std::string newValue, bool response) { + return writeValue((uint8_t*) newValue.data(), newValue.length(), response); +} // writeValue + + +/** + * @brief Write a byte value to the Descriptor. + * @param [in] The single byte to write. + * @param [in] True if we expect a response. + */ +bool NimBLERemoteDescriptor::writeValue(uint8_t newValue, bool response) { + return writeValue(&newValue, 1, response); +} // writeValue + + +/** + * @brief In the event of an error this is called to make sure we don't block. + */ +void NimBLERemoteDescriptor::releaseSemaphores() { + m_semaphoreDescWrite.give(1); + m_semaphoreReadDescrEvt.give(1); +} +#endif /* CONFIG_BT_ENABLED */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h b/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h new file mode 100644 index 000000000..004d89705 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h @@ -0,0 +1,58 @@ +/* + * NimBLERemoteDescriptor.h + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteDescriptor.h + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ +#define COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLERemoteCharacteristic.h" + +class NimBLERemoteCharacteristic; +/** + * @brief A model of remote %BLE descriptor. + */ +class NimBLERemoteDescriptor { +public: + uint16_t getHandle(); + NimBLERemoteCharacteristic* getRemoteCharacteristic(); + NimBLEUUID getUUID(); + std::string readValue(void); + uint8_t readUInt8(void); + uint16_t readUInt16(void); + uint32_t readUInt32(void); + std::string toString(void); + bool writeValue(uint8_t* data, size_t length, bool response = false); + bool writeValue(std::string newValue, bool response = false); + bool writeValue(uint8_t newValue, bool response = false); + + +private: + friend class NimBLERemoteCharacteristic; + NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic, const struct ble_gatt_dsc *dsc); + static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg); + static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg); + void releaseSemaphores(); + + uint16_t m_handle; // Server handle of this descriptor. + NimBLEUUID m_uuid; // UUID of this descriptor. + std::string m_value; // Last received value of the descriptor. + NimBLERemoteCharacteristic* m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated. + FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt"); + FreeRTOS::Semaphore m_semaphoreDescWrite = FreeRTOS::Semaphore("WriteDescEvt"); + + +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp b/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp new file mode 100644 index 000000000..28c2cc33e --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp @@ -0,0 +1,357 @@ +/* + * NimBLERemoteService.cpp + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteService.cpp + * + * Created on: Jul 8, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLERemoteService.h" +#include "NimBLEUtils.h" +#include "NimBLEDevice.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLERemoteService"; + +/** + * @brief Remote Service constructor. + * @param [in] Reference to the client this belongs to. + * @param [in] Refernce to the structure with the services' information. + */ +NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) { + + NIMBLE_LOGD(LOG_TAG, ">> BLERemoteService()"); + m_pClient = pClient; + switch (service->uuid.u.type) { + case BLE_UUID_TYPE_16: + m_uuid = NimBLEUUID(service->uuid.u16.value); + break; + case BLE_UUID_TYPE_32: + m_uuid = NimBLEUUID(service->uuid.u32.value); + break; + case BLE_UUID_TYPE_128: + m_uuid = NimBLEUUID(const_cast(&service->uuid.u128)); + break; + default: + m_uuid = nullptr; + break; + } + m_startHandle = service->start_handle; + m_endHandle = service->end_handle; + m_haveCharacteristics = false; + + NIMBLE_LOGD(LOG_TAG, "<< BLERemoteService()"); +} + + +/** + * @brief When deleting the service make sure we delete all characteristics and descriptors. + * Also release any semaphores they may be holding. + */ +NimBLERemoteService::~NimBLERemoteService() { + removeCharacteristics(); +} + + +/** + * @brief Get the remote characteristic object for the characteristic UUID. + * @param [in] uuid Remote characteristic uuid. + * @return Reference to the remote characteristic object. + */ +NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) { + return getCharacteristic(NimBLEUUID(uuid)); +} // getCharacteristic + + +/** + * @brief Get the characteristic object for the UUID. + * @param [in] uuid Characteristic uuid. + * @return Reference to the characteristic object, or nullptr if not found. + */ +NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(NimBLEUUID uuid) { + if (m_haveCharacteristics) { + std::string v = uuid.toString(); + for (auto &myPair : m_characteristicMap) { + if (myPair.first == v) { + return myPair.second; + } + } + } + + return nullptr; +} // getCharacteristic + + +/** + * @brief Callback for Characterisic discovery. + */ +int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d", error->status, conn_handle); + + NimBLERemoteService *service = (NimBLERemoteService*)arg; + int rc=0; + + // Make sure the discovery is for this device + if(service->getClient()->getConnId() != conn_handle){ + return 0; + } + + switch (error->status) { + case 0: { + // Found a service - add it to the map + NimBLERemoteCharacteristic* pRemoteCharacteristic = new NimBLERemoteCharacteristic(service, chr); + service->m_characteristicMap.insert(std::pair(pRemoteCharacteristic->getUUID().toString(), pRemoteCharacteristic)); + service->m_characteristicMapByHandle.insert(std::pair(chr->val_handle, pRemoteCharacteristic)); + break; + } + case BLE_HS_EDONE:{ + /** All characteristics in this service discovered; start discovering + * characteristics in the next service. + */ + service->m_semaphoreGetCharEvt.give(0); + rc = 0; + break; + } + default: + rc = error->status; + break; + } + if (rc != 0) { + /* Error; abort discovery. */ + // pass non-zero to semaphore on error to indicate an error finding characteristics + // release memory from any characteristics we created + //service->removeCharacteristics(); --this will now be done when we clear services on returning with error + NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + service->m_semaphoreGetCharEvt.give(1); + } + NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered. status: %d", rc); + return rc; +} + + +/** + * @brief Retrieve all the characteristics for this service. + * This function will not return until we have all the characteristics. + * @return N/A + */ +bool NimBLERemoteService::retrieveCharacteristics() { + NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str()); + + int rc = 0; + //removeCharacteristics(); // Forget any previous characteristics. + + m_semaphoreGetCharEvt.take("retrieveCharacteristics"); + + rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(), + m_startHandle, + m_endHandle, + NimBLERemoteService::characteristicDiscCB, + this); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + m_haveCharacteristics = false; + m_semaphoreGetCharEvt.give(); + return false; + } + + m_haveCharacteristics = (m_semaphoreGetCharEvt.wait("retrieveCharacteristics") == 0); + if(m_haveCharacteristics){ + uint16_t endHdl = 0xFFFF; + NIMBLE_LOGD(LOG_TAG, "Found %d Characteristics", m_characteristicMapByHandle.size()); + for (auto it = m_characteristicMapByHandle.cbegin(); it != m_characteristicMapByHandle.cend(); ++it) { + NIMBLE_LOGD(LOG_TAG, "Found UUID: %s Handle: %d Def Handle: %d", (*it).second->getUUID().toString().c_str(), (*it).second->getHandle(), (*it).second->getDefHandle()); + // The descriptor handle is between this characteristic val_handle and the next ones def_handle + // so make the end of the scan at the handle before the next characteristic def_handle + + // Make sure we don't go past the service end handle + if(++it != m_characteristicMapByHandle.cend()){ + NIMBLE_LOGD(LOG_TAG, "Next UUID: %s Handle: %d Def Handle: %d", (*it).second->getUUID().toString().c_str(), (*it).second->getHandle(),(*it).second->getDefHandle()); + + endHdl = (*it).second->getDefHandle()-1; + } + else{ + NIMBLE_LOGD(LOG_TAG, "END CHARS"); + endHdl = m_endHandle; + } + --it; + + //If there is no handles between this characteristic and the next there is no descriptor so skip to the next + if((*it).second->getHandle() != endHdl){ + if(!m_pClient->m_isConnected || !(*it).second->retrieveDescriptors(endHdl)) { + return false; + } + } + //NIMBLE_LOGD(LOG_TAG, "Found %d Characteristics in service UUID: %s", chars->size(), myPair.first.c_str()); + } + + NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()"); + return true; + } + + NIMBLE_LOGE(LOG_TAG, "Could not retrieve characteristics"); + return false; + +} // retrieveCharacteristics + + +/** + * @brief Retrieve a map of all the characteristics of this service. + * @return A map of all the characteristics of this service. + */ +std::map* NimBLERemoteService::getCharacteristics() { + return &m_characteristicMap; +} // getCharacteristics + + +/** + * @brief Retrieve a map of all the characteristics of this service. + * @return A map of all the characteristics of this service. + */ +std::map* NimBLERemoteService::getCharacteristicsByHandle() { + return &m_characteristicMapByHandle; +} // getCharacteristicsByHandle + + +/** + * @brief Get the client associated with this service. + * @return A reference to the client associated with this service. + */ +NimBLEClient* NimBLERemoteService::getClient() { + return m_pClient; +} // getClient + + +/** + * @brief Get the service end handle. + */ +uint16_t NimBLERemoteService::getEndHandle() { + return m_endHandle; +} // getEndHandle + + +/** + * @brief Get the service start handle. + */ +uint16_t NimBLERemoteService::getStartHandle() { + return m_startHandle; +} // getStartHandle + + +/** + * @brief Get the service UUID. + */ +NimBLEUUID NimBLERemoteService::getUUID() { + return m_uuid; +} + + +/** + * @brief Read the value of a characteristic associated with this service. + * @param [in] characteristicUuid The characteristic to read. + * @returns a string containing the value or an empty string if not found or error. + */ +std::string NimBLERemoteService::getValue(NimBLEUUID characteristicUuid) { + NIMBLE_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str()); + + std::string ret = ""; + NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid); + + if(pChar != nullptr) { + ret = pChar->readValue(); + } + + NIMBLE_LOGD(LOG_TAG, "<< readValue"); + return ret; +} // readValue + + +/** + * @brief Set the value of a characteristic. + * @param [in] characteristicUuid The characteristic to set. + * @param [in] value The value to set. + * @returns true on success, false if not found or error + */ +bool NimBLERemoteService::setValue(NimBLEUUID characteristicUuid, std::string value) { + NIMBLE_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str()); + + bool ret = false; + NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid); + + if(pChar != nullptr) { + ret = pChar->writeValue(value); + } + + NIMBLE_LOGD(LOG_TAG, "<< setValue"); + return ret; +} // setValue + + +/** + * @brief Delete the characteristics in the characteristics map. + * We maintain a map called m_characteristicsMap that contains pointers to BLERemoteCharacteristic + * object references. Since we allocated these in this class, we are also responsible for deleteing + * them. This method does just that. + * @return N/A. + */ +void NimBLERemoteService::removeCharacteristics() { + m_characteristicMap.clear(); // Clear the map + + for (auto &myPair : m_characteristicMapByHandle) { + delete myPair.second; + } + m_characteristicMapByHandle.clear(); // Clear the map + +} // removeCharacteristics + + +/** + * @brief Create a string representation of this remote service. + * @return A string representation of this remote service. + */ +std::string NimBLERemoteService::toString() { + std::string res = "Service: uuid: " + m_uuid.toString(); + char val[6]; + res += ", start_handle: "; + snprintf(val, sizeof(val), "%d", m_startHandle); + res += val; + snprintf(val, sizeof(val), "%04x", m_startHandle); + res += " 0x"; + res += val; + res += ", end_handle: "; + snprintf(val, sizeof(val), "%d", m_endHandle); + res += val; + snprintf(val, sizeof(val), "%04x", m_endHandle); + res += " 0x"; + res += val; + + for (auto &myPair : m_characteristicMap) { + res += "\n" + myPair.second->toString(); + } + + return res; +} // toString + + +/** + * @brief called when an error occurrs and we need to release the semaphores to resume operations. + * Will release all characteristic and subsequently all descriptor semaphores for this service. + */ +void NimBLERemoteService::releaseSemaphores() { + for (auto &cPair : m_characteristicMapByHandle) { + cPair.second->releaseSemaphores(); + } + m_semaphoreGetCharEvt.give(1); +} + +#endif /* CONFIG_BT_ENABLED */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h b/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h new file mode 100644 index 000000000..bef8f0b52 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h @@ -0,0 +1,89 @@ +/* + * NimBLERemoteService.h + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteService.h + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEREMOTESERVICE_H_ +#define COMPONENTS_NIMBLEREMOTESERVICE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEClient.h" +#include "NimBLEUUID.h" +#include "FreeRTOS.h" +#include "NimBLERemoteCharacteristic.h" + +#include + +class NimBLEClient; +class NimBLERemoteCharacteristic; + + +/** + * @brief A model of a remote %BLE service. + */ +class NimBLERemoteService { +public: + virtual ~NimBLERemoteService(); + + // Public methods + NimBLERemoteCharacteristic* getCharacteristic(const char* uuid); // Get the specified characteristic reference. + NimBLERemoteCharacteristic* getCharacteristic(NimBLEUUID uuid); // Get the specified characteristic reference. +// BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference. + std::map* getCharacteristics(); + std::map* getCharacteristicsByHandle(); // Get the characteristics map. +// void getCharacteristics(std::map* pCharacteristicMap); + + NimBLEClient* getClient(void); // Get a reference to the client associated with this service. + uint16_t getHandle(); // Get the handle of this service. + NimBLEUUID getUUID(void); // Get the UUID of this service. + std::string getValue(NimBLEUUID characteristicUuid); // Get the value of a characteristic. + bool setValue(NimBLEUUID characteristicUuid, std::string value); // Set the value of a characteristic. + std::string toString(void); + +private: + // Private constructor ... never meant to be created by a user application. + NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc *service); + + // Friends + friend class NimBLEClient; + friend class NimBLERemoteCharacteristic; + + // Private methods + bool retrieveCharacteristics(void); // Retrieve the characteristics from the BLE Server. + static int characteristicDiscCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg); + + uint16_t getStartHandle(); // Get the start handle for this service. + uint16_t getEndHandle(); // Get the end handle for this service. + void releaseSemaphores(); + void removeCharacteristics(); + + // Properties + + // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. + std::map m_characteristicMap; + + // We maintain a map of characteristics owned by this service keyed by a handle. + std::map m_characteristicMapByHandle; + + bool m_haveCharacteristics; // Have we previously obtained the characteristics. + NimBLEClient* m_pClient; + FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt"); + NimBLEUUID m_uuid; // The UUID of this service. + uint16_t m_startHandle; // The starting handle of this service. + uint16_t m_endHandle; // The ending handle of this service. +}; // BLERemoteService + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_NIMBLEREMOTESERVICE_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp b/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp new file mode 100644 index 000000000..e5cbed1f0 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp @@ -0,0 +1,403 @@ +/* + * NimBLEScan.cpp + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEScan.cpp + * + * Created on: Jul 1, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEScan.h" +#include "NimBLEUtils.h" +#include "NimBLEDevice.h" +#include "NimBLELog.h" + +#include + +static const char* LOG_TAG = "NimBLEScan"; + +/* + * Scanning filter policy + * NO_WL: + * Scanner processes all advertising packets (white list not used) except + * directed, connectable advertising packets not sent to the scanner. + * USE_WL: + * Scanner processes advertisements from white list only. A connectable, + * directed advertisment is ignored unless it contains scanners address. + * NO_WL_INITA: + * Scanner process all advertising packets (white list not used). A + * connectable, directed advertisement shall not be ignored if the InitA + * is a resolvable private address. + * USE_WL_INITA: + * Scanner process advertisements from white list only. A connectable, + * directed advertisement shall not be ignored if the InitA is a + * resolvable private address. + */ + +//#define BLE_HCI_SCAN_FILT_NO_WL (0) +//#define BLE_HCI_SCAN_FILT_USE_WL (1) +//#define BLE_HCI_SCAN_FILT_NO_WL_INITA (2) +//#define BLE_HCI_SCAN_FILT_USE_WL_INITA (3) +//#define BLE_HCI_SCAN_FILT_MAX (3) + + +/** + * @brief Scan constuctor. + */ +NimBLEScan::NimBLEScan() { + uint8_t own_addr_type; + if(ble_hs_id_infer_auto(0, &own_addr_type) !=0){ + NIMBLE_LOGE(LOG_TAG, "error determining address type\n"); + return; + } + m_own_addr_type = own_addr_type; + m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL; + m_scan_params.passive = 1; // If set, don’t send scan requests to advertisers (i.e., don’t request additional advertising data). + m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec) + m_scan_params.window = 0; // The duration of the LE scan. LE_Scan_Window shall be less than or equal to LE_Scan_Interval (units=0.625 msec) + m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode. + m_scan_params.filter_duplicates = 1; // If set, the controller ignores all but the first advertisement from each device. + m_pAdvertisedDeviceCallbacks = nullptr; + m_stopped = true; + m_wantDuplicates = false; +} + + +/** + * @brief Handle GAP events related to scans. + * @param [in] event The event type for this event. + * @param [in] param Parameter data for this event. + */ +/*STATIC*/int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { + + NimBLEScan* pScan = (NimBLEScan*)arg; + struct ble_hs_adv_fields fields; + int rc = 0; + + switch(event->type) { + + case BLE_GAP_EVENT_DISC: { + if(pScan->m_stopped) { + NIMBLE_LOGE(LOG_TAG, "Scan stop called, ignoring results."); + return 0; + } + + rc = ble_hs_adv_parse_fields(&fields, event->disc.data, + event->disc.length_data); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Gap Event Parse ERROR."); + return 0; + } + + NimBLEAddress advertisedAddress(event->disc.addr); + + // Print advertisement data + // print_adv_fields(&fields); + + // If we are not scanning, nothing to do with the extra results. + if (pScan->m_stopped) { + return 0; + } + + // Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected + if(NimBLEDevice::isIgnored(advertisedAddress)) { + NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s", advertisedAddress.toString().c_str()); + return 0; + } + + NimBLEAdvertisedDevice* advertisedDevice = nullptr; + + // If we've seen this device before get a pointer to it from the map + auto it = pScan->m_scanResults.m_advertisedDevicesMap.find(advertisedAddress.toString()); + if(it != pScan->m_scanResults.m_advertisedDevicesMap.cend()) { + advertisedDevice = (*it).second; + } + + // If we haven't seen this device before; create a new instance and insert it in the map. + // Otherwise just update the relevant parameters of the already known device. + if(advertisedDevice == nullptr){ + advertisedDevice = new NimBLEAdvertisedDevice(); + advertisedDevice->setAddressType(event->disc.addr.type); + advertisedDevice->setAddress(advertisedAddress); + //NIMBLE_LOGE(LOG_TAG, "advertisement type: %d, %s",event->disc.event_type, NimBLEUtils::advTypeToString(event->disc.event_type)); + advertisedDevice->setAdvType(event->disc.event_type); + pScan->m_scanResults.m_advertisedDevicesMap.insert(std::pair(advertisedAddress.toString(), advertisedDevice)); + NIMBLE_LOGI(LOG_TAG, "NEW DEVICE FOUND: %s", advertisedAddress.toString().c_str()); + } + else{ + NIMBLE_LOGI(LOG_TAG, "UPDATING PREVIOUSLY FOUND DEVICE: %s", advertisedAddress.toString().c_str()); + } + advertisedDevice->setRSSI(event->disc.rssi); + advertisedDevice->parseAdvertisement(&fields); + advertisedDevice->setScan(pScan); + advertisedDevice->setAdvertisementResult(event->disc.data, event->disc.length_data); + + if (pScan->m_pAdvertisedDeviceCallbacks) { + // If not active scanning report the result to the listener. + if(pScan->m_scan_params.passive) { + pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + // Otherwise wait for the scan response so we can report all of the data at once. + } else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + } + //m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); + } + + return 0; + } + case BLE_GAP_EVENT_DISC_COMPLETE: { + NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", + event->disc_complete.reason); + + if (pScan->m_scanCompleteCB != nullptr) { + pScan->m_scanCompleteCB(pScan->m_scanResults); + } + + pScan->m_stopped = true; + pScan->m_semaphoreScanEnd.give(); + return 0; + } + + default: + return 0; + } +} // gapEventHandler + + +/** + * @brief Should we perform an active or passive scan? + * The default is a passive scan. An active scan means that we will wish a scan response. + * @param [in] active If true, we perform an active scan otherwise a passive scan. + * @return N/A. + */ +void NimBLEScan::setActiveScan(bool active) { + if (active) { + m_scan_params.passive = 0; + } else { + m_scan_params.passive = 1; + } +} // setActiveScan + + +/** + * @brief Set the call backs to be invoked. + * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. + * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. + */ +void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks/*, bool wantDuplicates*/) { + //m_wantDuplicates = wantDuplicates; + m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; +} // setAdvertisedDeviceCallbacks + + +/** + * @brief Set the interval to scan. + * @param [in] The interval in msecs. + */ +void NimBLEScan::setInterval(uint16_t intervalMSecs) { + m_scan_params.itvl = intervalMSecs / 0.625; +} // setInterval + + +/** + * @brief Set the window to actively scan. + * @param [in] windowMSecs How long to actively scan. + */ +void NimBLEScan::setWindow(uint16_t windowMSecs) { + m_scan_params.window = windowMSecs / 0.625; +} // setWindow + + +/** + * @brief Start scanning. + * @param [in] duration The duration in seconds for which to scan. + * @param [in] scanCompleteCB A function to be called when scanning has completed. + * @param [in] are we continue scan (true) or we want to clear stored devices (false) + * @return True if scan started or false if there was an error. + */ +bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) { + NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration); + + // If Host is not synced we cannot start scanning. + if(!NimBLEDevice::m_synced) { + NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync."); + return false; + } + + if(ble_gap_conn_active()) { + NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait."); + return false; + } + + // If we are already scanning don't start again or we will get stuck on the semaphore. + if(!m_stopped || ble_gap_disc_active()) { // double check - can cause host reset. + NIMBLE_LOGE(LOG_TAG, "Scan already in progress"); + return false; + } + + m_stopped = false; + m_semaphoreScanEnd.take("start"); + + // Save the callback to be invoked when the scan completes. + m_scanCompleteCB = scanCompleteCB; + // Save the duration in the case that the host is reset so we can reuse it. + m_duration = duration; + + // If 0 duration specified then we assume a continuous scan is desired. + if(duration == 0){ + duration = BLE_HS_FOREVER; + } + else{ + duration = duration*1000; // convert duration to milliseconds + } + + // if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals + // then we should not clear map or we will connect the same device few times + if(!is_continue) { + clearResults(); + } + + int rc = 0; + do{ + rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params, + NimBLEScan::handleGapEvent, this); + if(rc == BLE_HS_EBUSY) { + vTaskDelay(2); + } + } while(rc == BLE_HS_EBUSY); + + if (rc != 0 && rc != BLE_HS_EDONE) { + NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + m_stopped = true; + m_semaphoreScanEnd.give(); + return false; + } + + NIMBLE_LOGD(LOG_TAG, "<< start()"); + return true; +} // start + + +/** + * @brief Start scanning and block until scanning has been completed. + * @param [in] duration The duration in seconds for which to scan. + * @return The BLEScanResults. + */ +NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) { + if(start(duration, nullptr, is_continue)) { + m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. + } + return m_scanResults; +} // start + + +/** + * @brief Stop an in progress scan. + * @return N/A. + */ +void NimBLEScan::stop() { + NIMBLE_LOGD(LOG_TAG, ">> stop()"); + + int rc = ble_gap_disc_cancel(); + if (rc != 0 && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d\n", rc); + return; + } + + m_stopped = true; + + if (m_scanCompleteCB != nullptr) { + m_scanCompleteCB(m_scanResults); + } + + m_semaphoreScanEnd.give(); + + NIMBLE_LOGD(LOG_TAG, "<< stop()"); +} // stop + + +// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address +void NimBLEScan::erase(NimBLEAddress address) { + NIMBLE_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str()); + NimBLEAdvertisedDevice *advertisedDevice = m_scanResults.m_advertisedDevicesMap.find(address.toString())->second; + m_scanResults.m_advertisedDevicesMap.erase(address.toString()); + delete advertisedDevice; +} + + +/** + * @brief If the host reset the scan will have stopped so we should flag it and release the semaphore. + * @return N/A. + */ +void NimBLEScan::onHostReset() { + m_stopped = true; + m_semaphoreScanEnd.give(); +} + + +/** + * @brief Get the results of the scan. + * @return NimBLEScanResults object. + */ +NimBLEScanResults NimBLEScan::getResults() { + return m_scanResults; +} + + +/** + * @brief Clear the results of the scan. + */ +void NimBLEScan::clearResults() { + for(auto _dev : m_scanResults.m_advertisedDevicesMap){ + delete _dev.second; + } + m_scanResults.m_advertisedDevicesMap.clear(); +} + + +/** + * @brief Dump the scan results to the log. + */ +void NimBLEScanResults::dump() { + NIMBLE_LOGD(LOG_TAG, ">> Dump scan results:"); + for (int i=0; isecond; + for (auto it = m_advertisedDevicesMap.begin(); it != m_advertisedDevicesMap.end(); it++) { + dev = *it->second; + if (x==i) break; + x++; + } + return dev; +} + +#endif /* CONFIG_BT_ENABLED */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLEScan.h b/libesp32/NimBLE-Arduino/src/NimBLEScan.h new file mode 100644 index 000000000..a19a3da0e --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEScan.h @@ -0,0 +1,87 @@ +/* + * NimBLEScan.h + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEScan.h + * + * Created on: Jul 1, 2017 + * Author: kolban + */ +#ifndef COMPONENTS_NIMBLE_SCAN_H_ +#define COMPONENTS_NIMBLE_SCAN_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEAdvertisedDevice.h" +#include "FreeRTOS.h" + +#include "host/ble_gap.h" + +#include + +class NimBLEDevice; +class NimBLEScan; +class NimBLEAdvertisedDevice; +class NimBLEAdvertisedDeviceCallbacks; + +/** + * @brief The result of having performed a scan. + * When a scan completes, we have a set of found devices. Each device is described + * by a BLEAdvertisedDevice object. The number of items in the set is given by + * getCount(). We can retrieve a device by calling getDevice() passing in the + * index (starting at 0) of the desired device. + */ +class NimBLEScanResults { +public: + void dump(); + int getCount(); + NimBLEAdvertisedDevice getDevice(uint32_t i); + +private: + friend NimBLEScan; + std::map m_advertisedDevicesMap; +}; + +/** + * @brief Perform and manage %BLE scans. + * + * Scanning is associated with a %BLE client that is attempting to locate BLE servers. + */ +class NimBLEScan { +public: + bool start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue = false); + NimBLEScanResults start(uint32_t duration, bool is_continue = false); + void setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks/*, bool wantDuplicates = false*/); + void setActiveScan(bool active); + void setInterval(uint16_t intervalMSecs); + void setWindow(uint16_t windowMSecs); + void stop(); + void clearResults(); + NimBLEScanResults getResults(); + void erase(NimBLEAddress address); + + +private: + NimBLEScan(); + friend class NimBLEDevice; + static int handleGapEvent(ble_gap_event* event, void* arg); + void onHostReset(); + + NimBLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr; + void (*m_scanCompleteCB)(NimBLEScanResults scanResults); + ble_gap_disc_params m_scan_params; + uint8_t m_own_addr_type; + bool m_stopped; + bool m_wantDuplicates; + NimBLEScanResults m_scanResults; + FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); + uint32_t m_duration; +}; + + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_NIMBLE_SCAN_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp b/libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp new file mode 100644 index 000000000..0651858d0 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp @@ -0,0 +1,138 @@ +/* + * NimBLESecurity.cpp + * + * Created: on Feb 22 2020 + * Author H2zero + * + * Originally: + * + * BLESecurity.cpp + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLESecurity.h" +#include "NimBLEDevice.h" + +/** + * @brief This class is for backward compatibility with the bluedroid based library. + * Use the new security functions in NimBLEDevice instead. + * New callback functions in NimBLEServer and NimBLEClient. + */ + +NimBLESecurity::NimBLESecurity() { +} + +NimBLESecurity::~NimBLESecurity() { +} + + +/** + * @brief Set requested authentication mode + */ +void NimBLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) { + NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0, + (auth_req & BLE_SM_PAIR_AUTHREQ_MITM)>0, + (auth_req & BLE_SM_PAIR_AUTHREQ_SC)>0); +} + + +/** + * @brief Set our device IO capability to let end user perform authorization + * either by displaying or entering generated 6-digits pin code + */ +void NimBLESecurity::setCapability(esp_ble_io_cap_t iocap) { + NimBLEDevice::setSecurityIOCap(iocap); +} // setCapability + + +/** + * @brief Init encryption key by server + * @param key_size is value between 7 and 16 + */ +void NimBLESecurity::setInitEncryptionKey(uint8_t init_key) { + NimBLEDevice::setSecurityInitKey(init_key); +} // setInitEncryptionKey + + +/** + * @brief Init encryption key by client + * @param key_size is value between 7 and 16 + */ +void NimBLESecurity::setRespEncryptionKey(uint8_t resp_key) { + NimBLEDevice::setSecurityRespKey(resp_key); +} // setRespEncryptionKey + + +/** + *@todo Requires implementation + * + */ +void NimBLESecurity::setKeySize(uint8_t key_size) { + + //m_keySize = key_size; + //esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); +} //setKeySize + + +/** + * Setup for static PIN connection. + */ +void NimBLESecurity::setStaticPIN(uint32_t pin){ + //uint32_t passkey = pin; + //esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t)); + NimBLEDevice::setSecurityPasskey(pin); + setCapability(ESP_IO_CAP_OUT); + setKeySize(); + setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); +} + + +/** + * @brief Debug function to display what keys are exchanged by peers + */ + /* +char* BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { + char* key_str = nullptr; + switch (key_type) { + case ESP_LE_KEY_NONE: + key_str = (char*) "ESP_LE_KEY_NONE"; + break; + case ESP_LE_KEY_PENC: + key_str = (char*) "ESP_LE_KEY_PENC"; + break; + case ESP_LE_KEY_PID: + key_str = (char*) "ESP_LE_KEY_PID"; + break; + case ESP_LE_KEY_PCSRK: + key_str = (char*) "ESP_LE_KEY_PCSRK"; + break; + case ESP_LE_KEY_PLK: + key_str = (char*) "ESP_LE_KEY_PLK"; + break; + case ESP_LE_KEY_LLK: + key_str = (char*) "ESP_LE_KEY_LLK"; + break; + case ESP_LE_KEY_LENC: + key_str = (char*) "ESP_LE_KEY_LENC"; + break; + case ESP_LE_KEY_LID: + key_str = (char*) "ESP_LE_KEY_LID"; + break; + case ESP_LE_KEY_LCSRK: + key_str = (char*) "ESP_LE_KEY_LCSRK"; + break; + default: + key_str = (char*) "INVALID BLE KEY TYPE"; + break; + } + return key_str; + +} // esp_key_type_to_str +*/ +#endif // CONFIG_BT_ENABLED diff --git a/libesp32/NimBLE-Arduino/src/NimBLESecurity.h b/libesp32/NimBLE-Arduino/src/NimBLESecurity.h new file mode 100644 index 000000000..60e4f443d --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLESecurity.h @@ -0,0 +1,117 @@ +/* + * NimBLESecurity.h + * + * Created: on Feb 22 2020 + * Author H2zero + * + * Originally: + * + * BLESecurity.h + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +/** This class exists for backward compatibility - Should not be used in new code + * See the security functions in NimBLEDevice and callbacks in NimBLEServer / NimBLEClient + */ + +#ifndef COMPONENTS_NIMBLESECURITY_H_ +#define COMPONENTS_NIMBLESECURITY_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "host/ble_gap.h" +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include + +#define ESP_LE_AUTH_NO_BOND 0x00 /*!< 0*/ /* relate to BTM_LE_AUTH_NO_BOND in stack/btm_api.h */ +#define ESP_LE_AUTH_BOND 0x01 /*!< 1 << 0 */ /* relate to BTM_LE_AUTH_BOND in stack/btm_api.h */ +#define ESP_LE_AUTH_REQ_MITM (1 << 2) /*!< 1 << 2 */ /* relate to BTM_LE_AUTH_REQ_MITM in stack/btm_api.h */ +#define ESP_LE_AUTH_REQ_BOND_MITM (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_MITM)/*!< 0101*/ +#define ESP_LE_AUTH_REQ_SC_ONLY (1 << 3) /*!< 1 << 3 */ /* relate to BTM_LE_AUTH_REQ_SC_ONLY in stack/btm_api.h */ +#define ESP_LE_AUTH_REQ_SC_BOND (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_SC_ONLY) /*!< 1001 */ /* relate to BTM_LE_AUTH_REQ_SC_BOND in stack/btm_api.h */ +#define ESP_LE_AUTH_REQ_SC_MITM (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY) /*!< 1100 */ /* relate to BTM_LE_AUTH_REQ_SC_MITM in stack/btm_api.h */ +#define ESP_LE_AUTH_REQ_SC_MITM_BOND (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY | ESP_LE_AUTH_BOND) /*!< 1101 */ /* relate to BTM_LE_AUTH_REQ_SC_MITM_BOND in stack/btm_api.h */ + +#define ESP_IO_CAP_OUT 0 /*!< DisplayOnly */ /* relate to BTM_IO_CAP_OUT in stack/btm_api.h */ +#define ESP_IO_CAP_IO 1 /*!< DisplayYesNo */ /* relate to BTM_IO_CAP_IO in stack/btm_api.h */ +#define ESP_IO_CAP_IN 2 /*!< KeyboardOnly */ /* relate to BTM_IO_CAP_IN in stack/btm_api.h */ +#define ESP_IO_CAP_NONE 3 /*!< NoInputNoOutput */ /* relate to BTM_IO_CAP_NONE in stack/btm_api.h */ +#define ESP_IO_CAP_KBDISP 4 /*!< Keyboard display */ /* relate to BTM_IO_CAP_KBDISP in stack/btm_api.h */ + +/// Used to exchange the encryption key in the init key & response key +#define ESP_BLE_ENC_KEY_MASK (1 << 0) /* relate to BTM_BLE_ENC_KEY_MASK in stack/btm_api.h */ +/// Used to exchange the IRK key in the init key & response key +#define ESP_BLE_ID_KEY_MASK (1 << 1) /* relate to BTM_BLE_ID_KEY_MASK in stack/btm_api.h */ +/// Used to exchange the CSRK key in the init key & response key +#define ESP_BLE_CSR_KEY_MASK (1 << 2) /* relate to BTM_BLE_CSR_KEY_MASK in stack/btm_api.h */ +/// Used to exchange the link key(this key just used in the BLE & BR/EDR coexist mode) in the init key & response key +#define ESP_BLE_LINK_KEY_MASK (1 << 3) /* relate to BTM_BLE_LINK_KEY_MASK in stack/btm_api.h */ + +typedef uint8_t esp_ble_auth_req_t; /*!< combination of the above bit pattern */ +typedef uint8_t esp_ble_io_cap_t; /*!< combination of the io capability */ + +class NimBLESecurity { +public: + NimBLESecurity(); + virtual ~NimBLESecurity(); + void setAuthenticationMode(esp_ble_auth_req_t auth_req); + void setCapability(esp_ble_io_cap_t iocap); + void setInitEncryptionKey(uint8_t init_key); + void setRespEncryptionKey(uint8_t resp_key); + void setKeySize(uint8_t key_size = 16); + void setStaticPIN(uint32_t pin); + //static char* esp_key_type_to_str(esp_ble_key_type_t key_type); +/* +private: + esp_ble_auth_req_t m_authReq; + esp_ble_io_cap_t m_iocap; + uint8_t m_initKey; + uint8_t m_respKey; + uint8_t m_keySize; +*/ +}; // BLESecurity + + +/* + * @brief Callbacks to handle GAP events related to authorization + */ +class NimBLESecurityCallbacks { +public: + virtual ~NimBLESecurityCallbacks() {}; + + /** + * @brief Its request from peer device to input authentication pin code displayed on peer device. + * It requires that our device is capable to input 6-digits code by end user + * @return Return 6-digits integer value from input device + */ + virtual uint32_t onPassKeyRequest() = 0; + + /** + * @brief Provide us 6-digits code to perform authentication. + * It requires that our device is capable to display this code to end user + * @param + */ + virtual void onPassKeyNotify(uint32_t pass_key) = 0; + + /** + * @brief Here we can make decision if we want to let negotiate authorization with peer device or not + * return Return true if we accept this peer device request + */ + + virtual bool onSecurityRequest() = 0 ; + /** + * Provide us information when authentication process is completed + */ + virtual void onAuthenticationComplete(ble_gap_conn_desc*) = 0; + + virtual bool onConfirmPIN(uint32_t pin) = 0; +}; // BLESecurityCallbacks + +#endif // CONFIG_BT_ENABLED +#endif // COMPONENTS_NIMBLESECURITY_H_ diff --git a/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp b/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp new file mode 100644 index 000000000..5e94b563d --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp @@ -0,0 +1,608 @@ +/* + * NimBLEServer.cpp + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEServer.cpp + * + * Created on: Apr 16, 2017 + * Author: kolban + */ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEServer.h" +#include "NimBLE2902.h" +#include "NimBLEUtils.h" +#include "NimBLEDevice.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEServer"; +static NimBLEServerCallbacks defaultCallbacks; + + +/** + * @brief Construct a %BLE Server + * + * This class is not designed to be individually instantiated. Instead one should create a server by asking + * the BLEDevice class. + */ +NimBLEServer::NimBLEServer() { + m_connId = BLE_HS_CONN_HANDLE_NONE; + m_svcChgChrHdl = 0xffff; + m_pServerCallbacks = &defaultCallbacks; + m_gattsStarted = false; +} // BLEServer + + +/** + * @brief Create a %BLE Service. + * + * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition + * of a new service. Every service must have a unique UUID. + * @param [in] uuid The UUID of the new service. + * @return A reference to the new service object. + */ +NimBLEService* NimBLEServer::createService(const char* uuid) { + return createService(NimBLEUUID(uuid)); +} + + +/** + * @brief Create a %BLE Service. + * + * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition + * of a new service. Every service must have a unique UUID. + * @param [in] uuid The UUID of the new service. + * @param [in] numHandles The maximum number of handles associated with this service. + * @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service. + * @return A reference to the new service object. + */ +NimBLEService* NimBLEServer::createService(NimBLEUUID uuid, uint32_t numHandles, uint8_t inst_id) { + NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); + + // Check that a service with the supplied UUID does not already exist. + if (m_serviceMap.getByUUID(uuid) != nullptr) { + NIMBLE_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", + uuid.toString().c_str()); + } + + NimBLEService* pService = new NimBLEService(uuid, numHandles, this); + pService->m_instId = inst_id; + m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. + + NIMBLE_LOGD(LOG_TAG, "<< createService"); + return pService; +} // createService + + +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the new service. + * @return A reference to the service object. + */ +NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) { + return m_serviceMap.getByUUID(uuid); +} + + +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the new service. + * @return A reference to the service object. + */ +NimBLEService* NimBLEServer::getServiceByUUID(NimBLEUUID uuid) { + return m_serviceMap.getByUUID(uuid); +} + + +/** + * @brief Retrieve the advertising object that can be used to advertise the existence of the server. + * + * @return An advertising object. + */ +NimBLEAdvertising* NimBLEServer::getAdvertising() { + return BLEDevice::getAdvertising(); +} + + +/** + * @brief Retrieve the connection id of the last connected client. + * @todo Not very useful, should refactor or remove. + * @return Client connection id. + */ +uint16_t NimBLEServer::getConnId() { + return m_connId; +} + + +/** + * @brief Start the GATT server. Required to be called after setup of all + * services and characteristics / descriptors for the NimBLE host to register them. + */ +void NimBLEServer::start() { + if(m_gattsStarted) { + NIMBLE_LOGW(LOG_TAG, "Gatt server already started"); + return; + } + + int rc = ble_gatts_start(); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, + NimBLEUtils::returnCodeToString(rc)); + abort(); + } + +#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4) + ble_gatts_show_local(); +#endif + + ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801}; + ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05}; + + rc = ble_gatts_find_chr(&svc.u, &chr.u, NULL, &m_svcChgChrHdl); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_find_chr: rc=%d, %s", rc, + NimBLEUtils::returnCodeToString(rc)); + abort(); + } + + NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl); + + // Build a map of characteristics with Notify / Indicate capabilities for event handling + uint8_t numSvcs = m_serviceMap.getRegisteredServiceCount(); + NimBLEService* pService = m_serviceMap.getFirst(); + + for(int i = 0; i < numSvcs; i++) { + uint8_t numChrs = pService->m_characteristicMap.getSize(); + NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst(); + + if(pChr != nullptr) { + for( int d = 0; d < numChrs; d++) { + // if Notify / Indicate is enabled but we didn't create the descriptor + // we do it now. + if((pChr->m_properties & BLE_GATT_CHR_F_INDICATE) || + (pChr->m_properties & BLE_GATT_CHR_F_NOTIFY)) { + + if(nullptr == pChr->getDescriptorByUUID("2902")) { + pChr->createDescriptor("2902"); + } + m_notifyChrMap.insert(std::pair + (pChr->getHandle(), pChr)); + } + pChr = pService->m_characteristicMap.getNext(); + } + } + pService = m_serviceMap.getNext(); + } + + m_gattsStarted = true; +} + + +/** + * @brief Disconnect the specified client with optional reason. + * @param [in] Connection Id of the client to disconnect. + * @param [in] Reason code for disconnecting. + * @return NimBLE host return code. + */ +int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) { + NIMBLE_LOGD(LOG_TAG, ">> disconnect()"); + + int rc = ble_gap_terminate(connId, reason); + if(rc != 0){ + NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, + NimBLEUtils::returnCodeToString(rc)); + } + + return rc; + NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); +} + + +/** + * @brief Return the number of connected clients. + * @return The number of connected clients. + */ +uint32_t NimBLEServer::getConnectedCount() { + return m_connectedServersMap.size(); +} // getConnectedCount + + +/** + * @brief Handle a GATT Server Event. + * + * @param [in] event + * @param [in] gatts_if + * @param [in] param + * + */ +/*STATIC*/int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { + NimBLEServer* server = (NimBLEServer*)arg; + NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", + NimBLEUtils::gapEventToString(event->type)); + int rc = 0; + struct ble_gap_conn_desc desc; + + switch(event->type) { + + case BLE_GAP_EVENT_CONNECT: { + if (event->connect.status != 0) { + /* Connection failed; resume advertising */ + NIMBLE_LOGC(LOG_TAG, "Connection failed"); + NimBLEDevice::startAdvertising(); + server->m_connId = BLE_HS_CONN_HANDLE_NONE; + } + else { + server->m_connId = event->connect.conn_handle; + server->addPeerDevice((void*)server, false, server->m_connId); + + ble_gap_conn_desc desc; + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + + server->m_pServerCallbacks->onConnect(server); + server->m_pServerCallbacks->onConnect(server, &desc); + } + + return 0; + } // BLE_GAP_EVENT_CONNECT + + + case BLE_GAP_EVENT_DISCONNECT: { + // If Host reset tell the device now before returning to prevent + // any errors caused by calling host functions before resyncing. + switch(event->disconnect.reason) { + case BLE_HS_ETIMEOUT_HCI: + case BLE_HS_EOS: + case BLE_HS_ECONTROLLER: + case BLE_HS_ENOTSYNCED: + NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason); + NimBLEDevice::onReset(event->disconnect.reason); + break; + default: + break; + } + + server->removePeerDevice(event->disconnect.conn.conn_handle, false); + server->m_connId = BLE_HS_CONN_HANDLE_NONE; + server->m_pServerCallbacks->onDisconnect(server); + + return 0; + } // BLE_GAP_EVENT_DISCONNECT + + case BLE_GAP_EVENT_SUBSCRIBE: { + NIMBLE_LOGI(LOG_TAG, "subscribe event; cur_notify=%d\n value handle; " + "val_handle=%d\n", + event->subscribe.cur_notify, event->subscribe.attr_handle); + + auto it = server->m_notifyChrMap.find(event->subscribe.attr_handle); + if(it != server->m_notifyChrMap.cend()) { + (*it).second->setSubscribe(event); + } + + return 0; + } // BLE_GAP_EVENT_SUBSCRIBE + + case BLE_GAP_EVENT_MTU: { + NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", + event->mtu.conn_handle, + event->mtu.value); + server->updatePeerMTU(event->mtu.conn_handle, event->mtu.value); + return 0; + } // BLE_GAP_EVENT_MTU + + case BLE_GAP_EVENT_NOTIFY_TX: { + if(event->notify_tx.indication && event->notify_tx.status != 0) { + auto it = server->m_notifyChrMap.find(event->notify_tx.attr_handle); + if(it != server->m_notifyChrMap.cend()) { + (*it).second->m_semaphoreConfEvt.give(event->notify_tx.status); + } + } + + return 0; + } // BLE_GAP_EVENT_NOTIFY_TX + + case BLE_GAP_EVENT_CONN_UPDATE: { + NIMBLE_LOGD(LOG_TAG, "Connection parameters updated."); + return 0; + } // BLE_GAP_EVENT_CONN_UPDATE + + case BLE_GAP_EVENT_REPEAT_PAIRING: { + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + return BLE_GAP_REPEAT_PAIRING_RETRY; + } // BLE_GAP_EVENT_REPEAT_PAIRING + + case BLE_GAP_EVENT_ENC_CHANGE: { + rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + if(rc != 0) { + return BLE_ATT_ERR_INVALID_HANDLE; + } + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); + ///////////////////////////////////////////// + } else { + server->m_pServerCallbacks->onAuthenticationComplete(&desc); + } + + return 0; + } // BLE_GAP_EVENT_ENC_CHANGE + + case BLE_GAP_EVENT_PASSKEY_ACTION: { + struct ble_sm_io pkey = {0}; + + if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + pkey.action = event->passkey.params.action; + // backward compatibility + pkey.passkey = NimBLEDevice::getSecurityPasskey(); // This is the passkey to be entered on peer + // if the (static)passkey is the default, check the callback for custom value + // both values default to the same. + if(pkey.passkey == 123456) { + pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc); + + } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %d", event->passkey.params.numcmp); + pkey.action = event->passkey.params.action; + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + pkey.numcmp_accept = NimBLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp); + ///////////////////////////////////////////// + } else { + pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp); + } + + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc); + + //TODO: Handle out of band pairing + } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { + static uint8_t tem_oob[16] = {0}; + pkey.action = event->passkey.params.action; + for (int i = 0; i < 16; i++) { + pkey.oob[i] = tem_oob[i]; + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc); + ////////////////////////////////// + } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { + NIMBLE_LOGD(LOG_TAG, "Enter the passkey"); + pkey.action = event->passkey.params.action; + + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + pkey.passkey = NimBLEDevice::m_securityCallbacks->onPassKeyRequest(); + ///////////////////////////////////////////// + } else { + pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); + } + + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc); + + } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { + NIMBLE_LOGD(LOG_TAG, "No passkey action required"); + } + + NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); + return 0; + } // BLE_GAP_EVENT_PASSKEY_ACTION + + default: + break; + } + + NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); + return 0; +} // handleGATTServerEvent + + +/** + * @brief Set the server callbacks. + * + * As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client + * disconnecting. This function can be called to register a callback handler that will be invoked when these + * events are detected. + * + * @param [in] pCallbacks The callbacks to be invoked. + */ +void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks) { + if (pCallbacks != nullptr){ + m_pServerCallbacks = pCallbacks; + } else { + m_pServerCallbacks = &defaultCallbacks; + } +} // setCallbacks + + +/* + * Remove service + */ +/* +void BLEServer::removeService(BLEService* service) { + service->stop(); + service->executeDelete(); + m_serviceMap.removeService(service); +} +*/ + + +/** + * @brief Start advertising. + * + * Start the server advertising its existence. This is a convenience function and is equivalent to + * retrieving the advertising object and invoking start upon it. + */ +void NimBLEServer::startAdvertising() { + NIMBLE_LOGD(LOG_TAG, ">> startAdvertising"); + NimBLEDevice::startAdvertising(); + NIMBLE_LOGD(LOG_TAG, "<< startAdvertising"); +} // startAdvertising + + +/** + * @brief Stop advertising. + */ +void NimBLEServer::stopAdvertising() { + NIMBLE_LOGD(LOG_TAG, ">> stopAdvertising"); + NimBLEDevice::stopAdvertising(); + NIMBLE_LOGD(LOG_TAG, "<< stopAdvertising"); +} // startAdvertising + + +/** + * Allow to connect GATT server to peer device + * Probably can be used in ANCS for iPhone + */ + /* +bool BLEServer::connect(BLEAddress address) { + esp_bd_addr_t addr; + memcpy(&addr, address.getNative(), 6); + // Perform the open connection request against the target BLE Server. + m_semaphoreOpenEvt.take("connect"); + esp_err_t errRc = ::esp_ble_gatts_open( + getGattsIf(), + addr, // address + 1 // direct connection + ); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return false; + } + + uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete. + ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK); + return rc == ESP_GATT_OK; +} // connect +*/ + + +void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); +} // onConnect + + +void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); +} // onConnect + + +void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default"); +} // onDisconnect + +uint32_t NimBLEServerCallbacks::onPassKeyRequest(){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456"); + return 123456; +} + +void NimBLEServerCallbacks::onPassKeyNotify(uint32_t pass_key){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyNotify: default: %d", pass_key); +} + +bool NimBLEServerCallbacks::onSecurityRequest(){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onSecurityRequest: default: true"); + return true; +} +void NimBLEServerCallbacks::onAuthenticationComplete(ble_gap_conn_desc*){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default"); +} +bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true"); + return true; +} + +/* multi connect support */ +void NimBLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) { + const std::map::iterator it = m_connectedServersMap.find(conn_id); + if (it != m_connectedServersMap.end()) { + it->second.mtu = mtu; + } +} + +std::map NimBLEServer::getPeerDevices() { + return m_connectedServersMap; +} + + +/** + * @brief Get the MTU of the client. + * @returns The client MTU or 0 if not found/connected. + */ +uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) { + auto it = m_connectedServersMap.find(conn_id); + if(it != m_connectedServersMap.cend()) { + return (*it).second.mtu; + } else { + return 0; + } +} + +void NimBLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) { + conn_status_t status = { + .peer_device = peer, + .connected = true, + .mtu = 23 + }; + + m_connectedServersMap.insert(std::pair(conn_id, status)); +} + +void NimBLEServer::removePeerDevice(uint16_t conn_id, bool _client) { + m_connectedServersMap.erase(conn_id); +} +/* multi connect support */ + + +/** + * Update connection parameters can be called only after connection has been established + */ +void NimBLEServer::updateConnParams(uint16_t conn_handle, + uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t minConnTime, uint16_t maxConnTime) +{ + ble_gap_upd_params params; + + params.latency = latency; + params.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms + params.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms + params.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms + params.min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units + params.max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units + + int rc = ble_gap_update_params(conn_handle, ¶ms); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + } +} + +/* Don't think this is needed + +void NimBLEServer::onHostReset() { + for(auto it = m_notifyChrMap.cbegin(); it != m_notifyChrMap.cend(); ++it) { + (*it).second->m_semaphoreConfEvt.give(0); + } + +} +*/ +#endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEServer.h b/libesp32/NimBLE-Arduino/src/NimBLEServer.h new file mode 100644 index 000000000..436f20361 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEServer.h @@ -0,0 +1,154 @@ +/* + * NimBLEServer.h + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEServer.h + * + * Created on: Apr 16, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLESERVER_H_ +#define MAIN_NIMBLESERVER_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEAddress.h" +#include "NimBLEUUID.h" +#include "NimBLEAdvertising.h" +#include "NimBLEService.h" +#include "NimBLESecurity.h" +#include "FreeRTOS.h" + + +#include + +class NimBLEService; +class NimBLECharacteristic; +class NimBLEServerCallbacks; + +/* TODO possibly refactor this struct */ +typedef struct { + void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here + bool connected; // do we need it? + uint16_t mtu; // every peer device negotiate own mtu +} conn_status_t; + + +/** + * @brief A data structure that manages the %BLE servers owned by a BLE server. + */ +class NimBLEServiceMap { +public: +// NimBLEService* getByHandle(uint16_t handle); + NimBLEService* getByUUID(const char* uuid); + NimBLEService* getByUUID(NimBLEUUID uuid, uint8_t inst_id = 0); +// void setByHandle(uint16_t handle, NimBLEService* service); + void setByUUID(const char* uuid, NimBLEService* service); + void setByUUID(NimBLEUUID uuid, NimBLEService* service); + std::string toString(); + NimBLEService* getFirst(); + NimBLEService* getNext(); + void removeService(NimBLEService *service); + int getRegisteredServiceCount(); + +private: +// std::map m_handleMap; + std::map m_uuidMap; + std::map::iterator m_iterator; +}; + + +/** + * @brief The model of a %BLE server. + */ +class NimBLEServer { +public: + uint32_t getConnectedCount(); + NimBLEService* createService(const char* uuid); + NimBLEService* createService(NimBLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); + NimBLEAdvertising* getAdvertising(); + void setCallbacks(NimBLEServerCallbacks* pCallbacks); + void startAdvertising(); + void stopAdvertising(); + void start(); +// void removeService(BLEService* service); + NimBLEService* getServiceByUUID(const char* uuid); + NimBLEService* getServiceByUUID(NimBLEUUID uuid); + int disconnect(uint16_t connID, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); +// bool connect(BLEAddress address); + void updateConnParams(uint16_t conn_handle, + uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t minConnTime=0, uint16_t maxConnTime=0); + + /* multi connection support */ + std::map getPeerDevices(); + void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); + void removePeerDevice(uint16_t conn_id, bool client); + NimBLEServer* getServerByConnId(uint16_t conn_id); + void updatePeerMTU(uint16_t connId, uint16_t mtu); + uint16_t getPeerMTU(uint16_t conn_id); + uint16_t getConnId(); + + +private: + NimBLEServer(); + //friend class BLEService; + friend class NimBLECharacteristic; + friend class NimBLEDevice; + friend class NimBLEAdvertising; + // void onHostReset(); + // BLEAdvertising m_bleAdvertising; + uint16_t m_connId; + uint16_t m_svcChgChrHdl; + bool m_gattsStarted; + + std::map m_connectedServersMap; + std::map m_notifyChrMap; + + NimBLEServiceMap m_serviceMap; + NimBLEServerCallbacks* m_pServerCallbacks; + + static int handleGapEvent(struct ble_gap_event *event, void *arg); +}; // NimBLEServer + + +/** + * @brief Callbacks associated with the operation of a %BLE server. + */ +class NimBLEServerCallbacks { +public: + virtual ~NimBLEServerCallbacks() {}; + /** + * @brief Handle a new client connection. + * + * When a new client connects, we are invoked. + * + * @param [in] pServer A reference to the %BLE server that received the client connection. + */ + virtual void onConnect(NimBLEServer* pServer); + virtual void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc); + /** + * @brief Handle an existing client disconnection. + * + * When an existing client disconnects, we are invoked. + * + * @param [in] pServer A reference to the %BLE server that received the existing client disconnection. + */ + virtual void onDisconnect(NimBLEServer* pServer); + + virtual uint32_t onPassKeyRequest(); //{return 0;} + virtual void onPassKeyNotify(uint32_t pass_key); //{} + virtual bool onSecurityRequest(); //{return true;} + virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);//{}; + virtual bool onConfirmPIN(uint32_t pin);//{return true;} +}; // BLEServerCallbacks + + +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_NIMBLESERVER_H_ */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEService.cpp b/libesp32/NimBLE-Arduino/src/NimBLEService.cpp new file mode 100644 index 000000000..4bf6f34ff --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEService.cpp @@ -0,0 +1,293 @@ +/* + * NimBLEService.cpp + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEService.cpp + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +// A service is identified by a UUID. A service is also the container for one or more characteristics. + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEService.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +#include + +static const char* LOG_TAG = "NimBLEService"; // Tag for logging. + +#define NULL_HANDLE (0xffff) + + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. + */ +NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer) +: NimBLEService(NimBLEUUID(uuid), numHandles, pServer) { +} + + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. + */ +NimBLEService::NimBLEService(NimBLEUUID uuid, uint16_t numHandles, NimBLEServer* pServer) { + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_pServer = pServer; + m_numHandles = numHandles; +} // NimBLEService + + +/** + * @brief Dump details of this BLE GATT service. + * @return N/A. + */ +void NimBLEService::dump() { + NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x", + m_uuid.toString().c_str(), + m_handle); + NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); +} // dump + + +/** + * @brief Get the UUID of the service. + * @return the UUID of the service. + */ +NimBLEUUID NimBLEService::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Start the service. + * Here we wish to start the service which means that we will respond to partner requests about it. + * Starting a service also means that we can create the corresponding characteristics. + * @return Start the service. + */ + +bool NimBLEService::start() { + NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str()); + int rc = 0; + // Nimble requires an array of services to be sent to the api + // Since we are adding 1 at a time we create an array of 2 and set the type + // of the second service to 0 to indicate the end of the array. + ble_gatt_svc_def* svc = new ble_gatt_svc_def[2]; + ble_gatt_chr_def* pChr_a = nullptr; + ble_gatt_dsc_def* pDsc_a = nullptr; + + svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY; + svc[0].uuid = &m_uuid.getNative()->u; + svc[0].includes = NULL; + + uint8_t numChrs = m_characteristicMap.getSize(); + + NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str()); + + if(!numChrs){ + svc[0].characteristics = NULL; + }else{ + // Nimble requires the last characteristic to have it's uuid = 0 to indicate the end + // of the characteristics for the service. We create 1 extra and set it to null + // for this purpose. + pChr_a = new ble_gatt_chr_def[numChrs+1]; + NimBLECharacteristic* pCharacteristic = m_characteristicMap.getFirst(); + + for(uint8_t i=0; i < numChrs; i++) { + uint8_t numDscs = pCharacteristic->m_descriptorMap.getSize(); + if(numDscs) { + // skip 2902 as it's automatically created by NimBLE + // if Indicate or Notify flags are set + if((pCharacteristic->m_properties & BLE_GATT_CHR_F_INDICATE) || + (pCharacteristic->m_properties & BLE_GATT_CHR_F_NOTIFY)) { + numDscs--; + } + } + + if(!numDscs){ + pChr_a[i].descriptors = NULL; + } else { + // Must have last descriptor uuid = 0 so we have to create 1 extra + //NIMBLE_LOGD(LOG_TAG, "Adding %d descriptors", numDscs); + pDsc_a = new ble_gatt_dsc_def[numDscs+1]; + NimBLEDescriptor* pDescriptor = pCharacteristic->m_descriptorMap.getFirst(); + for(uint8_t d=0; d < numDscs;) { + // skip 2902 + if(pDescriptor->m_uuid.equals(NimBLEUUID((uint16_t)0x2902))) { + //NIMBLE_LOGD(LOG_TAG, "Skipped 0x2902"); + pDescriptor = pCharacteristic->m_descriptorMap.getNext(); + continue; + } + pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u; + pDsc_a[d].att_flags = pDescriptor->m_properties; + pDsc_a[d].min_key_size = 0; + pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent; + pDsc_a[d].arg = pDescriptor; + pDescriptor = pCharacteristic->m_descriptorMap.getNext(); + d++; + } + + pDsc_a[numDscs].uuid = NULL; + pChr_a[i].descriptors = pDsc_a; + } + + pChr_a[i].uuid = &pCharacteristic->m_uuid.getNative()->u; + pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent; + pChr_a[i].arg = pCharacteristic; + pChr_a[i].flags = pCharacteristic->m_properties; + pChr_a[i].min_key_size = 0; + pChr_a[i].val_handle = &pCharacteristic->m_handle; + pCharacteristic = m_characteristicMap.getNext(); + } + + pChr_a[numChrs].uuid = NULL; + svc[0].characteristics = pChr_a; + } + + // end of services must indicate to api with type = 0 + svc[1].type = 0; + + rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)svc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + } + + rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)svc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + + } + + NIMBLE_LOGD(LOG_TAG, "<< start()"); + return true; +} // start + + +/** + * @brief Set the handle associated with this service. + * @param [in] handle The handle associated with the service. + */ +void NimBLEService::setHandle(uint16_t handle) { + NIMBLE_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str()); + if (m_handle != NULL_HANDLE) { + NIMBLE_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle); + return; + } + m_handle = handle; + NIMBLE_LOGD(LOG_TAG, "<< setHandle"); +} // setHandle + + +/** + * @brief Get the handle associated with this service. + * @return The handle associated with this service. + */ +uint16_t NimBLEService::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Add a characteristic to the service. + * @param [in] pCharacteristic A pointer to the characteristic to be added. + */ +void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) { + // We maintain a mapping of characteristics owned by this service. These are managed by the + // BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic + // to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). + + NIMBLE_LOGD(LOG_TAG, ">> addCharacteristic()"); + NIMBLE_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", + pCharacteristic->getUUID().toString().c_str(), + toString().c_str()); + + // Check that we don't add the same characteristic twice. + if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { + NIMBLE_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); + //return; + } + + // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID + // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. + m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); + + NIMBLE_LOGD(LOG_TAG, "<< addCharacteristic()"); +} // addCharacteristic + + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @return The new BLE characteristic. + */ +NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties) { + return createCharacteristic(NimBLEUUID(uuid), properties); +} + + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @return The new BLE characteristic. + */ +NimBLECharacteristic* NimBLEService::createCharacteristic(NimBLEUUID uuid, uint32_t properties) { + NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this); + addCharacteristic(pCharacteristic); + //pCharacteristic->executeCreate(this); + return pCharacteristic; +} // createCharacteristic + + +NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) { + return getCharacteristic(NimBLEUUID(uuid)); +} + + +NimBLECharacteristic* NimBLEService::getCharacteristic(NimBLEUUID uuid) { + return m_characteristicMap.getByUUID(uuid); +} + + +/** + * @brief Return a string representation of this service. + * A service is defined by: + * * Its UUID + * * Its handle + * @return A string representation of this service. + */ +std::string NimBLEService::toString() { + std::string res = "UUID: " + getUUID().toString(); + char hex[5]; + snprintf(hex, sizeof(hex), "%04x", getHandle()); + res += ", handle: 0x"; + res += hex; + return res; +} // toString + + +/** + * @brief Get the BLE server associated with this service. + * @return The BLEServer associated with this service. + */ +NimBLEServer* NimBLEService::getServer() { + return m_pServer; +} // getServer + +#endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEService.h b/libesp32/NimBLE-Arduino/src/NimBLEService.h new file mode 100644 index 000000000..70fed3bb2 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEService.h @@ -0,0 +1,96 @@ +/* + * NimBLEService.h + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEService.h + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLESERVICE_H_ +#define MAIN_NIMBLESERVICE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLECharacteristic.h" +#include "NimBLEServer.h" +#include "NimBLEUUID.h" +#include "FreeRTOS.h" + + +class NimBLEServer; +class NimBLECharacteristic; + +/** + * @brief A data mapping used to manage the set of %BLE characteristics known to the server. + */ +class NimBLECharacteristicMap { +public: + void setByUUID(NimBLECharacteristic* pCharacteristic, const char* uuid); + void setByUUID(NimBLECharacteristic* pCharacteristic, NimBLEUUID uuid); + void setByHandle(uint16_t handle, NimBLECharacteristic* pCharacteristic); + NimBLECharacteristic* getByUUID(const char* uuid); + NimBLECharacteristic* getByUUID(NimBLEUUID uuid); + NimBLECharacteristic* getByHandle(uint16_t handle); + NimBLECharacteristic* getFirst(); + NimBLECharacteristic* getNext(); + uint8_t getSize(); + std::string toString(); + +private: + std::map m_uuidMap; + std::map m_handleMap; + std::map::iterator m_iterator; +}; + + +/** + * @brief The model of a %BLE service. + * + */ +class NimBLEService { +public: + NimBLECharacteristic* createCharacteristic(const char* uuid, + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE); + + NimBLECharacteristic* createCharacteristic(NimBLEUUID uuid, + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE); + + void dump(); + NimBLECharacteristic* getCharacteristic(const char* uuid); + NimBLECharacteristic* getCharacteristic(NimBLEUUID uuid); + NimBLEUUID getUUID(); + NimBLEServer* getServer(); + bool start(); +// void stop(); + std::string toString(); + uint16_t getHandle(); + uint8_t m_instId = 0; + +private: + NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer); + NimBLEService(NimBLEUUID uuid, uint16_t numHandles, NimBLEServer* pServer); + friend class NimBLEServer; + friend class NimBLEDevice; + + void addCharacteristic(NimBLECharacteristic* pCharacteristic); + + NimBLECharacteristicMap m_characteristicMap; + uint16_t m_handle; + NimBLEServer* m_pServer = nullptr; + NimBLEUUID m_uuid; + + uint16_t m_numHandles; + void setHandle(uint16_t handle); +}; // BLEService + + +#endif // CONFIG_BT_ENABLED +#endif /* MAIN_NIMBLESERVICE_H_ */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEServiceMap.cpp b/libesp32/NimBLE-Arduino/src/NimBLEServiceMap.cpp new file mode 100644 index 000000000..779c49a13 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEServiceMap.cpp @@ -0,0 +1,137 @@ +/* + * NimBLEService.cpp + * + * Created: on March 7, 2020 + * Author H2zero + * + * Originally: + * + * BLEServiceMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEService.h" + + +/** + * @brief Return the service by UUID. + * @param [in] UUID The UUID to look up the service. + * @return The characteristic. + */ +NimBLEService* NimBLEServiceMap::getByUUID(const char* uuid) { + return getByUUID(NimBLEUUID(uuid)); +} + +/** + * @brief Return the service by UUID. + * @param [in] UUID The UUID to look up the service. + * @return The characteristic. + */ +NimBLEService* NimBLEServiceMap::getByUUID(NimBLEUUID uuid, uint8_t inst_id) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + //return m_uuidMap.at(uuid.toString()); + return nullptr; +} // getByUUID + + +/** + * @brief Return the service by handle. + * @param [in] handle The handle to look up the service. + * @return The service. + */ +/* +NimBLEService* NimBLEServiceMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle +*/ + +/** + * @brief Set the service by UUID. + * @param [in] uuid The uuid of the service. + * @param [in] characteristic The service to cache. + * @return N/A. + */ +void NimBLEServiceMap::setByUUID(NimBLEUUID uuid, NimBLEService* service) { + m_uuidMap.insert(std::pair(service, uuid.toString())); +} // setByUUID + + +/** + * @brief Set the service by handle. + * @param [in] handle The handle of the service. + * @param [in] service The service to cache. + * @return N/A. + */ + /* +void NimBLEServiceMap::setByHandle(uint16_t handle, NimBLEService* service) { + m_handleMap.insert(std::pair(handle, service)); +} // setByHandle +*/ + +/** + * @brief Return a string representation of the service map. + * @return A string representation of the service map. + */ +std::string NimBLEServiceMap::toString() { + std::string res; + //char hex[5]; + for (auto &myPair: m_uuidMap) { + // res += "handle: 0x"; + // snprintf(hex, sizeof(hex), "%04x", myPair.first); + // res += hex; + res += ", uuid: " + myPair.second + "\n"; + } + return res; +} // toString + + +/** + * @brief Get the first service in the map. + * @return The first service in the map. + */ +NimBLEService* NimBLEServiceMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + +/** + * @brief Get the next service in the map. + * @return The next service in the map. + */ +NimBLEService* NimBLEServiceMap::getNext() { + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext + +/** + * @brief Removes service from maps. + * @return N/A. + */ +void NimBLEServiceMap::removeService(NimBLEService* service) { + //m_handleMap.erase(service->getHandle()); + m_uuidMap.erase(service); +} // removeService + +/** + * @brief Returns the amount of registered services + * @return amount of registered services + */ +int NimBLEServiceMap::getRegisteredServiceCount(){ + //return m_handleMap.size(); + return m_uuidMap.size(); +} + +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp b/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp new file mode 100644 index 000000000..0002d1911 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp @@ -0,0 +1,301 @@ +/* + * NimBLEUUID.cpp + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEUUID.cpp + * + * Created on: Jun 21, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEUtils.h" +#include "NimBLEUUID.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEUUID"; + + +/** + * @brief Create a UUID from a string. + * + * Create a UUID from a string. There will be two possible stories here. Either the string represents + * a binary data field or the string represents a hex encoding of a UUID. + * For the hex encoding, here is an example: + * + * ``` + * "beb5483e-36e1-4688-b7f5-ea07361b26a8" + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * 12345678-90ab-cdef-1234-567890abcdef + * ``` + * + * This has a length of 36 characters. We need to parse this into 16 bytes. + * + * @param [in] value The string to build a UUID from. + */ + NimBLEUUID::NimBLEUUID(std::string value) { + m_valueSet = true; + if (value.length() == 4) { + m_uuid.u.type = BLE_UUID_TYPE_16; + m_uuid.u16.value = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.u16.value += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(2-i)*4; + i+=2; + } + } + else if (value.length() == 8) { + m_uuid.u.type = BLE_UUID_TYPE_32; + m_uuid.u32.value = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.u32.value += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(6-i)*4; + i+=2; + } + } + else if (value.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be investigated (lack of time) + m_uuid.u.type = BLE_UUID_TYPE_128; + NimBLEUtils::memrcpy(m_uuid.u128.value, (uint8_t*)value.data(), 16); + } + else if (value.length() == 36) { + // If the length of the string is 36 bytes then we will assume it is a long hex string in + // UUID format. + m_uuid.u.type = BLE_UUID_TYPE_128; + int n = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.u128.value[15-n++] = ((MSB&0x0F) <<4) | (LSB & 0x0F); + i+=2; + } + } + else { + NIMBLE_LOGE(LOG_TAG,"ERROR: UUID value not 2, 4, 16 or 36 bytes"); + m_valueSet = false; + } +} // NimBLEUUID(std::string) + + +/** + * @brief Create a UUID from 16 bytes of memory. + * + * @param [in] pData The pointer to the start of the UUID. + * @param [in] size The size of the data. + * @param [in] msbFirst Is the MSB first in pData memory? + */ +NimBLEUUID::NimBLEUUID(uint8_t* pData, size_t size, bool msbFirst) { +/*** TODO: change this to use the Nimble function for various lenght UUIDs: + int ble_uuid_init_from_buf(ble_uuid_any_t *uuid, const void *buf, size_t len); +***/ + if (size != 16) { + NIMBLE_LOGE(LOG_TAG,"ERROR: UUID length not 16 bytes"); + return; + } + m_uuid.u.type = BLE_UUID_TYPE_128; + if (msbFirst) { + NimBLEUtils::memrcpy(m_uuid.u128.value, pData, 16); + } else { + memcpy(m_uuid.u128.value, pData, 16); + } + m_valueSet = true; +} // NimBLEUUID + + +/** + * @brief Create a UUID from the 16bit value. + * + * @param [in] uuid The 16bit short form UUID. + */ +NimBLEUUID::NimBLEUUID(uint16_t uuid) { + m_uuid.u.type = BLE_UUID_TYPE_16; + m_uuid.u16.value = uuid; + m_valueSet = true; +} // NimBLEUUID + + +/** + * @brief Create a UUID from the 32bit value. + * + * @param [in] uuid The 32bit short form UUID. + */ +NimBLEUUID::NimBLEUUID(uint32_t uuid) { + m_uuid.u.type = BLE_UUID_TYPE_32; + m_uuid.u32.value = uuid; + m_valueSet = true; +} // NimBLEUUID + + +/** + * @brief Create a UUID from the native UUID. + * + * @param [in] uuid The native UUID. + */ + +NimBLEUUID::NimBLEUUID(ble_uuid128_t* uuid) { + m_uuid.u.type = BLE_UUID_TYPE_128; + memcpy(m_uuid.u128.value, uuid->value, 16); + m_valueSet = true; +} // NimBLEUUID + + +NimBLEUUID::NimBLEUUID() { + m_valueSet = false; +} // NimBLEUUID + + +/** + * @brief Get the number of bits in this uuid. + * @return The number of bits in the UUID. One of 16, 32 or 128. + */ +uint8_t NimBLEUUID::bitSize() { + if (!m_valueSet) return 0; + return m_uuid.u.type; +} // bitSize + + +/** + * @brief Compare a UUID against this UUID. + * + * @param [in] uuid The UUID to compare against. + * @return True if the UUIDs are equal and false otherwise. + */ +bool NimBLEUUID::equals(NimBLEUUID uuid) { + if(ble_uuid_cmp(&m_uuid.u, &uuid.getNative()->u) == 0){ + return true; + } + return false; +} + + +/** + * Create a BLEUUID from a string of the form: + * 0xNNNN + * 0xNNNNNNNN + * 0x + * NNNN + * NNNNNNNN + * + */ + +NimBLEUUID NimBLEUUID::fromString(std::string _uuid) { + uint8_t start = 0; + if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters. + start = 2; + } + uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use. + + if(len == 4) { + uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); + return NimBLEUUID(x); + } else if (len == 8) { + uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); + return NimBLEUUID(x); + } else if (len == 36) { + return NimBLEUUID(_uuid); + } + return NimBLEUUID(); +} // fromString + + +/** + * @brief Get the native UUID value. + * + * @return The native UUID value or NULL if not set. + */ +ble_uuid_any_t* NimBLEUUID::getNative() { + if (m_valueSet == false) { + NIMBLE_LOGD(LOG_TAG,"<< Return of un-initialized UUID!"); + return nullptr; + } + return &m_uuid; +} // getNative + + +/** + * @brief Convert a UUID to its 128 bit representation. + * + * A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method + * will convert 16 or 32 bit representations to the full 128bit. + */ +NimBLEUUID NimBLEUUID::to128() { + // If we either don't have a value or are already a 128 bit UUID, nothing further to do. + if (!m_valueSet || m_uuid.u.type == BLE_UUID_TYPE_128) { + return *this; + } + + // If we are 16 bit or 32 bit, then set the 4 bytes of the variable part of the UUID. + if (m_uuid.u.type == BLE_UUID_TYPE_16) { + uint16_t temp = m_uuid.u16.value; + m_uuid.u128.value[15] = 0; + m_uuid.u128.value[14] = 0; + m_uuid.u128.value[13] = (temp >> 8) & 0xff; + m_uuid.u128.value[12] = temp & 0xff; + + } + else if (m_uuid.u.type == BLE_UUID_TYPE_32) { + uint32_t temp = m_uuid.u32.value; + m_uuid.u128.value[15] = (temp >> 24) & 0xff; + m_uuid.u128.value[14] = (temp >> 16) & 0xff; + m_uuid.u128.value[13] = (temp >> 8) & 0xff; + m_uuid.u128.value[12] = temp & 0xff; + } + + // Set the fixed parts of the UUID. + m_uuid.u128.value[11] = 0x00; + m_uuid.u128.value[10] = 0x00; + + m_uuid.u128.value[9] = 0x10; + m_uuid.u128.value[8] = 0x00; + + m_uuid.u128.value[7] = 0x80; + m_uuid.u128.value[6] = 0x00; + + m_uuid.u128.value[5] = 0x00; + m_uuid.u128.value[4] = 0x80; + m_uuid.u128.value[3] = 0x5f; + m_uuid.u128.value[2] = 0x9b; + m_uuid.u128.value[1] = 0x34; + m_uuid.u128.value[0] = 0xfb; + + m_uuid.u.type = BLE_UUID_TYPE_128; + return *this; +} // to128 + + +/** + * @brief Get a string representation of the UUID. + * + * The format of a string is: + * 01234567 8901 2345 6789 012345678901 + * 0000180d-0000-1000-8000-00805f9b34fb + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * + * @return A string representation of the UUID. + */ +std::string NimBLEUUID::toString() { + if (!m_valueSet) return ""; // If we have no value, nothing to format. + + char buf[BLE_UUID_STR_LEN]; + + return ble_uuid_to_str(&m_uuid.u, buf); +} // toString + +#endif /* CONFIG_BT_ENABLED */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUUID.h b/libesp32/NimBLE-Arduino/src/NimBLEUUID.h new file mode 100644 index 000000000..63230fba5 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEUUID.h @@ -0,0 +1,51 @@ +/* + * NimBLEUUID.h + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEUUID.h + * + * Created on: Jun 21, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEUUID_H_ +#define COMPONENTS_NIMBLEUUID_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "host/ble_uuid.h" +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include + +/** + * @brief A model of a %BLE UUID. + */ +class NimBLEUUID { +public: + NimBLEUUID(std::string uuid); + NimBLEUUID(uint16_t uuid); + NimBLEUUID(uint32_t uuid); + NimBLEUUID(ble_uuid128_t* uuid); + NimBLEUUID(uint8_t* pData, size_t size, bool msbFirst); + NimBLEUUID(); + uint8_t bitSize(); // Get the number of bits in this uuid. + bool equals(NimBLEUUID uuid); + ble_uuid_any_t* getNative(); + NimBLEUUID to128(); + std::string toString(); + static NimBLEUUID fromString(std::string uuid); // Create a NimBLEUUID from a string + +private: + ble_uuid_any_t m_uuid; // The underlying UUID structure that this class wraps. + bool m_valueSet = false; // Is there a value set for this instance. +}; // NimBLEUUID +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_NIMBLEUUID_H_ */ diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp b/libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp new file mode 100644 index 000000000..3911fd7ed --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp @@ -0,0 +1,701 @@ +/* + * NimBLEUtils.cpp + * + * Created: on Jan 25 2020 + * Author H2zero + * + */ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEUtils"; + +/** + * @brief Copy memory from source to target but in reverse order. + * + * When we move memory from one location it is normally: + * + * ``` + * [0][1][2]...[n] -> [0][1][2]...[n] + * ``` + * + * with this function, it is: + * + * ``` + * [0][1][2]...[n] -> [n][n-1][n-2]...[0] + * ``` + * + * @param [in] target The target of the copy + * @param [in] source The source of the copy + * @param [in] size The number of bytes to copy + */ +void NimBLEUtils::memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { + assert(size > 0); + target += (size - 1); // Point target to the last byte of the target data + while (size > 0) { + *target = *source; + target--; + source++; + size--; + } +} // memrcpy + +int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) { + /* Check connection interval min */ + if ((params->itvl_min < BLE_HCI_CONN_ITVL_MIN) || + (params->itvl_min > BLE_HCI_CONN_ITVL_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + /* Check connection interval max */ + if ((params->itvl_max < BLE_HCI_CONN_ITVL_MIN) || + (params->itvl_max > BLE_HCI_CONN_ITVL_MAX) || + (params->itvl_max < params->itvl_min)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection latency */ + if (params->latency > BLE_HCI_CONN_LATENCY_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check supervision timeout */ + if ((params->supervision_timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) || + (params->supervision_timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection event length */ + if (params->min_ce_len > params->max_ce_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return 0; +} + + +const char* NimBLEUtils::returnCodeToString(int rc) { + switch(rc) { + case 0: + return "SUCCESS"; + case BLE_HS_EAGAIN: + return "Temporary failure; try again."; + case BLE_HS_EALREADY: + return "Operation already in progress or completed."; + case BLE_HS_EINVAL: + return "One or more arguments are invalid."; + case BLE_HS_EMSGSIZE: + return "The provided buffer is too small."; + case BLE_HS_ENOENT: + return "No entry matching the specified criteria."; + case BLE_HS_ENOMEM: + return "Operation failed due to resource exhaustion."; + case BLE_HS_ENOTCONN: + return "No open connection with the specified handle."; + case BLE_HS_ENOTSUP: + return "Operation disabled at compile time."; + case BLE_HS_EAPP: + return "Application callback behaved unexpectedly."; + case BLE_HS_EBADDATA: + return "Command from peer is invalid."; + case BLE_HS_EOS: + return "Mynewt OS error."; + case BLE_HS_ECONTROLLER: + return "Event from controller is invalid."; + case BLE_HS_ETIMEOUT: + return "Operation timed out."; + case BLE_HS_EDONE: + return "Operation completed successfully."; + case BLE_HS_EBUSY: + return "Operation cannot be performed until procedure completes."; + case BLE_HS_EREJECT: + return "Peer rejected a connection parameter update request."; + case BLE_HS_EUNKNOWN: + return "Unexpected failure; catch all."; + case BLE_HS_EROLE: + return "Operation requires different role (e.g., central vs. peripheral)."; + case BLE_HS_ETIMEOUT_HCI: + return "HCI request timed out; controller unresponsive."; + case BLE_HS_ENOMEM_EVT: + return "Controller failed to send event due to memory exhaustion (combined host-controller only)."; + case BLE_HS_ENOADDR: + return "Operation requires an identity address but none configured."; + case BLE_HS_ENOTSYNCED: + return "Attempt to use the host before it is synced with controller."; + case BLE_HS_EAUTHEN: + return "Insufficient authentication."; + case BLE_HS_EAUTHOR: + return "Insufficient authorization."; + case BLE_HS_EENCRYPT: + return "Insufficient encryption level."; + case BLE_HS_EENCRYPT_KEY_SZ: + return "Insufficient key size."; + case BLE_HS_ESTORE_CAP: + return "Storage at capacity."; + case BLE_HS_ESTORE_FAIL: + return "Storage IO error."; + case (0x0100+BLE_ATT_ERR_INVALID_HANDLE ): + return "The attribute handle given was not valid on this server."; + case (0x0100+BLE_ATT_ERR_READ_NOT_PERMITTED ): + return "The attribute cannot be read."; + case (0x0100+BLE_ATT_ERR_WRITE_NOT_PERMITTED ): + return "The attribute cannot be written."; + case (0x0100+BLE_ATT_ERR_INVALID_PDU ): + return "The attribute PDU was invalid."; + case (0x0100+BLE_ATT_ERR_INSUFFICIENT_AUTHEN ): + return "The attribute requires authentication before it can be read or written."; + case (0x0100+BLE_ATT_ERR_REQ_NOT_SUPPORTED ): + return "Attribute server does not support the request received from the client."; + case (0x0100+BLE_ATT_ERR_INVALID_OFFSET ): + return "Offset specified was past the end of the attribute."; + case (0x0100+BLE_ATT_ERR_INSUFFICIENT_AUTHOR ): + return "The attribute requires authorization before it can be read or written."; + case (0x0100+BLE_ATT_ERR_PREPARE_QUEUE_FULL ): + return "Too many prepare writes have been queued."; + case (0x0100+BLE_ATT_ERR_ATTR_NOT_FOUND ): + return "No attribute found within the given attribute handle range."; + case (0x0100+BLE_ATT_ERR_ATTR_NOT_LONG ): + return "The attribute cannot be read or written using the Read Blob Request."; + case (0x0100+BLE_ATT_ERR_INSUFFICIENT_KEY_SZ ): + return "The Encryption Key Size used for encrypting this link is insufficient."; + case (0x0100+BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN ): + return "The attribute value length is invalid for the operation."; + case (0x0100+BLE_ATT_ERR_UNLIKELY ): + return "The attribute request has encountered an error that was unlikely, could not be completed as requested."; + case (0x0100+BLE_ATT_ERR_INSUFFICIENT_ENC ): + return "The attribute requires encryption before it can be read or written."; + case (0x0100+BLE_ATT_ERR_UNSUPPORTED_GROUP ): + return "The attribute type is not a supported grouping attribute as defined by a higher layer specification."; + case (0x0100+BLE_ATT_ERR_INSUFFICIENT_RES ): + return "Insufficient Resources to complete the request."; + case (0x0200+BLE_ERR_UNKNOWN_HCI_CMD ): + return "Unknown HCI Command"; + case (0x0200+BLE_ERR_UNK_CONN_ID ): + return "Unknown Connection Identifier"; + case (0x0200+BLE_ERR_HW_FAIL ): + return "Hardware Failure"; + case (0x0200+BLE_ERR_PAGE_TMO ): + return "Page Timeout"; + case (0x0200+BLE_ERR_AUTH_FAIL ): + return "Authentication Failure"; + case (0x0200+BLE_ERR_PINKEY_MISSING ): + return "PIN or Key Missing"; + case (0x0200+BLE_ERR_MEM_CAPACITY ): + return "Memory Capacity Exceeded"; + case (0x0200+BLE_ERR_CONN_SPVN_TMO ): + return "Connection Timeout"; + case (0x0200+BLE_ERR_CONN_LIMIT ): + return "Connection Limit Exceeded"; + case (0x0200+BLE_ERR_SYNCH_CONN_LIMIT ): + return "Synchronous Connection Limit To A Device Exceeded"; + case (0x0200+BLE_ERR_ACL_CONN_EXISTS ): + return "ACL Connection Already Exists"; + case (0x0200+BLE_ERR_CMD_DISALLOWED ): + return "Command Disallowed"; + case (0x0200+BLE_ERR_CONN_REJ_RESOURCES ): + return "Connection Rejected due to Limited Resources"; + case (0x0200+BLE_ERR_CONN_REJ_SECURITY ): + return "Connection Rejected Due To Security Reasons"; + case (0x0200+BLE_ERR_CONN_REJ_BD_ADDR ): + return "Connection Rejected due to Unacceptable BD_ADDR"; + case (0x0200+BLE_ERR_CONN_ACCEPT_TMO ): + return "Connection Accept Timeout Exceeded"; + case (0x0200+BLE_ERR_UNSUPPORTED ): + return "Unsupported Feature or Parameter Value"; + case (0x0200+BLE_ERR_INV_HCI_CMD_PARMS ): + return "Invalid HCI Command Parameters"; + case (0x0200+BLE_ERR_REM_USER_CONN_TERM ): + return "Remote User Terminated Connection"; + case (0x0200+BLE_ERR_RD_CONN_TERM_RESRCS ): + return "Remote Device Terminated Connection due to Low Resources"; + case (0x0200+BLE_ERR_RD_CONN_TERM_PWROFF ): + return "Remote Device Terminated Connection due to Power Off"; + case (0x0200+BLE_ERR_CONN_TERM_LOCAL ): + return "Connection Terminated By Local Host"; + case (0x0200+BLE_ERR_REPEATED_ATTEMPTS ): + return "Repeated Attempts"; + case (0x0200+BLE_ERR_NO_PAIRING ): + return "Pairing Not Allowed"; + case (0x0200+BLE_ERR_UNK_LMP ): + return "Unknown LMP PDU"; + case (0x0200+BLE_ERR_UNSUPP_REM_FEATURE ): + return "Unsupported Remote Feature / Unsupported LMP Feature"; + case (0x0200+BLE_ERR_SCO_OFFSET ): + return "SCO Offset Rejected"; + case (0x0200+BLE_ERR_SCO_ITVL ): + return "SCO Interval Rejected"; + case (0x0200+BLE_ERR_SCO_AIR_MODE ): + return "SCO Air Mode Rejected"; + case (0x0200+BLE_ERR_INV_LMP_LL_PARM ): + return "Invalid LMP Parameters / Invalid LL Parameters"; + case (0x0200+BLE_ERR_UNSPECIFIED ): + return "Unspecified Error"; + case (0x0200+BLE_ERR_UNSUPP_LMP_LL_PARM ): + return "Unsupported LMP Parameter Value / Unsupported LL Parameter Value"; + case (0x0200+BLE_ERR_NO_ROLE_CHANGE ): + return "Role Change Not Allowed"; + case (0x0200+BLE_ERR_LMP_LL_RSP_TMO ): + return "LMP Response Timeout / LL Response Timeout"; + case (0x0200+BLE_ERR_LMP_COLLISION ): + return "LMP Error Transaction Collision"; + case (0x0200+BLE_ERR_LMP_PDU ): + return "LMP PDU Not Allowed"; + case (0x0200+BLE_ERR_ENCRYPTION_MODE ): + return "Encryption Mode Not Acceptable"; + case (0x0200+BLE_ERR_LINK_KEY_CHANGE ): + return "Link Key cannot be Changed"; + case (0x0200+BLE_ERR_UNSUPP_QOS ): + return "Requested QoS Not Supported"; + case (0x0200+BLE_ERR_INSTANT_PASSED ): + return "Instant Passed"; + case (0x0200+BLE_ERR_UNIT_KEY_PAIRING ): + return "Pairing With Unit Key Not Supported"; + case (0x0200+BLE_ERR_DIFF_TRANS_COLL ): + return "Different Transaction Collision"; + case (0x0200+BLE_ERR_QOS_PARM ): + return "QoS Unacceptable Parameter"; + case (0x0200+BLE_ERR_QOS_REJECTED ): + return "QoS Rejected"; + case (0x0200+BLE_ERR_CHAN_CLASS ): + return "Channel Classification Not Supported"; + case (0x0200+BLE_ERR_INSUFFICIENT_SEC ): + return "Insufficient Security"; + case (0x0200+BLE_ERR_PARM_OUT_OF_RANGE ): + return "Parameter Out Of Mandatory Range"; + case (0x0200+BLE_ERR_PENDING_ROLE_SW ): + return "Role Switch Pending"; + case (0x0200+BLE_ERR_RESERVED_SLOT ): + return "Reserved Slot Violation"; + case (0x0200+BLE_ERR_ROLE_SW_FAIL ): + return "Role Switch Failed"; + case (0x0200+BLE_ERR_INQ_RSP_TOO_BIG ): + return "Extended Inquiry Response Too Large"; + case (0x0200+BLE_ERR_SEC_SIMPLE_PAIR ): + return "Secure Simple Pairing Not Supported By Host"; + case (0x0200+BLE_ERR_HOST_BUSY_PAIR ): + return "Host Busy - Pairing"; + case (0x0200+BLE_ERR_CONN_REJ_CHANNEL ): + return "Connection Rejected, No Suitable Channel Found"; + case (0x0200+BLE_ERR_CTLR_BUSY ): + return "Controller Busy"; + case (0x0200+BLE_ERR_CONN_PARMS ): + return "Unacceptable Connection Parameters"; + case (0x0200+BLE_ERR_DIR_ADV_TMO ): + return "Directed Advertising Timeout"; + case (0x0200+BLE_ERR_CONN_TERM_MIC ): + return "Connection Terminated due to MIC Failure"; + case (0x0200+BLE_ERR_CONN_ESTABLISHMENT ): + return "Connection Failed to be Established"; + case (0x0200+BLE_ERR_MAC_CONN_FAIL ): + return "MAC Connection Failed"; + case (0x0200+BLE_ERR_COARSE_CLK_ADJ ): + return "Coarse Clock Adjustment Rejected"; + case (0x0300+BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD ): + return "Invalid or unsupported incoming L2CAP sig command."; + case (0x0300+BLE_L2CAP_SIG_ERR_MTU_EXCEEDED ): + return "Incoming packet too large."; + case (0x0300+BLE_L2CAP_SIG_ERR_INVALID_CID ): + return "No channel with specified ID."; + case (0x0400+BLE_SM_ERR_PASSKEY ): + return "The user input of passkey failed, for example, the user cancelled the operation."; + case (0x0400+BLE_SM_ERR_OOB ): + return "The OOB data is not available."; + case (0x0400+BLE_SM_ERR_AUTHREQ ): + return "The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices."; + case (0x0400+BLE_SM_ERR_CONFIRM_MISMATCH ): + return "The confirm value does not match the calculated compare value."; + case (0x0400+BLE_SM_ERR_PAIR_NOT_SUPP ): + return "Pairing is not supported by the device."; + case (0x0400+BLE_SM_ERR_ENC_KEY_SZ ): + return "The resultant encryption key size is insufficient for the security requirements of this device."; + case (0x0400+BLE_SM_ERR_CMD_NOT_SUPP ): + return "The SMP command received is not supported on this device."; + case (0x0400+BLE_SM_ERR_UNSPECIFIED ): + return "Pairing failed due to an unspecified reason."; + case (0x0400+BLE_SM_ERR_REPEATED ): + return "Pairing or authentication procedure disallowed, too little time has elapsed since last pairing request or security request."; + case (0x0400+BLE_SM_ERR_INVAL ): + return "Command length is invalid or that a parameter is outside of the specified range."; + case (0x0400+BLE_SM_ERR_DHKEY ): + return "DHKey Check value received doesn't match the one calculated by the local device."; + case (0x0400+BLE_SM_ERR_NUMCMP ): + return "Confirm values in the numeric comparison protocol do not match."; + case (0x0400+BLE_SM_ERR_ALREADY ): + return "Pairing over the LE transport failed - Pairing Request sent over the BR/EDR transport in process."; + case (0x0400+BLE_SM_ERR_CROSS_TRANS ): + return "BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport."; + case (0x0500+BLE_SM_ERR_PASSKEY ): + return "The user input of passkey failed or the user cancelled the operation."; + case (0x0500+BLE_SM_ERR_OOB ): + return "The OOB data is not available."; + case (0x0500+BLE_SM_ERR_AUTHREQ ): + return "The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices."; + case (0x0500+BLE_SM_ERR_CONFIRM_MISMATCH ): + return "The confirm value does not match the calculated compare value."; + case (0x0500+BLE_SM_ERR_PAIR_NOT_SUPP ): + return "Pairing is not supported by the device."; + case (0x0500+BLE_SM_ERR_ENC_KEY_SZ ): + return "The resultant encryption key size is insufficient for the security requirements of this device."; + case (0x0500+BLE_SM_ERR_CMD_NOT_SUPP ): + return "The SMP command received is not supported on this device."; + case (0x0500+BLE_SM_ERR_UNSPECIFIED ): + return "Pairing failed due to an unspecified reason."; + case (0x0500+BLE_SM_ERR_REPEATED ): + return "Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request."; + case (0x0500+BLE_SM_ERR_INVAL ): + return "Command length is invalid or a parameter is outside of the specified range."; + case (0x0500+BLE_SM_ERR_DHKEY ): + return "Indicates to the remote device that the DHKey Check value received doesn’t match the one calculated by the local device."; + case (0x0500+BLE_SM_ERR_NUMCMP ): + return "Confirm values in the numeric comparison protocol do not match."; + case (0x0500+BLE_SM_ERR_ALREADY ): + return "Pairing over the LE transport failed - Pairing Request sent over the BR/EDR transport in process."; + case (0x0500+BLE_SM_ERR_CROSS_TRANS ): + return "BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport."; + + default: + return "Unknown"; + } +} + +/** + * @brief Convert the BLE Advertising Data flags to a string. + * @param adFlags The flags to convert + * @return std::string A string representation of the advertising flags. + */ + +const char* NimBLEUtils::advTypeToString(uint8_t advType) { + switch(advType) { + case BLE_HCI_ADV_TYPE_ADV_IND : //0 + return "Undirected - Connectable / Scannable"; + case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD: //1 + return "Directed High Duty - Connectable"; + case BLE_HCI_ADV_TYPE_ADV_SCAN_IND: //2 + return "Non-Connectable - Scan Response Available"; + case BLE_HCI_ADV_TYPE_ADV_NONCONN_IND: //3 + return "Non-Connectable - No Scan Response"; + case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD: //4 + return "Directed Low Duty - Connectable"; + default: + return "Unknown flag"; + } + +} // adFlagsToString + + +/** + * @brief Create a hex representation of data. + * + * @param [in] target Where to write the hex string. If this is null, we malloc storage. + * @param [in] source The start of the binary data. + * @param [in] length The length of the data to convert. + * @return A pointer to the formatted buffer. + */ +char* NimBLEUtils::buildHexData(uint8_t* target, uint8_t* source, uint8_t length) { + // Guard against too much data. + if (length > 100) length = 100; + + if (target == nullptr) { + target = (uint8_t*) malloc(length * 2 + 1); + if (target == nullptr) { + NIMBLE_LOGE(LOG_TAG, "buildHexData: malloc failed"); + return nullptr; + } + } + char* startOfData = (char*) target; + + for (int i = 0; i < length; i++) { + sprintf((char*) target, "%.2x", (char) *source); + source++; + target += 2; + } + + // Handle the special case where there was no data. + if (length == 0) { + *startOfData = 0; + } + + return startOfData; +} // buildHexData + + + +void NimBLEUtils::dumpGapEvent(ble_gap_event *event, void *arg){ + NIMBLE_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event->type)); +} + +/** + * @brief Convert a BT GAP event type to a string representation. + * @param [in] eventType The type of event. + * @return A string representation of the event type. + */ +const char* NimBLEUtils::gapEventToString(uint8_t eventType) { + switch (eventType) { + case BLE_GAP_EVENT_CONNECT : //0 + return "BLE_GAP_EVENT_CONNECT "; + + case BLE_GAP_EVENT_DISCONNECT: //1 + return "BLE_GAP_EVENT_DISCONNECT"; + + case BLE_GAP_EVENT_CONN_UPDATE: //3 + return "BLE_GAP_EVENT_CONN_UPDATE"; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: //4 + return "BLE_GAP_EVENT_CONN_UPDATE_REQ"; + + case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: //5 + return "BLE_GAP_EVENT_L2CAP_UPDATE_REQ"; + + case BLE_GAP_EVENT_TERM_FAILURE: //6 + return "BLE_GAP_EVENT_TERM_FAILURE"; + + case BLE_GAP_EVENT_DISC: //7 + return "BLE_GAP_EVENT_DISC"; + + case BLE_GAP_EVENT_DISC_COMPLETE: //8 + return "BLE_GAP_EVENT_DISC_COMPLETE"; + + case BLE_GAP_EVENT_ADV_COMPLETE: //9 + return "BLE_GAP_EVENT_ADV_COMPLETE"; + + case BLE_GAP_EVENT_ENC_CHANGE: //10 + return "BLE_GAP_EVENT_ENC_CHANGE"; + + case BLE_GAP_EVENT_PASSKEY_ACTION : //11 + return "BLE_GAP_EVENT_PASSKEY_ACTION"; + + case BLE_GAP_EVENT_NOTIFY_RX: //12 + return "BLE_GAP_EVENT_NOTIFY_RX"; + + case BLE_GAP_EVENT_NOTIFY_TX : //13 + return "BLE_GAP_EVENT_NOTIFY_TX"; + + case BLE_GAP_EVENT_SUBSCRIBE : //14 + return "BLE_GAP_EVENT_SUBSCRIBE"; + + case BLE_GAP_EVENT_MTU: //15 + return "BLE_GAP_EVENT_MTU"; + + case BLE_GAP_EVENT_IDENTITY_RESOLVED: //16 + return "BLE_GAP_EVENT_IDENTITY_RESOLVED"; + + case BLE_GAP_EVENT_REPEAT_PAIRING: //17 + return "BLE_GAP_EVENT_REPEAT_PAIRING"; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: //18 + return "BLE_GAP_EVENT_PHY_UPDATE_COMPLETE"; + + case BLE_GAP_EVENT_EXT_DISC: //19 + return "BLE_GAP_EVENT_EXT_DISC"; +#ifdef BLE_GAP_EVENT_PERIODIC_SYNC // IDF 4.0 does not support these + case BLE_GAP_EVENT_PERIODIC_SYNC: //20 + return "BLE_GAP_EVENT_PERIODIC_SYNC"; + + case BLE_GAP_EVENT_PERIODIC_REPORT: //21 + return "BLE_GAP_EVENT_PERIODIC_REPORT"; + + case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: //22 + return "BLE_GAP_EVENT_PERIODIC_SYNC_LOST"; + + case BLE_GAP_EVENT_SCAN_REQ_RCVD: //23 + return "BLE_GAP_EVENT_SCAN_REQ_RCVD"; +#endif + default: + NIMBLE_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); + return "Unknown event type"; + } +} // gapEventToString + + +/** + * Utility function to log an array of bytes. + */ +void print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void print_mbuf(const struct os_mbuf *om) +{ + int colon; + + colon = 0; + while (om != NULL) { + if (colon) { + MODLOG_DFLT(DEBUG, ":"); + } else { + colon = 1; + } + print_bytes(om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + } +} + +char *addr_str(const void *addr) +{ + static char buf[6 * 2 + 5 + 1]; + const uint8_t *u8p; + + u8p = (const uint8_t*)addr; + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); + + return buf; +} + +void print_uuid(const ble_uuid_t *uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf)); +} + +void print_adv_fields(const struct ble_hs_adv_fields *fields) +{ + char s[BLE_HS_ADV_MAX_SZ]; + const uint8_t *u8p; + int i; + + if (fields->flags != 0) { + MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags); + } + + if (fields->uuids16 != NULL) { + MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=", + fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) { + print_uuid(&fields->uuids16[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids32 != NULL) { + MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=", + fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) { + print_uuid(&fields->uuids32[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids128 != NULL) { + MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=", + fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) { + print_uuid(&fields->uuids128[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->name != NULL) { + assert(fields->name_len < sizeof s - 1); + memcpy(s, fields->name, fields->name_len); + s[fields->name_len] = '\0'; + MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n", + fields->name_is_complete ? "" : "in", s); + } + + if (fields->tx_pwr_lvl_is_present) { + MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) { + MODLOG_DFLT(DEBUG, " slave_itvl_range="); + print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid16 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->public_tgt_addr != NULL) { + MODLOG_DFLT(DEBUG, " public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p)); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->appearance_is_present) { + MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) { + MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid128 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uri != NULL) { + MODLOG_DFLT(DEBUG, " uri="); + print_bytes(fields->uri, fields->uri_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->mfg_data != NULL) { + MODLOG_DFLT(DEBUG, " mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + MODLOG_DFLT(DEBUG, "\n"); + } +} + + + /** + * Logs information about a connection to the console. + */ +void print_conn_desc(const struct ble_gap_conn_desc *desc) +{ + MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ", + desc->conn_handle, desc->our_ota_addr.type, + addr_str(desc->our_ota_addr.val)); + MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ", + desc->our_id_addr.type, addr_str(desc->our_id_addr.val)); + MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ", + desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val)); + MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ", + desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val)); + MODLOG_DFLT(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + + +void print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = (uint8_t*)addr; + MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} + +#endif //CONFIG_BT_ENABLED \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUtils.h b/libesp32/NimBLE-Arduino/src/NimBLEUtils.h new file mode 100644 index 000000000..3c7021db0 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEUtils.h @@ -0,0 +1,37 @@ +/* + * NimBLEUtils.h + * + * Created: on Jan 25 2020 + * Author H2zero + * + */ + +#ifndef COMPONENTS_NIMBLEUTILS_H_ +#define COMPONENTS_NIMBLEUTILS_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "host/ble_gap.h" + +extern "C"{ +char *addr_str(const void *addr); +void print_conn_desc(const struct ble_gap_conn_desc *desc); +void print_adv_fields(const struct ble_hs_adv_fields *fields); +void print_addr(const void *addr); +void print_bytes(const uint8_t *bytes, int len); +} + +class NimBLEUtils { +public: + static void dumpGapEvent(ble_gap_event *event, void *arg); + static const char* gapEventToString(uint8_t eventType); + static char* buildHexData(uint8_t* target, uint8_t* source, uint8_t length); + static const char* advTypeToString(uint8_t advType); + static const char* returnCodeToString(int rc); + static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size); + static int checkConnParams(ble_gap_conn_params* params); +}; + + +#endif // CONFIG_BT_ENABLED +#endif // COMPONENTS_NIMBLEUTILS_H_ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEValue.cpp b/libesp32/NimBLE-Arduino/src/NimBLEValue.cpp new file mode 100644 index 000000000..808812bc3 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEValue.cpp @@ -0,0 +1,140 @@ +/* + * NimNimBLEValue.cpp + * + * Created: on March 6, 2020 + * Author H2zero + * + * Originally: + * + * BLEValue.cpp + * + * Created on: Jul 17, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEValue.h" +#include "NimBLELog.h" + +static const char* LOG_TAG="NimBLEValue"; + +NimBLEValue::NimBLEValue() { + m_accumulation = ""; + m_value = ""; + m_readOffset = 0; +} // NimBLEValue + + +/** + * @brief Add a message part to the accumulation. + * The accumulation is a growing set of data that is added to until a commit or cancel. + * @param [in] part A message part being added. + */ +void NimBLEValue::addPart(std::string part) { + NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", part.length()); + m_accumulation += part; +} // addPart + + +/** + * @brief Add a message part to the accumulation. + * The accumulation is a growing set of data that is added to until a commit or cancel. + * @param [in] pData A message part being added. + * @param [in] length The number of bytes being added. + */ +void NimBLEValue::addPart(uint8_t* pData, size_t length) { + NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", length); + m_accumulation += std::string((char*) pData, length); +} // addPart + + +/** + * @brief Cancel the current accumulation. + */ +void NimBLEValue::cancel() { + NIMBLE_LOGD(LOG_TAG, ">> cancel"); + m_accumulation = ""; + m_readOffset = 0; +} // cancel + + +/** + * @brief Commit the current accumulation. + * When writing a value, we may find that we write it in "parts" meaning that the writes come in in pieces + * of the overall message. After the last part has been received, we may perform a commit which means that + * we now have the complete message and commit the change as a unit. + */ +void NimBLEValue::commit() { + NIMBLE_LOGD(LOG_TAG, ">> commit"); + // If there is nothing to commit, do nothing. + if (m_accumulation.length() == 0) return; + setValue(m_accumulation); + m_accumulation = ""; + m_readOffset = 0; +} // commit + + +/** + * @brief Get a pointer to the data. + * @return A pointer to the data. + */ +uint8_t* NimBLEValue::getData() { + return (uint8_t*) m_value.data(); +} + + +/** + * @brief Get the length of the data in bytes. + * @return The length of the data in bytes. + */ +size_t NimBLEValue::getLength() { + return m_value.length(); +} // getLength + + +/** + * @brief Get the read offset. + * @return The read offset into the read. + */ +uint16_t NimBLEValue::getReadOffset() { + return m_readOffset; +} // getReadOffset + + +/** + * @brief Get the current value. + */ +std::string NimBLEValue::getValue() { + return m_value; +} // getValue + + +/** + * @brief Set the read offset + * @param [in] readOffset The offset into the read. + */ +void NimBLEValue::setReadOffset(uint16_t readOffset) { + m_readOffset = readOffset; +} // setReadOffset + + +/** + * @brief Set the current value. + */ +void NimBLEValue::setValue(std::string value) { + m_value = value; +} // setValue + + +/** + * @brief Set the current value. + * @param [in] pData The data for the current value. + * @param [in] The length of the new current value. + */ +void NimBLEValue::setValue(uint8_t* pData, size_t length) { + m_value = std::string((char*) pData, length); +} // setValue + + +#endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/NimBLEValue.h b/libesp32/NimBLE-Arduino/src/NimBLEValue.h new file mode 100644 index 000000000..2fa1fcb4e --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/NimBLEValue.h @@ -0,0 +1,46 @@ +/* + * NimBLEValue.h + * + * Created: on March 6, 2020 + * Author H2zero + * + * Originally: + * + * BLEValue.h + * + * Created on: Jul 17, 2017 + * Author: kolban + */ + +#ifndef MAIN_BLEVALUE_H_ +#define MAIN_BLEVALUE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include + +/** + * @brief The model of a %BLE value. + */ +class NimBLEValue { +public: + NimBLEValue(); + void addPart(std::string part); + void addPart(uint8_t* pData, size_t length); + void cancel(); + void commit(); + uint8_t* getData(); + size_t getLength(); + uint16_t getReadOffset(); + std::string getValue(); + void setReadOffset(uint16_t readOffset); + void setValue(std::string value); + void setValue(uint8_t* pData, size_t length); + +private: + std::string m_accumulation; + uint16_t m_readOffset; + std::string m_value; + +}; +#endif // CONFIG_BT_ENABLED +#endif /* MAIN_BLEVALUE_H_ */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/README.md b/libesp32/NimBLE-Arduino/src/README.md new file mode 100644 index 000000000..bbd17fc85 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/README.md @@ -0,0 +1,173 @@ + + +Apache Mynewt + +## Overview + +Apache NimBLE is an open-source Bluetooth 5.0 stack (both Host & Controller) +that completely replaces the proprietary SoftDevice on Nordic chipsets. It is +part of [Apache Mynewt project](https://github.com/apache/mynewt-core). + +Features highlight: + - Support for 251 byte packet size + - Support for all 4 roles concurrently - Broadcaster, Observer, Peripheral and Central + - Support for up to 32 simultaneous connections. + - Legacy and SC (secure connections) SMP support (pairing and bonding). + - Advertising Extensions. + - Periodic Advertising. + - Coded (aka Long Range) and 2M PHYs. + - Bluetooth Mesh. + +## Supported hardware + +Controller supports Nordic nRF51 and nRF52 chipsets. Host runs on any board +and architecture [supported](https://github.com/apache/mynewt-core#overview) +by Apache Mynewt OS. + + +## Browsing + +If you are browsing around the source tree, and want to see some of the +major functional chunks, here are a few pointers: + +- nimble/controller: Contains code for controller including Link Layer and HCI implementation +([controller](https://github.com/apache/mynewt-nimble/tree/master/nimble/controller)) + +- nimble/drivers: Contains drivers for supported radio transceivers (Nordic nRF51 and nRF52) +([drivers](https://github.com/apache/mynewt-nimble/tree/master/nimble/drivers)) + +- nimble/host: Contains code for host subsystem. This includes protocols like +L2CAP and ATT, support for HCI commands and events, Generic Access Profile (GAP), +Generic Attribute Profile (GATT) and Security Manager (SM). +([host](https://github.com/apache/mynewt-nimble/tree/master/nimble/host)) + +- nimble/host/mesh: Contains code for Bluetooth Mesh subsystem. +([mesh](https://github.com/apache/mynewt-nimble/tree/master/nimble/host/mesh)) + +- nimble/transport: Contains code for supported transport protocols between host +and controller. This includes UART, emSPI and RAM (used in combined build when +host and controller run on same CPU) +([transport](https://github.com/apache/mynewt-nimble/tree/master/nimble/transport)) + +- porting: Contains implementation of NimBLE Porting Layer (NPL) for supported +operating systems +([porting](https://github.com/apache/mynewt-nimble/tree/master/porting)) + +- ext: Contains external libraries used by NimBLE. Those are used if not +provided by OS +([ext](https://github.com/apache/mynewt-nimble/tree/master/ext)) + +- kernel: Contains the core of the RTOS ([kernel/os](https://github.com/apache/mynewt-core/tree/master/kernel/os)) + +## Sample Applications + +There are also some sample applications that show how to Apache Mynewt NimBLE +stack. These sample applications are located in the `apps/` directory of +Apache Mynewt [repo](https://github.com/apache/mynewt-core). Some examples: + +* [blecent](https://github.com/apache/mynewt-core/tree/master/apps/blecent): +A basic central device with no user interface. This application scans for +a peripheral that supports the alert notification service (ANS). Upon +discovering such a peripheral, blecent connects and performs a characteristic +read, characteristic write, and notification subscription. +* [blehci](https://github.com/apache/mynewt-core/tree/master/apps/blehci): +Implements a BLE controller-only application. A separate host-only +implementation, such as Linux's BlueZ, can interface with this application via +HCI over UART. +* [bleprph](https://github.com/apache/mynewt-core/tree/master/apps/bleprph): An + implementation of a minimal BLE peripheral. +* [btshell](https://github.com/apache/mynewt-core/tree/master/apps/btshell): A + shell-like application allowing to configure and use most of NimBLE + functionality from command line. +* [bleuart](https://github.com/apache/mynewt-core/tree/master/apps/bleuart): +Implements a simple BLE peripheral that supports the Nordic +UART / Serial Port Emulation service +(https://developer.nordicsemi.com/nRF5_SDK/nRF51_SDK_v8.x.x/doc/8.0.0/s110/html/a00072.html). +* [test](https://github.com/apache/mynewt-core/tree/master/apps/test): Test + project which can be compiled either with the simulator, or on a per-architecture basis. + Test will run all the package's unit tests. + +# Getting Help + +If you are having trouble using or contributing to Apache Mynewt NimBLE, or just +want to talk to a human about what you're working on, you can contact us via the +[developers mailing list](mailto:dev@mynewt.apache.org). + +Although not a formal channel, you can also find a number of core developers +on the #mynewt channel on Freenode IRC or #general channel on [Mynewt Slack](https://join.slack.com/mynewt/shared_invite/MTkwMTg1ODM1NTg5LTE0OTYxNzQ4NzQtZTU1YmNhYjhkMg) + +Also, be sure to checkout the [Frequently Asked Questions](https://mynewt.apache.org/faq/answers) +for some help troubleshooting first. + +# Contributing + +Anybody who works with Apache Mynewt can be a contributing member of the +community that develops and deploys it. The process of releasing an operating +system for microcontrollers is never done: and we welcome your contributions +to that effort. + +More information can be found at the Community section of the Apache Mynewt +website, located [here](https://mynewt.apache.org/community). + +## Pull Requests + +Apache Mynewt welcomes pull request via Github. Discussions are done on Github, +but depending on the topic, can also be relayed to the official Apache Mynewt +developer mailing list dev@mynewt.apache.org. + +If you are suggesting a new feature, please email the developer list directly, +with a description of the feature you are planning to work on. + +## Filing Bugs + +Bugs can be filed on the +[Apache Mynewt NimBLE Issues](https://github.com/apache/mynewt-nimble/issues). +Please label the issue as a "Bug". + +Where possible, please include a self-contained reproduction case! + +## Feature Requests + +Feature requests should also be filed on the +[Apache Mynewt NimBLE Bug Tracker](https://github.com/apache/mynewt-nimble/issues). +Please label the issue as a "Feature" or "Enhancement" depending on the scope. + +## Writing Tests + +We love getting newt tests! Apache Mynewt is a huge undertaking, and improving +code coverage is a win for every Apache Mynewt user. + + + +# License + +The code in this repository is all under either the Apache 2 license, or a +license compatible with the Apache 2 license. See the LICENSE file for more +information. diff --git a/libesp32/NimBLE-Arduino/src/RELEASE_NOTES.md b/libesp32/NimBLE-Arduino/src/RELEASE_NOTES.md new file mode 100644 index 000000000..cda8fe2e4 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/RELEASE_NOTES.md @@ -0,0 +1,27 @@ +# RELEASE NOTES + +16 July 2019 - Apache NimBLE v1.2.0 + +For full release notes, please visit the +[Apache Mynewt Wiki](https://cwiki.apache.org/confluence/display/MYNEWT/Release+Notes). + +Apache NimBLE is an open-source Bluetooth 5.0 stack (both Host & Controller) that completely +replaces the proprietary SoftDevice on Nordic chipsets. + +New features in this version of NimBLE include: + +* Perdiodic Advertising support with up to 1650 bytes of data (scanner and advertiser) +* Support for scan request notification in GAP API +* Updated host qualification ID +* Qualification related bugfixes +* GAP API doxygen documentation update +* BLE Mesh improvements - fixes and resync with latest Zephyr code +* RIOT OS port fixes and improvements +* btshell sample application improvements +* improvements for bttester application +* Controller duplicates filtering improvements +* Memory and CPU usage optimizations in controller + +If working on next-generation RTOS and Bluetooth protocol stack +sounds exciting to you, get in touch, by sending a mail to the Apache Mynewt +Developer's list, dev@mynewt.apache.org. diff --git a/libesp32/NimBLE-Arduino/src/console/console.h b/libesp32/NimBLE-Arduino/src/console/console.h new file mode 100644 index 000000000..342d6f699 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/console/console.h @@ -0,0 +1,21 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +#ifndef _CONSOLE_H +#define _CONSOLE_H + +#include + +#define console_printf printf + +#endif \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c b/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c new file mode 100644 index 000000000..8d994c444 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c @@ -0,0 +1,522 @@ +/* + * Copyright 2019 Espressif Systems (Shanghai) PTE LTD + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include "sysinit/sysinit.h" +#include "nimble/hci_common.h" +#include "host/ble_hs.h" +#include "nimble/nimble_port.h" +#include "nimble/nimble_port_freertos.h" +#include "esp_nimble_hci.h" +#include "esp_nimble_mem.h" +#include "esp_bt.h" +#include "freertos/semphr.h" +#include "esp_compiler.h" + +#define NIMBLE_VHCI_TIMEOUT_MS 2000 + +static ble_hci_trans_rx_cmd_fn *ble_hci_rx_cmd_hs_cb; +static void *ble_hci_rx_cmd_hs_arg; + +static ble_hci_trans_rx_acl_fn *ble_hci_rx_acl_hs_cb; +static void *ble_hci_rx_acl_hs_arg; + +static struct os_mbuf_pool ble_hci_acl_mbuf_pool; +static struct os_mempool_ext ble_hci_acl_pool; +/* + * The MBUF payload size must accommodate the HCI data header size plus the + * maximum ACL data packet length. The ACL block size is the size of the + * mbufs we will allocate. + */ +#define ACL_BLOCK_SIZE OS_ALIGN(MYNEWT_VAL(BLE_ACL_BUF_SIZE) \ + + BLE_MBUF_MEMBLOCK_OVERHEAD \ + + BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT) + +static os_membuf_t *ble_hci_acl_buf; + +static struct os_mempool ble_hci_cmd_pool; +static os_membuf_t *ble_hci_cmd_buf; + +static struct os_mempool ble_hci_evt_hi_pool; +static os_membuf_t *ble_hci_evt_hi_buf; + +static struct os_mempool ble_hci_evt_lo_pool; +static os_membuf_t *ble_hci_evt_lo_buf; + +static SemaphoreHandle_t vhci_send_sem; +const static char *TAG = "NimBLE"; + +int os_msys_buf_alloc(void); +void os_msys_buf_free(void); + +void ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_rx_cmd_hs_cb = cmd_cb; + ble_hci_rx_cmd_hs_arg = cmd_arg; + ble_hci_rx_acl_hs_cb = acl_cb; + ble_hci_rx_acl_hs_arg = acl_arg; +} + + +int ble_hci_trans_hs_cmd_tx(uint8_t *cmd) +{ + uint16_t len; + uint8_t rc = 0; + + assert(cmd != NULL); + *cmd = BLE_HCI_UART_H4_CMD; + len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1; + if (!esp_vhci_host_check_send_available()) { + ESP_LOGD(TAG, "Controller not ready to receive packets"); + } + + if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) { + esp_vhci_host_send_packet(cmd, len); + } else { + rc = BLE_HS_ETIMEOUT_HCI; + } + + ble_hci_trans_buf_free(cmd); + return rc; +} + +int ble_hci_trans_ll_evt_tx(uint8_t *hci_ev) +{ + int rc = ESP_FAIL; + + if (ble_hci_rx_cmd_hs_cb) { + rc = ble_hci_rx_cmd_hs_cb(hci_ev, ble_hci_rx_cmd_hs_arg); + } + return rc; +} + +int ble_hci_trans_hs_acl_tx(struct os_mbuf *om) +{ + uint16_t len = 0; + uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 1], rc = 0; + /* If this packet is zero length, just free it */ + if (OS_MBUF_PKTLEN(om) == 0) { + os_mbuf_free_chain(om); + return 0; + } + data[0] = BLE_HCI_UART_H4_ACL; + len++; + + if (!esp_vhci_host_check_send_available()) { + ESP_LOGD(TAG, "Controller not ready to receive packets"); + } + + os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]); + len += OS_MBUF_PKTLEN(om); + + if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) { + esp_vhci_host_send_packet(data, len); + } else { + rc = BLE_HS_ETIMEOUT_HCI; + } + + os_mbuf_free_chain(om); + + return rc; +} + +int ble_hci_trans_ll_acl_tx(struct os_mbuf *om) +{ + int rc = ESP_FAIL; + + if (ble_hci_rx_acl_hs_cb) { + rc = ble_hci_rx_acl_hs_cb(om, ble_hci_rx_acl_hs_arg); + } + return rc; +} + +uint8_t *ble_hci_trans_buf_alloc(int type) +{ + uint8_t *buf; + + switch (type) { + case BLE_HCI_TRANS_BUF_CMD: + buf = os_memblock_get(&ble_hci_cmd_pool); + break; + + case BLE_HCI_TRANS_BUF_EVT_HI: + buf = os_memblock_get(&ble_hci_evt_hi_pool); + if (buf == NULL) { + /* If no high-priority event buffers remain, try to grab a + * low-priority one. + */ + buf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + } + break; + + case BLE_HCI_TRANS_BUF_EVT_LO: + buf = os_memblock_get(&ble_hci_evt_lo_pool); + break; + + default: + assert(0); + buf = NULL; + } + + return buf; +} + +void ble_hci_trans_buf_free(uint8_t *buf) +{ + int rc; + /* XXX: this may look a bit odd, but the controller uses the command + * buffer to send back the command complete/status as an immediate + * response to the command. This was done to insure that the controller + * could always send back one of these events when a command was received. + * Thus, we check to see which pool the buffer came from so we can free + * it to the appropriate pool + */ + if (os_memblock_from(&ble_hci_evt_hi_pool, buf)) { + rc = os_memblock_put(&ble_hci_evt_hi_pool, buf); + assert(rc == 0); + } else if (os_memblock_from(&ble_hci_evt_lo_pool, buf)) { + rc = os_memblock_put(&ble_hci_evt_lo_pool, buf); + assert(rc == 0); + } else { + assert(os_memblock_from(&ble_hci_cmd_pool, buf)); + rc = os_memblock_put(&ble_hci_cmd_pool, buf); + assert(rc == 0); + } +} + +/** + * Unsupported; the RAM transport does not have a dedicated ACL data packet + * pool. + */ +int ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg) +{ + return BLE_ERR_UNSUPPORTED; +} + +int ble_hci_trans_reset(void) +{ + /* No work to do. All allocated buffers are owned by the host or + * controller, and they will get freed by their owners. + */ + return 0; +} + +/** + * Allocates a buffer (mbuf) for ACL operation. + * + * @return The allocated buffer on success; + * NULL on buffer exhaustion. + */ +static struct os_mbuf *ble_hci_trans_acl_buf_alloc(void) +{ + struct os_mbuf *m; + uint8_t usrhdr_len; + +#if MYNEWT_VAL(BLE_DEVICE) + usrhdr_len = sizeof(struct ble_mbuf_hdr); +#elif MYNEWT_VAL(BLE_HS_FLOW_CTRL) + usrhdr_len = BLE_MBUF_HS_HDR_LEN; +#else + usrhdr_len = 0; +#endif + + m = os_mbuf_get_pkthdr(&ble_hci_acl_mbuf_pool, usrhdr_len); + return m; +} + +static void ble_hci_rx_acl(uint8_t *data, uint16_t len) +{ + struct os_mbuf *m; + int sr; + if (len < BLE_HCI_DATA_HDR_SZ || len > MYNEWT_VAL(BLE_ACL_BUF_SIZE)) { + return; + } + + m = ble_hci_trans_acl_buf_alloc(); + + if (!m) { + return; + } + if (os_mbuf_append(m, data, len)) { + os_mbuf_free_chain(m); + return; + } + OS_ENTER_CRITICAL(sr); + if (ble_hci_rx_acl_hs_cb) { + ble_hci_rx_acl_hs_cb(m, NULL); + } + OS_EXIT_CRITICAL(sr); +} + +static void ble_hci_transport_init(void) +{ + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + rc = os_mempool_ext_init(&ble_hci_acl_pool, + MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE, + ble_hci_acl_buf, + "ble_hci_acl_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mbuf_pool_init(&ble_hci_acl_mbuf_pool, + &ble_hci_acl_pool.mpe_mp, + ACL_BLOCK_SIZE, + MYNEWT_VAL(BLE_ACL_BUF_COUNT)); + SYSINIT_PANIC_ASSERT(rc == 0); + + /* + * Create memory pool of HCI command buffers. NOTE: we currently dont + * allow this to be configured. The controller will only allow one + * outstanding command. We decided to keep this a pool in case we allow + * allow the controller to handle more than one outstanding command. + */ + rc = os_mempool_init(&ble_hci_cmd_pool, + 1, + BLE_HCI_TRANS_CMD_SZ, + ble_hci_cmd_buf, + "ble_hci_cmd_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_evt_hi_pool, + MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_evt_hi_buf, + "ble_hci_evt_hi_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_evt_lo_pool, + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_evt_lo_buf, + "ble_hci_evt_lo_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); +} + +/* + * @brief: BT controller callback function, used to notify the upper layer that + * controller is ready to receive command + */ +static void controller_rcv_pkt_ready(void) +{ + if (vhci_send_sem) { + xSemaphoreGive(vhci_send_sem); + } +} + +/* + * @brief: BT controller callback function, to transfer data packet to the host + */ +static int host_rcv_pkt(uint8_t *data, uint16_t len) +{ + + if (data[0] == BLE_HCI_UART_H4_EVT) { + uint8_t *evbuf; + int totlen; + int rc; + + totlen = BLE_HCI_EVENT_HDR_LEN + data[2]; + assert(totlen <= UINT8_MAX + BLE_HCI_EVENT_HDR_LEN); + + if (data[1] == BLE_HCI_EVCODE_HW_ERROR) { + assert(0); + } + + /* Allocate LE Advertising Report Event from lo pool only */ + if ((data[1] == BLE_HCI_EVCODE_LE_META) && (data[3] == BLE_HCI_LE_SUBEV_ADV_RPT)) { + evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + /* Skip advertising report if we're out of memory */ + if (!evbuf) { + return 0; + } + } else { + evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + assert(evbuf != NULL); + } + + memcpy(evbuf, &data[1], totlen); + + rc = ble_hci_trans_ll_evt_tx(evbuf); + assert(rc == 0); + } else if (data[0] == BLE_HCI_UART_H4_ACL) { + ble_hci_rx_acl(data + 1, len - 1); + } + return 0; +} + +static const esp_vhci_host_callback_t vhci_host_cb = { + .notify_host_send_available = controller_rcv_pkt_ready, + .notify_host_recv = host_rcv_pkt, +}; + +static void ble_buf_free(void) +{ + os_msys_buf_free(); + + nimble_platform_mem_free(ble_hci_evt_hi_buf); + ble_hci_evt_hi_buf = NULL; + nimble_platform_mem_free(ble_hci_evt_lo_buf); + ble_hci_evt_lo_buf = NULL; + nimble_platform_mem_free(ble_hci_cmd_buf); + ble_hci_cmd_buf = NULL; + nimble_platform_mem_free(ble_hci_acl_buf); + ble_hci_acl_buf = NULL; +} + +static esp_err_t ble_buf_alloc(void) +{ + if (os_msys_buf_alloc()) { + return ESP_ERR_NO_MEM; + } + + ble_hci_evt_hi_buf = (os_membuf_t *) nimble_platform_mem_calloc(1, + (sizeof(os_membuf_t) * OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)))); + + ble_hci_evt_lo_buf = (os_membuf_t *) nimble_platform_mem_calloc(1, + (sizeof(os_membuf_t) * OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)))); + + ble_hci_cmd_buf = (os_membuf_t *) nimble_platform_mem_calloc(1, + (sizeof(os_membuf_t) * OS_MEMPOOL_SIZE(1, BLE_HCI_TRANS_CMD_SZ))); + + ble_hci_acl_buf = (os_membuf_t *) nimble_platform_mem_calloc(1, + (sizeof(os_membuf_t) * OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE))); + + if (!ble_hci_evt_hi_buf || !ble_hci_evt_lo_buf || !ble_hci_cmd_buf || !ble_hci_acl_buf) { + ble_buf_free(); + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +esp_err_t esp_nimble_hci_init(void) +{ + esp_err_t ret; + + ret = ble_buf_alloc(); + if (ret != ESP_OK) { + goto err; + } + if ((ret = esp_vhci_host_register_callback(&vhci_host_cb)) != ESP_OK) { + goto err; + } + + ble_hci_transport_init(); + + vhci_send_sem = xSemaphoreCreateBinary(); + if (vhci_send_sem == NULL) { + ret = ESP_ERR_NO_MEM; + goto err; + } + + xSemaphoreGive(vhci_send_sem); + + return ret; +err: + ble_buf_free(); + return ret; + +} + +esp_err_t esp_nimble_hci_and_controller_init(void) +{ + esp_err_t ret; + + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + + if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { + return ret; + } + + if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) { + return ret; + } + return esp_nimble_hci_init(); +} + +static esp_err_t ble_hci_transport_deinit(void) +{ + int ret = 0; + + ret += os_mempool_clear(&ble_hci_evt_lo_pool); + + ret += os_mempool_clear(&ble_hci_evt_hi_pool); + + ret += os_mempool_clear(&ble_hci_cmd_pool); + + ret += os_mempool_ext_clear(&ble_hci_acl_pool); + + if (ret) { + return ESP_FAIL; + } else { + return ESP_OK; + } +} + +esp_err_t esp_nimble_hci_deinit(void) +{ + if (vhci_send_sem) { + /* Dummy take & give semaphore before deleting */ + xSemaphoreTake(vhci_send_sem, portMAX_DELAY); + xSemaphoreGive(vhci_send_sem); + vSemaphoreDelete(vhci_send_sem); + vhci_send_sem = NULL; + } + esp_err_t ret = ble_hci_transport_deinit(); + if (ret != ESP_OK) { + return ret; + } + + ble_buf_free(); + + return ESP_OK; +} + +esp_err_t esp_nimble_hci_and_controller_deinit(void) +{ + int ret; + ret = esp_nimble_hci_deinit(); + if (ret != ESP_OK) { + return ret; + } + + ret = esp_bt_controller_disable(); + if (ret != ESP_OK) { + return ret; + } + + ret = esp_bt_controller_deinit(); + if (ret != ESP_OK) { + return ret; + } + + return ESP_OK; +} \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/esp_compiler.h b/libesp32/NimBLE-Arduino/src/esp_compiler.h new file mode 100644 index 000000000..94ec29c23 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/esp_compiler.h @@ -0,0 +1,33 @@ +// Copyright 2016-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP_COMPILER_H +#define __ESP_COMPILER_H + +/* + * The likely and unlikely macro pairs: + * These macros are useful to place when application + * knows the majority ocurrence of a decision paths, + * placing one of these macros can hint the compiler + * to reorder instructions producing more optimized + * code. + */ +#if (CONFIG_COMPILER_OPTIMIZATION_PERF) +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#endif \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h b/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h new file mode 100644 index 000000000..16d4253be --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h @@ -0,0 +1,1103 @@ + +/* Modifications copyright (C) 2020 Ryan Powell */ + +#ifndef __ESP_NIMBLE_CFG__ +#define __ESP_NIMBLE_CFG__ +#include "nimconfig.h" + +/** + * This macro exists to ensure code includes this header when needed. If code + * checks the existence of a setting directly via ifdef without including this + * header, the setting macro will silently evaluate to 0. In contrast, an + * attempt to use these macros without including this header will result in a + * compiler error. + */ +#define MYNEWT_VAL(x) MYNEWT_VAL_ ## x + +/*** kernel/os */ +#ifndef MYNEWT_VAL_MSYS_1_BLOCK_COUNT +#ifdef CONFIG_BT_NIMBLE_MESH +#define MYNEWT_VAL_MSYS_1_BLOCK_COUNT (CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT + 8) +#else +#define MYNEWT_VAL_MSYS_1_BLOCK_COUNT CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT +#endif +#endif + +#ifndef MYNEWT_VAL_MSYS_1_BLOCK_SIZE +#define MYNEWT_VAL_MSYS_1_BLOCK_SIZE (292) +#endif + +#ifndef MYNEWT_VAL_MSYS_2_BLOCK_COUNT +#define MYNEWT_VAL_MSYS_2_BLOCK_COUNT (0) +#endif + +#ifndef MYNEWT_VAL_MSYS_2_BLOCK_SIZE +#define MYNEWT_VAL_MSYS_2_BLOCK_SIZE (0) +#endif + +#ifndef MYNEWT_VAL_OS_CPUTIME_FREQ +#define MYNEWT_VAL_OS_CPUTIME_FREQ (1000000) +#endif + +#ifndef MYNEWT_VAL_OS_CPUTIME_TIMER_NUM +#define MYNEWT_VAL_OS_CPUTIME_TIMER_NUM (0) +#endif + +/*** nimble */ +#ifndef MYNEWT_VAL_BLE_EXT_ADV +#define MYNEWT_VAL_BLE_EXT_ADV (0) +#endif + +#ifndef MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE +#define MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE (31) +#endif + +#ifndef MYNEWT_VAL_BLE_MAX_CONNECTIONS +#define MYNEWT_VAL_BLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS +#endif + +#ifndef MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES +#define MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MAX_PERIODIC_SYNCS +#define MYNEWT_VAL_BLE_MAX_PERIODIC_SYNCS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_ROLE_BROADCASTER +#ifdef CONFIG_BT_NIMBLE_ROLE_BROADCASTER +#define MYNEWT_VAL_BLE_ROLE_BROADCASTER (1) +#else +#define MYNEWT_VAL_BLE_ROLE_BROADCASTER (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_ROLE_CENTRAL +#ifdef CONFIG_BT_NIMBLE_ROLE_CENTRAL +#define MYNEWT_VAL_BLE_ROLE_CENTRAL (1) +#else +#define MYNEWT_VAL_BLE_ROLE_CENTRAL (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_ROLE_OBSERVER +#ifdef CONFIG_BT_NIMBLE_ROLE_OBSERVER +#define MYNEWT_VAL_BLE_ROLE_OBSERVER (1) +#else +#define MYNEWT_VAL_BLE_ROLE_OBSERVER (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_ROLE_PERIPHERAL +#ifdef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#define MYNEWT_VAL_BLE_ROLE_PERIPHERAL (1) +#else +#define MYNEWT_VAL_BLE_ROLE_PERIPHERAL (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_WHITELIST +#define MYNEWT_VAL_BLE_WHITELIST (1) +#endif + +/*** @apache-mynewt-nimble/nimble/controller */ +#ifndef MYNEWT_VAL_BLE_DEVICE +#define MYNEWT_VAL_BLE_DEVICE (0) +#endif + +/* Overridden by @apache-mynewt-nimble/nimble/controller (defined by @apache-mynewt-nimble/nimble/controller) */ +#ifndef MYNEWT_VAL_BLE_HW_WHITELIST_ENABLE +#define MYNEWT_VAL_BLE_HW_WHITELIST_ENABLE (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_ADD_STRICT_SCHED_PERIODS +#define MYNEWT_VAL_BLE_LL_ADD_STRICT_SCHED_PERIODS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_CONN_PARAM_REQ +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_CONN_PARAM_REQ (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_DATA_LEN_EXT +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_DATA_LEN_EXT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_EXT_SCAN_FILT +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_EXT_SCAN_FILT (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_2M_PHY +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_2M_PHY (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_CODED_PHY +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_CODED_PHY (0) +#endif + +/* Overridden by @apache-mynewt-nimble/nimble/controller (defined by @apache-mynewt-nimble/nimble/controller) */ +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_CSA2 +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_CSA2 (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_ENCRYPTION +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_ENCRYPTION (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_PING +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_PING (MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_ENCRYPTION) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_EXT_ADV +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_EXT_ADV (MYNEWT_VAL_BLE_EXT_ADV) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_PRIVACY +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_PRIVACY (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CONN_INIT_MAX_TX_BYTES +#define MYNEWT_VAL_BLE_LL_CONN_INIT_MAX_TX_BYTES (27) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CONN_INIT_MIN_WIN_OFFSET +#define MYNEWT_VAL_BLE_LL_CONN_INIT_MIN_WIN_OFFSET (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CONN_INIT_SLOTS +#define MYNEWT_VAL_BLE_LL_CONN_INIT_SLOTS (4) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_DIRECT_TEST_MODE +#define MYNEWT_VAL_BLE_LL_DIRECT_TEST_MODE (0) +#endif + +/* Overridden by @apache-mynewt-nimble/nimble/controller (defined by @apache-mynewt-nimble/nimble/controller) */ +#ifndef MYNEWT_VAL_BLE_LL_EXT_ADV_AUX_PTR_CNT +#define MYNEWT_VAL_BLE_LL_EXT_ADV_AUX_PTR_CNT (5) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_MASTER_SCA +#define MYNEWT_VAL_BLE_LL_MASTER_SCA (4) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_MAX_PKT_SIZE +#define MYNEWT_VAL_BLE_LL_MAX_PKT_SIZE (251) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_MFRG_ID +#define MYNEWT_VAL_BLE_LL_MFRG_ID (0xFFFF) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_NUM_SCAN_DUP_ADVS +#define MYNEWT_VAL_BLE_LL_NUM_SCAN_DUP_ADVS (8) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_NUM_SCAN_RSP_ADVS +#define MYNEWT_VAL_BLE_LL_NUM_SCAN_RSP_ADVS (8) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_OUR_SCA +#define MYNEWT_VAL_BLE_LL_OUR_SCA (60) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_PRIO +#define MYNEWT_VAL_BLE_LL_PRIO (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_RESOLV_LIST_SIZE +#define MYNEWT_VAL_BLE_LL_RESOLV_LIST_SIZE (4) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_RNG_BUFSIZE +#define MYNEWT_VAL_BLE_LL_RNG_BUFSIZE (32) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_STRICT_CONN_SCHEDULING +#define MYNEWT_VAL_BLE_LL_STRICT_CONN_SCHEDULING (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_SUPP_MAX_RX_BYTES +#define MYNEWT_VAL_BLE_LL_SUPP_MAX_RX_BYTES (MYNEWT_VAL_BLE_LL_MAX_PKT_SIZE) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_SUPP_MAX_TX_BYTES +#define MYNEWT_VAL_BLE_LL_SUPP_MAX_TX_BYTES (MYNEWT_VAL_BLE_LL_MAX_PKT_SIZE) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_SYSVIEW +#define MYNEWT_VAL_BLE_LL_SYSVIEW (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_TX_PWR_DBM +#define MYNEWT_VAL_BLE_LL_TX_PWR_DBM (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_USECS_PER_PERIOD +#define MYNEWT_VAL_BLE_LL_USECS_PER_PERIOD (3250) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_VND_EVENT_ON_ASSERT +#define MYNEWT_VAL_BLE_LL_VND_EVENT_ON_ASSERT (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_WHITELIST_SIZE +#define MYNEWT_VAL_BLE_LL_WHITELIST_SIZE (8) +#endif + +#ifndef MYNEWT_VAL_BLE_LP_CLOCK +#define MYNEWT_VAL_BLE_LP_CLOCK (1) +#endif + +#ifndef MYNEWT_VAL_BLE_NUM_COMP_PKT_RATE +#define MYNEWT_VAL_BLE_NUM_COMP_PKT_RATE ((2 * OS_TICKS_PER_SEC)) +#endif + +#ifndef MYNEWT_VAL_BLE_PUBLIC_DEV_ADDR +#define MYNEWT_VAL_BLE_PUBLIC_DEV_ADDR ((uint8_t[6]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +#endif + +#ifndef MYNEWT_VAL_BLE_XTAL_SETTLE_TIME +#define MYNEWT_VAL_BLE_XTAL_SETTLE_TIME (0) +#endif + +/*** @apache-mynewt-nimble/nimble/host */ +#ifndef MYNEWT_VAL_BLE_ATT_PREFERRED_MTU +#define MYNEWT_VAL_BLE_ATT_PREFERRED_MTU CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU +#endif + +#ifndef MYNEWT_VAL_BLE_PERIODIC_ADV +#define MYNEWT_VAL_BLE_PERIODIC_ADV (0) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_FIND_INFO +#define MYNEWT_VAL_BLE_ATT_SVR_FIND_INFO (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_FIND_TYPE +#define MYNEWT_VAL_BLE_ATT_SVR_FIND_TYPE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_INDICATE +#define MYNEWT_VAL_BLE_ATT_SVR_INDICATE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_MAX_PREP_ENTRIES +#define MYNEWT_VAL_BLE_ATT_SVR_MAX_PREP_ENTRIES (64) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_NOTIFY +#define MYNEWT_VAL_BLE_ATT_SVR_NOTIFY (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_QUEUED_WRITE +#define MYNEWT_VAL_BLE_ATT_SVR_QUEUED_WRITE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_QUEUED_WRITE_TMO +#define MYNEWT_VAL_BLE_ATT_SVR_QUEUED_WRITE_TMO (30000) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ +#define MYNEWT_VAL_BLE_ATT_SVR_READ (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ_BLOB +#define MYNEWT_VAL_BLE_ATT_SVR_READ_BLOB (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ_GROUP_TYPE +#define MYNEWT_VAL_BLE_ATT_SVR_READ_GROUP_TYPE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ_MULT +#define MYNEWT_VAL_BLE_ATT_SVR_READ_MULT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ_TYPE +#define MYNEWT_VAL_BLE_ATT_SVR_READ_TYPE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_SIGNED_WRITE +#define MYNEWT_VAL_BLE_ATT_SVR_SIGNED_WRITE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_WRITE +#define MYNEWT_VAL_BLE_ATT_SVR_WRITE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_WRITE_NO_RSP +#define MYNEWT_VAL_BLE_ATT_SVR_WRITE_NO_RSP (1) +#endif + +#ifndef MYNEWT_VAL_BLE_GAP_MAX_PENDING_CONN_PARAM_UPDATE +#define MYNEWT_VAL_BLE_GAP_MAX_PENDING_CONN_PARAM_UPDATE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_DISC_ALL_CHRS +#define MYNEWT_VAL_BLE_GATT_DISC_ALL_CHRS (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_DISC_ALL_DSCS +#define MYNEWT_VAL_BLE_GATT_DISC_ALL_DSCS (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_DISC_ALL_SVCS +#define MYNEWT_VAL_BLE_GATT_DISC_ALL_SVCS (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_DISC_CHR_UUID +#define MYNEWT_VAL_BLE_GATT_DISC_CHR_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_DISC_SVC_UUID +#define MYNEWT_VAL_BLE_GATT_DISC_SVC_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_FIND_INC_SVCS +#define MYNEWT_VAL_BLE_GATT_FIND_INC_SVCS (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_INDICATE +#define MYNEWT_VAL_BLE_GATT_INDICATE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_MAX_PROCS +#define MYNEWT_VAL_BLE_GATT_MAX_PROCS (4) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_NOTIFY +#define MYNEWT_VAL_BLE_GATT_NOTIFY (1) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_READ +#define MYNEWT_VAL_BLE_GATT_READ (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_READ_LONG +#define MYNEWT_VAL_BLE_GATT_READ_LONG (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_READ_MAX_ATTRS +#define MYNEWT_VAL_BLE_GATT_READ_MAX_ATTRS (8) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_READ_MULT +#define MYNEWT_VAL_BLE_GATT_READ_MULT (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_READ_UUID +#define MYNEWT_VAL_BLE_GATT_READ_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_RESUME_RATE +#define MYNEWT_VAL_BLE_GATT_RESUME_RATE (1000) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_SIGNED_WRITE +#define MYNEWT_VAL_BLE_GATT_SIGNED_WRITE (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_WRITE +#define MYNEWT_VAL_BLE_GATT_WRITE (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_WRITE_LONG +#define MYNEWT_VAL_BLE_GATT_WRITE_LONG (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_WRITE_MAX_ATTRS +#define MYNEWT_VAL_BLE_GATT_WRITE_MAX_ATTRS (4) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_WRITE_NO_RSP +#define MYNEWT_VAL_BLE_GATT_WRITE_NO_RSP (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_WRITE_RELIABLE +#define MYNEWT_VAL_BLE_GATT_WRITE_RELIABLE (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_HOST +#define MYNEWT_VAL_BLE_HOST (1) +#endif + +#ifndef MYNEWT_VAL_ESP_BLE_MESH +#ifdef CONFIG_BLE_MESH_HCI_5_0 +#define MYNEWT_VAL_ESP_BLE_MESH (1) +#else +#define MYNEWT_VAL_ESP_BLE_MESH (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_HS_DEBUG +#ifdef CONFIG_BT_NIMBLE_DEBUG +#define MYNEWT_VAL_BLE_HS_DEBUG (1) +#else +#define MYNEWT_VAL_BLE_HS_DEBUG (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS +#ifdef CONFIG_BT_NIMBLE_SM_SC_DEBUG_KEYS +#define MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS (1) +#else +#define MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_HS_AUTO_START +#define MYNEWT_VAL_BLE_HS_AUTO_START (1) +#endif + +#ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL +#ifdef CONFIG_BT_NIMBLE_HS_FLOW_CTRL +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL (1) +#else +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL_ITVL +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_ITVL CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL +#endif + +#ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL_THRESH +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_THRESH CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH +#endif + +#ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL_TX_ON_DISCONNECT +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_TX_ON_DISCONNECT CONFIG_BT_NIMBLE_FLOW_CTRL_TX_ON_DISCONNECT +#endif + +#ifndef MYNEWT_VAL_BLE_HS_PHONY_HCI_ACKS +#define MYNEWT_VAL_BLE_HS_PHONY_HCI_ACKS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_HS_REQUIRE_OS +#define MYNEWT_VAL_BLE_HS_REQUIRE_OS (1) +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM +#define MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS +#define MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS (1) +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_MAX_CHANS +#define MYNEWT_VAL_BLE_L2CAP_MAX_CHANS (3*MYNEWT_VAL_BLE_MAX_CONNECTIONS) +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_RX_FRAG_TIMEOUT +#define MYNEWT_VAL_BLE_L2CAP_RX_FRAG_TIMEOUT (30000) +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_SIG_MAX_PROCS +#define MYNEWT_VAL_BLE_L2CAP_SIG_MAX_PROCS (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH +#ifdef CONFIG_BT_NIMBLE_MESH +#define MYNEWT_VAL_BLE_MESH (1) +#else +#define MYNEWT_VAL_BLE_MESH (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_CONSOLE_BUFFER_SIZE +#define MYNEWT_VAL_BLE_MONITOR_CONSOLE_BUFFER_SIZE (128) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_RTT +#define MYNEWT_VAL_BLE_MONITOR_RTT (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_RTT_BUFFERED +#define MYNEWT_VAL_BLE_MONITOR_RTT_BUFFERED (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_RTT_BUFFER_NAME +#define MYNEWT_VAL_BLE_MONITOR_RTT_BUFFER_NAME ("monitor") +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_RTT_BUFFER_SIZE +#define MYNEWT_VAL_BLE_MONITOR_RTT_BUFFER_SIZE (256) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_UART +#define MYNEWT_VAL_BLE_MONITOR_UART (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_UART_BAUDRATE +#define MYNEWT_VAL_BLE_MONITOR_UART_BAUDRATE (1000000) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_UART_BUFFER_SIZE +#define MYNEWT_VAL_BLE_MONITOR_UART_BUFFER_SIZE (64) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_UART_DEV +#define MYNEWT_VAL_BLE_MONITOR_UART_DEV ("uart0") +#endif + +#ifndef MYNEWT_VAL_BLE_HOST_BASED_PRIVACY +#define MYNEWT_VAL_BLE_HOST_BASED_PRIVACY (1) +#endif + +#ifndef MYNEWT_VAL_BLE_RPA_TIMEOUT +#define MYNEWT_VAL_BLE_RPA_TIMEOUT (CONFIG_BT_NIMBLE_RPA_TIMEOUT) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_BONDING +#define MYNEWT_VAL_BLE_SM_BONDING (1) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_IO_CAP +#define MYNEWT_VAL_BLE_SM_IO_CAP (BLE_HS_IO_NO_INPUT_OUTPUT) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_KEYPRESS +#define MYNEWT_VAL_BLE_SM_KEYPRESS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_LEGACY +#ifdef CONFIG_BT_NIMBLE_SM_LEGACY +#define MYNEWT_VAL_BLE_SM_LEGACY (1) +#else +#define MYNEWT_VAL_BLE_SM_LEGACY (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_SM_MAX_PROCS +#define MYNEWT_VAL_BLE_SM_MAX_PROCS (1) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_MITM +#define MYNEWT_VAL_BLE_SM_MITM (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_OOB_DATA_FLAG +#define MYNEWT_VAL_BLE_SM_OOB_DATA_FLAG (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_OUR_KEY_DIST +#define MYNEWT_VAL_BLE_SM_OUR_KEY_DIST (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_SC +#ifdef CONFIG_BT_NIMBLE_SM_SC +#define MYNEWT_VAL_BLE_SM_SC (1) +#else +#define MYNEWT_VAL_BLE_SM_SC (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST +#define MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST (0) +#endif + +#ifndef MYNEWT_VAL_BLE_CRYPTO_STACK_MBEDTLS +#define MYNEWT_VAL_BLE_CRYPTO_STACK_MBEDTLS (CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS) +#endif + +#ifndef MYNEWT_VAL_BLE_STORE_MAX_BONDS +#define MYNEWT_VAL_BLE_STORE_MAX_BONDS CONFIG_BT_NIMBLE_MAX_BONDS +#endif + +#ifndef MYNEWT_VAL_BLE_STORE_MAX_CCCDS +#define MYNEWT_VAL_BLE_STORE_MAX_CCCDS CONFIG_BT_NIMBLE_MAX_CCCDS +#endif + +#ifndef MYNEWT_VAL_BLE_STORE_CONFIG_PERSIST +#ifdef CONFIG_BT_NIMBLE_NVS_PERSIST +#define MYNEWT_VAL_BLE_STORE_CONFIG_PERSIST (1) +#else +#define MYNEWT_VAL_BLE_STORE_CONFIG_PERSIST (0) +#endif +#endif + +/*** nimble/host/services/ans */ +#ifndef MYNEWT_VAL_BLE_SVC_ANS_NEW_ALERT_CAT +#define MYNEWT_VAL_BLE_SVC_ANS_NEW_ALERT_CAT (0) +#endif + + +#ifndef MYNEWT_VAL_BLE_SVC_ANS_UNR_ALERT_CAT +#define MYNEWT_VAL_BLE_SVC_ANS_UNR_ALERT_CAT (0) +#endif + +/*** nimble/host/services/bas */ +#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE +#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM +#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM (0) +#endif +#ifndef MYNEWT_VAL_BLE_MESH_ADV_TASK_PRIO +#define MYNEWT_VAL_BLE_MESH_ADV_TASK_PRIO (9) +#endif + + +/*** @apache-mynewt-nimble/nimble/host/mesh */ +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_ADV_BUF_COUNT +#define MYNEWT_VAL_BLE_MESH_ADV_BUF_COUNT (20) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_APP_KEY_COUNT +#define MYNEWT_VAL_BLE_MESH_APP_KEY_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_CFG_CLI +#define MYNEWT_VAL_BLE_MESH_CFG_CLI (0) +#endif +#ifndef MYNEWT_VAL_BLE_MESH_CRPL +#define MYNEWT_VAL_BLE_MESH_CRPL (10) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG +#define MYNEWT_VAL_BLE_MESH_DEBUG (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_ACCESS +#define MYNEWT_VAL_BLE_MESH_DEBUG_ACCESS (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_ADV +#define MYNEWT_VAL_BLE_MESH_DEBUG_ADV (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG +#define MYNEWT_VAL_BLE_MESH_DEBUG (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_ACCESS +#define MYNEWT_VAL_BLE_MESH_DEBUG_ACCESS (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_ADV +#define MYNEWT_VAL_BLE_MESH_DEBUG_ADV (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_BEACON +#define MYNEWT_VAL_BLE_MESH_DEBUG_BEACON (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_CRYPTO +#define MYNEWT_VAL_BLE_MESH_DEBUG_CRYPTO (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_FRIEND +#define MYNEWT_VAL_BLE_MESH_DEBUG_FRIEND (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_LOW_POWER +#define MYNEWT_VAL_BLE_MESH_DEBUG_LOW_POWER (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_MODEL +#define MYNEWT_VAL_BLE_MESH_DEBUG_MODEL (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_NET +#define MYNEWT_VAL_BLE_MESH_DEBUG_NET (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_PROV +#define MYNEWT_VAL_BLE_MESH_DEBUG_PROV (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_PROXY +#define MYNEWT_VAL_BLE_MESH_DEBUG_PROXY (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_SETTINGS +#define MYNEWT_VAL_BLE_MESH_DEBUG_SETTINGS (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_TRANS +#define MYNEWT_VAL_BLE_MESH_DEBUG_TRANS (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_DEVICE_NAME +#define MYNEWT_VAL_BLE_MESH_DEVICE_NAME CONFIG_BT_NIMBLE_MESH_DEVICE_NAME +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_DEV_UUID +#define MYNEWT_VAL_BLE_MESH_DEV_UUID (((uint8_t[16]){0x11, 0x22, 0})) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND +#ifdef CONFIG_BT_NIMBLE_MESH_FRIEND +#define MYNEWT_VAL_BLE_MESH_FRIEND (1) +#else +#define MYNEWT_VAL_BLE_MESH_FRIEND (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_LPN_COUNT +#define MYNEWT_VAL_BLE_MESH_FRIEND_LPN_COUNT (2) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_QUEUE_SIZE +#define MYNEWT_VAL_BLE_MESH_FRIEND_QUEUE_SIZE (16) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_RECV_WIN +#define MYNEWT_VAL_BLE_MESH_FRIEND_RECV_WIN (255) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_SEG_RX +#define MYNEWT_VAL_BLE_MESH_FRIEND_SEG_RX (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_SUB_LIST_SIZE +#define MYNEWT_VAL_BLE_MESH_FRIEND_SUB_LIST_SIZE (3) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_GATT_PROXY +#ifdef CONFIG_BT_NIMBLE_MESH_GATT_PROXY +#define MYNEWT_VAL_BLE_MESH_GATT_PROXY (1) +#else +#define MYNEWT_VAL_BLE_MESH_GATT_PROXY (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_HEALTH_CLI +#define MYNEWT_VAL_BLE_MESH_HEALTH_CLI (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_IVU_DIVIDER +#define MYNEWT_VAL_BLE_MESH_IVU_DIVIDER (4) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_IV_UPDATE_TEST +#define MYNEWT_VAL_BLE_MESH_IV_UPDATE_TEST (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LABEL_COUNT +#define MYNEWT_VAL_BLE_MESH_LABEL_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LOW_POWER +#ifdef CONFIG_BT_NIMBLE_MESH_LOW_POWER +#define MYNEWT_VAL_BLE_MESH_LOW_POWER (1) +#else +#define MYNEWT_VAL_BLE_MESH_LOW_POWER (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_AUTO +#define MYNEWT_VAL_BLE_MESH_LPN_AUTO (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_AUTO_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_LPN_AUTO_TIMEOUT (15) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_ESTABLISHMENT +#define MYNEWT_VAL_BLE_MESH_LPN_ESTABLISHMENT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_GROUPS +#define MYNEWT_VAL_BLE_MESH_LPN_GROUPS (10) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_INIT_POLL_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_LPN_INIT_POLL_TIMEOUT (MYNEWT_VAL_BLE_MESH_LPN_POLL_TIMEOUT) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_MIN_QUEUE_SIZE +#define MYNEWT_VAL_BLE_MESH_LPN_MIN_QUEUE_SIZE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_POLL_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_LPN_POLL_TIMEOUT (300) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_RECV_DELAY +#define MYNEWT_VAL_BLE_MESH_LPN_RECV_DELAY (100) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_RECV_WIN_FACTOR +#define MYNEWT_VAL_BLE_MESH_LPN_RECV_WIN_FACTOR (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_RETRY_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_LPN_RETRY_TIMEOUT (8) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_RSSI_FACTOR +#define MYNEWT_VAL_BLE_MESH_LPN_RSSI_FACTOR (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_SCAN_LATENCY +#define MYNEWT_VAL_BLE_MESH_LPN_SCAN_LATENCY (10) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_MODEL_GROUP_COUNT +#define MYNEWT_VAL_BLE_MESH_MODEL_GROUP_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_MODEL_KEY_COUNT +#define MYNEWT_VAL_BLE_MESH_MODEL_KEY_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_MSG_CACHE_SIZE +#define MYNEWT_VAL_BLE_MESH_MSG_CACHE_SIZE (10) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_NODE_ID_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_NODE_ID_TIMEOUT (60) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_OOB_INPUT_ACTIONS +#define MYNEWT_VAL_BLE_MESH_OOB_INPUT_ACTIONS (((BT_MESH_NO_INPUT))) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_OOB_INPUT_SIZE +#define MYNEWT_VAL_BLE_MESH_OOB_INPUT_SIZE (4) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_OOB_OUTPUT_ACTIONS +#define MYNEWT_VAL_BLE_MESH_OOB_OUTPUT_ACTIONS (((BT_MESH_DISPLAY_NUMBER))) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_OOB_OUTPUT_SIZE +#define MYNEWT_VAL_BLE_MESH_OOB_OUTPUT_SIZE (4) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PB_ADV +#ifdef CONFIG_BT_NIMBLE_MESH_PB_ADV +#define MYNEWT_VAL_BLE_MESH_PB_ADV (1) +#else +#define MYNEWT_VAL_BLE_MESH_PB_ADV (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PB_GATT +#ifdef CONFIG_BT_NIMBLE_MESH_PB_GATT +#define MYNEWT_VAL_BLE_MESH_PB_GATT (1) +#else +#define MYNEWT_VAL_BLE_MESH_PB_GATT (0) +#endif +#endif + +/* Overridden by @apache-mynewt-nimble/nimble/host/mesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_PROV +#ifdef CONFIG_BT_NIMBLE_MESH_PROV +#define MYNEWT_VAL_BLE_MESH_PROV (1) +#else +#define MYNEWT_VAL_BLE_MESH_PROV (0) +#endif +#endif + +/* Overridden by @apache-mynewt-nimble/nimble/host/mesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_PROXY +#ifdef CONFIG_BT_NIMBLE_MESH_PROXY +#define MYNEWT_VAL_BLE_MESH_PROXY (1) +#else +#define MYNEWT_VAL_BLE_MESH_PROXY (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PROXY_FILTER_SIZE +#define MYNEWT_VAL_BLE_MESH_PROXY_FILTER_SIZE (1) +#endif + + +#ifndef MYNEWT_VAL_BLE_MESH_RELAY +#ifdef CONFIG_BT_NIMBLE_MESH_RELAY +#define MYNEWT_VAL_BLE_MESH_RELAY (1) +#else +#define MYNEWT_VAL_BLE_MESH_RELAY (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_RPL_STORE_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_RPL_STORE_TIMEOUT (5) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_RX_SDU_MAX +#define MYNEWT_VAL_BLE_MESH_RX_SDU_MAX (72) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_RX_SEG_MSG_COUNT +#define MYNEWT_VAL_BLE_MESH_RX_SEG_MSG_COUNT (2) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SEQ_STORE_RATE +#define MYNEWT_VAL_BLE_MESH_SEQ_STORE_RATE (128) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_SETTINGS +#define MYNEWT_VAL_BLE_MESH_SETTINGS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SHELL +#define MYNEWT_VAL_BLE_MESH_SHELL (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SHELL_MODELS +#define MYNEWT_VAL_BLE_MESH_SHELL_MODELS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_STORE_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_STORE_TIMEOUT (2) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SUBNET_COUNT +#define MYNEWT_VAL_BLE_MESH_SUBNET_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_TESTING +#define MYNEWT_VAL_BLE_MESH_TESTING (0) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_MAX +#define MYNEWT_VAL_BLE_MESH_TX_SEG_MAX (6) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_MSG_COUNT +#define MYNEWT_VAL_BLE_MESH_TX_SEG_MSG_COUNT (4) +#endif + +/*** @apache-mynewt-nimble/nimble/host/services/gap */ +#ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE +#define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE_WRITE_PERM +#define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE_WRITE_PERM (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION +#define MYNEWT_VAL_BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME +#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH +#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM +#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL +#define MYNEWT_VAL_BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL +#define MYNEWT_VAL_BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_PPCP_SLAVE_LATENCY +#define MYNEWT_VAL_BLE_SVC_GAP_PPCP_SLAVE_LATENCY (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_PPCP_SUPERVISION_TMO +#define MYNEWT_VAL_BLE_SVC_GAP_PPCP_SUPERVISION_TMO (0) +#endif + +/*** nimble/transport */ +#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_EMSPI +#define MYNEWT_VAL_BLE_HCI_TRANSPORT_EMSPI (0) +#endif + +/* Overridden by targets/porting-nimble (defined by nimble/transport) */ +#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_NIMBLE_BUILTIN +#define MYNEWT_VAL_BLE_HCI_TRANSPORT_NIMBLE_BUILTIN (0) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_RAM +#define MYNEWT_VAL_BLE_HCI_TRANSPORT_RAM (0) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_SOCKET +#define MYNEWT_VAL_BLE_HCI_TRANSPORT_SOCKET (0) +#endif + +/* Overridden by targets/porting-nimble (defined by nimble/transport) */ +#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_UART +#define MYNEWT_VAL_BLE_HCI_TRANSPORT_UART (1) +#endif + +/*** nimble/transport/uart */ +#ifndef MYNEWT_VAL_BLE_ACL_BUF_COUNT +#define MYNEWT_VAL_BLE_ACL_BUF_COUNT CONFIG_BT_NIMBLE_ACL_BUF_COUNT +#endif + +#ifndef MYNEWT_VAL_BLE_ACL_BUF_SIZE +#define MYNEWT_VAL_BLE_ACL_BUF_SIZE CONFIG_BT_NIMBLE_ACL_BUF_SIZE +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_ACL_OUT_COUNT +#define MYNEWT_VAL_BLE_HCI_ACL_OUT_COUNT (12) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_EVT_BUF_SIZE +#define MYNEWT_VAL_BLE_HCI_EVT_BUF_SIZE CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_EVT_HI_BUF_COUNT +#define MYNEWT_VAL_BLE_HCI_EVT_HI_BUF_COUNT CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_EVT_LO_BUF_COUNT +#define MYNEWT_VAL_BLE_HCI_EVT_LO_BUF_COUNT CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT +#endif + +/* Overridden by targets/porting-nimble (defined by nimble/transport/uart) */ +#ifndef MYNEWT_VAL_BLE_HCI_UART_BAUD +#define MYNEWT_VAL_BLE_HCI_UART_BAUD (115200) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_UART_DATA_BITS +#define MYNEWT_VAL_BLE_HCI_UART_DATA_BITS (8) +#endif + +/* Overridden by targets/porting-nimble (defined by nimble/transport/uart) */ +#ifndef MYNEWT_VAL_BLE_HCI_UART_FLOW_CTRL +#define MYNEWT_VAL_BLE_HCI_UART_FLOW_CTRL (0) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_UART_PARITY +#define MYNEWT_VAL_BLE_HCI_UART_PARITY (HAL_UART_PARITY_NONE) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_UART_PORT +#define MYNEWT_VAL_BLE_HCI_UART_PORT (0) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_UART_STOP_BITS +#define MYNEWT_VAL_BLE_HCI_UART_STOP_BITS (1) +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/esp_nimble_hci.h b/libesp32/NimBLE-Arduino/src/esp_nimble_hci.h new file mode 100644 index 000000000..3c66ca796 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/esp_nimble_hci.h @@ -0,0 +1,138 @@ +/* + * Copyright 2019 Espressif Systems (Shanghai) PTE LTD + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __ESP_NIMBLE_HCI_H__ +#define __ESP_NIMBLE_HCI_H__ + +#include "nimble/ble_hci_trans.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_HCI_UART_H4_NONE 0x00 +#define BLE_HCI_UART_H4_CMD 0x01 +#define BLE_HCI_UART_H4_ACL 0x02 +#define BLE_HCI_UART_H4_SCO 0x03 +#define BLE_HCI_UART_H4_EVT 0x04 + +/** + * @brief Initialize VHCI transport layer between NimBLE Host and + * ESP Bluetooth controller + * + * This function initializes the transport buffers to be exchanged + * between NimBLE host and ESP controller. It also registers required + * host callbacks with the controller. + * + * @return + * - ESP_OK if the initialization is successful + * - Appropriate error code from esp_err_t in case of an error + */ +esp_err_t esp_nimble_hci_init(void); + +/** + * @brief Initialize ESP Bluetooth controller(link layer) and VHCI transport + * layer between NimBLE Host and ESP Bluetooth controller + * + * This function initializes ESP controller in BLE only mode and the + * transport buffers to be exchanged between NimBLE host and ESP controller. + * It also registers required host callbacks with the controller. + * + * Below is the sequence of APIs to be called to init/enable NimBLE host and ESP controller: + * + * @code{c} + * void ble_host_task(void *param) + * { + * nimble_port_run(); //This function will return only when nimble_port_stop() is executed. + * nimble_port_freertos_deinit(); + * } + * + * int ret = esp_nimble_hci_and_controller_init(); + * if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_nimble_hci_and_controller_init() failed with error: %d", ret); + * return; + * } + * + * nimble_port_init(); + * + * //Initialize the NimBLE Host configuration + * + * nimble_port_freertos_init(ble_host_task); + * @endcode + * + * nimble_port_freertos_init() is an optional call that creates a new task in which the NimBLE + * host will run. The task function should have a call to nimble_port_run(). If a separate task + * is not required, calling nimble_port_run() will run the NimBLE host in the current task. + * + * @return + * - ESP_OK if the initialization is successful + * - Appropriate error code from esp_err_t in case of an error + */ +esp_err_t esp_nimble_hci_and_controller_init(void); + +/** + * @brief Deinitialize VHCI transport layer between NimBLE Host and + * ESP Bluetooth controller + * + * @note This function should be called after the NimBLE host is deinitialized. + * + * @return + * - ESP_OK if the deinitialization is successful + * - Appropriate error codes from esp_err_t in case of an error + */ +esp_err_t esp_nimble_hci_deinit(void); + +/** + * @brief Deinitialize VHCI transport layer between NimBLE Host and + * ESP Bluetooth controller and disable and deinitialize the controller + * + * @note This function should not be executed in the context of Bluetooth host task. + * + * @note This function should be called after the NimBLE host is deinitialized. + * + * Below is the sequence of APIs to be called to disable/deinit NimBLE host and ESP controller: + * + * @code{c} + * int ret = nimble_port_stop(); + * if (ret == 0) { + * nimble_port_deinit(); + * + * ret = esp_nimble_hci_and_controller_deinit(); + * if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret); + * } + * } + * @endcode + * + * If nimble_port_freertos_init() is used during initialization, then + * nimble_port_freertos_deinit() should be called in the host task after nimble_port_run(). + * + * @return + * - ESP_OK if the deinitialization is successful + * - Appropriate error codes from esp_err_t in case of an error + */ +esp_err_t esp_nimble_hci_and_controller_deinit(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_NIMBLE_HCI_H__ */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/esp_nimble_mem.h b/libesp32/NimBLE-Arduino/src/esp_nimble_mem.h new file mode 100644 index 000000000..d257c5466 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/esp_nimble_mem.h @@ -0,0 +1,40 @@ + +/* + * Copyright 2020 Espressif Systems (Shanghai) PTE LTD + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __ESP_NIMBLE_MEM_H__ +#define __ESP_NIMBLE_MEM_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *nimble_platform_mem_malloc(size_t size); +void *nimble_platform_mem_calloc(size_t n, size_t size); +void nimble_platform_mem_free(void *ptr); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_NIMBLE_MEM_H__ */ \ No newline at end of file diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/AUTHORS b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/AUTHORS new file mode 100644 index 000000000..0a8e9f806 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/AUTHORS @@ -0,0 +1,15 @@ +Architect: +Rafael Misoczki + +Open Source Maintainer: +Constanza Heath +Rafael Misoczki + +Contributors: +Constanza Heath +Rafael Misoczki +Flavio Santes +Jarkko Sakkinen +Chris Morrison +Marti Bolivar +Colin Ian King diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/LICENSE b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/LICENSE new file mode 100644 index 000000000..2e1db516a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/LICENSE @@ -0,0 +1,61 @@ + +================================================================================ + + TinyCrypt Cryptographic Library + +================================================================================ + + Copyright (c) 2017, Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + - Neither the name of the Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ +Copyright (c) 2014, Kenneth MacKay +All rights reserved. + +https://github.com/kmackay/micro-ecc + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/README b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/README new file mode 100644 index 000000000..fb52c196a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/README @@ -0,0 +1,71 @@ + +================================================================================ + + TinyCrypt Cryptographic Library + +================================================================================ + + Copyright (c) 2017, Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + - Neither the name of the Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ + +Overview: + +The TinyCrypt Library provides an implementation for constrained devices of a +minimal set of standard cryptography primitives. + +Please, ***SEE THE DOCUMENTATION*** folder for more information on the supported +cryptographic primitives and the limitations of TinyCrypt library. For usage, +security and technicalities, please see the corresponding header file of each +cryptographic primitive. + +================================================================================ + +Organization: + +/lib: C source code of the cryptographic primitives. +/lib/include/tinycrypt: C header files of the cryptographic primitives. +/tests: Test vectors of the cryptographic primitives. +/doc: Documentation of TinyCrypt. + +================================================================================ + +Building: + +1) In Makefile.conf set: + - CFLAGS for compiler flags. + - CC for compiler. + - ENABLE_TESTS for enabling (true) or disabling (false) tests compilation. +2) In lib/Makefile select the primitives required by your project. +3) In tests/Makefile select the corresponding tests of the selected primitives. +4) make +5) run tests in tests/ + +================================================================================ + diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/VERSION b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/VERSION new file mode 100644 index 000000000..a45be4627 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/VERSION @@ -0,0 +1 @@ +0.2.8 diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/documentation/tinycrypt.rst b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/documentation/tinycrypt.rst new file mode 100644 index 000000000..356c099a0 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/documentation/tinycrypt.rst @@ -0,0 +1,352 @@ + +TinyCrypt Cryptographic Library +############################### +Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + +Overview +******** +The TinyCrypt Library provides an implementation for targeting constrained devices +with a minimal set of standard cryptography primitives, as listed below. To better +serve applications targeting constrained devices, TinyCrypt implementations differ +from the standard specifications (see the Important Remarks section for some +important differences). Certain cryptographic primitives depend on other +primitives, as mentioned in the list below. + +Aside from the Important Remarks section below, valuable information on the usage, +security and technicalities of each cryptographic primitive are found in the +corresponding header file. + +* SHA-256: + + * Type of primitive: Hash function. + * Standard Specification: NIST FIPS PUB 180-4. + * Requires: -- + +* HMAC-SHA256: + + * Type of primitive: Message authentication code. + * Standard Specification: RFC 2104. + * Requires: SHA-256 + +* HMAC-PRNG: + + * Type of primitive: Pseudo-random number generator (256-bit strength). + * Standard Specification: NIST SP 800-90A. + * Requires: SHA-256 and HMAC-SHA256. + +* AES-128: + + * Type of primitive: Block cipher. + * Standard Specification: NIST FIPS PUB 197. + * Requires: -- + +* AES-CBC mode: + + * Type of primitive: Encryption mode of operation. + * Standard Specification: NIST SP 800-38A. + * Requires: AES-128. + +* AES-CTR mode: + + * Type of primitive: Encryption mode of operation. + * Standard Specification: NIST SP 800-38A. + * Requires: AES-128. + +* AES-CMAC mode: + + * Type of primitive: Message authentication code. + * Standard Specification: NIST SP 800-38B. + * Requires: AES-128. + +* AES-CCM mode: + + * Type of primitive: Authenticated encryption. + * Standard Specification: NIST SP 800-38C. + * Requires: AES-128. + +* CTR-PRNG: + + * Type of primitive: Pseudo-random number generator (128-bit strength). + * Standard Specification: NIST SP 800-90A. + * Requires: AES-128. + +* ECC-DH: + + * Type of primitive: Key exchange based on curve NIST p-256. + * Standard Specification: RFC 6090. + * Requires: ECC auxiliary functions (ecc.h/c). + +* ECC-DSA: + + * Type of primitive: Digital signature based on curve NIST p-256. + * Standard Specification: RFC 6090. + * Requires: ECC auxiliary functions (ecc.h/c). + +Design Goals +************ + +* Minimize the code size of each cryptographic primitive. This means minimize + the size of a platform-independent implementation, as presented in TinyCrypt. + Note that various applications may require further features, optimizations with + respect to other metrics and countermeasures for particular threats. These + peculiarities would increase the code size and thus are not considered here. + +* Minimize the dependencies among the cryptographic primitives. This means + that it is unnecessary to build and allocate object code for more primitives + than the ones strictly required by the intended application. In other words, + one can select and compile only the primitives required by the application. + + +Important Remarks +***************** + +The cryptographic implementations in TinyCrypt library have some limitations. +Some of these limitations are inherent to the cryptographic primitives +themselves, while others are specific to TinyCrypt. These limitations were accepted +in order to meet its design goals (in special, minimal code size) and to better +serve applications targeting constrained devices in general. Some of these +limitations are discussed in-depth below. + +General Remarks +*************** + +* TinyCrypt does **not** intend to be fully side-channel resistant. Due to the + variety of side-channel attacks, many of them only relevant to certain + platforms. In this sense, instead of penalizing all library users with + side-channel countermeasures such as increasing the overall code size, + TinyCrypt only implements certain generic timing-attack countermeasures. + +Specific Remarks +**************** + +* SHA-256: + + * The number of bits_hashed in the state is not checked for overflow. Note + however that this will only be a problem if you intend to hash more than + 2^64 bits, which is an extremely large window. + +* HMAC: + + * The HMAC verification process is assumed to be performed by the application. + This compares the computed tag with some given tag. + Note that conventional memory-comparison methods (such as memcmp function) + might be vulnerable to timing attacks; thus be sure to use a constant-time + memory comparison function (such as compare_constant_time + function provided in lib/utils.c). + + * The tc_hmac_final function, responsible for computing the message tag, + cleans the state context before exiting. Thus, applications do not need to + clean the TCHmacState_t ctx after calling tc_hmac_final. This should not + be changed in future versions of the library as there are applications + currently relying on this good-practice/feature of TinyCrypt. + +* HMAC-PRNG: + + * Before using HMAC-PRNG, you *must* find an entropy source to produce a seed. + PRNGs only stretch the seed into a seemingly random output of arbitrary + length. The security of the output is exactly equal to the + unpredictability of the seed. + + * NIST SP 800-90A requires three items as seed material in the initialization + step: entropy seed, personalization and a nonce (which is not implemented). + TinyCrypt requires the personalization byte array and automatically creates + the entropy seed using a mandatory call to the re-seed function. + +* AES-128: + + * The current implementation does not support other key-lengths (such as 256 + bits). Note that if you need AES-256, it doesn't sound as though your + application is running in a constrained environment. AES-256 requires keys + twice the size as for AES-128, and the key schedule is 40% larger. + +* CTR mode: + + * The AES-CTR mode limits the size of a data message they encrypt to 2^32 + blocks. If you need to encrypt larger data sets, your application would + need to replace the key after 2^32 block encryptions. + +* CTR-PRNG: + + * Before using CTR-PRNG, you *must* find an entropy source to produce a seed. + PRNGs only stretch the seed into a seemingly random output of arbitrary + length. The security of the output is exactly equal to the + unpredictability of the seed. + +* CBC mode: + + * TinyCrypt CBC decryption assumes that the iv and the ciphertext are + contiguous (as produced by TinyCrypt CBC encryption). This allows for a + very efficient decryption algorithm that would not otherwise be possible. + +* CMAC mode: + + * AES128-CMAC mode of operation offers 64 bits of security against collision + attacks. Note however that an external attacker cannot generate the tags + him/herself without knowing the MAC key. In this sense, to attack the + collision property of AES128-CMAC, an external attacker would need the + cooperation of the legal user to produce an exponentially high number of + tags (e.g. 2^64) to finally be able to look for collisions and benefit + from them. As an extra precaution, the current implementation allows to at + most 2^48 calls to tc_cmac_update function before re-calling tc_cmac_setup + (allowing a new key to be set), as suggested in Appendix B of SP 800-38B. + +* CCM mode: + + * There are a few tradeoffs for the selection of the parameters of CCM mode. + In special, there is a tradeoff between the maximum number of invocations + of CCM under a given key and the maximum payload length for those + invocations. Both things are related to the parameter 'q' of CCM mode. The + maximum number of invocations of CCM under a given key is determined by + the nonce size, which is: 15-q bytes. The maximum payload length for those + invocations is defined as 2^(8q) bytes. + + To achieve minimal code size, TinyCrypt CCM implementation fixes q = 2, + which is a quite reasonable choice for constrained applications. The + implications of this choice are: + + The nonce size is: 13 bytes. + + The maximum payload length is: 2^16 bytes = 65 KB. + + The mac size parameter is an important parameter to estimate the security + against collision attacks (that aim at finding different messages that + produce the same authentication tag). TinyCrypt CCM implementation + accepts any even integer between 4 and 16, as suggested in SP 800-38C. + + * TinyCrypt CCM implementation accepts associated data of any length between + 0 and (2^16 - 2^8) = 65280 bytes. + + * TinyCrypt CCM implementation accepts: + + * Both non-empty payload and associated data (it encrypts and + authenticates the payload and only authenticates the associated data); + + * Non-empty payload and empty associated data (it encrypts and + authenticates the payload); + + * Non-empty associated data and empty payload (it degenerates to an + authentication-only mode on the associated data). + + * RFC-3610, which also specifies CCM, presents a few relevant security + suggestions, such as: it is recommended for most applications to use a + mac size greater than 8. Besides, it is emphasized that the usage of the + same nonce for two different messages which are encrypted with the same + key obviously destroys the security properties of CCM mode. + +* ECC-DH and ECC-DSA: + + * TinyCrypt ECC implementation is based on micro-ecc (see + https://github.com/kmackay/micro-ecc). In the original micro-ecc + documentation, there is an important remark about the way integers are + represented: + + "Integer representation: To reduce code size, all large integers are + represented using little-endian words - so the least significant word is + first. You can use the 'ecc_bytes2native()' and 'ecc_native2bytes()' + functions to convert between the native integer representation and the + standardized octet representation." + + Note that the assumed bit layout is: {31, 30, ..., 0}, {63, 62, ..., 32}, + {95, 94, ..., 64}, {127, 126, ..., 96} for a very-long-integer (vli) + consisting of 4 unsigned integers (as an example). + + * A cryptographically-secure PRNG function must be set (using uECC_set_rng()) + before calling uECC_make_key() or uECC_sign(). + +Examples of Applications +************************ +It is possible to do useful cryptography with only the given small set of +primitives. With this list of primitives it becomes feasible to support a range +of cryptography usages: + + * Measurement of code, data structures, and other digital artifacts (SHA256); + + * Generate commitments (SHA256); + + * Construct keys (HMAC-SHA256); + + * Extract entropy from strings containing some randomness (HMAC-SHA256); + + * Construct random mappings (HMAC-SHA256); + + * Construct nonces and challenges (HMAC-PRNG, CTR-PRNG); + + * Authenticate using a shared secret (HMAC-SHA256); + + * Create an authenticated, replay-protected session (HMAC-SHA256 + HMAC-PRNG); + + * Authenticated encryption (AES-128 + AES-CCM); + + * Key-exchange (EC-DH); + + * Digital signature (EC-DSA); + +Test Vectors +************ + +The library provides a test program for each cryptographic primitive (see 'test' +folder). Besides illustrating how to use the primitives, these tests evaluate +the correctness of the implementations by checking the results against +well-known publicly validated test vectors. + +For the case of the HMAC-PRNG, due to the necessity of performing an extensive +battery test to produce meaningful conclusions, we suggest the user to evaluate +the unpredictability of the implementation by using the NIST Statistical Test +Suite (see References). + +For the case of the EC-DH and EC-DSA implementations, most of the test vectors +were obtained from the site of the NIST Cryptographic Algorithm Validation +Program (CAVP), see References. + +References +********** + +* `NIST FIPS PUB 180-4 (SHA-256)`_ + +.. _NIST FIPS PUB 180-4 (SHA-256): + http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + +* `NIST FIPS PUB 197 (AES-128)`_ + +.. _NIST FIPS PUB 197 (AES-128): + http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + +* `NIST SP800-90A (HMAC-PRNG)`_ + +.. _NIST SP800-90A (HMAC-PRNG): + http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf + +* `NIST SP 800-38A (AES-CBC and AES-CTR)`_ + +.. _NIST SP 800-38A (AES-CBC and AES-CTR): + http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + +* `NIST SP 800-38B (AES-CMAC)`_ + +.. _NIST SP 800-38B (AES-CMAC): + http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf + +* `NIST SP 800-38C (AES-CCM)`_ + +.. _NIST SP 800-38C (AES-CCM): + http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf + +* `NIST Statistical Test Suite (useful for testing HMAC-PRNG)`_ + +.. _NIST Statistical Test Suite (useful for testing HMAC-PRNG): + http://csrc.nist.gov/groups/ST/toolkit/rng/documentation_software.html + +* `NIST Cryptographic Algorithm Validation Program (CAVP) site`_ + +.. _NIST Cryptographic Algorithm Validation Program (CAVP) site: + http://csrc.nist.gov/groups/STM/cavp/ + +* `RFC 2104 (HMAC-SHA256)`_ + +.. _RFC 2104 (HMAC-SHA256): + https://www.ietf.org/rfc/rfc2104.txt + +* `RFC 6090 (ECC-DH and ECC-DSA)`_ + +.. _RFC 6090 (ECC-DH and ECC-DSA): + https://www.ietf.org/rfc/rfc6090.txt diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_decrypt.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_decrypt.c new file mode 100644 index 000000000..993a6180c --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_decrypt.c @@ -0,0 +1,164 @@ +/* aes_decrypt.c - TinyCrypt implementation of AES decryption procedure */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +static const uint8_t inv_sbox[256] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, + 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, + 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, + 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, + 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, + 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, + 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, + 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, + 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, + 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, + 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, + 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, + 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, + 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0c, 0x7d +}; + +int tc_aes128_set_decrypt_key(TCAesKeySched_t s, const uint8_t *k) +{ + return tc_aes128_set_encrypt_key(s, k); +} + +#define mult8(a)(_double_byte(_double_byte(_double_byte(a)))) +#define mult9(a)(mult8(a)^(a)) +#define multb(a)(mult8(a)^_double_byte(a)^(a)) +#define multd(a)(mult8(a)^_double_byte(_double_byte(a))^(a)) +#define multe(a)(mult8(a)^_double_byte(_double_byte(a))^_double_byte(a)) + +static inline void mult_row_column(uint8_t *out, const uint8_t *in) +{ + out[0] = multe(in[0]) ^ multb(in[1]) ^ multd(in[2]) ^ mult9(in[3]); + out[1] = mult9(in[0]) ^ multe(in[1]) ^ multb(in[2]) ^ multd(in[3]); + out[2] = multd(in[0]) ^ mult9(in[1]) ^ multe(in[2]) ^ multb(in[3]); + out[3] = multb(in[0]) ^ multd(in[1]) ^ mult9(in[2]) ^ multe(in[3]); +} + +static inline void inv_mix_columns(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + mult_row_column(t, s); + mult_row_column(&t[Nb], s+Nb); + mult_row_column(&t[2*Nb], s+(2*Nb)); + mult_row_column(&t[3*Nb], s+(3*Nb)); + (void)_copy(s, sizeof(t), t, sizeof(t)); +} + +static inline void add_round_key(uint8_t *s, const unsigned int *k) +{ + s[0] ^= (uint8_t)(k[0] >> 24); s[1] ^= (uint8_t)(k[0] >> 16); + s[2] ^= (uint8_t)(k[0] >> 8); s[3] ^= (uint8_t)(k[0]); + s[4] ^= (uint8_t)(k[1] >> 24); s[5] ^= (uint8_t)(k[1] >> 16); + s[6] ^= (uint8_t)(k[1] >> 8); s[7] ^= (uint8_t)(k[1]); + s[8] ^= (uint8_t)(k[2] >> 24); s[9] ^= (uint8_t)(k[2] >> 16); + s[10] ^= (uint8_t)(k[2] >> 8); s[11] ^= (uint8_t)(k[2]); + s[12] ^= (uint8_t)(k[3] >> 24); s[13] ^= (uint8_t)(k[3] >> 16); + s[14] ^= (uint8_t)(k[3] >> 8); s[15] ^= (uint8_t)(k[3]); +} + +static inline void inv_sub_bytes(uint8_t *s) +{ + unsigned int i; + + for (i = 0; i < (Nb*Nk); ++i) { + s[i] = inv_sbox[s[i]]; + } +} + +/* + * This inv_shift_rows also implements the matrix flip required for + * inv_mix_columns, but performs it here to reduce the number of memory + * operations. + */ +static inline void inv_shift_rows(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + t[0] = s[0]; t[1] = s[13]; t[2] = s[10]; t[3] = s[7]; + t[4] = s[4]; t[5] = s[1]; t[6] = s[14]; t[7] = s[11]; + t[8] = s[8]; t[9] = s[5]; t[10] = s[2]; t[11] = s[15]; + t[12] = s[12]; t[13] = s[9]; t[14] = s[6]; t[15] = s[3]; + (void)_copy(s, sizeof(t), t, sizeof(t)); +} + +int tc_aes_decrypt(uint8_t *out, const uint8_t *in, const TCAesKeySched_t s) +{ + uint8_t state[Nk*Nb]; + unsigned int i; + + if (out == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (in == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)_copy(state, sizeof(state), in, sizeof(state)); + + add_round_key(state, s->words + Nb*Nr); + + for (i = Nr - 1; i > 0; --i) { + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, s->words + Nb*i); + inv_mix_columns(state); + } + + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, s->words); + + (void)_copy(out, sizeof(state), state, sizeof(state)); + + /*zeroing out the state buffer */ + _set(state, TC_ZERO_BYTE, sizeof(state)); + + + return TC_CRYPTO_SUCCESS; +} diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_encrypt.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_encrypt.c new file mode 100644 index 000000000..8991aee52 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_encrypt.c @@ -0,0 +1,191 @@ +/* aes_encrypt.c - TinyCrypt implementation of AES encryption procedure */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +static const uint8_t sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, + 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, + 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, + 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, + 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, + 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, + 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, + 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, + 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, + 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, + 0xb0, 0x54, 0xbb, 0x16 +}; + +static inline unsigned int rotword(unsigned int a) +{ + return (((a) >> 24)|((a) << 8)); +} + +#define subbyte(a, o)(sbox[((a) >> (o))&0xff] << (o)) +#define subword(a)(subbyte(a, 24)|subbyte(a, 16)|subbyte(a, 8)|subbyte(a, 0)) + +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k) +{ + const unsigned int rconst[11] = { + 0x00000000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 + }; + unsigned int i; + unsigned int t; + + if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } else if (k == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + for (i = 0; i < Nk; ++i) { + s->words[i] = (k[Nb*i]<<24) | (k[Nb*i+1]<<16) | + (k[Nb*i+2]<<8) | (k[Nb*i+3]); + } + + for (; i < (Nb * (Nr + 1)); ++i) { + t = s->words[i-1]; + if ((i % Nk) == 0) { + t = subword(rotword(t)) ^ rconst[i/Nk]; + } + s->words[i] = s->words[i-Nk] ^ t; + } + + return TC_CRYPTO_SUCCESS; +} + +static inline void add_round_key(uint8_t *s, const unsigned int *k) +{ + s[0] ^= (uint8_t)(k[0] >> 24); s[1] ^= (uint8_t)(k[0] >> 16); + s[2] ^= (uint8_t)(k[0] >> 8); s[3] ^= (uint8_t)(k[0]); + s[4] ^= (uint8_t)(k[1] >> 24); s[5] ^= (uint8_t)(k[1] >> 16); + s[6] ^= (uint8_t)(k[1] >> 8); s[7] ^= (uint8_t)(k[1]); + s[8] ^= (uint8_t)(k[2] >> 24); s[9] ^= (uint8_t)(k[2] >> 16); + s[10] ^= (uint8_t)(k[2] >> 8); s[11] ^= (uint8_t)(k[2]); + s[12] ^= (uint8_t)(k[3] >> 24); s[13] ^= (uint8_t)(k[3] >> 16); + s[14] ^= (uint8_t)(k[3] >> 8); s[15] ^= (uint8_t)(k[3]); +} + +static inline void sub_bytes(uint8_t *s) +{ + unsigned int i; + + for (i = 0; i < (Nb * Nk); ++i) { + s[i] = sbox[s[i]]; + } +} + +#define triple(a)(_double_byte(a)^(a)) + +static inline void mult_row_column(uint8_t *out, const uint8_t *in) +{ + out[0] = _double_byte(in[0]) ^ triple(in[1]) ^ in[2] ^ in[3]; + out[1] = in[0] ^ _double_byte(in[1]) ^ triple(in[2]) ^ in[3]; + out[2] = in[0] ^ in[1] ^ _double_byte(in[2]) ^ triple(in[3]); + out[3] = triple(in[0]) ^ in[1] ^ in[2] ^ _double_byte(in[3]); +} + +static inline void mix_columns(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + mult_row_column(t, s); + mult_row_column(&t[Nb], s+Nb); + mult_row_column(&t[2 * Nb], s + (2 * Nb)); + mult_row_column(&t[3 * Nb], s + (3 * Nb)); + (void) _copy(s, sizeof(t), t, sizeof(t)); +} + +/* + * This shift_rows also implements the matrix flip required for mix_columns, but + * performs it here to reduce the number of memory operations. + */ +static inline void shift_rows(uint8_t *s) +{ + uint8_t t[Nb * Nk]; + + t[0] = s[0]; t[1] = s[5]; t[2] = s[10]; t[3] = s[15]; + t[4] = s[4]; t[5] = s[9]; t[6] = s[14]; t[7] = s[3]; + t[8] = s[8]; t[9] = s[13]; t[10] = s[2]; t[11] = s[7]; + t[12] = s[12]; t[13] = s[1]; t[14] = s[6]; t[15] = s[11]; + (void) _copy(s, sizeof(t), t, sizeof(t)); +} + +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, const TCAesKeySched_t s) +{ + uint8_t state[Nk*Nb]; + unsigned int i; + + if (out == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (in == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)_copy(state, sizeof(state), in, sizeof(state)); + add_round_key(state, s->words); + + for (i = 0; i < (Nr - 1); ++i) { + sub_bytes(state); + shift_rows(state); + mix_columns(state); + add_round_key(state, s->words + Nb*(i+1)); + } + + sub_bytes(state); + shift_rows(state); + add_round_key(state, s->words + Nb*(i+1)); + + (void)_copy(out, sizeof(state), state, sizeof(state)); + + /* zeroing out the state buffer */ + _set(state, TC_ZERO_BYTE, sizeof(state)); + + return TC_CRYPTO_SUCCESS; +} diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cbc_mode.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cbc_mode.c new file mode 100644 index 000000000..62d7879eb --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cbc_mode.c @@ -0,0 +1,114 @@ +/* cbc_mode.c - TinyCrypt implementation of CBC mode encryption & decryption */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +int tc_cbc_mode_encrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + unsigned int n, m; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (const uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + (inlen % TC_AES_BLOCK_SIZE) != 0 || + (outlen % TC_AES_BLOCK_SIZE) != 0 || + outlen != inlen + TC_AES_BLOCK_SIZE) { + return TC_CRYPTO_FAIL; + } + + /* copy iv to the buffer */ + (void)_copy(buffer, TC_AES_BLOCK_SIZE, iv, TC_AES_BLOCK_SIZE); + /* copy iv to the output buffer */ + (void)_copy(out, TC_AES_BLOCK_SIZE, iv, TC_AES_BLOCK_SIZE); + out += TC_AES_BLOCK_SIZE; + + for (n = m = 0; n < inlen; ++n) { + buffer[m++] ^= *in++; + if (m == TC_AES_BLOCK_SIZE) { + (void)tc_aes_encrypt(buffer, buffer, sched); + (void)_copy(out, TC_AES_BLOCK_SIZE, + buffer, TC_AES_BLOCK_SIZE); + out += TC_AES_BLOCK_SIZE; + m = 0; + } + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_cbc_mode_decrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + const uint8_t *p; + unsigned int n, m; + + /* sanity check the inputs */ + if (out == (uint8_t *) 0 || + in == (const uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + (inlen % TC_AES_BLOCK_SIZE) != 0 || + (outlen % TC_AES_BLOCK_SIZE) != 0 || + outlen != inlen - TC_AES_BLOCK_SIZE) { + return TC_CRYPTO_FAIL; + } + + /* + * Note that in == iv + ciphertext, i.e. the iv and the ciphertext are + * contiguous. This allows for a very efficient decryption algorithm + * that would not otherwise be possible. + */ + p = iv; + for (n = m = 0; n < inlen; ++n) { + if ((n % TC_AES_BLOCK_SIZE) == 0) { + (void)tc_aes_decrypt(buffer, in, sched); + in += TC_AES_BLOCK_SIZE; + m = 0; + } + *out++ = buffer[m++] ^ *p++; + } + + return TC_CRYPTO_SUCCESS; +} diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ccm_mode.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ccm_mode.c new file mode 100644 index 000000000..929adac63 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ccm_mode.c @@ -0,0 +1,266 @@ +/* ccm_mode.c - TinyCrypt implementation of CCM mode */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +int tc_ccm_config(TCCcmMode_t c, TCAesKeySched_t sched, uint8_t *nonce, + unsigned int nlen, unsigned int mlen) +{ + + /* input sanity check: */ + if (c == (TCCcmMode_t) 0 || + sched == (TCAesKeySched_t) 0 || + nonce == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (nlen != 13) { + return TC_CRYPTO_FAIL; /* The allowed nonce size is: 13. See documentation.*/ + } else if ((mlen < 4) || (mlen > 16) || (mlen & 1)) { + return TC_CRYPTO_FAIL; /* The allowed mac sizes are: 4, 6, 8, 10, 12, 14, 16.*/ + } + + c->mlen = mlen; + c->sched = sched; + c->nonce = nonce; + + return TC_CRYPTO_SUCCESS; +} + +/** + * Variation of CBC-MAC mode used in CCM. + */ +static void ccm_cbc_mac(uint8_t *T, const uint8_t *data, unsigned int dlen, + unsigned int flag, TCAesKeySched_t sched) +{ + + unsigned int i; + + if (flag > 0) { + T[0] ^= (uint8_t)(dlen >> 8); + T[1] ^= (uint8_t)(dlen); + dlen += 2; i = 2; + } else { + i = 0; + } + + while (i < dlen) { + T[i++ % (Nb * Nk)] ^= *data++; + if (((i % (Nb * Nk)) == 0) || dlen == i) { + (void) tc_aes_encrypt(T, T, sched); + } + } +} + +/** + * Variation of CTR mode used in CCM. + * The CTR mode used by CCM is slightly different than the conventional CTR + * mode (the counter is increased before encryption, instead of after + * encryption). Besides, it is assumed that the counter is stored in the last + * 2 bytes of the nonce. + */ +static int ccm_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + uint8_t nonce[TC_AES_BLOCK_SIZE]; + uint16_t block_num; + unsigned int i; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (uint8_t *) 0 || + ctr == (uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + outlen != inlen) { + return TC_CRYPTO_FAIL; + } + + /* copy the counter to the nonce */ + (void) _copy(nonce, sizeof(nonce), ctr, sizeof(nonce)); + + /* select the last 2 bytes of the nonce to be incremented */ + block_num = (uint16_t) ((nonce[14] << 8)|(nonce[15])); + for (i = 0; i < inlen; ++i) { + if ((i % (TC_AES_BLOCK_SIZE)) == 0) { + block_num++; + nonce[14] = (uint8_t)(block_num >> 8); + nonce[15] = (uint8_t)(block_num); + if (!tc_aes_encrypt(buffer, nonce, sched)) { + return TC_CRYPTO_FAIL; + } + } + /* update the output */ + *out++ = buffer[i % (TC_AES_BLOCK_SIZE)] ^ *in++; + } + + /* update the counter */ + ctr[14] = nonce[14]; ctr[15] = nonce[15]; + + return TC_CRYPTO_SUCCESS; +} + +int tc_ccm_generation_encryption(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c) +{ + + /* input sanity check: */ + if ((out == (uint8_t *) 0) || + (c == (TCCcmMode_t) 0) || + ((plen > 0) && (payload == (uint8_t *) 0)) || + ((alen > 0) && (associated_data == (uint8_t *) 0)) || + (alen >= TC_CCM_AAD_MAX_BYTES) || /* associated data size unsupported */ + (plen >= TC_CCM_PAYLOAD_MAX_BYTES) || /* payload size unsupported */ + (olen < (plen + c->mlen))) { /* invalid output buffer size */ + return TC_CRYPTO_FAIL; + } + + uint8_t b[Nb * Nk]; + uint8_t tag[Nb * Nk]; + unsigned int i; + + /* GENERATING THE AUTHENTICATION TAG: */ + + /* formatting the sequence b for authentication: */ + b[0] = ((alen > 0) ? 0x40:0) | (((c->mlen - 2) / 2 << 3)) | (1); + for (i = 1; i <= 13; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = (uint8_t)(plen >> 8); + b[15] = (uint8_t)(plen); + + /* computing the authentication tag using cbc-mac: */ + (void) tc_aes_encrypt(tag, b, c->sched); + if (alen > 0) { + ccm_cbc_mac(tag, associated_data, alen, 1, c->sched); + } + if (plen > 0) { + ccm_cbc_mac(tag, payload, plen, 0, c->sched); + } + + /* ENCRYPTION: */ + + /* formatting the sequence b for encryption: */ + b[0] = 1; /* q - 1 = 2 - 1 = 1 */ + b[14] = b[15] = TC_ZERO_BYTE; + + /* encrypting payload using ctr mode: */ + ccm_ctr_mode(out, plen, payload, plen, b, c->sched); + + b[14] = b[15] = TC_ZERO_BYTE; /* restoring initial counter for ctr_mode (0):*/ + + /* encrypting b and adding the tag to the output: */ + (void) tc_aes_encrypt(b, b, c->sched); + out += plen; + for (i = 0; i < c->mlen; ++i) { + *out++ = tag[i] ^ b[i]; + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_ccm_decryption_verification(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c) +{ + + /* input sanity check: */ + if ((out == (uint8_t *) 0) || + (c == (TCCcmMode_t) 0) || + ((plen > 0) && (payload == (uint8_t *) 0)) || + ((alen > 0) && (associated_data == (uint8_t *) 0)) || + (alen >= TC_CCM_AAD_MAX_BYTES) || /* associated data size unsupported */ + (plen >= TC_CCM_PAYLOAD_MAX_BYTES) || /* payload size unsupported */ + (olen < plen - c->mlen)) { /* invalid output buffer size */ + return TC_CRYPTO_FAIL; + } + + uint8_t b[Nb * Nk]; + uint8_t tag[Nb * Nk]; + unsigned int i; + + /* DECRYPTION: */ + + /* formatting the sequence b for decryption: */ + b[0] = 1; /* q - 1 = 2 - 1 = 1 */ + for (i = 1; i < 14; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = b[15] = TC_ZERO_BYTE; /* initial counter value is 0 */ + + /* decrypting payload using ctr mode: */ + ccm_ctr_mode(out, plen - c->mlen, payload, plen - c->mlen, b, c->sched); + + b[14] = b[15] = TC_ZERO_BYTE; /* restoring initial counter value (0) */ + + /* encrypting b and restoring the tag from input: */ + (void) tc_aes_encrypt(b, b, c->sched); + for (i = 0; i < c->mlen; ++i) { + tag[i] = *(payload + plen - c->mlen + i) ^ b[i]; + } + + /* VERIFYING THE AUTHENTICATION TAG: */ + + /* formatting the sequence b for authentication: */ + b[0] = ((alen > 0) ? 0x40:0)|(((c->mlen - 2) / 2 << 3)) | (1); + for (i = 1; i < 14; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = (uint8_t)((plen - c->mlen) >> 8); + b[15] = (uint8_t)(plen - c->mlen); + + /* computing the authentication tag using cbc-mac: */ + (void) tc_aes_encrypt(b, b, c->sched); + if (alen > 0) { + ccm_cbc_mac(b, associated_data, alen, 1, c->sched); + } + if (plen > 0) { + ccm_cbc_mac(b, out, plen - c->mlen, 0, c->sched); + } + + /* comparing the received tag and the computed one: */ + if (_compare(b, tag, c->mlen) == 0) { + return TC_CRYPTO_SUCCESS; + } else { + /* erase the decrypted buffer in case of mac validation failure: */ + _set(out, 0, plen - c->mlen); + return TC_CRYPTO_FAIL; + } +} diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cmac_mode.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cmac_mode.c new file mode 100644 index 000000000..96d147e80 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cmac_mode.c @@ -0,0 +1,254 @@ +/* cmac_mode.c - TinyCrypt CMAC mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +/* max number of calls until change the key (2^48).*/ +const static uint64_t MAX_CALLS = ((uint64_t)1 << 48); + +/* + * gf_wrap -- In our implementation, GF(2^128) is represented as a 16 byte + * array with byte 0 the most significant and byte 15 the least significant. + * High bit carry reduction is based on the primitive polynomial + * + * X^128 + X^7 + X^2 + X + 1, + * + * which leads to the reduction formula X^128 = X^7 + X^2 + X + 1. Indeed, + * since 0 = (X^128 + X^7 + X^2 + 1) mod (X^128 + X^7 + X^2 + X + 1) and since + * addition of polynomials with coefficients in Z/Z(2) is just XOR, we can + * add X^128 to both sides to get + * + * X^128 = (X^7 + X^2 + X + 1) mod (X^128 + X^7 + X^2 + X + 1) + * + * and the coefficients of the polynomial on the right hand side form the + * string 1000 0111 = 0x87, which is the value of gf_wrap. + * + * This gets used in the following way. Doubling in GF(2^128) is just a left + * shift by 1 bit, except when the most significant bit is 1. In the latter + * case, the relation X^128 = X^7 + X^2 + X + 1 says that the high order bit + * that overflows beyond 128 bits can be replaced by addition of + * X^7 + X^2 + X + 1 <--> 0x87 to the low order 128 bits. Since addition + * in GF(2^128) is represented by XOR, we therefore only have to XOR 0x87 + * into the low order byte after a left shift when the starting high order + * bit is 1. + */ +const unsigned char gf_wrap = 0x87; + +/* + * assumes: out != NULL and points to a GF(2^n) value to receive the + * doubled value; + * in != NULL and points to a 16 byte GF(2^n) value + * to double; + * the in and out buffers do not overlap. + * effects: doubles the GF(2^n) value pointed to by "in" and places + * the result in the GF(2^n) value pointed to by "out." + */ +void gf_double(uint8_t *out, uint8_t *in) +{ + + /* start with low order byte */ + uint8_t *x = in + (TC_AES_BLOCK_SIZE - 1); + + /* if msb == 1, we need to add the gf_wrap value, otherwise add 0 */ + uint8_t carry = (in[0] >> 7) ? gf_wrap : 0; + + out += (TC_AES_BLOCK_SIZE - 1); + for (;;) { + *out-- = (*x << 1) ^ carry; + if (x == in) { + break; + } + carry = *x-- >> 7; + } +} + +int tc_cmac_setup(TCCmacState_t s, const uint8_t *key, TCAesKeySched_t sched) +{ + + /* input sanity check: */ + if (s == (TCCmacState_t) 0 || + key == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + /* put s into a known state */ + _set(s, 0, sizeof(*s)); + s->sched = sched; + + /* configure the encryption key used by the underlying block cipher */ + tc_aes128_set_encrypt_key(s->sched, key); + + /* compute s->K1 and s->K2 from s->iv using s->keyid */ + _set(s->iv, 0, TC_AES_BLOCK_SIZE); + tc_aes_encrypt(s->iv, s->iv, s->sched); + gf_double (s->K1, s->iv); + gf_double (s->K2, s->K1); + + /* reset s->iv to 0 in case someone wants to compute now */ + tc_cmac_init(s); + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_erase(TCCmacState_t s) +{ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* destroy the current state */ + _set(s, 0, sizeof(*s)); + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_init(TCCmacState_t s) +{ + /* input sanity check: */ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* CMAC starts with an all zero initialization vector */ + _set(s->iv, 0, TC_AES_BLOCK_SIZE); + + /* and the leftover buffer is empty */ + _set(s->leftover, 0, TC_AES_BLOCK_SIZE); + s->leftover_offset = 0; + + /* Set countdown to max number of calls allowed before re-keying: */ + s->countdown = MAX_CALLS; + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_update(TCCmacState_t s, const uint8_t *data, size_t data_length) +{ + unsigned int i; + + /* input sanity check: */ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + if (data_length == 0) { + return TC_CRYPTO_SUCCESS; + } + if (data == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + if (s->countdown == 0) { + return TC_CRYPTO_FAIL; + } + + s->countdown--; + + if (s->leftover_offset > 0) { + /* last data added to s didn't end on a TC_AES_BLOCK_SIZE byte boundary */ + size_t remaining_space = TC_AES_BLOCK_SIZE - s->leftover_offset; + + if (data_length < remaining_space) { + /* still not enough data to encrypt this time either */ + _copy(&s->leftover[s->leftover_offset], data_length, data, data_length); + s->leftover_offset += data_length; + return TC_CRYPTO_SUCCESS; + } + /* leftover block is now full; encrypt it first */ + _copy(&s->leftover[s->leftover_offset], + remaining_space, + data, + remaining_space); + data_length -= remaining_space; + data += remaining_space; + s->leftover_offset = 0; + + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= s->leftover[i]; + } + tc_aes_encrypt(s->iv, s->iv, s->sched); + } + + /* CBC encrypt each (except the last) of the data blocks */ + while (data_length > TC_AES_BLOCK_SIZE) { + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= data[i]; + } + tc_aes_encrypt(s->iv, s->iv, s->sched); + data += TC_AES_BLOCK_SIZE; + data_length -= TC_AES_BLOCK_SIZE; + } + + if (data_length > 0) { + /* save leftover data for next time */ + _copy(s->leftover, data_length, data, data_length); + s->leftover_offset = data_length; + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_final(uint8_t *tag, TCCmacState_t s) +{ + uint8_t *k; + unsigned int i; + + /* input sanity check: */ + if (tag == (uint8_t *) 0 || + s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + if (s->leftover_offset == TC_AES_BLOCK_SIZE) { + /* the last message block is a full-sized block */ + k = (uint8_t *) s->K1; + } else { + /* the final message block is not a full-sized block */ + size_t remaining = TC_AES_BLOCK_SIZE - s->leftover_offset; + + _set(&s->leftover[s->leftover_offset], 0, remaining); + s->leftover[s->leftover_offset] = TC_CMAC_PADDING; + k = (uint8_t *) s->K2; + } + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= s->leftover[i] ^ k[i]; + } + + tc_aes_encrypt(tag, s->iv, s->sched); + + /* erasing state: */ + tc_cmac_erase(s); + + return TC_CRYPTO_SUCCESS; +} diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_mode.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_mode.c new file mode 100644 index 000000000..1dfb92dfe --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_mode.c @@ -0,0 +1,85 @@ +/* ctr_mode.c - TinyCrypt CTR mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +int tc_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + uint8_t nonce[TC_AES_BLOCK_SIZE]; + unsigned int block_num; + unsigned int i; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (uint8_t *) 0 || + ctr == (uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + outlen != inlen) { + return TC_CRYPTO_FAIL; + } + + /* copy the ctr to the nonce */ + (void)_copy(nonce, sizeof(nonce), ctr, sizeof(nonce)); + + /* select the last 4 bytes of the nonce to be incremented */ + block_num = (nonce[12] << 24) | (nonce[13] << 16) | + (nonce[14] << 8) | (nonce[15]); + for (i = 0; i < inlen; ++i) { + if ((i % (TC_AES_BLOCK_SIZE)) == 0) { + /* encrypt data using the current nonce */ + if (tc_aes_encrypt(buffer, nonce, sched)) { + block_num++; + nonce[12] = (uint8_t)(block_num >> 24); + nonce[13] = (uint8_t)(block_num >> 16); + nonce[14] = (uint8_t)(block_num >> 8); + nonce[15] = (uint8_t)(block_num); + } else { + return TC_CRYPTO_FAIL; + } + } + /* update the output */ + *out++ = buffer[i%(TC_AES_BLOCK_SIZE)] ^ *in++; + } + + /* update the counter */ + ctr[12] = nonce[12]; ctr[13] = nonce[13]; + ctr[14] = nonce[14]; ctr[15] = nonce[15]; + + return TC_CRYPTO_SUCCESS; +} diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_prng.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_prng.c new file mode 100644 index 000000000..cac2cc41d --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_prng.c @@ -0,0 +1,283 @@ +/* ctr_prng.c - TinyCrypt implementation of CTR-PRNG */ + +/* + * Copyright (c) 2016, Chris Morrison + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +/* + * This PRNG is based on the CTR_DRBG described in Recommendation for Random + * Number Generation Using Deterministic Random Bit Generators, + * NIST SP 800-90A Rev. 1. + * + * Annotations to particular steps (e.g. 10.2.1.2 Step 1) refer to the steps + * described in that document. + * + */ + +/** + * @brief Array incrementer + * Treats the supplied array as one contiguous number (MSB in arr[0]), and + * increments it by one + * @return none + * @param arr IN/OUT -- array to be incremented + * @param len IN -- size of arr in bytes + */ +static void arrInc(uint8_t arr[], unsigned int len) +{ + unsigned int i; + if (0 != arr) { + for (i = len; i > 0U; i--) { + if (++arr[i-1] != 0U) { + break; + } + } + } +} + +/** + * @brief CTR PRNG update + * Updates the internal state of supplied the CTR PRNG context + * increments it by one + * @return none + * @note Assumes: providedData is (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) bytes long + * @param ctx IN/OUT -- CTR PRNG state + * @param providedData IN -- data used when updating the internal state + */ +static void tc_ctr_prng_update(TCCtrPrng_t * const ctx, uint8_t const * const providedData) +{ + if (0 != ctx) { + /* 10.2.1.2 step 1 */ + uint8_t temp[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + unsigned int len = 0U; + + /* 10.2.1.2 step 2 */ + while (len < sizeof temp) { + unsigned int blocklen = sizeof(temp) - len; + uint8_t output_block[TC_AES_BLOCK_SIZE]; + + /* 10.2.1.2 step 2.1 */ + arrInc(ctx->V, sizeof ctx->V); + + /* 10.2.1.2 step 2.2 */ + if (blocklen > TC_AES_BLOCK_SIZE) { + blocklen = TC_AES_BLOCK_SIZE; + } + (void)tc_aes_encrypt(output_block, ctx->V, &ctx->key); + + /* 10.2.1.2 step 2.3/step 3 */ + memcpy(&(temp[len]), output_block, blocklen); + + len += blocklen; + } + + /* 10.2.1.2 step 4 */ + if (0 != providedData) { + unsigned int i; + for (i = 0U; i < sizeof temp; i++) { + temp[i] ^= providedData[i]; + } + } + + /* 10.2.1.2 step 5 */ + (void)tc_aes128_set_encrypt_key(&ctx->key, temp); + + /* 10.2.1.2 step 6 */ + memcpy(ctx->V, &(temp[TC_AES_KEY_SIZE]), TC_AES_BLOCK_SIZE); + } +} + +int tc_ctr_prng_init(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const personalization, + unsigned int pLen) +{ + int result = TC_CRYPTO_FAIL; + unsigned int i; + uint8_t personalization_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + uint8_t seed_material[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + uint8_t zeroArr[TC_AES_BLOCK_SIZE] = {0U}; + + if (0 != personalization) { + /* 10.2.1.3.1 step 1 */ + unsigned int len = pLen; + if (len > sizeof personalization_buf) { + len = sizeof personalization_buf; + } + + /* 10.2.1.3.1 step 2 */ + memcpy(personalization_buf, personalization, len); + } + + if ((0 != ctx) && (0 != entropy) && (entropyLen >= sizeof seed_material)) { + /* 10.2.1.3.1 step 3 */ + memcpy(seed_material, entropy, sizeof seed_material); + for (i = 0U; i < sizeof seed_material; i++) { + seed_material[i] ^= personalization_buf[i]; + } + + /* 10.2.1.3.1 step 4 */ + (void)tc_aes128_set_encrypt_key(&ctx->key, zeroArr); + + /* 10.2.1.3.1 step 5 */ + memset(ctx->V, 0x00, sizeof ctx->V); + + /* 10.2.1.3.1 step 6 */ + tc_ctr_prng_update(ctx, seed_material); + + /* 10.2.1.3.1 step 7 */ + ctx->reseedCount = 1U; + + result = TC_CRYPTO_SUCCESS; + } + return result; +} + +int tc_ctr_prng_reseed(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const additional_input, + unsigned int additionallen) +{ + unsigned int i; + int result = TC_CRYPTO_FAIL; + uint8_t additional_input_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + uint8_t seed_material[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + + if (0 != additional_input) { + /* 10.2.1.4.1 step 1 */ + unsigned int len = additionallen; + if (len > sizeof additional_input_buf) { + len = sizeof additional_input_buf; + } + + /* 10.2.1.4.1 step 2 */ + memcpy(additional_input_buf, additional_input, len); + } + + unsigned int seedlen = (unsigned int)TC_AES_KEY_SIZE + (unsigned int)TC_AES_BLOCK_SIZE; + if ((0 != ctx) && (entropyLen >= seedlen)) { + /* 10.2.1.4.1 step 3 */ + memcpy(seed_material, entropy, sizeof seed_material); + for (i = 0U; i < sizeof seed_material; i++) { + seed_material[i] ^= additional_input_buf[i]; + } + + /* 10.2.1.4.1 step 4 */ + tc_ctr_prng_update(ctx, seed_material); + + /* 10.2.1.4.1 step 5 */ + ctx->reseedCount = 1U; + + result = TC_CRYPTO_SUCCESS; + } + return result; +} + +int tc_ctr_prng_generate(TCCtrPrng_t * const ctx, + uint8_t const * const additional_input, + unsigned int additionallen, + uint8_t * const out, + unsigned int outlen) +{ + /* 2^48 - see section 10.2.1 */ + static const uint64_t MAX_REQS_BEFORE_RESEED = 0x1000000000000ULL; + + /* 2^19 bits - see section 10.2.1 */ + static const unsigned int MAX_BYTES_PER_REQ = 65536U; + + unsigned int result = TC_CRYPTO_FAIL; + + if ((0 != ctx) && (0 != out) && (outlen < MAX_BYTES_PER_REQ)) { + /* 10.2.1.5.1 step 1 */ + if (ctx->reseedCount > MAX_REQS_BEFORE_RESEED) { + result = TC_CTR_PRNG_RESEED_REQ; + } else { + uint8_t additional_input_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + if (0 != additional_input) { + /* 10.2.1.5.1 step 2 */ + unsigned int len = additionallen; + if (len > sizeof additional_input_buf) { + len = sizeof additional_input_buf; + } + memcpy(additional_input_buf, additional_input, len); + tc_ctr_prng_update(ctx, additional_input_buf); + } + + /* 10.2.1.5.1 step 3 - implicit */ + + /* 10.2.1.5.1 step 4 */ + unsigned int len = 0U; + while (len < outlen) { + unsigned int blocklen = outlen - len; + uint8_t output_block[TC_AES_BLOCK_SIZE]; + + /* 10.2.1.5.1 step 4.1 */ + arrInc(ctx->V, sizeof ctx->V); + + /* 10.2.1.5.1 step 4.2 */ + (void)tc_aes_encrypt(output_block, ctx->V, &ctx->key); + + /* 10.2.1.5.1 step 4.3/step 5 */ + if (blocklen > TC_AES_BLOCK_SIZE) { + blocklen = TC_AES_BLOCK_SIZE; + } + memcpy(&(out[len]), output_block, blocklen); + + len += blocklen; + } + + /* 10.2.1.5.1 step 6 */ + tc_ctr_prng_update(ctx, additional_input_buf); + + /* 10.2.1.5.1 step 7 */ + ctx->reseedCount++; + + /* 10.2.1.5.1 step 8 */ + result = TC_CRYPTO_SUCCESS; + } + } + + return result; +} + +void tc_ctr_prng_uninstantiate(TCCtrPrng_t * const ctx) +{ + if (0 != ctx) { + memset(ctx->key.words, 0x00, sizeof ctx->key.words); + memset(ctx->V, 0x00, sizeof ctx->V); + ctx->reseedCount = 0U; + } +} + + + + diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc.c new file mode 100644 index 000000000..46080bf61 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc.c @@ -0,0 +1,942 @@ +/* ecc.c - TinyCrypt implementation of common ECC functions */ + +/* + * Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +/* IMPORTANT: Make sure a cryptographically-secure PRNG is set and the platform + * has access to enough entropy in order to feed the PRNG regularly. */ +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +void uECC_set_rng(uECC_RNG_Function rng_function) +{ + g_rng_function = rng_function; +} + +uECC_RNG_Function uECC_get_rng(void) +{ + return g_rng_function; +} + +int uECC_curve_private_key_size(uECC_Curve curve) +{ + return BITS_TO_BYTES(curve->num_n_bits); +} + +int uECC_curve_public_key_size(uECC_Curve curve) +{ + return 2 * curve->num_bytes; +} + +void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) +{ + wordcount_t i; + for (i = 0; i < num_words; ++i) { + vli[i] = 0; + } +} + +uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words) +{ + uECC_word_t bits = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + bits |= vli[i]; + } + return (bits == 0); +} + +uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit) +{ + return (vli[bit >> uECC_WORD_BITS_SHIFT] & + ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); +} + +/* Counts the number of words in vli. */ +static wordcount_t vli_numDigits(const uECC_word_t *vli, + const wordcount_t max_words) +{ + + wordcount_t i; + /* Search from the end until we find a non-zero digit. We do it in reverse + * because we expect that most digits will be nonzero. */ + for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { + } + + return (i + 1); +} + +bitcount_t uECC_vli_numBits(const uECC_word_t *vli, + const wordcount_t max_words) +{ + + uECC_word_t i; + uECC_word_t digit; + + wordcount_t num_digits = vli_numDigits(vli, max_words); + if (num_digits == 0) { + return 0; + } + + digit = vli[num_digits - 1]; + for (i = 0; digit; ++i) { + digit >>= 1; + } + + return (((bitcount_t)(num_digits - 1) << uECC_WORD_BITS_SHIFT) + i); +} + +void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, + wordcount_t num_words) +{ + wordcount_t i; + + for (i = 0; i < num_words; ++i) { + dest[i] = src[i]; + } +} + +cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) +{ + wordcount_t i; + + for (i = num_words - 1; i >= 0; --i) { + if (left[i] > right[i]) { + return 1; + } else if (left[i] < right[i]) { + return -1; + } + } + return 0; +} + +uECC_word_t uECC_vli_equal(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words) +{ + + uECC_word_t diff = 0; + wordcount_t i; + + for (i = num_words - 1; i >= 0; --i) { + diff |= (left[i] ^ right[i]); + } + return !(diff == 0); +} + +uECC_word_t cond_set(uECC_word_t p_true, uECC_word_t p_false, unsigned int cond) +{ + return (p_true*(cond)) | (p_false*(!cond)); +} + +/* Computes result = left - right, returning borrow, in constant time. + * Can modify in place. */ +uECC_word_t uECC_vli_sub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words) +{ + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t diff = left[i] - right[i] - borrow; + uECC_word_t val = (diff > left[i]); + borrow = cond_set(val, borrow, (diff != left[i])); + + result[i] = diff; + } + return borrow; +} + +/* Computes result = left + right, returning carry, in constant time. + * Can modify in place. */ +static uECC_word_t uECC_vli_add(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words) +{ + uECC_word_t carry = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t sum = left[i] + right[i] + carry; + uECC_word_t val = (sum < left[i]); + carry = cond_set(val, carry, (sum != left[i])); + result[i] = sum; + } + return carry; +} + +cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words) +{ + uECC_word_t tmp[NUM_ECC_WORDS]; + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + uECC_word_t equal = uECC_vli_isZero(tmp, num_words); + return (!equal - 2 * neg); +} + +/* Computes vli = vli >> 1. */ +static void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) +{ + uECC_word_t *end = vli; + uECC_word_t carry = 0; + + vli += num_words; + while (vli-- > end) { + uECC_word_t temp = *vli; + *vli = (temp >> 1) | carry; + carry = temp << (uECC_WORD_BITS - 1); + } +} + +static void muladd(uECC_word_t a, uECC_word_t b, uECC_word_t *r0, + uECC_word_t *r1, uECC_word_t *r2) +{ + + uECC_dword_t p = (uECC_dword_t)a * b; + uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; + r01 += p; + *r2 += (r01 < p); + *r1 = r01 >> uECC_WORD_BITS; + *r0 = (uECC_word_t)r01; + +} + +/* Computes result = left * right. Result must be 2 * num_words long. */ +static void uECC_vli_mult(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words) +{ + + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t i, k; + + /* Compute each digit of result in sequence, maintaining the carries. */ + for (k = 0; k < num_words; ++k) { + + for (i = 0; i <= k; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + for (k = num_words; k < num_words * 2 - 1; ++k) { + + for (i = (k + 1) - num_words; i < num_words; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + result[num_words * 2 - 1] = r0; +} + +void uECC_vli_modAdd(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words) +{ + uECC_word_t carry = uECC_vli_add(result, left, right, num_words); + if (carry || uECC_vli_cmp_unsafe(mod, result, num_words) != 1) { + /* result > mod (result = mod + remainder), so subtract mod to get + * remainder. */ + uECC_vli_sub(result, result, mod, num_words); + } +} + +void uECC_vli_modSub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words) +{ + uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words); + if (l_borrow) { + /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x, + * we can get the correct result from result + mod (with overflow). */ + uECC_vli_add(result, result, mod, num_words); + } +} + +/* Computes result = product % mod, where product is 2N words long. */ +/* Currently only designed to work for curve_p or curve_n. */ +void uECC_vli_mmod(uECC_word_t *result, uECC_word_t *product, + const uECC_word_t *mod, wordcount_t num_words) +{ + uECC_word_t mod_multiple[2 * NUM_ECC_WORDS]; + uECC_word_t tmp[2 * NUM_ECC_WORDS]; + uECC_word_t *v[2] = {tmp, product}; + uECC_word_t index; + + /* Shift mod so its highest set bit is at the maximum position. */ + bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) - + uECC_vli_numBits(mod, num_words); + wordcount_t word_shift = shift / uECC_WORD_BITS; + wordcount_t bit_shift = shift % uECC_WORD_BITS; + uECC_word_t carry = 0; + uECC_vli_clear(mod_multiple, word_shift); + if (bit_shift > 0) { + for(index = 0; index < (uECC_word_t)num_words; ++index) { + mod_multiple[word_shift + index] = (mod[index] << bit_shift) | carry; + carry = mod[index] >> (uECC_WORD_BITS - bit_shift); + } + } else { + uECC_vli_set(mod_multiple + word_shift, mod, num_words); + } + + for (index = 1; shift >= 0; --shift) { + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words * 2; ++i) { + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + if (diff != v[index][i]) { + borrow = (diff > v[index][i]); + } + v[1 - index][i] = diff; + } + /* Swap the index if there was no borrow */ + index = !(index ^ borrow); + uECC_vli_rshift1(mod_multiple, num_words); + mod_multiple[num_words - 1] |= mod_multiple[num_words] << + (uECC_WORD_BITS - 1); + uECC_vli_rshift1(mod_multiple + num_words, num_words); + } + uECC_vli_set(result, v[index], num_words); +} + +void uECC_vli_modMult(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words) +{ + uECC_word_t product[2 * NUM_ECC_WORDS]; + uECC_vli_mult(product, left, right, num_words); + uECC_vli_mmod(result, product, mod, num_words); +} + +void uECC_vli_modMult_fast(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, uECC_Curve curve) +{ + uECC_word_t product[2 * NUM_ECC_WORDS]; + uECC_vli_mult(product, left, right, curve->num_words); + + curve->mmod_fast(result, product); +} + +static void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) +{ + uECC_vli_modMult_fast(result, left, left, curve); +} + + +#define EVEN(vli) (!(vli[0] & 1)) + +static void vli_modInv_update(uECC_word_t *uv, + const uECC_word_t *mod, + wordcount_t num_words) +{ + + uECC_word_t carry = 0; + + if (!EVEN(uv)) { + carry = uECC_vli_add(uv, uv, mod, num_words); + } + uECC_vli_rshift1(uv, num_words); + if (carry) { + uv[num_words - 1] |= HIGH_BIT_SET; + } +} + +void uECC_vli_modInv(uECC_word_t *result, const uECC_word_t *input, + const uECC_word_t *mod, wordcount_t num_words) +{ + uECC_word_t a[NUM_ECC_WORDS], b[NUM_ECC_WORDS]; + uECC_word_t u[NUM_ECC_WORDS], v[NUM_ECC_WORDS]; + cmpresult_t cmpResult; + + if (uECC_vli_isZero(input, num_words)) { + uECC_vli_clear(result, num_words); + return; + } + + uECC_vli_set(a, input, num_words); + uECC_vli_set(b, mod, num_words); + uECC_vli_clear(u, num_words); + u[0] = 1; + uECC_vli_clear(v, num_words); + while ((cmpResult = uECC_vli_cmp_unsafe(a, b, num_words)) != 0) { + if (EVEN(a)) { + uECC_vli_rshift1(a, num_words); + vli_modInv_update(u, mod, num_words); + } else if (EVEN(b)) { + uECC_vli_rshift1(b, num_words); + vli_modInv_update(v, mod, num_words); + } else if (cmpResult > 0) { + uECC_vli_sub(a, a, b, num_words); + uECC_vli_rshift1(a, num_words); + if (uECC_vli_cmp_unsafe(u, v, num_words) < 0) { + uECC_vli_add(u, u, mod, num_words); + } + uECC_vli_sub(u, u, v, num_words); + vli_modInv_update(u, mod, num_words); + } else { + uECC_vli_sub(b, b, a, num_words); + uECC_vli_rshift1(b, num_words); + if (uECC_vli_cmp_unsafe(v, u, num_words) < 0) { + uECC_vli_add(v, v, mod, num_words); + } + uECC_vli_sub(v, v, u, num_words); + vli_modInv_update(v, mod, num_words); + } + } + uECC_vli_set(result, u, num_words); +} + +/* ------ Point operations ------ */ + +void double_jacobian_default(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * Z1, uECC_Curve curve) +{ + /* t1 = X, t2 = Y, t3 = Z */ + uECC_word_t t4[NUM_ECC_WORDS]; + uECC_word_t t5[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + if (uECC_vli_isZero(Z1, num_words)) { + return; + } + + uECC_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ + uECC_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ + uECC_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ + uECC_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ + uECC_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ + + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ + uECC_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ + uECC_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ + uECC_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ + + uECC_vli_modAdd(Z1, X1, X1, curve->p, num_words); /* t3 = 2*(x1^2 - z1^4) */ + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = 3*(x1^2 - z1^4) */ + if (uECC_vli_testBit(X1, 0)) { + uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); + uECC_vli_rshift1(X1, num_words); + X1[num_words - 1] |= l_carry << (uECC_WORD_BITS - 1); + } else { + uECC_vli_rshift1(X1, num_words); + } + + /* t1 = 3/2*(x1^2 - z1^4) = B */ + uECC_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ + uECC_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ + /* t4 = B * (A - x3) - y1^4 = y3: */ + uECC_vli_modSub(t4, X1, t4, curve->p, num_words); + + uECC_vli_set(X1, Z1, num_words); + uECC_vli_set(Z1, Y1, num_words); + uECC_vli_set(Y1, t4, num_words); +} + +void x_side_default(uECC_word_t *result, + const uECC_word_t *x, + uECC_Curve curve) +{ + uECC_word_t _3[NUM_ECC_WORDS] = {3}; /* -a = 3 */ + wordcount_t num_words = curve->num_words; + + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ + /* r = x^3 - 3x + b: */ + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words); +} + +uECC_Curve uECC_secp256r1(void) +{ + return &curve_secp256r1; +} + +void vli_mmod_fast_secp256r1(unsigned int *result, unsigned int*product) +{ + unsigned int tmp[NUM_ECC_WORDS]; + int carry; + + /* t */ + uECC_vli_set(result, product, NUM_ECC_WORDS); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = 0; + tmp[3] = product[11]; + tmp[4] = product[12]; + tmp[5] = product[13]; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry = uECC_vli_add(tmp, tmp, tmp, NUM_ECC_WORDS); + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* s2 */ + tmp[3] = product[12]; + tmp[4] = product[13]; + tmp[5] = product[14]; + tmp[6] = product[15]; + tmp[7] = 0; + carry += uECC_vli_add(tmp, tmp, tmp, NUM_ECC_WORDS); + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* s3 */ + tmp[0] = product[8]; + tmp[1] = product[9]; + tmp[2] = product[10]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* s4 */ + tmp[0] = product[9]; + tmp[1] = product[10]; + tmp[2] = product[11]; + tmp[3] = product[13]; + tmp[4] = product[14]; + tmp[5] = product[15]; + tmp[6] = product[13]; + tmp[7] = product[8]; + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* d1 */ + tmp[0] = product[11]; + tmp[1] = product[12]; + tmp[2] = product[13]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[8]; + tmp[7] = product[10]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + /* d2 */ + tmp[0] = product[12]; + tmp[1] = product[13]; + tmp[2] = product[14]; + tmp[3] = product[15]; + tmp[4] = tmp[5] = 0; + tmp[6] = product[9]; + tmp[7] = product[11]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + /* d3 */ + tmp[0] = product[13]; + tmp[1] = product[14]; + tmp[2] = product[15]; + tmp[3] = product[8]; + tmp[4] = product[9]; + tmp[5] = product[10]; + tmp[6] = 0; + tmp[7] = product[12]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + /* d4 */ + tmp[0] = product[14]; + tmp[1] = product[15]; + tmp[2] = 0; + tmp[3] = product[9]; + tmp[4] = product[10]; + tmp[5] = product[11]; + tmp[6] = 0; + tmp[7] = product[13]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, NUM_ECC_WORDS); + } + while (carry < 0); + } else { + while (carry || + uECC_vli_cmp_unsafe(curve_secp256r1.p, result, NUM_ECC_WORDS) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, NUM_ECC_WORDS); + } + } +} + +uECC_word_t EccPoint_isZero(const uECC_word_t *point, uECC_Curve curve) +{ + return uECC_vli_isZero(point, curve->num_words * 2); +} + +void apply_z(uECC_word_t * X1, uECC_word_t * Y1, const uECC_word_t * const Z, + uECC_Curve curve) +{ + uECC_word_t t1[NUM_ECC_WORDS]; + + uECC_vli_modSquare_fast(t1, Z, curve); /* z^2 */ + uECC_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ + uECC_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ + uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ +} + +/* P = (x1, y1) => 2P, (x2, y2) => P' */ +static void XYcZ_initial_double(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * X2, uECC_word_t * Y2, + const uECC_word_t * const initial_Z, + uECC_Curve curve) +{ + uECC_word_t z[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + if (initial_Z) { + uECC_vli_set(z, initial_Z, num_words); + } else { + uECC_vli_clear(z, num_words); + z[0] = 1; + } + + uECC_vli_set(X2, X1, num_words); + uECC_vli_set(Y2, Y1, num_words); + + apply_z(X1, Y1, z, curve); + curve->double_jacobian(X1, Y1, z, curve); + apply_z(X2, Y2, z, curve); +} + +void XYcZ_add(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * X2, uECC_word_t * Y2, + uECC_Curve curve) +{ + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + uECC_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ + + uECC_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ + uECC_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ + uECC_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */ + uECC_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ + + uECC_vli_set(X2, t5, num_words); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) + or P => P - Q, Q => P + Q + */ +static void XYcZ_addC(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * X2, uECC_word_t * Y2, + uECC_Curve curve) +{ + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[NUM_ECC_WORDS]; + uECC_word_t t6[NUM_ECC_WORDS]; + uECC_word_t t7[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + + uECC_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ + uECC_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ + uECC_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ + uECC_vli_modSub(X2, X2, t6, curve->p, num_words); /* t3 = D - (B + C) = x3 */ + + uECC_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ + /* t4 = (y2 - y1)*(B - x3) - E = y3: */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); + + uECC_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ + uECC_vli_modSub(t7, t7, t6, curve->p, num_words); /* t7 = F - (B + C) = x3' */ + uECC_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ + uECC_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ + /* t2 = (y2+y1)*(x3' - B) - E = y3': */ + uECC_vli_modSub(Y1, t6, Y1, curve->p, num_words); + + uECC_vli_set(X1, t7, num_words); +} + +void EccPoint_mult(uECC_word_t * result, const uECC_word_t * point, + const uECC_word_t * scalar, + const uECC_word_t * initial_Z, + bitcount_t num_bits, uECC_Curve curve) +{ + /* R0 and R1 */ + uECC_word_t Rx[2][NUM_ECC_WORDS]; + uECC_word_t Ry[2][NUM_ECC_WORDS]; + uECC_word_t z[NUM_ECC_WORDS]; + bitcount_t i; + uECC_word_t nb; + wordcount_t num_words = curve->num_words; + + uECC_vli_set(Rx[1], point, num_words); + uECC_vli_set(Ry[1], point + num_words, num_words); + + XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve); + + for (i = num_bits - 2; i > 0; --i) { + nb = !uECC_vli_testBit(scalar, i); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + } + + nb = !uECC_vli_testBit(scalar, 0); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + + /* Find final 1/Z value. */ + uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ + uECC_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ + uECC_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ + uECC_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0))*/ + /* yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, point + num_words, curve); + /* Xb * yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, Rx[1 - nb], curve); + /* End 1/Z calculation */ + + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + apply_z(Rx[0], Ry[0], z, curve); + + uECC_vli_set(result, Rx[0], num_words); + uECC_vli_set(result + num_words, Ry[0], num_words); +} + +uECC_word_t regularize_k(const uECC_word_t * const k, uECC_word_t *k0, + uECC_word_t *k1, uECC_Curve curve) +{ + + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + + bitcount_t num_n_bits = curve->num_n_bits; + + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && + uECC_vli_testBit(k0, num_n_bits)); + + uECC_vli_add(k1, k0, curve->n, num_n_words); + + return carry; +} + +uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, + uECC_word_t *private_key, + uECC_Curve curve) +{ + + uECC_word_t tmp1[NUM_ECC_WORDS]; + uECC_word_t tmp2[NUM_ECC_WORDS]; + uECC_word_t *p2[2] = {tmp1, tmp2}; + uECC_word_t carry; + + /* Regularize the bitcount for the private key so that attackers cannot + * use a side channel attack to learn the number of leading zeros. */ + carry = regularize_k(private_key, tmp1, tmp2, curve); + + EccPoint_mult(result, curve->G, p2[!carry], 0, curve->num_n_bits + 1, curve); + + if (EccPoint_isZero(result, curve)) { + return 0; + } + return 1; +} + +/* Converts an integer in uECC native format to big-endian bytes. */ +void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, + const unsigned int *native) +{ + wordcount_t i; + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + bytes[i] = native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE)); + } +} + +/* Converts big-endian bytes to an integer in uECC native format. */ +void uECC_vli_bytesToNative(unsigned int *native, const uint8_t *bytes, + int num_bytes) +{ + wordcount_t i; + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + native[b / uECC_WORD_SIZE] |= + (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); + } +} + +int uECC_generate_random_int(uECC_word_t *random, const uECC_word_t *top, + wordcount_t num_words) +{ + uECC_word_t mask = (uECC_word_t)-1; + uECC_word_t tries; + bitcount_t num_bits = uECC_vli_numBits(top, num_words); + + if (!g_rng_function) { + return 0; + } + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + return 0; + } + random[num_words - 1] &= + mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + if (!uECC_vli_isZero(random, num_words) && + uECC_vli_cmp(top, random, num_words) == 1) { + return 1; + } + } + return 0; +} + + +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) +{ + uECC_word_t tmp1[NUM_ECC_WORDS]; + uECC_word_t tmp2[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + /* The point at infinity is invalid. */ + if (EccPoint_isZero(point, curve)) { + return -1; + } + + /* x and y must be smaller than p. */ + if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || + uECC_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { + return -2; + } + + uECC_vli_modSquare_fast(tmp1, point + num_words, curve); + curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ + + /* Make sure that y^2 == x^3 + ax + b */ + if (uECC_vli_equal(tmp1, tmp2, num_words) != 0) + return -3; + + return 0; +} + +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) +{ + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative( + _public + curve->num_words, + public_key + curve->num_bytes, + curve->num_bytes); + + if (uECC_vli_cmp_unsafe(_public, curve->G, NUM_ECC_WORDS * 2) == 0) { + return -4; + } + + return uECC_valid_point(_public, curve); +} + +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, + uECC_Curve curve) +{ + + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + uECC_vli_bytesToNative( + _private, + private_key, + BITS_TO_BYTES(curve->num_n_bits)); + + /* Make sure the private key is in the range [1, n-1]. */ + if (uECC_vli_isZero(_private, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (uECC_vli_cmp(curve->n, _private, BITS_TO_WORDS(curve->num_n_bits)) != 1) { + return 0; + } + + /* Compute public key. */ + if (!EccPoint_compute_public_key(_public, _private, curve)) { + return 0; + } + + uECC_vli_nativeToBytes(public_key, curve->num_bytes, _public); + uECC_vli_nativeToBytes( + public_key + + curve->num_bytes, curve->num_bytes, _public + curve->num_words); + return 1; +} + + + diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dh.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dh.c new file mode 100644 index 000000000..e5257d2d4 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dh.c @@ -0,0 +1,200 @@ +/* ec_dh.c - TinyCrypt implementation of EC-DH */ + +/* + * Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +int uECC_make_key_with_d(uint8_t *public_key, uint8_t *private_key, + unsigned int *d, uECC_Curve curve) +{ + + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + /* This function is designed for test purposes-only (such as validating NIST + * test vectors) as it uses a provided value for d instead of generating + * it uniformly at random. */ + memcpy (_private, d, NUM_ECC_BYTES); + + /* Computing public-key from private: */ + if (EccPoint_compute_public_key(_public, _private, curve)) { + + /* Converting buffers to correct bit order: */ + uECC_vli_nativeToBytes(private_key, + BITS_TO_BYTES(curve->num_n_bits), + _private); + uECC_vli_nativeToBytes(public_key, + curve->num_bytes, + _public); + uECC_vli_nativeToBytes(public_key + curve->num_bytes, + curve->num_bytes, + _public + curve->num_words); + + /* erasing temporary buffer used to store secret: */ + memset(_private, 0, NUM_ECC_BYTES); + + return 1; + } + return 0; +} + +int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve) +{ + + uECC_word_t _random[NUM_ECC_WORDS * 2]; + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + /* Generating _private uniformly at random: */ + uECC_RNG_Function rng_function = uECC_get_rng(); + if (!rng_function || + !rng_function((uint8_t *)_random, 2 * NUM_ECC_WORDS*uECC_WORD_SIZE)) { + return 0; + } + + /* computing modular reduction of _random (see FIPS 186.4 B.4.1): */ + uECC_vli_mmod(_private, _random, curve->n, BITS_TO_WORDS(curve->num_n_bits)); + + /* Computing public-key from private: */ + if (EccPoint_compute_public_key(_public, _private, curve)) { + + /* Converting buffers to correct bit order: */ + uECC_vli_nativeToBytes(private_key, + BITS_TO_BYTES(curve->num_n_bits), + _private); + uECC_vli_nativeToBytes(public_key, + curve->num_bytes, + _public); + uECC_vli_nativeToBytes(public_key + curve->num_bytes, + curve->num_bytes, + _public + curve->num_words); + + /* erasing temporary buffer that stored secret: */ + memset(_private, 0, NUM_ECC_BYTES); + + return 1; + } + } + return 0; +} + +int uECC_shared_secret(const uint8_t *public_key, const uint8_t *private_key, + uint8_t *secret, uECC_Curve curve) +{ + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t _private[NUM_ECC_WORDS]; + + uECC_word_t tmp[NUM_ECC_WORDS]; + uECC_word_t *p2[2] = {_private, tmp}; + uECC_word_t *initial_Z = 0; + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_bytes = curve->num_bytes; + int r; + + /* Converting buffers to correct bit order: */ + uECC_vli_bytesToNative(_private, + private_key, + BITS_TO_BYTES(curve->num_n_bits)); + uECC_vli_bytesToNative(_public, + public_key, + num_bytes); + uECC_vli_bytesToNative(_public + num_words, + public_key + num_bytes, + num_bytes); + + /* Regularize the bitcount for the private key so that attackers cannot use a + * side channel attack to learn the number of leading zeros. */ + carry = regularize_k(_private, _private, tmp, curve); + + /* If an RNG function was specified, try to get a random initial Z value to + * improve protection against side-channel attacks. */ + if (g_rng_function) { + if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) { + r = 0; + goto clear_and_out; + } + initial_Z = p2[carry]; + } + + EccPoint_mult(_public, _public, p2[!carry], initial_Z, curve->num_n_bits + 1, + curve); + + uECC_vli_nativeToBytes(secret, num_bytes, _public); + r = !EccPoint_isZero(_public, curve); + +clear_and_out: + /* erasing temporary buffer used to store secret: */ + memset(p2, 0, sizeof(p2)); + __asm__ __volatile__("" :: "g"(p2) : "memory"); + memset(tmp, 0, sizeof(tmp)); + __asm__ __volatile__("" :: "g"(tmp) : "memory"); + memset(_private, 0, sizeof(_private)); + __asm__ __volatile__("" :: "g"(_private) : "memory"); + + return r; +} diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dsa.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dsa.c new file mode 100644 index 000000000..064dfe5ae --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dsa.c @@ -0,0 +1,295 @@ +/* ec_dsa.c - TinyCrypt implementation of EC-DSA */ + +/* Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +static void bits2int(uECC_word_t *native, const uint8_t *bits, + unsigned bits_size, uECC_Curve curve) +{ + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); + int shift; + uECC_word_t carry; + uECC_word_t *ptr; + + if (bits_size > num_n_bytes) { + bits_size = num_n_bytes; + } + + uECC_vli_clear(native, num_n_words); + uECC_vli_bytesToNative(native, bits, bits_size); + if (bits_size * 8 <= (unsigned)curve->num_n_bits) { + return; + } + shift = bits_size * 8 - curve->num_n_bits; + carry = 0; + ptr = native + num_n_words; + while (ptr-- > native) { + uECC_word_t temp = *ptr; + *ptr = (temp >> shift) | carry; + carry = temp << (uECC_WORD_BITS - shift); + } + + /* Reduce mod curve_n */ + if (uECC_vli_cmp_unsafe(curve->n, native, num_n_words) != 1) { + uECC_vli_sub(native, native, curve->n, num_n_words); + } +} + +int uECC_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash, + unsigned hash_size, uECC_word_t *k, uint8_t *signature, + uECC_Curve curve) +{ + + uECC_word_t tmp[NUM_ECC_WORDS]; + uECC_word_t s[NUM_ECC_WORDS]; + uECC_word_t *k2[2] = {tmp, s}; + uECC_word_t p[NUM_ECC_WORDS * 2]; + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + + /* Make sure 0 < k < curve_n */ + if (uECC_vli_isZero(k, num_words) || + uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + return 0; + } + + carry = regularize_k(k, tmp, s, curve); + EccPoint_mult(p, curve->G, k2[!carry], 0, num_n_bits + 1, curve); + if (uECC_vli_isZero(p, num_words)) { + return 0; + } + + /* If an RNG function was specified, get a random number + to prevent side channel analysis of k. */ + if (!g_rng_function) { + uECC_vli_clear(tmp, num_n_words); + tmp[0] = 1; + } + else if (!uECC_generate_random_int(tmp, curve->n, num_n_words)) { + return 0; + } + + /* Prevent side channel analysis of uECC_vli_modInv() to determine + bits of k / the private key by premultiplying by a random number */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ + uECC_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ + + uECC_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ + + /* tmp = d: */ + uECC_vli_bytesToNative(tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); + + s[num_n_words - 1] = 0; + uECC_vli_set(s, p, num_words); + uECC_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ + + bits2int(tmp, message_hash, hash_size, curve); + uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ + uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ + if (uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) { + return 0; + } + + uECC_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); + return 1; +} + +int uECC_sign(const uint8_t *private_key, const uint8_t *message_hash, + unsigned hash_size, uint8_t *signature, uECC_Curve curve) +{ + uECC_word_t _random[2*NUM_ECC_WORDS]; + uECC_word_t k[NUM_ECC_WORDS]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + /* Generating _random uniformly at random: */ + uECC_RNG_Function rng_function = uECC_get_rng(); + if (!rng_function || + !rng_function((uint8_t *)_random, 2*NUM_ECC_WORDS*uECC_WORD_SIZE)) { + return 0; + } + + // computing k as modular reduction of _random (see FIPS 186.4 B.5.1): + uECC_vli_mmod(k, _random, curve->n, BITS_TO_WORDS(curve->num_n_bits)); + + if (uECC_sign_with_k(private_key, message_hash, hash_size, k, signature, + curve)) { + return 1; + } + } + return 0; +} + +static bitcount_t smax(bitcount_t a, bitcount_t b) +{ + return (a > b ? a : b); +} + +int uECC_verify(const uint8_t *public_key, const uint8_t *message_hash, + unsigned hash_size, const uint8_t *signature, + uECC_Curve curve) +{ + + uECC_word_t u1[NUM_ECC_WORDS], u2[NUM_ECC_WORDS]; + uECC_word_t z[NUM_ECC_WORDS]; + uECC_word_t sum[NUM_ECC_WORDS * 2]; + uECC_word_t rx[NUM_ECC_WORDS]; + uECC_word_t ry[NUM_ECC_WORDS]; + uECC_word_t tx[NUM_ECC_WORDS]; + uECC_word_t ty[NUM_ECC_WORDS]; + uECC_word_t tz[NUM_ECC_WORDS]; + const uECC_word_t *points[4]; + const uECC_word_t *point; + bitcount_t num_bits; + bitcount_t i; + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t r[NUM_ECC_WORDS], s[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + + rx[num_n_words - 1] = 0; + r[num_n_words - 1] = 0; + s[num_n_words - 1] = 0; + + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative(_public + num_words, public_key + curve->num_bytes, + curve->num_bytes); + uECC_vli_bytesToNative(r, signature, curve->num_bytes); + uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); + + /* r, s must not be 0. */ + if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) { + return 0; + } + + /* r, s must be < n. */ + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + return 0; + } + + /* Calculate u1 and u2. */ + uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ + u1[num_n_words - 1] = 0; + bits2int(u1, message_hash, hash_size, curve); + uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ + uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ + + /* Calculate sum = G + Q. */ + uECC_vli_set(sum, _public, num_words); + uECC_vli_set(sum + num_words, _public + num_words, num_words); + uECC_vli_set(tx, curve->G, num_words); + uECC_vli_set(ty, curve->G + num_words, num_words); + uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ + XYcZ_add(tx, ty, sum, sum + num_words, curve); + uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ + apply_z(sum, sum + num_words, z, curve); + + /* Use Shamir's trick to calculate u1*G + u2*Q */ + points[0] = 0; + points[1] = curve->G; + points[2] = _public; + points[3] = sum; + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + uECC_vli_numBits(u2, num_n_words)); + + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + uECC_vli_set(rx, point, num_words); + uECC_vli_set(ry, point + num_words, num_words); + uECC_vli_clear(z, num_words); + z[0] = 1; + + for (i = num_bits - 2; i >= 0; --i) { + uECC_word_t index; + curve->double_jacobian(rx, ry, z, curve); + + index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1); + point = points[index]; + if (point) { + uECC_vli_set(tx, point, num_words); + uECC_vli_set(ty, point + num_words, num_words); + apply_z(tx, ty, z, curve); + uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ + XYcZ_add(tx, ty, rx, ry, curve); + uECC_vli_modMult_fast(z, z, tz, curve); + } + } + + uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ + apply_z(rx, ry, z, curve); + + /* v = x1 (mod n) */ + if (uECC_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { + uECC_vli_sub(rx, rx, curve->n, num_n_words); + } + + /* Accept only if v == r. */ + return (int)(uECC_vli_equal(rx, r, num_words) == 0); +} + diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_platform_specific.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_platform_specific.c new file mode 100644 index 000000000..1867988f9 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_platform_specific.c @@ -0,0 +1,105 @@ +/* uECC_platform_specific.c - Implementation of platform specific functions*/ + +/* Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * uECC_platform_specific.c -- Implementation of platform specific functions + */ + + +#if defined(unix) || defined(__linux__) || defined(__unix__) || \ + defined(__unix) | (defined(__APPLE__) && defined(__MACH__)) || \ + defined(uECC_POSIX) + +/* Some POSIX-like system with /dev/urandom or /dev/random. */ +#include +#include +#include + +#include + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +int default_CSPRNG(uint8_t *dest, unsigned int size) { + + /* input sanity check: */ + if (dest == (uint8_t *) 0 || (size <= 0)) + return 0; + + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + fd = open("/dev/random", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + return 0; + } + } + + char *ptr = (char *)dest; + size_t left = (size_t) size; + while (left > 0) { + ssize_t bytes_read = read(fd, ptr, left); + if (bytes_read <= 0) { // read failed + close(fd); + return 0; + } + left -= bytes_read; + ptr += bytes_read; + } + + close(fd); + return 1; +} + +#endif /* platform */ + diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac.c new file mode 100644 index 000000000..89878cec7 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac.c @@ -0,0 +1,148 @@ +/* hmac.c - TinyCrypt implementation of the HMAC algorithm */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +static void rekey(uint8_t *key, const uint8_t *new_key, unsigned int key_size) +{ + const uint8_t inner_pad = (uint8_t) 0x36; + const uint8_t outer_pad = (uint8_t) 0x5c; + unsigned int i; + + for (i = 0; i < key_size; ++i) { + key[i] = inner_pad ^ new_key[i]; + key[i + TC_SHA256_BLOCK_SIZE] = outer_pad ^ new_key[i]; + } + for (; i < TC_SHA256_BLOCK_SIZE; ++i) { + key[i] = inner_pad; key[i + TC_SHA256_BLOCK_SIZE] = outer_pad; + } +} + +int tc_hmac_set_key(TCHmacState_t ctx, const uint8_t *key, + unsigned int key_size) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0 || + key == (const uint8_t *) 0 || + key_size == 0) { + return TC_CRYPTO_FAIL; + } + + const uint8_t dummy_key[key_size]; + struct tc_hmac_state_struct dummy_state; + + if (key_size <= TC_SHA256_BLOCK_SIZE) { + /* + * The next three lines consist of dummy calls just to avoid + * certain timing attacks. Without these dummy calls, + * adversaries would be able to learn whether the key_size is + * greater than TC_SHA256_BLOCK_SIZE by measuring the time + * consumed in this process. + */ + (void)tc_sha256_init(&dummy_state.hash_state); + (void)tc_sha256_update(&dummy_state.hash_state, + dummy_key, + key_size); + (void)tc_sha256_final(&dummy_state.key[TC_SHA256_DIGEST_SIZE], + &dummy_state.hash_state); + + /* Actual code for when key_size <= TC_SHA256_BLOCK_SIZE: */ + rekey(ctx->key, key, key_size); + } else { + (void)tc_sha256_init(&ctx->hash_state); + (void)tc_sha256_update(&ctx->hash_state, key, key_size); + (void)tc_sha256_final(&ctx->key[TC_SHA256_DIGEST_SIZE], + &ctx->hash_state); + rekey(ctx->key, + &ctx->key[TC_SHA256_DIGEST_SIZE], + TC_SHA256_DIGEST_SIZE); + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_init(TCHmacState_t ctx) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void) tc_sha256_init(&ctx->hash_state); + (void) tc_sha256_update(&ctx->hash_state, ctx->key, TC_SHA256_BLOCK_SIZE); + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_update(TCHmacState_t ctx, + const void *data, + unsigned int data_length) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)tc_sha256_update(&ctx->hash_state, data, data_length); + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_final(uint8_t *tag, unsigned int taglen, TCHmacState_t ctx) +{ + + /* input sanity check: */ + if (tag == (uint8_t *) 0 || + taglen != TC_SHA256_DIGEST_SIZE || + ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void) tc_sha256_final(tag, &ctx->hash_state); + + (void)tc_sha256_init(&ctx->hash_state); + (void)tc_sha256_update(&ctx->hash_state, + &ctx->key[TC_SHA256_BLOCK_SIZE], + TC_SHA256_BLOCK_SIZE); + (void)tc_sha256_update(&ctx->hash_state, tag, TC_SHA256_DIGEST_SIZE); + (void)tc_sha256_final(tag, &ctx->hash_state); + + /* destroy the current state */ + _set(ctx, 0, sizeof(*ctx)); + + return TC_CRYPTO_SUCCESS; +} diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac_prng.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac_prng.c new file mode 100644 index 000000000..68b5b1faf --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac_prng.c @@ -0,0 +1,212 @@ +/* hmac_prng.c - TinyCrypt implementation of HMAC-PRNG */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +/* + * min bytes in the seed string. + * MIN_SLEN*8 must be at least the expected security level. + */ +static const unsigned int MIN_SLEN = 32; + +/* + * max bytes in the seed string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_SLEN = UINT32_MAX; + +/* + * max bytes in the personalization string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_PLEN = UINT32_MAX; + +/* + * max bytes in the additional_info string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_ALEN = UINT32_MAX; + +/* + * max number of generates between re-seeds; + * TinyCrypt accepts up to (2^32 - 1) which is the maximal value of + * a 32-bit unsigned int variable, while SP800-90A specifies a maximum of 2^48. + */ +static const unsigned int MAX_GENS = UINT32_MAX; + +/* + * maximum bytes per generate call; + * SP800-90A specifies a maximum up to 2^19. + */ +static const unsigned int MAX_OUT = (1 << 19); + +/* + * Assumes: prng != NULL, e != NULL, len >= 0. + */ +static void update(TCHmacPrng_t prng, const uint8_t *e, unsigned int len) +{ + const uint8_t separator0 = 0x00; + const uint8_t separator1 = 0x01; + + /* use current state, e and separator 0 to compute a new prng key: */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_update(&prng->h, &separator0, sizeof(separator0)); + (void)tc_hmac_update(&prng->h, e, len); + (void)tc_hmac_final(prng->key, sizeof(prng->key), &prng->h); + /* configure the new prng key into the prng's instance of hmac */ + (void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* use the new key to compute a new state variable v */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); + + /* use current state, e and separator 1 to compute a new prng key: */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_update(&prng->h, &separator1, sizeof(separator1)); + (void)tc_hmac_update(&prng->h, e, len); + (void)tc_hmac_final(prng->key, sizeof(prng->key), &prng->h); + /* configure the new prng key into the prng's instance of hmac */ + (void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* use the new key to compute a new state variable v */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); +} + +int tc_hmac_prng_init(TCHmacPrng_t prng, + const uint8_t *personalization, + unsigned int plen) +{ + + /* input sanity check: */ + if (prng == (TCHmacPrng_t) 0 || + personalization == (uint8_t *) 0 || + plen > MAX_PLEN) { + return TC_CRYPTO_FAIL; + } + + /* put the generator into a known state: */ + _set(prng->key, 0x00, sizeof(prng->key)); + _set(prng->v, 0x01, sizeof(prng->v)); + tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + /* update assumes SOME key has been configured into HMAC */ + + update(prng, personalization, plen); + + /* force a reseed before allowing tc_hmac_prng_generate to succeed: */ + prng->countdown = 0; + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_prng_reseed(TCHmacPrng_t prng, + const uint8_t *seed, + unsigned int seedlen, + const uint8_t *additional_input, + unsigned int additionallen) +{ + + /* input sanity check: */ + if (prng == (TCHmacPrng_t) 0 || + seed == (const uint8_t *) 0 || + seedlen < MIN_SLEN || + seedlen > MAX_SLEN) { + return TC_CRYPTO_FAIL; + } + + if (additional_input != (const uint8_t *) 0) { + /* + * Abort if additional_input is provided but has inappropriate + * length + */ + if (additionallen == 0 || + additionallen > MAX_ALEN) { + return TC_CRYPTO_FAIL; + } else { + /* call update for the seed and additional_input */ + update(prng, seed, seedlen); + update(prng, additional_input, additionallen); + } + } else { + /* call update only for the seed */ + update(prng, seed, seedlen); + } + + /* ... and enable hmac_prng_generate */ + prng->countdown = MAX_GENS; + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_prng_generate(uint8_t *out, unsigned int outlen, TCHmacPrng_t prng) +{ + unsigned int bufferlen; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + prng == (TCHmacPrng_t) 0 || + outlen == 0 || + outlen > MAX_OUT) { + return TC_CRYPTO_FAIL; + } else if (prng->countdown == 0) { + return TC_HMAC_PRNG_RESEED_REQ; + } + + prng->countdown--; + + while (outlen != 0) { + /* operate HMAC in OFB mode to create "random" outputs */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); + + bufferlen = (TC_SHA256_DIGEST_SIZE > outlen) ? + outlen : TC_SHA256_DIGEST_SIZE; + (void)_copy(out, bufferlen, prng->v, bufferlen); + + out += bufferlen; + outlen = (outlen > TC_SHA256_DIGEST_SIZE) ? + (outlen - TC_SHA256_DIGEST_SIZE) : 0; + } + + /* block future PRNG compromises from revealing past state */ + update(prng, prng->v, TC_SHA256_DIGEST_SIZE); + + return TC_CRYPTO_SUCCESS; +} diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/sha256.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/sha256.c new file mode 100644 index 000000000..b4efd2044 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/sha256.c @@ -0,0 +1,217 @@ +/* sha256.c - TinyCrypt SHA-256 crypto hash algorithm implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +static void compress(unsigned int *iv, const uint8_t *data); + +int tc_sha256_init(TCSha256State_t s) +{ + /* input sanity check: */ + if (s == (TCSha256State_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* + * Setting the initial state values. + * These values correspond to the first 32 bits of the fractional parts + * of the square roots of the first 8 primes: 2, 3, 5, 7, 11, 13, 17 + * and 19. + */ + _set((uint8_t *) s, 0x00, sizeof(*s)); + s->iv[0] = 0x6a09e667; + s->iv[1] = 0xbb67ae85; + s->iv[2] = 0x3c6ef372; + s->iv[3] = 0xa54ff53a; + s->iv[4] = 0x510e527f; + s->iv[5] = 0x9b05688c; + s->iv[6] = 0x1f83d9ab; + s->iv[7] = 0x5be0cd19; + + return TC_CRYPTO_SUCCESS; +} + +int tc_sha256_update(TCSha256State_t s, const uint8_t *data, size_t datalen) +{ + /* input sanity check: */ + if (s == (TCSha256State_t) 0 || + data == (void *) 0) { + return TC_CRYPTO_FAIL; + } else if (datalen == 0) { + return TC_CRYPTO_SUCCESS; + } + + while (datalen-- > 0) { + s->leftover[s->leftover_offset++] = *(data++); + if (s->leftover_offset >= TC_SHA256_BLOCK_SIZE) { + compress(s->iv, s->leftover); + s->leftover_offset = 0; + s->bits_hashed += (TC_SHA256_BLOCK_SIZE << 3); + } + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_sha256_final(uint8_t *digest, TCSha256State_t s) +{ + unsigned int i; + + /* input sanity check: */ + if (digest == (uint8_t *) 0 || + s == (TCSha256State_t) 0) { + return TC_CRYPTO_FAIL; + } + + s->bits_hashed += (s->leftover_offset << 3); + + s->leftover[s->leftover_offset++] = 0x80; /* always room for one byte */ + if (s->leftover_offset > (sizeof(s->leftover) - 8)) { + /* there is not room for all the padding in this block */ + _set(s->leftover + s->leftover_offset, 0x00, + sizeof(s->leftover) - s->leftover_offset); + compress(s->iv, s->leftover); + s->leftover_offset = 0; + } + + /* add the padding and the length in big-Endian format */ + _set(s->leftover + s->leftover_offset, 0x00, + sizeof(s->leftover) - 8 - s->leftover_offset); + s->leftover[sizeof(s->leftover) - 1] = (uint8_t)(s->bits_hashed); + s->leftover[sizeof(s->leftover) - 2] = (uint8_t)(s->bits_hashed >> 8); + s->leftover[sizeof(s->leftover) - 3] = (uint8_t)(s->bits_hashed >> 16); + s->leftover[sizeof(s->leftover) - 4] = (uint8_t)(s->bits_hashed >> 24); + s->leftover[sizeof(s->leftover) - 5] = (uint8_t)(s->bits_hashed >> 32); + s->leftover[sizeof(s->leftover) - 6] = (uint8_t)(s->bits_hashed >> 40); + s->leftover[sizeof(s->leftover) - 7] = (uint8_t)(s->bits_hashed >> 48); + s->leftover[sizeof(s->leftover) - 8] = (uint8_t)(s->bits_hashed >> 56); + + /* hash the padding and length */ + compress(s->iv, s->leftover); + + /* copy the iv out to digest */ + for (i = 0; i < TC_SHA256_STATE_BLOCKS; ++i) { + unsigned int t = *((unsigned int *) &s->iv[i]); + *digest++ = (uint8_t)(t >> 24); + *digest++ = (uint8_t)(t >> 16); + *digest++ = (uint8_t)(t >> 8); + *digest++ = (uint8_t)(t); + } + + /* destroy the current state */ + _set(s, 0, sizeof(*s)); + + return TC_CRYPTO_SUCCESS; +} + +/* + * Initializing SHA-256 Hash constant words K. + * These values correspond to the first 32 bits of the fractional parts of the + * cube roots of the first 64 primes between 2 and 311. + */ +static const unsigned int k256[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static inline unsigned int ROTR(unsigned int a, unsigned int n) +{ + return (((a) >> n) | ((a) << (32 - n))); +} + +#define Sigma0(a)(ROTR((a), 2) ^ ROTR((a), 13) ^ ROTR((a), 22)) +#define Sigma1(a)(ROTR((a), 6) ^ ROTR((a), 11) ^ ROTR((a), 25)) +#define sigma0(a)(ROTR((a), 7) ^ ROTR((a), 18) ^ ((a) >> 3)) +#define sigma1(a)(ROTR((a), 17) ^ ROTR((a), 19) ^ ((a) >> 10)) + +#define Ch(a, b, c)(((a) & (b)) ^ ((~(a)) & (c))) +#define Maj(a, b, c)(((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))) + +static inline unsigned int BigEndian(const uint8_t **c) +{ + unsigned int n = 0; + + n = (((unsigned int)(*((*c)++))) << 24); + n |= ((unsigned int)(*((*c)++)) << 16); + n |= ((unsigned int)(*((*c)++)) << 8); + n |= ((unsigned int)(*((*c)++))); + return n; +} + +static void compress(unsigned int *iv, const uint8_t *data) +{ + unsigned int a, b, c, d, e, f, g, h; + unsigned int s0, s1; + unsigned int t1, t2; + unsigned int work_space[16]; + unsigned int n; + unsigned int i; + + a = iv[0]; b = iv[1]; c = iv[2]; d = iv[3]; + e = iv[4]; f = iv[5]; g = iv[6]; h = iv[7]; + + for (i = 0; i < 16; ++i) { + n = BigEndian(&data); + t1 = work_space[i] = n; + t1 += h + Sigma1(e) + Ch(e, f, g) + k256[i]; + t2 = Sigma0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + for ( ; i < 64; ++i) { + s0 = work_space[(i+1)&0x0f]; + s0 = sigma0(s0); + s1 = work_space[(i+14)&0x0f]; + s1 = sigma1(s1); + + t1 = work_space[i&0xf] += s0 + s1 + work_space[(i+9)&0xf]; + t1 += h + Sigma1(e) + Ch(e, f, g) + k256[i]; + t2 = Sigma0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + iv[0] += a; iv[1] += b; iv[2] += c; iv[3] += d; + iv[4] += e; iv[5] += f; iv[6] += g; iv[7] += h; +} diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/utils.c b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/utils.c new file mode 100644 index 000000000..13cc49512 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/utils.c @@ -0,0 +1,74 @@ +/* utils.c - TinyCrypt platform-dependent run-time operations */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#define MASK_TWENTY_SEVEN 0x1b + +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len) +{ + if (from_len <= to_len) { + (void)memcpy(to, from, from_len); + return from_len; + } else { + return TC_CRYPTO_FAIL; + } +} + +void _set(void *to, uint8_t val, unsigned int len) +{ + (void)memset(to, val, len); +} + +/* + * Doubles the value of a byte for values up to 127. + */ +uint8_t _double_byte(uint8_t a) +{ + return ((a<<1) ^ ((a>>7) * MASK_TWENTY_SEVEN)); +} + +int _compare(const uint8_t *a, const uint8_t *b, size_t size) +{ + const uint8_t *tempa = a; + const uint8_t *tempb = b; + uint8_t result = 0; + + for (unsigned int i = 0; i < size; i++) { + result |= tempa[i] ^ tempb[i]; + } + return result; +} diff --git a/libesp32/NimBLE-Arduino/src/hal/hal_timer.h b/libesp32/NimBLE-Arduino/src/hal/hal_timer.h new file mode 100644 index 000000000..be41c6095 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/hal/hal_timer.h @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +/** + * @addtogroup HAL + * @{ + * @defgroup HALTimer HAL Timer + * @{ + */ + +#ifndef H_HAL_TIMER_ +#define H_HAL_TIMER_ + +#include +#include "os/queue.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* HAL timer callback */ +typedef void (*hal_timer_cb)(void *arg); + +/** + * The HAL timer structure. The user can declare as many of these structures + * as desired. They are enqueued on a particular HW timer queue when the user + * calls the :c:func:`hal_timer_start()` or :c:func:`hal_timer_start_at()` API. + * The user must have called :c:func:`hal_timer_set_cb()` before starting a + * timer. + * + * NOTE: the user should not have to modify/examine the contents of this + * structure; the hal timer API should be used. + */ +struct hal_timer { + /** Internal platform specific pointer */ + void *bsp_timer; + /** Callback function */ + hal_timer_cb cb_func; + /** Callback argument */ + void *cb_arg; + /** Tick at which timer should expire */ + uint32_t expiry; + TAILQ_ENTRY(hal_timer) link; /* Queue linked list structure */ +}; + +/** + * Initialize a HW timer. + * + * @param timer_num The number of the HW timer to initialize + * @param cfg Hardware specific timer configuration. This is + * passed from BSP directly to the MCU specific driver. + */ +int hal_timer_init(int timer_num, void *cfg); + +/** + * Un-initialize a HW timer. + * + * @param timer_num The number of the HW timer to un-initialize + */ +int hal_timer_deinit(int timer_num); + +/** + * Config a HW timer at the given frequency and start it. If the exact + * frequency is not obtainable the closest obtainable frequency is set. + * + * @param timer_num The number of the HW timer to configure + * @param freq_hz The frequency in Hz to configure the timer at + * + * @return 0 on success, non-zero error code on failure + */ +int hal_timer_config(int timer_num, uint32_t freq_hz); + +/** + * Returns the resolution of the HW timer. NOTE: the frequency may not be + * obtainable so the caller can use this to determine the resolution. + * Returns resolution in nanoseconds. A return value of 0 indicates an invalid + * timer was used. + * + * @param timer_num The number of the HW timer to get resolution for + * + * @return The resolution of the timer + */ +uint32_t hal_timer_get_resolution(int timer_num); + +/** + * Returns the HW timer current tick value + * + * @param timer_num The HW timer to read the tick value from + * + * @return The current tick value + */ +uint32_t hal_timer_read(int timer_num); + +/** + * Perform a blocking delay for a number of ticks. + * + * @param timer_num The timer number to use for the blocking delay + * @param ticks The number of ticks to delay for + * + * @return 0 on success, non-zero error code on failure + */ +int hal_timer_delay(int timer_num, uint32_t ticks); + +/** + * Set the timer structure prior to use. Should not be called if the timer + * is running. Must be called at least once prior to using timer. + * + * @param timer_num The number of the HW timer to configure the callback on + * @param tmr The timer structure to use for this timer + * @param cb_func The timer callback to call when the timer fires + * @param arg An opaque argument to provide the timer callback + * + * @return 0 on success, non-zero error code on failure. + */ +int hal_timer_set_cb(int timer_num, struct hal_timer *tmr, hal_timer_cb cb_func, + void *arg); + +/** + * Start a timer that will expire in 'ticks' ticks. Ticks cannot be 0 + * + * @param tmr The timer to start + * @param ticks The number of ticks to expire the timer in + * + * @return 0 on success, non-zero error code on failure. + */ +int hal_timer_start(struct hal_timer *tmr, uint32_t ticks); + +/** + * Start a timer that will expire when the timer reaches 'tick'. If tick + * has already passed the timer callback will be called "immediately" (at + * interrupt context). + * + * @param tmr The timer to start + * @param tick The absolute tick value to fire the timer at + * + * @return 0 on success, non-zero error code on failure. + */ +int hal_timer_start_at(struct hal_timer *tmr, uint32_t tick); + +/** + * Stop a currently running timer; associated callback will NOT be called + * + * @param tmr The timer to stop + */ +int hal_timer_stop(struct hal_timer *tmr); + +#ifdef __cplusplus +} +#endif + +#endif /* H_HAL_TIMER_ */ + +/** + * @} HALTimer + * @} HAL + */ diff --git a/libesp32/NimBLE-Arduino/src/host/ble_att.h b/libesp32/NimBLE-Arduino/src/host/ble_att.h new file mode 100644 index 000000000..391a992ae --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_att.h @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_ATT_ +#define H_BLE_ATT_ + +/** + * @brief Bluetooth Attribute Protocol (ATT) + * @defgroup bt_att Bluetooth Attribute Protocol (ATT) + * @ingroup bt_host + * @{ + */ + +#include "os/queue.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mbuf; + +#define BLE_ATT_UUID_PRIMARY_SERVICE 0x2800 +#define BLE_ATT_UUID_SECONDARY_SERVICE 0x2801 +#define BLE_ATT_UUID_INCLUDE 0x2802 +#define BLE_ATT_UUID_CHARACTERISTIC 0x2803 + +#define BLE_ATT_ERR_INVALID_HANDLE 0x01 +#define BLE_ATT_ERR_READ_NOT_PERMITTED 0x02 +#define BLE_ATT_ERR_WRITE_NOT_PERMITTED 0x03 +#define BLE_ATT_ERR_INVALID_PDU 0x04 +#define BLE_ATT_ERR_INSUFFICIENT_AUTHEN 0x05 +#define BLE_ATT_ERR_REQ_NOT_SUPPORTED 0x06 +#define BLE_ATT_ERR_INVALID_OFFSET 0x07 +#define BLE_ATT_ERR_INSUFFICIENT_AUTHOR 0x08 +#define BLE_ATT_ERR_PREPARE_QUEUE_FULL 0x09 +#define BLE_ATT_ERR_ATTR_NOT_FOUND 0x0a +#define BLE_ATT_ERR_ATTR_NOT_LONG 0x0b +#define BLE_ATT_ERR_INSUFFICIENT_KEY_SZ 0x0c +#define BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN 0x0d +#define BLE_ATT_ERR_UNLIKELY 0x0e +#define BLE_ATT_ERR_INSUFFICIENT_ENC 0x0f +#define BLE_ATT_ERR_UNSUPPORTED_GROUP 0x10 +#define BLE_ATT_ERR_INSUFFICIENT_RES 0x11 + +#define BLE_ATT_OP_ERROR_RSP 0x01 +#define BLE_ATT_OP_MTU_REQ 0x02 +#define BLE_ATT_OP_MTU_RSP 0x03 +#define BLE_ATT_OP_FIND_INFO_REQ 0x04 +#define BLE_ATT_OP_FIND_INFO_RSP 0x05 +#define BLE_ATT_OP_FIND_TYPE_VALUE_REQ 0x06 +#define BLE_ATT_OP_FIND_TYPE_VALUE_RSP 0x07 +#define BLE_ATT_OP_READ_TYPE_REQ 0x08 +#define BLE_ATT_OP_READ_TYPE_RSP 0x09 +#define BLE_ATT_OP_READ_REQ 0x0a +#define BLE_ATT_OP_READ_RSP 0x0b +#define BLE_ATT_OP_READ_BLOB_REQ 0x0c +#define BLE_ATT_OP_READ_BLOB_RSP 0x0d +#define BLE_ATT_OP_READ_MULT_REQ 0x0e +#define BLE_ATT_OP_READ_MULT_RSP 0x0f +#define BLE_ATT_OP_READ_GROUP_TYPE_REQ 0x10 +#define BLE_ATT_OP_READ_GROUP_TYPE_RSP 0x11 +#define BLE_ATT_OP_WRITE_REQ 0x12 +#define BLE_ATT_OP_WRITE_RSP 0x13 +#define BLE_ATT_OP_PREP_WRITE_REQ 0x16 +#define BLE_ATT_OP_PREP_WRITE_RSP 0x17 +#define BLE_ATT_OP_EXEC_WRITE_REQ 0x18 +#define BLE_ATT_OP_EXEC_WRITE_RSP 0x19 +#define BLE_ATT_OP_NOTIFY_REQ 0x1b +#define BLE_ATT_OP_INDICATE_REQ 0x1d +#define BLE_ATT_OP_INDICATE_RSP 0x1e +#define BLE_ATT_OP_WRITE_CMD 0x52 + +#define BLE_ATT_ATTR_MAX_LEN 512 + +#define BLE_ATT_F_READ 0x01 +#define BLE_ATT_F_WRITE 0x02 +#define BLE_ATT_F_READ_ENC 0x04 +#define BLE_ATT_F_READ_AUTHEN 0x08 +#define BLE_ATT_F_READ_AUTHOR 0x10 +#define BLE_ATT_F_WRITE_ENC 0x20 +#define BLE_ATT_F_WRITE_AUTHEN 0x40 +#define BLE_ATT_F_WRITE_AUTHOR 0x80 + +#define HA_FLAG_PERM_RW (BLE_ATT_F_READ | BLE_ATT_F_WRITE) + +#define BLE_ATT_ACCESS_OP_READ 1 +#define BLE_ATT_ACCESS_OP_WRITE 2 + +/** Default ATT MTU. Also the minimum. */ +#define BLE_ATT_MTU_DFLT 23 + +/** + * An ATT MTU of 527 allows the largest ATT command (signed write) to contain a + * 512-byte attribute value. + */ +#define BLE_ATT_MTU_MAX 527 + +/** + * Reads a locally registered attribute. If the specified attribute handle + * corresponds to a GATT characteristic value or descriptor, the read is + * performed by calling the registered GATT access callback. + * + * @param attr_handle The 16-bit handle of the attribute to read. + * @param out_om On success, this is made to point to a + * newly-allocated mbuf containing the + * attribute data read. + * + * @return 0 on success; + * NimBLE host ATT return code if the attribute + * access callback reports failure; + * NimBLE host core return code on unexpected + * error. + */ +int ble_att_svr_read_local(uint16_t attr_handle, struct os_mbuf **out_om); + +/** + * Writes a locally registered attribute. This function consumes the supplied + * mbuf regardless of the outcome. If the specified attribute handle + * corresponds to a GATT characteristic value or descriptor, the write is + * performed by calling the registered GATT access callback. + * + * @param attr_handle The 16-bit handle of the attribute to write. + * @param om The value to write to the attribute. + * + * @return 0 on success; + * NimBLE host ATT return code if the attribute + * access callback reports failure; + * NimBLE host core return code on unexpected + * error. + */ +int ble_att_svr_write_local(uint16_t attr_handle, struct os_mbuf *om); + +/** + * Retrieves the ATT MTU of the specified connection. If an MTU exchange for + * this connection has occurred, the MTU is the lower of the two peers' + * preferred values. Otherwise, the MTU is the default value of 23. + * + * @param conn_handle The handle of the connection to query. + * + * @return The specified connection's ATT MTU, or 0 if + * there is no such connection. + */ +uint16_t ble_att_mtu(uint16_t conn_handle); + +/** + * Retrieves the preferred ATT MTU. This is the value indicated by the device + * during an ATT MTU exchange. + * + * @return The preferred ATT MTU. + */ +uint16_t ble_att_preferred_mtu(void); + +/** + * Sets the preferred ATT MTU; the device will indicate this value in all + * subsequent ATT MTU exchanges. The ATT MTU of a connection is equal to the + * lower of the two peers' preferred MTU values. The ATT MTU is what dictates + * the maximum size of any message sent during a GATT procedure. + * + * The specified MTU must be within the following range: [23, BLE_ATT_MTU_MAX]. + * 23 is a minimum imposed by the Bluetooth specification; BLE_ATT_MTU_MAX is a + * NimBLE compile-time setting. + * + * @param mtu The preferred ATT MTU. + * + * @return 0 on success; + * BLE_HS_EINVAL if the specified value is not + * within the allowed range. + */ +int ble_att_set_preferred_mtu(uint16_t mtu); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_eddystone.h b/libesp32/NimBLE-Arduino/src/host/ble_eddystone.h new file mode 100644 index 000000000..76b7e2b01 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_eddystone.h @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_EDDYSTONE_ +#define H_BLE_EDDYSTONE_ + +/** + * @brief Eddystone - BLE beacon from Google + * @defgroup bt_eddystone Eddystone - BLE beacon from Google + * @ingroup bt_host + * @{ + */ + +#include +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_adv_fields; + +#define BLE_EDDYSTONE_MAX_UUIDS16 3 +#define BLE_EDDYSTONE_URL_MAX_LEN 17 + +#define BLE_EDDYSTONE_URL_SCHEME_HTTP_WWW 0 +#define BLE_EDDYSTONE_URL_SCHEME_HTTPS_WWW 1 +#define BLE_EDDYSTONE_URL_SCHEME_HTTP 2 +#define BLE_EDDYSTONE_URL_SCHEME_HTTPS 3 + +#define BLE_EDDYSTONE_URL_SUFFIX_COM_SLASH 0x00 +#define BLE_EDDYSTONE_URL_SUFFIX_ORG_SLASH 0x01 +#define BLE_EDDYSTONE_URL_SUFFIX_EDU_SLASH 0x02 +#define BLE_EDDYSTONE_URL_SUFFIX_NET_SLASH 0x03 +#define BLE_EDDYSTONE_URL_SUFFIX_INFO_SLASH 0x04 +#define BLE_EDDYSTONE_URL_SUFFIX_BIZ_SLASH 0x05 +#define BLE_EDDYSTONE_URL_SUFFIX_GOV_SLASH 0x06 +#define BLE_EDDYSTONE_URL_SUFFIX_COM 0x07 +#define BLE_EDDYSTONE_URL_SUFFIX_ORG 0x08 +#define BLE_EDDYSTONE_URL_SUFFIX_EDU 0x09 +#define BLE_EDDYSTONE_URL_SUFFIX_NET 0x0a +#define BLE_EDDYSTONE_URL_SUFFIX_INFO 0x0b +#define BLE_EDDYSTONE_URL_SUFFIX_BIZ 0x0c +#define BLE_EDDYSTONE_URL_SUFFIX_GOV 0x0d +#define BLE_EDDYSTONE_URL_SUFFIX_NONE 0xff + +/** + * Configures the device to advertise Eddystone UID beacons. + * + * @param adv_fields The base advertisement fields to transform into + * an eddystone beacon. All configured fields + * are preserved; you probably want to clear + * this struct before calling this function. + * @param uid The 16-byte UID to advertise. + * @param measured_power The Measured Power (RSSI value at 0 Meter). + * + * @return 0 on success; + * BLE_HS_EBUSY if advertising is in progress; + * BLE_HS_EMSGSIZE if the specified data is too + * large to fit in an advertisement; + * Other nonzero on failure. + */ +int ble_eddystone_set_adv_data_uid(struct ble_hs_adv_fields *adv_fields, + void *uid, int8_t measured_power); + +/** + * Configures the device to advertise Eddystone URL beacons. + * + * @param adv_fields The base advertisement fields to transform into + * an eddystone beacon. All configured fields + * are preserved; you probably want to clear + * this struct before calling this function. + * @param url_scheme The prefix of the URL; one of the + * BLE_EDDYSTONE_URL_SCHEME values. + * @param url_body The middle of the URL. Don't include the + * suffix if there is a suitable suffix code. + * @param url_body_len The string length of the url_body argument. + * @param url_suffix The suffix of the URL; one of the + * BLE_EDDYSTONE_URL_SUFFIX values; use + * BLE_EDDYSTONE_URL_SUFFIX_NONE if the suffix + * is embedded in the body argument. + * @param measured_power The Measured Power (RSSI value at 0 Meter). + * + * @return 0 on success; + * BLE_HS_EBUSY if advertising is in progress; + * BLE_HS_EMSGSIZE if the specified data is too + * large to fit in an advertisement; + * Other nonzero on failure. + */ +int ble_eddystone_set_adv_data_url(struct ble_hs_adv_fields *adv_fields, + uint8_t url_scheme, char *url_body, + uint8_t url_body_len, uint8_t suffix, + int8_t measured_power); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_gap.h b/libesp32/NimBLE-Arduino/src/host/ble_gap.h new file mode 100644 index 000000000..9ef4e18ef --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_gap.h @@ -0,0 +1,1939 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_GAP_ +#define H_BLE_GAP_ + +/** + * @brief Bluetooth Host Generic Access Profile (GAP) + * @defgroup bt_host_gap Bluetooth Host Generic Access Profile (GAP) + * @ingroup bt_host + * @{ + */ + +#include +#include "host/ble_hs.h" +#include "host/ble_hs_adv.h" +#include "syscfg/syscfg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct hci_le_conn_complete; +struct hci_conn_update; + +/** 30 ms. */ +#define BLE_GAP_ADV_FAST_INTERVAL1_MIN (30 * 1000 / BLE_HCI_ADV_ITVL) + +/** 60 ms. */ +#define BLE_GAP_ADV_FAST_INTERVAL1_MAX (60 * 1000 / BLE_HCI_ADV_ITVL) + +/** 100 ms. */ +#define BLE_GAP_ADV_FAST_INTERVAL2_MIN (100 * 1000 / BLE_HCI_ADV_ITVL) + +/** 150 ms. */ +#define BLE_GAP_ADV_FAST_INTERVAL2_MAX (150 * 1000 / BLE_HCI_ADV_ITVL) + +/** 30 ms; active scanning. */ +#define BLE_GAP_SCAN_FAST_INTERVAL_MIN (30 * 1000 / BLE_HCI_ADV_ITVL) + +/** 60 ms; active scanning. */ +#define BLE_GAP_SCAN_FAST_INTERVAL_MAX (60 * 1000 / BLE_HCI_ADV_ITVL) + +/** 11.25 ms; limited discovery interval. */ +#define BLE_GAP_LIM_DISC_SCAN_INT (11.25 * 1000 / BLE_HCI_SCAN_ITVL) + +/** 11.25 ms; limited discovery window (not from the spec). */ +#define BLE_GAP_LIM_DISC_SCAN_WINDOW (11.25 * 1000 / BLE_HCI_SCAN_ITVL) + +/** 30 ms; active scanning. */ +#define BLE_GAP_SCAN_FAST_WINDOW (30 * 1000 / BLE_HCI_SCAN_ITVL) + +/* 30.72 seconds; active scanning. */ +#define BLE_GAP_SCAN_FAST_PERIOD (30.72 * 1000) + +/** 1.28 seconds; background scanning. */ +#define BLE_GAP_SCAN_SLOW_INTERVAL1 (1280 * 1000 / BLE_HCI_SCAN_ITVL) + +/** 11.25 ms; background scanning. */ +#define BLE_GAP_SCAN_SLOW_WINDOW1 (11.25 * 1000 / BLE_HCI_SCAN_ITVL) + +/** 10.24 seconds. */ +#define BLE_GAP_DISC_DUR_DFLT (10.24 * 1000) + +/** 30 seconds (not from the spec). */ +#define BLE_GAP_CONN_DUR_DFLT (30 * 1000) + +/** 1 second. */ +#define BLE_GAP_CONN_PAUSE_CENTRAL (1 * 1000) + +/** 5 seconds. */ +#define BLE_GAP_CONN_PAUSE_PERIPHERAL (5 * 1000) + +/* 30 ms. */ +#define BLE_GAP_INITIAL_CONN_ITVL_MIN (30 * 1000 / BLE_HCI_CONN_ITVL) + +/* 50 ms. */ +#define BLE_GAP_INITIAL_CONN_ITVL_MAX (50 * 1000 / BLE_HCI_CONN_ITVL) + +/** Default channels mask: all three channels are used. */ +#define BLE_GAP_ADV_DFLT_CHANNEL_MAP 0x07 + +#define BLE_GAP_INITIAL_CONN_LATENCY 0 +#define BLE_GAP_INITIAL_SUPERVISION_TIMEOUT 0x0100 +#define BLE_GAP_INITIAL_CONN_MIN_CE_LEN 0x0010 +#define BLE_GAP_INITIAL_CONN_MAX_CE_LEN 0x0300 + +#define BLE_GAP_ROLE_MASTER 0 +#define BLE_GAP_ROLE_SLAVE 1 + +#define BLE_GAP_EVENT_CONNECT 0 +#define BLE_GAP_EVENT_DISCONNECT 1 +/* Reserved 2 */ +#define BLE_GAP_EVENT_CONN_UPDATE 3 +#define BLE_GAP_EVENT_CONN_UPDATE_REQ 4 +#define BLE_GAP_EVENT_L2CAP_UPDATE_REQ 5 +#define BLE_GAP_EVENT_TERM_FAILURE 6 +#define BLE_GAP_EVENT_DISC 7 +#define BLE_GAP_EVENT_DISC_COMPLETE 8 +#define BLE_GAP_EVENT_ADV_COMPLETE 9 +#define BLE_GAP_EVENT_ENC_CHANGE 10 +#define BLE_GAP_EVENT_PASSKEY_ACTION 11 +#define BLE_GAP_EVENT_NOTIFY_RX 12 +#define BLE_GAP_EVENT_NOTIFY_TX 13 +#define BLE_GAP_EVENT_SUBSCRIBE 14 +#define BLE_GAP_EVENT_MTU 15 +#define BLE_GAP_EVENT_IDENTITY_RESOLVED 16 +#define BLE_GAP_EVENT_REPEAT_PAIRING 17 +#define BLE_GAP_EVENT_PHY_UPDATE_COMPLETE 18 +#define BLE_GAP_EVENT_EXT_DISC 19 +#define BLE_GAP_EVENT_PERIODIC_SYNC 20 +#define BLE_GAP_EVENT_PERIODIC_REPORT 21 +#define BLE_GAP_EVENT_PERIODIC_SYNC_LOST 22 +#define BLE_GAP_EVENT_SCAN_REQ_RCVD 23 + +/*** Reason codes for the subscribe GAP event. */ + +/** Peer's CCCD subscription state changed due to a descriptor write. */ +#define BLE_GAP_SUBSCRIBE_REASON_WRITE 1 + +/** Peer's CCCD subscription state cleared due to connection termination. */ +#define BLE_GAP_SUBSCRIBE_REASON_TERM 2 + +/** + * Peer's CCCD subscription state changed due to restore from persistence + * (bonding restored). + */ +#define BLE_GAP_SUBSCRIBE_REASON_RESTORE 3 + +#define BLE_GAP_REPEAT_PAIRING_RETRY 1 +#define BLE_GAP_REPEAT_PAIRING_IGNORE 2 + +/** Connection security state */ +struct ble_gap_sec_state { + /** If connection is encrypted */ + unsigned encrypted:1; + + /** If connection is authenticated */ + unsigned authenticated:1; + + /** If connection is bonded (security information is stored) */ + unsigned bonded:1; + + /** Size of a key used for encryption */ + unsigned key_size:5; +}; + +/** Advertising parameters */ +struct ble_gap_adv_params { + /** Advertising mode. Can be one of following constants: + * - BLE_GAP_CONN_MODE_NON (non-connectable; 3.C.9.3.2). + * - BLE_GAP_CONN_MODE_DIR (directed-connectable; 3.C.9.3.3). + * - BLE_GAP_CONN_MODE_UND (undirected-connectable; 3.C.9.3.4). + */ + uint8_t conn_mode; + /** Discoverable mode. Can be one of following constants: + * - BLE_GAP_DISC_MODE_NON (non-discoverable; 3.C.9.2.2). + * - BLE_GAP_DISC_MODE_LTD (limited-discoverable; 3.C.9.2.3). + * - BLE_GAP_DISC_MODE_GEN (general-discoverable; 3.C.9.2.4). + */ + uint8_t disc_mode; + + /** Minimum advertising interval, if 0 stack use sane defaults */ + uint16_t itvl_min; + /** Maximum advertising interval, if 0 stack use sane defaults */ + uint16_t itvl_max; + /** Advertising channel map , if 0 stack use sane defaults */ + uint8_t channel_map; + + /** Advertising Filter policy */ + uint8_t filter_policy; + + /** If do High Duty cycle for Directed Advertising */ + uint8_t high_duty_cycle:1; +}; + +/** @brief Connection descriptor */ +struct ble_gap_conn_desc { + /** Connection security state */ + struct ble_gap_sec_state sec_state; + + /** Local identity address */ + ble_addr_t our_id_addr; + + /** Peer identity address */ + ble_addr_t peer_id_addr; + + /** Local over-the-air address */ + ble_addr_t our_ota_addr; + + /** Peer over-the-air address */ + ble_addr_t peer_ota_addr; + + /** Connection handle */ + uint16_t conn_handle; + + /** Connection interval */ + uint16_t conn_itvl; + + /** Connection latency */ + uint16_t conn_latency; + + /** Connection supervision timeout */ + uint16_t supervision_timeout; + + /** Connection Role + * Possible values BLE_GAP_ROLE_SLAVE or BLE_GAP_ROLE_MASTER + */ + uint8_t role; + + /** Master clock accuracy */ + uint8_t master_clock_accuracy; +}; + +/** @brief Connection parameters */ +struct ble_gap_conn_params { + /** Scan interval in 0.625ms units */ + uint16_t scan_itvl; + + /** Scan window in 0.625ms units */ + uint16_t scan_window; + + /** Minimum value for connection interval in 1.25ms units */ + uint16_t itvl_min; + + /** Maximum value for connection interval in 1.25ms units */ + uint16_t itvl_max; + + /** Connection latency */ + uint16_t latency; + + /** Supervision timeout in 10ms units */ + uint16_t supervision_timeout; + + /** Minimum length of connection event in 0.625ms units */ + uint16_t min_ce_len; + + /** Maximum length of connection event in 0.625ms units */ + uint16_t max_ce_len; +}; + +/** @brief Extended discovery parameters */ +struct ble_gap_ext_disc_params { + /** Scan interval in 0.625ms units */ + uint16_t itvl; + + /** Scan window in 0.625ms units */ + uint16_t window; + + /** If passive scan should be used */ + uint8_t passive:1; +}; + +/** @brief Discovery parameters */ +struct ble_gap_disc_params { + /** Scan interval in 0.625ms units */ + uint16_t itvl; + + /** Scan window in 0.625ms units */ + uint16_t window; + + /** Scan filter policy */ + uint8_t filter_policy; + + /** If limited discovery procedure should be used */ + uint8_t limited:1; + + /** If passive scan should be used */ + uint8_t passive:1; + + /** If enable duplicates filtering */ + uint8_t filter_duplicates:1; +}; + +/** @brief Connection parameters update parameters */ +struct ble_gap_upd_params { + /** Minimum value for connection interval in 1.25ms units */ + uint16_t itvl_min; + + /** Maximum value for connection interval in 1.25ms units */ + uint16_t itvl_max; + + /** Connection latency */ + uint16_t latency; + + /** Supervision timeout in 10ms units */ + uint16_t supervision_timeout; + + /** Minimum length of connection event in 0.625ms units */ + uint16_t min_ce_len; + + /** Maximum length of connection event in 0.625ms units */ + uint16_t max_ce_len; +}; + +/** @brief Passkey query */ +struct ble_gap_passkey_params { + /** Passkey action, can be one of following constants: + * - BLE_SM_IOACT_NONE + * - BLE_SM_IOACT_OOB + * - BLE_SM_IOACT_INPUT + * - BLE_SM_IOACT_DISP + * - BLE_SM_IOACT_NUMCMP + */ + uint8_t action; + + /** Passkey to compare, valid for BLE_SM_IOACT_NUMCMP action */ + uint32_t numcmp; +}; + +#if MYNEWT_VAL(BLE_EXT_ADV) + +#define BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE 0x00 +#define BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE 0x01 +#define BLE_GAP_EXT_ADV_DATA_STATUS_TRUNCATED 0x02 + +/** @brief Extended advertising report */ +struct ble_gap_ext_disc_desc { + /** Report properties bitmask + * - BLE_HCI_ADV_CONN_MASK + * - BLE_HCI_ADV_SCAN_MASK + * - BLE_HCI_ADV_DIRECT_MASK + * - BLE_HCI_ADV_SCAN_RSP_MASK + * - BLE_HCI_ADV_LEGACY_MASK + * */ + uint8_t props; + + /** Advertising data status, can be one of following constants: + * - BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE + * - BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE + * - BLE_GAP_EXT_ADV_DATA_STATUS_TRUNCATED + */ + uint8_t data_status; + + /** Legacy advertising PDU type. Valid if BLE_HCI_ADV_LEGACY_MASK props is + * set. Can be one of following constants: + * - BLE_HCI_ADV_RPT_EVTYPE_ADV_IND + * - BLE_HCI_ADV_RPT_EVTYPE_DIR_IND + * - BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND + * - BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND + * - BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP + */ + uint8_t legacy_event_type; + + /** Advertiser address */ + ble_addr_t addr; + + /** Received signal strength indication in dBm (127 if unavailable) */ + int8_t rssi; + + /** Advertiser transmit power in dBm (127 if unavailable) */ + int8_t tx_power; + + /** Advertising Set ID */ + uint8_t sid; + + /** Primary advertising PHY, can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t prim_phy; + + /** Secondary advertising PHY, can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - LE_HCI_LE_PHY_2M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t sec_phy; + + /** Periodic advertising interval. 0 if no periodic advertising. */ + uint16_t periodic_adv_itvl; + + /** Advertising Data length */ + uint8_t length_data; + + /** Advertising data */ + uint8_t *data; + + /** Directed advertising address. Valid if BLE_HCI_ADV_DIRECT_MASK props is + * set (BLE_ADDR_ANY otherwise). + */ + ble_addr_t direct_addr; +}; +#endif + +/** @brief Advertising report */ +struct ble_gap_disc_desc { + /** Advertising PDU type. Can be one of following constants: + * - BLE_HCI_ADV_RPT_EVTYPE_ADV_IND + * - BLE_HCI_ADV_RPT_EVTYPE_DIR_IND + * - BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND + * - BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND + * - BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP + */ + uint8_t event_type; + + /** Advertising Data length */ + uint8_t length_data; + + /** Advertiser address */ + ble_addr_t addr; + + /** Received signal strength indication in dBm (127 if unavailable) */ + int8_t rssi; + + /** Advertising data */ + uint8_t *data; + + /** Directed advertising address. Valid for BLE_HCI_ADV_RPT_EVTYPE_DIR_IND + * event type (BLE_ADDR_ANY otherwise). + */ + ble_addr_t direct_addr; +}; + +struct ble_gap_repeat_pairing { + /** The handle of the relevant connection. */ + uint16_t conn_handle; + + /** Properties of the existing bond. */ + uint8_t cur_key_size; + uint8_t cur_authenticated:1; + uint8_t cur_sc:1; + + /** + * Properties of the imminent secure link if the pairing procedure is + * allowed to continue. + */ + uint8_t new_key_size; + uint8_t new_authenticated:1; + uint8_t new_sc:1; + uint8_t new_bonding:1; +}; + +/** + * Represents a GAP-related event. When such an event occurs, the host + * notifies the application by passing an instance of this structure to an + * application-specified callback. + */ +struct ble_gap_event { + /** + * Indicates the type of GAP event that occurred. This is one of the + * BLE_GAP_EVENT codes. + */ + uint8_t type; + + /** + * A discriminated union containing additional details concerning the GAP + * event. The 'type' field indicates which member of the union is valid. + */ + union { + /** + * Represents a connection attempt. Valid for the following event + * types: + * o BLE_GAP_EVENT_CONNECT + */ + struct { + /** + * The status of the connection attempt; + * o 0: the connection was successfully established. + * o BLE host error code: the connection attempt failed for + * the specified reason. + */ + int status; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } connect; + + /** + * Represents a terminated connection. Valid for the following event + * types: + * o BLE_GAP_EVENT_DISCONNECT + */ + struct { + /** + * A BLE host return code indicating the reason for the + * disconnect. + */ + int reason; + + /** Information about the connection prior to termination. */ + struct ble_gap_conn_desc conn; + } disconnect; + + /** + * Represents an advertising report received during a discovery + * procedure. Valid for the following event types: + * o BLE_GAP_EVENT_DISC + */ + struct ble_gap_disc_desc disc; + +#if MYNEWT_VAL(BLE_EXT_ADV) + /** + * Represents an extended advertising report received during a discovery + * procedure. Valid for the following event types: + * o BLE_GAP_EVENT_EXT_DISC + */ + struct ble_gap_ext_disc_desc ext_disc; +#endif + + /** + * Represents a completed discovery procedure. Valid for the following + * event types: + * o BLE_GAP_EVENT_DISC_COMPLETE + */ + struct { + /** + * The reason the discovery procedure stopped. Typical reason + * codes are: + * o 0: Duration expired. + * o BLE_HS_EPREEMPTED: Host aborted procedure to configure a + * peer's identity. + */ + int reason; + } disc_complete; + + /** + * Represents a completed advertise procedure. Valid for the following + * event types: + * o BLE_GAP_EVENT_ADV_COMPLETE + */ + struct { + /** + * The reason the advertise procedure stopped. Typical reason + * codes are: + * o 0: Terminated due to connection. + * o BLE_HS_ETIMEOUT: Duration expired. + * o BLE_HS_EPREEMPTED: Host aborted procedure to configure a + * peer's identity. + */ + int reason; + +#if MYNEWT_VAL(BLE_EXT_ADV) + /** Advertising instance */ + uint8_t instance; + /** The handle of the relevant connection - valid if reason=0 */ + uint16_t conn_handle; + /** + * Number of completed extended advertising events + * + * This field is only valid if non-zero max_events was passed to + * ble_gap_ext_adv_start() and advertising completed due to duration + * timeout or max events transmitted. + * */ + uint8_t num_ext_adv_events; +#endif + } adv_complete; + + /** + * Represents an attempt to update a connection's parameters. If the + * attempt was successful, the connection's descriptor reflects the + * updated parameters. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_CONN_UPDATE + */ + struct { + /** + * The result of the connection update attempt; + * o 0: the connection was successfully updated. + * o BLE host error code: the connection update attempt failed + * for the specified reason. + */ + int status; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } conn_update; + + /** + * Represents a peer's request to update the connection parameters. + * This event is generated when a peer performs any of the following + * procedures: + * o L2CAP Connection Parameter Update Procedure + * o Link-Layer Connection Parameters Request Procedure + * + * To reject the request, return a non-zero HCI error code. The value + * returned is the reject reason given to the controller. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_L2CAP_UPDATE_REQ + * o BLE_GAP_EVENT_CONN_UPDATE_REQ + */ + struct { + /** + * Indicates the connection parameters that the peer would like to + * use. + */ + const struct ble_gap_upd_params *peer_params; + + /** + * Indicates the connection parameters that the local device would + * like to use. The application callback should fill this in. By + * default, this struct contains the requested parameters (i.e., + * it is a copy of 'peer_params'). + */ + struct ble_gap_upd_params *self_params; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } conn_update_req; + + /** + * Represents a failed attempt to terminate an established connection. + * Valid for the following event types: + * o BLE_GAP_EVENT_TERM_FAILURE + */ + struct { + /** + * A BLE host return code indicating the reason for the failure. + */ + int status; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } term_failure; + + /** + * Represents an attempt to change the encrypted state of a + * connection. If the attempt was successful, the connection + * descriptor reflects the updated encrypted state. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_ENC_CHANGE + */ + struct { + /** + * Indicates the result of the encryption state change attempt; + * o 0: the encrypted state was successfully updated; + * o BLE host error code: the encryption state change attempt + * failed for the specified reason. + */ + int status; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } enc_change; + + /** + * Represents a passkey query needed to complete a pairing procedure. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_PASSKEY_ACTION + */ + struct { + /** Contains details about the passkey query. */ + struct ble_gap_passkey_params params; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } passkey; + + /** + * Represents a received ATT notification or indication. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_NOTIFY_RX + */ + struct { + /** + * The contents of the notification or indication. If the + * application wishes to retain this mbuf for later use, it must + * set this pointer to NULL to prevent the stack from freeing it. + */ + struct os_mbuf *om; + + /** The handle of the relevant ATT attribute. */ + uint16_t attr_handle; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + + /** + * Whether the received command is a notification or an + * indication; + * o 0: Notification; + * o 1: Indication. + */ + uint8_t indication:1; + } notify_rx; + + /** + * Represents a transmitted ATT notification or indication, or a + * completed indication transaction. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_NOTIFY_TX + */ + struct { + /** + * The status of the notification or indication transaction; + * o 0: Command successfully sent; + * o BLE_HS_EDONE: Confirmation (indication ack) received; + * o BLE_HS_ETIMEOUT: Confirmation (indication ack) never + * received; + * o Other return code: Error. + */ + int status; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + + /** The handle of the relevant characteristic value. */ + uint16_t attr_handle; + + /** + * Whether the transmitted command is a notification or an + * indication; + * o 0: Notification; + * o 1: Indication. + */ + uint8_t indication:1; + } notify_tx; + + /** + * Represents a state change in a peer's subscription status. In this + * comment, the term "update" is used to refer to either a notification + * or an indication. This event is triggered by any of the following + * occurrences: + * o Peer enables or disables updates via a CCCD write. + * o Connection is about to be terminated and the peer is + * subscribed to updates. + * o Peer is now subscribed to updates after its state was restored + * from persistence. This happens when bonding is restored. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_SUBSCRIBE + */ + struct { + /** The handle of the relevant connection. */ + uint16_t conn_handle; + + /** The value handle of the relevant characteristic. */ + uint16_t attr_handle; + + /** One of the BLE_GAP_SUBSCRIBE_REASON codes. */ + uint8_t reason; + + /** Whether the peer was previously subscribed to notifications. */ + uint8_t prev_notify:1; + + /** Whether the peer is currently subscribed to notifications. */ + uint8_t cur_notify:1; + + /** Whether the peer was previously subscribed to indications. */ + uint8_t prev_indicate:1; + + /** Whether the peer is currently subscribed to indications. */ + uint8_t cur_indicate:1; + } subscribe; + + /** + * Represents a change in an L2CAP channel's MTU. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_MTU + */ + struct { + /** The handle of the relevant connection. */ + uint16_t conn_handle; + + /** + * Indicates the channel whose MTU has been updated; either + * BLE_L2CAP_CID_ATT or the ID of a connection-oriented channel. + */ + uint16_t channel_id; + + /* The channel's new MTU. */ + uint16_t value; + } mtu; + + /** + * Represents a change in peer's identity. This is issued after + * successful pairing when Identity Address Information was received. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_IDENTITY_RESOLVED + */ + struct { + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } identity_resolved; + + /** + * Represents a peer's attempt to pair despite a bond already existing. + * The application has two options for handling this event type: + * o Retry: Return BLE_GAP_REPEAT_PAIRING_RETRY after deleting the + * conflicting bond. The stack will verify the bond has + * been deleted and continue the pairing procedure. If + * the bond is still present, this event will be reported + * again. + * o Ignore: Return BLE_GAP_REPEAT_PAIRING_IGNORE. The stack will + * silently ignore the pairing request. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_REPEAT_PAIRING + */ + struct ble_gap_repeat_pairing repeat_pairing; + + /** + * Represents a change of PHY. This is issue after successful + * change on PHY. + */ + struct { + int status; + uint16_t conn_handle; + + /** + * Indicates enabled TX/RX PHY. Possible values: + * o BLE_GAP_LE_PHY_1M + * o BLE_GAP_LE_PHY_2M + * o BLE_GAP_LE_PHY_CODED + */ + uint8_t tx_phy; + uint8_t rx_phy; + } phy_updated; +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + /** + * Represents a periodic advertising sync established during discovery + * procedure. Valid for the following event types: + * o BLE_GAP_EVENT_PERIODIC_SYNC + */ + struct { + /** BLE_ERR_SUCCESS on success or error code on failure. Other + * fields are valid only for success + */ + uint8_t status; + /** Periodic sync handle */ + uint16_t sync_handle; + + /** Advertising Set ID */ + uint8_t sid; + + /** Advertiser address */ + ble_addr_t adv_addr; + + /** Advertising PHY, can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - LE_HCI_LE_PHY_2M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t adv_phy; + + /** Periodic advertising interval */ + uint16_t per_adv_ival; + + /** Advertiser clock accuracy */ + uint8_t adv_clk_accuracy; + } periodic_sync; + + /** + * Represents a periodic advertising report received on established + * sync. Valid for the following event types: + * o BLE_GAP_EVENT_PERIODIC_REPORT + */ + struct { + /** Periodic sync handle */ + uint16_t sync_handle; + + /** Advertiser transmit power in dBm (127 if unavailable) */ + int8_t tx_power; + + /** Received signal strength indication in dBm (127 if unavailable) */ + int8_t rssi; + + /** Advertising data status, can be one of following constants: + * - BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE + * - BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE + * - BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED + */ + uint8_t data_status; + + /** Advertising Data length */ + uint8_t data_length; + + /** Advertising data */ + uint8_t *data; + } periodic_report; + + /** + * Represents a periodic advertising sync lost of established sync. + * Sync lost reason can be BLE_HS_ETIMEOUT (sync timeout) or + * BLE_HS_EDONE (sync terminated locally). + * Valid for the following event types: + * o BLE_GAP_EVENT_PERIODIC_SYNC_LOST + */ + struct { + /** Periodic sync handle */ + uint16_t sync_handle; + + /** Reason for sync lost, can be BLE_HS_ETIMEOUT for timeout or + * BLE_HS_EDONE for locally terminated sync + */ + int reason; + } periodic_sync_lost; +#endif + +#if MYNEWT_VAL(BLE_EXT_ADV) + /** + * Represents a scan request for an extended advertising instance where + * scan request notifications were enabled. + * Valid for the following event types: + * o BLE_GAP_EVENT_SCAN_REQ_RCVD + */ + struct { + /** Extended advertising instance */ + uint8_t instance; + /** Address of scanner */ + ble_addr_t scan_addr; + } scan_req_rcvd; +#endif + }; +}; + +typedef int ble_gap_event_fn(struct ble_gap_event *event, void *arg); + +#define BLE_GAP_CONN_MODE_NON 0 +#define BLE_GAP_CONN_MODE_DIR 1 +#define BLE_GAP_CONN_MODE_UND 2 + +#define BLE_GAP_DISC_MODE_NON 0 +#define BLE_GAP_DISC_MODE_LTD 1 +#define BLE_GAP_DISC_MODE_GEN 2 + +/** + * Searches for a connection with the specified handle. If a matching + * connection is found, the supplied connection descriptor is filled + * correspondingly. + * + * @param handle The connection handle to search for. + * @param out_desc On success, this is populated with information relating to + * the matching connection. Pass NULL if you don't need this + * information. + * + * @return 0 on success, BLE_HS_ENOTCONN if no matching connection was + * found. + */ +int ble_gap_conn_find(uint16_t handle, struct ble_gap_conn_desc *out_desc); + +/** + * Searches for a connection with a peer with the specified address. + * If a matching connection is found, the supplied connection descriptor + * is filled correspondingly. + * + * @param addr The ble address of a connected peer device to search for. + * @param out_desc On success, this is populated with information relating to + * the matching connection. Pass NULL if you don't need this + * information. + * + * @return 0 on success, BLE_HS_ENOTCONN if no matching connection was + * found. + */ +int ble_gap_conn_find_by_addr(const ble_addr_t *addr, + struct ble_gap_conn_desc *out_desc); + +/** + * Configures a connection to use the specified GAP event callback. A + * connection's GAP event callback is first specified when the connection is + * created, either via advertising or initiation. This function replaces the + * callback that was last configured. + * + * @param conn_handle The handle of the connection to configure. + * @param cb The callback to associate with the connection. + * @param cb_arg An optional argument that the callback receives. + * + * @return 0 on success, BLE_HS_ENOTCONN if there is no connection + * with the specified handle. + */ +int ble_gap_set_event_cb(uint16_t conn_handle, + ble_gap_event_fn *cb, void *cb_arg); + +/** @brief Start advertising + * + * This function configures and start advertising procedure. + * + * @param own_addr_type The type of address the stack should use for itself. + * Valid values are: + * - BLE_OWN_ADDR_PUBLIC + * - BLE_OWN_ADDR_RANDOM + * - BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT + * - BLE_OWN_ADDR_RPA_RANDOM_DEFAULT + * @param direct_addr The peer's address for directed advertising. This + * parameter shall be non-NULL if directed advertising is + * being used. + * @param duration_ms The duration of the advertisement procedure. On + * expiration, the procedure ends and a + * BLE_GAP_EVENT_ADV_COMPLETE event is reported. Units are + * milliseconds. Specify BLE_HS_FOREVER for no expiration. + * @param adv_params Additional arguments specifying the particulars of the + * advertising procedure. + * @param cb The callback to associate with this advertising + * procedure. If advertising ends, the event is reported + * through this callback. If advertising results in a + * connection, the connection inherits this callback as its + * event-reporting mechanism. + * @param cb_arg The optional argument to pass to the callback function. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr, + int32_t duration_ms, + const struct ble_gap_adv_params *adv_params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Stops the currently-active advertising procedure. A success return + * code indicates that advertising has been fully aborted and a new advertising + * procedure can be initiated immediately. + * + * NOTE: If the caller is running in the same task as the NimBLE host, or if it + * is running in a higher priority task than that of the host, care must be + * taken when restarting advertising. Under these conditions, the following is + * *not* a reliable method to restart advertising: + * ble_gap_adv_stop() + * ble_gap_adv_start() + * + * Instead, the call to `ble_gap_adv_start()` must be made in a separate event + * context. That is, `ble_gap_adv_start()` must be called asynchronously by + * enqueueing an event on the current task's event queue. See + * https://github.com/apache/mynewt-nimble/pull/211 for more information. + * + * @return 0 on success, BLE_HS_EALREADY if there is no active advertising + * procedure, other error code on failure. + */ +int ble_gap_adv_stop(void); + +/** + * Indicates whether an advertisement procedure is currently in progress. + * + * @return 0 if no advertisement procedure in progress, 1 otherwise. + */ +int ble_gap_adv_active(void); + +/** + * Configures the data to include in subsequent advertisements. + * + * @param data Buffer containing the advertising data. + * @param data_len The size of the advertising data, in bytes. + * + * @return 0 on succes, BLE_HS_EBUSY if advertising is in progress, + * other error code on failure. + */ +int ble_gap_adv_set_data(const uint8_t *data, int data_len); + +/** + * Configures the data to include in subsequent scan responses. + * + * @param data Buffer containing the scan response data. + * @param data_len The size of the response data, in bytes. + * + * @return 0 on succes, BLE_HS_EBUSY if advertising is in progress, + * other error code on failure. + */ +int ble_gap_adv_rsp_set_data(const uint8_t *data, int data_len); + +/** + * Configures the fields to include in subsequent advertisements. This is a + * convenience wrapper for ble_gap_adv_set_data(). + * + * @param adv_fields Specifies the advertisement data. + * + * @return 0 on success, + * BLE_HS_EBUSY if advertising is in progress, + * BLE_HS_EMSGSIZE if the specified data is too large to + * fit in an advertisement, + * other error code on failure. + */ +int ble_gap_adv_set_fields(const struct ble_hs_adv_fields *rsp_fields); + +/** + * Configures the fields to include in subsequent scan responses. This is a + * convenience wrapper for ble_gap_adv_rsp_set_data(). + * + * @param adv_fields Specifies the scan response data. + * + * @return 0 on success, + * BLE_HS_EBUSY if advertising is in progress, + * BLE_HS_EMSGSIZE if the specified data is too large to + * fit in a scan response, + * other error code on failure. + */ +int ble_gap_adv_rsp_set_fields(const struct ble_hs_adv_fields *rsp_fields); + +#if MYNEWT_VAL(BLE_EXT_ADV) +/** @brief Extended advertising parameters */ +struct ble_gap_ext_adv_params { + /** If perform connectable advertising */ + unsigned int connectable:1; + + /** If perform scannable advertising */ + unsigned int scannable:1; + + /** If perform directed advertising */ + unsigned int directed:1; + + /** If perform high-duty directed advertising */ + unsigned int high_duty_directed:1; + + /** If use legacy PDUs for advertising */ + unsigned int legacy_pdu:1; + + /** If perform anonymous advertising */ + unsigned int anonymous:1; + + /** If include TX power in advertising PDU */ + unsigned int include_tx_power:1; + + /** If enable scan request notification */ + unsigned int scan_req_notif:1; + + /** Minimum advertising interval in 0.625ms units, if 0 stack use sane + * defaults + */ + uint32_t itvl_min; + + /** Maximum advertising interval in 0.625ms units, if 0 stack use sane + * defaults + */ + uint32_t itvl_max; + + /** Advertising channel map , if 0 stack use sane defaults */ + uint8_t channel_map; + + /** Own address type to be used by advertising instance */ + uint8_t own_addr_type; + + /** Peer address for directed advertising, valid only if directed is set */ + ble_addr_t peer; + + /** Advertising Filter policy */ + uint8_t filter_policy; + + /** Primary advertising PHY to use , can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t primary_phy; + + /** Secondary advertising PHY to use, can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - LE_HCI_LE_PHY_2M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t secondary_phy; + + /** Preferred advertiser transmit power */ + int8_t tx_power; + + /** Advertising Set ID */ + uint8_t sid; +}; + +/** + * Configure extended advertising instance + * + * @param instance Instance ID + * @param params Additional arguments specifying the particulars + * of the advertising. + * @param selected_tx_power Selected advertising transmit power will be + * stored in that param if non-NULL. + * @param cb The callback to associate with this advertising + * procedure. Advertising complete event is reported + * through this callback + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_ext_adv_configure(uint8_t instance, + const struct ble_gap_ext_adv_params *params, + int8_t *selected_tx_power, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Set random address for configured advertising instance. + * + * @param instance Instance ID + * @param addr Random address to be set + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_ext_adv_set_addr(uint8_t instance, const ble_addr_t *addr); + +/** + * Start advertising instance. + * + * @param instance Instance ID + * @param duration The duration of the advertisement procedure. On + * expiration, the procedure ends and + * a BLE_HS_FOREVER event is reported. + * Units are milliseconds. Specify 0 for no + * expiration. + * @params max_events Number of advertising events that should be sent + * before advertising ends and + * a BLE_GAP_EVENT_ADV_COMPLETE event is reported. + * Specify 0 for no limit. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_ext_adv_start(uint8_t instance, int duration, int max_events); + +/** + * Stops advertising procedure for specified instance. + * + * @param instance Instance ID + * + * @return 0 on success, BLE_HS_EALREADY if there is no active advertising + * procedure for instance, other error code on failure. + */ +int ble_gap_ext_adv_stop(uint8_t instance); + +/** + * Configures the data to include in advertisements packets for specified + * advertising instance. + * + * @param instance Instance ID + * @param data Chain containing the advertising data. + * + * @return 0 on success or error code on failure. + */ +int ble_gap_ext_adv_set_data(uint8_t instance, struct os_mbuf *data); + +/** + * Configures the data to include in subsequent scan responses for specified + * advertisign instance. + * + * @param instance Instance ID + * @param data Chain containing the scan response data. + * + * @return 0 on success or error code on failure. + */ + +int ble_gap_ext_adv_rsp_set_data(uint8_t instance, struct os_mbuf *data); + +/** + * Remove existing advertising instance. + * + * @param instance Instance ID + * + * @return 0 on success, + * BLE_HS_EBUSY if advertising is in progress, + * other error code on failure. + */ +int ble_gap_ext_adv_remove(uint8_t instance); + +/** + * Clear all existing advertising instances + * @return 0 on success, + * BLE_HS_EBUSY if advertising is in progress, + * other error code on failure. + */ +int ble_gap_ext_adv_clear(void); +#endif + +/* Periodic Advertising */ +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + +/** @brief Periodic advertising parameters */ +struct ble_gap_periodic_adv_params { + /** If include TX power in advertising PDU */ + unsigned int include_tx_power:1; + + /** Minimum advertising interval in 0.625ms units, if 0 stack use sane + * defaults + */ + uint16_t itvl_min; + + /** Maximum advertising interval in 0.625ms units, if 0 stack use sane + * defaults + */ + uint16_t itvl_max; +}; + +/** @brief Periodic sync parameters */ +struct ble_gap_periodic_sync_params { + /** The maximum number of periodic advertising events that controller can + * skip after a successful receive. + * */ + uint16_t skip; + + /** Synchronization timeout for the periodic advertising train in 10ms units + */ + uint16_t sync_timeout; +}; + +/** + * Configure periodic advertising for specified advertising instance + * + * This is allowed only for instances configured as non-announymous, + * non-connectable and non-scannable. + * + * @param instance Instance ID + * @param params Additional arguments specifying the particulars + * of periodic advertising. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_configure(uint8_t instance, + const struct ble_gap_periodic_adv_params *params); + +/** + * Start periodic advertising for specified advertising instance. + * + * @param instance Instance ID + * + * @return 0 on success, error code on failure. + */ +int ble_gap_periodic_adv_start(uint8_t instance); + +/** + * Stop periodic advertising for specified advertising instance. + * + * @param instance Instance ID + * + * @return 0 on success, error code on failure. + */ +int ble_gap_periodic_adv_stop(uint8_t instance); + +/** + * Configures the data to include in periodic advertisements for specified + * advertising instance. + * + * @param instance Instance ID + * @param data Chain containing the periodic advertising data. + * + * @return 0 on success or error code on failure. + */ +int ble_gap_periodic_adv_set_data(uint8_t instance, struct os_mbuf *data); + +/** + * Performs the Synchronization procedure with periodic advertiser. + * + * @param addr Peer address to synchronize with. If NULL than + * peers from periodic list are used. + * @param adv_sid Advertiser Set ID + * @param params Additional arguments specifying the particulars + * of the synchronization procedure. + * @param cb The callback to associate with this synchrnization + * procedure. BLE_GAP_EVENT_PERIODIC_REPORT events + * are reported only by this callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_create_sync(const ble_addr_t *addr, uint8_t adv_sid, + const struct ble_gap_periodic_sync_params *params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Cancel pending synchronization procedure. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_create_sync_cancel(void); + +/** + * Terminate synchronization procedure. + * + * @param sync_handle Handle identifying synchronization to terminate. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_terminate_sync(uint16_t sync_handle); + +/** + * Add peer device to periodic synchronization list. + * + * @param addr Peer address to add to list. + * @param adv_sid Advertiser Set ID + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_add_dev_to_periodic_adv_list(const ble_addr_t *peer_addr, + uint8_t adv_sid); + +/** + * Remove peer device from periodic synchronization list. + * + * @param addr Peer address to remove from list. + * @param adv_sid Advertiser Set ID + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_rem_dev_from_periodic_adv_list(const ble_addr_t *peer_addr, + uint8_t adv_sid); + +/** + * Clear periodic synchrnization list. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_clear_periodic_adv_list(void); + +/** + * Get periodic synchronization list size. + * + * @param per_adv_list_size On success list size is stored here. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_read_periodic_adv_list_size(uint8_t *per_adv_list_size); +#endif + + +/** + * Performs the Limited or General Discovery Procedures. + * + * @param own_addr_type The type of address the stack should use for + * itself when sending scan requests. Valid + * values are: + * - BLE_ADDR_TYPE_PUBLIC + * - BLE_ADDR_TYPE_RANDOM + * - BLE_ADDR_TYPE_RPA_PUB_DEFAULT + * - BLE_ADDR_TYPE_RPA_RND_DEFAULT + * This parameter is ignored unless active + * scanning is being used. + * @param duration_ms The duration of the discovery procedure. + * On expiration, the procedure ends and a + * BLE_GAP_EVENT_DISC_COMPLETE event is + * reported. Units are milliseconds. Specify + * BLE_HS_FOREVER for no expiration. + * @param disc_params Additional arguments specifying the particulars + * of the discovery procedure. + * @param cb The callback to associate with this discovery + * procedure. Advertising reports and + * discovery termination events are reported + * through this callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_disc(uint8_t own_addr_type, int32_t duration_ms, + const struct ble_gap_disc_params *disc_params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Performs the Limited or General Extended Discovery Procedures. + * + * @param own_addr_type The type of address the stack should use for + * itself when sending scan requests. Valid + * values are: + * - BLE_ADDR_TYPE_PUBLIC + * - BLE_ADDR_TYPE_RANDOM + * - BLE_ADDR_TYPE_RPA_PUB_DEFAULT + * - BLE_ADDR_TYPE_RPA_RND_DEFAULT + * This parameter is ignored unless active + * scanning is being used. + * @param duration The duration of the discovery procedure. + * On expiration, if period is set to 0, the + * procedure ends and a + * BLE_GAP_EVENT_DISC_COMPLETE event is + * reported. Units are 10 milliseconds. + * Specify 0 for no expiration. + * @param period Time interval from when the Controller started + * its last Scan Duration until it begins the + * subsequent Scan Duration. Specify 0 to scan + * continuously. Units are 1.28 second. + * @param limited If limited discovery procedure should be used. + * @param uncoded_params Additional arguments specifying the particulars + * of the discovery procedure for uncoded PHY. + * If NULL is provided no scan is performed for + * this PHY. + * @param coded_params Additional arguments specifying the particulars + * of the discovery procedure for coded PHY. + * If NULL is provided no scan is performed for + * this PHY. + * @param cb The callback to associate with this discovery + * procedure. Advertising reports and discovery + * termination events are reported through this + * callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_ext_disc(uint8_t own_addr_type, uint16_t duration, uint16_t period, + uint8_t filter_duplicates, uint8_t filter_policy, + uint8_t limited, + const struct ble_gap_ext_disc_params *uncoded_params, + const struct ble_gap_ext_disc_params *coded_params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Cancels the discovery procedure currently in progress. A success return + * code indicates that scanning has been fully aborted; a new discovery or + * connect procedure can be initiated immediately. + * + * @return 0 on success; + * BLE_HS_EALREADY if there is no discovery + * procedure to cancel; + * Other nonzero on unexpected error. + */ +int ble_gap_disc_cancel(void); + +/** + * Indicates whether a discovery procedure is currently in progress. + * + * @return 0: No discovery procedure in progress; + * 1: Discovery procedure in progress. + */ +int ble_gap_disc_active(void); + +/** + * Initiates a connect procedure. + * + * @param own_addr_type The type of address the stack should use for + * itself during connection establishment. + * - BLE_OWN_ADDR_PUBLIC + * - BLE_OWN_ADDR_RANDOM + * - BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT + * - BLE_OWN_ADDR_RPA_RANDOM_DEFAULT + * @param peer_addr The address of the peer to connect to. + * If this parameter is NULL, the white list + * is used. + * @param duration_ms The duration of the discovery procedure. + * On expiration, the procedure ends and a + * BLE_GAP_EVENT_DISC_COMPLETE event is + * reported. Units are milliseconds. + * @param conn_params Additional arguments specifying the particulars + * of the connect procedure. Specify null for + * default values. + * @param cb The callback to associate with this connect + * procedure. When the connect procedure + * completes, the result is reported through + * this callback. If the connect procedure + * succeeds, the connection inherits this + * callback as its event-reporting mechanism. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; + * BLE_HS_EALREADY if a connection attempt is + * already in progress; + * BLE_HS_EBUSY if initiating a connection is not + * possible because scanning is in progress; + * BLE_HS_EDONE if the specified peer is already + * connected; + * Other nonzero on error. + */ +int ble_gap_connect(uint8_t own_addr_type, const ble_addr_t *peer_addr, + int32_t duration_ms, + const struct ble_gap_conn_params *params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Initiates an extended connect procedure. + * + * @param own_addr_type The type of address the stack should use for + * itself during connection establishment. + * - BLE_OWN_ADDR_PUBLIC + * - BLE_OWN_ADDR_RANDOM + * - BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT + * - BLE_OWN_ADDR_RPA_RANDOM_DEFAULT + * @param peer_addr The address of the peer to connect to. + * If this parameter is NULL, the white list + * is used. + * @param duration_ms The duration of the discovery procedure. + * On expiration, the procedure ends and a + * BLE_GAP_EVENT_DISC_COMPLETE event is + * reported. Units are milliseconds. + * @param phy_mask Define on which PHYs connection attempt should + * be done + * @param phy_1m_conn_params Additional arguments specifying the + * particulars of the connect procedure. When + * BLE_GAP_LE_PHY_1M_MASK is set in phy_mask + * this parameter can be specify to null for + * default values. + * @param phy_2m_conn_params Additional arguments specifying the + * particulars of the connect procedure. When + * BLE_GAP_LE_PHY_2M_MASK is set in phy_mask + * this parameter can be specify to null for + * default values. + * @param phy_coded_conn_params Additional arguments specifying the + * particulars of the connect procedure. When + * BLE_GAP_LE_PHY_CODED_MASK is set in + * phy_mask this parameter can be specify to + * null for default values. + * @param cb The callback to associate with this connect + * procedure. When the connect procedure + * completes, the result is reported through + * this callback. If the connect procedure + * succeeds, the connection inherits this + * callback as its event-reporting mechanism. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; + * BLE_HS_EALREADY if a connection attempt is + * already in progress; + * BLE_HS_EBUSY if initiating a connection is not + * possible because scanning is in progress; + * BLE_HS_EDONE if the specified peer is already + * connected; + * Other nonzero on error. + */ +int ble_gap_ext_connect(uint8_t own_addr_type, const ble_addr_t *peer_addr, + int32_t duration_ms, uint8_t phy_mask, + const struct ble_gap_conn_params *phy_1m_conn_params, + const struct ble_gap_conn_params *phy_2m_conn_params, + const struct ble_gap_conn_params *phy_coded_conn_params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Aborts a connect procedure in progress. + * + * @return 0 on success; + * BLE_HS_EALREADY if there is no active connect + * procedure. + * Other nonzero on error. + */ +int ble_gap_conn_cancel(void); + +/** + * Indicates whether a connect procedure is currently in progress. + * + * @return 0: No connect procedure in progress; + * 1: Connect procedure in progress. + */ +int ble_gap_conn_active(void); + +/** + * Terminates an established connection. + * + * @param conn_handle The handle corresponding to the connection to + * terminate. + * @param hci_reason The HCI error code to indicate as the reason + * for termination. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if there is no connection with + * the specified handle; + * Other nonzero on failure. + */ +int ble_gap_terminate(uint16_t conn_handle, uint8_t hci_reason); + +/** + * Overwrites the controller's white list with the specified contents. + * + * @param addrs The entries to write to the white list. + * @param white_list_count The number of entries in the white list. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_wl_set(const ble_addr_t *addrs, uint8_t white_list_count); + +/** + * Initiates a connection parameter update procedure. + * + * @param conn_handle The handle corresponding to the connection to + * update. + * @param params The connection parameters to attempt to update + * to. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if the there is no connection + * with the specified handle; + * BLE_HS_EALREADY if a connection update + * procedure for this connection is already in + * progress; + * BLE_HS_EINVAL if requested parameters are + * invalid; + * Other nonzero on error. + */ +int ble_gap_update_params(uint16_t conn_handle, + const struct ble_gap_upd_params *params); + +/** + * Initiates the GAP security procedure. + * + * Depending on connection role and stored security information this function + * will start appropriate security procedure (pairing or encryption). + * + * @param conn_handle The handle corresponding to the connection to + * secure. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if the there is no connection + * with the specified handle; + * BLE_HS_EALREADY if an security procedure for + * this connection is already in progress; + * Other nonzero on error. + */ +int ble_gap_security_initiate(uint16_t conn_handle); + +/** + * Initiates the GAP pairing procedure as a master. This is for testing only and + * should not be used by application. Use ble_gap_security_initiate() instead. + * + * @param conn_handle The handle corresponding to the connection to + * start pairing on. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if the there is no connection + * with the specified handle; + * BLE_HS_EALREADY if an pairing procedure for + * this connection is already in progress; + * Other nonzero on error. + */ +int ble_gap_pair_initiate(uint16_t conn_handle); + +/** + * Initiates the GAP encryption procedure as a master. This is for testing only + * and should not be used by application. Use ble_gap_security_initiate() + * instead. + * + * @param conn_handle The handle corresponding to the connection to + * start encryption. + * @param key_size Encryption key size + * @param ltk Long Term Key to be used for encryption. + * @param udiv Encryption Diversifier for LTK + * @param rand_val Random Value for EDIV and LTK + * @param auth If LTK provided is authenticated. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if the there is no connection + * with the specified handle; + * BLE_HS_EALREADY if an encryption procedure for + * this connection is already in progress; + * Other nonzero on error. + */ +int ble_gap_encryption_initiate(uint16_t conn_handle, uint8_t key_size, + const uint8_t *ltk, uint16_t ediv, + uint64_t rand_val, int auth); + +/** + * Retrieves the most-recently measured RSSI for the specified connection. A + * connection's RSSI is updated whenever a data channel PDU is received. + * + * @param conn_handle Specifies the connection to query. + * @param out_rssi On success, the retrieved RSSI is written here. + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_gap_conn_rssi(uint16_t conn_handle, int8_t *out_rssi); + +/** + * Unpairs a device with the specified address. The keys related to that peer + * device are removed from storage and peer address is removed from the resolve + * list from the controller. If a peer is connected, the connection is terminated. + * + * @param peer_addr Address of the device to be unpaired + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_gap_unpair(const ble_addr_t *peer_addr); + +/** + * Unpairs the oldest bonded peer device. The keys related to that peer + * device are removed from storage and peer address is removed from the resolve + * list from the controller. If a peer is connected, the connection is terminated. + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_gap_unpair_oldest_peer(void); + +#define BLE_GAP_PRIVATE_MODE_NETWORK 0 +#define BLE_GAP_PRIVATE_MODE_DEVICE 1 + +/** + * Set privacy mode for specified peer device + * + * @param peer_addr Peer device address + * @param priv_mode Privacy mode to be used. Can be one of following + * constants: + * - BLE_GAP_PRIVATE_MODE_NETWORK + * - BLE_GAP_PRIVATE_MODE_DEVICE + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_set_priv_mode(const ble_addr_t *peer_addr, uint8_t priv_mode); + +#define BLE_GAP_LE_PHY_1M 1 +#define BLE_GAP_LE_PHY_2M 2 +#define BLE_GAP_LE_PHY_CODED 3 +/** + * Read PHYs used for specified connection. + * + * On success output parameters are filled with information about used PHY type. + * + * @param conn_handle Connection handle + * @param tx_phy TX PHY used. Can be one of following constants: + * - BLE_GAP_LE_PHY_1M + * - BLE_GAP_LE_PHY_2M + * - BLE_GAP_LE_PHY_CODED + * @param rx_phy RX PHY used. Can be one of following constants: + * - BLE_GAP_LE_PHY_1M + * - BLE_GAP_LE_PHY_2M + * - BLE_GAP_LE_PHY_CODED + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_read_le_phy(uint16_t conn_handle, uint8_t *tx_phy, uint8_t *rx_phy); + +#define BLE_GAP_LE_PHY_1M_MASK 0x01 +#define BLE_GAP_LE_PHY_2M_MASK 0x02 +#define BLE_GAP_LE_PHY_CODED_MASK 0x04 +#define BLE_GAP_LE_PHY_ANY_MASK 0x0F +/** + * Set preferred default PHYs to be used for connections. + * + * @params tx_phys_mask Preferred TX PHY. Can be mask of following + * constants: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @params rx_phys_mask Preferred RX PHY. Can be mask of following + * constants: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + + * @return 0 on success; nonzero on failure. + */ +int ble_gap_set_prefered_default_le_phy(uint8_t tx_phys_mask, + uint8_t rx_phys_mask); + +#define BLE_GAP_LE_PHY_CODED_ANY 0 +#define BLE_GAP_LE_PHY_CODED_S2 1 +#define BLE_GAP_LE_PHY_CODED_S8 2 +/** + * Set preferred PHYs to be used for connection. + * + * @param conn_handle Connection handle + * @params tx_phys_mask Preferred TX PHY. Can be mask of following + * constants: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @params rx_phys_mask Preferred RX PHY. Can be mask of following + * constants: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @param phy_opts Additional PHY options. Valid values are: + * - BLE_GAP_LE_PHY_CODED_ANY + * - BLE_GAP_LE_PHY_CODED_S2 + * - BLE_GAP_LE_PHY_CODED_S8 + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_set_prefered_le_phy(uint16_t conn_handle, uint8_t tx_phys_mask, + uint8_t rx_phys_mask, uint16_t phy_opts); + +/** + * Event listener structure + * + * This should be used as an opaque structure and not modified manually. + */ +struct ble_gap_event_listener { + ble_gap_event_fn *fn; + void *arg; + SLIST_ENTRY(ble_gap_event_listener) link; +}; + +/** + * Similar to `ble_gap_unpair_oldest_peer()`, except it makes sure that current + * peer is not deleted. + * + * @param peer_addr Address of the current peer (not to be deleted) + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_gap_unpair_oldest_except_curr(const ble_addr_t *curr_peer); + +/** + * Registers listener for GAP events + * + * On success listener structure will be initialized automatically and does not + * need to be initialized prior to calling this function. To change callback + * and/or argument unregister listener first and register it again. + * + * @param listener Listener structure + * @param fn Callback function + * @param arg Callback argument + * + * @return 0 on success + * BLE_HS_EINVAL if no callback is specified + * BLE_HS_EALREADY if listener is already registered + */ +int ble_gap_event_listener_register(struct ble_gap_event_listener *listener, + ble_gap_event_fn *fn, void *arg); + +/** + * Unregisters listener for GAP events + * + * @param listener Listener structure + * + * @return 0 on success + * BLE_HS_ENOENT if listener was not registered + */ +int ble_gap_event_listener_unregister(struct ble_gap_event_listener *listener); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_gatt.h b/libesp32/NimBLE-Arduino/src/host/ble_gatt.h new file mode 100644 index 000000000..d5c3269f0 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_gatt.h @@ -0,0 +1,902 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_GATT_ +#define H_BLE_GATT_ + +/** + * @brief Bluetooth Generic Attribute Profile (GATT) + * @defgroup bt_gatt Bluetooth Generic Attribute Profile (GATT) + * @ingroup bt_host + * @{ + */ + +#include +#include "host/ble_att.h" +#include "host/ble_uuid.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_conn; +struct ble_att_error_rsp; +struct ble_hs_cfg; + +#define BLE_GATT_REGISTER_OP_SVC 1 +#define BLE_GATT_REGISTER_OP_CHR 2 +#define BLE_GATT_REGISTER_OP_DSC 3 + +#define BLE_GATT_SVC_UUID16 0x1801 +#define BLE_GATT_DSC_CLT_CFG_UUID16 0x2902 + +#define BLE_GATT_CHR_PROP_BROADCAST 0x01 +#define BLE_GATT_CHR_PROP_READ 0x02 +#define BLE_GATT_CHR_PROP_WRITE_NO_RSP 0x04 +#define BLE_GATT_CHR_PROP_WRITE 0x08 +#define BLE_GATT_CHR_PROP_NOTIFY 0x10 +#define BLE_GATT_CHR_PROP_INDICATE 0x20 +#define BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE 0x40 +#define BLE_GATT_CHR_PROP_EXTENDED 0x80 + +#define BLE_GATT_ACCESS_OP_READ_CHR 0 +#define BLE_GATT_ACCESS_OP_WRITE_CHR 1 +#define BLE_GATT_ACCESS_OP_READ_DSC 2 +#define BLE_GATT_ACCESS_OP_WRITE_DSC 3 + +#define BLE_GATT_CHR_F_BROADCAST 0x0001 +#define BLE_GATT_CHR_F_READ 0x0002 +#define BLE_GATT_CHR_F_WRITE_NO_RSP 0x0004 +#define BLE_GATT_CHR_F_WRITE 0x0008 +#define BLE_GATT_CHR_F_NOTIFY 0x0010 +#define BLE_GATT_CHR_F_INDICATE 0x0020 +#define BLE_GATT_CHR_F_AUTH_SIGN_WRITE 0x0040 +#define BLE_GATT_CHR_F_RELIABLE_WRITE 0x0080 +#define BLE_GATT_CHR_F_AUX_WRITE 0x0100 +#define BLE_GATT_CHR_F_READ_ENC 0x0200 +#define BLE_GATT_CHR_F_READ_AUTHEN 0x0400 +#define BLE_GATT_CHR_F_READ_AUTHOR 0x0800 +#define BLE_GATT_CHR_F_WRITE_ENC 0x1000 +#define BLE_GATT_CHR_F_WRITE_AUTHEN 0x2000 +#define BLE_GATT_CHR_F_WRITE_AUTHOR 0x4000 + +#define BLE_GATT_SVC_TYPE_END 0 +#define BLE_GATT_SVC_TYPE_PRIMARY 1 +#define BLE_GATT_SVC_TYPE_SECONDARY 2 + +/*** @client. */ +struct ble_gatt_error { + uint16_t status; + uint16_t att_handle; +}; + +struct ble_gatt_svc { + uint16_t start_handle; + uint16_t end_handle; + ble_uuid_any_t uuid; +}; + +struct ble_gatt_attr { + uint16_t handle; + uint16_t offset; + struct os_mbuf *om; +}; + +struct ble_gatt_chr { + uint16_t def_handle; + uint16_t val_handle; + uint8_t properties; + ble_uuid_any_t uuid; +}; + +struct ble_gatt_dsc { + uint16_t handle; + ble_uuid_any_t uuid; +}; + +typedef int ble_gatt_mtu_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t mtu, void *arg); +typedef int ble_gatt_disc_svc_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, + void *arg); + +/** + * The host will free the attribute mbuf automatically after the callback is + * executed. The application can take ownership of the mbuf and prevent it + * from being freed by assigning NULL to attr->om. + */ +typedef int ble_gatt_attr_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg); + +/** + * The host will free the attribute mbufs automatically after the callback is + * executed. The application can take ownership of the mbufs and prevent them + * from being freed by assigning NULL to each attribute's om field. + */ +typedef int ble_gatt_reliable_attr_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attrs, + uint8_t num_attrs, void *arg); + +typedef int ble_gatt_chr_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg); + +typedef int ble_gatt_dsc_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *dsc, + void *arg); + +/** + * Initiates GATT procedure: Exchange MTU. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_exchange_mtu(uint16_t conn_handle, + ble_gatt_mtu_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Discover All Primary Services. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + */ +int ble_gattc_disc_all_svcs(uint16_t conn_handle, + ble_gatt_disc_svc_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Discover Primary Service by Service UUID. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param service_uuid128 The 128-bit UUID of the service to discover. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid, + ble_gatt_disc_svc_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Find Included Services. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param start_handle The handle to begin the search at (generally + * the service definition handle). + * @param end_handle The handle to end the search at (generally the + * last handle in the service). + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, + ble_gatt_disc_svc_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Discover All Characteristics of a Service. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param start_handle The handle to begin the search at (generally + * the service definition handle). + * @param end_handle The handle to end the search at (generally the + * last handle in the service). + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, ble_gatt_chr_fn *cb, + void *cb_arg); + +/** + * Initiates GATT procedure: Discover Characteristics by UUID. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param start_handle The handle to begin the search at (generally + * the service definition handle). + * @param end_handle The handle to end the search at (generally the + * last handle in the service). + * @param chr_uuid128 The 128-bit UUID of the characteristic to + * discover. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid, + ble_gatt_chr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Discover All Characteristic Descriptors. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param chr_val_handle The handle of the characteristic value + * attribute. + * @param chr_end_handle The last handle in the characteristic + * definition. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, + ble_gatt_dsc_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Read Characteristic Value. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to read. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_read(uint16_t conn_handle, uint16_t attr_handle, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Read Using Characteristic UUID. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param start_handle The first handle to search (generally the + * handle of the service definition). + * @param end_handle The last handle to search (generally the + * last handle in the service definition). + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_read_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Read Long Characteristic Values. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param handle The handle of the characteristic value to read. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_read_long(uint16_t conn_handle, uint16_t handle, uint16_t offset, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Read Multiple Characteristic Values. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param handles An array of 16-bit attribute handles to read. + * @param num_handles The number of entries in the "handles" array. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_read_mult(uint16_t conn_handle, const uint16_t *handles, + uint8_t num_handles, ble_gatt_attr_fn *cb, + void *cb_arg); + +/** + * Initiates GATT procedure: Write Without Response. This function consumes + * the supplied mbuf regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to write + * to. + * @param txom The value to write to the characteristic. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om); + +/** + * Initiates GATT procedure: Write Without Response. This function consumes + * the supplied mbuf regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to write + * to. + * @param value The value to write to the characteristic. + * @param value_len The number of bytes to write. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_write_no_rsp_flat(uint16_t conn_handle, uint16_t attr_handle, + const void *data, uint16_t data_len); + +/** + * Initiates GATT procedure: Write Characteristic Value. This function + * consumes the supplied mbuf regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to write + * to. + * @param txom The value to write to the characteristic. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_write(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Write Characteristic Value (flat buffer version). + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to write + * to. + * @param value The value to write to the characteristic. + * @param value_len The number of bytes to write. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_write_flat(uint16_t conn_handle, uint16_t attr_handle, + const void *data, uint16_t data_len, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Write Long Characteristic Values. This function + * consumes the supplied mbuf regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to write + * to. + * @param txom The value to write to the characteristic. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_write_long(uint16_t conn_handle, uint16_t attr_handle, + uint16_t offset, struct os_mbuf *om, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Reliable Writes. This function consumes the + * supplied mbufs regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attrs An array of attribute descriptors; specifies + * which characteristics to write to and what + * data to write to them. The mbuf pointer in + * each attribute is set to NULL by this + * function. + * @param num_attrs The number of characteristics to write; equal + * to the number of elements in the 'attrs' + * array. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + */ +int ble_gattc_write_reliable(uint16_t conn_handle, + struct ble_gatt_attr *attrs, + int num_attrs, ble_gatt_reliable_attr_fn *cb, + void *cb_arg); + +/** + * Sends a "free-form" characteristic notification. This function consumes the + * supplied mbuf regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param chr_val_handle The attribute handle to indicate in the + * outgoing notification. + * @param txom The value to write to the characteristic. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_notify_custom(uint16_t conn_handle, uint16_t att_handle, + struct os_mbuf *om); + +/** + * Sends a characteristic notification. The content of the message is read + * from the specified characteristic. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param chr_val_handle The value attribute handle of the + * characteristic to include in the outgoing + * notification. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_notify(uint16_t conn_handle, uint16_t chr_val_handle); + +/** + * Sends a "free-form" characteristic indication. The provided mbuf contains + * the indication payload. This function consumes the supplied mbuf regardless + * of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param chr_val_handle The value attribute handle of the + * characteristic to include in the outgoing + * indication. + * @param txom The data to include in the indication. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_indicate_custom(uint16_t conn_handle, uint16_t chr_val_handle, + struct os_mbuf *txom); + +/** + * Sends a characteristic indication. The content of the message is read from + * the specified characteristic. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param chr_val_handle The value attribute handle of the + * characteristic to include in the outgoing + * indication. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_indicate(uint16_t conn_handle, uint16_t chr_val_handle); + +int ble_gattc_init(void); + +/*** @server. */ + +struct ble_gatt_access_ctxt; +typedef int ble_gatt_access_fn(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +typedef uint16_t ble_gatt_chr_flags; + +struct ble_gatt_chr_def { + /** + * Pointer to characteristic UUID; use BLE_UUIDxx_DECLARE macros to declare + * proper UUID; NULL if there are no more characteristics in the service. + */ + const ble_uuid_t *uuid; + + /** + * Callback that gets executed when this characteristic is read or + * written. + */ + ble_gatt_access_fn *access_cb; + + /** Optional argument for callback. */ + void *arg; + + /** + * Array of this characteristic's descriptors. NULL if no descriptors. + * Do not include CCCD; it gets added automatically if this + * characteristic's notify or indicate flag is set. + */ + struct ble_gatt_dsc_def *descriptors; + + /** Specifies the set of permitted operations for this characteristic. */ + ble_gatt_chr_flags flags; + + /** Specifies minimum required key size to access this characteristic. */ + uint8_t min_key_size; + + /** + * At registration time, this is filled in with the characteristic's value + * attribute handle. + */ + uint16_t *val_handle; +}; + +struct ble_gatt_svc_def { + /** + * One of the following: + * o BLE_GATT_SVC_TYPE_PRIMARY - primary service + * o BLE_GATT_SVC_TYPE_SECONDARY - secondary service + * o 0 - No more services in this array. + */ + uint8_t type; + + /** + * Pointer to service UUID; use BLE_UUIDxx_DECLARE macros to declare + * proper UUID; NULL if there are no more characteristics in the service. + */ + const ble_uuid_t *uuid; + + /** + * Array of pointers to other service definitions. These services are + * reported as "included services" during service discovery. Terminate the + * array with NULL. + */ + const struct ble_gatt_svc_def **includes; + + /** + * Array of characteristic definitions corresponding to characteristics + * belonging to this service. + */ + const struct ble_gatt_chr_def *characteristics; +}; + +struct ble_gatt_dsc_def { + /** + * Pointer to descriptor UUID; use BLE_UUIDxx_DECLARE macros to declare + * proper UUID; NULL if there are no more characteristics in the service. + */ + const ble_uuid_t *uuid; + + /** Specifies the set of permitted operations for this descriptor. */ + uint8_t att_flags; + + /** Specifies minimum required key size to access this descriptor. */ + uint8_t min_key_size; + + /** Callback that gets executed when the descriptor is read or written. */ + ble_gatt_access_fn *access_cb; + + /** Optional argument for callback. */ + void *arg; +}; + +/** + * Context for an access to a GATT characteristic or descriptor. When a client + * reads or writes a locally registered characteristic or descriptor, an + * instance of this struct gets passed to the application callback. + */ +struct ble_gatt_access_ctxt { + /** + * Indicates the gatt operation being performed. This is equal to one of + * the following values: + * o BLE_GATT_ACCESS_OP_READ_CHR + * o BLE_GATT_ACCESS_OP_WRITE_CHR + * o BLE_GATT_ACCESS_OP_READ_DSC + * o BLE_GATT_ACCESS_OP_WRITE_DSC + */ + uint8_t op; + + /** + * A container for the GATT access data. + * o For reads: The application populates this with the value of the + * characteristic or descriptor being read. + * o For writes: This is already populated with the value being written + * by the peer. If the application wishes to retain this mbuf for + * later use, the access callback must set this pointer to NULL to + * prevent the stack from freeing it. + */ + struct os_mbuf *om; + + /** + * The GATT operation being performed dictates which field in this union is + * valid. If a characteristic is being accessed, the chr field is valid. + * Otherwise a descriptor is being accessed, in which case the dsc field + * is valid. + */ + union { + /** + * The characteristic definition corresponding to the characteristic + * being accessed. This is what the app registered at startup. + */ + const struct ble_gatt_chr_def *chr; + + /** + * The descriptor definition corresponding to the descriptor being + * accessed. This is what the app registered at startup. + */ + const struct ble_gatt_dsc_def *dsc; + }; +}; + +/** + * Context passed to the registration callback; represents the GATT service, + * characteristic, or descriptor being registered. + */ +struct ble_gatt_register_ctxt { + /** + * Indicates the gatt registration operation just performed. This is + * equal to one of the following values: + * o BLE_GATT_REGISTER_OP_SVC + * o BLE_GATT_REGISTER_OP_CHR + * o BLE_GATT_REGISTER_OP_DSC + */ + uint8_t op; + + /** + * The value of the op field determines which field in this union is valid. + */ + union { + /** Service; valid if op == BLE_GATT_REGISTER_OP_SVC. */ + struct { + /** The ATT handle of the service definition attribute. */ + uint16_t handle; + + /** + * The service definition representing the service being + * registered. + */ + const struct ble_gatt_svc_def *svc_def; + } svc; + + /** Characteristic; valid if op == BLE_GATT_REGISTER_OP_CHR. */ + struct { + /** The ATT handle of the characteristic definition attribute. */ + uint16_t def_handle; + + /** The ATT handle of the characteristic value attribute. */ + uint16_t val_handle; + + /** + * The characteristic definition representing the characteristic + * being registered. + */ + const struct ble_gatt_chr_def *chr_def; + + /** + * The service definition corresponding to the characteristic's + * parent service. + */ + const struct ble_gatt_svc_def *svc_def; + } chr; + + /** Descriptor; valid if op == BLE_GATT_REGISTER_OP_DSC. */ + struct { + /** The ATT handle of the descriptor definition attribute. */ + uint16_t handle; + + /** + * The descriptor definition corresponding to the descriptor being + * registered. + */ + const struct ble_gatt_dsc_def *dsc_def; + + /** + * The characteristic definition corresponding to the descriptor's + * parent characteristic. + */ + const struct ble_gatt_chr_def *chr_def; + + /** + * The service definition corresponding to the descriptor's + * grandparent service + */ + const struct ble_gatt_svc_def *svc_def; + } dsc; + }; +}; + +typedef void ble_gatt_register_fn(struct ble_gatt_register_ctxt *ctxt, + void *arg); + +/** + * Queues a set of service definitions for registration. All services queued + * in this manner get registered when ble_gatts_start() is called. + * + * @param svcs An array of service definitions to queue for + * registration. This array must be + * terminated with an entry whose 'type' + * equals 0. + * + * @return 0 on success; + * BLE_HS_ENOMEM on heap exhaustion. + */ +int ble_gatts_add_svcs(const struct ble_gatt_svc_def *svcs); + +/** + * Set visibility of local GATT service. Invisible services are not removed + * from database but are not discoverable by peer devices. Service Changed + * should be handled by application when needed by calling + * ble_svc_gatt_changed(). + * + * @param handle Handle of service + * @param visible non-zero if service should be visible + * + * @return 0 on success; + * BLE_HS_ENOENT if service wasn't found. + */ +int ble_gatts_svc_set_visibility(uint16_t handle, int visible); + +/** + * Adjusts a host configuration object's settings to accommodate the specified + * service definition array. This function adds the counts to the appropriate + * fields in the supplied configuration object without clearing them first, so + * it can be called repeatedly with different inputs to calculate totals. Be + * sure to zero the GATT server settings prior to the first call to this + * function. + * + * @param defs The service array containing the resource + * definitions to be counted. + * + * @return 0 on success; + * BLE_HS_EINVAL if the svcs array contains an + * invalid resource definition. + */ +int ble_gatts_count_cfg(const struct ble_gatt_svc_def *defs); + +/** + * Send notification (or indication) to any connected devices that have + * subscribed for notification (or indication) for specified characteristic. + * + * @param chr_val_handle Characteristic value handle + */ +void ble_gatts_chr_updated(uint16_t chr_val_handle); + +/** + * Retrieves the attribute handle associated with a local GATT service. + * + * @param uuid The UUID of the service to look up. + * @param out_handle On success, populated with the handle of the + * service attribute. Pass null if you don't + * need this value. + * + * @return 0 on success; + * BLE_HS_ENOENT if the specified service could + * not be found. + */ +int ble_gatts_find_svc(const ble_uuid_t *uuid, uint16_t *out_handle); + +/** + * Retrieves the pair of attribute handles associated with a local GATT + * characteristic. + * + * @param svc_uuid The UUID of the parent service. + * @param chr_uuid The UUID of the characteristic to look up. + * @param out_def_handle On success, populated with the handle + * of the characteristic definition attribute. + * Pass null if you don't need this value. + * @param out_val_handle On success, populated with the handle + * of the characteristic value attribute. + * Pass null if you don't need this value. + * + * @return 0 on success; + * BLE_HS_ENOENT if the specified service or + * characteristic could not be found. + */ +int ble_gatts_find_chr(const ble_uuid_t *svc_uuid, const ble_uuid_t *chr_uuid, + uint16_t *out_def_handle, uint16_t *out_val_handle); + +/** + * Retrieves the attribute handle associated with a local GATT descriptor. + * + * @param svc_uuid The UUID of the grandparent service. + * @param chr_uuid The UUID of the parent characteristic. + * @param dsc_uuid The UUID of the descriptor ro look up. + * @param out_handle On success, populated with the handle + * of the descriptor attribute. Pass null if + * you don't need this value. + * + * @return 0 on success; + * BLE_HS_ENOENT if the specified service, + * characteristic, or descriptor could not be + * found. + */ +int ble_gatts_find_dsc(const ble_uuid_t *svc_uuid, const ble_uuid_t *chr_uuid, + const ble_uuid_t *dsc_uuid, uint16_t *out_dsc_handle); + +typedef void (*ble_gatt_svc_foreach_fn)(const struct ble_gatt_svc_def *svc, + uint16_t handle, + uint16_t end_group_handle, + void *arg); + +/** + * Prints dump of local GATT database. This is useful to log local state of + * database in human readable form. + */ +void ble_gatts_show_local(void); + +/** + * Resets the GATT server to its initial state. On success, this function + * removes all supported services, characteristics, and descriptors. This + * function requires that: + * o No peers are connected, and + * o No GAP operations are active (advertise, discover, or connect). + * + * @return 0 on success; + * BLE_HS_EBUSY if the GATT server could not be + * reset due to existing connections or active + * GAP procedures. + */ +int ble_gatts_reset(void); + +/** + * Makes all registered services available to peers. This function gets called + * automatically by the NimBLE host on startup; manual calls are only necessary + * for replacing the set of supported services with a new one. This function + * requires that: + * o No peers are connected, and + * o No GAP operations are active (advertise, discover, or connect). + * + * @return 0 on success; + * A BLE host core return code on unexpected + * error. + */ +int ble_gatts_start(void); + +/** + * Resets the GATT configuration parameters and deallocates the memory of attributes. + * + */ +void ble_gatts_stop(void); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs.h b/libesp32/NimBLE-Arduino/src/host/ble_hs.h new file mode 100644 index 000000000..43979ba57 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_hs.h @@ -0,0 +1,392 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_ +#define H_BLE_HS_ + +/** + * @brief Bluetooth Host + * @defgroup bt_host Bluetooth Host + * @{ + */ + +#include +#include "nimble/hci_common.h" +#include "host/ble_att.h" +#include "host/ble_eddystone.h" +#include "host/ble_gap.h" +#include "host/ble_gatt.h" +#include "host/ble_hs_adv.h" +#include "host/ble_hs_id.h" +#include "host/ble_hs_hci.h" +#include "host/ble_hs_log.h" +#include "host/ble_hs_mbuf.h" +#include "host/ble_hs_stop.h" +#include "host/ble_ibeacon.h" +#include "host/ble_l2cap.h" +#include "host/ble_sm.h" +#include "host/ble_store.h" +#include "host/ble_uuid.h" +#include "nimble/nimble_npl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_HS_FOREVER INT32_MAX + +/** Connection handle not present */ +#define BLE_HS_CONN_HANDLE_NONE 0xffff + +/** + * @brief Bluetooth Host Error Code + * @defgroup bt_host_err Bluetooth Host Error Code + * + * Defines error codes returned by Bluetooth host. If error comes from specific + * component (eg L2CAP or Security Manager) it is shifted by base allowing to + * identify component. + * @{ + */ + +#define BLE_HS_EAGAIN 1 +#define BLE_HS_EALREADY 2 +#define BLE_HS_EINVAL 3 +#define BLE_HS_EMSGSIZE 4 +#define BLE_HS_ENOENT 5 +#define BLE_HS_ENOMEM 6 +#define BLE_HS_ENOTCONN 7 +#define BLE_HS_ENOTSUP 8 +#define BLE_HS_EAPP 9 +#define BLE_HS_EBADDATA 10 +#define BLE_HS_EOS 11 +#define BLE_HS_ECONTROLLER 12 +#define BLE_HS_ETIMEOUT 13 +#define BLE_HS_EDONE 14 +#define BLE_HS_EBUSY 15 +#define BLE_HS_EREJECT 16 +#define BLE_HS_EUNKNOWN 17 +#define BLE_HS_EROLE 18 +#define BLE_HS_ETIMEOUT_HCI 19 +#define BLE_HS_ENOMEM_EVT 20 +#define BLE_HS_ENOADDR 21 +#define BLE_HS_ENOTSYNCED 22 +#define BLE_HS_EAUTHEN 23 +#define BLE_HS_EAUTHOR 24 +#define BLE_HS_EENCRYPT 25 +#define BLE_HS_EENCRYPT_KEY_SZ 26 +#define BLE_HS_ESTORE_CAP 27 +#define BLE_HS_ESTORE_FAIL 28 +#define BLE_HS_EPREEMPTED 29 +#define BLE_HS_EDISABLED 30 +#define BLE_HS_ESTALLED 31 + +/** Error base for ATT errors */ +#define BLE_HS_ERR_ATT_BASE 0x100 + +/** Converts error to ATT base */ +#define BLE_HS_ATT_ERR(x) ((x) ? BLE_HS_ERR_ATT_BASE + (x) : 0) + +/** Error base for HCI errors */ +#define BLE_HS_ERR_HCI_BASE 0x200 + +/** Converts error to HCI base */ +#define BLE_HS_HCI_ERR(x) ((x) ? BLE_HS_ERR_HCI_BASE + (x) : 0) + +/** Error base for L2CAP errors */ +#define BLE_HS_ERR_L2C_BASE 0x300 + +/** Converts error to L2CAP base */ +#define BLE_HS_L2C_ERR(x) ((x) ? BLE_HS_ERR_L2C_BASE + (x) : 0) + +/** Error base for local Security Manager errors */ +#define BLE_HS_ERR_SM_US_BASE 0x400 + +/** Converts error to local Security Manager base */ +#define BLE_HS_SM_US_ERR(x) ((x) ? BLE_HS_ERR_SM_US_BASE + (x) : 0) + +/** Error base for remote (peer) Security Manager errors */ +#define BLE_HS_ERR_SM_PEER_BASE 0x500 + +/** Converts error to remote (peer) Security Manager base */ +#define BLE_HS_SM_PEER_ERR(x) ((x) ? BLE_HS_ERR_SM_PEER_BASE + (x) : 0) + +/** Error base for hardware errors */ +#define BLE_HS_ERR_HW_BASE 0x600 + +/** Converts error to hardware error base */ +#define BLE_HS_HW_ERR(x) (BLE_HS_ERR_HW_BASE + (x)) + +/** + * @} + */ + +/** + * @brief Bluetooth Host Configuration + * @defgroup bt_host_conf Bluetooth Host Configuration + * + * @{ + */ + +/** + * @brief Local Input-Output capabilities of device + * @defgroup bt_host_io_local Local Input-Output capabilities of device + * + * @{ + */ + +/** DisplayOnly IO capability */ +#define BLE_HS_IO_DISPLAY_ONLY 0x00 + +/** DisplayYesNo IO capability */ +#define BLE_HS_IO_DISPLAY_YESNO 0x01 + +/** KeyboardOnly IO capability */ +#define BLE_HS_IO_KEYBOARD_ONLY 0x02 + +/** NoInputNoOutput IO capability */ +#define BLE_HS_IO_NO_INPUT_OUTPUT 0x03 + +/** KeyboardDisplay Only IO capability */ +#define BLE_HS_IO_KEYBOARD_DISPLAY 0x04 + +/** + * @} + */ + +/** @brief Stack reset callback + * + * @param reason Reason code for reset + */ +typedef void ble_hs_reset_fn(int reason); + + +/** @brief Stack sync callback */ +typedef void ble_hs_sync_fn(void); + +/** @brief Bluetooth Host main configuration structure + * + * Those can be used by application to configure stack. + * + * The only reason Security Manager (sm_ members) is configurable at runtime is + * to simplify security testing. Defaults for those are configured by selecting + * proper options in application's syscfg. + */ +struct ble_hs_cfg { + /** + * An optional callback that gets executed upon registration of each GATT + * resource (service, characteristic, or descriptor). + */ + ble_gatt_register_fn *gatts_register_cb; + + /** + * An optional argument that gets passed to the GATT registration + * callback. + */ + void *gatts_register_arg; + + /** Security Manager Local Input Output Capabilities */ + uint8_t sm_io_cap; + + /** @brief Security Manager OOB flag + * + * If set proper flag in Pairing Request/Response will be set. + */ + unsigned sm_oob_data_flag:1; + + /** @brief Security Manager Bond flag + * + * If set proper flag in Pairing Request/Response will be set. This results + * in storing keys distributed during bonding. + */ + unsigned sm_bonding:1; + + /** @brief Security Manager MITM flag + * + * If set proper flag in Pairing Request/Response will be set. This results + * in requiring Man-In-The-Middle protection when pairing. + */ + unsigned sm_mitm:1; + + /** @brief Security Manager Secure Connections flag + * + * If set proper flag in Pairing Request/Response will be set. This results + * in using LE Secure Connections for pairing if also supported by remote + * device. Fallback to legacy pairing if not supported by remote. + */ + unsigned sm_sc:1; + + /** @brief Security Manager Key Press Notification flag + * + * Currently unsupported and should not be set. + */ + unsigned sm_keypress:1; + + /** @brief Security Manager Local Key Distribution Mask */ + uint8_t sm_our_key_dist; + + /** @brief Security Manager Remote Key Distribution Mask */ + uint8_t sm_their_key_dist; + + /** @brief Stack reset callback + * + * This callback is executed when the host resets itself and the controller + * due to fatal error. + */ + ble_hs_reset_fn *reset_cb; + + /** @brief Stack sync callback + * + * This callback is executed when the host and controller become synced. + * This happens at startup and after a reset. + */ + ble_hs_sync_fn *sync_cb; + + /* XXX: These need to go away. Instead, the nimble host package should + * require the host-store API (not yet implemented).. + */ + /** Storage Read callback handles read of security material */ + ble_store_read_fn *store_read_cb; + + /** Storage Write callback handles write of security material */ + ble_store_write_fn *store_write_cb; + + /** Storage Delete callback handles deletion of security material */ + ble_store_delete_fn *store_delete_cb; + + /** @brief Storage Status callback. + * + * This callback gets executed when a persistence operation cannot be + * performed or a persistence failure is imminent. For example, if is + * insufficient storage capacity for a record to be persisted, this + * function gets called to give the application the opportunity to make + * room. + */ + ble_store_status_fn *store_status_cb; + + /** An optional argument that gets passed to the storage status callback. */ + void *store_status_arg; +}; + +extern struct ble_hs_cfg ble_hs_cfg; + +/** + * @} + */ + +/** + * @brief Indicates whether the host is enabled. The host is enabled if it is + * starting or fully started. It is disabled if it is stopping or stopped. + * + * @return 1 if the host is enabled; + * 0 if the host is disabled. + */ +int ble_hs_is_enabled(void); + +/** + * Indicates whether the host has synchronized with the controller. + * Synchronization must occur before any host procedures can be performed. + * + * @return 1 if the host and controller are in sync; + * 0 if the host and controller are out of sync. + */ +int ble_hs_synced(void); + +/** + * Synchronizes the host with the controller by sending a sequence of HCI + * commands. This function must be called before any other host functionality + * is used, but it must be called after both the host and controller are + * initialized. Typically, the host-parent-task calls this function at the top + * of its task routine. This function must only be called in the host parent + * task. A safe alternative for starting the stack from any task is to call + * `ble_hs_sched_start()`. + * + * If the host fails to synchronize with the controller (if the controller is + * not fully booted, for example), the host will attempt to resynchronize every + * 100 ms. For this reason, an error return code is not necessarily fatal. + * + * @return 0 on success; nonzero on error. + */ +int ble_hs_start(void); + +/** + * Enqueues a host start event to the default event queue. The actual host + * startup is performed in the host parent task, but using the default queue + * here ensures the event won't run until the end of main() when this is + * called during system initialization. This allows the application to + * configure the host package in the meantime. + * + * If auto-start is disabled, the application should use this function to start + * the BLE stack. This function can be called at any time as long as the host + * is stopped. When the host successfully starts, the application is notified + * via the ble_hs_cfg.sync_cb callback. + */ +void ble_hs_sched_start(void); + +/** + * Causes the host to reset the NimBLE stack as soon as possible. The + * application is notified when the reset occurs via the host reset callback. + * + * @param reason The host error code that gets passed to the reset callback. + */ +void ble_hs_sched_reset(int reason); + +/** + * Designates the specified event queue for NimBLE host work. By default, the + * host uses the default event queue and runs in the main task. This function + * is useful if you want the host to run in a different task. + * + * @param evq The event queue to use for host work. + */ +void ble_hs_evq_set(struct ble_npl_eventq *evq); + +/** + * Initializes the NimBLE host. This function must be called before the OS is + * started. The NimBLE stack requires an application task to function. One + * application task in particular is designated as the "host parent task". In + * addition to application-specific work, the host parent task does work for + * NimBLE by processing events generated by the host. + */ +void ble_hs_init(void); + +/** + * Deinitializes the NimBLE host. This function must be called after the + * NimBLE host stop procedure is complete. + */ +void ble_hs_deinit(void); + +/** + * @brief Called when the system is shutting down. Stops the BLE host. + * + * @param reason The reason for the shutdown. One of the + * HAL_RESET_[...] codes or an + * implementation-defined value. + * + * @return SYSDOWN_IN_PROGRESS. + */ +int ble_hs_shutdown(int reason); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_adv.h b/libesp32/NimBLE-Arduino/src/host/ble_hs_adv.h new file mode 100644 index 000000000..b0d85c02a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_hs_adv.h @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_ADV_ +#define H_BLE_HS_ADV_ + +#include +#include "host/ble_uuid.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_HS_ADV_MAX_SZ BLE_HCI_MAX_ADV_DATA_LEN + +/** Max field payload size (account for 2-byte header). */ +#define BLE_HS_ADV_MAX_FIELD_SZ (BLE_HS_ADV_MAX_SZ - 2) + +struct ble_hs_adv_field { + uint8_t length; + uint8_t type; + uint8_t value[]; +}; + +typedef int (* ble_hs_adv_parse_func_t) (const struct ble_hs_adv_field *, + void *); + +struct ble_hs_adv_fields { + /*** 0x01 - Flags. */ + uint8_t flags; + + /*** 0x02,0x03 - 16-bit service class UUIDs. */ + ble_uuid16_t *uuids16; + uint8_t num_uuids16; + unsigned uuids16_is_complete:1; + + /*** 0x04,0x05 - 32-bit service class UUIDs. */ + ble_uuid32_t *uuids32; + uint8_t num_uuids32; + unsigned uuids32_is_complete:1; + + /*** 0x06,0x07 - 128-bit service class UUIDs. */ + ble_uuid128_t *uuids128; + uint8_t num_uuids128; + unsigned uuids128_is_complete:1; + + /*** 0x08,0x09 - Local name. */ + uint8_t *name; + uint8_t name_len; + unsigned name_is_complete:1; + + /*** 0x0a - Tx power level. */ + int8_t tx_pwr_lvl; + unsigned tx_pwr_lvl_is_present:1; + + /*** 0x0d - Slave connection interval range. */ + uint8_t *slave_itvl_range; + + /*** 0x16 - Service data - 16-bit UUID. */ + uint8_t *svc_data_uuid16; + uint8_t svc_data_uuid16_len; + + /*** 0x17 - Public target address. */ + uint8_t *public_tgt_addr; + uint8_t num_public_tgt_addrs; + + /*** 0x19 - Appearance. */ + uint16_t appearance; + unsigned appearance_is_present:1; + + /*** 0x1a - Advertising interval. */ + uint16_t adv_itvl; + unsigned adv_itvl_is_present:1; + + /*** 0x20 - Service data - 32-bit UUID. */ + uint8_t *svc_data_uuid32; + uint8_t svc_data_uuid32_len; + + /*** 0x21 - Service data - 128-bit UUID. */ + uint8_t *svc_data_uuid128; + uint8_t svc_data_uuid128_len; + + /*** 0x24 - URI. */ + uint8_t *uri; + uint8_t uri_len; + + /*** 0xff - Manufacturer specific data. */ + uint8_t *mfg_data; + uint8_t mfg_data_len; +}; + +#define BLE_HS_ADV_TYPE_FLAGS 0x01 +#define BLE_HS_ADV_TYPE_INCOMP_UUIDS16 0x02 +#define BLE_HS_ADV_TYPE_COMP_UUIDS16 0x03 +#define BLE_HS_ADV_TYPE_INCOMP_UUIDS32 0x04 +#define BLE_HS_ADV_TYPE_COMP_UUIDS32 0x05 +#define BLE_HS_ADV_TYPE_INCOMP_UUIDS128 0x06 +#define BLE_HS_ADV_TYPE_COMP_UUIDS128 0x07 +#define BLE_HS_ADV_TYPE_INCOMP_NAME 0x08 +#define BLE_HS_ADV_TYPE_COMP_NAME 0x09 +#define BLE_HS_ADV_TYPE_TX_PWR_LVL 0x0a +#define BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE 0x12 +#define BLE_HS_ADV_TYPE_SOL_UUIDS16 0x14 +#define BLE_HS_ADV_TYPE_SOL_UUIDS128 0x15 +#define BLE_HS_ADV_TYPE_SVC_DATA_UUID16 0x16 +#define BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR 0x17 +#define BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR 0x18 +#define BLE_HS_ADV_TYPE_APPEARANCE 0x19 +#define BLE_HS_ADV_TYPE_ADV_ITVL 0x1a +#define BLE_HS_ADV_TYPE_SVC_DATA_UUID32 0x20 +#define BLE_HS_ADV_TYPE_SVC_DATA_UUID128 0x21 +#define BLE_HS_ADV_TYPE_URI 0x24 +#define BLE_HS_ADV_TYPE_MESH_PROV 0x29 +#define BLE_HS_ADV_TYPE_MESH_MESSAGE 0x2a +#define BLE_HS_ADV_TYPE_MESH_BEACON 0x2b +#define BLE_HS_ADV_TYPE_MFG_DATA 0xff + +#define BLE_HS_ADV_FLAGS_LEN 1 +#define BLE_HS_ADV_F_DISC_LTD 0x01 +#define BLE_HS_ADV_F_DISC_GEN 0x02 +#define BLE_HS_ADV_F_BREDR_UNSUP 0x04 + +#define BLE_HS_ADV_TX_PWR_LVL_LEN 1 + +/** + * Set the tx_pwr_lvl field to this if you want the stack to fill in the tx + * power level field. + */ +#define BLE_HS_ADV_TX_PWR_LVL_AUTO (-128) + +#define BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN 4 + +#define BLE_HS_ADV_SVC_DATA_UUID16_MIN_LEN 2 + +#define BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN 6 + +#define BLE_HS_ADV_APPEARANCE_LEN 2 + +#define BLE_HS_ADV_ADV_ITVL_LEN 2 + +#define BLE_HS_ADV_SVC_DATA_UUID32_MIN_LEN 4 + +#define BLE_HS_ADV_SVC_DATA_UUID128_MIN_LEN 16 + +int ble_hs_adv_set_fields_mbuf(const struct ble_hs_adv_fields *adv_fields, + struct os_mbuf *om); + +int ble_hs_adv_set_fields(const struct ble_hs_adv_fields *adv_fields, + uint8_t *dst, uint8_t *dst_len, uint8_t max_len); + +int ble_hs_adv_parse_fields(struct ble_hs_adv_fields *adv_fields, uint8_t *src, + uint8_t src_len); + +int ble_hs_adv_parse(const uint8_t *data, uint8_t length, + ble_hs_adv_parse_func_t func, void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_hci.h b/libesp32/NimBLE-Arduino/src/host/ble_hs_hci.h new file mode 100644 index 000000000..e10b8e62a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_hs_hci.h @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_HCI_ +#define H_BLE_HS_HCI_ + +/** + * @brief Bluetooth Host HCI utils + * @defgroup bt_host_hci Bluetooth Host HCI utils + * @ingroup bt_host + * @{ + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Queries the controller for the channel map used with the specified + * connection. The channel map is represented as an array of five bytes, with + * each bit corresponding to an individual channel. The array is interpreted + * as little-endian, such that: + * map[0] & 0x01 --> Channel 0. + * map[0] & 0x02 --> Channel 1. + * ... + * map[1] & 0x01 --> Channel 8. + * + * As there are 37 channels, only the first 37 bits get written. + * + * If a bit is 1, the corresponding channel is used. Otherwise, the channel is + * unused. + * + * @param conn_handle The handle of the connection whose channel map + * is being read. + * @param out_chan_map On success, the retrieved channel map gets + * written here. This buffer must have a size + * >= 5 bytes. + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_hs_hci_read_chan_map(uint16_t conn_handle, uint8_t *out_chan_map); + +/** + * Instructs the controller to use the specified channel map. The channel map + * is represented as an array of five bytes, with each bit corresponding to an + * individual channel. The array is interpreted as little-endian, such that: + * map[0] & 0x01 --> Channel 0. + * map[0] & 0x02 --> Channel 1. + * ... + * map[1] & 0x01 --> Channel 8. + * + * As there are 37 channels, only the first 37 bits should be written are used. + * + * If a bit is 1, the corresponding channel can be used. Otherwise, the + * channel should not be used. + * + * @param chan_map The channel map to configure. This buffer + * should have a size of 5 bytes. + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_hs_hci_set_chan_class(const uint8_t *chan_map); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_id.h b/libesp32/NimBLE-Arduino/src/host/ble_hs_id.h new file mode 100644 index 000000000..c96bd20f5 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_hs_id.h @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_ID_ +#define H_BLE_HS_ID_ + +/** + * @brief Bluetooth Host Identity + * @defgroup bt_host_id Bluetooth Host Identity + * @ingroup bt_host + * @{ + */ + +#include +#include "nimble/ble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Generates a new random address. This function does not configure the device + * with the new address; the caller can use the address in subsequent + * operations. + * + * @param nrpa The type of random address to generate: + * 0: static + * 1: non-resolvable private + * @param out_addr On success, the generated address gets written + * here. + * + * @return 0 on success; nonzero on failure. + */ +int ble_hs_id_gen_rnd(int nrpa, ble_addr_t *out_addr); + +/** + * Sets the device's random address. The address type (static vs. + * non-resolvable private) is inferred from the most-significant byte of the + * address. The address is specified in host byte order (little-endian!). + * + * @param rnd_addr The random address to set. + * + * @return 0 on success; + * BLE_HS_EINVAL if the specified address is not a + * valid static random or non-resolvable + * private address. + * Other nonzero on error. + */ +int ble_hs_id_set_rnd(const uint8_t *rnd_addr); + +/** + * Retrieves one of the device's identity addresses. The device can have two + * identity addresses: one public and one random. The id_addr_type argument + * specifies which of these two addresses to retrieve. + * + * @param id_addr_type The type of identity address to retrieve. + * Valid values are: + * o BLE_ADDR_PUBLIC + * o BLE_ADDR_RANDOM + * @param out_id_addr On success, the requested identity address is + * copied into this buffer. The buffer must + * be at least six bytes in size. Pass NULL + * if you do not require this information. + * @param out_is_nrpa On success, the pointed-to value indicates + * whether the retrieved address is a + * non-resolvable private address. Pass NULL + * if you do not require this information. + * + * @return 0 on success; + * BLE_HS_EINVAL if an invalid address type was + * specified; + * BLE_HS_ENOADDR if the device does not have an + * identity address of the requested type; + * Other BLE host core code on error. + */ +int ble_hs_id_copy_addr(uint8_t id_addr_type, uint8_t *out_id_addr, + int *out_is_nrpa); + +/** + * Determines the best address type to use for automatic address type + * resolution. Calculation of the best address type is done as follows: + * + * if privacy requested: + * if we have a random static address: + * --> RPA with static random ID + * else + * --> RPA with public ID + * end + * else + * if we have a random static address: + * --> random static address + * else + * --> public address + * end + * end + * + * @param privacy (0/1) Whether to use a private address. + * @param out_addr_type On success, the "own addr type" code gets + * written here. + * + * @return 0 if an address type was successfully inferred. + * BLE_HS_ENOADDR if the device does not have a + * suitable address. + * Other BLE host core code on error. + */ +int ble_hs_id_infer_auto(int privacy, uint8_t *out_addr_type); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_log.h b/libesp32/NimBLE-Arduino/src/host/ble_hs_log.h new file mode 100644 index 000000000..3fb1db978 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_hs_log.h @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_LOG_ +#define H_BLE_HS_LOG_ + +#include "modlog/modlog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mbuf; + +#define BLE_HS_LOG(lvl, ...) \ + MODLOG_ ## lvl(LOG_MODULE_NIMBLE_HOST, __VA_ARGS__) + +#define BLE_HS_LOG_ADDR(lvl, addr) \ + MODLOG_ ## lvl(LOG_MODULE_NIMBLE_HOST, \ + "%02x:%02x:%02x:%02x:%02x:%02x", \ + (addr)[5], (addr)[4], (addr)[3], \ + (addr)[2], (addr)[1], (addr)[0]) + +void ble_hs_log_mbuf(const struct os_mbuf *om); +void ble_hs_log_flat_buf(const void *data, int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_mbuf.h b/libesp32/NimBLE-Arduino/src/host/ble_hs_mbuf.h new file mode 100644 index 000000000..a3c2c0296 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_hs_mbuf.h @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_MBUF_ +#define H_BLE_HS_MBUF_ + +/** + * @brief Bluetooth Host chained memory buffer (mbuf) + * @defgroup bt_host_mbuf Bluetooth Host chained memory buffer (mbuf) + * @ingroup bt_host + * @{ + */ + +#include +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mbuf; + +/** + * Allocates an mbuf suitable for an ATT command packet. The resulting packet + * has sufficient leading space for: + * - ACL data header + * - L2CAP B-frame header + * - Largest ATT command base (prepare write request / response). + * + * @return An empty mbuf on success, NULL on error. + */ +struct os_mbuf *ble_hs_mbuf_att_pkt(void); + +/** + * Allocates an mbuf and fills it with the contents of the specified flat + * buffer. + * + * @param buf The flat buffer to copy from. + * @param len The length of the flat buffer. + * + * @return A newly-allocated mbuf on success, NULL on error. + */ +struct os_mbuf *ble_hs_mbuf_from_flat(const void *buf, uint16_t len); + +/** + * Copies the contents of an mbuf into the specified flat buffer. If the flat + * buffer is too small to contain the mbuf's contents, it is filled to capacity + * and BLE_HS_EMSGSIZE is returned. + * + * @param om The mbuf to copy from. + * @param flat The destination flat buffer. + * @param max_len The size of the flat buffer. + * @param out_copy_len The number of bytes actually copied gets written here. + * + * @return 0 on success or BLE host core return code on error. + */ +int ble_hs_mbuf_to_flat(const struct os_mbuf *om, void *flat, uint16_t max_len, + uint16_t *out_copy_len); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_pvcy.h b/libesp32/NimBLE-Arduino/src/host/ble_hs_pvcy.h new file mode 100644 index 000000000..0ff32b80b --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_hs_pvcy.h @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Espressif Systems (Shanghai) PTE LTD + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "host/ble_hs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +/* Called to configure local(own) privacy (RPA) when using host based privacy. In + * Host based privacy as controller is not aware of RPA, we do it via + * 'BLE_ADDR_RANDOM' addr_type route. + * + * @param enable RPA when enable is not 0 + * disable RPA otherwise + * + * @return return 0 when successful. + * return appropriate error code otherwise + */ +int ble_hs_pvcy_rpa_config(uint8_t enable); +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_stop.h b/libesp32/NimBLE-Arduino/src/host/ble_hs_stop.h new file mode 100644 index 000000000..d16c9c27b --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_hs_stop.h @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_STOP_ +#define H_BLE_HS_STOP_ + +/** @typedef ble_hs_stop_fn + * @brief Callback function; reports the result of a host stop procedure. + * + * @param status The result of the host stop procedure. One of + * the HAL_RESET_[...] codes or an + * implementation-defined value. + * @param arg Optional argument specified when the stop + * procedure was initiated. + * + */ +typedef void ble_hs_stop_fn(int status, void *arg); + +/** + * @brief Used to report the result of a stop procedure. + * + * This should be used as an opaque structure and not modified manually. + */ +struct ble_hs_stop_listener { + ble_hs_stop_fn *fn; + void *arg; + SLIST_ENTRY(ble_hs_stop_listener) link; +}; + +/** + * @brief Stops the BLE host. + * + * Aborts all active GAP procedures and terminates all open connections. + * Connection termination is performed asynchronously, so this function's + * result is reported via the provided listener. + * + * @param listener A listener to populate. This object's initial + * value doesn't matter, but its lifetime must + * extend until the stop procedure completes. + * @param fn The callback to execute when the stop procedure + * completes. + * @param arg Optional argument to pass to the callback. + * + * @return 0: Stop procedure successfully initiated. + * BLE_HS_EBUSY: Stop procedure already in + * progress; the provided callback gets called + * when the procedure completes. + * BLE_HS_EALREADY: Host already stopped; the + * provided callback does *not* get called. + */ +int ble_hs_stop(struct ble_hs_stop_listener *listener, + ble_hs_stop_fn *fn, void *arg); + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_ibeacon.h b/libesp32/NimBLE-Arduino/src/host/ble_ibeacon.h new file mode 100644 index 000000000..fff7c57ae --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_ibeacon.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_IBEACON_ +#define H_BLE_IBEACON_ + +#ifdef __cplusplus +extern "C" { +#endif + +int ble_ibeacon_set_adv_data(void *uuid128, uint16_t major, + uint16_t minor, int8_t measured_power); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_l2cap.h b/libesp32/NimBLE-Arduino/src/host/ble_l2cap.h new file mode 100644 index 000000000..644bd9d0d --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_l2cap.h @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_L2CAP_ +#define H_BLE_L2CAP_ + +#include "nimble/nimble_opt.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_l2cap_sig_update_req; +struct ble_hs_conn; + +#define BLE_L2CAP_CID_ATT 4 +#define BLE_L2CAP_CID_SIG 5 +#define BLE_L2CAP_CID_SM 6 + +#define BLE_L2CAP_SIG_OP_REJECT 0x01 +#define BLE_L2CAP_SIG_OP_CONNECT_REQ 0x02 +#define BLE_L2CAP_SIG_OP_CONNECT_RSP 0x03 +#define BLE_L2CAP_SIG_OP_CONFIG_REQ 0x04 +#define BLE_L2CAP_SIG_OP_CONFIG_RSP 0x05 +#define BLE_L2CAP_SIG_OP_DISCONN_REQ 0x06 +#define BLE_L2CAP_SIG_OP_DISCONN_RSP 0x07 +#define BLE_L2CAP_SIG_OP_ECHO_REQ 0x08 +#define BLE_L2CAP_SIG_OP_ECHO_RSP 0x09 +#define BLE_L2CAP_SIG_OP_INFO_REQ 0x0a +#define BLE_L2CAP_SIG_OP_INFO_RSP 0x0b +#define BLE_L2CAP_SIG_OP_CREATE_CHAN_REQ 0x0c +#define BLE_L2CAP_SIG_OP_CREATE_CHAN_RSP 0x0d +#define BLE_L2CAP_SIG_OP_MOVE_CHAN_REQ 0x0e +#define BLE_L2CAP_SIG_OP_MOVE_CHAN_RSP 0x0f +#define BLE_L2CAP_SIG_OP_MOVE_CHAN_CONF_REQ 0x10 +#define BLE_L2CAP_SIG_OP_MOVE_CHAN_CONF_RSP 0x11 +#define BLE_L2CAP_SIG_OP_UPDATE_REQ 0x12 +#define BLE_L2CAP_SIG_OP_UPDATE_RSP 0x13 +#define BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ 0x14 +#define BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP 0x15 +#define BLE_L2CAP_SIG_OP_FLOW_CTRL_CREDIT 0x16 +#define BLE_L2CAP_SIG_OP_MAX 0x17 + +#define BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD 0x0000 +#define BLE_L2CAP_SIG_ERR_MTU_EXCEEDED 0x0001 +#define BLE_L2CAP_SIG_ERR_INVALID_CID 0x0002 + +#define BLE_L2CAP_COC_ERR_CONNECTION_SUCCESS 0x0000 +#define BLE_L2CAP_COC_ERR_UNKNOWN_LE_PSM 0x0002 +#define BLE_L2CAP_COC_ERR_NO_RESOURCES 0x0004 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHEN 0x0005 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHOR 0x0006 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_KEY_SZ 0x0007 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_ENC 0x0008 +#define BLE_L2CAP_COC_ERR_INVALID_SOURCE_CID 0x0009 +#define BLE_L2CAP_COC_ERR_SOURCE_CID_ALREADY_USED 0x000A +#define BLE_L2CAP_COC_ERR_UNACCEPTABLE_PARAMETERS 0x000B + +#define BLE_L2CAP_EVENT_COC_CONNECTED 0 +#define BLE_L2CAP_EVENT_COC_DISCONNECTED 1 +#define BLE_L2CAP_EVENT_COC_ACCEPT 2 +#define BLE_L2CAP_EVENT_COC_DATA_RECEIVED 3 +#define BLE_L2CAP_EVENT_COC_TX_UNSTALLED 4 + +typedef void ble_l2cap_sig_update_fn(uint16_t conn_handle, int status, + void *arg); + +struct ble_l2cap_sig_update_params { + uint16_t itvl_min; + uint16_t itvl_max; + uint16_t slave_latency; + uint16_t timeout_multiplier; +}; + +int ble_l2cap_sig_update(uint16_t conn_handle, + struct ble_l2cap_sig_update_params *params, + ble_l2cap_sig_update_fn *cb, void *cb_arg); + +struct ble_l2cap_chan; + +/** + * Represents a L2CAP-related event. + * When such an event occurs, the host notifies the application by passing an + * instance of this structure to an application-specified callback. + */ +struct ble_l2cap_event { + /** + * Indicates the type of L2CAP event that occurred. This is one of the + * BLE_L2CAP_EVENT codes. + */ + uint8_t type; + + /** + * A discriminated union containing additional details concerning the L2CAP + * event. The 'type' field indicates which member of the union is valid. + */ + union { + /** + * Represents a connection attempt. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_CONNECTED */ + struct { + /** + * The status of the connection attempt; + * o 0: the connection was successfully established. + * o BLE host error code: the connection attempt failed for + * the specified reason. + */ + int status; + + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** The L2CAP channel of the relevant L2CAP connection. */ + struct ble_l2cap_chan *chan; + } connect; + + /** + * Represents a terminated connection. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_DISCONNECTED + */ + struct { + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** Information about the L2CAP connection prior to termination. */ + struct ble_l2cap_chan *chan; + } disconnect; + + /** + * Represents connection accept. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_ACCEPT + */ + struct { + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** MTU supported by peer device on the channel */ + uint16_t peer_sdu_size; + + /** The L2CAP channel of the relevant L2CAP connection. */ + struct ble_l2cap_chan *chan; + } accept; + + /** + * Represents received data. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_DATA_RECEIVED + */ + struct { + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** The L2CAP channel of the relevant L2CAP connection. */ + struct ble_l2cap_chan *chan; + + /** The mbuf with received SDU. */ + struct os_mbuf *sdu_rx; + } receive; + + /** + * Represents tx_unstalled data. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_TX_UNSTALLED + */ + struct { + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** The L2CAP channel of the relevant L2CAP connection. */ + struct ble_l2cap_chan *chan; + + /** + * The status of the send attempt which was stalled due to + * lack of credits; This can be non zero only if there + * is an issue with memory allocation for following SDU fragments. + * In such a case last SDU has been partially sent to peer device + * and it is up to application to decide how to handle it. + */ + int status; + } tx_unstalled; + }; +}; + +typedef int ble_l2cap_event_fn(struct ble_l2cap_event *event, void *arg); + +uint16_t ble_l2cap_get_conn_handle(struct ble_l2cap_chan *chan); +int ble_l2cap_create_server(uint16_t psm, uint16_t mtu, + ble_l2cap_event_fn *cb, void *cb_arg); + +int ble_l2cap_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu, + struct os_mbuf *sdu_rx, + ble_l2cap_event_fn *cb, void *cb_arg); +int ble_l2cap_disconnect(struct ble_l2cap_chan *chan); +int ble_l2cap_send(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_tx); +int ble_l2cap_recv_ready(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_rx); +int ble_l2cap_get_scid(struct ble_l2cap_chan *chan); +int ble_l2cap_get_dcid(struct ble_l2cap_chan *chan); +int ble_l2cap_get_our_mtu(struct ble_l2cap_chan *chan); +int ble_l2cap_get_peer_mtu(struct ble_l2cap_chan *chan); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_monitor.h b/libesp32/NimBLE-Arduino/src/host/ble_monitor.h new file mode 100644 index 000000000..61722f7db --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_monitor.h @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_MONITOR_ +#define H_BLE_MONITOR_ + +#include + +#undef BLE_MONITOR +#define BLE_MONITOR (MYNEWT_VAL(BLE_MONITOR_UART) || MYNEWT_VAL(BLE_MONITOR_RTT)) + +#ifdef __cplusplus +extern "C" { +#endif + +int ble_monitor_log(int level, const char *fmt, ...); + +int ble_monitor_out(int c); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_sm.h b/libesp32/NimBLE-Arduino/src/host/ble_sm.h new file mode 100644 index 000000000..9bd25adfc --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_sm.h @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_SM_ +#define H_BLE_SM_ + +#include +#include "syscfg/syscfg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_SM_ERR_PASSKEY 0x01 +#define BLE_SM_ERR_OOB 0x02 +#define BLE_SM_ERR_AUTHREQ 0x03 +#define BLE_SM_ERR_CONFIRM_MISMATCH 0x04 +#define BLE_SM_ERR_PAIR_NOT_SUPP 0x05 +#define BLE_SM_ERR_ENC_KEY_SZ 0x06 +#define BLE_SM_ERR_CMD_NOT_SUPP 0x07 +#define BLE_SM_ERR_UNSPECIFIED 0x08 +#define BLE_SM_ERR_REPEATED 0x09 +#define BLE_SM_ERR_INVAL 0x0a +#define BLE_SM_ERR_DHKEY 0x0b +#define BLE_SM_ERR_NUMCMP 0x0c +#define BLE_SM_ERR_ALREADY 0x0d +#define BLE_SM_ERR_CROSS_TRANS 0x0e +#define BLE_SM_ERR_MAX_PLUS_1 0x0f + +#define BLE_SM_PAIR_ALG_JW 0 +#define BLE_SM_PAIR_ALG_PASSKEY 1 +#define BLE_SM_PAIR_ALG_OOB 2 +#define BLE_SM_PAIR_ALG_NUMCMP 3 + +#define BLE_SM_PAIR_KEY_DIST_ENC 0x01 +#define BLE_SM_PAIR_KEY_DIST_ID 0x02 +#define BLE_SM_PAIR_KEY_DIST_SIGN 0x04 +#define BLE_SM_PAIR_KEY_DIST_LINK 0x08 +#define BLE_SM_PAIR_KEY_DIST_RESERVED 0xf0 + +#define BLE_SM_IO_CAP_DISP_ONLY 0x00 +#define BLE_SM_IO_CAP_DISP_YES_NO 0x01 +#define BLE_SM_IO_CAP_KEYBOARD_ONLY 0x02 +#define BLE_SM_IO_CAP_NO_IO 0x03 +#define BLE_SM_IO_CAP_KEYBOARD_DISP 0x04 +#define BLE_SM_IO_CAP_RESERVED 0x05 + +#define BLE_SM_PAIR_OOB_NO 0x00 +#define BLE_SM_PAIR_OOB_YES 0x01 +#define BLE_SM_PAIR_OOB_RESERVED 0x02 + +#define BLE_SM_PAIR_AUTHREQ_BOND 0x01 +#define BLE_SM_PAIR_AUTHREQ_MITM 0x04 +#define BLE_SM_PAIR_AUTHREQ_SC 0x08 +#define BLE_SM_PAIR_AUTHREQ_KEYPRESS 0x10 +#define BLE_SM_PAIR_AUTHREQ_RESERVED 0xe2 + +#define BLE_SM_PAIR_KEY_SZ_MIN 7 +#define BLE_SM_PAIR_KEY_SZ_MAX 16 + +/* + * The security manager asks the application to perform a key generation + * action. The application passes the passkey back to SM via + * ble_sm_inject_io(). + */ +#define BLE_SM_IOACT_NONE 0 +#define BLE_SM_IOACT_OOB 1 +#define BLE_SM_IOACT_INPUT 2 +#define BLE_SM_IOACT_DISP 3 +#define BLE_SM_IOACT_NUMCMP 4 +#define BLE_SM_IOACT_MAX_PLUS_ONE 5 + +struct ble_sm_io { + uint8_t action; + union { + uint32_t passkey; + uint8_t oob[16]; + uint8_t numcmp_accept; + }; +}; + +#if NIMBLE_BLE_SM +int ble_sm_inject_io(uint16_t conn_handle, struct ble_sm_io *pkey); +#else +#define ble_sm_inject_io(conn_handle, pkey) \ + ((void)(conn_handle), BLE_HS_ENOTSUP) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_store.h b/libesp32/NimBLE-Arduino/src/host/ble_store.h new file mode 100644 index 000000000..e470f8ec3 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_store.h @@ -0,0 +1,306 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_STORE_ +#define H_BLE_STORE_ + +#include +#include "nimble/ble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_STORE_OBJ_TYPE_OUR_SEC 1 +#define BLE_STORE_OBJ_TYPE_PEER_SEC 2 +#define BLE_STORE_OBJ_TYPE_CCCD 3 +#define BLE_STORE_OBJ_TYPE_PEER_DEV_REC 4 + +/** Failed to persist record; insufficient storage capacity. */ +#define BLE_STORE_EVENT_OVERFLOW 1 + +/** About to execute a procedure that may fail due to overflow. */ +#define BLE_STORE_EVENT_FULL 2 + +/** + * Used as a key for lookups of security material. This struct corresponds to + * the following store object types: + * o BLE_STORE_OBJ_TYPE_OUR_SEC + * o BLE_STORE_OBJ_TYPE_PEER_SEC + */ +struct ble_store_key_sec { + /** + * Key by peer identity address; + * peer_addr=BLE_ADDR_NONE means don't key off peer. + */ + ble_addr_t peer_addr; + + /** Key by ediv; ediv_rand_present=0 means don't key off ediv. */ + uint16_t ediv; + + /** Key by rand_num; ediv_rand_present=0 means don't key off rand_num. */ + uint64_t rand_num; + + unsigned ediv_rand_present:1; + + /** Number of results to skip; 0 means retrieve the first match. */ + uint8_t idx; +}; + +/** + * Represents stored security material. This struct corresponds to the + * following store object types: + * o BLE_STORE_OBJ_TYPE_OUR_SEC + * o BLE_STORE_OBJ_TYPE_PEER_SEC + */ +struct ble_store_value_sec { + ble_addr_t peer_addr; + + uint8_t key_size; + uint16_t ediv; + uint64_t rand_num; + uint8_t ltk[16]; + uint8_t ltk_present:1; + + uint8_t irk[16]; + uint8_t irk_present:1; + + uint8_t csrk[16]; + uint8_t csrk_present:1; + + unsigned authenticated:1; + uint8_t sc:1; +}; + +/** + * Used as a key for lookups of stored client characteristic configuration + * descriptors (CCCDs). This struct corresponds to the BLE_STORE_OBJ_TYPE_CCCD + * store object type. + */ +struct ble_store_key_cccd { + /** + * Key by peer identity address; + * peer_addr=BLE_ADDR_NONE means don't key off peer. + */ + ble_addr_t peer_addr; + + /** + * Key by characteristic value handle; + * chr_val_handle=0 means don't key off characteristic handle. + */ + uint16_t chr_val_handle; + + /** Number of results to skip; 0 means retrieve the first match. */ + uint8_t idx; +}; + +/** + * Represents a stored client characteristic configuration descriptor (CCCD). + * This struct corresponds to the BLE_STORE_OBJ_TYPE_CCCD store object type. + */ +struct ble_store_value_cccd { + ble_addr_t peer_addr; + uint16_t chr_val_handle; + uint16_t flags; + unsigned value_changed:1; +}; + +/** + * Used as a key for store lookups. This union must be accompanied by an + * object type code to indicate which field is valid. + */ +union ble_store_key { + struct ble_store_key_sec sec; + struct ble_store_key_cccd cccd; +}; + +/** + * Represents stored data. This union must be accompanied by an object type + * code to indicate which field is valid. + */ +union ble_store_value { + struct ble_store_value_sec sec; + struct ble_store_value_cccd cccd; +}; + +struct ble_store_status_event { + /** + * The type of event being reported; one of the BLE_STORE_EVENT_TYPE_[...] + * codes. + */ + int event_code; + + /** + * Additional data related to the event; the valid field is inferred from + * the obj_type,event_code pair. + */ + union { + /** + * Represents a write that failed due to storage exhaustion. Valid for + * the following event types: + * o BLE_STORE_EVENT_OVERFLOW + */ + struct { + /** The type of object that failed to be written. */ + int obj_type; + + /** The object that failed to be written. */ + const union ble_store_value *value; + } overflow; + + /** + * Represents the possibility that a scheduled write will fail due to + * storage exhaustion. Valid for the following event types: + * o BLE_STORE_EVENT_FULL + */ + struct { + /** The type of object that may fail to be written. */ + int obj_type; + + /** The handle of the connection which prompted the write. */ + uint16_t conn_handle; + } full; + }; +}; + +/** + * Searches the store for an object matching the specified criteria. If a + * match is found, it is read from the store and the dst parameter is populated + * with the retrieved object. + * + * @param obj_type The type of object to search for; one of the + * BLE_STORE_OBJ_TYPE_[...] codes. + * @param key Specifies properties of the object to search + * for. An object is retrieved if it matches + * these criteria. + * @param dst On success, this is populated with the + * retrieved object. + * + * @return 0 if an object was successfully retreived; + * BLE_HS_ENOENT if no matching object was found; + * Other nonzero on error. + */ +typedef int ble_store_read_fn(int obj_type, const union ble_store_key *key, + union ble_store_value *dst); + +/** + * Writes the specified object to the store. If an object with the same + * identity is already in the store, it is replaced. If the store lacks + * sufficient capacity to write the object, this function may remove previously + * stored values to make room. + * + * @param obj_type The type of object being written; one of the + * BLE_STORE_OBJ_TYPE_[...] codes. + * @param val The object to persist. + * + * @return 0 if the object was successfully written; + * Other nonzero on error. + */ +typedef int ble_store_write_fn(int obj_type, const union ble_store_value *val); + +/** + * Searches the store for the first object matching the specified criteria. If + * a match is found, it is deleted from the store. + * + * @param obj_type The type of object to delete; one of the + * BLE_STORE_OBJ_TYPE_[...] codes. + * @param key Specifies properties of the object to search + * for. An object is deleted if it matches + * these criteria. + * @return 0 if an object was successfully retrieved; + * BLE_HS_ENOENT if no matching object was found; + * Other nonzero on error. + */ +typedef int ble_store_delete_fn(int obj_type, const union ble_store_key *key); + +/** + * Indicates an inability to perform a store operation. This callback should + * do one of two things: + * o Address the problem and return 0, indicating that the store operation + * should proceed. + * o Return nonzero to indicate that the store operation should be aborted. + * + * @param event Describes the store event being reported. + * @param arg Optional user argument. + * + * @return 0 if the store operation should proceed; + * nonzero if the store operation should be + * aborted. + */ +typedef int ble_store_status_fn(struct ble_store_status_event *event, + void *arg); + +int ble_store_read(int obj_type, const union ble_store_key *key, + union ble_store_value *val); +int ble_store_write(int obj_type, const union ble_store_value *val); +int ble_store_delete(int obj_type, const union ble_store_key *key); +int ble_store_overflow_event(int obj_type, const union ble_store_value *value); +int ble_store_full_event(int obj_type, uint16_t conn_handle); + +int ble_store_read_our_sec(const struct ble_store_key_sec *key_sec, + struct ble_store_value_sec *value_sec); +int ble_store_write_our_sec(const struct ble_store_value_sec *value_sec); +int ble_store_delete_our_sec(const struct ble_store_key_sec *key_sec); +int ble_store_read_peer_sec(const struct ble_store_key_sec *key_sec, + struct ble_store_value_sec *value_sec); +int ble_store_write_peer_sec(const struct ble_store_value_sec *value_sec); +int ble_store_delete_peer_sec(const struct ble_store_key_sec *key_sec); + +int ble_store_read_cccd(const struct ble_store_key_cccd *key, + struct ble_store_value_cccd *out_value); +int ble_store_write_cccd(const struct ble_store_value_cccd *value); +int ble_store_delete_cccd(const struct ble_store_key_cccd *key); + +void ble_store_key_from_value_sec(struct ble_store_key_sec *out_key, + const struct ble_store_value_sec *value); +void ble_store_key_from_value_cccd(struct ble_store_key_cccd *out_key, + const struct ble_store_value_cccd *value); + +void ble_store_key_from_value(int obj_type, + union ble_store_key *out_key, + const union ble_store_value *value); + +typedef int ble_store_iterator_fn(int obj_type, + union ble_store_value *val, + void *cookie); + +int ble_store_iterate(int obj_type, + ble_store_iterator_fn *callback, + void *cookie); + +int ble_store_clear(void); + +/*** Utility functions. */ + +int ble_store_clean_old_cccds(const ble_addr_t *curr_peer); + +int ble_store_util_bonded_peers(ble_addr_t *out_peer_id_addrs, + int *out_num_peers, + int max_peers); +int ble_store_util_delete_all(int type, const union ble_store_key *key); +int ble_store_util_delete_peer(const ble_addr_t *peer_id_addr); +int ble_store_util_delete_oldest_peer(void); +int ble_store_util_count(int type, int *out_count); +int ble_store_util_status_rr(struct ble_store_status_event *event, void *arg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/host/ble_uuid.h b/libesp32/NimBLE-Arduino/src/host/ble_uuid.h new file mode 100644 index 000000000..d3576c595 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/ble_uuid.h @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_UUID_ +#define H_BLE_UUID_ + +/** + * @brief Bluetooth UUID + * @defgroup bt_uuid Bluetooth UUID + * @ingroup bt_host + * @{ + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mbuf; + +/** Type of UUID */ +enum { + /** 16-bit UUID (BT SIG assigned) */ + BLE_UUID_TYPE_16 = 16, + + /** 32-bit UUID (BT SIG assigned) */ + BLE_UUID_TYPE_32 = 32, + + /** 128-bit UUID */ + BLE_UUID_TYPE_128 = 128, +}; + +/** Generic UUID type, to be used only as a pointer */ +typedef struct { + /** Type of the UUID */ + uint8_t type; +} ble_uuid_t; + +/** 16-bit UUID */ +typedef struct { + ble_uuid_t u; + uint16_t value; +} ble_uuid16_t; + +/** 32-bit UUID */ +typedef struct { + ble_uuid_t u; + uint32_t value; +} ble_uuid32_t; + +/** 128-bit UUID */ +typedef struct { + ble_uuid_t u; + uint8_t value[16]; +} ble_uuid128_t; + +/** Universal UUID type, to be used for any-UUID static allocation */ +typedef union { + ble_uuid_t u; + ble_uuid16_t u16; + ble_uuid32_t u32; + ble_uuid128_t u128; +} ble_uuid_any_t; + +#define BLE_UUID16_INIT(uuid16) \ + { \ + .u.type = BLE_UUID_TYPE_16, \ + .value = (uuid16), \ + } + +#define BLE_UUID32_INIT(uuid32) \ + { \ + .u.type = BLE_UUID_TYPE_32, \ + .value = (uuid32), \ + } + +#define BLE_UUID128_INIT(uuid128...) \ + { \ + .u.type = BLE_UUID_TYPE_128, \ + .value = { uuid128 }, \ + } + +#define BLE_UUID16_DECLARE(uuid16) \ + ((ble_uuid_t *) (&(ble_uuid16_t) BLE_UUID16_INIT(uuid16))) + +#define BLE_UUID32_DECLARE(uuid32) \ + ((ble_uuid_t *) (&(ble_uuid32_t) BLE_UUID32_INIT(uuid32))) + +#define BLE_UUID128_DECLARE(uuid128...) \ + ((ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT(uuid128))) + +#define BLE_UUID16(u) \ + ((ble_uuid16_t *) (u)) + +#define BLE_UUID32(u) \ + ((ble_uuid32_t *) (u)) + +#define BLE_UUID128(u) \ + ((ble_uuid128_t *) (u)) + +/** Size of buffer needed to store UUID as a string. + * Includes trailing \0. + */ +#define BLE_UUID_STR_LEN (37) + +/** @brief Constructs a UUID object from a byte array. + * + * @param uuid On success, this gets populated with the constructed UUID. + * @param buf The source buffer to parse. + * @param len The size of the buffer, in bytes. + * + * @return 0 on success, BLE_HS_EINVAL if the source buffer does not contain + * a valid UUID. + */ +int ble_uuid_init_from_buf(ble_uuid_any_t *uuid, const void *buf, size_t len); + +/** @brief Compares two Bluetooth UUIDs. + * + * @param uuid1 The first UUID to compare. + * @param uuid2 The second UUID to compare. + * + * @return 0 if the two UUIDs are equal, nonzero if the UUIDs differ. + */ +int ble_uuid_cmp(const ble_uuid_t *uuid1, const ble_uuid_t *uuid2); + +/** @brief Copy Bluetooth UUID + * + * @param dst Destination UUID. + * @param src Source UUID. + */ +void ble_uuid_copy(ble_uuid_any_t *dst, const ble_uuid_t *src); + +/** @brief Converts the specified UUID to its string representation. + * + * Example string representations: + * o 16-bit: 0x1234 + * o 32-bit: 0x12345678 + * o 128-bit: 12345678-1234-1234-1234-123456789abc + * + * @param uuid The source UUID to convert. + * @param dst The destination buffer. + * + * @return A pointer to the supplied destination buffer. + */ +char *ble_uuid_to_str(const ble_uuid_t *uuid, char *dst); + +/** @brief Converts the specified 16-bit UUID to a uint16_t. + * + * @param uuid The source UUID to convert. + * + * @return The converted integer on success, NULL if the specified UUID is + * not 16 bits. + */ +uint16_t ble_uuid_u16(const ble_uuid_t *uuid); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* _BLE_HOST_UUID_H */ diff --git a/libesp32/NimBLE-Arduino/src/host/util/util.h b/libesp32/NimBLE-Arduino/src/host/util/util.h new file mode 100644 index 000000000..3f07c005a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/host/util/util.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_HOST_UTIL_ +#define H_HOST_UTIL_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Tries to configure the device with at least one Bluetooth address. + * Addresses are restored in a hardware-specific fashion. + * + * @param prefer_random Whether to attempt to restore a random address + * before checking if a public address has + * already been configured. + * + * @return 0 on success; + * BLE_HS_ENOADDR if the device does not have any + * available addresses. + * Other BLE host core code on error. + */ +int ble_hs_util_ensure_addr(int prefer_random); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/log/log.h b/libesp32/NimBLE-Arduino/src/log/log.h new file mode 100644 index 000000000..e5a05fc2c --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/log/log.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __LOG_H__ +#define __LOG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct log { +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __LOG_H__ */ diff --git a/libesp32/NimBLE-Arduino/src/mem/mem.h b/libesp32/NimBLE-Arduino/src/mem/mem.h new file mode 100644 index 000000000..13a325b5a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mem/mem.h @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_UTIL_MEM_ +#define H_UTIL_MEM_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mempool; +struct os_mbuf_pool; + +int mem_malloc_mempool(struct os_mempool *mempool, uint16_t num_blocks, + uint32_t block_size, char *name, void **out_buf); +int mem_malloc_mempool_ext(struct os_mempool_ext *mempool, uint16_t num_blocks, + uint32_t block_size, char *name, void **out_buf); + +int mem_malloc_mbuf_pool(struct os_mempool *mempool, + struct os_mbuf_pool *mbuf_pool, uint16_t num_blocks, + uint32_t block_size, char *name, + void **out_buf); +int mem_malloc_mbufpkt_pool(struct os_mempool *mempool, + struct os_mbuf_pool *mbuf_pool, int num_blocks, + int block_size, char *name, + void **out_buf); +int mem_init_mbuf_pool(void *mem, struct os_mempool *mempool, + struct os_mbuf_pool *mbuf_pool, int num_blocks, + int block_size, const char *name); + +/** + * Specifies a function used as a callback. Functions of this type allocate an + * mbuf chain meant to hold a packet fragment. The resulting mbuf must contain + * a pkthdr. + * + * @param frag_size The number of data bytes that the mbuf will + * eventually contain. + * @param arg A generic parameter. + * + * @return An allocated mbuf chain on success; + * NULL on failure. + */ +typedef struct os_mbuf *mem_frag_alloc_fn(uint16_t frag_size, void *arg); + +struct os_mbuf *mem_split_frag(struct os_mbuf **om, uint16_t max_frag_sz, + mem_frag_alloc_fn *alloc_cb, void *cb_arg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/mesh/access.h b/libesp32/NimBLE-Arduino/src/mesh/access.h new file mode 100644 index 000000000..71ca34e2d --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/access.h @@ -0,0 +1,423 @@ +/** @file + * @brief Bluetooth Mesh Access Layer APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_ACCESS_H +#define __BT_MESH_ACCESS_H + +/** + * @brief Bluetooth Mesh Access Layer + * @defgroup bt_mesh_access Bluetooth Mesh Access Layer + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define BT_MESH_ADDR_UNASSIGNED 0x0000 +#define BT_MESH_ADDR_ALL_NODES 0xffff +#define BT_MESH_ADDR_PROXIES 0xfffc +#define BT_MESH_ADDR_FRIENDS 0xfffd +#define BT_MESH_ADDR_RELAYS 0xfffe + +#define BT_MESH_KEY_UNUSED 0xffff +#define BT_MESH_KEY_DEV 0xfffe + +/** Helper to define a mesh element within an array. + * + * In case the element has no SIG or Vendor models the helper + * macro BT_MESH_MODEL_NONE can be given instead. + * + * @param _loc Location Descriptor. + * @param _mods Array of models. + * @param _vnd_mods Array of vendor models. + */ +#define BT_MESH_ELEM(_loc, _mods, _vnd_mods) \ +{ \ + .loc = (_loc), \ + .model_count = ARRAY_SIZE(_mods), \ + .models = (_mods), \ + .vnd_model_count = ARRAY_SIZE(_vnd_mods), \ + .vnd_models = (_vnd_mods), \ +} + +/** Abstraction that describes a Mesh Element */ +struct bt_mesh_elem { + /* Unicast Address. Set at runtime during provisioning. */ + u16_t addr; + + /* Location Descriptor (GATT Bluetooth Namespace Descriptors) */ + const u16_t loc; + + const u8_t model_count; + const u8_t vnd_model_count; + + struct bt_mesh_model * const models; + struct bt_mesh_model * const vnd_models; +}; + +/* Foundation Models */ +#define BT_MESH_MODEL_ID_CFG_SRV 0x0000 +#define BT_MESH_MODEL_ID_CFG_CLI 0x0001 +#define BT_MESH_MODEL_ID_HEALTH_SRV 0x0002 +#define BT_MESH_MODEL_ID_HEALTH_CLI 0x0003 + +/* Models from the Mesh Model Specification */ +#define BT_MESH_MODEL_ID_GEN_ONOFF_SRV 0x1000 +#define BT_MESH_MODEL_ID_GEN_ONOFF_CLI 0x1001 +#define BT_MESH_MODEL_ID_GEN_LEVEL_SRV 0x1002 +#define BT_MESH_MODEL_ID_GEN_LEVEL_CLI 0x1003 +#define BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV 0x1004 +#define BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI 0x1005 +#define BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV 0x1006 +#define BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV 0x1007 +#define BT_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI 0x1008 +#define BT_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV 0x1009 +#define BT_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV 0x100a +#define BT_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI 0x100b +#define BT_MESH_MODEL_ID_GEN_BATTERY_SRV 0x100c +#define BT_MESH_MODEL_ID_GEN_BATTERY_CLI 0x100d +#define BT_MESH_MODEL_ID_GEN_LOCATION_SRV 0x100e +#define BT_MESH_MODEL_ID_GEN_LOCATION_SETUPSRV 0x100f +#define BT_MESH_MODEL_ID_GEN_LOCATION_CLI 0x1010 +#define BT_MESH_MODEL_ID_GEN_ADMIN_PROP_SRV 0x1011 +#define BT_MESH_MODEL_ID_GEN_MANUFACTURER_PROP_SRV 0x1012 +#define BT_MESH_MODEL_ID_GEN_USER_PROP_SRV 0x1013 +#define BT_MESH_MODEL_ID_GEN_CLIENT_PROP_SRV 0x1014 +#define BT_MESH_MODEL_ID_GEN_PROP_CLI 0x1015 +#define BT_MESH_MODEL_ID_SENSOR_SRV 0x1100 +#define BT_MESH_MODEL_ID_SENSOR_SETUP_SRV 0x1101 +#define BT_MESH_MODEL_ID_SENSOR_CLI 0x1102 +#define BT_MESH_MODEL_ID_TIME_SRV 0x1200 +#define BT_MESH_MODEL_ID_TIME_SETUP_SRV 0x1201 +#define BT_MESH_MODEL_ID_TIME_CLI 0x1202 +#define BT_MESH_MODEL_ID_SCENE_SRV 0x1203 +#define BT_MESH_MODEL_ID_SCENE_SETUP_SRV 0x1204 +#define BT_MESH_MODEL_ID_SCENE_CLI 0x1205 +#define BT_MESH_MODEL_ID_SCHEDULER_SRV 0x1206 +#define BT_MESH_MODEL_ID_SCHEDULER_SETUP_SRV 0x1207 +#define BT_MESH_MODEL_ID_SCHEDULER_CLI 0x1208 +#define BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV 0x1300 +#define BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV 0x1301 +#define BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI 0x1302 +#define BT_MESH_MODEL_ID_LIGHT_CTL_SRV 0x1303 +#define BT_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV 0x1304 +#define BT_MESH_MODEL_ID_LIGHT_CTL_CLI 0x1305 +#define BT_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV 0x1306 +#define BT_MESH_MODEL_ID_LIGHT_HSL_SRV 0x1307 +#define BT_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV 0x1308 +#define BT_MESH_MODEL_ID_LIGHT_HSL_CLI 0x1309 +#define BT_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV 0x130a +#define BT_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV 0x130b +#define BT_MESH_MODEL_ID_LIGHT_XYL_SRV 0x130c +#define BT_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV 0x130d +#define BT_MESH_MODEL_ID_LIGHT_XYL_CLI 0x130e +#define BT_MESH_MODEL_ID_LIGHT_LC_SRV 0x130f +#define BT_MESH_MODEL_ID_LIGHT_LC_SETUPSRV 0x1310 +#define BT_MESH_MODEL_ID_LIGHT_LC_CLI 0x1311 + +/** Message sending context. */ +struct bt_mesh_msg_ctx { + /** NetKey Index of the subnet to send the message on. */ + u16_t net_idx; + + /** AppKey Index to encrypt the message with. */ + u16_t app_idx; + + /** Remote address. */ + u16_t addr; + + /** Destination address of a received message. Not used for sending. */ + u16_t recv_dst; + + /** Received TTL value. Not used for sending. */ + u8_t recv_ttl:7; + + /** Force sending reliably by using segment acknowledgement */ + u8_t send_rel:1; + + /** TTL, or BT_MESH_TTL_DEFAULT for default TTL. */ + u8_t send_ttl; +}; + +struct bt_mesh_model_op { + /* OpCode encoded using the BT_MESH_MODEL_OP_* macros */ + const u32_t opcode; + + /* Minimum required message length */ + const size_t min_len; + + /* Message handler for the opcode */ + void (*const func)(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf); +}; + +#define BT_MESH_MODEL_OP_1(b0) (b0) +#define BT_MESH_MODEL_OP_2(b0, b1) (((b0) << 8) | (b1)) +#define BT_MESH_MODEL_OP_3(b0, cid) ((((b0) << 16) | 0xc00000) | (cid)) + +#define BT_MESH_MODEL_OP_END { 0, 0, NULL } +#define BT_MESH_MODEL_NO_OPS ((struct bt_mesh_model_op []) \ + { BT_MESH_MODEL_OP_END }) + +/** Helper to define an empty model array */ +#define BT_MESH_MODEL_NONE ((struct bt_mesh_model []){}) + +#define BT_MESH_MODEL(_id, _op, _pub, _user_data) \ +{ \ + .id = (_id), \ + .op = _op, \ + .keys = { [0 ... (CONFIG_BT_MESH_MODEL_KEY_COUNT - 1)] = \ + BT_MESH_KEY_UNUSED }, \ + .pub = _pub, \ + .groups = { [0 ... (CONFIG_BT_MESH_MODEL_GROUP_COUNT - 1)] = \ + BT_MESH_ADDR_UNASSIGNED }, \ + .user_data = _user_data, \ +} + +#define BT_MESH_MODEL_VND(_company, _id, _op, _pub, _user_data) \ +{ \ + .vnd.company = (_company), \ + .vnd.id = (_id), \ + .op = _op, \ + .pub = _pub, \ + .keys = { [0 ... (CONFIG_BT_MESH_MODEL_KEY_COUNT - 1)] = \ + BT_MESH_KEY_UNUSED }, \ + .groups = { [0 ... (CONFIG_BT_MESH_MODEL_GROUP_COUNT - 1)] = \ + BT_MESH_ADDR_UNASSIGNED }, \ + .user_data = _user_data, \ +} + +/** @def BT_MESH_TRANSMIT + * + * @brief Encode transmission count & interval steps. + * + * @param count Number of retransmissions (first transmission is excluded). + * @param int_ms Interval steps in milliseconds. Must be greater than 0, + * less than or equal to 320, and a multiple of 10. + * + * @return Mesh transmit value that can be used e.g. for the default + * values of the configuration model data. + */ +#define BT_MESH_TRANSMIT(count, int_ms) ((count) | (((int_ms / 10) - 1) << 3)) + +/** @def BT_MESH_TRANSMIT_COUNT + * + * @brief Decode transmit count from a transmit value. + * + * @param transmit Encoded transmit count & interval value. + * + * @return Transmission count (actual transmissions is N + 1). + */ +#define BT_MESH_TRANSMIT_COUNT(transmit) (((transmit) & (u8_t)BIT_MASK(3))) + +/** @def BT_MESH_TRANSMIT_INT + * + * @brief Decode transmit interval from a transmit value. + * + * @param transmit Encoded transmit count & interval value. + * + * @return Transmission interval in milliseconds. + */ +#define BT_MESH_TRANSMIT_INT(transmit) ((((transmit) >> 3) + 1) * 10) + +/** @def BT_MESH_PUB_TRANSMIT + * + * @brief Encode Publish Retransmit count & interval steps. + * + * @param count Number of retransmissions (first transmission is excluded). + * @param int_ms Interval steps in milliseconds. Must be greater than 0 + * and a multiple of 50. + * + * @return Mesh transmit value that can be used e.g. for the default + * values of the configuration model data. + */ +#define BT_MESH_PUB_TRANSMIT(count, int_ms) BT_MESH_TRANSMIT(count, \ + (int_ms) / 5) + +/** @def BT_MESH_PUB_TRANSMIT_COUNT + * + * @brief Decode Pubhlish Retransmit count from a given value. + * + * @param transmit Encoded Publish Retransmit count & interval value. + * + * @return Retransmission count (actual transmissions is N + 1). + */ +#define BT_MESH_PUB_TRANSMIT_COUNT(transmit) BT_MESH_TRANSMIT_COUNT(transmit) + +/** @def BT_MESH_PUB_TRANSMIT_INT + * + * @brief Decode Publish Retransmit interval from a given value. + * + * @param transmit Encoded Publish Retransmit count & interval value. + * + * @return Transmission interval in milliseconds. + */ +#define BT_MESH_PUB_TRANSMIT_INT(transmit) ((((transmit) >> 3) + 1) * 50) + +/** Model publication context. */ +struct bt_mesh_model_pub { + /** The model the context belongs to. Initialized by the stack. */ + struct bt_mesh_model *mod; + + u16_t addr; /**< Publish Address. */ + u16_t key; /**< Publish AppKey Index. */ + + u8_t ttl; /**< Publish Time to Live. */ + u8_t retransmit; /**< Retransmit Count & Interval Steps. */ + u8_t period; /**< Publish Period. */ + u8_t period_div:4, /**< Divisor for the Period. */ + cred:1, /**< Friendship Credentials Flag. */ + count:3; /**< Retransmissions left. */ + + u32_t period_start; /**< Start of the current period. */ + + /** @brief Publication buffer, containing the publication message. + * + * The application is expected to initialize this with + * a valid net_buf_simple pointer, with the help of e.g. + * the NET_BUF_SIMPLE() macro. The publication buffer must + * contain a valid publication message before calling the + * bt_mesh_model_publish() API or after the publication's + * @ref bt_mesh_model_pub.update callback has been called + * and returned success. The buffer must be created outside + * of function context, i.e. it must not be on the stack. + * This is most conveniently acheived by creating it inline + * when declaring the publication context: + * + * static struct bt_mesh_model_pub my_pub = { + * .msg = NET_BUF_SIMPLE(size), + * }; + */ + struct os_mbuf *msg; + + /** @brief Callback for updating the publication buffer. + * + * When set to NULL, the model is assumed not to support + * periodic publishing. When set to non-NULL the callback + * will be called periodically and is expected to update + * @ref bt_mesh_model_pub.msg with a valid publication + * message. + * + * @param mod The Model the Publication Context belogs to. + * + * @return Zero on success or (negative) error code otherwise. + */ + int (*update)(struct bt_mesh_model *mod); + + /** Publish Period Timer. Only for stack-internal use. */ + struct k_delayed_work timer; +}; + +/** Abstraction that describes a Mesh Model instance */ +struct bt_mesh_model { + union { + const u16_t id; + struct { + u16_t company; + u16_t id; + } vnd; + }; + + /* Internal information, mainly for persistent storage */ + u8_t elem_idx; /* Belongs to Nth element */ + u8_t mod_idx; /* Is the Nth model in the element */ + u16_t flags; /* Information about what has changed */ + + /* Model Publication */ + struct bt_mesh_model_pub * const pub; + + /* AppKey List */ + u16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT]; + + /* Subscription List (group or virtual addresses) */ + u16_t groups[CONFIG_BT_MESH_MODEL_GROUP_COUNT]; + + const struct bt_mesh_model_op * const op; + + /* Model-specific user data */ + void *user_data; +}; + +struct bt_mesh_send_cb { + void (*start)(u16_t duration, int err, void *cb_data); + void (*end)(int err, void *cb_data); +}; + +void bt_mesh_model_msg_init(struct os_mbuf *msg, u32_t opcode); + +/** Special TTL value to request using configured default TTL */ +#define BT_MESH_TTL_DEFAULT 0xff + +/** Maximum allowed TTL value */ +#define BT_MESH_TTL_MAX 0x7f + +/** + * @brief Send an Access Layer message. + * + * @param model Mesh (client) Model that the message belongs to. + * @param ctx Message context, includes keys, TTL, etc. + * @param msg Access Layer payload (the actual message to be sent). + * @param cb Optional "message sent" callback. + * @param cb_data User data to be passed to the callback. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_model_send(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *msg, + const struct bt_mesh_send_cb *cb, + void *cb_data); + +/** + * @brief Send a model publication message. + * + * Before calling this function, the user needs to ensure that the model + * publication message (@ref bt_mesh_model_pub.msg) contains a valid + * message to be sent. Note that this API is only to be used for + * non-period publishing. For periodic publishing the app only needs + * to make sure that @ref bt_mesh_model_pub.msg contains a valid message + * whenever the @ref bt_mesh_model_pub.update callback is called. + * + * @param model Mesh (client) Model that's publishing the message. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_model_publish(struct bt_mesh_model *model); + +/** + * @brief Get the element that a model belongs to. + * + * @param mod Mesh model. + * + * @return Pointer to the element that the given model belongs to. + */ +struct bt_mesh_elem *bt_mesh_model_elem(struct bt_mesh_model *mod); + +/** Node Composition */ +struct bt_mesh_comp { + u16_t cid; + u16_t pid; + u16_t vid; + + size_t elem_count; + struct bt_mesh_elem *elem; +}; + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_ACCESS_H */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/cfg_cli.h b/libesp32/NimBLE-Arduino/src/mesh/cfg_cli.h new file mode 100644 index 000000000..9d80ccda7 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/cfg_cli.h @@ -0,0 +1,233 @@ +/** @file + * @brief Bluetooth Mesh Configuration Client Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_CFG_CLI_H +#define __BT_MESH_CFG_CLI_H + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh_cfg_cli Bluetooth Mesh Configuration Client Model + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Mesh Configuration Client Model Context */ +struct bt_mesh_cfg_cli { + struct bt_mesh_model *model; + + struct k_sem op_sync; + u32_t op_pending; + void *op_param; +}; + +extern const struct bt_mesh_model_op bt_mesh_cfg_cli_op[]; + +#define BT_MESH_MODEL_CFG_CLI(cli_data) \ + BT_MESH_MODEL(BT_MESH_MODEL_ID_CFG_CLI, \ + bt_mesh_cfg_cli_op, NULL, cli_data) + +int bt_mesh_cfg_comp_data_get(u16_t net_idx, u16_t addr, u8_t page, + u8_t *status, struct os_mbuf *comp); + +int bt_mesh_cfg_beacon_get(u16_t net_idx, u16_t addr, u8_t *status); + +int bt_mesh_cfg_beacon_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status); + +int bt_mesh_cfg_ttl_get(u16_t net_idx, u16_t addr, u8_t *ttl); + +int bt_mesh_cfg_ttl_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *ttl); + +int bt_mesh_cfg_friend_get(u16_t net_idx, u16_t addr, u8_t *status); + +int bt_mesh_cfg_friend_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status); + +int bt_mesh_cfg_gatt_proxy_get(u16_t net_idx, u16_t addr, u8_t *status); + +int bt_mesh_cfg_gatt_proxy_set(u16_t net_idx, u16_t addr, u8_t val, + u8_t *status); + +int bt_mesh_cfg_relay_get(u16_t net_idx, u16_t addr, u8_t *status, + u8_t *transmit); + +int bt_mesh_cfg_relay_set(u16_t net_idx, u16_t addr, u8_t new_relay, + u8_t new_transmit, u8_t *status, u8_t *transmit); + +int bt_mesh_cfg_net_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx, + const u8_t net_key[16], u8_t *status); + +int bt_mesh_cfg_app_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx, + u16_t key_app_idx, const u8_t app_key[16], + u8_t *status); + +int bt_mesh_cfg_mod_app_bind(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u8_t *status); + +int bt_mesh_cfg_mod_app_bind_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u16_t cid, + u8_t *status); + +/** @def BT_MESH_PUB_PERIOD_100MS + * + * @brief Helper macro to encode model publication period in units of 100ms + * + * @param steps Number of 100ms steps. + * + * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period + */ +#define BT_MESH_PUB_PERIOD_100MS(steps) ((steps) & BIT_MASK(6)) + +/** @def BT_MESH_PUB_PERIOD_SEC + * + * @brief Helper macro to encode model publication period in units of 1 second + * + * @param steps Number of 1 second steps. + * + * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period + */ +#define BT_MESH_PUB_PERIOD_SEC(steps) (((steps) & BIT_MASK(6)) | (1 << 6)) + +/** @def BT_MESH_PUB_PERIOD_10SEC + * + * @brief Helper macro to encode model publication period in units of 10 + * seconds + * + * @param steps Number of 10 second steps. + * + * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period + */ +#define BT_MESH_PUB_PERIOD_10SEC(steps) (((steps) & BIT_MASK(6)) | (2 << 6)) + +/** @def BT_MESH_PUB_PERIOD_10MIN + * + * @brief Helper macro to encode model publication period in units of 10 + * minutes + * + * @param steps Number of 10 minute steps. + * + * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period + */ +#define BT_MESH_PUB_PERIOD_10MIN(steps) (((steps) & BIT_MASK(6)) | (3 << 6)) + +struct bt_mesh_cfg_mod_pub { + u16_t addr; + u16_t app_idx; + bool cred_flag; + u8_t ttl; + u8_t period; + u8_t transmit; +}; + +int bt_mesh_cfg_mod_pub_get(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub, + u8_t *status); + +int bt_mesh_cfg_mod_pub_get_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status); + +int bt_mesh_cfg_mod_pub_set(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub, + u8_t *status); + +int bt_mesh_cfg_mod_pub_set_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status); + +int bt_mesh_cfg_mod_sub_add(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status); + +int bt_mesh_cfg_mod_sub_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid, + u8_t *status); + +int bt_mesh_cfg_mod_sub_del(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status); + +int bt_mesh_cfg_mod_sub_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid, + u8_t *status); + +int bt_mesh_cfg_mod_sub_overwrite(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status); + +int bt_mesh_cfg_mod_sub_overwrite_vnd(u16_t net_idx, u16_t addr, + u16_t elem_addr, u16_t sub_addr, + u16_t mod_id, u16_t cid, u8_t *status); + +int bt_mesh_cfg_mod_sub_va_add(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t *virt_addr, u8_t *status); + +int bt_mesh_cfg_mod_sub_va_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t cid, u16_t *virt_addr, u8_t *status); + +int bt_mesh_cfg_mod_sub_va_del(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t *virt_addr, u8_t *status); + +int bt_mesh_cfg_mod_sub_va_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t cid, u16_t *virt_addr, u8_t *status); + +int bt_mesh_cfg_mod_sub_va_overwrite(u16_t net_idx, u16_t addr, + u16_t elem_addr, const u8_t label[16], + u16_t mod_id, u16_t *virt_addr, + u8_t *status); + +int bt_mesh_cfg_mod_sub_va_overwrite_vnd(u16_t net_idx, u16_t addr, + u16_t elem_addr, const u8_t label[16], + u16_t mod_id, u16_t cid, + u16_t *virt_addr, u8_t *status); + +struct bt_mesh_cfg_hb_sub { + u16_t src; + u16_t dst; + u8_t period; + u8_t count; + u8_t min; + u8_t max; +}; + +int bt_mesh_cfg_hb_sub_set(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_sub *sub, u8_t *status); + +int bt_mesh_cfg_hb_sub_get(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_sub *sub, u8_t *status); + +struct bt_mesh_cfg_hb_pub { + u16_t dst; + u8_t count; + u8_t period; + u8_t ttl; + u16_t feat; + u16_t net_idx; +}; + +int bt_mesh_cfg_hb_pub_set(u16_t net_idx, u16_t addr, + const struct bt_mesh_cfg_hb_pub *pub, u8_t *status); + +int bt_mesh_cfg_hb_pub_get(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_pub *pub, u8_t *status); + +s32_t bt_mesh_cfg_cli_timeout_get(void); +void bt_mesh_cfg_cli_timeout_set(s32_t timeout); + +#ifdef __cplusplus +} +#endif +/** + * @} + */ + +#endif /* __BT_MESH_CFG_CLI_H */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/cfg_srv.h b/libesp32/NimBLE-Arduino/src/mesh/cfg_srv.h new file mode 100644 index 000000000..cb5d25e69 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/cfg_srv.h @@ -0,0 +1,77 @@ +/** @file + * @brief Bluetooth Mesh Configuration Server Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_CFG_SRV_H +#define __BT_MESH_CFG_SRV_H + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh_cfg_srv Bluetooth Mesh Configuration Server Model + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Mesh Configuration Server Model Context */ +struct bt_mesh_cfg_srv { + struct bt_mesh_model *model; + + u8_t net_transmit; /* Network Transmit state */ + u8_t relay; /* Relay Mode state */ + u8_t relay_retransmit; /* Relay Retransmit state */ + u8_t beacon; /* Secure Network Beacon state */ + u8_t gatt_proxy; /* GATT Proxy state */ + u8_t frnd; /* Friend state */ + u8_t default_ttl; /* Default TTL */ + + /* Heartbeat Publication */ + struct bt_mesh_hb_pub { + struct k_delayed_work timer; + + u16_t dst; + u16_t count; + u8_t period; + u8_t ttl; + u16_t feat; + u16_t net_idx; + } hb_pub; + + /* Heartbeat Subscription */ + struct bt_mesh_hb_sub { + s64_t expiry; + + u16_t src; + u16_t dst; + u16_t count; + u8_t min_hops; + u8_t max_hops; + + /* Optional subscription tracking function */ + void (*func)(u8_t hops, u16_t feat); + } hb_sub; +}; + +extern const struct bt_mesh_model_op bt_mesh_cfg_srv_op[]; + +#define BT_MESH_MODEL_CFG_SRV(srv_data) \ + BT_MESH_MODEL(BT_MESH_MODEL_ID_CFG_SRV, \ + bt_mesh_cfg_srv_op, NULL, srv_data) + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_CFG_SRV_H */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/glue.h b/libesp32/NimBLE-Arduino/src/mesh/glue.h new file mode 100644 index 000000000..3b1636dcc --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/glue.h @@ -0,0 +1,484 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _MESH_GLUE_ +#define _MESH_GLUE_ + +#include +#include + +#include "syscfg/syscfg.h" +#include "nimble/nimble_npl.h" + +#include "os/os_mbuf.h" +#include "os/queue.h" + +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "../src/ble_sm_priv.h" +#include "../src/ble_hs_hci_priv.h" + +#if MYNEWT_VAL(BLE_CRYPTO_STACK_MBEDTLS) +#include "mbedtls/aes.h" +#include "mbedtls/cipher.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/cmac.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/ecp.h" + +#else +#include "tinycrypt/aes.h" +#include "tinycrypt/constants.h" +#include "tinycrypt/utils.h" +#include "tinycrypt/cmac_mode.h" +#include "tinycrypt/ecc_dh.h" +#endif + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) +#include "config/config.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define u8_t uint8_t +#define s8_t int8_t +#define u16_t uint16_t +#define s16_t int16_t +#define u32_t uint32_t +#define u64_t uint64_t +#define s64_t int64_t +#define s32_t int32_t + +/** @brief Helper to declare elements of bt_data arrays + * + * This macro is mainly for creating an array of struct bt_data + * elements which is then passed to bt_le_adv_start(). + * + * @param _type Type of advertising data field + * @param _data Pointer to the data field payload + * @param _data_len Number of bytes behind the _data pointer + */ +#define BT_DATA(_type, _data, _data_len) \ + { \ + .type = (_type), \ + .data_len = (_data_len), \ + .data = (const u8_t *)(_data), \ + } + +/** @brief Helper to declare elements of bt_data arrays + * + * This macro is mainly for creating an array of struct bt_data + * elements which is then passed to bt_le_adv_start(). + * + * @param _type Type of advertising data field + * @param _bytes Variable number of single-byte parameters + */ +#define BT_DATA_BYTES(_type, _bytes...) \ + BT_DATA(_type, ((u8_t []) { _bytes }), \ + sizeof((u8_t []) { _bytes })) + +/* EIR/AD data type definitions */ +#define BT_DATA_FLAGS 0x01 /* AD flags */ +#define BT_DATA_UUID16_SOME 0x02 /* 16-bit UUID, more available */ +#define BT_DATA_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ +#define BT_DATA_UUID32_SOME 0x04 /* 32-bit UUID, more available */ +#define BT_DATA_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ +#define BT_DATA_UUID128_SOME 0x06 /* 128-bit UUID, more available */ +#define BT_DATA_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ +#define BT_DATA_NAME_SHORTENED 0x08 /* Shortened name */ +#define BT_DATA_NAME_COMPLETE 0x09 /* Complete name */ +#define BT_DATA_TX_POWER 0x0a /* Tx Power */ +#define BT_DATA_SOLICIT16 0x14 /* Solicit UUIDs, 16-bit */ +#define BT_DATA_SOLICIT128 0x15 /* Solicit UUIDs, 128-bit */ +#define BT_DATA_SVC_DATA16 0x16 /* Service data, 16-bit UUID */ +#define BT_DATA_GAP_APPEARANCE 0x19 /* GAP appearance */ +#define BT_DATA_SOLICIT32 0x1f /* Solicit UUIDs, 32-bit */ +#define BT_DATA_SVC_DATA32 0x20 /* Service data, 32-bit UUID */ +#define BT_DATA_SVC_DATA128 0x21 /* Service data, 128-bit UUID */ +#define BT_DATA_URI 0x24 /* URI */ +#define BT_DATA_MESH_PROV 0x29 /* Mesh Provisioning PDU */ +#define BT_DATA_MESH_MESSAGE 0x2a /* Mesh Networking PDU */ +#define BT_DATA_MESH_BEACON 0x2b /* Mesh Beacon */ + +#define BT_DATA_MANUFACTURER_DATA 0xff /* Manufacturer Specific Data */ + +#define BT_LE_AD_LIMITED 0x01 /* Limited Discoverable */ +#define BT_LE_AD_GENERAL 0x02 /* General Discoverable */ +#define BT_LE_AD_NO_BREDR 0x04 /* BR/EDR not supported */ + +#define sys_put_be16(a,b) put_be16(b, a) +#define sys_put_le16(a,b) put_le16(b, a) +#define sys_put_be32(a,b) put_be32(b, a) +#define sys_get_be16(a) get_be16(a) +#define sys_get_le16(a) get_le16(a) +#define sys_get_be32(a) get_be32(a) +#define sys_cpu_to_be16(a) htobe16(a) +#define sys_cpu_to_be32(a) htobe32(a) +#define sys_be32_to_cpu(a) be32toh(a) +#define sys_be16_to_cpu(a) be16toh(a) +#define sys_le16_to_cpu(a) le16toh(a) + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#define CODE_UNREACHABLE __builtin_unreachable() +#define __ASSERT(code, str) \ + do { \ + if (!(code)) BT_ERR(str); \ + assert(code); \ + } while (0); + +#define __ASSERT_NO_MSG(test) __ASSERT(test, "") + +/* Mesh is designed to not use mbuf chains */ +#if BT_DBG_ENABLED +#define ASSERT_NOT_CHAIN(om) assert(SLIST_NEXT(om, om_next) == NULL) +#else +#define ASSERT_NOT_CHAIN(om) (void)(om) +#endif + +#define __packed __attribute__((__packed__)) + +#define MSEC_PER_SEC (1000) +#define K_MSEC(ms) (ms) +#define K_SECONDS(s) K_MSEC((s) * MSEC_PER_SEC) +#define K_MINUTES(m) K_SECONDS((m) * 60) +#define K_HOURS(h) K_MINUTES((h) * 60) + +#ifndef BIT +#define BIT(n) (1UL << (n)) +#endif + +#define BIT_MASK(n) (BIT(n) - 1) + +#define BT_GAP_ADV_FAST_INT_MIN_1 0x0030 /* 30 ms */ +#define BT_GAP_ADV_FAST_INT_MAX_1 0x0060 /* 60 ms */ +#define BT_GAP_ADV_FAST_INT_MIN_2 0x00a0 /* 100 ms */ +#define BT_GAP_ADV_FAST_INT_MAX_2 0x00f0 /* 150 ms */ +#define BT_GAP_ADV_SLOW_INT_MIN 0x0640 /* 1 s */ +#define BT_GAP_ADV_SLOW_INT_MAX 0x0780 /* 1.2 s */ + +#define BT_DBG(fmt, ...) \ + if (BT_DBG_ENABLED) { \ + BLE_HS_LOG(DEBUG, "%s: " fmt "\n", __func__, ## __VA_ARGS__); \ + } +#define BT_INFO(fmt, ...) BLE_HS_LOG(INFO, "%s: " fmt "\n", __func__, ## __VA_ARGS__); +#define BT_WARN(fmt, ...) BLE_HS_LOG(WARN, "%s: " fmt "\n", __func__, ## __VA_ARGS__); +#define BT_ERR(fmt, ...) BLE_HS_LOG(ERROR, "%s: " fmt "\n", __func__, ## __VA_ARGS__); +#define BT_GATT_ERR(_att_err) (-(_att_err)) + +typedef ble_addr_t bt_addr_le_t; + +#define k_fifo_init(queue) ble_npl_eventq_init(queue) +#define net_buf_simple_tailroom(buf) OS_MBUF_TRAILINGSPACE(buf) +#define net_buf_tailroom(buf) net_buf_simple_tailroom(buf) +#define net_buf_headroom(buf) ((buf)->om_data - &(buf)->om_databuf[buf->om_pkthdr_len]) +#define net_buf_simple_headroom(buf) net_buf_headroom(buf) +#define net_buf_simple_tail(buf) ((buf)->om_data + (buf)->om_len) + +struct net_buf_simple_state { + /** Offset of the data pointer from the beginning of the storage */ + u16_t offset; + /** Length of data */ + u16_t len; +}; + +static inline struct os_mbuf * NET_BUF_SIMPLE(uint16_t size) +{ + struct os_mbuf *buf; + + buf = os_msys_get(size, 0); + assert(buf); + + return buf; +} + +#define K_NO_WAIT (0) +#define K_FOREVER (-1) + +/* This is by purpose */ +static inline void net_buf_simple_init(struct os_mbuf *buf, + size_t reserve_head) +{ + /* This is called in Zephyr after init. + * Note in Mynewt case we don't care abour reserved head*/ + buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + reserve_head; + buf->om_len = 0; +} + +void net_buf_put(struct ble_npl_eventq *fifo, struct os_mbuf *buf); +void * net_buf_ref(struct os_mbuf *om); +void net_buf_unref(struct os_mbuf *om); +uint16_t net_buf_simple_pull_le16(struct os_mbuf *om); +uint16_t net_buf_simple_pull_be16(struct os_mbuf *om); +uint32_t net_buf_simple_pull_be32(struct os_mbuf *om); +uint32_t net_buf_simple_pull_le32(struct os_mbuf *om); +uint8_t net_buf_simple_pull_u8(struct os_mbuf *om); +void net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val); +void net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val); +void net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val); +void net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val); +void net_buf_simple_add_le32(struct os_mbuf *om, uint32_t val); +void net_buf_add_zeros(struct os_mbuf *om, uint8_t len); +void net_buf_simple_push_le16(struct os_mbuf *om, uint16_t val); +void net_buf_simple_push_be16(struct os_mbuf *om, uint16_t val); +void net_buf_simple_push_u8(struct os_mbuf *om, uint8_t val); +void *net_buf_simple_pull(struct os_mbuf *om, uint8_t len); +void *net_buf_simple_pull_mem(struct os_mbuf *om, uint8_t len); +void *net_buf_simple_add(struct os_mbuf *om, uint8_t len); +bool k_fifo_is_empty(struct ble_npl_eventq *q); +void *net_buf_get(struct ble_npl_eventq *fifo,s32_t t); +uint8_t *net_buf_simple_push(struct os_mbuf *om, uint8_t len); +void net_buf_reserve(struct os_mbuf *om, size_t reserve); + +#define net_buf_add_mem(a,b,c) os_mbuf_append(a,b,c) +#define net_buf_simple_add_mem(a,b,c) os_mbuf_append(a,b,c) +#define net_buf_add_u8(a,b) net_buf_simple_add_u8(a,b) +#define net_buf_add(a,b) net_buf_simple_add(a,b) + +#define net_buf_clone(a, b) os_mbuf_dup(a) +#define net_buf_add_be32(a, b) net_buf_simple_add_be32(a, b) +#define net_buf_add_be16(a, b) net_buf_simple_add_be16(a, b) + +#define BT_GATT_CCC_NOTIFY BLE_GATT_CHR_PROP_NOTIFY + +/** Description of different data types that can be encoded into + * advertising data. Used to form arrays that are passed to the + * bt_le_adv_start() function. + */ +struct bt_data { + u8_t type; + u8_t data_len; + const u8_t *data; +}; + +struct bt_pub_key_cb { + /** @brief Callback type for Public Key generation. + * + * Used to notify of the local public key or that the local key is not + * available (either because of a failure to read it or because it is + * being regenerated). + * + * @param key The local public key, or NULL in case of no key. + */ + void (*func)(const u8_t key[64]); + + struct bt_pub_key_cb *_next; +}; + +typedef void (*bt_dh_key_cb_t)(const u8_t key[32]); +int bt_dh_key_gen(const u8_t remote_pk[64], bt_dh_key_cb_t cb); +int bt_pub_key_gen(struct bt_pub_key_cb *new_cb); +uint8_t *bt_pub_key_get(void); +int bt_rand(void *buf, size_t len); +const char * bt_hex(const void *buf, size_t len); +int bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data); +void bt_mesh_register_gatt(void); +int bt_le_adv_start(const struct ble_gap_adv_params *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len); +int bt_le_adv_stop(bool proxy); + +struct k_delayed_work { + struct ble_npl_callout work; +}; + +void k_work_init(struct ble_npl_callout *work, ble_npl_event_fn handler); +void k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f); +void k_delayed_work_cancel(struct k_delayed_work *w); +void k_delayed_work_submit(struct k_delayed_work *w, uint32_t ms); +int64_t k_uptime_get(void); +u32_t k_uptime_get_32(void); +void k_sleep(int32_t duration); +void k_work_submit(struct ble_npl_callout *w); +void k_work_add_arg(struct ble_npl_callout *w, void *arg); +void k_delayed_work_add_arg(struct k_delayed_work *w, void *arg); +uint32_t k_delayed_work_remaining_get(struct k_delayed_work *w); + +static inline void net_buf_simple_save(struct os_mbuf *buf, + struct net_buf_simple_state *state) +{ + state->offset = net_buf_simple_headroom(buf); + state->len = buf->om_len; +} + +static inline void net_buf_simple_restore(struct os_mbuf *buf, + struct net_buf_simple_state *state) +{ + buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + state->offset; + buf->om_len = state->len; +} + +static inline void sys_memcpy_swap(void *destination, const void *source, size_t length) +{ + u8_t *dst = destination; + const u8_t *src = source; + + __ASSERT(((src < dst && (src + length) <= dst) || + (src > dst && (dst + length) <= src)), + "Source and destination buffers must not overlap"); + + src += length - 1; + + for (; length > 0; length--) { + *dst++ = *src--; + } +} + +#define popcount(x) __builtin_popcount(x) + +static inline unsigned int find_lsb_set(u32_t op) +{ + return __builtin_ffs(op); +} + +static inline unsigned int find_msb_set(u32_t op) +{ + if (!op) + return 0; + + return 32 - __builtin_clz(op); +} + +#define CONFIG_BT_MESH_FRIEND BLE_MESH_FRIEND +#define CONFIG_BT_MESH_GATT_PROXY BLE_MESH_GATT_PROXY +#define CONFIG_BT_MESH_IV_UPDATE_TEST BLE_MESH_IV_UPDATE_TEST +#define CONFIG_BT_MESH_LOW_POWER BLE_MESH_LOW_POWER +#define CONFIG_BT_MESH_LPN_AUTO BLE_MESH_LPN_AUTO +#define CONFIG_BT_MESH_LPN_ESTABLISHMENT BLE_MESH_LPN_ESTABLISHMENT +#define CONFIG_BT_MESH_PB_ADV BLE_MESH_PB_ADV +#define CONFIG_BT_MESH_PB_GATT BLE_MESH_PB_GATT +#define CONFIG_BT_MESH_PROV BLE_MESH_PROV +#define CONFIG_BT_TESTING BLE_MESH_TESTING +#define CONFIG_BT_SETTINGS BLE_MESH_SETTINGS +#define CONFIG_SETTINGS BLE_MESH_SETTINGS + +/* Above flags are used with IS_ENABLED macro */ +#define IS_ENABLED(config) MYNEWT_VAL(config) + +#define CONFIG_BT_MESH_LPN_GROUPS MYNEWT_VAL(BLE_MESH_LPN_GROUPS) +#define CONFIG_BT_MESH_ADV_BUF_COUNT MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT) +#define CONFIG_BT_MESH_FRIEND_QUEUE_SIZE MYNEWT_VAL(BLE_MESH_FRIEND_QUEUE_SIZE) +#define CONFIG_BT_MESH_FRIEND_RECV_WIN MYNEWT_VAL(BLE_MESH_FRIEND_RECV_WIN) +#define CONFIG_BT_MESH_LPN_POLL_TIMEOUT MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT) +#define CONFIG_BT_MESH_MODEL_GROUP_COUNT MYNEWT_VAL(BLE_MESH_MODEL_GROUP_COUNT) +#define CONFIG_BT_MESH_MODEL_KEY_COUNT MYNEWT_VAL(BLE_MESH_MODEL_KEY_COUNT) +#define CONFIG_BT_MESH_NODE_ID_TIMEOUT MYNEWT_VAL(BLE_MESH_NODE_ID_TIMEOUT) +#define CONFIG_BT_MAX_CONN MYNEWT_VAL(BLE_MAX_CONNECTIONS) +#define CONFIG_BT_MESH_SEQ_STORE_RATE MYNEWT_VAL(BLE_MESH_SEQ_STORE_RATE) +#define CONFIG_BT_MESH_RPL_STORE_TIMEOUT MYNEWT_VAL(BLE_MESH_RPL_STORE_TIMEOUT) +#define CONFIG_BT_MESH_APP_KEY_COUNT MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT) +#define CONFIG_BT_MESH_SUBNET_COUNT MYNEWT_VAL(BLE_MESH_SUBNET_COUNT) +#define CONFIG_BT_MESH_STORE_TIMEOUT MYNEWT_VAL(BLE_MESH_STORE_TIMEOUT) +#define CONFIG_BT_MESH_IVU_DIVIDER MYNEWT_VAL(BLE_MESH_IVU_DIVIDER) +#define CONFIG_BT_DEVICE_NAME MYNEWT_VAL(BLE_MESH_DEVICE_NAME) +#define CONFIG_BT_MESH_TX_SEG_MAX MYNEWT_VAL(BLE_MESH_TX_SEG_MAX) + +#define printk console_printf + +#define CONTAINER_OF(ptr, type, field) \ + ((type *)(((char *)(ptr)) - offsetof(type, field))) + + +#define k_sem ble_npl_sem + +static inline void k_sem_init(struct k_sem *sem, unsigned int initial_count, + unsigned int limit) +{ + ble_npl_sem_init(sem, initial_count); +} + +static inline int k_sem_take(struct k_sem *sem, s32_t timeout) +{ + uint32_t ticks; + + ble_npl_time_ms_to_ticks(timeout, &ticks); + return - ble_npl_sem_pend(sem, ticks); +} + +static inline void k_sem_give(struct k_sem *sem) +{ + ble_npl_sem_release(sem); +} + +/* Helpers to access the storage array, since we don't have access to its + * type at this point anymore. + */ + +#define BUF_SIZE(pool) (pool->omp_pool->mp_block_size) + +static inline int net_buf_id(struct os_mbuf *buf) +{ + struct os_mbuf_pool *pool = buf->om_omp; + u8_t *pool_start = (u8_t *)pool->omp_pool->mp_membuf_addr; + u8_t *buf_ptr = (u8_t *)buf; + + return (buf_ptr - pool_start) / BUF_SIZE(pool); +} + +/* XXX: We should not use os_mbuf_pkthdr chains to represent a list of + * packets, this is a hack. For now this is not an issue, because mesh + * does not use os_mbuf chains. We should change this in the future. + */ +STAILQ_HEAD(net_buf_slist_t, os_mbuf_pkthdr); + +void net_buf_slist_init(struct net_buf_slist_t *list); +bool net_buf_slist_is_empty(struct net_buf_slist_t *list); +struct os_mbuf *net_buf_slist_peek_head(struct net_buf_slist_t *list); +struct os_mbuf *net_buf_slist_peek_next(struct os_mbuf *buf); +struct os_mbuf *net_buf_slist_get(struct net_buf_slist_t *list); +void net_buf_slist_put(struct net_buf_slist_t *list, struct os_mbuf *buf); +void net_buf_slist_remove(struct net_buf_slist_t *list, struct os_mbuf *prev, + struct os_mbuf *cur); +void net_buf_slist_merge_slist(struct net_buf_slist_t *list, + struct net_buf_slist_t *list_to_append); +#define NET_BUF_SLIST_FOR_EACH_NODE(head, var) STAILQ_FOREACH(var, head, omp_next) + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) + +#define settings_load conf_load +int settings_bytes_from_str(char *val_str, void *vp, int *len); +char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len); + +#define snprintk snprintf +#define BT_SETTINGS_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1) +#define settings_save_one conf_save_one + +#else + +static inline int +settings_load(void) +{ + return 0; +} + +#endif /* MYNEWT_VAL(MYNEWT_VAL_BLE_MESH_SETTINGS) */ + +#define BUILD_ASSERT(cond) _Static_assert(cond, "") + +#ifdef __cplusplus +} +#endif + +#endif /* _MESH_GLUE_ */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/health_cli.h b/libesp32/NimBLE-Arduino/src/mesh/health_cli.h new file mode 100644 index 000000000..719d621e0 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/health_cli.h @@ -0,0 +1,80 @@ +/** @file + * @brief Bluetooth Mesh Health Client Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_HEALTH_CLI_H +#define __BT_MESH_HEALTH_CLI_H + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh_health_cli Bluetooth Mesh Health Client Model + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Mesh Health Client Model Context */ +struct bt_mesh_health_cli { + struct bt_mesh_model *model; + + void (*current_status)(struct bt_mesh_health_cli *cli, u16_t addr, + u8_t test_id, u16_t cid, u8_t *faults, + size_t fault_count); + + struct k_sem op_sync; + u32_t op_pending; + void *op_param; +}; + +extern const struct bt_mesh_model_op bt_mesh_health_cli_op[]; + +#define BT_MESH_MODEL_HEALTH_CLI(cli_data) \ + BT_MESH_MODEL(BT_MESH_MODEL_ID_HEALTH_CLI, \ + bt_mesh_health_cli_op, NULL, cli_data) + +int bt_mesh_health_cli_set(struct bt_mesh_model *model); + +int bt_mesh_health_fault_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t *test_id, u8_t *faults, + size_t *fault_count); + +int bt_mesh_health_fault_clear(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t *test_id, u8_t *faults, + size_t *fault_count); + +int bt_mesh_health_fault_test(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t test_id, u8_t *faults, + size_t *fault_count); + +int bt_mesh_health_period_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *divisor); + +int bt_mesh_health_period_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t divisor, u8_t *updated_divisor); + +int bt_mesh_health_attention_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *attention); + +int bt_mesh_health_attention_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t attention, u8_t *updated_attention); + +s32_t bt_mesh_health_cli_timeout_get(void); +void bt_mesh_health_cli_timeout_set(s32_t timeout); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_HEALTH_CLI_H */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/health_srv.h b/libesp32/NimBLE-Arduino/src/mesh/health_srv.h new file mode 100644 index 000000000..0638c982a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/health_srv.h @@ -0,0 +1,99 @@ +/** @file + * @brief Bluetooth Mesh Health Server Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_HEALTH_SRV_H +#define __BT_MESH_HEALTH_SRV_H + +/** + * @brief Mesh Bluetooth Mesh Health Server Model + * @defgroup bt_mesh_health_srv + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_health_srv_cb { + /* Fetch current faults */ + int (*fault_get_cur)(struct bt_mesh_model *model, u8_t *test_id, + u16_t *company_id, u8_t *faults, + u8_t *fault_count); + + /* Fetch registered faults */ + int (*fault_get_reg)(struct bt_mesh_model *model, u16_t company_id, + u8_t *test_id, u8_t *faults, + u8_t *fault_count); + + /* Clear registered faults */ + int (*fault_clear)(struct bt_mesh_model *model, u16_t company_id); + + /* Run a specific test */ + int (*fault_test)(struct bt_mesh_model *model, u8_t test_id, + u16_t company_id); + + /* Attention on */ + void (*attn_on)(struct bt_mesh_model *model); + + /* Attention off */ + void (*attn_off)(struct bt_mesh_model *model); +}; + +/** @def BT_MESH_HEALTH_FAULT_MSG + * + * A helper to define a health fault message. + * + * @param max_faults Maximum number of faults the element can have. + * + * @return a New net_buf_simple of the needed size. + */ +#define BT_MESH_HEALTH_FAULT_MSG(max_faults) \ + NET_BUF_SIMPLE(1 + 3 + (max_faults)) + +/** Mesh Health Server Model Context */ +struct bt_mesh_health_srv { + struct bt_mesh_model *model; + + /* Optional callback struct */ + const struct bt_mesh_health_srv_cb *cb; + + /* Attention Timer state */ + struct k_delayed_work attn_timer; +}; + +int bt_mesh_fault_update(struct bt_mesh_elem *elem); + +extern const struct bt_mesh_model_op bt_mesh_health_srv_op[]; + +/** @def BT_MESH_MODEL_HEALTH_SRV + * + * Define a new health server model. Note that this API needs to be + * repeated for each element that the application wants to have a + * health server model on. Each instance also needs a unique + * bt_mesh_health_srv and bt_mesh_model_pub context. + * + * @param srv Pointer to a unique struct bt_mesh_health_srv. + * @param pub Pointer to a unique struct bt_mesh_model_pub. + * + * @return New mesh model instance. + */ +#define BT_MESH_MODEL_HEALTH_SRV(srv, pub) \ + BT_MESH_MODEL(BT_MESH_MODEL_ID_HEALTH_SRV, \ + bt_mesh_health_srv_op, pub, srv) + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_HEALTH_SRV_H */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/main.h b/libesp32/NimBLE-Arduino/src/mesh/main.h new file mode 100644 index 000000000..515d1d527 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/main.h @@ -0,0 +1,394 @@ +/** @file + * @brief Bluetooth Mesh Profile APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_MAIN_H +#define __BT_MESH_MAIN_H + +/** + * @brief Bluetooth Mesh Provisioning + * @defgroup bt_mesh_prov Bluetooth Mesh Provisioning + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + BT_MESH_NO_OUTPUT = 0, + BT_MESH_BLINK = BIT(0), + BT_MESH_BEEP = BIT(1), + BT_MESH_VIBRATE = BIT(2), + BT_MESH_DISPLAY_NUMBER = BIT(3), + BT_MESH_DISPLAY_STRING = BIT(4), +} bt_mesh_output_action_t; + +typedef enum { + BT_MESH_NO_INPUT = 0, + BT_MESH_PUSH = BIT(0), + BT_MESH_TWIST = BIT(1), + BT_MESH_ENTER_NUMBER = BIT(2), + BT_MESH_ENTER_STRING = BIT(3), +} bt_mesh_input_action_t; + +typedef enum { + BT_MESH_PROV_ADV = BIT(0), + BT_MESH_PROV_GATT = BIT(1), +} bt_mesh_prov_bearer_t; + +typedef enum { + BT_MESH_PROV_OOB_OTHER = BIT(0), + BT_MESH_PROV_OOB_URI = BIT(1), + BT_MESH_PROV_OOB_2D_CODE = BIT(2), + BT_MESH_PROV_OOB_BAR_CODE = BIT(3), + BT_MESH_PROV_OOB_NFC = BIT(4), + BT_MESH_PROV_OOB_NUMBER = BIT(5), + BT_MESH_PROV_OOB_STRING = BIT(6), + /* 7 - 10 are reserved */ + BT_MESH_PROV_OOB_ON_BOX = BIT(11), + BT_MESH_PROV_OOB_IN_BOX = BIT(12), + BT_MESH_PROV_OOB_ON_PAPER = BIT(13), + BT_MESH_PROV_OOB_IN_MANUAL = BIT(14), + BT_MESH_PROV_OOB_ON_DEV = BIT(15), +} bt_mesh_prov_oob_info_t; + +/** Provisioning properties & capabilities. */ +struct bt_mesh_prov { + /** The UUID that's used when advertising as unprovisioned */ + const u8_t *uuid; + + /** Optional URI. This will be advertised separately from the + * unprovisioned beacon, however the unprovisioned beacon will + * contain a hash of it so the two can be associated by the + * provisioner. + */ + const char *uri; + + /** Out of Band information field. */ + bt_mesh_prov_oob_info_t oob_info; + + /** Static OOB value */ + const u8_t *static_val; + /** Static OOB value length */ + u8_t static_val_len; + + /** Maximum size of Output OOB supported */ + u8_t output_size; + /** Supported Output OOB Actions */ + u16_t output_actions; + + /* Maximum size of Input OOB supported */ + u8_t input_size; + /** Supported Input OOB Actions */ + u16_t input_actions; + + /** @brief Output of a number is requested. + * + * This callback notifies the application that it should + * output the given number using the given action. + * + * @param act Action for outputting the number. + * @param num Number to be outputted. + * + * @return Zero on success or negative error code otherwise + */ + int (*output_number)(bt_mesh_output_action_t act, u32_t num); + + /** @brief Output of a string is requested. + * + * This callback notifies the application that it should + * display the given string to the user. + * + * @param str String to be displayed. + * + * @return Zero on success or negative error code otherwise + */ + int (*output_string)(const char *str); + + /** @brief Input is requested. + * + * This callback notifies the application that it should + * request input from the user using the given action. The + * requested input will either be a string or a number, and + * the application needs to consequently call the + * bt_mesh_input_string() or bt_mesh_input_number() functions + * once the data has been acquired from the user. + * + * @param act Action for inputting data. + * @param num Maximum size of the inputted data. + * + * @return Zero on success or negative error code otherwise + */ + int (*input)(bt_mesh_input_action_t act, u8_t size); + + /** @brief Provisioning link has been opened. + * + * This callback notifies the application that a provisioning + * link has been opened on the given provisioning bearer. + * + * @param bearer Provisioning bearer. + */ + void (*link_open)(bt_mesh_prov_bearer_t bearer); + + /** @brief Provisioning link has been closed. + * + * This callback notifies the application that a provisioning + * link has been closed on the given provisioning bearer. + * + * @param bearer Provisioning bearer. + */ + void (*link_close)(bt_mesh_prov_bearer_t bearer); + + /** @brief Provisioning is complete. + * + * This callback notifies the application that provisioning has + * been successfully completed, and that the local node has been + * assigned the specified NetKeyIndex and primary element address. + * + * @param net_idx NetKeyIndex given during provisioning. + * @param addr Primary element address. + */ + void (*complete)(u16_t net_idx, u16_t addr); + + /** @brief Node has been reset. + * + * This callback notifies the application that the local node + * has been reset and needs to be reprovisioned. The node will + * not automatically advertise as unprovisioned, rather the + * bt_mesh_prov_enable() API needs to be called to enable + * unprovisioned advertising on one or more provisioning bearers. + */ + void (*reset)(void); +}; + +/** @brief Provide provisioning input OOB string. + * + * This is intended to be called after the bt_mesh_prov input callback + * has been called with BT_MESH_ENTER_STRING as the action. + * + * @param str String. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_input_string(const char *str); + +/** @brief Provide provisioning input OOB number. + * + * This is intended to be called after the bt_mesh_prov input callback + * has been called with BT_MESH_ENTER_NUMBER as the action. + * + * @param num Number. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_input_number(u32_t num); + +/** @brief Enable specific provisioning bearers + * + * Enable one or more provisioning bearers. + * + * @param bearers Bit-wise or of provisioning bearers. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers); + +/** @brief Disable specific provisioning bearers + * + * Disable one or more provisioning bearers. + * + * @param bearers Bit-wise or of provisioning bearers. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers); + +/** + * @} + */ + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh Bluetooth Mesh + * @ingroup bluetooth + * @{ + */ + +/* Primary Network Key index */ +#define BT_MESH_NET_PRIMARY 0x000 + +#define BT_MESH_RELAY_DISABLED 0x00 +#define BT_MESH_RELAY_ENABLED 0x01 +#define BT_MESH_RELAY_NOT_SUPPORTED 0x02 + +#define BT_MESH_BEACON_DISABLED 0x00 +#define BT_MESH_BEACON_ENABLED 0x01 + +#define BT_MESH_GATT_PROXY_DISABLED 0x00 +#define BT_MESH_GATT_PROXY_ENABLED 0x01 +#define BT_MESH_GATT_PROXY_NOT_SUPPORTED 0x02 + +#define BT_MESH_FRIEND_DISABLED 0x00 +#define BT_MESH_FRIEND_ENABLED 0x01 +#define BT_MESH_FRIEND_NOT_SUPPORTED 0x02 + +#define BT_MESH_NODE_IDENTITY_STOPPED 0x00 +#define BT_MESH_NODE_IDENTITY_RUNNING 0x01 +#define BT_MESH_NODE_IDENTITY_NOT_SUPPORTED 0x02 + +/* Features */ +#define BT_MESH_FEAT_RELAY BIT(0) +#define BT_MESH_FEAT_PROXY BIT(1) +#define BT_MESH_FEAT_FRIEND BIT(2) +#define BT_MESH_FEAT_LOW_POWER BIT(3) +#define BT_MESH_FEAT_SUPPORTED (BT_MESH_FEAT_RELAY | \ + BT_MESH_FEAT_PROXY | \ + BT_MESH_FEAT_FRIEND | \ + BT_MESH_FEAT_LOW_POWER) + +/** @brief Initialize Mesh support + * + * After calling this API, the node will not automatically advertise as + * unprovisioned, rather the bt_mesh_prov_enable() API needs to be called + * to enable unprovisioned advertising on one or more provisioning bearers. + * + * @param own_addr_type Node address type + * @param prov Node provisioning information. + * @param comp Node Composition. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_init(u8_t own_addr_type, + const struct bt_mesh_prov *prov, + const struct bt_mesh_comp *comp); + +/** @brief Reset the state of the local Mesh node. + * + * Resets the state of the node, which means that it needs to be + * reprovisioned to become an active node in a Mesh network again. + * + * After calling this API, the node will not automatically advertise as + * unprovisioned, rather the bt_mesh_prov_enable() API needs to be called + * to enable unprovisioned advertising on one or more provisioning bearers. + * + */ +void bt_mesh_reset(void); + +/** @brief Suspend the Mesh network temporarily. + * + * This API can be used for power saving purposes, but the user should be + * aware that leaving the local node suspended for a long period of time + * may cause it to become permanently disconnected from the Mesh network. + * If at all possible, the Friendship feature should be used instead, to + * make the node into a Low Power Node. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_suspend(void); + +/** @brief Resume a suspended Mesh network. + * + * This API resumes the local node, after it has been suspended using the + * bt_mesh_suspend() API. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_resume(void); + +/** @brief Provision the local Mesh Node. + * + * This API should normally not be used directly by the application. The + * only exception is for testing purposes where manual provisioning is + * desired without an actual external provisioner. + * + * @param net_key Network Key + * @param net_idx Network Key Index + * @param flags Provisioning Flags + * @param iv_index IV Index + * @param addr Primary element address + * @param dev_key Device Key + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx, + u8_t flags, u32_t iv_index, u16_t addr, + const u8_t dev_key[16]); + +/** @brief Check if the local node has been provisioned. + * + * This API can be used to check if the local node has been provisioned + * or not. It can e.g. be helpful to determine if there was a stored + * network in flash, i.e. if the network was restored after calling + * settings_load(). + * + * @return True if the node is provisioned. False otherwise. + */ +bool bt_mesh_is_provisioned(void); + +/** @brief Toggle the IV Update test mode + * + * This API is only available if the IV Update test mode has been enabled + * in Kconfig. It is needed for passing most of the IV Update qualification + * test cases. + * + * @param enable true to enable IV Update test mode, false to disable it. + */ +void bt_mesh_iv_update_test(bool enable); + +/** @brief Toggle the IV Update state + * + * This API is only available if the IV Update test mode has been enabled + * in Kconfig. It is needed for passing most of the IV Update qualification + * test cases. + * + * @return true if IV Update In Progress state was entered, false otherwise. + */ +bool bt_mesh_iv_update(void); + +/** @brief Toggle the Low Power feature of the local device + * + * Enables or disables the Low Power feature of the local device. This is + * exposed as a run-time feature, since the device might want to change + * this e.g. based on being plugged into a stable power source or running + * from a battery power source. + * + * @param enable true to enable LPN functionality, false to disable it. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_lpn_set(bool enable); + +/** @brief Send out a Friend Poll message. + * + * Send a Friend Poll message to the Friend of this node. If there is no + * established Friendship the function will return an error. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_lpn_poll(void); + +/** @brief Register a callback for Friendship changes. + * + * Registers a callback that will be called whenever Friendship gets + * established or is lost. + * + * @param cb Function to call when the Friendship status changes. + */ +void bt_mesh_lpn_set_cb(void (*cb)(u16_t friend_addr, bool established)); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_MAIN_H */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/mesh.h b/libesp32/NimBLE-Arduino/src/mesh/mesh.h new file mode 100644 index 000000000..9ba63ef0e --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/mesh.h @@ -0,0 +1,26 @@ +/** @file + * @brief Bluetooth Mesh Profile APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_H +#define __BT_MESH_H + +#include +#include "syscfg/syscfg.h" +#include "os/os_mbuf.h" + +#include "glue.h" +#include "access.h" +#include "main.h" +#include "cfg_srv.h" +#include "health_srv.h" +#include "cfg_cli.h" +#include "health_cli.h" +#include "proxy.h" + +#endif /* __BT_MESH_H */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/model_cli.h b/libesp32/NimBLE-Arduino/src/mesh/model_cli.h new file mode 100644 index 000000000..33fbd6e9d --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/model_cli.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MODEL_CLI_H__ +#define __MODEL_CLI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_gen_model_cli { + struct bt_mesh_model *model; + + struct k_sem op_sync; + u32_t op_pending; + void *op_param; +}; + +extern struct bt_mesh_gen_model_cli gen_onoff_cli; +extern const struct bt_mesh_model_op gen_onoff_cli_op[]; + +#define BT_MESH_MODEL_GEN_ONOFF_CLI() \ + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, \ + gen_onoff_cli_op, NULL, &gen_onoff_cli) + +extern struct bt_mesh_gen_model_cli gen_level_cli; +extern const struct bt_mesh_model_op gen_level_cli_op[]; + +#define BT_MESH_MODEL_GEN_LEVEL_CLI(pub) \ + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_CLI, \ + gen_level_cli_op, NULL, &gen_level_cli) + + +int bt_mesh_gen_onoff_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *state); +int bt_mesh_gen_onoff_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t val, u8_t *state); +int bt_mesh_gen_level_get(u16_t net_idx, u16_t addr, u16_t app_idx, + s16_t *level); +int bt_mesh_gen_level_set(u16_t net_idx, u16_t addr, u16_t app_idx, + s16_t val, s16_t *state); +int bt_mesh_gen_model_cli_init(struct bt_mesh_model *model, bool primary); + +#ifdef __cplusplus +} +#endif + +#endif /* __MODEL_CLI_H__ */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/model_srv.h b/libesp32/NimBLE-Arduino/src/mesh/model_srv.h new file mode 100644 index 000000000..c4f4033df --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/model_srv.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MODEL_SRV_H__ +#define __MODEL_SRV_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_gen_onoff_srv_cb { + int (*get)(struct bt_mesh_model *model, u8_t *state); + int (*set)(struct bt_mesh_model *model, u8_t state); +}; + +extern const struct bt_mesh_model_op gen_onoff_srv_op[]; + +#define BT_MESH_MODEL_GEN_ONOFF_SRV(srv, pub) \ + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, \ + gen_onoff_srv_op, pub, srv) + +struct bt_mesh_gen_level_srv_cb { + int (*get)(struct bt_mesh_model *model, s16_t *level); + int (*set)(struct bt_mesh_model *model, s16_t level); +}; + +extern const struct bt_mesh_model_op gen_level_srv_op[]; + +#define BT_MESH_MODEL_GEN_LEVEL_SRV(srv, pub) \ + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, \ + gen_level_srv_op, pub, srv) + +struct bt_mesh_light_lightness_srv_cb { + int (*get)(struct bt_mesh_model *model, s16_t *level); + int (*set)(struct bt_mesh_model *model, s16_t level); +}; + +extern const struct bt_mesh_model_op light_lightness_srv_op[]; + +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_SRV(srv, pub) \ + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV, \ + light_lightness_srv_op, pub, srv) + + +void bt_mesh_set_gen_onoff_srv_cb(struct bt_mesh_gen_onoff_srv_cb *gen_onoff_cb); +void bt_mesh_set_gen_level_srv_cb(struct bt_mesh_gen_level_srv_cb *gen_level_cb); +void bt_mesh_set_light_lightness_srv_cb(struct bt_mesh_light_lightness_srv_cb *light_lightness_cb); + +#ifdef __cplusplus +} +#endif + +#endif /* __MODEL_SRV_H__ */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/porting.h b/libesp32/NimBLE-Arduino/src/mesh/porting.h new file mode 100644 index 000000000..1667a8a04 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/porting.h @@ -0,0 +1,27 @@ +/** @file + * @brief Bluetooth Mesh Porting APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_PORTING_H +#define __BT_MESH_PORTING_H + +#ifdef __cplusplus +extern "C" { +#endif + +void mesh_adv_thread(void *args); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_PORTING_H */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/proxy.h b/libesp32/NimBLE-Arduino/src/mesh/proxy.h new file mode 100644 index 000000000..63bbfa3e5 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/proxy.h @@ -0,0 +1,43 @@ +/** @file + * @brief Bluetooth Mesh Proxy APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_PROXY_H +#define __BT_MESH_PROXY_H + +/** + * @brief Bluetooth Mesh Proxy + * @defgroup bt_mesh_proxy Bluetooth Mesh Proxy + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enable advertising with Node Identity. + * + * This API requires that GATT Proxy support has been enabled. Once called + * each subnet will start advertising using Node Identity for the next + * 60 seconds. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_proxy_identity_enable(void); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_PROXY_H */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/slist.h b/libesp32/NimBLE-Arduino/src/mesh/slist.h new file mode 100644 index 000000000..8a858f83b --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/slist.h @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief Single-linked list implementation + * + * Single-linked list implementation using inline macros/functions. + * This API is not thread safe, and thus if a list is used across threads, + * calls to functions must be protected with synchronization primitives. + */ + +#ifndef __SLIST_H__ +#define __SLIST_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +struct _snode { + struct _snode *next; +}; + +typedef struct _snode sys_snode_t; + +struct _slist { + sys_snode_t *head; + sys_snode_t *tail; +}; + +typedef struct _slist sys_slist_t; + +/** + * @brief Provide the primitive to iterate on a list + * Note: the loop is unsafe and thus __sn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE(l, n) { + * + * } + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + */ +#define SYS_SLIST_FOR_EACH_NODE(__sl, __sn) \ + for (__sn = sys_slist_peek_head(__sl); __sn; \ + __sn = sys_slist_peek_next(__sn)) + +/** + * @brief Provide the primitive to iterate on a list, from a node in the list + * Note: the loop is unsafe and thus __sn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_ITERATE_FROM_NODE(l, n) { + * + * } + * + * Like SYS_SLIST_FOR_EACH_NODE(), but __dn already contains a node in the list + * where to start searching for the next entry from. If NULL, it starts from + * the head. + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + * it contains the starting node, or NULL to start from the head + */ +#define SYS_SLIST_ITERATE_FROM_NODE(__sl, __sn) \ + for (__sn = __sn ? sys_slist_peek_next_no_check(__sn) \ + : sys_slist_peek_head(__sl); \ + __sn; \ + __sn = sys_slist_peek_next(__sn)) + +/** + * @brief Provide the primitive to safely iterate on a list + * Note: __sn can be removed, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE_SAFE(l, n, s) { + * + * } + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + * @param __sns A sys_snode_t pointer for the loop to run safely + */ +#define SYS_SLIST_FOR_EACH_NODE_SAFE(__sl, __sn, __sns) \ + for (__sn = sys_slist_peek_head(__sl), \ + __sns = sys_slist_peek_next(__sn); \ + __sn; __sn = __sns, \ + __sns = sys_slist_peek_next(__sn)) + +/* + * @brief Provide the primitive to resolve the container of a list node + * Note: it is safe to use with NULL pointer nodes + * + * @param __ln A pointer on a sys_node_t to get its container + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_CONTAINER(__ln, __cn, __n) \ + ((__ln) ? CONTAINER_OF((__ln), __typeof__(*(__cn)), __n) : NULL) +/* + * @brief Provide the primitive to peek container of the list head + * + * @param __sl A pointer on a sys_slist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n) \ + SYS_SLIST_CONTAINER(sys_slist_peek_head(__sl), __cn, __n) + +/* + * @brief Provide the primitive to peek container of the list tail + * + * @param __sl A pointer on a sys_slist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_PEEK_TAIL_CONTAINER(__sl, __cn, __n) \ + SYS_SLIST_CONTAINER(sys_slist_peek_tail(__sl), __cn, __n) + +/* + * @brief Provide the primitive to peek the next container + * + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ + +#define SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n) \ + ((__cn) ? SYS_SLIST_CONTAINER(sys_slist_peek_next(&((__cn)->__n)), \ + __cn, __n) : NULL) + +/** + * @brief Provide the primitive to iterate on a list under a container + * Note: the loop is unsafe and thus __cn should not be detached + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_CONTAINER(l, c, n) { + * + * } + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_FOR_EACH_CONTAINER(__sl, __cn, __n) \ + for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n); __cn; \ + __cn = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +/** + * @brief Provide the primitive to safely iterate on a list under a container + * Note: __cn can be detached, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE_SAFE(l, c, cn, n) { + * + * } + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __cns A pointer for the loop to run safely + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_FOR_EACH_CONTAINER_SAFE(__sl, __cn, __cns, __n) \ + for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n), \ + __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n); __cn; \ + __cn = __cns, __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +/** + * @brief Initialize a list + * + * @param list A pointer on the list to initialize + */ +static inline void sys_slist_init(sys_slist_t *list) +{ + list->head = NULL; + list->tail = NULL; +} + +#define SYS_SLIST_STATIC_INIT(ptr_to_list) {NULL, NULL} + +/** + * @brief Test if the given list is empty + * + * @param list A pointer on the list to test + * + * @return a boolean, true if it's empty, false otherwise + */ +static inline bool sys_slist_is_empty(sys_slist_t *list) +{ + return (!list->head); +} + +/** + * @brief Peek the first node from the list + * + * @param list A point on the list to peek the first node from + * + * @return A pointer on the first node of the list (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_head(sys_slist_t *list) +{ + return list->head; +} + +/** + * @brief Peek the last node from the list + * + * @param list A point on the list to peek the last node from + * + * @return A pointer on the last node of the list (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_tail(sys_slist_t *list) +{ + return list->tail; +} + +/** + * @brief Peek the next node from current node, node is not NULL + * + * Faster then sys_slist_peek_next() if node is known not to be NULL. + * + * @param node A pointer on the node where to peek the next node + * + * @return a pointer on the next node (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node) +{ + return node->next; +} + +/** + * @brief Peek the next node from current node + * + * @param node A pointer on the node where to peek the next node + * + * @return a pointer on the next node (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_next(sys_snode_t *node) +{ + return node ? sys_slist_peek_next_no_check(node) : NULL; +} + +/** + * @brief Prepend a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to prepend + */ +static inline void sys_slist_prepend(sys_slist_t *list, + sys_snode_t *node) +{ + node->next = list->head; + list->head = node; + + if (!list->tail) { + list->tail = list->head; + } +} + +/** + * @brief Append a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to append + */ +static inline void sys_slist_append(sys_slist_t *list, + sys_snode_t *node) +{ + node->next = NULL; + + if (!list->tail) { + list->tail = node; + list->head = node; + } else { + list->tail->next = node; + list->tail = node; + } +} + +/** + * @brief Append a list to the given list + * + * Append a singly-linked, NULL-terminated list consisting of nodes containing + * the pointer to the next node as the first element of a node, to @a list. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param head A pointer to the first element of the list to append + * @param tail A pointer to the last element of the list to append + */ +static inline void sys_slist_append_list(sys_slist_t *list, + void *head, void *tail) +{ + if (!list->tail) { + list->head = (sys_snode_t *)head; + list->tail = (sys_snode_t *)tail; + } else { + list->tail->next = (sys_snode_t *)head; + list->tail = (sys_snode_t *)tail; + } +} + +/** + * @brief merge two slists, appending the second one to the first + * + * When the operation is completed, the appending list is empty. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param list_to_append A pointer to the list to append. + */ +static inline void sys_slist_merge_slist(sys_slist_t *list, + sys_slist_t *list_to_append) +{ + sys_slist_append_list(list, list_to_append->head, + list_to_append->tail); + sys_slist_init(list_to_append); +} + +/** + * @brief Insert a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param prev A pointer on the previous node + * @param node A pointer on the node to insert + */ +static inline void sys_slist_insert(sys_slist_t *list, + sys_snode_t *prev, + sys_snode_t *node) +{ + if (!prev) { + sys_slist_prepend(list, node); + } else if (!prev->next) { + sys_slist_append(list, node); + } else { + node->next = prev->next; + prev->next = node; + } +} + +/** + * @brief Fetch and remove the first node of the given list + * + * List must be known to be non-empty. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * + * @return A pointer to the first node of the list + */ +static inline sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list) +{ + sys_snode_t *node = list->head; + + list->head = node->next; + if (list->tail == node) { + list->tail = list->head; + } + + return node; +} + +/** + * @brief Fetch and remove the first node of the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * + * @return A pointer to the first node of the list (or NULL if empty) + */ +static inline sys_snode_t *sys_slist_get(sys_slist_t *list) +{ + return sys_slist_is_empty(list) ? NULL : sys_slist_get_not_empty(list); +} + +/** + * @brief Remove a node + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param prev_node A pointer on the previous node + * (can be NULL, which means the node is the list's head) + * @param node A pointer on the node to remove + */ +static inline void sys_slist_remove(sys_slist_t *list, + sys_snode_t *prev_node, + sys_snode_t *node) +{ + if (!prev_node) { + list->head = node->next; + + /* Was node also the tail? */ + if (list->tail == node) { + list->tail = list->head; + } + } else { + prev_node->next = node->next; + + /* Was node the tail? */ + if (list->tail == node) { + list->tail = prev_node; + } + } + + node->next = NULL; +} + +/** + * @brief Find and remove a node from a list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to remove from the list + * + * @return true if node was removed + */ +static inline bool sys_slist_find_and_remove(sys_slist_t *list, + sys_snode_t *node) +{ + sys_snode_t *prev = NULL; + sys_snode_t *test; + + SYS_SLIST_FOR_EACH_NODE(list, test) { + if (test == node) { + sys_slist_remove(list, prev, node); + return true; + } + + prev = test; + } + + return false; +} + + +#ifdef __cplusplus +} +#endif + +#endif /* __SLIST_H__ */ diff --git a/libesp32/NimBLE-Arduino/src/mesh/testing.h b/libesp32/NimBLE-Arduino/src/mesh/testing.h new file mode 100644 index 000000000..4c2b2a619 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/mesh/testing.h @@ -0,0 +1,105 @@ +/** + * @file testing.h + * @brief Internal API for Bluetooth testing. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BT_TESTING_H +#define __BT_TESTING_H + +#include "slist.h" +#include "glue.h" +#include "access.h" + +/** + * @brief Bluetooth testing + * @defgroup bt_test_cb Bluetooth testing callbacks + * @ingroup bluetooth + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Bluetooth Testing callbacks structure. + * + * Callback structure to be used for Bluetooth testing purposes. + * Allows access to Bluetooth stack internals, not exposed by public API. + */ +struct bt_test_cb { + void (*mesh_net_recv)(u8_t ttl, u8_t ctl, u16_t src, u16_t dst, + const void *payload, size_t payload_len); + void (*mesh_model_bound)(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx); + void (*mesh_model_unbound)(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx); + void (*mesh_prov_invalid_bearer)(u8_t opcode); + void (*mesh_trans_incomp_timer_exp)(void); + + sys_snode_t node; +}; + +/** Register callbacks for Bluetooth testing purposes + * + * @param cb bt_test_cb callback structure + */ +void bt_test_cb_register(struct bt_test_cb *cb); + +/** Unregister callbacks for Bluetooth testing purposes + * + * @param cb bt_test_cb callback structure + */ +void bt_test_cb_unregister(struct bt_test_cb *cb); + +/** Send Friend Subscription List Add message. + * + * Used by Low Power node to send the group address for which messages are to + * be stored by Friend node. + * + * @param group Group address + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_test_mesh_lpn_group_add(u16_t group); + +/** Send Friend Subscription List Remove message. + * + * Used by Low Power node to remove the group addresses from Friend node + * subscription list. Messages sent to those addresses will not be stored + * by Friend node. + * + * @param groups Group addresses + * @param groups_count Group addresses count + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_test_mesh_lpn_group_remove(u16_t *groups, size_t groups_count); + +/** Clear replay protection list cache. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_test_mesh_rpl_clear(void); + +u8_t mod_bind(struct bt_mesh_model *model, u16_t key_idx); +u8_t mod_unbind(struct bt_mesh_model *model, u16_t key_idx, bool store); +int cmd_mesh_init(int argc, char *argv[]); + +int bt_test_shell_init(void); +int bt_test_bind_app_key_to_model(struct bt_mesh_model *model, u16_t key_idx, u16_t id); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_TESTING_H */ diff --git a/libesp32/NimBLE-Arduino/src/modlog/modlog.h b/libesp32/NimBLE-Arduino/src/modlog/modlog.h new file mode 100644 index 000000000..e8752d465 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/modlog/modlog.h @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_MODLOG_ +#define H_MODLOG_ + +#include + +#include "log/log.h" + +#ifdef ESP_PLATFORM +#include "esp_log.h" +#include +#include +#endif + +#define MODLOG_MODULE_DFLT 255 + +#if (MYNEWT_VAL(LOG_LEVEL) > 0) +static inline void +modlog_dummy(const char *msg, ...) +{ + (void)msg; +} +#endif + +#ifdef ESP_PLATFORM +/// Uncomment these and comment out the 3 defines below to see NimBLE messages in Arduino. +/* +#define MODLOG_DEBUG(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_ERROR, "NimBLE",ml_msg_, ##__VA_ARGS__) + +#define MODLOG_INFO(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_ERROR, "NimBLE",ml_msg_, ##__VA_ARGS__) + +#define MODLOG_WARN(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_ERROR, "NimBLE",ml_msg_, ##__VA_ARGS__) +*/ +#define MODLOG_DEBUG(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_DEBUG, "NimBLE",ml_msg_, ##__VA_ARGS__) + +#define MODLOG_INFO(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_INFO, "NimBLE",ml_msg_, ##__VA_ARGS__) + +#define MODLOG_WARN(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_WARN, "NimBLE",ml_msg_, ##__VA_ARGS__) + +#define MODLOG_ERROR(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_ERROR, "NimBLE",ml_msg_, ##__VA_ARGS__) + +#define MODLOG_CRITICAL(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_ERROR, "NimBLE",ml_msg_, ##__VA_ARGS__) + +#else + +#if (MYNEWT_VAL(LOG_LEVEL) > 1) +#define MODLOG_INFO(ml_mod_, ml_msg_, ...) \ + modlog_dummy((ml_msg_), ##__VA_ARGS__) +#else +#define MODLOG_INFO(ml_mod_, ml_msg_, ...) \ + printf((ml_msg_), ##__VA_ARGS__); +#endif + +#if (MYNEWT_VAL(LOG_LEVEL) > 2) +#define MODLOG_WARN(ml_mod_, ml_msg_, ...) \ + modlog_dummy((ml_msg_), ##__VA_ARGS__) +#else +#define MODLOG_WARN(ml_mod_, ml_msg_, ...) \ + printf((ml_msg_), ##__VA_ARGS__); +#endif + +#if (MYNEWT_VAL(LOG_LEVEL) > 3) +#define MODLOG_ERROR(ml_mod_, ml_msg_, ...) \ + modlog_dummy((ml_msg_), ##__VA_ARGS__) +#else +#define MODLOG_ERROR(ml_mod_, ml_msg_, ...) \ + printf((ml_msg_), ##__VA_ARGS__); +#endif + +#if (MYNEWT_VAL(LOG_LEVEL) > 4) +#define MODLOG_CRITICAL(ml_mod_, ml_msg_, ...) \ + modlog_dummy((ml_msg_), ##__VA_ARGS__) +#else +#define MODLOG_CRITICAL(ml_mod_, ml_msg_, ...) \ + printf((ml_msg_), ##__VA_ARGS__); +#endif + +#endif + +#define MODLOG(ml_lvl_, ml_mod_, ...) \ + MODLOG_ ## ml_lvl_((ml_mod_), __VA_ARGS__) + +#define MODLOG_DFLT(ml_lvl_, ...) \ + MODLOG(ml_lvl_, LOG_MODULE_DEFAULT, __VA_ARGS__) + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/ble.h b/libesp32/NimBLE-Arduino/src/nimble/ble.h new file mode 100644 index 000000000..1d726958d --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/ble.h @@ -0,0 +1,290 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_ +#define H_BLE_ + +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* The number of advertising instances */ +#define BLE_ADV_INSTANCES (MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES) + 1) + +/* BLE encryption block definitions */ +#define BLE_ENC_BLOCK_SIZE (16) + +/* 4 byte header + 251 byte payload. */ +#define BLE_ACL_MAX_PKT_SIZE 255 + +struct ble_encryption_block +{ + uint8_t key[BLE_ENC_BLOCK_SIZE]; + uint8_t plain_text[BLE_ENC_BLOCK_SIZE]; + uint8_t cipher_text[BLE_ENC_BLOCK_SIZE]; +}; + +/* + * BLE MBUF structure: + * + * The BLE mbuf structure is as follows. Note that this structure applies to + * the packet header mbuf (not mbufs that are part of a "packet chain"): + * struct os_mbuf (16) + * struct os_mbuf_pkthdr (8) + * struct ble_mbuf_hdr (8) + * Data buffer (payload size, in bytes) + * + * The BLE mbuf header contains the following: + * flags: bitfield with the following values + * 0x01: Set if there was a match on the whitelist + * 0x02: Set if a connect request was transmitted upon receiving pdu + * 0x04: Set the first time we transmit the PDU (used to detect retry). + * channel: The logical BLE channel PHY channel # (0 - 39) + * crcok: flag denoting CRC check passed (1) or failed (0). + * rssi: RSSI, in dBm. + */ +struct ble_mbuf_hdr_rxinfo +{ + uint16_t flags; + uint8_t channel; + uint8_t handle; + int8_t rssi; + /* XXX: we could just use single phy_mode field */ + int8_t phy; + uint8_t phy_mode; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + void *user_data; +#endif +}; + +/* Flag definitions for rxinfo */ +#define BLE_MBUF_HDR_F_INITA_RESOLVED (0x2000) +#define BLE_MBUF_HDR_F_EXT_ADV_SEC (0x1000) +#define BLE_MBUF_HDR_F_EXT_ADV (0x0800) +#define BLE_MBUF_HDR_F_RESOLVED (0x0400) +#define BLE_MBUF_HDR_F_AUX_PTR_WAIT (0x0200) +#define BLE_MBUF_HDR_F_AUX_INVALID (0x0100) +#define BLE_MBUF_HDR_F_CRC_OK (0x0080) +#define BLE_MBUF_HDR_F_DEVMATCH (0x0040) +#define BLE_MBUF_HDR_F_MIC_FAILURE (0x0020) +#define BLE_MBUF_HDR_F_SCAN_RSP_TXD (0x0010) +#define BLE_MBUF_HDR_F_SCAN_RSP_CHK (0x0008) +#define BLE_MBUF_HDR_F_RXSTATE_MASK (0x0007) + +/* Transmit info. NOTE: no flags defined */ +struct ble_mbuf_hdr_txinfo +{ + uint8_t flags; + uint8_t offset; + uint8_t pyld_len; + uint8_t hdr_byte; +}; + +struct ble_mbuf_hdr +{ + union { + struct ble_mbuf_hdr_rxinfo rxinfo; + struct ble_mbuf_hdr_txinfo txinfo; + }; + uint32_t beg_cputime; + uint32_t rem_usecs; +}; + +#define BLE_MBUF_HDR_EXT_ADV_SEC(hdr) \ + (!!((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_EXT_ADV_SEC)) + +#define BLE_MBUF_HDR_EXT_ADV(hdr) \ + (!!((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_EXT_ADV)) + +#define BLE_MBUF_HDR_DEVMATCH(hdr) \ + (!!((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_DEVMATCH)) + +#define BLE_MBUF_HDR_SCAN_RSP_RCV(hdr) \ + (!!((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_SCAN_RSP_CHK)) + +#define BLE_MBUF_HDR_AUX_INVALID(hdr) \ + (!!((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_AUX_INVALID)) + +#define BLE_MBUF_HDR_WAIT_AUX(hdr) \ + (!!((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_AUX_PTR_WAIT)) + +#define BLE_MBUF_HDR_CRC_OK(hdr) \ + (!!((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_CRC_OK)) + +#define BLE_MBUF_HDR_MIC_FAILURE(hdr) \ + (!!((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_MIC_FAILURE)) + +#define BLE_MBUF_HDR_RESOLVED(hdr) \ + (!!((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_RESOLVED)) + +#define BLE_MBUF_HDR_INITA_RESOLVED(hdr) \ + (!!((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_INITA_RESOLVED)) + +#define BLE_MBUF_HDR_RX_STATE(hdr) \ + ((uint8_t)((hdr)->rxinfo.flags & BLE_MBUF_HDR_F_RXSTATE_MASK)) + +#define BLE_MBUF_HDR_PTR(om) \ + (struct ble_mbuf_hdr *)((uint8_t *)om + sizeof(struct os_mbuf) + \ + sizeof(struct os_mbuf_pkthdr)) + +/* BLE mbuf overhead per packet header mbuf */ +#define BLE_MBUF_PKTHDR_OVERHEAD \ + (sizeof(struct os_mbuf_pkthdr) + sizeof(struct ble_mbuf_hdr)) + +#define BLE_MBUF_MEMBLOCK_OVERHEAD \ + (sizeof(struct os_mbuf) + BLE_MBUF_PKTHDR_OVERHEAD) + +/* Length of host user header. Only contains the peer's connection handle. */ +#define BLE_MBUF_HS_HDR_LEN (2) + +#define BLE_DEV_ADDR_LEN (6) +extern uint8_t g_dev_addr[BLE_DEV_ADDR_LEN]; +extern uint8_t g_random_addr[BLE_DEV_ADDR_LEN]; + +/* BLE Error Codes (Core v4.2 Vol 2 part D) */ +enum ble_error_codes +{ + /* An "error" code of 0x0 means success */ + BLE_ERR_SUCCESS = 0x00, + BLE_ERR_UNKNOWN_HCI_CMD = 0x01, + BLE_ERR_UNK_CONN_ID = 0x02, + BLE_ERR_HW_FAIL = 0x03, + BLE_ERR_PAGE_TMO = 0x04, + BLE_ERR_AUTH_FAIL = 0x05, + BLE_ERR_PINKEY_MISSING = 0x06, + BLE_ERR_MEM_CAPACITY = 0x07, + BLE_ERR_CONN_SPVN_TMO = 0x08, + BLE_ERR_CONN_LIMIT = 0x09, + BLE_ERR_SYNCH_CONN_LIMIT = 0x0a, + BLE_ERR_ACL_CONN_EXISTS = 0x0b, + BLE_ERR_CMD_DISALLOWED = 0x0c, + BLE_ERR_CONN_REJ_RESOURCES = 0x0d, + BLE_ERR_CONN_REJ_SECURITY = 0x0e, + BLE_ERR_CONN_REJ_BD_ADDR = 0x0f, + BLE_ERR_CONN_ACCEPT_TMO = 0x10, + BLE_ERR_UNSUPPORTED = 0x11, + BLE_ERR_INV_HCI_CMD_PARMS = 0x12, + BLE_ERR_REM_USER_CONN_TERM = 0x13, + BLE_ERR_RD_CONN_TERM_RESRCS = 0x14, + BLE_ERR_RD_CONN_TERM_PWROFF = 0x15, + BLE_ERR_CONN_TERM_LOCAL = 0x16, + BLE_ERR_REPEATED_ATTEMPTS = 0x17, + BLE_ERR_NO_PAIRING = 0x18, + BLE_ERR_UNK_LMP = 0x19, + BLE_ERR_UNSUPP_REM_FEATURE = 0x1a, + BLE_ERR_SCO_OFFSET = 0x1b, + BLE_ERR_SCO_ITVL = 0x1c, + BLE_ERR_SCO_AIR_MODE = 0x1d, + BLE_ERR_INV_LMP_LL_PARM = 0x1e, + BLE_ERR_UNSPECIFIED = 0x1f, + BLE_ERR_UNSUPP_LMP_LL_PARM = 0x20, + BLE_ERR_NO_ROLE_CHANGE = 0x21, + BLE_ERR_LMP_LL_RSP_TMO = 0x22, + BLE_ERR_LMP_COLLISION = 0x23, + BLE_ERR_LMP_PDU = 0x24, + BLE_ERR_ENCRYPTION_MODE = 0x25, + BLE_ERR_LINK_KEY_CHANGE = 0x26, + BLE_ERR_UNSUPP_QOS = 0x27, + BLE_ERR_INSTANT_PASSED = 0x28, + BLE_ERR_UNIT_KEY_PAIRING = 0x29, + BLE_ERR_DIFF_TRANS_COLL = 0x2a, + /* BLE_ERR_RESERVED = 0x2b */ + BLE_ERR_QOS_PARM = 0x2c, + BLE_ERR_QOS_REJECTED = 0x2d, + BLE_ERR_CHAN_CLASS = 0x2e, + BLE_ERR_INSUFFICIENT_SEC = 0x2f, + BLE_ERR_PARM_OUT_OF_RANGE = 0x30, + /* BLE_ERR_RESERVED = 0x31 */ + BLE_ERR_PENDING_ROLE_SW = 0x32, + /* BLE_ERR_RESERVED = 0x33 */ + BLE_ERR_RESERVED_SLOT = 0x34, + BLE_ERR_ROLE_SW_FAIL = 0x35, + BLE_ERR_INQ_RSP_TOO_BIG = 0x36, + BLE_ERR_SEC_SIMPLE_PAIR = 0x37, + BLE_ERR_HOST_BUSY_PAIR = 0x38, + BLE_ERR_CONN_REJ_CHANNEL = 0x39, + BLE_ERR_CTLR_BUSY = 0x3a, + BLE_ERR_CONN_PARMS = 0x3b, + BLE_ERR_DIR_ADV_TMO = 0x3c, + BLE_ERR_CONN_TERM_MIC = 0x3d, + BLE_ERR_CONN_ESTABLISHMENT = 0x3e, + BLE_ERR_MAC_CONN_FAIL = 0x3f, + BLE_ERR_COARSE_CLK_ADJ = 0x40, + BLE_ERR_TYPE0_SUBMAP_NDEF = 0x41, + BLE_ERR_UNK_ADV_INDENT = 0x42, + BLE_ERR_LIMIT_REACHED = 0x43, + BLE_ERR_OPERATION_CANCELLED = 0x44, + BLE_ERR_MAX = 0xff +}; + +int ble_err_from_os(int os_err); + +/* HW error codes */ +#define BLE_HW_ERR_DO_NOT_USE (0) /* XXX: reserve this one for now */ +#define BLE_HW_ERR_HCI_SYNC_LOSS (1) + +/* Own Bluetooth Device address type */ +#define BLE_OWN_ADDR_PUBLIC (0x00) +#define BLE_OWN_ADDR_RANDOM (0x01) +#define BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT (0x02) +#define BLE_OWN_ADDR_RPA_RANDOM_DEFAULT (0x03) + +/* Bluetooth Device address type */ +#define BLE_ADDR_PUBLIC (0x00) +#define BLE_ADDR_RANDOM (0x01) +#define BLE_ADDR_PUBLIC_ID (0x02) +#define BLE_ADDR_RANDOM_ID (0x03) + +#define BLE_ADDR_ANY (&(ble_addr_t) { 0, {0, 0, 0, 0, 0, 0} }) + +#define BLE_ADDR_IS_RPA(addr) (((addr)->type == BLE_ADDR_RANDOM) && \ + ((addr)->val[5] & 0xc0) == 0x40) +#define BLE_ADDR_IS_NRPA(addr) (((addr)->type == BLE_ADDR_RANDOM) && \ + ((addr)->val[5] & 0xc0) == 0x00) +#define BLE_ADDR_IS_STATIC(addr) (((addr)->type == BLE_ADDR_RANDOM) && \ + ((addr)->val[5] & 0xc0) == 0xc0) + +typedef struct { + uint8_t type; + uint8_t val[6]; +} ble_addr_t; + + +static inline int ble_addr_cmp(const ble_addr_t *a, const ble_addr_t *b) +{ + int type_diff; + + type_diff = a->type - b->type; + if (type_diff != 0) { + return type_diff; + } + + return memcmp(a->val, b->val, sizeof(a->val)); +} + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_ */ diff --git a/libesp32/NimBLE-Arduino/src/nimble/ble_hci_trans.h b/libesp32/NimBLE-Arduino/src/nimble/ble_hci_trans.h new file mode 100644 index 000000000..e8d3ec031 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/ble_hci_trans.h @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_HCI_TRANSPORT_ +#define H_HCI_TRANSPORT_ + +#include +#include "os/os_mempool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mbuf; + +#define BLE_HCI_TRANS_CMD_SZ 260 + +/*** Type of buffers for holding commands and events. */ +/** + * Controller-to-host event buffers. Events have one of two priorities: + * o Low-priority (BLE_HCI_TRANS_BUF_EVT_LO) + * o High-priority (BLE_HCI_TRANS_BUF_EVT_HI) + * + * Low-priority event buffers are only used for advertising reports. If there + * are no free low-priority event buffers, then an incoming advertising report + * will get dropped. + * + * High-priority event buffers are for everything except advertising reports. + * If there are no free high-priority event buffers, a request to allocate one + * will try to allocate a low-priority buffer instead. + * + * If you want all events to be given equal treatment, then you should allocate + * low-priority events only. + * + * Event priorities solve the problem of critical events getting dropped due to + * a flood of advertising reports. This solution is likely temporary: when + * HCI flow control is added, event priorities may become obsolete. + * + * Not all transports distinguish between low and high priority events. If the + * transport does not have separate settings for low and high buffer counts, + * then it treats all events with equal priority. + */ +#define BLE_HCI_TRANS_BUF_EVT_LO 1 +#define BLE_HCI_TRANS_BUF_EVT_HI 2 + +/* Host-to-controller command. */ +#define BLE_HCI_TRANS_BUF_CMD 3 + +/** Callback function types; executed when HCI packets are received. */ +typedef int ble_hci_trans_rx_cmd_fn(uint8_t *cmd, void *arg); +typedef int ble_hci_trans_rx_acl_fn(struct os_mbuf *om, void *arg); + +/** + * Sends an HCI event from the controller to the host. + * + * @param cmd The HCI event to send. This buffer must be + * allocated via ble_hci_trans_buf_alloc(). + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int ble_hci_trans_ll_evt_tx(uint8_t *hci_ev); + +/** + * Sends ACL data from controller to host. + * + * @param om The ACL data packet to send. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int ble_hci_trans_ll_acl_tx(struct os_mbuf *om); + +/** + * Sends an HCI command from the host to the controller. + * + * @param cmd The HCI command to send. This buffer must be + * allocated via ble_hci_trans_buf_alloc(). + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int ble_hci_trans_hs_cmd_tx(uint8_t *cmd); + +/** + * Sends ACL data from host to controller. + * + * @param om The ACL data packet to send. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int ble_hci_trans_hs_acl_tx(struct os_mbuf *om); + +/** + * Allocates a flat buffer of the specified type. + * + * @param type The type of buffer to allocate; one of the + * BLE_HCI_TRANS_BUF_[...] constants. + * + * @return The allocated buffer on success; + * NULL on buffer exhaustion. + */ +uint8_t *ble_hci_trans_buf_alloc(int type); + +/** + * Frees the specified flat buffer. The buffer must have been allocated via + * ble_hci_trans_buf_alloc(). + * + * @param buf The buffer to free. + */ +void ble_hci_trans_buf_free(uint8_t *buf); + +/** + * Configures a callback to get executed whenever an ACL data packet is freed. + * The function is called immediately before the free occurs. + * + * @param cb The callback to configure. + * @param arg An optional argument to pass to the callback. + * + * @return 0 on success; + * BLE_ERR_UNSUPPORTED if the transport does not + * support this operation. + */ +int ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg); + +/** + * Configures the HCI transport to operate with a controller. The transport + * will execute specified callbacks upon receiving HCI packets from the host. + * + * @param cmd_cb The callback to execute upon receiving an HCI + * command. + * @param cmd_arg Optional argument to pass to the command + * callback. + * @param acl_cb The callback to execute upon receiving ACL + * data. + * @param acl_arg Optional argument to pass to the ACL + * callback. + */ +void ble_hci_trans_cfg_ll(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg); + +/** + * Configures the HCI transport to operate with a host. The transport will + * execute specified callbacks upon receiving HCI packets from the controller. + * + * @param evt_cb The callback to execute upon receiving an HCI + * event. + * @param evt_arg Optional argument to pass to the event + * callback. + * @param acl_cb The callback to execute upon receiving ACL + * data. + * @param acl_arg Optional argument to pass to the ACL + * callback. + */ +void ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *evt_cb, + void *evt_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg); + +/** + * Resets the HCI module to a clean state. Frees all buffers and reinitializes + * the underlying transport. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int ble_hci_trans_reset(void); + +#ifdef __cplusplus +} +#endif + +#endif /* H_HCI_TRANSPORT_ */ diff --git a/libesp32/NimBLE-Arduino/src/nimble/hci_common.h b/libesp32/NimBLE-Arduino/src/nimble/hci_common.h new file mode 100644 index 000000000..1b049c88b --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/hci_common.h @@ -0,0 +1,1254 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HCI_COMMON_ +#define H_BLE_HCI_COMMON_ + +#include "ble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * HCI Command Header + * + * Comprises the following fields + * -> Opcode group field & Opcode command field (2) + * -> Parameter Length (1) + * Length of all the parameters (does not include any part of the hci + * command header + */ +#define BLE_HCI_CMD_HDR_LEN (3) + +#define BLE_HCI_OPCODE_NOP (0) + +/* Set opcode based on OCF and OGF */ +#define BLE_HCI_OP(ogf, ocf) ((ocf) | ((ogf) << 10)) + +/* Get the OGF and OCF from the opcode in the command */ +#define BLE_HCI_OGF(opcode) (((opcode) >> 10) & 0x003F) +#define BLE_HCI_OCF(opcode) ((opcode) & 0x03FF) + +/* Opcode Group definitions (note: 0x07 not defined in spec) */ +#define BLE_HCI_OGF_LINK_CTRL (0x01) +#define BLE_HCI_OGF_LINK_POLICY (0x02) +#define BLE_HCI_OGF_CTLR_BASEBAND (0x03) +#define BLE_HCI_OGF_INFO_PARAMS (0x04) +#define BLE_HCI_OGF_STATUS_PARAMS (0x05) +#define BLE_HCI_OGF_TESTING (0x06) +#define BLE_HCI_OGF_LE (0x08) +#define BLE_HCI_OGF_VENDOR (0x3F) + +/* + * Number of LE commands. NOTE: this is really just used to size the array + * containing the lengths of the LE commands. + */ +#define BLE_HCI_NUM_LE_CMDS (79) + +/* List of OCF for Link Control commands (OGF=0x01) */ +#define BLE_HCI_OCF_DISCONNECT_CMD (0x0006) +#define BLE_HCI_OCF_RD_REM_VER_INFO (0x001D) + +/* List of OCF for Controller and Baseband commands (OGF=0x03) */ +#define BLE_HCI_OCF_CB_SET_EVENT_MASK (0x0001) +#define BLE_HCI_OCF_CB_RESET (0x0003) +#define BLE_HCI_OCF_CB_READ_TX_PWR (0x002D) +#define BLE_HCI_OCF_CB_SET_CTLR_TO_HOST_FC (0x0031) +#define BLE_HCI_OCF_CB_HOST_BUF_SIZE (0x0033) +#define BLE_HCI_OCF_CB_HOST_NUM_COMP_PKTS (0x0035) +#define BLE_HCI_OCF_CB_SET_EVENT_MASK2 (0x0063) +#define BLE_HCI_OCF_CB_RD_AUTH_PYLD_TMO (0x007B) +#define BLE_HCI_OCF_CB_WR_AUTH_PYLD_TMO (0x007C) + +/* List of OCF for Info Param commands (OGF=0x04) */ +#define BLE_HCI_OCF_IP_RD_LOCAL_VER (0x0001) +#define BLE_HCI_OCF_IP_RD_LOC_SUPP_CMD (0x0002) +#define BLE_HCI_OCF_IP_RD_LOC_SUPP_FEAT (0x0003) +#define BLE_HCI_OCF_IP_RD_BUF_SIZE (0x0005) +#define BLE_HCI_OCF_IP_RD_BD_ADDR (0x0009) + +/* List of OCF for Status parameters commands (OGF = 0x05) */ +#define BLE_HCI_OCF_RD_RSSI (0x0005) + +/* List of OCF for LE commands (OGF = 0x08) */ +#define BLE_HCI_OCF_LE_SET_EVENT_MASK (0x0001) +#define BLE_HCI_OCF_LE_RD_BUF_SIZE (0x0002) +#define BLE_HCI_OCF_LE_RD_LOC_SUPP_FEAT (0x0003) +/* NOTE: 0x0004 is intentionally left undefined */ +#define BLE_HCI_OCF_LE_SET_RAND_ADDR (0x0005) +#define BLE_HCI_OCF_LE_SET_ADV_PARAMS (0x0006) +#define BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR (0x0007) +#define BLE_HCI_OCF_LE_SET_ADV_DATA (0x0008) +#define BLE_HCI_OCF_LE_SET_SCAN_RSP_DATA (0x0009) +#define BLE_HCI_OCF_LE_SET_ADV_ENABLE (0x000A) +#define BLE_HCI_OCF_LE_SET_SCAN_PARAMS (0x000B) +#define BLE_HCI_OCF_LE_SET_SCAN_ENABLE (0x000C) +#define BLE_HCI_OCF_LE_CREATE_CONN (0x000D) +#define BLE_HCI_OCF_LE_CREATE_CONN_CANCEL (0x000E) +#define BLE_HCI_OCF_LE_RD_WHITE_LIST_SIZE (0x000F) +#define BLE_HCI_OCF_LE_CLEAR_WHITE_LIST (0x0010) +#define BLE_HCI_OCF_LE_ADD_WHITE_LIST (0x0011) +#define BLE_HCI_OCF_LE_RMV_WHITE_LIST (0x0012) +#define BLE_HCI_OCF_LE_CONN_UPDATE (0x0013) +#define BLE_HCI_OCF_LE_SET_HOST_CHAN_CLASS (0x0014) +#define BLE_HCI_OCF_LE_RD_CHAN_MAP (0x0015) +#define BLE_HCI_OCF_LE_RD_REM_FEAT (0x0016) +#define BLE_HCI_OCF_LE_ENCRYPT (0x0017) +#define BLE_HCI_OCF_LE_RAND (0x0018) +#define BLE_HCI_OCF_LE_START_ENCRYPT (0x0019) +#define BLE_HCI_OCF_LE_LT_KEY_REQ_REPLY (0x001A) +#define BLE_HCI_OCF_LE_LT_KEY_REQ_NEG_REPLY (0x001B) +#define BLE_HCI_OCF_LE_RD_SUPP_STATES (0x001C) +#define BLE_HCI_OCF_LE_RX_TEST (0x001D) +#define BLE_HCI_OCF_LE_TX_TEST (0x001E) +#define BLE_HCI_OCF_LE_TEST_END (0x001F) +#define BLE_HCI_OCF_LE_REM_CONN_PARAM_RR (0x0020) +#define BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR (0x0021) +#define BLE_HCI_OCF_LE_SET_DATA_LEN (0x0022) +#define BLE_HCI_OCF_LE_RD_SUGG_DEF_DATA_LEN (0x0023) +#define BLE_HCI_OCF_LE_WR_SUGG_DEF_DATA_LEN (0x0024) +#define BLE_HCI_OCF_LE_RD_P256_PUBKEY (0x0025) +#define BLE_HCI_OCF_LE_GEN_DHKEY (0x0026) +#define BLE_HCI_OCF_LE_ADD_RESOLV_LIST (0x0027) +#define BLE_HCI_OCF_LE_RMV_RESOLV_LIST (0x0028) +#define BLE_HCI_OCF_LE_CLR_RESOLV_LIST (0x0029) +#define BLE_HCI_OCF_LE_RD_RESOLV_LIST_SIZE (0x002A) +#define BLE_HCI_OCF_LE_RD_PEER_RESOLV_ADDR (0x002B) +#define BLE_HCI_OCF_LE_RD_LOCAL_RESOLV_ADDR (0x002C) +#define BLE_HCI_OCF_LE_SET_ADDR_RES_EN (0x002D) +#define BLE_HCI_OCF_LE_SET_RPA_TMO (0x002E) +#define BLE_HCI_OCF_LE_RD_MAX_DATA_LEN (0x002F) +#define BLE_HCI_OCF_LE_RD_PHY (0x0030) +#define BLE_HCI_OCF_LE_SET_DEFAULT_PHY (0x0031) +#define BLE_HCI_OCF_LE_SET_PHY (0x0032) +#define BLE_HCI_OCF_LE_ENH_RX_TEST (0x0033) +#define BLE_HCI_OCF_LE_ENH_TX_TEST (0x0034) +#define BLE_HCI_OCF_LE_SET_ADV_SET_RND_ADDR (0x0035) +#define BLE_HCI_OCF_LE_SET_EXT_ADV_PARAM (0x0036) +#define BLE_HCI_OCF_LE_SET_EXT_ADV_DATA (0x0037) +#define BLE_HCI_OCF_LE_SET_EXT_SCAN_RSP_DATA (0x0038) +#define BLE_HCI_OCF_LE_SET_EXT_ADV_ENABLE (0x0039) +#define BLE_HCI_OCF_LE_RD_MAX_ADV_DATA_LEN (0x003A) +#define BLE_HCI_OCF_LE_RD_NUM_OF_ADV_SETS (0x003B) +#define BLE_HCI_OCF_LE_REMOVE_ADV_SET (0x003C) +#define BLE_HCI_OCF_LE_CLEAR_ADV_SETS (0x003D) +#define BLE_HCI_OCF_LE_SET_PERIODIC_ADV_PARAMS (0x003E) +#define BLE_HCI_OCF_LE_SET_PERIODIC_ADV_DATA (0x003F) +#define BLE_HCI_OCF_LE_SET_PERIODIC_ADV_ENABLE (0x0040) +#define BLE_HCI_OCF_LE_SET_EXT_SCAN_PARAM (0x0041) +#define BLE_HCI_OCF_LE_SET_EXT_SCAN_ENABLE (0x0042) +#define BLE_HCI_OCF_LE_EXT_CREATE_CONN (0x0043) +#define BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC (0x0044) +#define BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC_CANCEL (0x0045) +#define BLE_HCI_OCF_LE_PERIODIC_ADV_TERM_SYNC (0x0046) +#define BLE_HCI_OCF_LE_ADD_DEV_TO_PERIODIC_ADV_LIST (0x0047) +#define BLE_HCI_OCF_LE_REM_DEV_FROM_PERIODIC_ADV_LIST (0x0048) +#define BLE_HCI_OCF_LE_CLEAR_PERIODIC_ADV_LIST (0x0049) +#define BLE_HCI_OCF_LE_RD_PERIODIC_ADV_LIST_SIZE (0x004A) +#define BLE_HCI_OCF_LE_RD_TRANSMIT_POWER (0x004B) +#define BLE_HCI_OCF_LE_RD_RF_PATH_COMPENSATION (0x004C) +#define BLE_HCI_OCF_LE_WR_RF_PATH_COMPENSATION (0x004D) +#define BLE_HCI_OCF_LE_SET_PRIVACY_MODE (0x004E) + +/* Command Specific Definitions */ +#define BLE_HCI_VARIABLE_LEN (0xFF) + +/* --- Disconnect command (OGF 0x01, OCF 0x0006) --- */ +#define BLE_HCI_DISCONNECT_CMD_LEN (3) + +/* --- Set event mask (OGF 0x03, OCF 0x0001 --- */ +#define BLE_HCI_SET_EVENT_MASK_LEN (8) + +/* --- Set controller to host flow control (OGF 0x03, OCF 0x0031) --- */ +#define BLE_HCI_CTLR_TO_HOST_FC_LEN (1) + +#define BLE_HCI_CTLR_TO_HOST_FC_OFF (0) +#define BLE_HCI_CTLR_TO_HOST_FC_ACL (1) +#define BLE_HCI_CTLR_TO_HOST_FC_SYNC (2) +#define BLE_HCI_CTLR_TO_HOST_FC_BOTH (3) + +/* --- Host buffer size (OGF 0x03, OCF 0x0033) --- */ +#define BLE_HCI_HOST_BUF_SIZE_LEN (7) + +/* --- Host number of completed packets (OGF 0x03, OCF 0x0035) --- */ +#define BLE_HCI_HOST_NUM_COMP_PKTS_HDR_LEN (1) +#define BLE_HCI_HOST_NUM_COMP_PKTS_ENT_LEN (4) + +/* --- Read BD_ADDR (OGF 0x04, OCF 0x0009 --- */ +#define BLE_HCI_IP_RD_BD_ADDR_ACK_PARAM_LEN (6) + +/* --- Read buffer size (OGF 0x04, OCF 0x0005) --- */ +#define BLE_HCI_IP_RD_BUF_SIZE_LEN (0) +#define BLE_HCI_IP_RD_BUF_SIZE_RSPLEN (7) /* No status byte. */ + +/* --- Read/Write authenticated payload timeout (ocf 0x007B/0x007C) */ +#define BLE_HCI_RD_AUTH_PYLD_TMO_LEN (4) +#define BLE_HCI_WR_AUTH_PYLD_TMO_LEN (2) + +/* --- Read local version information (OGF 0x04, OCF 0x0001) --- */ +#define BLE_HCI_RD_LOC_VER_INFO_RSPLEN (8) /* No status byte. */ + +/* --- Read local supported command (OGF 0x04, OCF 0x0002) --- */ +#define BLE_HCI_RD_LOC_SUPP_CMD_RSPLEN (64) /* No status byte. */ + +/* --- Read local supported features (OGF 0x04, OCF 0x0003) --- */ +#define BLE_HCI_RD_LOC_SUPP_FEAT_RSPLEN (8) /* No status byte. */ + +/* --- Read RSSI (OGF 0x05, OCF 0x0005) --- */ +#define BLE_HCI_READ_RSSI_LEN (2) +#define BLE_HCI_READ_RSSI_ACK_PARAM_LEN (3) /* No status byte. */ + +/* --- LE set event mask (OCF 0x0001) --- */ +#define BLE_HCI_SET_LE_EVENT_MASK_LEN (8) + +/* --- LE read buffer size (OCF 0x0002) --- */ +#define BLE_HCI_RD_BUF_SIZE_LEN (0) +#define BLE_HCI_RD_BUF_SIZE_RSPLEN (3) /* No status byte. */ + +/* --- LE read local supported features (OCF 0x0003) --- */ +#define BLE_HCI_RD_LE_LOC_SUPP_FEAT_RSPLEN (8) /* No status byte. */ + +/* --- LE set random address (OCF 0x0005) */ +#define BLE_HCI_SET_RAND_ADDR_LEN (6) + +/* --- LE set advertising parameters (OCF 0x0006) */ +#define BLE_HCI_SET_ADV_PARAM_LEN (15) + +/* Advertising types */ +#define BLE_HCI_ADV_TYPE_ADV_IND (0) +#define BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) +#define BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) +#define BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) +#define BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) +#define BLE_HCI_ADV_TYPE_MAX (4) + +#define BLE_HCI_ADV_CONN_MASK (0x0001) +#define BLE_HCI_ADV_SCAN_MASK (0x0002) +#define BLE_HCI_ADV_DIRECT_MASK (0x0004) +#define BLE_HCI_ADV_SCAN_RSP_MASK (0x0008) +#define BLE_HCI_ADV_LEGACY_MASK (0x0010) + +#define BLE_HCI_ADV_DATA_STATUS_COMPLETE (0x0000) +#define BLE_HCI_ADV_DATA_STATUS_INCOMPLETE (0x0020) +#define BLE_HCI_ADV_DATA_STATUS_TRUNCATED (0x0040) +#define BLE_HCI_ADV_DATA_STATUS_MASK (0x0060) + +/* Own address types */ +#define BLE_HCI_ADV_OWN_ADDR_PUBLIC (0) +#define BLE_HCI_ADV_OWN_ADDR_RANDOM (1) +#define BLE_HCI_ADV_OWN_ADDR_PRIV_PUB (2) +#define BLE_HCI_ADV_OWN_ADDR_PRIV_RAND (3) +#define BLE_HCI_ADV_OWN_ADDR_MAX (3) + +/* Advertisement peer address Type */ +#define BLE_HCI_ADV_PEER_ADDR_PUBLIC (0) +#define BLE_HCI_ADV_PEER_ADDR_RANDOM (1) +#define BLE_HCI_ADV_PEER_ADDR_MAX (1) + +/* --- LE advertising channel tx power (OCF 0x0007) */ +#define BLE_HCI_ADV_CHAN_TXPWR_ACK_PARAM_LEN (2) /* Includes status byte. */ +#define BLE_HCI_ADV_CHAN_TXPWR_MIN (-20) +#define BLE_HCI_ADV_CHAN_TXPWR_MAX (10) + +/* --- LE set advertising data (OCF 0x0008) */ +#define BLE_HCI_MAX_ADV_DATA_LEN (31) +#define BLE_HCI_SET_ADV_DATA_LEN (32) + +/* --- LE set scan response data (OCF 0x0009) */ +#define BLE_HCI_MAX_SCAN_RSP_DATA_LEN (31) +#define BLE_HCI_SET_SCAN_RSP_DATA_LEN (32) + +/* --- LE set advertising enable (OCF 0x000a) */ +#define BLE_HCI_SET_ADV_ENABLE_LEN (1) + +/* --- LE set scan enable (OCF 0x000c) */ +#define BLE_HCI_SET_SCAN_ENABLE_LEN (2) + +/* Connect peer address type */ +#define BLE_HCI_CONN_PEER_ADDR_PUBLIC (0) +#define BLE_HCI_CONN_PEER_ADDR_RANDOM (1) +#define BLE_HCI_CONN_PEER_ADDR_PUBLIC_IDENT (2) +#define BLE_HCI_CONN_PEER_ADDR_RANDOM_IDENT (3) +#define BLE_HCI_CONN_PEER_ADDR_MAX (3) + +/* + * Advertising filter policy + * + * Determines how an advertiser filters scan and connection requests. + * + * NONE: no filtering (default value). No whitelist used. + * SCAN: process all connection requests but only scans from white list. + * CONN: process all scan request but only connection requests from white list + * BOTH: ignore all scan and connection requests unless in white list. + */ +#define BLE_HCI_ADV_FILT_NONE (0) +#define BLE_HCI_ADV_FILT_SCAN (1) +#define BLE_HCI_ADV_FILT_CONN (2) +#define BLE_HCI_ADV_FILT_BOTH (3) +#define BLE_HCI_ADV_FILT_MAX (3) + +#define BLE_HCI_ADV_FILT_DEF (BLE_HCI_ADV_FILT_NONE) + +/* Advertising interval */ +#define BLE_HCI_ADV_ITVL (625) /* usecs */ +#define BLE_HCI_ADV_ITVL_MIN (32) /* units */ +#define BLE_HCI_ADV_ITVL_MAX (16384) /* units */ +#define BLE_HCI_ADV_ITVL_NONCONN_MIN (160) /* units */ + +#define BLE_HCI_ADV_ITVL_DEF (0x800) /* 1.28 seconds */ +#define BLE_HCI_ADV_CHANMASK_DEF (0x7) /* all channels */ + +/* Set scan parameters */ +#define BLE_HCI_SET_SCAN_PARAM_LEN (7) +#define BLE_HCI_SCAN_TYPE_PASSIVE (0) +#define BLE_HCI_SCAN_TYPE_ACTIVE (1) + +/* Scan interval and scan window timing */ +#define BLE_HCI_SCAN_ITVL (625) /* usecs */ +#define BLE_HCI_SCAN_ITVL_MIN (4) /* units */ +#define BLE_HCI_SCAN_ITVL_MAX (16384) /* units */ +#define BLE_HCI_SCAN_ITVL_DEF (16) /* units */ +#define BLE_HCI_SCAN_WINDOW_MIN (4) /* units */ +#define BLE_HCI_SCAN_WINDOW_MAX (16384) /* units */ +#define BLE_HCI_SCAN_WINDOW_DEF (16) /* units */ + +/* + * Scanning filter policy + * NO_WL: + * Scanner processes all advertising packets (white list not used) except + * directed, connectable advertising packets not sent to the scanner. + * USE_WL: + * Scanner processes advertisements from white list only. A connectable, + * directed advertisment is ignored unless it contains scanners address. + * NO_WL_INITA: + * Scanner process all advertising packets (white list not used). A + * connectable, directed advertisement shall not be ignored if the InitA + * is a resolvable private address. + * USE_WL_INITA: + * Scanner process advertisements from white list only. A connectable, + * directed advertisement shall not be ignored if the InitA is a + * resolvable private address. + */ +#define BLE_HCI_SCAN_FILT_NO_WL (0) +#define BLE_HCI_SCAN_FILT_USE_WL (1) +#define BLE_HCI_SCAN_FILT_NO_WL_INITA (2) +#define BLE_HCI_SCAN_FILT_USE_WL_INITA (3) +#define BLE_HCI_SCAN_FILT_MAX (3) + +/* Whitelist commands */ +#define BLE_HCI_ADD_WHITE_LIST_LEN (7) +#define BLE_HCI_RMV_WHITE_LIST_LEN (7) + +/* Create Connection */ +#define BLE_HCI_CREATE_CONN_LEN (25) +#define BLE_HCI_CONN_ITVL (1250) /* usecs */ +#define BLE_HCI_CONN_FILT_NO_WL (0) +#define BLE_HCI_CONN_FILT_USE_WL (1) +#define BLE_HCI_CONN_FILT_MAX (1) +#define BLE_HCI_CONN_ITVL_MIN (0x0006) +#define BLE_HCI_CONN_ITVL_MAX (0x0c80) +#define BLE_HCI_CONN_LATENCY_MIN (0x0000) +#define BLE_HCI_CONN_LATENCY_MAX (0x01f3) +#define BLE_HCI_CONN_SPVN_TIMEOUT_MIN (0x000a) +#define BLE_HCI_CONN_SPVN_TIMEOUT_MAX (0x0c80) +#define BLE_HCI_CONN_SPVN_TMO_UNITS (10) /* msecs */ +#define BLE_HCI_INITIATOR_FILT_POLICY_MAX (1) + +/* Peer Address Type */ +#define BLE_HCI_CONN_PEER_ADDR_PUBLIC (0) +#define BLE_HCI_CONN_PEER_ADDR_RANDOM (1) +#define BLE_HCI_CONN_PEER_ADDR_PUB_ID (2) +#define BLE_HCI_CONN_PEER_ADDR_RAND_ID (3) +#define BLE_HCI_CONN_PEER_ADDR_MAX (3) + +/* --- LE connection update (OCF 0x0013) */ +#define BLE_HCI_CONN_UPDATE_LEN (14) + +/* --- LE set host channel classification command (OCF 0x0014) */ +#define BLE_HCI_SET_HOST_CHAN_CLASS_LEN (5) + +/* --- LE read channel map command (OCF 0x0015) */ +#define BLE_HCI_RD_CHANMAP_LEN (2) +#define BLE_HCI_RD_CHANMAP_RSP_LEN (7) + +/* --- LE read remote features (OCF 0x0016) */ +#define BLE_HCI_CONN_RD_REM_FEAT_LEN (2) + +/* --- LE encrypt (OCF 0x0017) */ +#define BLE_HCI_LE_ENCRYPT_LEN (32) + +/* --- LE rand (OCF 0x0018) */ +#define BLE_HCI_LE_RAND_LEN (8) + +/* --- LE start encryption (OCF 0x0019) */ +#define BLE_HCI_LE_START_ENCRYPT_LEN (28) + +/* --- LE long term key request reply command (OCF 0x001a) */ +#define BLE_HCI_LT_KEY_REQ_REPLY_LEN (18) +#define BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN (2) /* No status byte. */ + +/* --- LE long term key request negative reply command (OCF 0x001b) */ +#define BLE_HCI_LT_KEY_REQ_NEG_REPLY_LEN (2) +#define BLE_HCI_LT_KEY_REQ_NEG_REPLY_ACK_PARAM_LEN (2) + +/* --- LE read supported states (OCF 0x001C) --- */ +#define BLE_HCI_RD_SUPP_STATES_RSPLEN (8) + +/* --- LE receiver test command (OCF 0x001D) --- */ +#define BLE_HCI_RX_TEST_LEN (1) + +/* --- LE transitter test command (OCF 0x001E) --- */ +#define BLE_HCI_TX_TEST_LEN (3) + +/* --- LE remote connection parameter request reply (OCF 0x0020) */ +#define BLE_HCI_CONN_PARAM_REPLY_LEN (14) + +/* --- LE remote connection parameter request negative reply (OCF 0x0021) */ +#define BLE_HCI_CONN_PARAM_NEG_REPLY_LEN (3) + +/* --- LE set data length (OCF 0x0022) */ +#define BLE_HCI_SET_DATALEN_LEN (6) +#define BLE_HCI_SET_DATALEN_ACK_PARAM_LEN (2) /* No status byte. */ +#define BLE_HCI_SET_DATALEN_TX_OCTETS_MIN (0x001b) +#define BLE_HCI_SET_DATALEN_TX_OCTETS_MAX (0x00fb) +#define BLE_HCI_SET_DATALEN_TX_TIME_MIN (0x0148) +#define BLE_HCI_SET_DATALEN_TX_TIME_MAX (0x4290) + +/* --- LE read suggested default data length (OCF 0x0023) */ +#define BLE_HCI_RD_SUGG_DATALEN_RSPLEN (4) + +/* --- LE write suggested default data length (OCF 0x0024) */ +#define BLE_HCI_WR_SUGG_DATALEN_LEN (4) + +/* --- LE generate DHKEY command (OCF 0x0026) */ +#define BLE_HCI_GEN_DHKEY_LEN (64) + +/* --- LE add device to resolving list (OCF 0x0027) */ +#define BLE_HCI_ADD_TO_RESOLV_LIST_LEN (39) + +/* --- LE add device to resolving list (OCF 0x0028) */ +#define BLE_HCI_RMV_FROM_RESOLV_LIST_LEN (7) + +/* --- LE read peer resolvable address (OCF 0x002B) */ +#define BLE_HCI_RD_PEER_RESOLV_ADDR_LEN (7) + +/* --- LE read peer resolvable address (OCF 0x002C) */ +#define BLE_HCI_RD_LOC_RESOLV_ADDR_LEN (7) + +/* --- LE set address resolution enable (OCF 0x002D) */ +#define BLE_HCI_SET_ADDR_RESOL_ENA_LEN (1) + +/* --- LE set resolvable private address timeout (OCF 0x002E) */ +#define BLE_HCI_SET_RESOLV_PRIV_ADDR_TO_LEN (2) + +/* --- LE read maximum data length (OCF 0x002F) */ +#define BLE_HCI_RD_MAX_DATALEN_RSPLEN (8) + +/* --- LE read maximum default PHY (OCF 0x0030) */ +#define BLE_HCI_LE_RD_PHY_LEN (2) +#define BLE_HCI_LE_RD_PHY_RSPLEN (4) +#define BLE_HCI_LE_PHY_1M (1) +#define BLE_HCI_LE_PHY_2M (2) +#define BLE_HCI_LE_PHY_CODED (3) + +/* --- LE set default PHY (OCF 0x0031) */ +#define BLE_HCI_LE_SET_DEFAULT_PHY_LEN (3) +#define BLE_HCI_LE_PHY_NO_TX_PREF_MASK (0x01) +#define BLE_HCI_LE_PHY_NO_RX_PREF_MASK (0x02) +#define BLE_HCI_LE_PHY_1M_PREF_MASK (0x01) +#define BLE_HCI_LE_PHY_2M_PREF_MASK (0x02) +#define BLE_HCI_LE_PHY_CODED_PREF_MASK (0x04) + +#define BLE_HCI_LE_PHY_PREF_MASK_ALL \ + (BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK | \ + BLE_HCI_LE_PHY_CODED_PREF_MASK) + +/* --- LE set PHY (OCF 0x0032) */ +#define BLE_HCI_LE_SET_PHY_LEN (7) +#define BLE_HCI_LE_PHY_CODED_ANY (0x0000) +#define BLE_HCI_LE_PHY_CODED_S2_PREF (0x0001) +#define BLE_HCI_LE_PHY_CODED_S8_PREF (0x0002) + +/* --- LE enhanced receiver test (OCF 0x0033) */ +#define BLE_HCI_LE_ENH_RX_TEST_LEN (3) +#define BLE_HCI_LE_PHY_1M (1) +#define BLE_HCI_LE_PHY_2M (2) +#define BLE_HCI_LE_PHY_CODED (3) + +/* --- LE enhanced transmitter test (OCF 0x0034) */ +#define BLE_HCI_LE_ENH_TX_TEST_LEN (4) +#define BLE_HCI_LE_PHY_CODED_S8 (3) +#define BLE_HCI_LE_PHY_CODED_S2 (4) + +/* --- LE set advertising set random address (OCF 0x0035) */ +#define BLE_HCI_LE_SET_ADV_SET_RND_ADDR_LEN (7) + +/* --- LE set extended advertising parameters (OCF 0x0036) */ +#define BLE_HCI_LE_SET_EXT_ADV_PARAM_LEN (25) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE (0x0001) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE (0x0002) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED (0x0004) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED (0x0008) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY (0x0010) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV (0x0020) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_INC_TX_PWR (0x0040) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_MASK (0x7F) + +#define BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_IND (0x0013) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_LD_DIR (0x0015) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_HD_DIR (0x001d) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_SCAN (0x0012) +#define BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_NONCONN (0x0010) + +/* --- LE set extended advertising data (OCF 0x0037) */ +#define BLE_HCI_MAX_EXT_ADV_DATA_LEN (251) +#define BLE_HCI_SET_EXT_ADV_DATA_HDR_LEN (4) + +#define BLE_HCI_LE_SET_EXT_ADV_DATA_LEN BLE_HCI_VARIABLE_LEN +#define BLE_HCI_LE_SET_DATA_OPER_INT (0) +#define BLE_HCI_LE_SET_DATA_OPER_FIRST (1) +#define BLE_HCI_LE_SET_DATA_OPER_LAST (2) +#define BLE_HCI_LE_SET_DATA_OPER_COMPLETE (3) +#define BLE_HCI_LE_SET_DATA_OPER_UNCHANGED (4) + +/* --- LE set extended scan response data (OCF 0x0038) */ +#define BLE_HCI_MAX_EXT_SCAN_RSP_DATA_LEN (251) +#define BLE_HCI_SET_EXT_SCAN_RSP_DATA_HDR_LEN (4) + +#define BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_LEN BLE_HCI_VARIABLE_LEN + + +/* --- LE set extended advertising enable (OCF 0x0039) */ +#define BLE_HCI_LE_SET_EXT_ADV_ENABLE_LEN BLE_HCI_VARIABLE_LEN + +/* --- LE remove advertising set (OCF 0x003C) */ +#define BLE_HCI_LE_REMOVE_ADV_SET_LEN (1) + +/* --- LE read maximum advertising data length (OCF 0x003A) */ +#define BLE_HCI_RD_MAX_ADV_DATA_LEN (2) + +/* --- LE read number of supported advertising sets (OCF 0x003B) */ +#define BLE_HCI_RD_NR_SUP_ADV_SETS (1) + +/* --- LE set periodic advertising parameters (OCF 0x003E) */ +#define BLE_HCI_LE_SET_PERIODIC_ADV_PARAMS_LEN (7) +#define BLE_HCI_LE_SET_PERIODIC_ADV_PROP_INC_TX_PWR (0x0040) +#define BLE_HCI_LE_SET_PERIODIC_ADV_PROP_MASK (0x0040) + +/* --- LE set periodic advertising data (OCF 0x003F) */ +#define BLE_HCI_LE_SET_PERIODIC_ADV_DATA_LEN BLE_HCI_VARIABLE_LEN +#define BLE_HCI_MAX_PERIODIC_ADV_DATA_LEN (252) +#define BLE_HCI_SET_PERIODIC_ADV_DATA_HDR_LEN (3) + +/* --- LE periodic advertising enable (OCF 0x0040) */ +#define BLE_HCI_LE_SET_PERIODIC_ADV_ENABLE_LEN (2) + +/* --- LE set extended scan parameters (OCF 0x0041) */ +#define BLE_HCI_LE_SET_EXT_SCAN_PARAM_LEN BLE_HCI_VARIABLE_LEN +#define BLE_HCI_LE_EXT_SCAN_BASE_LEN (3) +#define BLE_HCI_LE_EXT_SCAN_SINGLE_PARAM_LEN (5) + +/* --- LE set extended scan enable (OCF 0x0042) */ +#define BLE_HCI_LE_SET_EXT_SCAN_ENABLE_LEN (6) + +/* --- LE extended create connection (OCF 0x0043) */ +#define BLE_HCI_LE_EXT_CREATE_CONN_LEN BLE_HCI_VARIABLE_LEN +#define BLE_HCI_LE_EXT_CREATE_CONN_BASE_LEN (10) + +/* --- LE periodic advertising create sync (OCF 0x0044) */ +#define BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_LEN (14) + +/* --- LE periodic advertising terminate (OCF 0x0046) */ +#define BLE_HCI_LE_PERIODIC_ADV_TERM_SYNC_LEN (2) + +/* --- LE add device to periodic advertising list (OCF 0x0047) */ +#define BLE_HCI_LE_ADD_DEV_TO_PERIODIC_ADV_LIST_LEN (8) + +/* --- LE remove device from periodic advertising list (OCF 0x0048) */ +#define BLE_HCI_LE_REM_DEV_FROM_PERIODIC_ADV_LIST_LEN (8) + +#define BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE 0x00 +#define BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE 0x01 +#define BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED 0x02 + +/* --- LE write RF path (OCF 0x004D) */ +#define BLE_HCI_LE_WR_RF_PATH_COMPENSATION_LEN (4) + +/* --- LE set privacy mode (OCF 0x004E) */ +#define BLE_HCI_LE_SET_PRIVACY_MODE_LEN (8) +#define BLE_HCI_PRIVACY_NETWORK (0) +#define BLE_HCI_PRIVACY_DEVICE (1) + +/* Event Codes */ +#define BLE_HCI_EVCODE_INQUIRY_CMP (0x01) +#define BLE_HCI_EVCODE_INQUIRY_RESULT (0x02) +#define BLE_HCI_EVCODE_CONN_DONE (0x03) +#define BLE_HCI_EVCODE_CONN_REQUEST (0x04) +#define BLE_HCI_EVCODE_DISCONN_CMP (0x05) +#define BLE_HCI_EVCODE_AUTH_CMP (0x06) +#define BLE_HCI_EVCODE_REM_NAME_REQ_CMP (0x07) +#define BLE_HCI_EVCODE_ENCRYPT_CHG (0x08) +#define BLE_HCI_EVCODE_CHG_LINK_KEY_CMP (0x09) +#define BLE_HCI_EVCODE_MASTER_LINK_KEY_CMP (0x0A) +#define BLE_HCI_EVCODE_RD_REM_SUPP_FEAT_CMP (0x0B) +#define BLE_HCI_EVCODE_RD_REM_VER_INFO_CMP (0x0C) +#define BLE_HCI_EVCODE_QOS_SETUP_CMP (0x0D) +#define BLE_HCI_EVCODE_COMMAND_COMPLETE (0x0E) +#define BLE_HCI_EVCODE_COMMAND_STATUS (0x0F) +#define BLE_HCI_EVCODE_HW_ERROR (0x10) +#define BLE_HCI_EVCODE_NUM_COMP_PKTS (0x13) +#define BLE_HCI_EVCODE_MODE_CHANGE (0x14) +#define BLE_HCI_EVCODE_RETURN_LINK_KEYS (0x15) +#define BLE_HCI_EVCODE_PIN_CODE_REQ (0x16) +#define BLE_HCI_EVCODE_LINK_KEY_REQ (0x17) +#define BLE_HCI_EVCODE_LINK_KEY_NOTIFY (0x18) +#define BLE_HCI_EVCODE_LOOPBACK_CMD (0x19) +#define BLE_HCI_EVCODE_DATA_BUF_OVERFLOW (0x1A) +#define BLE_HCI_EVCODE_MAX_SLOTS_CHG (0x1B) +#define BLE_HCI_EVCODE_READ_CLK_OFF_COMP (0x1C) +#define BLE_HCI_EVCODE_CONN_PKT_TYPE_CHG (0x1D) +#define BLE_HCI_EVCODE_QOS_VIOLATION (0x1E) +/* NOTE: 0x1F not defined */ +#define BLE_HCI_EVCODE_PSR_MODE_CHG (0x20) +#define BLE_HCI_EVCODE_FLOW_SPEC_COMP (0x21) +#define BLE_HCI_EVCODE_INQ_RESULT_RSSI (0x22) +#define BLE_HCI_EVCODE_READ_REM_EXT_FEAT (0x23) +/* NOTE: 0x24 - 0x2B not defined */ +#define BLE_HCI_EVCODE_SYNCH_CONN_COMP (0x2C) +#define BLE_HCI_EVCODE_SYNCH_CONN_CHG (0x2D) +#define BLE_HCI_EVCODE_SNIFF_SUBRATING (0x2E) +#define BLE_HCI_EVCODE_EXT_INQ_RESULT (0x2F) +#define BLE_HCI_EVCODE_ENC_KEY_REFRESH (0x30) +#define BLE_HCI_EVOCDE_IO_CAP_REQ (0x31) +#define BLE_HCI_EVCODE_IO_CAP_RSP (0x32) +#define BLE_HCI_EVCODE_USER_CONFIRM_REQ (0x33) +#define BLE_HCI_EVCODE_PASSKEY_REQ (0x34) +#define BLE_HCI_EVCODE_REM_OOB_DATA_REQ (0x35) +#define BLE_HCI_EVCODE_SIMPLE_PAIR_COMP (0x36) +/* NOTE: 0x37 not defined */ +#define BLE_HCI_EVCODE_LNK_SPVN_TMO_CHG (0x38) +#define BLE_HCI_EVCODE_ENH_FLUSH_COMP (0x39) +#define BLE_HCI_EVCODE_USER_PASSKEY_NOTIFY (0x3B) +#define BLE_HCI_EVCODE_KEYPRESS_NOTIFY (0x3C) +#define BLE_HCI_EVCODE_REM_HOST_SUPP_FEAT (0x3D) +#define BLE_HCI_EVCODE_LE_META (0x3E) +/* NOTE: 0x3F not defined */ +#define BLE_HCI_EVCODE_PHYS_LINK_COMP (0x40) +#define BLE_HCI_EVCODE_CHAN_SELECTED (0x41) +#define BLE_HCI_EVCODE_DISCONN_PHYS_LINK (0x42) +#define BLE_HCI_EVCODE_PHYS_LINK_LOSS_EARLY (0x43) +#define BLE_HCI_EVCODE_PHYS_LINK_RECOVERY (0x44) +#define BLE_HCI_EVCODE_LOGICAL_LINK_COMP (0x45) +#define BLE_HCI_EVCODE_DISCONN_LOGICAL_LINK (0x46) +#define BLE_HCI_EVCODE_FLOW_SPEC_MODE_COMP (0x47) +#define BLE_HCI_EVCODE_NUM_COMP_DATA_BLKS (0x48) +#define BLE_HCI_EVCODE_AMP_START_TEST (0x49) +#define BLE_HCI_EVOCDE_AMP_TEST_END (0x4A) +#define BLE_HCI_EVOCDE_AMP_RCVR_REPORT (0x4B) +#define BLE_HCI_EVCODE_SHORT_RANGE_MODE_CHG (0x4C) +#define BLE_HCI_EVCODE_AMP_STATUS_CHG (0x4D) +#define BLE_HCI_EVCODE_TRIG_CLK_CAPTURE (0x4E) +#define BLE_HCI_EVCODE_SYNCH_TRAIN_COMP (0x4F) +#define BLE_HCI_EVCODE_SYNCH_TRAIN_RCVD (0x50) +#define BLE_HCI_EVCODE_SLAVE_BCAST_RX (0x51) +#define BLE_HCI_EVCODE_SLAVE_BCAST_TMO (0x52) +#define BLE_HCI_EVCODE_TRUNC_PAGE_COMP (0x53) +#define BLE_HCI_EVCODE_SLAVE_PAGE_RSP_TMO (0x54) +#define BLE_HCI_EVCODE_SLAVE_BCAST_CHAN_MAP (0x55) +#define BLE_HCI_EVCODE_INQ_RSP_NOTIFY (0x56) +#define BLE_HCI_EVCODE_AUTH_PYLD_TMO (0x57) +#define BLE_HCI_EVCODE_VENDOR_DEBUG (0xFF) + +/* LE sub-event codes */ +#define BLE_HCI_LE_SUBEV_CONN_COMPLETE (0x01) +#define BLE_HCI_LE_SUBEV_ADV_RPT (0x02) +#define BLE_HCI_LE_SUBEV_CONN_UPD_COMPLETE (0x03) +#define BLE_HCI_LE_SUBEV_RD_REM_USED_FEAT (0x04) +#define BLE_HCI_LE_SUBEV_LT_KEY_REQ (0x05) +#define BLE_HCI_LE_SUBEV_REM_CONN_PARM_REQ (0x06) +#define BLE_HCI_LE_SUBEV_DATA_LEN_CHG (0x07) +#define BLE_HCI_LE_SUBEV_RD_LOC_P256_PUBKEY (0x08) +#define BLE_HCI_LE_SUBEV_GEN_DHKEY_COMPLETE (0x09) +#define BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE (0x0A) +#define BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT (0x0B) +#define BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE (0x0C) +#define BLE_HCI_LE_SUBEV_EXT_ADV_RPT (0x0D) +#define BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_ESTAB (0x0E) +#define BLE_HCI_LE_SUBEV_PERIODIC_ADV_RPT (0x0F) +#define BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_LOST (0x10) +#define BLE_HCI_LE_SUBEV_SCAN_TIMEOUT (0x11) +#define BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED (0x12) +#define BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD (0x13) +#define BLE_HCI_LE_SUBEV_CHAN_SEL_ALG (0x14) + +/* Generic event header */ +#define BLE_HCI_EVENT_HDR_LEN (2) + +/* Event specific definitions */ +/* Event disconnect complete */ +#define BLE_HCI_EVENT_DISCONN_COMPLETE_LEN (4) + +/* Event encryption change (code=0x08) */ +#define BLE_HCI_EVENT_ENCRYPT_CHG_LEN (4) + +/* Event hardware error (code=0x10) */ +#define BLE_HCI_EVENT_HW_ERROR_LEN (1) + +/* Event key refresh complete (code=0x30) */ +#define BLE_HCI_EVENT_ENC_KEY_REFRESH_LEN (3) + +/* Event command complete */ +#define BLE_HCI_EVENT_CMD_COMPLETE_HDR_LEN (5) +#define BLE_HCI_EVENT_CMD_COMPLETE_MIN_LEN (6) + +/* Event command status */ +#define BLE_HCI_EVENT_CMD_STATUS_LEN (6) + +/* Number of completed packets */ +#define BLE_HCI_EVENT_NUM_COMP_PKTS_HDR_LEN (1) +#define BLE_HCI_EVENT_NUM_COMP_PKTS_ENT_LEN (4) + +/* Read remote version informaton */ +#define BLE_HCI_EVENT_RD_RM_VER_LEN (8) + +/* Data buffer overflow event */ +#define BLE_HCI_EVENT_DATABUF_OVERFLOW_LEN (1) +#define BLE_HCI_EVENT_ACL_BUF_OVERFLOW (0x01) + +/* Advertising report */ +#define BLE_HCI_ADV_RPT_EVTYPE_ADV_IND (0) +#define BLE_HCI_ADV_RPT_EVTYPE_DIR_IND (1) +#define BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND (2) +#define BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND (3) +#define BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP (4) + +/* Bluetooth 5, Vol 2, Part E, 7.7.65.13 */ +#define BLE_HCI_LEGACY_ADV_EVTYPE_ADV_IND (0x13) +#define BLE_HCI_LEGACY_ADV_EVTYPE_ADV_DIRECT_IND (0x15) +#define BLE_HCI_LEGACY_ADV_EVTYPE_ADV_SCAN_IND (0x12) +#define BLE_HCI_LEGACY_ADV_EVTYPE_ADV_NONCON_IND (0x10) +#define BLE_HCI_LEGACY_ADV_EVTYPE_SCAN_RSP_ADV_IND (0x1b) +#define BLE_HCI_LEGACY_ADV_EVTYPE_SCAN_RSP_ADV_SCAN_IND (0x1a) + +/* LE sub-event specific definitions */ +#define BLE_HCI_LE_MIN_LEN (1) /* Not including event hdr. */ + +/* LE connection complete event (sub event 0x01) */ +#define BLE_HCI_LE_CONN_COMPLETE_LEN (19) +#define BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER (0x00) +#define BLE_HCI_LE_CONN_COMPLETE_ROLE_SLAVE (0x01) + +/* Maximum valid connection handle value */ +#define BLE_HCI_LE_CONN_HANDLE_MAX (0x0eff) + +/* LE advertising report event. (sub event 0x02) */ +#define BLE_HCI_LE_ADV_RPT_MIN_LEN (12) +#define BLE_HCI_LE_ADV_DIRECT_RPT_LEN (18) +#define BLE_HCI_LE_ADV_RPT_NUM_RPTS_MIN (1) +#define BLE_HCI_LE_ADV_RPT_NUM_RPTS_MAX (0x19) + +/* Length of each record in an LE direct advertising report event. */ +#define BLE_HCI_LE_ADV_DIRECT_RPT_SUB_LEN (16) + +/* LE connection update complete event (sub event 0x03) */ +#define BLE_HCI_LE_CONN_UPD_LEN (10) + +/* LE long term key request event (sub event 0x05) */ +#define BLE_HCI_LE_LT_KEY_REQ_LEN (13) + +/* LE connection update complete event (sub event 0x03) */ +#define BLE_HCI_LE_RD_REM_USED_FEAT_LEN (12) + +/* LE remote connection parameter request event (sub event 0x06) */ +#define BLE_HCI_LE_REM_CONN_PARM_REQ_LEN (11) + +/* LE data length change event (sub event 0x07) */ +#define BLE_HCI_LE_DATA_LEN_CHG_LEN (11) + +/* LE PHY update complete event (sub event 0x0C) */ +#define BLE_HCI_LE_PHY_UPD_LEN (6) + +/* LE Periodic Advertising Sync Established Event (sub event 0x0e) */ +#define BLE_HCI_LE_PERIODIC_ADV_SYNC_ESTAB_LEN (16) + +/* LE Periodic Advertising Report Event (sub event 0x0f) */ +#define BLE_HCI_LE_PERIODIC_ADV_RPT_LEN (8) + +/* LE Periodic Advertising Sync Lost Event (sub event 0x10) */ +#define BLE_HCI_LE_PERIODIC_ADV_SYNC_LOST_LEN (3) + +/* LE Scan Timeout Event (sub event 0x11) */ +#define BLE_HCI_LE_SUBEV_SCAN_TIMEOUT_LEN (1) + +/* LE Advertising Set Terminated Event (sub event 0x12) */ +#define BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED_LEN (6) + +/* LE Scan Request Received event (sub event 0x13) */ +#define BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD_LEN (9) + +/* LE Channel Selection Algorithm event (sub event 0x14) */ +#define BLE_HCI_LE_SUBEV_CHAN_SEL_ALG_LEN (4) + +/* Bluetooth Assigned numbers for version information.*/ +#define BLE_HCI_VER_BCS_1_0b (0) +#define BLE_HCI_VER_BCS_1_1 (1) +#define BLE_HCI_VER_BCS_1_2 (2) +#define BLE_HCI_VER_BCS_2_0_EDR (3) +#define BLE_HCI_VER_BCS_2_1_EDR (4) +#define BLE_HCI_VER_BCS_3_0_HCS (5) +#define BLE_HCI_VER_BCS_4_0 (6) +#define BLE_HCI_VER_BCS_4_1 (7) +#define BLE_HCI_VER_BCS_4_2 (8) +#define BLE_HCI_VER_BCS_5_0 (9) + +#define BLE_LMP_VER_BCS_1_0b (0) +#define BLE_LMP_VER_BCS_1_1 (1) +#define BLE_LMP_VER_BCS_1_2 (2) +#define BLE_LMP_VER_BCS_2_0_EDR (3) +#define BLE_LMP_VER_BCS_2_1_EDR (4) +#define BLE_LMP_VER_BCS_3_0_HCS (5) +#define BLE_LMP_VER_BCS_4_0 (6) +#define BLE_LMP_VER_BCS_4_1 (7) +#define BLE_LMP_VER_BCS_4_2 (8) +#define BLE_LMP_VER_BCS_5_0 (9) + +/* Sub-event 0x0A: enhanced connection complete */ +#define BLE_HCI_LE_ENH_CONN_COMPLETE_LEN (31) + +/*--- Shared data structures ---*/ + +/* Host buffer size (OGF=0x03, OCF=0x0033) */ +struct hci_host_buf_size +{ + uint16_t acl_pkt_len; + uint8_t sync_pkt_len; + uint16_t num_acl_pkts; + uint16_t num_sync_pkts; +}; + +/* Host number of completed packets (OGF=0x03, OCF=0x0035) */ +struct hci_host_num_comp_pkts_entry +{ + uint16_t conn_handle; + uint16_t num_pkts; +}; + +/* Read local version information (OGF=0x0004, OCF=0x0001) */ +struct hci_loc_ver_info +{ + uint8_t status; + uint8_t hci_version; + uint16_t hci_revision; + uint8_t lmp_pal_version; + uint16_t mfrg_name; + uint8_t lmp_pal_subversion; +}; + +/* set random address command (ocf = 0x0005) */ +struct hci_rand_addr +{ + uint8_t addr[6]; +}; + +/* set advertising parameters command (ocf = 0x0006) */ +struct hci_adv_params +{ + uint8_t adv_type; + uint8_t adv_channel_map; + uint8_t own_addr_type; + uint8_t peer_addr_type; + uint8_t adv_filter_policy; + uint16_t adv_itvl_min; + uint16_t adv_itvl_max; + uint8_t peer_addr[BLE_DEV_ADDR_LEN]; +}; + +/* LE create connection command (ocf=0x000d). */ +struct hci_create_conn +{ + uint16_t scan_itvl; + uint16_t scan_window; + uint8_t filter_policy; + uint8_t peer_addr_type; + uint8_t peer_addr[BLE_DEV_ADDR_LEN]; + uint8_t own_addr_type; + uint16_t conn_itvl_min; + uint16_t conn_itvl_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +}; + +/* LE Read Local P-256 Public Key Complete Event */ +struct hci_le_subev_rd_loc_p256_pubkey { + uint8_t status; + uint8_t pubkey[64]; +} __attribute__((packed)); + +/* LE Generate DHKey Complete Event */ +struct hci_le_subev_gen_dhkey_complete { + uint8_t status; + uint8_t dhkey[32]; +} __attribute__((packed)); + +/* LE Directed Advertising Report Event */ +struct hci_le_subev_direct_adv_rpt_param { + uint8_t evt_type; + uint8_t addr_type; + uint8_t addr[6]; + uint8_t dir_addr_type; + uint8_t dir_addr[6]; + int8_t rssi; +} __attribute__((packed)); + +struct hci_le_subev_direct_adv_rpt { + uint8_t num_reports; + struct hci_le_subev_direct_adv_rpt_param params[0]; +} __attribute__((packed)); + +#if MYNEWT_VAL(BLE_EXT_ADV) +/* LE create connection command (ocf=0x0043). */ +struct hci_ext_conn_params +{ + uint16_t scan_itvl; + uint16_t scan_window; + uint16_t conn_itvl_min; + uint16_t conn_itvl_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +}; + +struct hci_ext_create_conn +{ + uint8_t filter_policy; + uint8_t own_addr_type; + uint8_t peer_addr_type; + uint8_t peer_addr[BLE_DEV_ADDR_LEN]; + uint8_t init_phy_mask; + struct hci_ext_conn_params params[3]; +}; + +struct hci_ext_adv_report_param { + uint16_t evt_type; + uint8_t addr_type; + uint8_t addr[6]; + uint8_t prim_phy; + uint8_t sec_phy; + uint8_t sid; + int8_t tx_power; + int8_t rssi; + uint16_t per_adv_itvl; + uint8_t dir_addr_type; + uint8_t dir_addr[6]; + uint8_t adv_data_len; + uint8_t adv_data[0]; +} __attribute__((packed)); + +struct hci_ext_adv_report { + /* We support one report per event for now */ + uint8_t subevt; + uint8_t num_reports; + struct hci_ext_adv_report_param params[0]; +} __attribute__((packed)); + +/* Ext Adv Set enable parameters, not in HCI order */ +struct hci_ext_adv_set +{ + uint8_t handle; + uint8_t events; + uint16_t duration; +}; + +/* Ext Advertising Parameters */ +struct hci_ext_adv_params +{ + uint16_t properties; + uint32_t min_interval; + uint32_t max_interval; + uint8_t chan_map; + uint8_t own_addr_type; + uint8_t peer_addr_type; + uint8_t peer_addr[6]; + uint8_t filter_policy; + int8_t tx_power; + uint8_t primary_phy; + uint8_t max_skip; + uint8_t secondary_phy; + uint8_t sid; + uint8_t scan_req_notif; +}; + +/* LE Extended Advertising Report Event */ +struct hci_le_subev_ext_adv_rpt { + uint8_t num_reports; + struct hci_ext_adv_report_param params[0]; +} __attribute__((packed)); + +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +/* LE Periodic Advertising Sync Established Event */ +struct hci_le_subev_periodic_adv_sync_estab { + uint8_t status; + uint16_t sync_handle; + uint8_t sid; + uint8_t adv_addr_type; + uint8_t adv_addr[6]; + uint8_t adv_phy; + uint16_t per_adv_ival; + uint8_t adv_clk_accuracy; +} __attribute__((packed)); + +/* LE Periodic Advertising Report Event */ +struct hci_le_subev_periodic_adv_rpt { + uint16_t sync_handle; + int8_t tx_power; + int8_t rssi; + uint8_t unused; + uint8_t data_status; + uint8_t data_length; + uint8_t data[0]; +} __attribute__((packed)); + +/* LE Periodic Advertising Sync Lost Event */ +struct hci_le_subev_periodic_adv_sync_lost { + uint16_t sync_handle; +} __attribute__((packed)); +#endif + +/* LE Advertising Set Terminated Event */ +struct hci_le_subev_adv_set_terminated { + uint8_t status; + uint8_t adv_handle; + uint16_t conn_handle; + uint8_t num_compl_ext_adv_ev; +} __attribute__((packed)); + +/* LE Scan Request Received Event */ +struct hci_le_subev_scan_req_rcvd { + uint8_t adv_handle; + uint8_t scan_addr_type; + uint8_t scan_addr[6]; +} __attribute__((packed)); + +#endif + +/* LE Channel Selection Algorithm Event */ +struct hci_le_subev_chan_sel_alg { + uint16_t conn_handle; + uint8_t chan_sel_alg; +} __attribute__((packed)); + +/* LE connection update command (ocf=0x0013). */ +struct hci_conn_update +{ + uint16_t handle; + uint16_t conn_itvl_min; + uint16_t conn_itvl_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +}; + +/* LE start encryption command (ocf=0x0019) (note: fields out of order). */ +struct hci_start_encrypt +{ + uint16_t connection_handle; + uint16_t encrypted_diversifier; + uint64_t random_number; + uint8_t long_term_key[16]; +}; + +/* LE long term key request reply command (ocf=0x001a). */ +struct hci_lt_key_req_reply +{ + uint16_t conn_handle; + uint8_t long_term_key[16]; +}; + +/* LE Remote connection parameter request reply command */ +struct hci_conn_param_reply +{ + uint16_t handle; + uint16_t conn_itvl_min; + uint16_t conn_itvl_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +}; + +/* LE Remote connection parameter request negative reply command */ +struct hci_conn_param_neg_reply +{ + uint16_t handle; + uint8_t reason; +}; + +/* Encryption change event (code=0x08) (note: fields out of order) */ +struct hci_encrypt_change +{ + uint8_t status; + uint8_t encryption_enabled; + uint16_t connection_handle; +}; + +/* Encryption key refresh complete event (code=0x30) */ +struct hci_encrypt_key_refresh +{ + uint8_t status; + uint16_t connection_handle; +}; + +/* Connection complete LE meta subevent */ +struct hci_le_conn_complete +{ + uint8_t subevent_code; + uint8_t status; + uint16_t connection_handle; + uint8_t role; + uint8_t peer_addr_type; + uint8_t peer_addr[BLE_DEV_ADDR_LEN]; + uint16_t conn_itvl; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint8_t master_clk_acc; + uint8_t local_rpa[BLE_DEV_ADDR_LEN]; + uint8_t peer_rpa[BLE_DEV_ADDR_LEN]; +}; + +/* Connection update complete LE meta subevent */ +struct hci_le_conn_upd_complete +{ + uint8_t subevent_code; + uint8_t status; + uint16_t connection_handle; + uint16_t conn_itvl; + uint16_t conn_latency; + uint16_t supervision_timeout; +}; + +/* Remote connection parameter request LE meta subevent */ +struct hci_le_conn_param_req +{ + uint8_t subevent_code; + uint16_t connection_handle; + uint16_t itvl_min; + uint16_t itvl_max; + uint16_t latency; + uint16_t timeout; +}; + +/* Read Remote Supported Features complete LE meta subevent */ +struct hci_le_rd_rem_supp_feat_complete +{ + uint8_t subevent_code; + uint8_t status; + uint16_t connection_handle; + uint8_t features[8]; +}; + +/* LE long term key request event (note: fields out of order). */ +struct hci_le_lt_key_req +{ + uint64_t random_number; + uint16_t connection_handle; + uint16_t encrypted_diversifier; + uint8_t subevent_code; +}; + +/* Disconnection complete event (note: fields out of order). */ +struct hci_disconn_complete +{ + uint16_t connection_handle; + uint8_t status; + uint8_t reason; +}; + +/* Read RSSI command-complete parameters (note: fields out of order). */ +struct hci_read_rssi_ack_params +{ + uint16_t connection_handle; + uint8_t status; + int8_t rssi; +}; + +/* PHY updated completed LE meta subevent */ +struct hci_le_phy_upd_complete +{ + uint8_t subevent_code; + uint8_t status; + uint16_t connection_handle; + uint8_t tx_phy; + uint8_t rx_phy; +}; + +/* LE Advertising Set Terminated subevent*/ +struct hci_le_adv_set_terminated +{ + uint8_t subevent_code; + uint8_t status; + uint8_t adv_handle; + uint16_t conn_handle; + uint8_t completed_events; +}; + +/* LE Scan Request Received subevent */ +struct hci_le_scan_req_rcvd +{ + uint8_t subevent_code; + uint8_t adv_handle; + uint8_t scan_addr_type; + uint8_t scan_addr[BLE_DEV_ADDR_LEN]; +}; + +#define BLE_HCI_DATA_HDR_SZ 4 +#define BLE_HCI_DATA_HANDLE(handle_pb_bc) (((handle_pb_bc) & 0x0fff) >> 0) +#define BLE_HCI_DATA_PB(handle_pb_bc) (((handle_pb_bc) & 0x3000) >> 12) +#define BLE_HCI_DATA_BC(handle_pb_bc) (((handle_pb_bc) & 0xc000) >> 14) + +struct hci_data_hdr +{ + uint16_t hdh_handle_pb_bc; + uint16_t hdh_len; +}; + +#define BLE_HCI_PB_FIRST_NON_FLUSH 0 +#define BLE_HCI_PB_MIDDLE 1 +#define BLE_HCI_PB_FIRST_FLUSH 2 +#define BLE_HCI_PB_FULL 3 + +struct hci_add_dev_to_resolving_list { + uint8_t addr_type; + uint8_t addr[6]; + uint8_t local_irk[16]; + uint8_t peer_irk[16]; +}; + +/* External data structures */ +extern const uint8_t g_ble_hci_le_cmd_len[BLE_HCI_NUM_LE_CMDS]; + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_HCI_COMMON_ */ diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/pkg.yml b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/pkg.yml new file mode 100644 index 000000000..44cc0c732 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/pkg.yml @@ -0,0 +1,49 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: nimble/host/mesh +pkg.description: Bluetooth Mesh +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + - mesh + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/util/mem" + - "@apache-mynewt-core/crypto/tinycrypt" + - nimble + - nimble/host + +pkg.deps.BLE_MESH_SHELL: + - "@apache-mynewt-core/sys/shell" + +pkg.deps.BLE_MESH_SETTINGS: + - "@apache-mynewt-core/encoding/base64" + - "@apache-mynewt-core/sys/config" + +pkg.req_apis: + - log + - stats + +pkg.init: + bt_mesh_register_gatt: 'MYNEWT_VAL(BLE_MESH_SYSINIT_STAGE)' + ble_mesh_shell_init: 'MYNEWT_VAL(BLE_MESH_SYSINIT_STAGE_SHELL)' diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.c new file mode 100644 index 000000000..a6a12316b --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.c @@ -0,0 +1,767 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include "mesh/mesh.h" + +#include "syscfg/syscfg.h" +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_ACCESS)) +#include "host/ble_hs_log.h" + +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "lpn.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) +#include "mesh/model_cli.h" +#endif + +static const struct bt_mesh_comp *dev_comp; +static u16_t dev_primary_addr; + +static const struct { + const u16_t id; + int (*const init)(struct bt_mesh_model *model, bool primary); +} model_init[] = { + { BT_MESH_MODEL_ID_CFG_SRV, bt_mesh_cfg_srv_init }, + { BT_MESH_MODEL_ID_HEALTH_SRV, bt_mesh_health_srv_init }, +#if MYNEWT_VAL(BLE_MESH_CFG_CLI) + { BT_MESH_MODEL_ID_CFG_CLI, bt_mesh_cfg_cli_init }, +#endif +#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI) + { BT_MESH_MODEL_ID_HEALTH_CLI, bt_mesh_health_cli_init }, +#endif +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) + { BT_MESH_MODEL_ID_GEN_ONOFF_CLI, bt_mesh_gen_model_cli_init }, + { BT_MESH_MODEL_ID_GEN_LEVEL_CLI, bt_mesh_gen_model_cli_init }, +#endif +}; + +void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod, + struct bt_mesh_elem *elem, + bool vnd, bool primary, + void *user_data), + void *user_data) +{ + int i, j; + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + for (j = 0; j < elem->model_count; j++) { + struct bt_mesh_model *model = &elem->models[j]; + + func(model, elem, false, i == 0, user_data); + } + + for (j = 0; j < elem->vnd_model_count; j++) { + struct bt_mesh_model *model = &elem->vnd_models[j]; + + func(model, elem, true, i == 0, user_data); + } + } +} + +s32_t bt_mesh_model_pub_period_get(struct bt_mesh_model *mod) +{ + int period; + + if (!mod->pub) { + return 0; + } + + switch (mod->pub->period >> 6) { + case 0x00: + /* 1 step is 100 ms */ + period = K_MSEC((mod->pub->period & BIT_MASK(6)) * 100); + break; + case 0x01: + /* 1 step is 1 second */ + period = K_SECONDS(mod->pub->period & BIT_MASK(6)); + break; + case 0x02: + /* 1 step is 10 seconds */ + period = K_SECONDS((mod->pub->period & BIT_MASK(6)) * 10); + break; + case 0x03: + /* 1 step is 10 minutes */ + period = K_MINUTES((mod->pub->period & BIT_MASK(6)) * 10); + break; + default: + CODE_UNREACHABLE; + } + + return period >> mod->pub->period_div; +} + +static s32_t next_period(struct bt_mesh_model *mod) +{ + struct bt_mesh_model_pub *pub = mod->pub; + u32_t elapsed, period; + + period = bt_mesh_model_pub_period_get(mod); + if (!period) { + return 0; + } + + elapsed = k_uptime_get_32() - pub->period_start; + + BT_DBG("Publishing took %ums", (unsigned) elapsed); + + if (elapsed > period) { + BT_WARN("Publication sending took longer than the period"); + /* Return smallest positive number since 0 means disabled */ + return K_MSEC(1); + } + + return period - elapsed; +} + +static void publish_sent(int err, void *user_data) +{ + struct bt_mesh_model *mod = user_data; + s32_t delay; + + BT_DBG("err %d", err); + + if (mod->pub->count) { + delay = BT_MESH_PUB_TRANSMIT_INT(mod->pub->retransmit); + } else { + delay = next_period(mod); + } + + if (delay) { + BT_DBG("Publishing next time in %dms", (int) delay); + k_delayed_work_submit(&mod->pub->timer, delay); + } +} + +static const struct bt_mesh_send_cb pub_sent_cb = { + .end = publish_sent, +}; + +static int publish_retransmit(struct bt_mesh_model *mod) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + struct bt_mesh_model_pub *pub = mod->pub; + struct bt_mesh_app_key *key; + struct bt_mesh_msg_ctx ctx = { + .addr = pub->addr, + .send_ttl = pub->ttl, + }; + struct bt_mesh_net_tx tx = { + .ctx = &ctx, + .src = bt_mesh_model_elem(mod)->addr, + .xmit = bt_mesh_net_transmit_get(), + .friend_cred = pub->cred, + }; + int err; + + key = bt_mesh_app_key_find(pub->key); + if (!key) { + err = -EADDRNOTAVAIL; + goto done; + } + + tx.sub = bt_mesh_subnet_get(key->net_idx); + + ctx.net_idx = key->net_idx; + ctx.app_idx = key->app_idx; + + net_buf_simple_init(sdu, 0); + net_buf_simple_add_mem(sdu, pub->msg->om_data, pub->msg->om_len); + + pub->count--; + + err = bt_mesh_trans_send(&tx, sdu, &pub_sent_cb, mod); + +done: + os_mbuf_free_chain(sdu); + return err; +} + +static void mod_publish(struct ble_npl_event *work) +{ + struct bt_mesh_model_pub *pub = ble_npl_event_get_arg(work); + s32_t period_ms; + int err; + + BT_DBG(""); + + period_ms = bt_mesh_model_pub_period_get(pub->mod); + BT_DBG("period %u ms", (unsigned) period_ms); + + if (pub->count) { + err = publish_retransmit(pub->mod); + if (err) { + BT_ERR("Failed to retransmit (err %d)", err); + + pub->count = 0; + + /* Continue with normal publication */ + if (period_ms) { + k_delayed_work_submit(&pub->timer, period_ms); + } + } + + return; + } + + if (!period_ms) { + return; + } + + __ASSERT_NO_MSG(pub->update != NULL); + + pub->period_start = k_uptime_get_32(); + + err = pub->update(pub->mod); + if (err) { + BT_ERR("Failed to update publication message"); + return; + } + + err = bt_mesh_model_publish(pub->mod); + if (err) { + BT_ERR("Publishing failed (err %d)", err); + } + + if (pub->count) { + /* Retransmissions also control the timer */ + k_delayed_work_cancel(&pub->timer); + } +} + +struct bt_mesh_elem *bt_mesh_model_elem(struct bt_mesh_model *mod) +{ + return &dev_comp->elem[mod->elem_idx]; +} + +struct bt_mesh_model *bt_mesh_model_get(bool vnd, u8_t elem_idx, u8_t mod_idx) +{ + struct bt_mesh_elem *elem; + + if (elem_idx >= dev_comp->elem_count) { + BT_ERR("Invalid element index %u", elem_idx); + return NULL; + } + + elem = &dev_comp->elem[elem_idx]; + + if (vnd) { + if (mod_idx >= elem->vnd_model_count) { + BT_ERR("Invalid vendor model index %u", mod_idx); + return NULL; + } + + return &elem->vnd_models[mod_idx]; + } else { + if (mod_idx >= elem->model_count) { + BT_ERR("Invalid SIG model index %u", mod_idx); + return NULL; + } + + return &elem->models[mod_idx]; + } +} + +static void mod_init(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + int i; + + if (mod->pub) { + mod->pub->mod = mod; + k_delayed_work_init(&mod->pub->timer, mod_publish); + k_delayed_work_add_arg(&mod->pub->timer, mod->pub); + } + + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + mod->keys[i] = BT_MESH_KEY_UNUSED; + } + + mod->elem_idx = elem - dev_comp->elem; + if (vnd) { + mod->mod_idx = mod - elem->vnd_models; + } else { + mod->mod_idx = mod - elem->models; + } + + if (vnd) { + return; + } + + for (i = 0; i < ARRAY_SIZE(model_init); i++) { + if (model_init[i].id == mod->id) { + model_init[i].init(mod, primary); + } + } +} + +int bt_mesh_comp_register(const struct bt_mesh_comp *comp) +{ + /* There must be at least one element */ + if (!comp->elem_count) { + return -EINVAL; + } + + dev_comp = comp; + + bt_mesh_model_foreach(mod_init, NULL); + + return 0; +} + +void bt_mesh_comp_provision(u16_t addr) +{ + int i; + + dev_primary_addr = addr; + + BT_DBG("addr 0x%04x elem_count %zu", addr, dev_comp->elem_count); + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + elem->addr = addr++; + + BT_DBG("addr 0x%04x mod_count %u vnd_mod_count %u", + elem->addr, elem->model_count, elem->vnd_model_count); + } +} + +void bt_mesh_comp_unprovision(void) +{ + BT_DBG(""); + + dev_primary_addr = BT_MESH_ADDR_UNASSIGNED; + + bt_mesh_model_foreach(mod_init, NULL); +} + +u16_t bt_mesh_primary_addr(void) +{ + return dev_primary_addr; +} + +u16_t *bt_mesh_model_find_group(struct bt_mesh_model *mod, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] == addr) { + return &mod->groups[i]; + } + } + + return NULL; +} + +static struct bt_mesh_model *bt_mesh_elem_find_group(struct bt_mesh_elem *elem, + u16_t group_addr) +{ + struct bt_mesh_model *model; + u16_t *match; + int i; + + for (i = 0; i < elem->model_count; i++) { + model = &elem->models[i]; + + match = bt_mesh_model_find_group(model, group_addr); + if (match) { + return model; + } + } + + for (i = 0; i < elem->vnd_model_count; i++) { + model = &elem->vnd_models[i]; + + match = bt_mesh_model_find_group(model, group_addr); + if (match) { + return model; + } + } + + return NULL; +} + +struct bt_mesh_elem *bt_mesh_elem_find(u16_t addr) +{ + int i; + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + if (BT_MESH_ADDR_IS_GROUP(addr) || + BT_MESH_ADDR_IS_VIRTUAL(addr)) { + if (bt_mesh_elem_find_group(elem, addr)) { + return elem; + } + } else if (elem->addr == addr) { + return elem; + } + } + + return NULL; +} + +u8_t bt_mesh_elem_count(void) +{ + return dev_comp->elem_count; +} + +static bool model_has_key(struct bt_mesh_model *mod, u16_t key) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + if (mod->keys[i] == key) { + return true; + } + } + + return false; +} + +static const struct bt_mesh_model_op *find_op(struct bt_mesh_model *models, + u8_t model_count, u16_t dst, + u16_t app_idx, u32_t opcode, + struct bt_mesh_model **model) +{ + u8_t i; + + for (i = 0; i < model_count; i++) { + const struct bt_mesh_model_op *op; + + *model = &models[i]; + + if (BT_MESH_ADDR_IS_GROUP(dst) || + BT_MESH_ADDR_IS_VIRTUAL(dst)) { + if (!bt_mesh_model_find_group(*model, dst)) { + continue; + } + } + + if (!model_has_key(*model, app_idx)) { + continue; + } + + for (op = (*model)->op; op->func; op++) { + if (op->opcode == opcode) { + return op; + } + } + } + + *model = NULL; + return NULL; +} + +static int get_opcode(struct os_mbuf *buf, u32_t *opcode) +{ + switch (buf->om_data[0] >> 6) { + case 0x00: + case 0x01: + if (buf->om_data[0] == 0x7f) { + BT_ERR("Ignoring RFU OpCode"); + return -EINVAL; + } + + *opcode = net_buf_simple_pull_u8(buf); + return 0; + case 0x02: + if (buf->om_len < 2) { + BT_ERR("Too short payload for 2-octet OpCode"); + return -EINVAL; + } + + *opcode = net_buf_simple_pull_be16(buf); + return 0; + case 0x03: + if (buf->om_len < 3) { + BT_ERR("Too short payload for 3-octet OpCode"); + return -EINVAL; + } + + *opcode = net_buf_simple_pull_u8(buf) << 16; + *opcode |= net_buf_simple_pull_le16(buf); + return 0; + } + + CODE_UNREACHABLE; +} + +bool bt_mesh_fixed_group_match(u16_t addr) +{ + /* Check for fixed group addresses */ + switch (addr) { + case BT_MESH_ADDR_ALL_NODES: + return true; + case BT_MESH_ADDR_PROXIES: + /* TODO: Proxy not yet supported */ + return false; + case BT_MESH_ADDR_FRIENDS: + return (bt_mesh_friend_get() == BT_MESH_FRIEND_ENABLED); + case BT_MESH_ADDR_RELAYS: + return (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED); + default: + return false; + } +} + +void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf) +{ + struct bt_mesh_model *models, *model; + const struct bt_mesh_model_op *op; + u32_t opcode; + u8_t count; + int i; + + BT_DBG("app_idx 0x%04x src 0x%04x dst 0x%04x", rx->ctx.app_idx, + rx->ctx.addr, rx->ctx.recv_dst); + BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (get_opcode(buf, &opcode) < 0) { + BT_WARN("Unable to decode OpCode"); + return; + } + + BT_DBG("OpCode 0x%08x", (unsigned) opcode); + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + if (BT_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) { + if (elem->addr != rx->ctx.recv_dst) { + continue; + } + } else if (BT_MESH_ADDR_IS_GROUP(rx->ctx.recv_dst) || + BT_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) { + /* find_op() will do proper model/group matching */ + } else if (i != 0 || + !bt_mesh_fixed_group_match(rx->ctx.recv_dst)) { + continue; + } + + /* SIG models cannot contain 3-byte (vendor) OpCodes, and + * vendor models cannot contain SIG (1- or 2-byte) OpCodes, so + * we only need to do the lookup in one of the model lists. + */ + if (opcode < 0x10000) { + models = elem->models; + count = elem->model_count; + } else { + models = elem->vnd_models; + count = elem->vnd_model_count; + } + + op = find_op(models, count, rx->ctx.recv_dst, rx->ctx.app_idx, + opcode, &model); + if (op) { + struct net_buf_simple_state state; + + if (buf->om_len < op->min_len) { + BT_ERR("Too short message for OpCode 0x%08x", + (unsigned) opcode); + continue; + } + + /* The callback will likely parse the buffer, so + * store the parsing state in case multiple models + * receive the message. + */ + net_buf_simple_save(buf, &state); + op->func(model, &rx->ctx, buf); + net_buf_simple_restore(buf, &state); + + } else { + BT_DBG("No OpCode 0x%08x for elem %d", + (unsigned) opcode, i); + } + } +} + +void bt_mesh_model_msg_init(struct os_mbuf *msg, u32_t opcode) +{ + net_buf_simple_init(msg, 0); + + if (opcode < 0x100) { + /* 1-byte OpCode */ + net_buf_simple_add_u8(msg, opcode); + return; + } + + if (opcode < 0x10000) { + /* 2-byte OpCode */ + net_buf_simple_add_be16(msg, opcode); + return; + } + + /* 3-byte OpCode */ + net_buf_simple_add_u8(msg, ((opcode >> 16) & 0xff)); + net_buf_simple_add_le16(msg, opcode & 0xffff); +} + +static int model_send(struct bt_mesh_model *model, + struct bt_mesh_net_tx *tx, bool implicit_bind, + struct os_mbuf *msg, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx->ctx->net_idx, + tx->ctx->app_idx, tx->ctx->addr); + BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len)); + + if (!bt_mesh_is_provisioned()) { + BT_ERR("Local node is not yet provisioned"); + return -EAGAIN; + } + + if (net_buf_simple_tailroom(msg) < 4) { + BT_ERR("Not enough tailroom for TransMIC"); + return -EINVAL; + } + + if (msg->om_len > BT_MESH_TX_SDU_MAX - 4) { + BT_ERR("Too big message"); + return -EMSGSIZE; + } + + if (!implicit_bind && !model_has_key(model, tx->ctx->app_idx)) { + BT_ERR("Model not bound to AppKey 0x%04x", tx->ctx->app_idx); + return -EINVAL; + } + + return bt_mesh_trans_send(tx, msg, cb, cb_data); +} + +int bt_mesh_model_send(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *msg, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + struct bt_mesh_net_tx tx = { + .sub = bt_mesh_subnet_get(ctx->net_idx), + .ctx = ctx, + .src = bt_mesh_model_elem(model)->addr, + .xmit = bt_mesh_net_transmit_get(), + .friend_cred = 0, + }; + + return model_send(model, &tx, false, msg, cb, cb_data); +} + +int bt_mesh_model_publish(struct bt_mesh_model *model) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + struct bt_mesh_model_pub *pub = model->pub; + struct bt_mesh_app_key *key; + struct bt_mesh_msg_ctx ctx = { + }; + struct bt_mesh_net_tx tx = { + .ctx = &ctx, + .src = bt_mesh_model_elem(model)->addr, + .xmit = bt_mesh_net_transmit_get(), + }; + int err; + + BT_DBG(""); + + if (!pub) { + err = -ENOTSUP; + goto done; + } + + if (pub->addr == BT_MESH_ADDR_UNASSIGNED) { + err = -EADDRNOTAVAIL; + goto done; + } + + key = bt_mesh_app_key_find(pub->key); + if (!key) { + err = -EADDRNOTAVAIL; + goto done; + } + + if (pub->msg->om_len + 4 > BT_MESH_TX_SDU_MAX) { + BT_ERR("Message does not fit maximum SDU size"); + err = -EMSGSIZE; + goto done; + } + + if (pub->count) { + BT_WARN("Clearing publish retransmit timer"); + k_delayed_work_cancel(&pub->timer); + } + + net_buf_simple_init(sdu, 0); + net_buf_simple_add_mem(sdu, pub->msg->om_data, pub->msg->om_len); + + ctx.addr = pub->addr; + ctx.send_ttl = pub->ttl; + ctx.net_idx = key->net_idx; + ctx.app_idx = key->app_idx; + + tx.friend_cred = pub->cred; + tx.sub = bt_mesh_subnet_get(ctx.net_idx), + + pub->count = BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit); + + BT_DBG("Publish Retransmit Count %u Interval %ums", pub->count, + BT_MESH_PUB_TRANSMIT_INT(pub->retransmit)); + + err = model_send(model, &tx, true, sdu, &pub_sent_cb, model); + if (err) { + /* Don't try retransmissions for this publish attempt */ + pub->count = 0; + /* Make sure the publish timer gets reset */ + publish_sent(err, model); + } + +done: + os_mbuf_free_chain(sdu); + return err; +} + +struct bt_mesh_model *bt_mesh_model_find_vnd(struct bt_mesh_elem *elem, + u16_t company, u16_t id) +{ + u8_t i; + + for (i = 0; i < elem->vnd_model_count; i++) { + if (elem->vnd_models[i].vnd.company == company && + elem->vnd_models[i].vnd.id == id) { + return &elem->vnd_models[i]; + } + } + + return NULL; +} + +struct bt_mesh_model *bt_mesh_model_find(struct bt_mesh_elem *elem, + u16_t id) +{ + u8_t i; + + for (i = 0; i < elem->model_count; i++) { + if (elem->models[i].id == id) { + return &elem->models[i]; + } + } + + return NULL; +} + +const struct bt_mesh_comp *bt_mesh_comp_get(void) +{ + return dev_comp; +} diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.h new file mode 100644 index 000000000..5ce7f1990 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.h @@ -0,0 +1,57 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ACCESS_H__ +#define __ACCESS_H__ + +#include "mesh/mesh.h" + +/* bt_mesh_model.flags */ +enum { + BT_MESH_MOD_BIND_PENDING = BIT(0), + BT_MESH_MOD_SUB_PENDING = BIT(1), + BT_MESH_MOD_PUB_PENDING = BIT(2), +}; + +void bt_mesh_elem_register(struct bt_mesh_elem *elem, u8_t count); + +u8_t bt_mesh_elem_count(void); + +/* Find local element based on unicast or group address */ +struct bt_mesh_elem *bt_mesh_elem_find(u16_t addr); + +struct bt_mesh_model *bt_mesh_model_find_vnd(struct bt_mesh_elem *elem, + u16_t company, u16_t id); +struct bt_mesh_model * bt_mesh_model_find(struct bt_mesh_elem *elem, + u16_t id); + +u16_t *bt_mesh_model_find_group(struct bt_mesh_model *mod, u16_t addr); + +bool bt_mesh_fixed_group_match(u16_t addr); + +void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod, + struct bt_mesh_elem *elem, + bool vnd, bool primary, + void *user_data), + void *user_data); + +s32_t bt_mesh_model_pub_period_get(struct bt_mesh_model *mod); + +void bt_mesh_comp_provision(u16_t addr); +void bt_mesh_comp_unprovision(void); + +u16_t bt_mesh_primary_addr(void); + +const struct bt_mesh_comp *bt_mesh_comp_get(void); + +struct bt_mesh_model *bt_mesh_model_get(bool vnd, u8_t elem_idx, u8_t mod_idx); + +void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf); + +int bt_mesh_comp_register(const struct bt_mesh_comp *comp); +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.c new file mode 100644 index 000000000..5364ddc73 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.c @@ -0,0 +1,423 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mesh/mesh.h" + +#include "syscfg/syscfg.h" +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_ADV)) +#include "host/ble_hs_log.h" + +#include "host/ble_hs_adv.h" +#include "host/ble_gap.h" +#include "nimble/hci_common.h" +#include "mesh/porting.h" +#include "nimble/nimble_port.h" + +#include "adv.h" +#include "net.h" +#include "foundation.h" +#include "beacon.h" +#include "prov.h" +#include "proxy.h" + +/* Convert from ms to 0.625ms units */ +#define ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5) + +/* Window and Interval are equal for continuous scanning */ +#define MESH_SCAN_INTERVAL_MS 30 +#define MESH_SCAN_WINDOW_MS 30 +#define MESH_SCAN_INTERVAL ADV_SCAN_UNIT(MESH_SCAN_INTERVAL_MS) +#define MESH_SCAN_WINDOW ADV_SCAN_UNIT(MESH_SCAN_WINDOW_MS) + +/* Pre-5.0 controllers enforce a minimum interval of 100ms + * whereas 5.0+ controllers can go down to 20ms. + */ +#define ADV_INT_DEFAULT_MS 100 +#define ADV_INT_FAST_MS 20 + +static s32_t adv_int_min = ADV_INT_DEFAULT_MS; + +/* TinyCrypt PRNG consumes a lot of stack space, so we need to have + * an increased call stack whenever it's used. + */ +#if MYNEWT +#define ADV_STACK_SIZE 768 +OS_TASK_STACK_DEFINE(g_blemesh_stack, ADV_STACK_SIZE); +struct os_task adv_task; +#else +static TaskHandle_t adv_task_h; +#endif + +static struct ble_npl_eventq adv_queue; +extern u8_t g_mesh_addr_type; +static int adv_initialized = false; + +static os_membuf_t adv_buf_mem[OS_MEMPOOL_SIZE( + MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT), + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE)]; + +struct os_mbuf_pool adv_os_mbuf_pool; +static struct os_mempool adv_buf_mempool; + +static const u8_t adv_type[] = { + [BT_MESH_ADV_PROV] = BLE_HS_ADV_TYPE_MESH_PROV, + [BT_MESH_ADV_DATA] = BLE_HS_ADV_TYPE_MESH_MESSAGE, + [BT_MESH_ADV_BEACON] = BLE_HS_ADV_TYPE_MESH_BEACON, + [BT_MESH_ADV_URI] = BLE_HS_ADV_TYPE_URI, +}; + + +static struct bt_mesh_adv adv_pool[CONFIG_BT_MESH_ADV_BUF_COUNT]; + +static struct bt_mesh_adv *adv_alloc(int id) +{ + return &adv_pool[id]; +} + +static inline void adv_send_start(u16_t duration, int err, + const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + if (cb && cb->start) { + cb->start(duration, err, cb_data); + } +} + +static inline void adv_send_end(int err, const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + if (cb && cb->end) { + cb->end(err, cb_data); + } +} + +static inline void adv_send(struct os_mbuf *buf) +{ + const struct bt_mesh_send_cb *cb = BT_MESH_ADV(buf)->cb; + void *cb_data = BT_MESH_ADV(buf)->cb_data; + struct ble_gap_adv_params param = { 0 }; + u16_t duration, adv_int; + struct bt_data ad; + int err; + + adv_int = max(adv_int_min, + BT_MESH_TRANSMIT_INT(BT_MESH_ADV(buf)->xmit)); + duration = (MESH_SCAN_WINDOW_MS + + ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) * + (adv_int + 10))); + + BT_DBG("type %u om_len %u: %s", BT_MESH_ADV(buf)->type, + buf->om_len, bt_hex(buf->om_data, buf->om_len)); + BT_DBG("count %u interval %ums duration %ums", + BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1, adv_int, + duration); + + ad.type = adv_type[BT_MESH_ADV(buf)->type]; + ad.data_len = buf->om_len; + ad.data = buf->om_data; + + param.itvl_min = ADV_SCAN_UNIT(adv_int); + param.itvl_max = param.itvl_min; + param.conn_mode = BLE_GAP_CONN_MODE_NON; + + err = bt_le_adv_start(¶m, &ad, 1, NULL, 0); + net_buf_unref(buf); + adv_send_start(duration, err, cb, cb_data); + if (err) { + BT_ERR("Advertising failed: err %d", err); + return; + } + + BT_DBG("Advertising started. Sleeping %u ms", duration); + + k_sleep(K_MSEC(duration)); + + err = bt_le_adv_stop(false); + adv_send_end(err, cb, cb_data); + if (err) { + BT_ERR("Stopping advertising failed: err %d", err); + return; + } + + BT_DBG("Advertising stopped"); +} + +void +mesh_adv_thread(void *args) +{ + static struct ble_npl_event *ev; + struct os_mbuf *buf; +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + s32_t timeout; +#endif + + BT_DBG("started"); + + while (1) { +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + ev = ble_npl_eventq_get(&adv_queue, 0); + while (!ev) { + timeout = bt_mesh_proxy_adv_start(); + BT_DBG("Proxy Advertising up to %d ms", (int) timeout); + + // FIXME: should we redefine K_SECONDS macro instead in glue? + if (timeout != K_FOREVER) { + timeout = ble_npl_time_ms_to_ticks32(timeout); + } + + ev = ble_npl_eventq_get(&adv_queue, timeout); + bt_mesh_proxy_adv_stop(); + } +#else + ev = ble_npl_eventq_get(&adv_queue, BLE_NPL_TIME_FOREVER); +#endif + + if (!ev || !ble_npl_event_get_arg(ev)) { + continue; + } + + buf = ble_npl_event_get_arg(ev); + + /* busy == 0 means this was canceled */ + if (BT_MESH_ADV(buf)->busy) { + BT_MESH_ADV(buf)->busy = 0; + adv_send(buf); + } + + /* os_sched(NULL); */ + } +} + +void bt_mesh_adv_update(void) +{ + static struct ble_npl_event ev = { }; + + BT_DBG(""); + + ble_npl_eventq_put(&adv_queue, &ev); +} + +struct os_mbuf *bt_mesh_adv_create_from_pool(struct os_mbuf_pool *pool, + bt_mesh_adv_alloc_t get_id, + enum bt_mesh_adv_type type, + u8_t xmit, s32_t timeout) +{ + struct bt_mesh_adv *adv; + struct os_mbuf *buf; + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { + BT_WARN("Refusing to allocate buffer while suspended"); + return NULL; + } + + buf = os_mbuf_get_pkthdr(pool, BT_MESH_ADV_USER_DATA_SIZE); + if (!buf) { + return NULL; + } + + adv = get_id(net_buf_id(buf)); + BT_MESH_ADV(buf) = adv; + + memset(adv, 0, sizeof(*adv)); + + adv->type = type; + adv->xmit = xmit; + + adv->ref_cnt = 1; + ble_npl_event_set_arg(&adv->ev, buf); + + return buf; +} + +struct os_mbuf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit, + s32_t timeout) +{ + return bt_mesh_adv_create_from_pool(&adv_os_mbuf_pool, adv_alloc, type, + xmit, timeout); +} + +void bt_mesh_adv_send(struct os_mbuf *buf, const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + BT_DBG("buf %p, type 0x%02x len %u: %s", buf, BT_MESH_ADV(buf)->type, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + BT_MESH_ADV(buf)->cb = cb; + BT_MESH_ADV(buf)->cb_data = cb_data; + BT_MESH_ADV(buf)->busy = 1; + + net_buf_put(&adv_queue, net_buf_ref(buf)); +} + +static void bt_mesh_scan_cb(const bt_addr_le_t *addr, s8_t rssi, + u8_t adv_type, struct os_mbuf *buf) +{ + if (adv_type != BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) { + return; + } + +#if BT_MESH_EXTENDED_DEBUG + BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); +#endif + + while (buf->om_len > 1) { + struct net_buf_simple_state state; + u8_t len, type; + + len = net_buf_simple_pull_u8(buf); + /* Check for early termination */ + if (len == 0) { + return; + } + + if (len > buf->om_len) { + BT_WARN("AD malformed"); + return; + } + + net_buf_simple_save(buf, &state); + + type = net_buf_simple_pull_u8(buf); + + switch (type) { + case BLE_HS_ADV_TYPE_MESH_MESSAGE: + bt_mesh_net_recv(buf, rssi, BT_MESH_NET_IF_ADV); + break; +#if MYNEWT_VAL(BLE_MESH_PB_ADV) + case BLE_HS_ADV_TYPE_MESH_PROV: + bt_mesh_pb_adv_recv(buf); + break; +#endif + case BLE_HS_ADV_TYPE_MESH_BEACON: + bt_mesh_beacon_recv(buf); + break; + default: + break; + } + + net_buf_simple_restore(buf, &state); + net_buf_simple_pull(buf, len); + } +} + +void bt_mesh_adv_init(void) +{ + int rc; + + /* Advertising should only be initialized once. Calling + * os_task init the second time will result in an assert. */ + if (adv_initialized) { + return; + } + + rc = os_mempool_init(&adv_buf_mempool, MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT), + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE, + adv_buf_mem, "adv_buf_pool"); + assert(rc == 0); + + rc = os_mbuf_pool_init(&adv_os_mbuf_pool, &adv_buf_mempool, + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE, + MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT)); + assert(rc == 0); + + ble_npl_eventq_init(&adv_queue); + +#if MYNEWT + os_task_init(&adv_task, "mesh_adv", mesh_adv_thread, NULL, + MYNEWT_VAL(BLE_MESH_ADV_TASK_PRIO), OS_WAIT_FOREVER, + g_blemesh_stack, ADV_STACK_SIZE); +#else + xTaskCreatePinnedToCore(mesh_adv_thread, "mesh_adv", 2768, + NULL, (configMAX_PRIORITIES - 5), &adv_task_h, NIMBLE_CORE); +#endif + + /* For BT5 controllers we can have fast advertising interval */ + if (ble_hs_hci_get_hci_version() >= BLE_HCI_VER_BCS_5_0) { + adv_int_min = ADV_INT_FAST_MS; + } + + adv_initialized = true; +} + +int +ble_adv_gap_mesh_cb(struct ble_gap_event *event, void *arg) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) + struct ble_gap_ext_disc_desc *ext_desc; +#endif + struct ble_gap_disc_desc *desc; + struct os_mbuf *buf = NULL; + +#if BT_MESH_EXTENDED_DEBUG + BT_DBG("event->type %d", event->type); +#endif + + switch (event->type) { +#if MYNEWT_VAL(BLE_EXT_ADV) + case BLE_GAP_EVENT_EXT_DISC: + ext_desc = &event->ext_disc; + buf = os_mbuf_get_pkthdr(&adv_os_mbuf_pool, 0); + if (!buf || os_mbuf_append(buf, ext_desc->data, ext_desc->length_data)) { + BT_ERR("Could not append data"); + goto done; + } + bt_mesh_scan_cb(&ext_desc->addr, ext_desc->rssi, + ext_desc->legacy_event_type, buf); + break; +#endif + case BLE_GAP_EVENT_DISC: + desc = &event->disc; + buf = os_mbuf_get_pkthdr(&adv_os_mbuf_pool, 0); + if (!buf || os_mbuf_append(buf, desc->data, desc->length_data)) { + BT_ERR("Could not append data"); + goto done; + } + + bt_mesh_scan_cb(&desc->addr, desc->rssi, desc->event_type, buf); + break; + default: + break; + } + +done: + if (buf) { + os_mbuf_free_chain(buf); + } + + return 0; +} + +int bt_mesh_scan_enable(void) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) + struct ble_gap_ext_disc_params uncoded_params = + { .itvl = MESH_SCAN_INTERVAL, .window = MESH_SCAN_WINDOW, + .passive = 1 }; + + BT_DBG(""); + + return ble_gap_ext_disc(g_mesh_addr_type, 0, 0, 0, 0, 0, + &uncoded_params, NULL, NULL, NULL); +#else + struct ble_gap_disc_params scan_param = + { .passive = 1, .filter_duplicates = 0, .itvl = + MESH_SCAN_INTERVAL, .window = MESH_SCAN_WINDOW }; + + BT_DBG(""); + + return ble_gap_disc(g_mesh_addr_type, BLE_HS_FOREVER, &scan_param, NULL, NULL); +#endif +} + +int bt_mesh_scan_disable(void) +{ + BT_DBG(""); + + return ble_gap_disc_cancel(); +} diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.h new file mode 100644 index 000000000..8e76e4197 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.h @@ -0,0 +1,82 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ADV_H__ +#define __ADV_H__ + +/* Maximum advertising data payload for a single data type */ +#include "mesh/mesh.h" + +#define BT_MESH_ADV(om) (*(struct bt_mesh_adv **) OS_MBUF_USRHDR(om)) + +#define BT_MESH_ADV_DATA_SIZE 31 + +/* The user data is a pointer (4 bytes) to struct bt_mesh_adv */ +#define BT_MESH_ADV_USER_DATA_SIZE (sizeof(struct bt_mesh_adv *)) + +#define BT_MESH_MBUF_HEADER_SIZE (sizeof(struct os_mbuf_pkthdr) + \ + BT_MESH_ADV_USER_DATA_SIZE +\ + sizeof(struct os_mbuf)) + +enum bt_mesh_adv_type +{ + BT_MESH_ADV_PROV, + BT_MESH_ADV_DATA, + BT_MESH_ADV_BEACON, + BT_MESH_ADV_URI, +}; + +typedef void (*bt_mesh_adv_func_t)(struct os_mbuf *buf, u16_t duration, + int err, void *user_data); + +struct bt_mesh_adv { + const struct bt_mesh_send_cb *cb; + void *cb_data; + + u8_t type:2, + busy:1; + u8_t xmit; + + union { + /* Address, used e.g. for Friend Queue messages */ + u16_t addr; + + /* For transport layer segment sending */ + struct { + u8_t attempts; + } seg; + }; + + int ref_cnt; + struct ble_npl_event ev; +}; + +typedef struct bt_mesh_adv *(*bt_mesh_adv_alloc_t)(int id); + +/* xmit_count: Number of retransmissions, i.e. 0 == 1 transmission */ +struct os_mbuf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit, + s32_t timeout); + +struct os_mbuf *bt_mesh_adv_create_from_pool(struct os_mbuf_pool *pool, + bt_mesh_adv_alloc_t get_id, + enum bt_mesh_adv_type type, + u8_t xmit, s32_t timeout); + +void bt_mesh_adv_send(struct os_mbuf *buf, const struct bt_mesh_send_cb *cb, + void *cb_data); + +void bt_mesh_adv_update(void); + +void bt_mesh_adv_init(void); + +int bt_mesh_scan_enable(void); + +int bt_mesh_scan_disable(void); + +int ble_adv_gap_mesh_cb(struct ble_gap_event *event, void *arg); +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/atomic.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/atomic.h new file mode 100644 index 000000000..2c7317948 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/atomic.h @@ -0,0 +1,409 @@ +/* atomic operations */ + +/* + * Copyright (c) 1997-2015, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ATOMIC_H__ +#define __ATOMIC_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef int atomic_t; +typedef atomic_t atomic_val_t; + +/** + * @defgroup atomic_apis Atomic Services APIs + * @ingroup kernel_apis + * @{ + */ + +/** + * @brief Atomic compare-and-set. + * + * This routine performs an atomic compare-and-set on @a target. If the current + * value of @a target equals @a old_value, @a target is set to @a new_value. + * If the current value of @a target does not equal @a old_value, @a target + * is left unchanged. + * + * @param target Address of atomic variable. + * @param old_value Original value to compare against. + * @param new_value New value to store. + * @return 1 if @a new_value is written, 0 otherwise. + */ +static inline int atomic_cas(atomic_t *target, atomic_val_t old_value, + atomic_val_t new_value) +{ + return __atomic_compare_exchange_n(target, &old_value, new_value, + 0, __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic addition. + * + * This routine performs an atomic addition on @a target. + * + * @param target Address of atomic variable. + * @param value Value to add. + * + * @return Previous value of @a target. + */ +static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic subtraction. + * + * This routine performs an atomic subtraction on @a target. + * + * @param target Address of atomic variable. + * @param value Value to subtract. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic increment. + * + * This routine performs an atomic increment by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_inc(atomic_t *target) +{ + return atomic_add(target, 1); +} + +/** + * + * @brief Atomic decrement. + * + * This routine performs an atomic decrement by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_dec(atomic_t *target) +{ + return atomic_sub(target, 1); +} + +/** + * + * @brief Atomic get. + * + * This routine performs an atomic read on @a target. + * + * @param target Address of atomic variable. + * + * @return Value of @a target. + */ + +static inline atomic_val_t atomic_get(const atomic_t *target) +{ + return __atomic_load_n(target, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic get-and-set. + * + * This routine atomically sets @a target to @a value and returns + * the previous value of @a target. + * + * @param target Address of atomic variable. + * @param value Value to write to @a target. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value) +{ + /* This builtin, as described by Intel, is not a traditional + * test-and-set operation, but rather an atomic exchange operation. It + * writes value into *ptr, and returns the previous contents of *ptr. + */ + return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic clear. + * + * This routine atomically sets @a target to zero and returns its previous + * value. (Hence, it is equivalent to atomic_set(target, 0).) + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_clear(atomic_t *target) +{ + return atomic_set(target, 0); +} + +/** + * + * @brief Atomic bitwise inclusive OR. + * + * This routine atomically sets @a target to the bitwise inclusive OR of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to OR. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise exclusive OR (XOR). + * + * This routine atomically sets @a target to the bitwise exclusive OR (XOR) of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to XOR + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise AND. + * + * This routine atomically sets @a target to the bitwise AND of @a target + * and @a value. + * + * @param target Address of atomic variable. + * @param value Value to AND. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise NAND. + * + * This routine atomically sets @a target to the bitwise NAND of @a target + * and @a value. (This operation is equivalent to target = ~(target & value).) + * + * @param target Address of atomic variable. + * @param value Value to NAND. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST); +} + + /** + * @brief Initialize an atomic variable. + * + * This macro can be used to initialize an atomic variable. For example, + * @code atomic_t my_var = ATOMIC_INIT(75); @endcode + * + * @param i Value to assign to atomic variable. + */ +#define ATOMIC_INIT(i) (i) + + /** + * @cond INTERNAL_HIDDEN + */ + +#define ATOMIC_BITS (sizeof(atomic_val_t) * 8) +#define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1))) +#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS)) + + /** + * INTERNAL_HIDDEN @endcond + */ + + /** + * @brief Define an array of atomic variables. + * + * This macro defines an array of atomic variables containing at least + * @a num_bits bits. + * + * @note + * If used from file scope, the bits of the array are initialized to zero; + * if used from within a function, the bits are left uninitialized. + * + * @param name Name of array of atomic variables. + * @param num_bits Number of bits needed. + */ +#define ATOMIC_DEFINE(name, num_bits) \ + atomic_t name[1 + ((num_bits) - 1) / ATOMIC_BITS] + + /** + * @brief Atomically test a bit. + * + * This routine tests whether bit number @a bit of @a target is set or not. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_bit(const atomic_t *target, int bit) + { + atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit)); + + return (1 & (val >> (bit & (ATOMIC_BITS - 1)))); + } + + /** + * @brief Atomically test and clear a bit. + * + * Atomically clear bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_and_clear_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_and(ATOMIC_ELEM(target, bit), ~mask); + + return (old & mask) != 0; + } + + /** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_and_set_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_or(ATOMIC_ELEM(target, bit), mask); + + return (old & mask) != 0; + } + + /** + * @brief Atomically clear a bit. + * + * Atomically clear bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + static inline void + atomic_clear_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_and(ATOMIC_ELEM(target, bit), ~mask); + } + + /** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + static inline void + atomic_set_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_or(ATOMIC_ELEM(target, bit), mask); + } + +/** +* @brief Atomically set a bit to a given value. +* +* Atomically set bit number @a bit of @a target to value @a val. +* The target may be a single atomic variable or an array of them. +* +* @param target Address of atomic variable or array. +* @param bit Bit number (starting from 0). +* @param val true for 1, false for 0. +* +* @return N/A +*/ +static inline void atomic_set_bit_to(atomic_t *target, int bit, bool val) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + + if (val) { + (void)atomic_or(ATOMIC_ELEM(target, bit), mask); + } else { + (void)atomic_and(ATOMIC_ELEM(target, bit), ~mask); + } +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ATOMIC_H__ */ diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.c new file mode 100644 index 000000000..da909852f --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.c @@ -0,0 +1,411 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "os/os_mbuf.h" +#include "mesh/mesh.h" + +#include "syscfg/syscfg.h" +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_BEACON)) +#include "host/ble_hs_log.h" + +#include "adv.h" +#include "mesh_priv.h" +#include "net.h" +#include "prov.h" +#include "crypto.h" +#include "beacon.h" +#include "foundation.h" + +#define UNPROVISIONED_INTERVAL (K_SECONDS(5)) +#define PROVISIONED_INTERVAL (K_SECONDS(10)) + +#define BEACON_TYPE_UNPROVISIONED 0x00 +#define BEACON_TYPE_SECURE 0x01 + +/* 3 transmissions, 20ms interval */ +#define UNPROV_XMIT BT_MESH_TRANSMIT(2, 20) + +/* 1 transmission, 20ms interval */ +#define PROV_XMIT BT_MESH_TRANSMIT(0, 20) + +static struct k_delayed_work beacon_timer; + +static struct bt_mesh_subnet *cache_check(u8_t data[21]) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (!memcmp(sub->beacon_cache, data, 21)) { + return sub; + } + } + + return NULL; +} + +static void cache_add(u8_t data[21], struct bt_mesh_subnet *sub) +{ + memcpy(sub->beacon_cache, data, 21); +} + +static void beacon_complete(int err, void *user_data) +{ + struct bt_mesh_subnet *sub = user_data; + + BT_DBG("err %d", err); + + sub->beacon_sent = k_uptime_get_32(); +} + +void bt_mesh_beacon_create(struct bt_mesh_subnet *sub, + struct os_mbuf *buf) +{ + u8_t flags = bt_mesh_net_flags(sub); + struct bt_mesh_subnet_keys *keys; + + net_buf_simple_add_u8(buf, BEACON_TYPE_SECURE); + + if (sub->kr_flag) { + keys = &sub->keys[1]; + } else { + keys = &sub->keys[0]; + } + + net_buf_simple_add_u8(buf, flags); + + /* Network ID */ + net_buf_simple_add_mem(buf, keys->net_id, 8); + + /* IV Index */ + net_buf_simple_add_be32(buf, bt_mesh.iv_index); + + net_buf_simple_add_mem(buf, sub->auth, 8); + + BT_DBG("net_idx 0x%04x flags 0x%02x NetID %s", sub->net_idx, + flags, bt_hex(keys->net_id, 8)); + BT_DBG("IV Index 0x%08x Auth %s", (unsigned) bt_mesh.iv_index, + bt_hex(sub->auth, 8)); +} + +/* If the interval has passed or is within 5 seconds from now send a beacon */ +#define BEACON_THRESHOLD(sub) (K_SECONDS(10 * ((sub)->beacons_last + 1)) - \ + K_SECONDS(5)) + +static int secure_beacon_send(void) +{ + static const struct bt_mesh_send_cb send_cb = { + .end = beacon_complete, + }; + u32_t now = k_uptime_get_32(); + int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + struct os_mbuf *buf; + u32_t time_diff; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + time_diff = now - sub->beacon_sent; + if (time_diff < K_SECONDS(600) && + time_diff < BEACON_THRESHOLD(sub)) { + continue; + } + + buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, PROV_XMIT, + K_NO_WAIT); + if (!buf) { + BT_ERR("Unable to allocate beacon buffer"); + return -ENOBUFS; + } + + bt_mesh_beacon_create(sub, buf); + + bt_mesh_adv_send(buf, &send_cb, sub); + net_buf_unref(buf); + } + + return 0; +} + +static int unprovisioned_beacon_send(void) +{ +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + const struct bt_mesh_prov *prov; + u8_t uri_hash[16] = { 0 }; + struct os_mbuf *buf; + u16_t oob_info; + + BT_DBG("unprovisioned_beacon_send"); + + buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, UNPROV_XMIT, K_NO_WAIT); + if (!buf) { + BT_ERR("Unable to allocate beacon buffer"); + return -ENOBUFS; + } + + prov = bt_mesh_prov_get(); + + net_buf_add_u8(buf, BEACON_TYPE_UNPROVISIONED); + net_buf_add_mem(buf, prov->uuid, 16); + + if (prov->uri && bt_mesh_s1(prov->uri, uri_hash) == 0) { + oob_info = prov->oob_info | BT_MESH_PROV_OOB_URI; + } else { + oob_info = prov->oob_info; + } + + net_buf_add_be16(buf, oob_info); + net_buf_add_mem(buf, uri_hash, 4); + + bt_mesh_adv_send(buf, NULL, NULL); + net_buf_unref(buf); + + if (prov->uri) { + size_t len; + + buf = bt_mesh_adv_create(BT_MESH_ADV_URI, UNPROV_XMIT, + K_NO_WAIT); + if (!buf) { + BT_ERR("Unable to allocate URI buffer"); + return -ENOBUFS; + } + + len = strlen(prov->uri); + if (net_buf_tailroom(buf) < len) { + BT_WARN("Too long URI to fit advertising data"); + } else { + net_buf_add_mem(buf, prov->uri, len); + bt_mesh_adv_send(buf, NULL, NULL); + } + + net_buf_unref(buf); + } + +#endif /* MYNEWT_VAL(BLE_MESH_PB_ADV) */ + return 0; +} + +static void update_beacon_observation(void) +{ + static bool first_half; + int i; + + /* Observation period is 20 seconds, whereas the beacon timer + * runs every 10 seconds. We process what's happened during the + * window only after the seconnd half. + */ + first_half = !first_half; + if (first_half) { + return; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + sub->beacons_last = sub->beacons_cur; + sub->beacons_cur = 0; + } +} + +static void beacon_send(struct ble_npl_event *work) +{ + /* Don't send anything if we have an active provisioning link */ + if ((MYNEWT_VAL(BLE_MESH_PROV)) && bt_prov_active()) { + k_delayed_work_submit(&beacon_timer, UNPROVISIONED_INTERVAL); + return; + } + + BT_DBG(""); + + if (bt_mesh_is_provisioned()) { + update_beacon_observation(); + secure_beacon_send(); + + /* Only resubmit if beaconing is still enabled */ + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED || + atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) { + k_delayed_work_submit(&beacon_timer, + PROVISIONED_INTERVAL); + } + } else { + unprovisioned_beacon_send(); + k_delayed_work_submit(&beacon_timer, UNPROVISIONED_INTERVAL); + } + +} + +static void secure_beacon_recv(struct os_mbuf *buf) +{ + u8_t *data, *net_id, *auth; + struct bt_mesh_subnet *sub; + u32_t iv_index; + bool new_key, kr_change, iv_change; + u8_t flags; + + if (buf->om_len < 21) { + BT_ERR("Too short secure beacon (len %u)", buf->om_len); + return; + } + + sub = cache_check(buf->om_data); + if (sub) { + /* We've seen this beacon before - just update the stats */ + goto update_stats; + } + + /* So we can add to the cache if auth matches */ + data = buf->om_data; + + flags = net_buf_simple_pull_u8(buf); + net_id = net_buf_simple_pull_mem(buf, 8); + iv_index = net_buf_simple_pull_be32(buf); + auth = buf->om_data; + + BT_DBG("flags 0x%02x id %s iv_index 0x%08x", + flags, bt_hex(net_id, 8), (unsigned) iv_index); + + sub = bt_mesh_subnet_find(net_id, flags, iv_index, auth, &new_key); + if (!sub) { + BT_DBG("No subnet that matched beacon"); + return; + } + + if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !new_key) { + BT_WARN("Ignoring Phase 2 KR Update secured using old key"); + return; + } + + cache_add(data, sub); + + /* If we have NetKey0 accept initiation only from it */ + if (bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY) && + sub->net_idx != BT_MESH_KEY_PRIMARY) { + BT_WARN("Ignoring secure beacon on non-primary subnet"); + goto update_stats; + } + + BT_DBG("net_idx 0x%04x iv_index 0x%08x, current iv_index 0x%08x", + sub->net_idx, (unsigned) iv_index, (unsigned) bt_mesh.iv_index); + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) && + (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) == + BT_MESH_IV_UPDATE(flags))) { + bt_mesh_beacon_ivu_initiator(false); + } + + iv_change = bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(flags)); + + kr_change = bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(flags), new_key); + if (kr_change) { + bt_mesh_net_beacon_update(sub); + } + + if (iv_change) { + /* Update all subnets */ + bt_mesh_net_sec_update(NULL); + } else if (kr_change) { + /* Key Refresh without IV Update only impacts one subnet */ + bt_mesh_net_sec_update(sub); + } + +update_stats: + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED && + sub->beacons_cur < 0xff) { + sub->beacons_cur++; + } +} + +void bt_mesh_beacon_recv(struct os_mbuf *buf) +{ + u8_t type; + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (buf->om_len < 1) { + BT_ERR("Too short beacon"); + return; + } + + type = net_buf_simple_pull_u8(buf); + switch (type) { + case BEACON_TYPE_UNPROVISIONED: + BT_DBG("Ignoring unprovisioned device beacon"); + break; + case BEACON_TYPE_SECURE: + secure_beacon_recv(buf); + break; + default: + BT_WARN("Unknown beacon type 0x%02x", type); + break; + } +} + +void bt_mesh_beacon_init(void) +{ + k_delayed_work_init(&beacon_timer, beacon_send); +} + +void bt_mesh_beacon_ivu_initiator(bool enable) +{ + atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_INITIATOR, enable); + + if (enable) { + k_work_submit(&beacon_timer.work); + } else if (bt_mesh_beacon_get() == BT_MESH_BEACON_DISABLED) { + k_delayed_work_cancel(&beacon_timer); + } +} + +void bt_mesh_beacon_enable(void) +{ + int i; + + if (!bt_mesh_is_provisioned()) { + k_work_submit(&beacon_timer.work); + return; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + sub->beacons_last = 0; + sub->beacons_cur = 0; + + bt_mesh_net_beacon_update(sub); + } + + k_work_submit(&beacon_timer.work); +} + +void bt_mesh_beacon_disable(void) +{ + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) { + k_delayed_work_cancel(&beacon_timer); + } +} diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.h new file mode 100644 index 000000000..ac4bfed8a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.h @@ -0,0 +1,26 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BEACON_H__ +#define __BEACON_H__ + +#include "os/os_mbuf.h" + +void bt_mesh_beacon_enable(void); +void bt_mesh_beacon_disable(void); + +void bt_mesh_beacon_ivu_initiator(bool enable); + +void bt_mesh_beacon_recv(struct os_mbuf *buf); + +void bt_mesh_beacon_create(struct bt_mesh_subnet *sub, + struct os_mbuf *buf); + +void bt_mesh_beacon_init(void); + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_att_priv.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_att_priv.h new file mode 100644 index 000000000..2201d4ddb --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_att_priv.h @@ -0,0 +1,307 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_ATT_PRIV_ +#define H_BLE_ATT_PRIV_ + +#include +#include "stats/stats.h" +#include "host/ble_att.h" +#include "host/ble_uuid.h" +#include "nimble/nimble_npl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mbuf; +struct ble_hs_conn; +struct ble_l2cap_chan; +struct ble_att_find_info_req; +struct ble_att_error_rsp; +struct ble_att_mtu_cmd; +struct ble_att_read_req; +struct ble_att_read_blob_req; +struct ble_att_read_type_req; +struct ble_att_read_group_type_req; +struct ble_att_read_group_type_rsp; +struct ble_att_find_type_value_req; +struct ble_att_write_req; +struct ble_att_prep_write_cmd; +struct ble_att_exec_write_req; +struct ble_att_notify_req; +struct ble_att_indicate_req; + +STATS_SECT_START(ble_att_stats) + STATS_SECT_ENTRY(error_rsp_rx) + STATS_SECT_ENTRY(error_rsp_tx) + STATS_SECT_ENTRY(mtu_req_rx) + STATS_SECT_ENTRY(mtu_req_tx) + STATS_SECT_ENTRY(mtu_rsp_rx) + STATS_SECT_ENTRY(mtu_rsp_tx) + STATS_SECT_ENTRY(find_info_req_rx) + STATS_SECT_ENTRY(find_info_req_tx) + STATS_SECT_ENTRY(find_info_rsp_rx) + STATS_SECT_ENTRY(find_info_rsp_tx) + STATS_SECT_ENTRY(find_type_value_req_rx) + STATS_SECT_ENTRY(find_type_value_req_tx) + STATS_SECT_ENTRY(find_type_value_rsp_rx) + STATS_SECT_ENTRY(find_type_value_rsp_tx) + STATS_SECT_ENTRY(read_type_req_rx) + STATS_SECT_ENTRY(read_type_req_tx) + STATS_SECT_ENTRY(read_type_rsp_rx) + STATS_SECT_ENTRY(read_type_rsp_tx) + STATS_SECT_ENTRY(read_req_rx) + STATS_SECT_ENTRY(read_req_tx) + STATS_SECT_ENTRY(read_rsp_rx) + STATS_SECT_ENTRY(read_rsp_tx) + STATS_SECT_ENTRY(read_blob_req_rx) + STATS_SECT_ENTRY(read_blob_req_tx) + STATS_SECT_ENTRY(read_blob_rsp_rx) + STATS_SECT_ENTRY(read_blob_rsp_tx) + STATS_SECT_ENTRY(read_mult_req_rx) + STATS_SECT_ENTRY(read_mult_req_tx) + STATS_SECT_ENTRY(read_mult_rsp_rx) + STATS_SECT_ENTRY(read_mult_rsp_tx) + STATS_SECT_ENTRY(read_group_type_req_rx) + STATS_SECT_ENTRY(read_group_type_req_tx) + STATS_SECT_ENTRY(read_group_type_rsp_rx) + STATS_SECT_ENTRY(read_group_type_rsp_tx) + STATS_SECT_ENTRY(write_req_rx) + STATS_SECT_ENTRY(write_req_tx) + STATS_SECT_ENTRY(write_rsp_rx) + STATS_SECT_ENTRY(write_rsp_tx) + STATS_SECT_ENTRY(prep_write_req_rx) + STATS_SECT_ENTRY(prep_write_req_tx) + STATS_SECT_ENTRY(prep_write_rsp_rx) + STATS_SECT_ENTRY(prep_write_rsp_tx) + STATS_SECT_ENTRY(exec_write_req_rx) + STATS_SECT_ENTRY(exec_write_req_tx) + STATS_SECT_ENTRY(exec_write_rsp_rx) + STATS_SECT_ENTRY(exec_write_rsp_tx) + STATS_SECT_ENTRY(notify_req_rx) + STATS_SECT_ENTRY(notify_req_tx) + STATS_SECT_ENTRY(indicate_req_rx) + STATS_SECT_ENTRY(indicate_req_tx) + STATS_SECT_ENTRY(indicate_rsp_rx) + STATS_SECT_ENTRY(indicate_rsp_tx) + STATS_SECT_ENTRY(write_cmd_rx) + STATS_SECT_ENTRY(write_cmd_tx) +STATS_SECT_END +extern STATS_SECT_DECL(ble_att_stats) ble_att_stats; + +struct ble_att_prep_entry { + SLIST_ENTRY(ble_att_prep_entry) bape_next; + uint16_t bape_handle; + uint16_t bape_offset; + + /* XXX: This is wasteful; we should use one mbuf chain for the entire + * prepared write, and compress the data into as few mbufs as possible. + */ + struct os_mbuf *bape_value; +}; + +SLIST_HEAD(ble_att_prep_entry_list, ble_att_prep_entry); + +struct ble_att_svr_conn { + /** This list is sorted by attribute handle ID. */ + struct ble_att_prep_entry_list basc_prep_list; + ble_npl_time_t basc_prep_timeout_at; +}; + +/** + * Handles a host attribute request. + * + * @param entry The host attribute being requested. + * @param op The operation being performed on the attribute. + * @param arg The request data associated with that host + * attribute. + * + * @return 0 on success; + * One of the BLE_ATT_ERR_[...] codes on + * failure. + */ +typedef int ble_att_svr_access_fn(uint16_t conn_handle, uint16_t attr_handle, + uint8_t op, uint16_t offset, + struct os_mbuf **om, void *arg); + +int ble_att_svr_register(const ble_uuid_t *uuid, uint8_t flags, + uint8_t min_key_size, uint16_t *handle_id, + ble_att_svr_access_fn *cb, void *cb_arg); + +struct ble_att_svr_entry { + STAILQ_ENTRY(ble_att_svr_entry) ha_next; + + const ble_uuid_t *ha_uuid; + uint8_t ha_flags; + uint8_t ha_min_key_size; + uint16_t ha_handle_id; + ble_att_svr_access_fn *ha_cb; + void *ha_cb_arg; +}; + +SLIST_HEAD(ble_att_clt_entry_list, ble_att_clt_entry); + +/*** @gen */ + +struct ble_l2cap_chan *ble_att_create_chan(uint16_t conn_handle); +int ble_att_conn_chan_find(uint16_t conn_handle, struct ble_hs_conn **out_conn, + struct ble_l2cap_chan **out_chan); +void ble_att_inc_tx_stat(uint8_t att_op); +void ble_att_truncate_to_mtu(const struct ble_l2cap_chan *att_chan, + struct os_mbuf *txom); +void ble_att_set_peer_mtu(struct ble_l2cap_chan *chan, uint16_t peer_mtu); +uint16_t ble_att_chan_mtu(const struct ble_l2cap_chan *chan); +int ble_att_init(void); + +#define BLE_ATT_LOG_CMD(is_tx, cmd_name, conn_handle, log_cb, cmd) \ + BLE_HS_LOG_CMD((is_tx), "att", (cmd_name), (conn_handle), (log_cb), (cmd)) + +#define BLE_ATT_LOG_EMPTY_CMD(is_tx, cmd_name, conn_handle) \ + BLE_HS_LOG_EMPTY_CMD((is_tx), "att", (cmd_name), (conn_handle)) + +/*** @svr */ + +int ble_att_svr_start(void); +void ble_att_svr_stop(void); + +struct ble_att_svr_entry * +ble_att_svr_find_by_uuid(struct ble_att_svr_entry *start_at, + const ble_uuid_t *uuid, + uint16_t end_handle); +uint16_t ble_att_svr_prev_handle(void); +int ble_att_svr_rx_mtu(uint16_t conn_handle, struct os_mbuf **rxom); +struct ble_att_svr_entry *ble_att_svr_find_by_handle(uint16_t handle_id); +int32_t ble_att_svr_ticks_until_tmo(const struct ble_att_svr_conn *svr, + ble_npl_time_t now); +int ble_att_svr_rx_find_info(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_svr_rx_find_type_value(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_svr_rx_read_type(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_svr_rx_read_group_type(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_svr_rx_read(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_svr_rx_read_blob(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_svr_rx_read_mult(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_svr_rx_write(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_svr_rx_write_no_rsp(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_svr_rx_prep_write(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_svr_rx_exec_write(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_svr_rx_notify(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_svr_rx_indicate(uint16_t conn_handle, + struct os_mbuf **rxom); +void ble_att_svr_prep_clear(struct ble_att_prep_entry_list *prep_list); +int ble_att_svr_read_handle(uint16_t conn_handle, uint16_t attr_handle, + uint16_t offset, struct os_mbuf *om, + uint8_t *out_att_err); +void ble_att_svr_reset(void); +int ble_att_svr_init(void); + +void ble_att_svr_hide_range(uint16_t start_handle, uint16_t end_handle); +void ble_att_svr_restore_range(uint16_t start_handle, uint16_t end_handle); + +int ble_att_svr_tx_error_rsp(uint16_t conn_handle, struct os_mbuf *txom, + uint8_t req_op, uint16_t handle, + uint8_t error_code); +/*** $clt */ + +/** An information-data entry in a find information response. */ +struct ble_att_find_info_idata { + uint16_t attr_handle; + ble_uuid_any_t uuid; +}; + +/** A handles-information entry in a find by type value response. */ +struct ble_att_find_type_value_hinfo { + uint16_t attr_handle; + uint16_t group_end_handle; +}; + +/** An attribute-data entry in a read by type response. */ +struct ble_att_read_type_adata { + uint16_t att_handle; + int value_len; + uint8_t *value; + +}; + +/** An attribute-data entry in a read by group type response. */ +struct ble_att_read_group_type_adata { + uint16_t att_handle; + uint16_t end_group_handle; + int value_len; + uint8_t *value; +}; + +int ble_att_clt_rx_error(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_tx_mtu(uint16_t conn_handle, uint16_t mtu); +int ble_att_clt_rx_mtu(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_tx_read(uint16_t conn_handle, uint16_t handle); +int ble_att_clt_rx_read(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_tx_read_blob(uint16_t conn_handle, uint16_t handle, + uint16_t offset); +int ble_att_clt_rx_read_blob(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_tx_read_mult(uint16_t conn_handle, + const uint16_t *handles, int num_handles); +int ble_att_clt_rx_read_mult(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_tx_read_type(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid); +int ble_att_clt_rx_read_type(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_tx_read_group_type(uint16_t conn_handle, + uint16_t start_handle, uint16_t end_handle, + const ble_uuid_t *uuid128); +int ble_att_clt_rx_read_group_type(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_clt_tx_find_info(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle); +int ble_att_clt_rx_find_info(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_tx_find_type_value(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, uint16_t attribute_type, + const void *attribute_value, int value_len); +int ble_att_clt_rx_find_type_value(uint16_t conn_handle, + struct os_mbuf **rxom); +int ble_att_clt_tx_write_req(uint16_t conn_handle, uint16_t handle, + struct os_mbuf *txom); +int ble_att_clt_tx_write_cmd(uint16_t conn_handle, uint16_t handle, + struct os_mbuf *txom); +int ble_att_clt_tx_prep_write(uint16_t conn_handle, uint16_t handle, + uint16_t offset, struct os_mbuf *txom); +int ble_att_clt_rx_prep_write(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_tx_exec_write(uint16_t conn_handle, uint8_t flags); +int ble_att_clt_rx_exec_write(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_rx_write(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_tx_notify(uint16_t conn_handle, uint16_t handle, + struct os_mbuf *txom); +int ble_att_clt_tx_indicate(uint16_t conn_handle, uint16_t handle, + struct os_mbuf *txom); +int ble_att_clt_rx_indicate(uint16_t conn_handle, struct os_mbuf **rxom); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_gatt_priv.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_gatt_priv.h new file mode 100644 index 000000000..4a59635b8 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_gatt_priv.h @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_GATT_PRIV_ +#define H_BLE_GATT_PRIV_ + +#include "syscfg/syscfg.h" +#include "stats/stats.h" +#include "host/ble_gatt.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_att_read_type_adata; +struct ble_att_find_type_value_hinfo; +struct ble_att_find_info_idata; +struct ble_att_read_group_type_adata; +struct ble_att_prep_write_cmd; + +STATS_SECT_START(ble_gattc_stats) + STATS_SECT_ENTRY(mtu) + STATS_SECT_ENTRY(mtu_fail) + STATS_SECT_ENTRY(disc_all_svcs) + STATS_SECT_ENTRY(disc_all_svcs_fail) + STATS_SECT_ENTRY(disc_svc_uuid) + STATS_SECT_ENTRY(disc_svc_uuid_fail) + STATS_SECT_ENTRY(find_inc_svcs) + STATS_SECT_ENTRY(find_inc_svcs_fail) + STATS_SECT_ENTRY(disc_all_chrs) + STATS_SECT_ENTRY(disc_all_chrs_fail) + STATS_SECT_ENTRY(disc_chrs_uuid) + STATS_SECT_ENTRY(disc_chrs_uuid_fail) + STATS_SECT_ENTRY(disc_all_dscs) + STATS_SECT_ENTRY(disc_all_dscs_fail) + STATS_SECT_ENTRY(read) + STATS_SECT_ENTRY(read_fail) + STATS_SECT_ENTRY(read_uuid) + STATS_SECT_ENTRY(read_uuid_fail) + STATS_SECT_ENTRY(read_long) + STATS_SECT_ENTRY(read_long_fail) + STATS_SECT_ENTRY(read_mult) + STATS_SECT_ENTRY(read_mult_fail) + STATS_SECT_ENTRY(write_no_rsp) + STATS_SECT_ENTRY(write_no_rsp_fail) + STATS_SECT_ENTRY(write) + STATS_SECT_ENTRY(write_fail) + STATS_SECT_ENTRY(write_long) + STATS_SECT_ENTRY(write_long_fail) + STATS_SECT_ENTRY(write_reliable) + STATS_SECT_ENTRY(write_reliable_fail) + STATS_SECT_ENTRY(notify) + STATS_SECT_ENTRY(notify_fail) + STATS_SECT_ENTRY(indicate) + STATS_SECT_ENTRY(indicate_fail) + STATS_SECT_ENTRY(proc_timeout) +STATS_SECT_END +extern STATS_SECT_DECL(ble_gattc_stats) ble_gattc_stats; + +STATS_SECT_START(ble_gatts_stats) + STATS_SECT_ENTRY(svcs) + STATS_SECT_ENTRY(chrs) + STATS_SECT_ENTRY(dscs) + STATS_SECT_ENTRY(svc_def_reads) + STATS_SECT_ENTRY(svc_inc_reads) + STATS_SECT_ENTRY(chr_def_reads) + STATS_SECT_ENTRY(chr_val_reads) + STATS_SECT_ENTRY(chr_val_writes) + STATS_SECT_ENTRY(dsc_reads) + STATS_SECT_ENTRY(dsc_writes) +STATS_SECT_END +extern STATS_SECT_DECL(ble_gatts_stats) ble_gatts_stats; + +#define BLE_GATT_CHR_DECL_SZ_16 5 +#define BLE_GATT_CHR_DECL_SZ_128 19 + +typedef uint8_t ble_gatts_conn_flags; + +struct ble_gatts_conn { + struct ble_gatts_clt_cfg *clt_cfgs; + int num_clt_cfgs; + + uint16_t indicate_val_handle; +}; + +/*** @client. */ + +int ble_gattc_locked_by_cur_task(void); +void ble_gatts_indicate_fail_notconn(uint16_t conn_handle); + +void ble_gattc_rx_err(uint16_t conn_handle, uint16_t handle, uint16_t status); +void ble_gattc_rx_mtu(uint16_t conn_handle, int status, uint16_t chan_mtu); +void ble_gattc_rx_read_type_adata(uint16_t conn_handle, + struct ble_att_read_type_adata *adata); +void ble_gattc_rx_read_type_complete(uint16_t conn_handle, int status); +void ble_gattc_rx_read_rsp(uint16_t conn_handle, int status, + struct os_mbuf **rxom); +void ble_gattc_rx_read_blob_rsp(uint16_t conn_handle, int status, + struct os_mbuf **rxom); +void ble_gattc_rx_read_mult_rsp(uint16_t conn_handle, int status, + struct os_mbuf **rxom); +void ble_gattc_rx_read_group_type_adata( + uint16_t conn_handle, struct ble_att_read_group_type_adata *adata); +void ble_gattc_rx_read_group_type_complete(uint16_t conn_handle, int rc); +void ble_gattc_rx_find_type_value_hinfo( + uint16_t conn_handle, struct ble_att_find_type_value_hinfo *hinfo); +void ble_gattc_rx_find_type_value_complete(uint16_t conn_handle, int status); +void ble_gattc_rx_write_rsp(uint16_t conn_handle); +void ble_gattc_rx_prep_write_rsp(uint16_t conn_handle, int status, + uint16_t handle, uint16_t offset, + struct os_mbuf **rxom); +void ble_gattc_rx_exec_write_rsp(uint16_t conn_handle, int status); +void ble_gattc_rx_indicate_rsp(uint16_t conn_handle); +void ble_gattc_rx_find_info_idata(uint16_t conn_handle, + struct ble_att_find_info_idata *idata); +void ble_gattc_rx_find_info_complete(uint16_t conn_handle, int status); +void ble_gattc_connection_txable(uint16_t conn_handle); +void ble_gattc_connection_broken(uint16_t conn_handle); +int32_t ble_gattc_timer(void); + +int ble_gattc_any_jobs(void); +int ble_gattc_init(void); + +/*** @server. */ +#define BLE_GATTS_CLT_CFG_F_NOTIFY 0x0001 +#define BLE_GATTS_CLT_CFG_F_INDICATE 0x0002 +#define BLE_GATTS_CLT_CFG_F_MODIFIED 0x0080 /* Internal only. */ +#define BLE_GATTS_CLT_CFG_F_RESERVED 0xfffc + +#define BLE_GATTS_INC_SVC_LEN_NO_UUID 4 +#define BLE_GATTS_INC_SVC_LEN_UUID 6 + +/** + * Contains counts of resources required by the GATT server. The contents of + * this struct are generally used to populate a configuration struct before + * the host is initialized. + */ +struct ble_gatt_resources { + /** Number of services. */ + uint16_t svcs; + + /** Number of included services. */ + uint16_t incs; + + /** Number of characteristics. */ + uint16_t chrs; + + /** Number of descriptors. */ + uint16_t dscs; + + /** + * Number of client characteristic configuration descriptors. Each of + * these also contributes to the total descriptor count. + */ + uint16_t cccds; + + /** Total number of ATT attributes. */ + uint16_t attrs; +}; + +int ble_gatts_rx_indicate_ack(uint16_t conn_handle, uint16_t chr_val_handle); +int ble_gatts_send_next_indicate(uint16_t conn_handle); +void ble_gatts_tx_notifications(void); +void ble_gatts_bonding_established(uint16_t conn_handle); +void ble_gatts_bonding_restored(uint16_t conn_handle); +void ble_gatts_connection_broken(uint16_t conn_handle); +void ble_gatts_lcl_svc_foreach(ble_gatt_svc_foreach_fn cb, void *arg); +int ble_gatts_register_svcs(const struct ble_gatt_svc_def *svcs, + ble_gatt_register_fn *register_cb, + void *cb_arg); +int ble_gatts_clt_cfg_access(uint16_t conn_handle, uint16_t attr_handle, + uint8_t op, uint16_t offset, struct os_mbuf **om, + void *arg); + +/*** @misc. */ +int ble_gatts_conn_can_alloc(void); +int ble_gatts_conn_init(struct ble_gatts_conn *gatts_conn); +int ble_gatts_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_hs_conn_priv.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_hs_conn_priv.h new file mode 100644 index 000000000..92aacd405 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_hs_conn_priv.h @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_CONN_ +#define H_BLE_HS_CONN_ + +#include +#include "ble_l2cap_priv.h" +#include "ble_gatt_priv.h" +#include "ble_att_priv.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct hci_le_conn_complete; +struct hci_create_conn; +struct ble_l2cap_chan; + +typedef uint8_t ble_hs_conn_flags_t; + +#define BLE_HS_CONN_F_MASTER 0x01 +#define BLE_HS_CONN_F_TERMINATING 0x02 +#define BLE_HS_CONN_F_TX_FRAG 0x04 /* Cur ACL packet partially txed. */ + +struct ble_hs_conn { + SLIST_ENTRY(ble_hs_conn) bhc_next; + uint16_t bhc_handle; + uint8_t bhc_our_addr_type; +#if MYNEWT_VAL(BLE_EXT_ADV) + uint8_t bhc_our_rnd_addr[6]; +#endif + ble_addr_t bhc_peer_addr; + ble_addr_t bhc_our_rpa_addr; + ble_addr_t bhc_peer_rpa_addr; + + uint16_t bhc_itvl; + uint16_t bhc_latency; + uint16_t bhc_supervision_timeout; + uint8_t bhc_master_clock_accuracy; + + uint32_t supported_feat; + + ble_hs_conn_flags_t bhc_flags; + + struct ble_l2cap_chan_list bhc_channels; + struct ble_l2cap_chan *bhc_rx_chan; /* Channel rxing current packet. */ + ble_npl_time_t bhc_rx_timeout; + + /** + * Count of packets sent over this connection that the controller has not + * transmitted or flushed yet. + */ + uint16_t bhc_outstanding_pkts; + +#if MYNEWT_VAL(BLE_HS_FLOW_CTRL) + /** + * Count of packets received over this connection that have been processed + * and freed. + */ + uint16_t bhc_completed_pkts; +#endif + + /** Queue of outgoing packets that could not be sent. */ + STAILQ_HEAD(, os_mbuf_pkthdr) bhc_tx_q; + + struct ble_att_svr_conn bhc_att_svr; + struct ble_gatts_conn bhc_gatt_svr; + + struct ble_gap_sec_state bhc_sec_state; + + ble_gap_event_fn *bhc_cb; + void *bhc_cb_arg; +}; + +struct ble_hs_conn_addrs { + ble_addr_t our_id_addr; + ble_addr_t peer_id_addr; + ble_addr_t our_ota_addr; + ble_addr_t peer_ota_addr; +}; + +int ble_hs_conn_can_alloc(void); +struct ble_hs_conn *ble_hs_conn_alloc(uint16_t conn_handle); +void ble_hs_conn_free(struct ble_hs_conn *conn); +void ble_hs_conn_insert(struct ble_hs_conn *conn); +void ble_hs_conn_remove(struct ble_hs_conn *conn); +struct ble_hs_conn *ble_hs_conn_find(uint16_t conn_handle); +struct ble_hs_conn *ble_hs_conn_find_assert(uint16_t conn_handle); +struct ble_hs_conn *ble_hs_conn_find_by_addr(const ble_addr_t *addr); +struct ble_hs_conn *ble_hs_conn_find_by_idx(int idx); +int ble_hs_conn_exists(uint16_t conn_handle); +struct ble_hs_conn *ble_hs_conn_first(void); +struct ble_l2cap_chan *ble_hs_conn_chan_find_by_scid(struct ble_hs_conn *conn, + uint16_t cid); +struct ble_l2cap_chan *ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn *conn, + uint16_t cid); +int ble_hs_conn_chan_insert(struct ble_hs_conn *conn, + struct ble_l2cap_chan *chan); +void +ble_hs_conn_delete_chan(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan); + +void ble_hs_conn_addrs(const struct ble_hs_conn *conn, + struct ble_hs_conn_addrs *addrs); +int32_t ble_hs_conn_timer(void); + +int ble_hs_conn_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_coc_priv.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_coc_priv.h new file mode 100644 index 000000000..0a1a97b77 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_coc_priv.h @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_L2CAP_COC_PRIV_ +#define H_L2CAP_COC_PRIV_ + +#include +#include "syscfg/syscfg.h" +#include "os/queue.h" +#include "os/os_mbuf.h" +#include "host/ble_l2cap.h" +#include "ble_l2cap_sig_priv.h" +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_L2CAP_COC_CID_START 0x0040 +#define BLE_L2CAP_COC_CID_END 0x007F + +struct ble_l2cap_chan; + +#define BLE_L2CAP_COC_FLAG_STALLED 0x01 + +struct ble_l2cap_coc_endpoint { + struct os_mbuf *sdu; + uint16_t mtu; + uint16_t credits; + uint16_t data_offset; + uint8_t flags; +}; + +struct ble_l2cap_coc_srv { + STAILQ_ENTRY(ble_l2cap_coc_srv) next; + uint16_t psm; + uint16_t mtu; + ble_l2cap_event_fn *cb; + void *cb_arg; +}; + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0 +int ble_l2cap_coc_init(void); +int ble_l2cap_coc_create_server(uint16_t psm, uint16_t mtu, + ble_l2cap_event_fn *cb, void *cb_arg); +int ble_l2cap_coc_create_srv_chan(uint16_t conn_handle, uint16_t psm, + struct ble_l2cap_chan **chan); +struct ble_l2cap_chan * ble_l2cap_coc_chan_alloc(uint16_t conn_handle, + uint16_t psm, uint16_t mtu, + struct os_mbuf *sdu_rx, + ble_l2cap_event_fn *cb, + void *cb_arg); +void ble_l2cap_coc_cleanup_chan(struct ble_l2cap_chan *chan); +void ble_l2cap_coc_le_credits_update(uint16_t conn_handle, uint16_t dcid, + uint16_t credits); +int ble_l2cap_coc_recv_ready(struct ble_l2cap_chan *chan, + struct os_mbuf *sdu_rx); +int ble_l2cap_coc_send(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_tx); +#else +#define ble_l2cap_coc_init() 0 +#define ble_l2cap_coc_create_server(psm, mtu, cb, cb_arg) BLE_HS_ENOTSUP +#define ble_l2cap_coc_recv_ready(chan, sdu_rx) BLE_HS_ENOTSUP +#define ble_l2cap_coc_cleanup_chan(chan) +#define ble_l2cap_coc_send(chan, sdu_tx) BLE_HS_ENOTSUP +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* H_L2CAP_COC_PRIV_ */ diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_priv.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_priv.h new file mode 100644 index 000000000..640974d2a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_priv.h @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_L2CAP_PRIV_ +#define H_L2CAP_PRIV_ + +#include "ble_l2cap_coc_priv.h" +#include "host/ble_l2cap.h" +#include +#include "stats/stats.h" +#include "os/queue.h" +#include "os/os_mbuf.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_conn; +struct hci_data_hdr; + +STATS_SECT_START(ble_l2cap_stats) + STATS_SECT_ENTRY(chan_create) + STATS_SECT_ENTRY(chan_delete) + STATS_SECT_ENTRY(update_init) + STATS_SECT_ENTRY(update_rx) + STATS_SECT_ENTRY(update_fail) + STATS_SECT_ENTRY(proc_timeout) + STATS_SECT_ENTRY(sig_tx) + STATS_SECT_ENTRY(sig_rx) + STATS_SECT_ENTRY(sm_tx) + STATS_SECT_ENTRY(sm_rx) +STATS_SECT_END +extern STATS_SECT_DECL(ble_l2cap_stats) ble_l2cap_stats; + +extern struct os_mempool ble_l2cap_chan_pool; + +/* This is nimble specific; packets sent to the black hole CID do not elicit + * an "invalid CID" response. + */ +#define BLE_L2CAP_CID_BLACK_HOLE 0xffff + +#define BLE_L2CAP_HDR_SZ 4 + +typedef uint8_t ble_l2cap_chan_flags; + +typedef int ble_l2cap_rx_fn(struct ble_l2cap_chan *chan); + +struct ble_l2cap_chan { + SLIST_ENTRY(ble_l2cap_chan) next; + uint16_t conn_handle; + uint16_t dcid; + uint16_t scid; + uint16_t my_mtu; + uint16_t peer_mtu; /* 0 if not exchanged. */ + ble_l2cap_chan_flags flags; + + struct os_mbuf *rx_buf; + uint16_t rx_len; /* Length of current reassembled rx packet. */ + + ble_l2cap_rx_fn *rx_fn; + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0 + uint16_t psm; + struct ble_l2cap_coc_endpoint coc_rx; + struct ble_l2cap_coc_endpoint coc_tx; + uint16_t initial_credits; + ble_l2cap_event_fn *cb; + void *cb_arg; +#endif +}; + +struct ble_l2cap_hdr { + uint16_t len; + uint16_t cid; +}; + +typedef int ble_l2cap_tx_fn(struct ble_hs_conn *conn, + struct ble_l2cap_chan *chan); + +#define BLE_L2CAP_CHAN_F_TXED_MTU 0x01 /* We have sent our MTU. */ + +SLIST_HEAD(ble_l2cap_chan_list, ble_l2cap_chan); + +int ble_l2cap_parse_hdr(struct os_mbuf *om, int off, + struct ble_l2cap_hdr *l2cap_hdr); +struct os_mbuf *ble_l2cap_prepend_hdr(struct os_mbuf *om, uint16_t cid, + uint16_t len); + +struct ble_l2cap_chan *ble_l2cap_chan_alloc(uint16_t conn_handle); +void ble_l2cap_chan_free(struct ble_l2cap_chan *chan); + +bool ble_l2cap_is_mtu_req_sent(const struct ble_l2cap_chan *chan); + +int ble_l2cap_rx(struct ble_hs_conn *conn, + struct hci_data_hdr *hci_hdr, + struct os_mbuf *om, + ble_l2cap_rx_fn **out_rx_cb, + int *out_reject_cid); +int ble_l2cap_tx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan, + struct os_mbuf *txom); + +void ble_l2cap_remove_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan); + +int ble_l2cap_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_sig_priv.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_sig_priv.h new file mode 100644 index 000000000..1a6fb8293 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_sig_priv.h @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_L2CAP_SIG_ +#define H_BLE_L2CAP_SIG_ + +#include "syscfg/syscfg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_L2CAP_SIG_MTU 100 /* This is our own default. */ + +#define BLE_L2CAP_SIG_HDR_SZ 4 +struct ble_l2cap_sig_hdr { + uint8_t op; + uint8_t identifier; + uint16_t length; + uint8_t data[0]; +} __attribute__((packed)); + +#define BLE_L2CAP_SIG_REJECT_MIN_SZ 2 +struct ble_l2cap_sig_reject { + uint16_t reason; + uint8_t data[0]; +} __attribute__((packed)); + +#define BLE_L2CAP_SIG_UPDATE_REQ_SZ 8 +struct ble_l2cap_sig_update_req { + uint16_t itvl_min; + uint16_t itvl_max; + uint16_t slave_latency; + uint16_t timeout_multiplier; +} __attribute__((packed)); + +#define BLE_L2CAP_SIG_UPDATE_RSP_SZ 2 +struct ble_l2cap_sig_update_rsp { + uint16_t result; +} __attribute__((packed)); + +#define BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT 0x0000 +#define BLE_L2CAP_SIG_UPDATE_RSP_RESULT_REJECT 0x0001 + +struct ble_l2cap_sig_le_con_req { + uint16_t psm; + uint16_t scid; + uint16_t mtu; + uint16_t mps; + uint16_t credits; +} __attribute__((packed)); + +struct ble_l2cap_sig_le_con_rsp { + uint16_t dcid; + uint16_t mtu; + uint16_t mps; + uint16_t credits; + uint16_t result; +} __attribute__((packed)); + +struct ble_l2cap_sig_disc_req { + uint16_t dcid; + uint16_t scid; +} __attribute__((packed)); + +struct ble_l2cap_sig_disc_rsp { + uint16_t dcid; + uint16_t scid; +} __attribute__((packed)); + +struct ble_l2cap_sig_le_credits { + uint16_t scid; + uint16_t credits; +} __attribute__((packed)); + +void ble_l2cap_sig_hdr_parse(void *payload, uint16_t len, + struct ble_l2cap_sig_hdr *hdr); +int ble_l2cap_sig_reject_tx(uint16_t conn_handle, + uint8_t id, uint16_t reason, + void *data, int data_len); +int ble_l2cap_sig_reject_invalid_cid_tx(uint16_t conn_handle, uint8_t id, + uint16_t src_cid, uint16_t dst_cid); +int ble_l2cap_sig_tx(uint16_t conn_handle, struct os_mbuf *txom); +void *ble_l2cap_sig_cmd_get(uint8_t opcode, uint8_t id, uint16_t len, + struct os_mbuf **txom); +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0 +int ble_l2cap_sig_coc_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu, + struct os_mbuf *sdu_rx, + ble_l2cap_event_fn *cb, void *cb_arg); +int ble_l2cap_sig_disconnect(struct ble_l2cap_chan *chan); +int ble_l2cap_sig_le_credits(uint16_t conn_handle, uint16_t scid, + uint16_t credits); +#else +#define ble_l2cap_sig_coc_connect(conn_handle, psm, mtu, sdu_rx, cb, cb_arg) \ + BLE_HS_ENOTSUP +#define ble_l2cap_sig_disconnect(chan) BLE_HS_ENOTSUP +#endif + +void ble_l2cap_sig_conn_broken(uint16_t conn_handle, int reason); +int32_t ble_l2cap_sig_timer(void); +struct ble_l2cap_chan *ble_l2cap_sig_create_chan(uint16_t conn_handle); +int ble_l2cap_sig_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_cli.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_cli.c new file mode 100644 index 000000000..17f01b993 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_cli.c @@ -0,0 +1,1488 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH_CFG_CLI) + +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_MODEL)) +#include "mesh/mesh.h" + +#include +#include +#include + +#include "net.h" +#include "foundation.h" + +#define CID_NVAL 0xffff + +struct comp_data { + u8_t *status; + struct os_mbuf *comp; +}; + +static s32_t msg_timeout = K_SECONDS(5); + +static struct bt_mesh_cfg_cli *cli; + +static void comp_data_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct comp_data *param; + size_t to_copy; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_DEV_COMP_DATA_STATUS) { + BT_WARN("Unexpected Composition Data Status"); + return; + } + + param = cli->op_param; + + *(param->status) = net_buf_simple_pull_u8(buf); + to_copy = min(net_buf_simple_tailroom(param->comp), buf->om_len); + net_buf_simple_add_mem(param->comp, buf->om_data, to_copy); + + k_sem_give(&cli->op_sync); +} + +static void state_status_u8(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf, + u32_t expect_status) +{ + u8_t *status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != expect_status) { + BT_WARN("Unexpected Status (0x%08x != 0x%08x)", + (unsigned) cli->op_pending, (unsigned) expect_status); + return; + } + + status = cli->op_param; + *status = net_buf_simple_pull_u8(buf); + + k_sem_give(&cli->op_sync); +} + +static void beacon_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + state_status_u8(model, ctx, buf, OP_BEACON_STATUS); +} + +static void ttl_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + state_status_u8(model, ctx, buf, OP_DEFAULT_TTL_STATUS); +} + +static void friend_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + state_status_u8(model, ctx, buf, OP_FRIEND_STATUS); +} + +static void gatt_proxy_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + state_status_u8(model, ctx, buf, OP_GATT_PROXY_STATUS); +} + +struct relay_param { + u8_t *status; + u8_t *transmit; +}; + +static void relay_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + struct relay_param *param; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_RELAY_STATUS) { + BT_WARN("Unexpected Relay Status message"); + return; + } + + param = cli->op_param; + *param->status = net_buf_simple_pull_u8(buf); + *param->transmit = net_buf_simple_pull_u8(buf); + + k_sem_give(&cli->op_sync); +} + +struct net_key_param { + u8_t *status; + u16_t net_idx; +}; + +static void net_key_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct net_key_param *param; + u16_t net_idx, app_idx; + u8_t status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_NET_KEY_STATUS) { + BT_WARN("Unexpected Net Key Status message"); + return; + } + + status = net_buf_simple_pull_u8(buf); + key_idx_unpack(buf, &net_idx, &app_idx); + + param = cli->op_param; + if (param->net_idx != net_idx) { + BT_WARN("Net Key Status key index does not match"); + return; + } + + if (param->status) { + *param->status = status; + } + + k_sem_give(&cli->op_sync); +} + +struct app_key_param { + u8_t *status; + u16_t net_idx; + u16_t app_idx; +}; + +static void app_key_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + struct app_key_param *param; + u16_t net_idx, app_idx; + u8_t status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_APP_KEY_STATUS) { + BT_WARN("Unexpected App Key Status message"); + return; + } + + status = net_buf_simple_pull_u8(buf); + key_idx_unpack(buf, &net_idx, &app_idx); + + param = cli->op_param; + if (param->net_idx != net_idx || param->app_idx != app_idx) { + BT_WARN("App Key Status key indices did not match"); + return; + } + + if (param->status) { + *param->status = status; + } + + k_sem_give(&cli->op_sync); +} + +struct mod_app_param { + u8_t *status; + u16_t elem_addr; + u16_t mod_app_idx; + u16_t mod_id; + u16_t cid; +}; + +static void mod_app_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + u16_t elem_addr, mod_app_idx, mod_id, cid; + struct mod_app_param *param; + u8_t status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_MOD_APP_STATUS) { + BT_WARN("Unexpected Model App Status message"); + return; + } + + status = net_buf_simple_pull_u8(buf); + elem_addr = net_buf_simple_pull_le16(buf); + mod_app_idx = net_buf_simple_pull_le16(buf); + + if (buf->om_len >= 4) { + cid = net_buf_simple_pull_le16(buf); + } else { + cid = CID_NVAL; + } + + mod_id = net_buf_simple_pull_le16(buf); + + param = cli->op_param; + if (param->elem_addr != elem_addr || + param->mod_app_idx != mod_app_idx || param->mod_id != mod_id || + param->cid != cid) { + BT_WARN("Model App Status parameters did not match"); + return; + } + + if (param->status) { + *param->status = status; + } + + k_sem_give(&cli->op_sync); +} + +struct mod_pub_param { + u16_t mod_id; + u16_t cid; + u16_t elem_addr; + u8_t *status; + struct bt_mesh_cfg_mod_pub *pub; +}; + +static void mod_pub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + u16_t mod_id, cid, elem_addr; + struct mod_pub_param *param; + u8_t status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_MOD_PUB_STATUS) { + BT_WARN("Unexpected Model Pub Status message"); + return; + } + + param = cli->op_param; + if (param->cid != CID_NVAL) { + if (buf->om_len < 14) { + BT_WARN("Unexpected Mod Pub Status with SIG Model"); + return; + } + + cid = sys_get_le16(&buf->om_data[10]); + mod_id = sys_get_le16(&buf->om_data[12]); + } else { + if (buf->om_len > 12) { + BT_WARN("Unexpected Mod Pub Status with Vendor Model"); + return; + } + + cid = CID_NVAL; + mod_id = sys_get_le16(&buf->om_data[10]); + } + + if (mod_id != param->mod_id || cid != param->cid) { + BT_WARN("Mod Pub Model ID or Company ID mismatch"); + return; + } + + status = net_buf_simple_pull_u8(buf); + + elem_addr = net_buf_simple_pull_le16(buf); + if (elem_addr != param->elem_addr) { + BT_WARN("Model Pub Status for unexpected element (0x%04x)", + elem_addr); + return; + } + + if (param->status) { + *param->status = status; + } + + if (param->pub) { + param->pub->addr = net_buf_simple_pull_le16(buf); + param->pub->app_idx = net_buf_simple_pull_le16(buf); + param->pub->cred_flag = (param->pub->app_idx & BIT(12)); + param->pub->app_idx &= BIT_MASK(12); + param->pub->ttl = net_buf_simple_pull_u8(buf); + param->pub->period = net_buf_simple_pull_u8(buf); + param->pub->transmit = net_buf_simple_pull_u8(buf); + } + + k_sem_give(&cli->op_sync); +} + +struct mod_sub_param { + u8_t *status; + u16_t elem_addr; + u16_t *sub_addr; + u16_t *expect_sub; + u16_t mod_id; + u16_t cid; +}; + +static void mod_sub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + u16_t elem_addr, sub_addr, mod_id, cid; + struct mod_sub_param *param; + u8_t status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_MOD_SUB_STATUS) { + BT_WARN("Unexpected Model Subscription Status message"); + return; + } + + status = net_buf_simple_pull_u8(buf); + elem_addr = net_buf_simple_pull_le16(buf); + sub_addr = net_buf_simple_pull_le16(buf); + + if (buf->om_len >= 4) { + cid = net_buf_simple_pull_le16(buf); + } else { + cid = CID_NVAL; + } + + mod_id = net_buf_simple_pull_le16(buf); + + param = cli->op_param; + if (param->elem_addr != elem_addr || param->mod_id != mod_id || + (param->expect_sub && *param->expect_sub != sub_addr) || + param->cid != cid) { + BT_WARN("Model Subscription Status parameters did not match"); + return; + } + + if (param->sub_addr) { + *param->sub_addr = sub_addr; + } + + if (param->status) { + *param->status = status; + } + + k_sem_give(&cli->op_sync); +} + +struct hb_sub_param { + u8_t *status; + struct bt_mesh_cfg_hb_sub *sub; +}; + +static void hb_sub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + struct hb_sub_param *param; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_HEARTBEAT_SUB_STATUS) { + BT_WARN("Unexpected Heartbeat Subscription Status message"); + return; + } + + param = cli->op_param; + + *param->status = net_buf_simple_pull_u8(buf); + + param->sub->src = net_buf_simple_pull_le16(buf); + param->sub->dst = net_buf_simple_pull_le16(buf); + param->sub->period = net_buf_simple_pull_u8(buf); + param->sub->count = net_buf_simple_pull_u8(buf); + param->sub->min = net_buf_simple_pull_u8(buf); + param->sub->max = net_buf_simple_pull_u8(buf); + + k_sem_give(&cli->op_sync); +} + +struct hb_pub_param { + u8_t *status; + struct bt_mesh_cfg_hb_pub *pub; +}; + +static void hb_pub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct hb_pub_param *param; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_HEARTBEAT_PUB_STATUS) { + BT_WARN("Unexpected Heartbeat Publication Status message"); + return; + } + + param = cli->op_param; + + *param->status = net_buf_simple_pull_u8(buf); + + if (param->pub) { + param->pub->dst = net_buf_simple_pull_le16(buf); + param->pub->count = net_buf_simple_pull_u8(buf); + param->pub->period = net_buf_simple_pull_u8(buf); + param->pub->ttl = net_buf_simple_pull_u8(buf); + param->pub->feat = net_buf_simple_pull_u8(buf); + param->pub->net_idx = net_buf_simple_pull_u8(buf); + } + + k_sem_give(&cli->op_sync); +} + +const struct bt_mesh_model_op bt_mesh_cfg_cli_op[] = { + { OP_DEV_COMP_DATA_STATUS, 15, comp_data_status }, + { OP_BEACON_STATUS, 1, beacon_status }, + { OP_DEFAULT_TTL_STATUS, 1, ttl_status }, + { OP_FRIEND_STATUS, 1, friend_status }, + { OP_GATT_PROXY_STATUS, 1, gatt_proxy_status }, + { OP_RELAY_STATUS, 2, relay_status }, + { OP_NET_KEY_STATUS, 3, net_key_status }, + { OP_APP_KEY_STATUS, 4, app_key_status }, + { OP_MOD_APP_STATUS, 7, mod_app_status }, + { OP_MOD_PUB_STATUS, 12, mod_pub_status }, + { OP_MOD_SUB_STATUS, 7, mod_sub_status }, + { OP_HEARTBEAT_SUB_STATUS, 9, hb_sub_status }, + { OP_HEARTBEAT_PUB_STATUS, 10, hb_pub_status }, + BT_MESH_MODEL_OP_END, +}; + +static int cli_prepare(void *param, u32_t op) +{ + if (!cli) { + BT_ERR("No available Configuration Client context!"); + return -EINVAL; + } + + if (cli->op_pending) { + BT_WARN("Another synchronous operation pending"); + return -EBUSY; + } + + cli->op_param = param; + cli->op_pending = op; + + return 0; +} + +static void cli_reset(void) +{ + cli->op_pending = 0; + cli->op_param = NULL; +} + +static int cli_wait(void) +{ + int err; + + err = k_sem_take(&cli->op_sync, msg_timeout); + + cli_reset(); + + return err; +} + +int bt_mesh_cfg_comp_data_get(u16_t net_idx, u16_t addr, u8_t page, + u8_t *status, struct os_mbuf *comp) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct comp_data param = { + .status = status, + .comp = comp, + }; + int err; + + err = cli_prepare(¶m, OP_DEV_COMP_DATA_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_DEV_COMP_DATA_GET); + net_buf_simple_add_u8(msg, page); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +static int get_state_u8(u16_t net_idx, u16_t addr, u32_t op, u32_t rsp, + u8_t *val) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = cli_prepare(val, rsp); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, op); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +static int set_state_u8(u16_t net_idx, u16_t addr, u32_t op, u32_t rsp, + u8_t new_val, u8_t *val) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = cli_prepare(val, rsp); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, op); + net_buf_simple_add_u8(msg, new_val); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_beacon_get(u16_t net_idx, u16_t addr, u8_t *status) +{ + return get_state_u8(net_idx, addr, OP_BEACON_GET, OP_BEACON_STATUS, + status); +} + +int bt_mesh_cfg_beacon_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status) +{ + return set_state_u8(net_idx, addr, OP_BEACON_SET, OP_BEACON_STATUS, + val, status); +} + +int bt_mesh_cfg_ttl_get(u16_t net_idx, u16_t addr, u8_t *ttl) +{ + return get_state_u8(net_idx, addr, OP_DEFAULT_TTL_GET, + OP_DEFAULT_TTL_STATUS, ttl); +} + +int bt_mesh_cfg_ttl_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *ttl) +{ + return set_state_u8(net_idx, addr, OP_DEFAULT_TTL_SET, + OP_DEFAULT_TTL_STATUS, val, ttl); +} + +int bt_mesh_cfg_friend_get(u16_t net_idx, u16_t addr, u8_t *status) +{ + return get_state_u8(net_idx, addr, OP_FRIEND_GET, + OP_FRIEND_STATUS, status); +} + +int bt_mesh_cfg_friend_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status) +{ + return set_state_u8(net_idx, addr, OP_FRIEND_SET, OP_FRIEND_STATUS, + val, status); +} + +int bt_mesh_cfg_gatt_proxy_get(u16_t net_idx, u16_t addr, u8_t *status) +{ + return get_state_u8(net_idx, addr, OP_GATT_PROXY_GET, + OP_GATT_PROXY_STATUS, status); +} + +int bt_mesh_cfg_gatt_proxy_set(u16_t net_idx, u16_t addr, u8_t val, + u8_t *status) +{ + return set_state_u8(net_idx, addr, OP_GATT_PROXY_SET, + OP_GATT_PROXY_STATUS, val, status); +} + +int bt_mesh_cfg_relay_get(u16_t net_idx, u16_t addr, u8_t *status, + u8_t *transmit) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct relay_param param = { + .status = status, + .transmit = transmit, + }; + int err; + + err = cli_prepare(¶m, OP_RELAY_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_RELAY_GET); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_relay_set(u16_t net_idx, u16_t addr, u8_t new_relay, + u8_t new_transmit, u8_t *status, u8_t *transmit) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct relay_param param = { + .status = status, + .transmit = transmit, + }; + int err; + + err = cli_prepare(¶m, OP_RELAY_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_RELAY_SET); + net_buf_simple_add_u8(msg, new_relay); + net_buf_simple_add_u8(msg, new_transmit); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_net_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx, + const u8_t net_key[16], u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 18 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct net_key_param param = { + .status = status, + .net_idx = key_net_idx, + }; + int err; + + err = cli_prepare(¶m, OP_NET_KEY_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_NET_KEY_ADD); + net_buf_simple_add_le16(msg, key_net_idx); + net_buf_simple_add_mem(msg, net_key, 16); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_app_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx, + u16_t key_app_idx, const u8_t app_key[16], + u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(1 + 19 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct app_key_param param = { + .status = status, + .net_idx = key_net_idx, + .app_idx = key_app_idx, + }; + int err; + + err = cli_prepare(¶m, OP_APP_KEY_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_APP_KEY_ADD); + key_idx_pack(msg, key_net_idx, key_app_idx); + net_buf_simple_add_mem(msg, app_key, 16); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +static int mod_app_bind(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u16_t cid, + u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 8 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct mod_app_param param = { + .status = status, + .elem_addr = elem_addr, + .mod_app_idx = mod_app_idx, + .mod_id = mod_id, + .cid = cid, + }; + int err; + + err = cli_prepare(¶m, OP_MOD_APP_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_MOD_APP_BIND); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, mod_app_idx); + + if (cid != CID_NVAL) { + net_buf_simple_add_le16(msg, cid); + } + + net_buf_simple_add_le16(msg, mod_id); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_mod_app_bind(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u8_t *status) +{ + return mod_app_bind(net_idx, addr, elem_addr, mod_app_idx, mod_id, + CID_NVAL, status); +} + +int bt_mesh_cfg_mod_app_bind_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u16_t cid, + u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_app_bind(net_idx, addr, elem_addr, mod_app_idx, mod_id, cid, + status); +} + +static int mod_sub(u32_t op, u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid, u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 8 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct mod_sub_param param = { + .status = status, + .elem_addr = elem_addr, + .expect_sub = &sub_addr, + .mod_id = mod_id, + .cid = cid, + }; + int err; + + err = cli_prepare(¶m, OP_MOD_SUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, op); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, sub_addr); + + if (cid != CID_NVAL) { + net_buf_simple_add_le16(msg, cid); + } + + net_buf_simple_add_le16(msg, mod_id); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_mod_sub_add(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status) +{ + return mod_sub(OP_MOD_SUB_ADD, net_idx, addr, elem_addr, sub_addr, + mod_id, CID_NVAL, status); +} + +int bt_mesh_cfg_mod_sub_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid, + u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub(OP_MOD_SUB_ADD, net_idx, addr, elem_addr, sub_addr, + mod_id, cid, status); +} + +int bt_mesh_cfg_mod_sub_del(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status) +{ + return mod_sub(OP_MOD_SUB_DEL, net_idx, addr, elem_addr, sub_addr, + mod_id, CID_NVAL, status); +} + +int bt_mesh_cfg_mod_sub_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid, + u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub(OP_MOD_SUB_DEL, net_idx, addr, elem_addr, sub_addr, + mod_id, cid, status); +} + +int bt_mesh_cfg_mod_sub_overwrite(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status) +{ + return mod_sub(OP_MOD_SUB_OVERWRITE, net_idx, addr, elem_addr, + sub_addr, mod_id, CID_NVAL, status); +} + +int bt_mesh_cfg_mod_sub_overwrite_vnd(u16_t net_idx, u16_t addr, + u16_t elem_addr, u16_t sub_addr, + u16_t mod_id, u16_t cid, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub(OP_MOD_SUB_OVERWRITE, net_idx, addr, elem_addr, + sub_addr, mod_id, cid, status); +} + +static int mod_sub_va(u32_t op, u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, u16_t cid, + u16_t *virt_addr, u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 22 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct mod_sub_param param = { + .status = status, + .elem_addr = elem_addr, + .sub_addr = virt_addr, + .mod_id = mod_id, + .cid = cid, + }; + int err; + + err = cli_prepare(¶m, OP_MOD_SUB_STATUS); + if (err) { + goto done; + } + + BT_DBG("net_idx 0x%04x addr 0x%04x elem_addr 0x%04x label %s", + net_idx, addr, elem_addr, label); + BT_DBG("mod_id 0x%04x cid 0x%04x", mod_id, cid); + + bt_mesh_model_msg_init(msg, op); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_mem(msg, label, 16); + + if (cid != CID_NVAL) { + net_buf_simple_add_le16(msg, cid); + } + + net_buf_simple_add_le16(msg, mod_id); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_mod_sub_va_add(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t *virt_addr, u8_t *status) +{ + return mod_sub_va(OP_MOD_SUB_VA_ADD, net_idx, addr, elem_addr, label, + mod_id, CID_NVAL, virt_addr, status); +} + +int bt_mesh_cfg_mod_sub_va_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t cid, u16_t *virt_addr, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub_va(OP_MOD_SUB_VA_ADD, net_idx, addr, elem_addr, label, + mod_id, cid, virt_addr, status); +} + +int bt_mesh_cfg_mod_sub_va_del(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t *virt_addr, u8_t *status) +{ + return mod_sub_va(OP_MOD_SUB_VA_DEL, net_idx, addr, elem_addr, label, + mod_id, CID_NVAL, virt_addr, status); +} + +int bt_mesh_cfg_mod_sub_va_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t cid, u16_t *virt_addr, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub_va(OP_MOD_SUB_VA_DEL, net_idx, addr, elem_addr, label, + mod_id, cid, virt_addr, status); +} + +int bt_mesh_cfg_mod_sub_va_overwrite(u16_t net_idx, u16_t addr, + u16_t elem_addr, const u8_t label[16], + u16_t mod_id, u16_t *virt_addr, + u8_t *status) +{ + return mod_sub_va(OP_MOD_SUB_VA_OVERWRITE, net_idx, addr, elem_addr, + label, mod_id, CID_NVAL, virt_addr, status); +} + +int bt_mesh_cfg_mod_sub_va_overwrite_vnd(u16_t net_idx, u16_t addr, + u16_t elem_addr, const u8_t label[16], + u16_t mod_id, u16_t cid, + u16_t *virt_addr, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub_va(OP_MOD_SUB_VA_OVERWRITE, net_idx, addr, elem_addr, + label, mod_id, cid, virt_addr, status); +} + +static int mod_pub_get(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 6 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct mod_pub_param param = { + .mod_id = mod_id, + .cid = cid, + .elem_addr = elem_addr, + .status = status, + .pub = pub, + }; + int err; + + err = cli_prepare(¶m, OP_MOD_PUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_MOD_PUB_GET); + + net_buf_simple_add_le16(msg, elem_addr); + + if (cid != CID_NVAL) { + net_buf_simple_add_le16(msg, cid); + } + + net_buf_simple_add_le16(msg, mod_id); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_mod_pub_get(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub, + u8_t *status) +{ + return mod_pub_get(net_idx, addr, elem_addr, mod_id, CID_NVAL, + pub, status); +} + +int bt_mesh_cfg_mod_pub_get_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_pub_get(net_idx, addr, elem_addr, mod_id, cid, pub, status); +} + +static int mod_pub_set(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 13 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct mod_pub_param param = { + .mod_id = mod_id, + .cid = cid, + .elem_addr = elem_addr, + .status = status, + .pub = pub, + }; + int err; + + err = cli_prepare(¶m, OP_MOD_PUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_MOD_PUB_SET); + + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, pub->addr); + net_buf_simple_add_le16(msg, (pub->app_idx & (pub->cred_flag << 12))); + net_buf_simple_add_u8(msg, pub->ttl); + net_buf_simple_add_u8(msg, pub->period); + net_buf_simple_add_u8(msg, pub->transmit); + + if (cid != CID_NVAL) { + net_buf_simple_add_le16(msg, cid); + } + + net_buf_simple_add_le16(msg, mod_id); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_mod_pub_set(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub, + u8_t *status) +{ + return mod_pub_set(net_idx, addr, elem_addr, mod_id, CID_NVAL, + pub, status); +} + +int bt_mesh_cfg_mod_pub_set_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_pub_set(net_idx, addr, elem_addr, mod_id, cid, pub, status); +} + +int bt_mesh_cfg_hb_sub_set(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_sub *sub, u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct hb_sub_param param = { + .status = status, + .sub = sub, + }; + int err; + + err = cli_prepare(¶m, OP_HEARTBEAT_SUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_SUB_SET); + net_buf_simple_add_le16(msg, sub->src); + net_buf_simple_add_le16(msg, sub->dst); + net_buf_simple_add_u8(msg, sub->period); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_hb_sub_get(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_sub *sub, u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct hb_sub_param param = { + .status = status, + .sub = sub, + }; + int err; + + err = cli_prepare(¶m, OP_HEARTBEAT_SUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_SUB_GET); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_hb_pub_set(u16_t net_idx, u16_t addr, + const struct bt_mesh_cfg_hb_pub *pub, u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct hb_pub_param param = { + .status = status, + }; + int err; + + err = cli_prepare(¶m, OP_HEARTBEAT_PUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_PUB_SET); + net_buf_simple_add_le16(msg, pub->dst); + net_buf_simple_add_u8(msg, pub->count); + net_buf_simple_add_u8(msg, pub->period); + net_buf_simple_add_u8(msg, pub->ttl); + net_buf_simple_add_le16(msg, pub->feat); + net_buf_simple_add_le16(msg, pub->net_idx); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_hb_pub_get(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_pub *pub, u8_t *status) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct hb_pub_param param = { + .status = status, + .pub = pub, + }; + int err; + + err = cli_prepare(¶m, OP_HEARTBEAT_PUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_PUB_GET); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +s32_t bt_mesh_cfg_cli_timeout_get(void) +{ + return msg_timeout; +} + +void bt_mesh_cfg_cli_timeout_set(s32_t timeout) +{ + msg_timeout = timeout; +} + +int bt_mesh_cfg_cli_init(struct bt_mesh_model *model, bool primary) +{ + BT_DBG("primary %u", primary); + + if (!primary) { + BT_ERR("Configuration Client only allowed in primary element"); + return -EINVAL; + } + + if (!model->user_data) { + BT_ERR("No Configuration Client context provided"); + return -EINVAL; + } + + cli = model->user_data; + cli->model = model; + + /* Configuration Model security is device-key based */ + model->keys[0] = BT_MESH_KEY_DEV; + + k_sem_init(&cli->op_sync, 0, 1); + + return 0; +} + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_srv.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_srv.c new file mode 100644 index 000000000..4ee9e8cfd --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_srv.c @@ -0,0 +1,3617 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "mesh/mesh.h" + +#include "syscfg/syscfg.h" +#define BT_DBG_ENABLED MYNEWT_VAL(BLE_MESH_DEBUG_MODEL) +#include "host/ble_hs_log.h" + +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "lpn.h" +#include "transport.h" +#include "crypto.h" +#include "access.h" +#include "beacon.h" +#include "proxy.h" +#include "foundation.h" +#include "friend.h" +#include "testing.h" +#include "settings.h" + +#define DEFAULT_TTL 7 + +static struct bt_mesh_cfg_srv *conf; + +static struct label { + u16_t ref; + u16_t addr; + u8_t uuid[16]; +} labels[MYNEWT_VAL(BLE_MESH_LABEL_COUNT)]; + +static void hb_send(struct bt_mesh_model *model) +{ + + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t feat = 0; + struct __packed { + u8_t init_ttl; + u16_t feat; + } hb; + struct bt_mesh_msg_ctx ctx = { + .net_idx = cfg->hb_pub.net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = cfg->hb_pub.dst, + .send_ttl = cfg->hb_pub.ttl, + }; + struct bt_mesh_net_tx tx = { + .sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx), + .ctx = &ctx, + .src = bt_mesh_model_elem(model)->addr, + .xmit = bt_mesh_net_transmit_get(), + }; + + hb.init_ttl = cfg->hb_pub.ttl; + + if (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED) { + feat |= BT_MESH_FEAT_RELAY; + } + + if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) { + feat |= BT_MESH_FEAT_PROXY; + } + + if (bt_mesh_friend_get() == BT_MESH_FRIEND_ENABLED) { + feat |= BT_MESH_FEAT_FRIEND; + } + +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + if (bt_mesh.lpn.state != BT_MESH_LPN_DISABLED) { + feat |= BT_MESH_FEAT_LOW_POWER; + } +#endif + + hb.feat = sys_cpu_to_be16(feat); + + BT_DBG("InitTTL %u feat 0x%04x", cfg->hb_pub.ttl, feat); + + bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb), + NULL, NULL, NULL); +} + +static int comp_add_elem(struct os_mbuf *buf, struct bt_mesh_elem *elem, + bool primary) +{ + struct bt_mesh_model *mod; + int i; + + if (net_buf_simple_tailroom(buf) < + 4 + (elem->model_count * 2) + (elem->vnd_model_count * 2)) { + BT_ERR("Too large device composition"); + return -E2BIG; + } + + net_buf_simple_add_le16(buf, elem->loc); + + net_buf_simple_add_u8(buf, elem->model_count); + net_buf_simple_add_u8(buf, elem->vnd_model_count); + + for (i = 0; i < elem->model_count; i++) { + mod = &elem->models[i]; + net_buf_simple_add_le16(buf, mod->id); + } + + for (i = 0; i < elem->vnd_model_count; i++) { + mod = &elem->vnd_models[i]; + net_buf_simple_add_le16(buf, mod->vnd.company); + net_buf_simple_add_le16(buf, mod->vnd.id); + } + + return 0; +} + +static int comp_get_page_0(struct os_mbuf *buf) +{ + u16_t feat = 0; + const struct bt_mesh_comp *comp; + int i; + + comp = bt_mesh_comp_get(); + + if ((MYNEWT_VAL(BLE_MESH_RELAY))) { + feat |= BT_MESH_FEAT_RELAY; + } + + if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) { + feat |= BT_MESH_FEAT_PROXY; + } + + if ((MYNEWT_VAL(BLE_MESH_FRIEND))) { + feat |= BT_MESH_FEAT_FRIEND; + } + + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) { + feat |= BT_MESH_FEAT_LOW_POWER; + } + + net_buf_simple_add_le16(buf, comp->cid); + net_buf_simple_add_le16(buf, comp->pid); + net_buf_simple_add_le16(buf, comp->vid); + net_buf_simple_add_le16(buf, MYNEWT_VAL(BLE_MESH_CRPL)); + net_buf_simple_add_le16(buf, feat); + + for (i = 0; i < comp->elem_count; i++) { + int err; + + err = comp_add_elem(buf, &comp->elem[i], i == 0); + if (err) { + return err; + } + } + + return 0; +} + +static void dev_comp_data_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + u8_t page; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + page = net_buf_simple_pull_u8(buf); + if (page != 0) { + BT_WARN("Composition page %u not available", page); + page = 0; + } + + bt_mesh_model_msg_init(sdu, OP_DEV_COMP_DATA_STATUS); + + net_buf_simple_add_u8(sdu, page); + if (comp_get_page_0(sdu) < 0) { + BT_ERR("Unable to get composition page 0"); + goto done; + } + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("Unable to send Device Composition Status response"); + } + +done: + os_mbuf_free_chain(sdu); +} + +static struct bt_mesh_model *get_model(struct bt_mesh_elem *elem, + struct os_mbuf *buf, bool *vnd) +{ + if (buf->om_len < 4) { + u16_t id; + + id = net_buf_simple_pull_le16(buf); + + BT_DBG("ID 0x%04x addr 0x%04x", id, elem->addr); + + *vnd = false; + + return bt_mesh_model_find(elem, id); + } else { + u16_t company, id; + + company = net_buf_simple_pull_le16(buf); + id = net_buf_simple_pull_le16(buf); + + BT_DBG("Company 0x%04x ID 0x%04x addr 0x%04x", company, id, + elem->addr); + + *vnd = true; + + return bt_mesh_model_find_vnd(elem, company, id); + } +} + +static bool app_key_is_valid(u16_t app_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx != BT_MESH_KEY_UNUSED && + key->app_idx == app_idx) { + return true; + } + } + + return false; +} + +static u8_t _mod_pub_set(struct bt_mesh_model *model, u16_t pub_addr, + u16_t app_idx, u8_t cred_flag, u8_t ttl, u8_t period, + u8_t retransmit, bool store) +{ + if (!model->pub) { + return STATUS_NVAL_PUB_PARAM; + } + + if (!(MYNEWT_VAL(BLE_MESH_LOW_POWER)) && cred_flag) { + return STATUS_FEAT_NOT_SUPP; + } + + if (!model->pub->update && period) { + return STATUS_NVAL_PUB_PARAM; + } + + if (pub_addr == BT_MESH_ADDR_UNASSIGNED) { + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return STATUS_SUCCESS; + } + + model->pub->addr = BT_MESH_ADDR_UNASSIGNED; + model->pub->key = 0; + model->pub->cred = 0; + model->pub->ttl = 0; + model->pub->period = 0; + model->pub->retransmit = 0; + model->pub->count = 0; + + if (model->pub->update) { + k_delayed_work_cancel(&model->pub->timer); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_store_mod_pub(model); + } + + return STATUS_SUCCESS; + } + + if (!bt_mesh_app_key_find(app_idx)) { + return STATUS_INVALID_APPKEY; + } + + model->pub->addr = pub_addr; + model->pub->key = app_idx; + model->pub->cred = cred_flag; + model->pub->ttl = ttl; + model->pub->period = period; + model->pub->retransmit = retransmit; + + if (model->pub->update) { + s32_t period_ms; + + period_ms = bt_mesh_model_pub_period_get(model); + BT_DBG("period %u ms", (unsigned) period_ms); + + if (period_ms) { + k_delayed_work_submit(&model->pub->timer, period_ms); + } else { + k_delayed_work_cancel(&model->pub->timer); + } + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_store_mod_pub(model); + } + + return STATUS_SUCCESS; +} + +u8_t mod_bind(struct bt_mesh_model *model, u16_t key_idx) +{ + int i; + + BT_DBG("model %p key_idx 0x%03x", model, key_idx); + + if (!app_key_is_valid(key_idx)) { + return STATUS_INVALID_APPKEY; + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + /* Treat existing binding as success */ + if (model->keys[i] == key_idx) { + return STATUS_SUCCESS; + } + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + if (model->keys[i] == BT_MESH_KEY_UNUSED) { + model->keys[i] = key_idx; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_bind(model); + } + + return STATUS_SUCCESS; + } + } + + return STATUS_INSUFF_RESOURCES; +} + +u8_t mod_unbind(struct bt_mesh_model *model, u16_t key_idx, bool store) +{ + int i; + + BT_DBG("model %p key_idx 0x%03x store %u", model, key_idx, store); + + if (!app_key_is_valid(key_idx)) { + return STATUS_INVALID_APPKEY; + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + if (model->keys[i] != key_idx) { + continue; + } + + model->keys[i] = BT_MESH_KEY_UNUSED; + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_store_mod_bind(model); + } + + if (model->pub && model->pub->key == key_idx) { + _mod_pub_set(model, BT_MESH_ADDR_UNASSIGNED, + 0, 0, 0, 0, 0, store); + } + } + + return STATUS_SUCCESS; +} + +struct bt_mesh_app_key *bt_mesh_app_key_alloc(u16_t app_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx == BT_MESH_KEY_UNUSED) { + return key; + } + } + + return NULL; +} + +static u8_t app_key_set(u16_t net_idx, u16_t app_idx, const u8_t val[16], + bool update) +{ + struct bt_mesh_app_keys *keys; + struct bt_mesh_app_key *key; + struct bt_mesh_subnet *sub; + + BT_DBG("net_idx 0x%04x app_idx %04x update %u val %s", + net_idx, app_idx, update, bt_hex(val, 16)); + + sub = bt_mesh_subnet_get(net_idx); + if (!sub) { + return STATUS_INVALID_NETKEY; + } + + key = bt_mesh_app_key_find(app_idx); + if (update) { + if (!key) { + return STATUS_INVALID_APPKEY; + } + + if (key->net_idx != net_idx) { + return STATUS_INVALID_BINDING; + } + + keys = &key->keys[1]; + + /* The AppKey Update message shall generate an error when node + * is in normal operation, Phase 2, or Phase 3 or in Phase 1 + * when the AppKey Update message on a valid AppKeyIndex when + * the AppKey value is different. + */ + if (sub->kr_phase != BT_MESH_KR_PHASE_1) { + return STATUS_CANNOT_UPDATE; + } + + if (key->updated) { + if (memcmp(keys->val, val, 16)) { + return STATUS_CANNOT_UPDATE; + } else { + return STATUS_SUCCESS; + } + } + + key->updated = true; + } else { + if (key) { + if (key->net_idx == net_idx && + !memcmp(key->keys[0].val, val, 16)) { + return STATUS_SUCCESS; + } + + if (key->net_idx == net_idx) { + return STATUS_IDX_ALREADY_STORED; + } else { + return STATUS_INVALID_NETKEY; + } + } + + key = bt_mesh_app_key_alloc(app_idx); + if (!key) { + return STATUS_INSUFF_RESOURCES; + } + + keys = &key->keys[0]; + } + + if (bt_mesh_app_id(val, &keys->id)) { + if (update) { + key->updated = false; + } + + return STATUS_STORAGE_FAIL; + } + + BT_DBG("app_idx 0x%04x AID 0x%02x", app_idx, keys->id); + + key->net_idx = net_idx; + key->app_idx = app_idx; + memcpy(keys->val, val, 16); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + BT_DBG("Storing AppKey persistently"); + bt_mesh_store_app_key(key); + } + + return STATUS_SUCCESS; +} + +static void app_key_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 4 + 4); + u16_t key_net_idx, key_app_idx; + u8_t status; + + key_idx_unpack(buf, &key_net_idx, &key_app_idx); + + BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx); + + bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS); + + status = app_key_set(key_net_idx, key_app_idx, buf->om_data, false); + BT_DBG("status 0x%02x", status); + net_buf_simple_add_u8(msg, status); + + key_idx_pack(msg, key_net_idx, key_app_idx); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send App Key Status response"); + } + + os_mbuf_free_chain(msg); +} + +static void app_key_update(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 4 + 4); + u16_t key_net_idx, key_app_idx; + u8_t status; + + key_idx_unpack(buf, &key_net_idx, &key_app_idx); + + BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx); + + bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS); + + status = app_key_set(key_net_idx, key_app_idx, buf->om_data, true); + BT_DBG("status 0x%02x", status); + net_buf_simple_add_u8(msg, status); + + key_idx_pack(msg, key_net_idx, key_app_idx); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send App Key Status response"); + } + + os_mbuf_free_chain(msg); +} + +struct unbind_data { + u16_t app_idx; + bool store; +}; + +static void _mod_unbind(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + struct unbind_data *data = user_data; + + mod_unbind(mod, data->app_idx, data->store); +} + +void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store) +{ + struct unbind_data data = { .app_idx = key->app_idx, .store = store }; + + BT_DBG("AppIdx 0x%03x store %u", key->app_idx, store); + + bt_mesh_model_foreach(_mod_unbind, &data); + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_clear_app_key(key); + } + + key->net_idx = BT_MESH_KEY_UNUSED; + memset(key->keys, 0, sizeof(key->keys)); +} + +static void app_key_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 4 + 4); + u16_t key_net_idx, key_app_idx; + struct bt_mesh_app_key *key; + u8_t status; + + key_idx_unpack(buf, &key_net_idx, &key_app_idx); + + BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx); + + if (!bt_mesh_subnet_get(key_net_idx)) { + status = STATUS_INVALID_NETKEY; + goto send_status; + } + + key = bt_mesh_app_key_find(key_app_idx); + if (!key) { + /* Treat as success since the client might have missed a + * previous response and is resending the request. + */ + status = STATUS_SUCCESS; + goto send_status; + } + + if (key->net_idx != key_net_idx) { + status = STATUS_INVALID_BINDING; + goto send_status; + } + + bt_mesh_app_key_del(key, true); + status = STATUS_SUCCESS; + +send_status: + bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS); + + net_buf_simple_add_u8(msg, status); + + key_idx_pack(msg, key_net_idx, key_app_idx); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send App Key Status response"); + } + + os_mbuf_free_chain(msg); +} + +/* Index list length: 3 bytes for every pair and 2 bytes for an odd idx */ +#define IDX_LEN(num) (((num) / 2) * 3 + ((num) % 2) * 2) + +static void app_key_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = + NET_BUF_SIMPLE(2 + 3 + 4 + + IDX_LEN(MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT))); + u16_t get_idx, i, prev; + u8_t status; + + get_idx = net_buf_simple_pull_le16(buf); + if (get_idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", get_idx); + goto done; + } + + BT_DBG("idx 0x%04x", get_idx); + + bt_mesh_model_msg_init(msg, OP_APP_KEY_LIST); + + if (!bt_mesh_subnet_get(get_idx)) { + status = STATUS_INVALID_NETKEY; + } else { + status = STATUS_SUCCESS; + } + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, get_idx); + + if (status != STATUS_SUCCESS) { + goto send_status; + } + + prev = BT_MESH_KEY_UNUSED; + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx != get_idx) { + continue; + } + + if (prev == BT_MESH_KEY_UNUSED) { + prev = key->app_idx; + continue; + } + + key_idx_pack(msg, prev, key->app_idx); + prev = BT_MESH_KEY_UNUSED; + } + + if (prev != BT_MESH_KEY_UNUSED) { + net_buf_simple_add_le16(msg, prev); + } + +send_status: + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send AppKey List"); + } + +done: + os_mbuf_free_chain(msg); +} + +static void beacon_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + bt_mesh_model_msg_init(msg, OP_BEACON_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_beacon_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Config Beacon Status response"); + } + os_mbuf_free_chain(msg); +} + +static void beacon_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else if (buf->om_data[0] == 0x00 || buf->om_data[0] == 0x01) { + if (buf->om_data[0] != cfg->beacon) { + cfg->beacon = buf->om_data[0]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + + if (cfg->beacon) { + bt_mesh_beacon_enable(); + } else { + bt_mesh_beacon_disable(); + } + } + } else { + BT_WARN("Invalid Config Beacon value 0x%02x", buf->om_data[0]); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_BEACON_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_beacon_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Config Beacon Status response"); + } + +done: + os_mbuf_free_chain(msg); + +} + +static void default_ttl_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + bt_mesh_model_msg_init(msg, OP_DEFAULT_TTL_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_default_ttl_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Default TTL Status response"); + } + + os_mbuf_free_chain(msg); + +} + +static void default_ttl_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else if (buf->om_data[0] <= BT_MESH_TTL_MAX && buf->om_data[0] != 0x01) { + if (cfg->default_ttl != buf->om_data[0]) { + cfg->default_ttl = buf->om_data[0]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + } + } else { + BT_WARN("Prohibited Default TTL value 0x%02x", buf->om_data[0]); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_DEFAULT_TTL_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_default_ttl_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Default TTL Status response"); + } + +done: + os_mbuf_free_chain(msg); +} + +static void send_gatt_proxy_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + + bt_mesh_model_msg_init(msg, OP_GATT_PROXY_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_gatt_proxy_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send GATT Proxy Status"); + } + + os_mbuf_free_chain(msg); + +} + +static void gatt_proxy_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + send_gatt_proxy_status(model, ctx); +} + +static void gatt_proxy_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + struct bt_mesh_subnet *sub; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (buf->om_data[0] != 0x00 && buf->om_data[0] != 0x01) { + BT_WARN("Invalid GATT Proxy value 0x%02x", buf->om_data[0]); + return; + } + + if (!(MYNEWT_VAL(BLE_MESH_GATT_PROXY)) || + bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_NOT_SUPPORTED) { + goto send_status; + } + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + goto send_status; + } + + BT_DBG("GATT Proxy 0x%02x -> 0x%02x", cfg->gatt_proxy, buf->om_data[0]); + + if (cfg->gatt_proxy == buf->om_data[0]) { + goto send_status; + } + + cfg->gatt_proxy = buf->om_data[0]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + + if (cfg->gatt_proxy == BT_MESH_GATT_PROXY_DISABLED) { + int i; + + /* Section 4.2.11.1: "When the GATT Proxy state is set to + * 0x00, the Node Identity state for all subnets shall be set + * to 0x00 and shall not be changed." + */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx != BT_MESH_KEY_UNUSED) { + bt_mesh_proxy_identity_stop(sub); + } + } + + /* Section 4.2.11: "Upon transition from GATT Proxy state 0x01 + * to GATT Proxy state 0x00 the GATT Bearer Server shall + * disconnect all GATT Bearer Clients. + */ + bt_mesh_proxy_gatt_disconnect(); + } + + bt_mesh_adv_update(); + + sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx); + if ((cfg->hb_pub.feat & BT_MESH_FEAT_PROXY) && sub) { + hb_send(model); + } + +send_status: + send_gatt_proxy_status(model, ctx); +} + +static void net_transmit_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + bt_mesh_model_msg_init(msg, OP_NET_TRANSMIT_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_net_transmit_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Config Network Transmit Status"); + } + + os_mbuf_free_chain(msg); + +} + +static void net_transmit_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + BT_DBG("Transmit 0x%02x (count %u interval %ums)", buf->om_data[0], + BT_MESH_TRANSMIT_COUNT(buf->om_data[0]), + BT_MESH_TRANSMIT_INT(buf->om_data[0])); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else { + cfg->net_transmit = buf->om_data[0]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + } + + bt_mesh_model_msg_init(msg, OP_NET_TRANSMIT_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_net_transmit_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Network Transmit Status"); + } + + os_mbuf_free_chain(msg); +} + +static void relay_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + bt_mesh_model_msg_init(msg, OP_RELAY_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_relay_get()); + net_buf_simple_add_u8(msg, bt_mesh_relay_retransmit_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Config Relay Status response"); + } + + os_mbuf_free_chain(msg); + +} + +static void relay_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else if (buf->om_data[0] == 0x00 || buf->om_data[0] == 0x01) { + struct bt_mesh_subnet *sub; + bool change; + + if (cfg->relay == BT_MESH_RELAY_NOT_SUPPORTED) { + change = false; + } else { + change = (cfg->relay != buf->om_data[0]); + cfg->relay = buf->om_data[0]; + cfg->relay_retransmit = buf->om_data[1]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + } + + BT_DBG("Relay 0x%02x (%s) xmit 0x%02x (count %u interval %u)", + cfg->relay, change ? "changed" : "not changed", + cfg->relay_retransmit, + BT_MESH_TRANSMIT_COUNT(cfg->relay_retransmit), + BT_MESH_TRANSMIT_INT(cfg->relay_retransmit)); + + sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx); + if ((cfg->hb_pub.feat & BT_MESH_FEAT_RELAY) && sub && change) { + hb_send(model); + } + } else { + BT_WARN("Invalid Relay value 0x%02x", buf->om_data[0]); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_RELAY_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_relay_get()); + net_buf_simple_add_u8(msg, bt_mesh_relay_retransmit_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Relay Status response"); + } + +done: + os_mbuf_free_chain(msg); + +} + +static void send_mod_pub_status(struct bt_mesh_model *cfg_mod, + struct bt_mesh_msg_ctx *ctx, + u16_t elem_addr, u16_t pub_addr, + bool vnd, struct bt_mesh_model *mod, + u8_t status, u8_t *mod_id) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 14 + 4); + + bt_mesh_model_msg_init(msg, OP_MOD_PUB_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, elem_addr); + + if (status != STATUS_SUCCESS) { + memset(net_buf_simple_add(msg, 7), 0, 7); + } else { + u16_t idx_cred; + + net_buf_simple_add_le16(msg, pub_addr); + + idx_cred = mod->pub->key | (u16_t)mod->pub->cred << 12; + net_buf_simple_add_le16(msg, idx_cred); + net_buf_simple_add_u8(msg, mod->pub->ttl); + net_buf_simple_add_u8(msg, mod->pub->period); + net_buf_simple_add_u8(msg, mod->pub->retransmit); + } + + if (vnd) { + memcpy(net_buf_simple_add(msg, 4), mod_id, 4); + } else { + memcpy(net_buf_simple_add(msg, 2), mod_id, 2); + } + + if (bt_mesh_model_send(cfg_mod, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model Publication Status"); + } + + os_mbuf_free_chain(msg); +} + +static void mod_pub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, pub_addr = 0; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + mod_id = buf->om_data; + + BT_DBG("elem_addr 0x%04x", elem_addr); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!mod->pub) { + status = STATUS_NVAL_PUB_PARAM; + goto send_status; + } + + pub_addr = mod->pub->addr; + status = STATUS_SUCCESS; + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} + +static void mod_pub_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t retransmit, status, pub_ttl, pub_period, cred_flag; + u16_t elem_addr, pub_addr, pub_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + pub_addr = net_buf_simple_pull_le16(buf); + pub_app_idx = net_buf_simple_pull_le16(buf); + cred_flag = ((pub_app_idx >> 12) & BIT_MASK(1)); + pub_app_idx &= BIT_MASK(12); + + pub_ttl = net_buf_simple_pull_u8(buf); + if (pub_ttl > BT_MESH_TTL_MAX && pub_ttl != BT_MESH_TTL_DEFAULT) { + BT_ERR("Invalid TTL value 0x%02x", pub_ttl); + return; + } + + pub_period = net_buf_simple_pull_u8(buf); + retransmit = net_buf_simple_pull_u8(buf); + mod_id = buf->om_data; + + BT_DBG("elem_addr 0x%04x pub_addr 0x%04x cred_flag %u", + elem_addr, pub_addr, cred_flag); + BT_DBG("pub_app_idx 0x%03x, pub_ttl %u pub_period 0x%02x", + pub_app_idx, pub_ttl, pub_period); + BT_DBG("retransmit 0x%02x (count %u interval %ums)", retransmit, + BT_MESH_PUB_TRANSMIT_COUNT(retransmit), + BT_MESH_PUB_TRANSMIT_INT(retransmit)); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = _mod_pub_set(mod, pub_addr, pub_app_idx, cred_flag, pub_ttl, + pub_period, retransmit, true); + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} + +#if MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0 +static u8_t va_add(u8_t *label_uuid, u16_t *addr) +{ + struct label *free_slot = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (!labels[i].ref) { + free_slot = &labels[i]; + continue; + } + + if (!memcmp(labels[i].uuid, label_uuid, 16)) { + *addr = labels[i].addr; + labels[i].ref++; + return STATUS_SUCCESS; + } + } + + if (!free_slot) { + return STATUS_INSUFF_RESOURCES; + } + + if (bt_mesh_virtual_addr(label_uuid, addr) < 0) { + return STATUS_UNSPECIFIED; + } + + free_slot->ref = 1; + free_slot->addr = *addr; + memcpy(free_slot->uuid, label_uuid, 16); + + return STATUS_SUCCESS; +} + +static u8_t va_del(u8_t *label_uuid, u16_t *addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (!memcmp(labels[i].uuid, label_uuid, 16)) { + if (addr) { + *addr = labels[i].addr; + } + + labels[i].ref--; + return STATUS_SUCCESS; + } + } + + if (addr) { + *addr = BT_MESH_ADDR_UNASSIGNED; + } + + return STATUS_CANNOT_REMOVE; +} + +static void mod_sub_list_clear(struct bt_mesh_model *mod) +{ + u8_t *label_uuid; + int i; + + /* Unref stored labels related to this model */ + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (!BT_MESH_ADDR_IS_VIRTUAL(mod->groups[i])) { + continue; + } + + label_uuid = bt_mesh_label_uuid_get(mod->groups[i]); + if (!label_uuid) { + BT_ERR("Label UUID not found"); + continue; + } + + va_del(label_uuid, NULL); + } + + /* Clear all subscriptions (0x0000 is the unassigned address) */ + memset(mod->groups, 0, sizeof(mod->groups)); +} + +static void mod_pub_va_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t retransmit, status, pub_ttl, pub_period, cred_flag; + u16_t elem_addr, pub_addr, pub_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + pub_app_idx = net_buf_simple_pull_le16(buf); + cred_flag = ((pub_app_idx >> 12) & BIT_MASK(1)); + pub_app_idx &= BIT_MASK(12); + pub_ttl = net_buf_simple_pull_u8(buf); + if (pub_ttl > BT_MESH_TTL_MAX && pub_ttl != BT_MESH_TTL_DEFAULT) { + BT_ERR("Invalid TTL value 0x%02x", pub_ttl); + return; + } + + pub_period = net_buf_simple_pull_u8(buf); + retransmit = net_buf_simple_pull_u8(buf); + mod_id = buf->om_data; + + BT_DBG("elem_addr 0x%04x pub_addr 0x%04x cred_flag %u", + elem_addr, pub_addr, cred_flag); + BT_DBG("pub_app_idx 0x%03x, pub_ttl %u pub_period 0x%02x", + pub_app_idx, pub_ttl, pub_period); + BT_DBG("retransmit 0x%02x (count %u interval %ums)", retransmit, + BT_MESH_PUB_TRANSMIT_COUNT(retransmit), + BT_MESH_PUB_TRANSMIT_INT(retransmit)); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + pub_addr = 0; + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + pub_addr = 0; + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = va_add(label_uuid, &pub_addr); + if (status == STATUS_SUCCESS) { + status = _mod_pub_set(mod, pub_addr, pub_app_idx, cred_flag, + pub_ttl, pub_period, retransmit, true); + } + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} +#else +static void mod_sub_list_clear(struct bt_mesh_model *mod) +{ + /* Clear all subscriptions (0x0000 is the unassigned address) */ + memset(mod->groups, 0, sizeof(mod->groups)); +} + +static void mod_pub_va_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t *mod_id, status; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t elem_addr, pub_addr = 0; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + net_buf_simple_pull(buf, 16); + mod_id = net_buf_simple_pull(buf, 4); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!mod->pub) { + status = STATUS_NVAL_PUB_PARAM; + goto send_status; + } + + pub_addr = mod->pub->addr; + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} +#endif /* MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0 */ + +static void send_mod_sub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, u8_t status, + u16_t elem_addr, u16_t sub_addr, u8_t *mod_id, + bool vnd) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); + + BT_DBG("status 0x%02x elem_addr 0x%04x sub_addr 0x%04x", status, + elem_addr, sub_addr); + + bt_mesh_model_msg_init(msg, OP_MOD_SUB_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, sub_addr); + + if (vnd) { + memcpy(net_buf_simple_add(msg, 4), mod_id, 4); + } else { + memcpy(net_buf_simple_add(msg, 2), mod_id, 2); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model Subscription Status"); + } + + os_mbuf_free_chain(msg); +} + +static void mod_sub_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + u8_t status; + bool vnd; + int i; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + sub_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("elem_addr 0x%04x, sub_addr 0x%04x", elem_addr, sub_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) { + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (bt_mesh_model_find_group(mod, sub_addr)) { + /* Tried to add existing subscription */ + status = STATUS_SUCCESS; + goto send_status; + } + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) { + mod->groups[i] = sub_addr; + break; + } + } + + if (i == ARRAY_SIZE(mod->groups)) { + status = STATUS_INSUFF_RESOURCES; + } else { + status = STATUS_SUCCESS; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + u16_t *match; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + sub_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) { + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + /* An attempt to remove a non-existing address shall be treated + * as a success. + */ + status = STATUS_SUCCESS; + + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) { + bt_mesh_lpn_group_del(&sub_addr, 1); + } + + match = bt_mesh_model_find_group(mod, sub_addr); + if (match) { + *match = BT_MESH_ADDR_UNASSIGNED; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_overwrite(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + sub_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) { + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) { + bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); + } + + mod_sub_list_clear(mod); + + if (ARRAY_SIZE(mod->groups) > 0) { + mod->groups[0] = sub_addr; + status = STATUS_SUCCESS; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + } else { + status = STATUS_INSUFF_RESOURCES; + } + + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_del_all(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) { + bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); + } + + mod_sub_list_clear(mod); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + status = STATUS_SUCCESS; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BT_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} + +static void mod_sub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = + NET_BUF_SIMPLE(2 + 5 + 4 + + MYNEWT_VAL(BLE_MESH_MODEL_GROUP_COUNT) * 2); + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t addr, id; + int i; + + addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(addr)) { + BT_WARN("Prohibited element address"); + goto done; + } + + id = net_buf_simple_pull_le16(buf); + + BT_DBG("addr 0x%04x id 0x%04x", addr, id); + + bt_mesh_model_msg_init(msg, OP_MOD_SUB_LIST); + + elem = bt_mesh_elem_find(addr); + if (!elem) { + net_buf_simple_add_u8(msg, STATUS_INVALID_ADDRESS); + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, id); + goto send_list; + } + + mod = bt_mesh_model_find(elem, id); + if (!mod) { + net_buf_simple_add_u8(msg, STATUS_INVALID_MODEL); + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, id); + goto send_list; + } + + net_buf_simple_add_u8(msg, STATUS_SUCCESS); + + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, id); + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { + net_buf_simple_add_le16(msg, mod->groups[i]); + } + } + +send_list: + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model Subscription List"); + } + +done: + os_mbuf_free_chain(msg); + +} + +static void mod_sub_get_vnd(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = + NET_BUF_SIMPLE(2 + 7 + 4 + + MYNEWT_VAL(BLE_MESH_MODEL_GROUP_COUNT) * 2); + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t company, addr, id; + int i; + + addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(addr)) { + BT_WARN("Prohibited element address"); + goto done; + } + + company = net_buf_simple_pull_le16(buf); + id = net_buf_simple_pull_le16(buf); + + BT_DBG("addr 0x%04x company 0x%04x id 0x%04x", addr, company, id); + + bt_mesh_model_msg_init(msg, OP_MOD_SUB_LIST_VND); + + elem = bt_mesh_elem_find(addr); + if (!elem) { + net_buf_simple_add_u8(msg, STATUS_INVALID_ADDRESS); + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, company); + net_buf_simple_add_le16(msg, id); + goto send_list; + } + + mod = bt_mesh_model_find_vnd(elem, company, id); + if (!mod) { + net_buf_simple_add_u8(msg, STATUS_INVALID_MODEL); + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, company); + net_buf_simple_add_le16(msg, id); + goto send_list; + } + + net_buf_simple_add_u8(msg, STATUS_SUCCESS); + + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, company); + net_buf_simple_add_le16(msg, id); + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { + net_buf_simple_add_le16(msg, mod->groups[i]); + } + } + +send_list: + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Vendor Model Subscription List"); + } + +done: + os_mbuf_free_chain(msg); + +} + +#if MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0 +static void mod_sub_va_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + u8_t status; + bool vnd; + int i; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->om_data; + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + sub_addr = BT_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + sub_addr = BT_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = va_add(label_uuid, &sub_addr); + if (status != STATUS_SUCCESS) { + goto send_status; + } + + if (bt_mesh_model_find_group(mod, sub_addr)) { + /* Tried to add existing subscription */ + status = STATUS_SUCCESS; + goto send_status; + } + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) { + mod->groups[i] = sub_addr; + break; + } + } + + if (i == ARRAY_SIZE(mod->groups)) { + status = STATUS_INSUFF_RESOURCES; + } else { + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) { + bt_mesh_lpn_group_add(sub_addr); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + status = STATUS_SUCCESS; + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_va_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + u16_t *match; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + sub_addr = BT_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + sub_addr = BT_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = va_del(label_uuid, &sub_addr); + if (sub_addr == BT_MESH_ADDR_UNASSIGNED) { + goto send_status; + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_del(&sub_addr, 1); + } + + match = bt_mesh_model_find_group(mod, sub_addr); + if (match) { + *match = BT_MESH_ADDR_UNASSIGNED; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + status = STATUS_SUCCESS; + } else { + status = STATUS_CANNOT_REMOVE; + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_va_overwrite(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr = BT_MESH_ADDR_UNASSIGNED; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) { + bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); + } + + mod_sub_list_clear(mod); + + if (ARRAY_SIZE(mod->groups) > 0) { + status = va_add(label_uuid, &sub_addr); + if (status == STATUS_SUCCESS) { + mod->groups[0] = sub_addr; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + } + } else { + status = STATUS_INSUFF_RESOURCES; + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} +#else +static void mod_sub_va_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + net_buf_simple_pull(buf, 16); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BT_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} + +static void mod_sub_va_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + net_buf_simple_pull(buf, 16); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (!get_model(elem, buf, &vnd)) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BT_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} + +static void mod_sub_va_overwrite(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + net_buf_simple_pull(buf, 18); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (!get_model(elem, buf, &vnd)) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BT_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} +#endif /* MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0 */ + +static void send_net_key_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + u16_t idx, u8_t status) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 3 + 4); + + bt_mesh_model_msg_init(msg, OP_NET_KEY_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, idx); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send NetKey Status"); + } + + os_mbuf_free_chain(msg); +} + +static void net_key_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + u16_t idx; + int err; + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + return; + } + + BT_DBG("idx 0x%04x", idx); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) { + sub = &bt_mesh.sub[i]; + break; + } + } + + if (!sub) { + send_net_key_status(model, ctx, idx, + STATUS_INSUFF_RESOURCES); + return; + } + } + + /* Check for already existing subnet */ + if (sub->net_idx == idx) { + u8_t status; + + if (memcmp(buf->om_data, sub->keys[0].net, 16)) { + status = STATUS_IDX_ALREADY_STORED; + } else { + status = STATUS_SUCCESS; + } + + send_net_key_status(model, ctx, idx, status); + return; + } + + err = bt_mesh_net_keys_create(&sub->keys[0], buf->om_data); + if (err) { + send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED); + return; + } + + sub->net_idx = idx; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + BT_DBG("Storing NetKey persistently"); + bt_mesh_store_subnet(sub); + } + + /* Make sure we have valid beacon data to be sent */ + bt_mesh_net_beacon_update(sub); + + if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) { + sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED; + bt_mesh_proxy_beacon_send(sub); + bt_mesh_adv_update(); + } else { + sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED; + } + + send_net_key_status(model, ctx, idx, STATUS_SUCCESS); +} + +static void net_key_update(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + u16_t idx; + int err; + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + return; + } + + BT_DBG("idx 0x%04x", idx); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + send_net_key_status(model, ctx, idx, STATUS_INVALID_NETKEY); + return; + } + + /* The node shall successfully process a NetKey Update message on a + * valid NetKeyIndex when the NetKey value is different and the Key + * Refresh procedure has not been started, or when the NetKey value is + * the same in Phase 1. The NetKey Update message shall generate an + * error when the node is in Phase 2, or Phase 3. + */ + switch (sub->kr_phase) { + case BT_MESH_KR_NORMAL: + if (!memcmp(buf->om_data, sub->keys[0].net, 16)) { + return; + } + break; + case BT_MESH_KR_PHASE_1: + if (!memcmp(buf->om_data, sub->keys[1].net, 16)) { + send_net_key_status(model, ctx, idx, STATUS_SUCCESS); + return; + } + /* fall through */ + case BT_MESH_KR_PHASE_2: + case BT_MESH_KR_PHASE_3: + send_net_key_status(model, ctx, idx, STATUS_CANNOT_UPDATE); + return; + } + + err = bt_mesh_net_keys_create(&sub->keys[1], buf->om_data); + if (!err && ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) || + (MYNEWT_VAL(BLE_MESH_FRIEND)))) { + err = friend_cred_update(sub); + } + + if (err) { + send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED); + return; + } + + sub->kr_phase = BT_MESH_KR_PHASE_1; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + BT_DBG("Storing NetKey persistently"); + bt_mesh_store_subnet(sub); + } + + bt_mesh_net_beacon_update(sub); + + send_net_key_status(model, ctx, idx, STATUS_SUCCESS); +} + +static void hb_pub_disable(struct bt_mesh_cfg_srv *cfg) +{ + BT_DBG(""); + + cfg->hb_pub.dst = BT_MESH_ADDR_UNASSIGNED; + cfg->hb_pub.count = 0; + cfg->hb_pub.ttl = 0; + cfg->hb_pub.period = 0; + + k_delayed_work_cancel(&cfg->hb_pub.timer); +} + +static void net_key_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + u16_t del_idx; + u8_t status; + + del_idx = net_buf_simple_pull_le16(buf); + if (del_idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", del_idx); + return; + } + + BT_DBG("idx 0x%04x", del_idx); + + sub = bt_mesh_subnet_get(del_idx); + if (!sub) { + /* This could be a retry of a previous attempt that had its + * response lost, so pretend that it was a success. + */ + status = STATUS_SUCCESS; + goto send_status; + } + + /* The key that the message was encrypted with cannot be removed. + * The NetKey List must contain a minimum of one NetKey. + */ + if (ctx->net_idx == del_idx) { + status = STATUS_CANNOT_REMOVE; + goto send_status; + } + + bt_mesh_subnet_del(sub, true); + status = STATUS_SUCCESS; + +send_status: + send_net_key_status(model, ctx, del_idx, status); +} + +static void net_key_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = + NET_BUF_SIMPLE(2 + 4 + + IDX_LEN(MYNEWT_VAL(BLE_MESH_SUBNET_COUNT))); + u16_t prev, i; + + bt_mesh_model_msg_init(msg, OP_NET_KEY_LIST); + + prev = BT_MESH_KEY_UNUSED; + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (prev == BT_MESH_KEY_UNUSED) { + prev = sub->net_idx; + continue; + } + + key_idx_pack(msg, prev, sub->net_idx); + prev = BT_MESH_KEY_UNUSED; + } + + if (prev != BT_MESH_KEY_UNUSED) { + net_buf_simple_add_le16(msg, prev); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send NetKey List"); + } + + os_mbuf_free_chain(msg); +} + +static void node_identity_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 4 + 4); + struct bt_mesh_subnet *sub; + u8_t node_id; + u16_t idx; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_STATUS); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + net_buf_simple_add_u8(msg, STATUS_INVALID_NETKEY); + node_id = 0x00; + } else { + net_buf_simple_add_u8(msg, STATUS_SUCCESS); + node_id = sub->node_id; + } + + net_buf_simple_add_le16(msg, idx); + net_buf_simple_add_u8(msg, node_id); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Node Identity Status"); + } + +done: + os_mbuf_free_chain(msg); +} + +static void node_identity_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 4 + 4); + struct bt_mesh_subnet *sub; + u8_t node_id; + u16_t idx; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_WARN("Invalid NetKeyIndex 0x%04x", idx); + goto done; + } + + node_id = net_buf_simple_pull_u8(buf); + if (node_id != 0x00 && node_id != 0x01) { + BT_WARN("Invalid Node ID value 0x%02x", node_id); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_STATUS); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + net_buf_simple_add_u8(msg, STATUS_INVALID_NETKEY); + net_buf_simple_add_le16(msg, idx); + net_buf_simple_add_u8(msg, node_id); + } else { + net_buf_simple_add_u8(msg, STATUS_SUCCESS); + net_buf_simple_add_le16(msg, idx); + + /* Section 4.2.11.1: "When the GATT Proxy state is set to + * 0x00, the Node Identity state for all subnets shall be set + * to 0x00 and shall not be changed." + */ + if (MYNEWT_VAL(BLE_MESH_GATT_PROXY) && + bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) { + if (node_id) { + bt_mesh_proxy_identity_start(sub); + } else { + bt_mesh_proxy_identity_stop(sub); + } + bt_mesh_adv_update(); + } + + net_buf_simple_add_u8(msg, sub->node_id); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Node Identity Status"); + } + +done: + os_mbuf_free_chain(msg); + +} + +static void create_mod_app_status(struct os_mbuf *msg, + struct bt_mesh_model *mod, bool vnd, + u16_t elem_addr, u16_t app_idx, + u8_t status, u8_t *mod_id) +{ + bt_mesh_model_msg_init(msg, OP_MOD_APP_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, app_idx); + + if (vnd) { + memcpy(net_buf_simple_add(msg, 4), mod_id, 4); + } else { + memcpy(net_buf_simple_add(msg, 2), mod_id, 2); + } +} + +static void mod_app_bind(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); + u16_t elem_addr, key_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + goto done; + } + + key_app_idx = net_buf_simple_pull_le16(buf); + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + /* Configuration Server only allows device key based access */ + if (model == mod) { + BT_ERR("Client tried to bind AppKey to Configuration Model"); + status = STATUS_CANNOT_BIND; + goto send_status; + } + + status = mod_bind(mod, key_app_idx); + + if (IS_ENABLED(CONFIG_BT_TESTING) && status == STATUS_SUCCESS) { + bt_test_mesh_model_bound(ctx->addr, mod, key_app_idx); + } + +send_status: + BT_DBG("status 0x%02x", status); + create_mod_app_status(msg, mod, vnd, elem_addr, key_app_idx, status, + mod_id); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model App Bind Status response"); + } + +done: + os_mbuf_free_chain(msg); + +} + +static void mod_app_unbind(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); + u16_t elem_addr, key_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + goto done; + } + + key_app_idx = net_buf_simple_pull_le16(buf); + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = mod_unbind(mod, key_app_idx, true); + + if (IS_ENABLED(CONFIG_BT_TESTING) && status == STATUS_SUCCESS) { + bt_test_mesh_model_unbound(ctx->addr, mod, key_app_idx); + } + +send_status: + BT_DBG("status 0x%02x", status); + create_mod_app_status(msg, mod, vnd, elem_addr, key_app_idx, status, + mod_id); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model App Unbind Status response"); + } + +done: + os_mbuf_free_chain(msg); +} + +#define KEY_LIST_LEN (MYNEWT_VAL(BLE_MESH_MODEL_KEY_COUNT) * 2) + +static void mod_app_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + KEY_LIST_LEN + 4); + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + u16_t elem_addr; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + goto done; + } + + mod_id = buf->om_data; + + BT_DBG("elem_addr 0x%04x", elem_addr); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_list; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_list; + } + + status = STATUS_SUCCESS; + +send_list: + if (vnd) { + bt_mesh_model_msg_init(msg, OP_VND_MOD_APP_LIST); + } else { + bt_mesh_model_msg_init(msg, OP_SIG_MOD_APP_LIST); + } + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, elem_addr); + + if (vnd) { + net_buf_simple_add_mem(msg, mod_id, 4); + } else { + net_buf_simple_add_mem(msg, mod_id, 2); + } + + if (mod) { + int i; + + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + if (mod->keys[i] != BT_MESH_KEY_UNUSED) { + net_buf_simple_add_le16(msg, mod->keys[i]); + } + } + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model Application List message"); + } + +done: + os_mbuf_free_chain(msg); +} + +static void node_reset(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + + bt_mesh_model_msg_init(msg, OP_NODE_RESET_STATUS); + + /* Send the response first since we wont have any keys left to + * send it later. + */ + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Node Reset Status"); + } + + bt_mesh_reset(); + os_mbuf_free_chain(msg); +} + +static void send_friend_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + bt_mesh_model_msg_init(msg, OP_FRIEND_STATUS); + net_buf_simple_add_u8(msg, cfg->frnd); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Friend Status"); + } + os_mbuf_free_chain(msg); +} + +static void friend_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + send_friend_status(model, ctx); +} + +static void friend_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + struct bt_mesh_subnet *sub; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (buf->om_data[0] != 0x00 && buf->om_data[0] != 0x01) { + BT_WARN("Invalid Friend value 0x%02x", buf->om_data[0]); + return; + } + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + goto send_status; + } + + BT_DBG("Friend 0x%02x -> 0x%02x", cfg->frnd, buf->om_data[0]); + + if (cfg->frnd == buf->om_data[0]) { + goto send_status; + } + + if (MYNEWT_VAL(BLE_MESH_FRIEND)) { + cfg->frnd = buf->om_data[0]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + + if (cfg->frnd == BT_MESH_FRIEND_DISABLED) { + bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY); + } + } + + sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx); + if ((cfg->hb_pub.feat & BT_MESH_FEAT_FRIEND) && sub) { + hb_send(model); + } + +send_status: + send_friend_status(model, ctx); +} + +static void lpn_timeout_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct bt_mesh_friend *frnd; + u16_t lpn_addr; + s32_t timeout; + + lpn_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x lpn_addr 0x%02x", + ctx->net_idx, ctx->app_idx, ctx->addr, lpn_addr); + + /* check if it's the address of the Low Power Node? */ + if (!BT_MESH_ADDR_IS_UNICAST(lpn_addr)) { + BT_WARN("Invalid LPNAddress; ignoring msg"); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_LPN_TIMEOUT_STATUS); + net_buf_simple_add_le16(msg, lpn_addr); + + if (!IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + timeout = 0; + goto send_rsp; + } + + frnd = bt_mesh_friend_find(BT_MESH_KEY_ANY, lpn_addr, true, true); + if (!frnd) { + timeout = 0; + goto send_rsp; + } + + timeout = k_delayed_work_remaining_get(&frnd->timer) / 100; + +send_rsp: + net_buf_simple_add_u8(msg, timeout); + net_buf_simple_add_u8(msg, timeout >> 8); + net_buf_simple_add_u8(msg, timeout >> 16); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send LPN PollTimeout Status"); + } + +done: + os_mbuf_free_chain(msg); +} + +static void send_krp_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + u16_t idx, u8_t phase, u8_t status) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 4 + 4); + + bt_mesh_model_msg_init(msg, OP_KRP_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, idx); + net_buf_simple_add_u8(msg, phase); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Key Refresh State Status"); + } + + os_mbuf_free_chain(msg); +} + +static void krp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + u16_t idx; + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + return; + } + + BT_DBG("idx 0x%04x", idx); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY); + } else { + send_krp_status(model, ctx, idx, sub->kr_phase, + STATUS_SUCCESS); + } +} + +static void krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + u8_t phase; + u16_t idx; + + idx = net_buf_simple_pull_le16(buf); + phase = net_buf_simple_pull_u8(buf); + + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + return; + } + + BT_DBG("idx 0x%04x transition 0x%02x", idx, phase); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY); + return; + } + + BT_DBG("%u -> %u", sub->kr_phase, phase); + + if (phase < BT_MESH_KR_PHASE_2 || phase > BT_MESH_KR_PHASE_3 || + (sub->kr_phase == BT_MESH_KR_NORMAL && + phase == BT_MESH_KR_PHASE_2)) { + BT_WARN("Prohibited transition %u -> %u", sub->kr_phase, phase); + return; + } + + if (sub->kr_phase == BT_MESH_KR_PHASE_1 && + phase == BT_MESH_KR_PHASE_2) { + sub->kr_phase = BT_MESH_KR_PHASE_2; + sub->kr_flag = 1; + bt_mesh_net_beacon_update(sub); + } else if ((sub->kr_phase == BT_MESH_KR_PHASE_1 || + sub->kr_phase == BT_MESH_KR_PHASE_2) && + phase == BT_MESH_KR_PHASE_3) { + bt_mesh_net_revoke_keys(sub); + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) || + (MYNEWT_VAL(BLE_MESH_FRIEND))) { + friend_cred_refresh(ctx->net_idx); + } + sub->kr_phase = BT_MESH_KR_NORMAL; + sub->kr_flag = 0; + bt_mesh_net_beacon_update(sub); + } + + send_krp_status(model, ctx, idx, sub->kr_phase, STATUS_SUCCESS); +} + +static u8_t hb_log(u16_t val) +{ + if (!val) { + return 0x00; + } else if (val == 0xffff) { + return 0xff; + } else { + return 32 - __builtin_clz(val); + } +} + +static u8_t hb_pub_count_log(u16_t val) +{ + if (!val) { + return 0x00; + } else if (val == 0x01) { + return 0x01; + } else if (val == 0xffff) { + return 0xff; + } else { + return 32 - __builtin_clz(val - 1) + 1; + } +} + +static u16_t hb_pwr2(u8_t val, u8_t sub) +{ + if (!val) { + return 0x0000; + } else if (val == 0xff || val == 0x11) { + return 0xffff; + } else { + return (1 << (val - sub)); + } +} + +struct hb_pub_param { + u16_t dst; + u8_t count_log; + u8_t period_log; + u8_t ttl; + u16_t feat; + u16_t net_idx; +} __packed; + +static void hb_pub_send_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, u8_t status, + struct hb_pub_param *orig_msg) +{ + /* Needed size: opcode (1 byte) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(1 + 10 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("src 0x%04x status 0x%02x", ctx->addr, status); + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_PUB_STATUS); + + net_buf_simple_add_u8(msg, status); + + if (orig_msg) { + memcpy(net_buf_simple_add(msg, sizeof(*orig_msg)), orig_msg, + sizeof(*orig_msg)); + goto send; + } + + net_buf_simple_add_le16(msg, cfg->hb_pub.dst); + net_buf_simple_add_u8(msg, hb_pub_count_log(cfg->hb_pub.count)); + net_buf_simple_add_u8(msg, cfg->hb_pub.period); + net_buf_simple_add_u8(msg, cfg->hb_pub.ttl); + net_buf_simple_add_le16(msg, cfg->hb_pub.feat); + net_buf_simple_add_le16(msg, cfg->hb_pub.net_idx); + +send: + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Heartbeat Publication Status"); + } + + os_mbuf_free_chain(msg); +} + +static void heartbeat_pub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG("src 0x%04x", ctx->addr); + + hb_pub_send_status(model, ctx, STATUS_SUCCESS, NULL); +} + +static void heartbeat_pub_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct hb_pub_param *param = (void *)buf->om_data; + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t dst, feat, idx; + u8_t status; + + BT_DBG("src 0x%04x", ctx->addr); + + dst = sys_le16_to_cpu(param->dst); + /* All other address types but virtual are valid */ + if (BT_MESH_ADDR_IS_VIRTUAL(dst)) { + status = STATUS_INVALID_ADDRESS; + goto failed; + } + + if (param->count_log > 0x11 && param->count_log != 0xff) { + status = STATUS_CANNOT_SET; + goto failed; + } + + if (param->period_log > 0x10) { + status = STATUS_CANNOT_SET; + goto failed; + } + + if (param->ttl > BT_MESH_TTL_MAX && param->ttl != BT_MESH_TTL_DEFAULT) { + BT_ERR("Invalid TTL value 0x%02x", param->ttl); + return; + } + + feat = sys_le16_to_cpu(param->feat); + + idx = sys_le16_to_cpu(param->net_idx); + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + return; + } + + if (!bt_mesh_subnet_get(idx)) { + status = STATUS_INVALID_NETKEY; + goto failed; + } + + cfg->hb_pub.dst = dst; + cfg->hb_pub.period = param->period_log; + cfg->hb_pub.feat = feat & BT_MESH_FEAT_SUPPORTED; + cfg->hb_pub.net_idx = idx; + + if (dst == BT_MESH_ADDR_UNASSIGNED) { + hb_pub_disable(cfg); + } else { + /* 2^(n-1) */ + cfg->hb_pub.count = hb_pwr2(param->count_log, 1); + cfg->hb_pub.ttl = param->ttl; + + BT_DBG("period %u ms", hb_pwr2(param->period_log, 1) * 1000); + + /* The first Heartbeat message shall be published as soon + * as possible after the Heartbeat Publication Period state + * has been configured for periodic publishing. + */ + if (param->period_log && param->count_log) { + k_work_submit(&cfg->hb_pub.timer.work); + } else { + k_delayed_work_cancel(&cfg->hb_pub.timer); + } + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_hb_pub(); + } + + hb_pub_send_status(model, ctx, STATUS_SUCCESS, NULL); + + return; + +failed: + hb_pub_send_status(model, ctx, status, param); +} + +static void hb_sub_send_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, u8_t status) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t period; + s64_t uptime; + + BT_DBG("src 0x%04x status 0x%02x", ctx->addr, status); + + uptime = k_uptime_get(); + if (uptime > cfg->hb_sub.expiry) { + period = 0; + } else { + period = (cfg->hb_sub.expiry - uptime) / 1000; + } + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_SUB_STATUS); + + net_buf_simple_add_u8(msg, status); + + net_buf_simple_add_le16(msg, cfg->hb_sub.src); + net_buf_simple_add_le16(msg, cfg->hb_sub.dst); + + net_buf_simple_add_u8(msg, hb_log(period)); + net_buf_simple_add_u8(msg, hb_log(cfg->hb_sub.count)); + net_buf_simple_add_u8(msg, cfg->hb_sub.min_hops); + net_buf_simple_add_u8(msg, cfg->hb_sub.max_hops); + + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Heartbeat Subscription Status"); + } + + os_mbuf_free_chain(msg); +} + +static void heartbeat_sub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG("src 0x%04x", ctx->addr); + + hb_sub_send_status(model, ctx, STATUS_SUCCESS); +} + +static void heartbeat_sub_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t sub_src, sub_dst; + u8_t sub_period; + s32_t period_ms; + + BT_DBG("src 0x%04x", ctx->addr); + + sub_src = net_buf_simple_pull_le16(buf); + sub_dst = net_buf_simple_pull_le16(buf); + sub_period = net_buf_simple_pull_u8(buf); + + BT_DBG("sub_src 0x%04x sub_dst 0x%04x period 0x%02x", + sub_src, sub_dst, sub_period); + + if (sub_src != BT_MESH_ADDR_UNASSIGNED && + !BT_MESH_ADDR_IS_UNICAST(sub_src)) { + BT_WARN("Prohibited source address"); + return; + } + + if (BT_MESH_ADDR_IS_VIRTUAL(sub_dst) || BT_MESH_ADDR_IS_RFU(sub_dst) || + (BT_MESH_ADDR_IS_UNICAST(sub_dst) && + sub_dst != bt_mesh_primary_addr())) { + BT_WARN("Prohibited destination address"); + return; + } + + if (sub_period > 0x11) { + BT_WARN("Prohibited subscription period 0x%02x", sub_period); + return; + } + + if (sub_src == BT_MESH_ADDR_UNASSIGNED || + sub_dst == BT_MESH_ADDR_UNASSIGNED || + sub_period == 0x00) { + /* Only an explicit address change to unassigned should + * trigger clearing of the values according to + * MESH/NODE/CFG/HBS/BV-02-C. + */ + if (sub_src == BT_MESH_ADDR_UNASSIGNED || + sub_dst == BT_MESH_ADDR_UNASSIGNED) { + cfg->hb_sub.src = BT_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.dst = BT_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.min_hops = BT_MESH_TTL_MAX; + cfg->hb_sub.max_hops = 0; + cfg->hb_sub.count = 0; + } + + period_ms = 0; + } else { + cfg->hb_sub.src = sub_src; + cfg->hb_sub.dst = sub_dst; + cfg->hb_sub.min_hops = BT_MESH_TTL_MAX; + cfg->hb_sub.max_hops = 0; + cfg->hb_sub.count = 0; + period_ms = hb_pwr2(sub_period, 1) * 1000; + } + + /* Let the transport layer know it needs to handle this address */ + bt_mesh_set_hb_sub_dst(cfg->hb_sub.dst); + + BT_DBG("period_ms %u", (unsigned) period_ms); + + if (period_ms) { + cfg->hb_sub.expiry = k_uptime_get() + period_ms; + } else { + cfg->hb_sub.expiry = 0; + } + + hb_sub_send_status(model, ctx, STATUS_SUCCESS); + + /* MESH/NODE/CFG/HBS/BV-01-C expects the MinHops to be 0x7f after + * disabling subscription, but 0x00 for subsequent Get requests. + */ + if (!period_ms) { + cfg->hb_sub.min_hops = 0; + } +} + +const struct bt_mesh_model_op bt_mesh_cfg_srv_op[] = { + { OP_DEV_COMP_DATA_GET, 1, dev_comp_data_get }, + { OP_APP_KEY_ADD, 19, app_key_add }, + { OP_APP_KEY_UPDATE, 19, app_key_update }, + { OP_APP_KEY_DEL, 3, app_key_del }, + { OP_APP_KEY_GET, 2, app_key_get }, + { OP_BEACON_GET, 0, beacon_get }, + { OP_BEACON_SET, 1, beacon_set }, + { OP_DEFAULT_TTL_GET, 0, default_ttl_get }, + { OP_DEFAULT_TTL_SET, 1, default_ttl_set }, + { OP_GATT_PROXY_GET, 0, gatt_proxy_get }, + { OP_GATT_PROXY_SET, 1, gatt_proxy_set }, + { OP_NET_TRANSMIT_GET, 0, net_transmit_get }, + { OP_NET_TRANSMIT_SET, 1, net_transmit_set }, + { OP_RELAY_GET, 0, relay_get }, + { OP_RELAY_SET, 2, relay_set }, + { OP_MOD_PUB_GET, 4, mod_pub_get }, + { OP_MOD_PUB_SET, 11, mod_pub_set }, + { OP_MOD_PUB_VA_SET, 24, mod_pub_va_set }, + { OP_MOD_SUB_ADD, 6, mod_sub_add }, + { OP_MOD_SUB_VA_ADD, 20, mod_sub_va_add }, + { OP_MOD_SUB_DEL, 6, mod_sub_del }, + { OP_MOD_SUB_VA_DEL, 20, mod_sub_va_del }, + { OP_MOD_SUB_OVERWRITE, 6, mod_sub_overwrite }, + { OP_MOD_SUB_VA_OVERWRITE, 20, mod_sub_va_overwrite }, + { OP_MOD_SUB_DEL_ALL, 4, mod_sub_del_all }, + { OP_MOD_SUB_GET, 4, mod_sub_get }, + { OP_MOD_SUB_GET_VND, 6, mod_sub_get_vnd }, + { OP_NET_KEY_ADD, 18, net_key_add }, + { OP_NET_KEY_UPDATE, 18, net_key_update }, + { OP_NET_KEY_DEL, 2, net_key_del }, + { OP_NET_KEY_GET, 0, net_key_get }, + { OP_NODE_IDENTITY_GET, 2, node_identity_get }, + { OP_NODE_IDENTITY_SET, 3, node_identity_set }, + { OP_MOD_APP_BIND, 6, mod_app_bind }, + { OP_MOD_APP_UNBIND, 6, mod_app_unbind }, + { OP_SIG_MOD_APP_GET, 4, mod_app_get }, + { OP_VND_MOD_APP_GET, 6, mod_app_get }, + { OP_NODE_RESET, 0, node_reset }, + { OP_FRIEND_GET, 0, friend_get }, + { OP_FRIEND_SET, 1, friend_set }, + { OP_LPN_TIMEOUT_GET, 2, lpn_timeout_get }, + { OP_KRP_GET, 2, krp_get }, + { OP_KRP_SET, 3, krp_set }, + { OP_HEARTBEAT_PUB_GET, 0, heartbeat_pub_get }, + { OP_HEARTBEAT_PUB_SET, 9, heartbeat_pub_set }, + { OP_HEARTBEAT_SUB_GET, 0, heartbeat_sub_get }, + { OP_HEARTBEAT_SUB_SET, 5, heartbeat_sub_set }, + BT_MESH_MODEL_OP_END, +}; + +static void hb_publish(struct ble_npl_event *work) +{ + struct bt_mesh_cfg_srv *cfg = ble_npl_event_get_arg(work); + struct bt_mesh_model *model = cfg->model; + struct bt_mesh_subnet *sub; + u16_t period_ms; + + BT_DBG("hb_pub.count: %u", cfg->hb_pub.count); + + sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx); + if (!sub) { + BT_ERR("No matching subnet for idx 0x%02x", + cfg->hb_pub.net_idx); + cfg->hb_pub.dst = BT_MESH_ADDR_UNASSIGNED; + return; + } + + if (cfg->hb_pub.count == 0) { + return; + } + + period_ms = hb_pwr2(cfg->hb_pub.period, 1) * 1000; + if (period_ms && cfg->hb_pub.count > 1) { + k_delayed_work_submit(&cfg->hb_pub.timer, period_ms); + } + + hb_send(model); + + if (cfg->hb_pub.count != 0xffff) { + cfg->hb_pub.count--; + } +} + +static bool conf_is_valid(struct bt_mesh_cfg_srv *cfg) +{ + if (cfg->relay > 0x02) { + return false; + } + + if (cfg->beacon > 0x01) { + return false; + } + + if (cfg->default_ttl > BT_MESH_TTL_MAX) { + return false; + } + + return true; +} + +int bt_mesh_cfg_srv_init(struct bt_mesh_model *model, bool primary) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + + if (!cfg) { + BT_ERR("No Configuration Server context provided"); + return -EINVAL; + } + + if (!conf_is_valid(cfg)) { + BT_ERR("Invalid values in configuration"); + return -EINVAL; + } + + /* Configuration Model security is device-key based */ + model->keys[0] = BT_MESH_KEY_DEV; + + if (!(MYNEWT_VAL(BLE_MESH_RELAY))) { + cfg->relay = BT_MESH_RELAY_NOT_SUPPORTED; + } + + if (!(MYNEWT_VAL(BLE_MESH_FRIEND))) { + cfg->frnd = BT_MESH_FRIEND_NOT_SUPPORTED; + } + + if (!(MYNEWT_VAL(BLE_MESH_GATT_PROXY))) { + cfg->gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED; + } + + k_delayed_work_init(&cfg->hb_pub.timer, hb_publish); + k_delayed_work_add_arg(&cfg->hb_pub.timer, cfg); + cfg->hb_pub.net_idx = BT_MESH_KEY_UNUSED; + cfg->hb_sub.expiry = 0; + + cfg->model = model; + + conf = cfg; + + return 0; +} + +static void mod_reset(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + /* Clear model state that isn't otherwise cleared. E.g. AppKey + * binding and model publication is cleared as a consequence + * of removing all app keys, however model subscription clearing + * must be taken care of here. + */ + + mod_sub_list_clear(mod); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } +} + +void bt_mesh_cfg_reset(void) +{ + struct bt_mesh_cfg_srv *cfg = conf; + int i; + + if (!cfg) { + return; + } + + bt_mesh_set_hb_sub_dst(BT_MESH_ADDR_UNASSIGNED); + + cfg->hb_sub.src = BT_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.dst = BT_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.expiry = 0; + + /* Delete all net keys, which also takes care of all app keys which + * are associated with each net key. + */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx != BT_MESH_KEY_UNUSED) { + bt_mesh_subnet_del(sub, true); + } + } + + bt_mesh_model_foreach(mod_reset, NULL); + + memset(labels, 0, sizeof(labels)); +} + +void bt_mesh_heartbeat(u16_t src, u16_t dst, u8_t hops, u16_t feat) +{ + struct bt_mesh_cfg_srv *cfg = conf; + + if (!cfg) { + BT_WARN("No configuaration server context available"); + return; + } + + if (src != cfg->hb_sub.src || dst != cfg->hb_sub.dst) { + BT_WARN("No subscription for received heartbeat"); + return; + } + + if (k_uptime_get() > cfg->hb_sub.expiry) { + BT_WARN("Heartbeat subscription period expired"); + return; + } + + cfg->hb_sub.min_hops = min(cfg->hb_sub.min_hops, hops); + cfg->hb_sub.max_hops = max(cfg->hb_sub.max_hops, hops); + + if (cfg->hb_sub.count < 0xffff) { + cfg->hb_sub.count++; + } + + BT_DBG("src 0x%04x dst 0x%04x hops %u min %u max %u count %u", src, + dst, hops, cfg->hb_sub.min_hops, cfg->hb_sub.max_hops, + cfg->hb_sub.count); + + if (cfg->hb_sub.func) { + cfg->hb_sub.func(hops, feat); + } +} + +u8_t bt_mesh_net_transmit_get(void) +{ + if (conf) { + return conf->net_transmit; + } + + return 0; +} + +u8_t bt_mesh_relay_get(void) +{ + if (conf) { + return conf->relay; + } + + return BT_MESH_RELAY_NOT_SUPPORTED; +} + +u8_t bt_mesh_friend_get(void) +{ + BT_DBG("conf %p conf->frnd 0x%02x", conf, conf->frnd); + + if (conf) { + return conf->frnd; + } + + return BT_MESH_FRIEND_NOT_SUPPORTED; +} + +u8_t bt_mesh_relay_retransmit_get(void) +{ + if (conf) { + return conf->relay_retransmit; + } + + return 0; +} + +u8_t bt_mesh_beacon_get(void) +{ + if (conf) { + return conf->beacon; + } + + return BT_MESH_BEACON_DISABLED; +} + +u8_t bt_mesh_gatt_proxy_get(void) +{ + if (conf) { + return conf->gatt_proxy; + } + + return BT_MESH_GATT_PROXY_NOT_SUPPORTED; +} + +u8_t bt_mesh_default_ttl_get(void) +{ + if (conf) { + return conf->default_ttl; + } + + return DEFAULT_TTL; +} + +u8_t *bt_mesh_label_uuid_get(u16_t addr) +{ + int i; + + BT_DBG("addr 0x%04x", addr); + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (labels[i].addr == addr) { + BT_DBG("Found Label UUID for 0x%04x: %s", addr, + bt_hex(labels[i].uuid, 16)); + return labels[i].uuid; + } + } + + BT_WARN("No matching Label UUID for 0x%04x", addr); + + return NULL; +} + +struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void) +{ + if (!conf) { + return NULL; + } + + return &conf->hb_pub; +} + +void bt_mesh_hb_pub_disable(void) +{ + if (conf) { + hb_pub_disable(conf); + } +} + +struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void) +{ + return conf; +} + +void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store) +{ + int i; + + BT_DBG("NetIdx 0x%03x store %u", sub->net_idx, store); + + if (conf && conf->hb_pub.net_idx == sub->net_idx) { + hb_pub_disable(conf); + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_store_hb_pub(); + } + } + + /* Delete any app keys bound to this NetKey index */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx == sub->net_idx) { + bt_mesh_app_key_del(key, store); + } + } + + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + bt_mesh_friend_clear_net_idx(sub->net_idx); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_clear_subnet(sub); + } + + memset(sub, 0, sizeof(*sub)); + sub->net_idx = BT_MESH_KEY_UNUSED; +} diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.c new file mode 100644 index 000000000..79553cba4 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.c @@ -0,0 +1,926 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "syscfg/syscfg.h" + +#if (MYNEWT_VAL(BLE_CRYPTO_STACK_MBEDTLS)) +#include "mbedtls/aes.h" +#include "mbedtls/cipher.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/cmac.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/ecp.h" + +#else +#include +#include +#include +#include +#include + +#endif + +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_CRYPTO)) +#include "host/ble_hs_log.h" + +#include "crypto.h" + +#define NET_MIC_LEN(pdu) (((pdu)[1] & 0x80) ? 8 : 4) +#define APP_MIC_LEN(aszmic) ((aszmic) ? 8 : 4) + +#if MYNEWT_VAL(BLE_CRYPTO_STACK_MBEDTLS) +int bt_mesh_aes_cmac(const u8_t key[16], struct bt_mesh_sg *sg, + size_t sg_len, u8_t mac[16]) +{ + int rc = BLE_HS_EUNKNOWN; + mbedtls_cipher_context_t ctx = {0}; + const mbedtls_cipher_info_t *cipher_info; + + mbedtls_cipher_init(&ctx); + + cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB); + if (cipher_info == NULL) { + goto exit; + } + + if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) { + goto exit; + } + + rc = mbedtls_cipher_cmac_starts(&ctx, key, 128); + if (rc != 0) { + goto exit; + } + + for (; sg_len; sg_len--, sg++) { + if (sg->len != 0 && sg->data != NULL) { + if ((rc = mbedtls_cipher_cmac_update(&ctx, sg->data, sg->len)) != 0) { + goto exit; + } + } + } + rc = mbedtls_cipher_cmac_finish(&ctx, mac); + +exit: + mbedtls_cipher_free(&ctx); + if (rc != 0) { + return -EIO; + } + + return 0; +} + +#else +int bt_mesh_aes_cmac(const u8_t key[16], struct bt_mesh_sg *sg, + size_t sg_len, u8_t mac[16]) +{ + struct tc_aes_key_sched_struct sched; + struct tc_cmac_struct state; + + if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) { + return -EIO; + } + + for (; sg_len; sg_len--, sg++) { + if (tc_cmac_update(&state, sg->data, + sg->len) == TC_CRYPTO_FAIL) { + return -EIO; + } + } + + if (tc_cmac_final(mac, &state) == TC_CRYPTO_FAIL) { + return -EIO; + } + + return 0; +} +#endif + +int bt_mesh_k1(const u8_t *ikm, size_t ikm_len, const u8_t salt[16], + const char *info, u8_t okm[16]) +{ + int err; + + err = bt_mesh_aes_cmac_one(salt, ikm, ikm_len, okm); + if (err < 0) { + return err; + } + + return bt_mesh_aes_cmac_one(okm, info, strlen(info), okm); +} + +int bt_mesh_k2(const u8_t n[16], const u8_t *p, size_t p_len, + u8_t net_id[1], u8_t enc_key[16], u8_t priv_key[16]) +{ + struct bt_mesh_sg sg[3]; + u8_t salt[16]; + u8_t out[16]; + u8_t t[16]; + u8_t pad; + int err; + + BT_DBG("n %s", bt_hex(n, 16)); + BT_DBG("p %s", bt_hex(p, p_len)); + + err = bt_mesh_s1("smk2", salt); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(salt, n, 16, t); + if (err) { + return err; + } + + pad = 0x01; + + sg[0].data = NULL; + sg[0].len = 0; + sg[1].data = p; + sg[1].len = p_len; + sg[2].data = &pad; + sg[2].len = sizeof(pad); + + err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out); + if (err) { + return err; + } + + net_id[0] = out[15] & 0x7f; + + sg[0].data = out; + sg[0].len = sizeof(out); + pad = 0x02; + + err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out); + if (err) { + return err; + } + + memcpy(enc_key, out, 16); + + pad = 0x03; + + err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out); + if (err) { + return err; + } + + memcpy(priv_key, out, 16); + + BT_DBG("NID 0x%02x enc_key %s", net_id[0], bt_hex(enc_key, 16)); + BT_DBG("priv_key %s", bt_hex(priv_key, 16)); + + return 0; +} + +int bt_mesh_k3(const u8_t n[16], u8_t out[8]) +{ + u8_t id64[] = { 'i', 'd', '6', '4', 0x01 }; + u8_t tmp[16]; + u8_t t[16]; + int err; + + err = bt_mesh_s1("smk3", tmp); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(tmp, n, 16, t); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(t, id64, sizeof(id64), tmp); + if (err) { + return err; + } + + memcpy(out, tmp + 8, 8); + + return 0; +} + +int bt_mesh_k4(const u8_t n[16], u8_t out[1]) +{ + u8_t id6[] = { 'i', 'd', '6', 0x01 }; + u8_t tmp[16]; + u8_t t[16]; + int err; + + err = bt_mesh_s1("smk4", tmp); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(tmp, n, 16, t); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(t, id6, sizeof(id6), tmp); + if (err) { + return err; + } + + out[0] = tmp[15] & BIT_MASK(6); + + return 0; +} + +int bt_mesh_id128(const u8_t n[16], const char *s, u8_t out[16]) +{ + const char *id128 = "id128\x01"; + u8_t salt[16]; + int err; + + err = bt_mesh_s1(s, salt); + if (err) { + return err; + } + + return bt_mesh_k1(n, 16, salt, id128, out); +} + +static int bt_mesh_ccm_decrypt(const u8_t key[16], u8_t nonce[13], + const u8_t *enc_msg, size_t msg_len, + const u8_t *aad, size_t aad_len, + u8_t *out_msg, size_t mic_size) +{ + u8_t msg[16], pmsg[16], cmic[16], cmsg[16], Xn[16], mic[16]; + u16_t last_blk, blk_cnt; + size_t i, j; + int err; + + if (msg_len < 1 || aad_len >= 0xff00) { + return -EINVAL; + } + + /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(0x0000, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmic); + if (err) { + return err; + } + + /* X_0 = e(AppKey, 0x09 || nonce || length) */ + if (mic_size == sizeof(u64_t)) { + pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00); + } else { + pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00); + } + + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(msg_len, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* If AAD is being used to authenticate, include it here */ + if (aad_len) { + sys_put_be16(aad_len, pmsg); + + for (i = 0; i < sizeof(u16_t); i++) { + pmsg[i] = Xn[i] ^ pmsg[i]; + } + + j = 0; + aad_len += sizeof(u16_t); + while (aad_len > 16) { + do { + pmsg[i] = Xn[i] ^ aad[j]; + i++, j++; + } while (i < 16); + + aad_len -= 16; + i = 0; + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + for (i = 0; i < aad_len; i++, j++) { + pmsg[i] = Xn[i] ^ aad[j]; + } + + for (i = aad_len; i < 16; i++) { + pmsg[i] = Xn[i]; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + last_blk = msg_len % 16; + blk_cnt = (msg_len + 15) / 16; + if (!last_blk) { + last_blk = 16; + } + + for (j = 0; j < blk_cnt; j++) { + if (j + 1 == blk_cnt) { + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_1 */ + for (i = 0; i < last_blk; i++) { + msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i]; + } + + memcpy(out_msg + (j * 16), msg, last_blk); + + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < last_blk; i++) { + pmsg[i] = Xn[i] ^ msg[i]; + } + + for (i = last_blk; i < 16; i++) { + pmsg[i] = Xn[i] ^ 0x00; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* MIC = C_mic ^ X_1 */ + for (i = 0; i < sizeof(mic); i++) { + mic[i] = cmic[i] ^ Xn[i]; + } + } else { + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_1 */ + for (i = 0; i < 16; i++) { + msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i]; + } + + memcpy(out_msg + (j * 16), msg, 16); + + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < 16; i++) { + pmsg[i] = Xn[i] ^ msg[i]; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + } + + if (memcmp(mic, enc_msg + msg_len, mic_size)) { + return -EBADMSG; + } + + return 0; +} + +static int bt_mesh_ccm_encrypt(const u8_t key[16], u8_t nonce[13], + const u8_t *msg, size_t msg_len, + const u8_t *aad, size_t aad_len, + u8_t *out_msg, size_t mic_size) +{ + u8_t pmsg[16], cmic[16], cmsg[16], mic[16], Xn[16]; + u16_t blk_cnt, last_blk; + size_t i, j; + int err; + + BT_DBG("key %s", bt_hex(key, 16)); + BT_DBG("nonce %s", bt_hex(nonce, 13)); + BT_DBG("msg (len %zu) %s", msg_len, bt_hex(msg, msg_len)); + BT_DBG("aad_len %zu mic_size %zu", aad_len, mic_size); + + /* Unsupported AAD size */ + if (aad_len >= 0xff00) { + return -EINVAL; + } + + /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(0x0000, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmic); + if (err) { + return err; + } + + /* X_0 = e(AppKey, 0x09 || nonce || length) */ + if (mic_size == sizeof(u64_t)) { + pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00); + } else { + pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00); + } + + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(msg_len, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* If AAD is being used to authenticate, include it here */ + if (aad_len) { + sys_put_be16(aad_len, pmsg); + + for (i = 0; i < sizeof(u16_t); i++) { + pmsg[i] = Xn[i] ^ pmsg[i]; + } + + j = 0; + aad_len += sizeof(u16_t); + while (aad_len > 16) { + do { + pmsg[i] = Xn[i] ^ aad[j]; + i++, j++; + } while (i < 16); + + aad_len -= 16; + i = 0; + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + for (i = 0; i < aad_len; i++, j++) { + pmsg[i] = Xn[i] ^ aad[j]; + } + + for (i = aad_len; i < 16; i++) { + pmsg[i] = Xn[i]; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + last_blk = msg_len % 16; + blk_cnt = (msg_len + 15) / 16; + if (!last_blk) { + last_blk = 16; + } + + for (j = 0; j < blk_cnt; j++) { + if (j + 1 == blk_cnt) { + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < last_blk; i++) { + pmsg[i] = Xn[i] ^ msg[(j * 16) + i]; + } + for (i = last_blk; i < 16; i++) { + pmsg[i] = Xn[i] ^ 0x00; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* MIC = C_mic ^ X_1 */ + for (i = 0; i < sizeof(mic); i++) { + mic[i] = cmic[i] ^ Xn[i]; + } + + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_1 */ + for (i = 0; i < last_blk; i++) { + out_msg[(j * 16) + i] = + msg[(j * 16) + i] ^ cmsg[i]; + } + } else { + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < 16; i++) { + pmsg[i] = Xn[i] ^ msg[(j * 16) + i]; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_N */ + for (i = 0; i < 16; i++) { + out_msg[(j * 16) + i] = + msg[(j * 16) + i] ^ cmsg[i]; + } + + } + } + + memcpy(out_msg + msg_len, mic, mic_size); + + return 0; +} + +#if (MYNEWT_VAL(BLE_MESH_PROXY)) +static void create_proxy_nonce(u8_t nonce[13], const u8_t *pdu, + u32_t iv_index) +{ + /* Nonce Type */ + nonce[0] = 0x03; + + /* Pad */ + nonce[1] = 0x00; + + /* Sequence Number */ + nonce[2] = pdu[2]; + nonce[3] = pdu[3]; + nonce[4] = pdu[4]; + + /* Source Address */ + nonce[5] = pdu[5]; + nonce[6] = pdu[6]; + + /* Pad */ + nonce[7] = 0; + nonce[8] = 0; + + /* IV Index */ + sys_put_be32(iv_index, &nonce[9]); +} +#endif /* PROXY */ + +static void create_net_nonce(u8_t nonce[13], const u8_t *pdu, + u32_t iv_index) +{ + /* Nonce Type */ + nonce[0] = 0x00; + + /* FRND + TTL */ + nonce[1] = pdu[1]; + + /* Sequence Number */ + nonce[2] = pdu[2]; + nonce[3] = pdu[3]; + nonce[4] = pdu[4]; + + /* Source Address */ + nonce[5] = pdu[5]; + nonce[6] = pdu[6]; + + /* Pad */ + nonce[7] = 0; + nonce[8] = 0; + + /* IV Index */ + sys_put_be32(iv_index, &nonce[9]); +} + +int bt_mesh_net_obfuscate(u8_t *pdu, u32_t iv_index, + const u8_t privacy_key[16]) +{ + u8_t priv_rand[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, }; + u8_t tmp[16]; + int err, i; + + BT_DBG("IVIndex %u, PrivacyKey %s", (unsigned) iv_index, + bt_hex(privacy_key, 16)); + + sys_put_be32(iv_index, &priv_rand[5]); + memcpy(&priv_rand[9], &pdu[7], 7); + + BT_DBG("PrivacyRandom %s", bt_hex(priv_rand, 16)); + + err = bt_encrypt_be(privacy_key, priv_rand, tmp); + if (err) { + return err; + } + + for (i = 0; i < 6; i++) { + pdu[1 + i] ^= tmp[i]; + } + + return 0; +} + +int bt_mesh_net_encrypt(const u8_t key[16], struct os_mbuf *buf, + u32_t iv_index, bool proxy) +{ + u8_t mic_len = NET_MIC_LEN(buf->om_data); + u8_t nonce[13]; + int err; + + BT_DBG("IVIndex %u EncKey %s mic_len %u", (unsigned) iv_index, + bt_hex(key, 16), mic_len); + BT_DBG("PDU (len %u) %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + if (proxy) { + create_proxy_nonce(nonce, buf->om_data, iv_index); + } else { + create_net_nonce(nonce, buf->om_data, iv_index); + } +#else + create_net_nonce(nonce, buf->om_data, iv_index); +#endif + + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + err = bt_mesh_ccm_encrypt(key, nonce, &buf->om_data[7], buf->om_len - 7, + NULL, 0, &buf->om_data[7], mic_len); + if (!err) { + net_buf_simple_add(buf, mic_len); + } + + return err; +} + +int bt_mesh_net_decrypt(const u8_t key[16], struct os_mbuf *buf, + u32_t iv_index, bool proxy) +{ + u8_t mic_len = NET_MIC_LEN(buf->om_data); + u8_t nonce[13]; + + BT_DBG("PDU (%u bytes) %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + BT_DBG("iv_index %u, key %s mic_len %u", (unsigned) iv_index, + bt_hex(key, 16), mic_len); + +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + if (proxy) { + create_proxy_nonce(nonce, buf->om_data, iv_index); + } else { + create_net_nonce(nonce, buf->om_data, iv_index); + } +#else + create_net_nonce(nonce, buf->om_data, iv_index); +#endif + + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + buf->om_len -= mic_len; + + return bt_mesh_ccm_decrypt(key, nonce, &buf->om_data[7], buf->om_len - 7, + NULL, 0, &buf->om_data[7], mic_len); +} + +static void create_app_nonce(u8_t nonce[13], bool dev_key, u8_t aszmic, + u16_t src, u16_t dst, u32_t seq_num, + u32_t iv_index) +{ + if (dev_key) { + nonce[0] = 0x02; + } else { + nonce[0] = 0x01; + } + + sys_put_be32((seq_num | ((u32_t)aszmic << 31)), &nonce[1]); + + sys_put_be16(src, &nonce[5]); + sys_put_be16(dst, &nonce[7]); + + sys_put_be32(iv_index, &nonce[9]); +} + +int bt_mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf *buf, const u8_t *ad, + u16_t src, u16_t dst, u32_t seq_num, u32_t iv_index) +{ + u8_t nonce[13]; + int err; + + BT_DBG("AppKey %s", bt_hex(key, 16)); + BT_DBG("dev_key %u src 0x%04x dst 0x%04x", dev_key, src, dst); + BT_DBG("seq_num 0x%08x iv_index 0x%08x", (unsigned) seq_num, + (unsigned) iv_index); + BT_DBG("Clear: %s", bt_hex(buf->om_data, buf->om_len)); + + create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index); + + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + err = bt_mesh_ccm_encrypt(key, nonce, buf->om_data, buf->om_len, ad, + ad ? 16 : 0, buf->om_data, APP_MIC_LEN(aszmic)); + if (!err) { + net_buf_simple_add(buf, APP_MIC_LEN(aszmic)); + BT_DBG("Encr: %s", bt_hex(buf->om_data, buf->om_len)); + } + + return err; +} + +int bt_mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf *buf, struct os_mbuf *out, + const u8_t *ad, u16_t src, u16_t dst, u32_t seq_num, + u32_t iv_index) +{ + u8_t nonce[13]; + int err; + + BT_DBG("EncData (len %u) %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index); + + BT_DBG("AppKey %s", bt_hex(key, 16)); + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + err = bt_mesh_ccm_decrypt(key, nonce, buf->om_data, buf->om_len, ad, + ad ? 16 : 0, out->om_data, APP_MIC_LEN(aszmic)); + if (!err) { + net_buf_simple_add(out, buf->om_len); + } + + return err; +} + +/* reversed, 8-bit, poly=0x07 */ +static const u8_t crc_table[256] = { + 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, + 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, + 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, + 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, + + 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, + 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, + 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, + 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, + + 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, + 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, + 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, + 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, + + 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, + 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, + 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, + 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, + + 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, + 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, + 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, + 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, + + 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, + 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, + 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, + 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, + + 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, + 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, + 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, + 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, + + 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, + 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, + 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, + 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf +}; + +u8_t bt_mesh_fcs_calc(const u8_t *data, u8_t data_len) +{ + u8_t fcs = 0xff; + + while (data_len--) { + fcs = crc_table[fcs ^ *data++]; + } + + BT_DBG("fcs 0x%02x", 0xff - fcs); + + return 0xff - fcs; +} + +bool bt_mesh_fcs_check(struct os_mbuf *buf, u8_t received_fcs) +{ + const u8_t *data = buf->om_data; + u16_t data_len = buf->om_len; + u8_t fcs = 0xff; + + while (data_len--) { + fcs = crc_table[fcs ^ *data++]; + } + + return crc_table[fcs ^ received_fcs] == 0xcf; +} + +int bt_mesh_virtual_addr(const u8_t virtual_label[16], u16_t *addr) +{ + u8_t salt[16]; + u8_t tmp[16]; + int err; + + err = bt_mesh_s1("vtad", salt); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(salt, virtual_label, 16, tmp); + if (err) { + return err; + } + + *addr = (sys_get_be16(&tmp[14]) & 0x3fff) | 0x8000; + + return 0; +} + +int bt_mesh_prov_conf_salt(const u8_t conf_inputs[145], u8_t salt[16]) +{ + const u8_t conf_salt_key[16] = { 0 }; + + return bt_mesh_aes_cmac_one(conf_salt_key, conf_inputs, 145, salt); +} + +int bt_mesh_prov_conf_key(const u8_t dhkey[32], const u8_t conf_salt[16], + u8_t conf_key[16]) +{ + return bt_mesh_k1(dhkey, 32, conf_salt, "prck", conf_key); +} + +int bt_mesh_prov_conf(const u8_t conf_key[16], const u8_t rand[16], + const u8_t auth[16], u8_t conf[16]) +{ + struct bt_mesh_sg sg[] = { { rand, 16 }, { auth, 16 } }; + + BT_DBG("ConfirmationKey %s", bt_hex(conf_key, 16)); + BT_DBG("RandomDevice %s", bt_hex(rand, 16)); + BT_DBG("AuthValue %s", bt_hex(auth, 16)); + + return bt_mesh_aes_cmac(conf_key, sg, ARRAY_SIZE(sg), conf); +} + +int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25 + 8], u8_t out[25]) +{ + return bt_mesh_ccm_decrypt(key, nonce, data, 25, NULL, 0, out, 8); +} + +int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags, + const u8_t net_id[8], u32_t iv_index, + u8_t auth[8]) +{ + u8_t msg[13], tmp[16]; + int err; + + BT_DBG("BeaconKey %s", bt_hex(beacon_key, 16)); + BT_DBG("NetId %s", bt_hex(net_id, 8)); + BT_DBG("IV Index 0x%08x", (unsigned) iv_index); + + msg[0] = flags; + memcpy(&msg[1], net_id, 8); + sys_put_be32(iv_index, &msg[9]); + + BT_DBG("BeaconMsg %s", bt_hex(msg, sizeof(msg))); + + err = bt_mesh_aes_cmac_one(beacon_key, msg, sizeof(msg), tmp); + if (!err) { + memcpy(auth, tmp, 8); + } + + return err; +} diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.h new file mode 100644 index 000000000..a56e6b9e8 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.h @@ -0,0 +1,160 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __CRYPTO_H__ +#define __CRYPTO_H__ + +#include "mesh/mesh.h" + +struct bt_mesh_sg { + const void *data; + size_t len; +}; + +int bt_mesh_aes_cmac(const u8_t key[16], struct bt_mesh_sg *sg, + size_t sg_len, u8_t mac[16]); + +static inline int bt_mesh_aes_cmac_one(const u8_t key[16], const void *m, + size_t len, u8_t mac[16]) +{ + struct bt_mesh_sg sg = { m, len }; + + return bt_mesh_aes_cmac(key, &sg, 1, mac); +} + +static inline bool bt_mesh_s1(const char *m, u8_t salt[16]) +{ + const u8_t zero[16] = { 0 }; + + return bt_mesh_aes_cmac_one(zero, m, strlen(m), salt); +} + +int bt_mesh_k1(const u8_t *ikm, size_t ikm_len, const u8_t salt[16], + const char *info, u8_t okm[16]); + +#define bt_mesh_k1_str(ikm, ikm_len, salt_str, info, okm) \ +({ \ + const u8_t salt[16] = salt_str; \ + bt_mesh_k1(ikm, ikm_len, salt, info, okm); \ +}) + +int bt_mesh_k2(const u8_t n[16], const u8_t *p, size_t p_len, + u8_t net_id[1], u8_t enc_key[16], u8_t priv_key[16]); + +int bt_mesh_k3(const u8_t n[16], u8_t out[8]); + +int bt_mesh_k4(const u8_t n[16], u8_t out[1]); + +int bt_mesh_id128(const u8_t n[16], const char *s, u8_t out[16]); + +static inline int bt_mesh_id_resolving_key(const u8_t net_key[16], + u8_t resolving_key[16]) +{ + return bt_mesh_k1_str(net_key, 16, "smbt", "smbi", resolving_key); +} + +static inline int bt_mesh_identity_key(const u8_t net_key[16], + u8_t identity_key[16]) +{ + return bt_mesh_id128(net_key, "nkik", identity_key); +} + +static inline int bt_mesh_beacon_key(const u8_t net_key[16], + u8_t beacon_key[16]) +{ + return bt_mesh_id128(net_key, "nkbk", beacon_key); +} + +int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags, + const u8_t net_id[16], u32_t iv_index, + u8_t auth[8]); + +static inline int bt_mesh_app_id(const u8_t app_key[16], u8_t app_id[1]) +{ + return bt_mesh_k4(app_key, app_id); +} + +static inline int bt_mesh_session_key(const u8_t dhkey[32], + const u8_t prov_salt[16], + u8_t session_key[16]) +{ + return bt_mesh_k1(dhkey, 32, prov_salt, "prsk", session_key); +} + +static inline int bt_mesh_prov_nonce(const u8_t dhkey[32], + const u8_t prov_salt[16], + u8_t nonce[13]) +{ + u8_t tmp[16]; + int err; + + err = bt_mesh_k1(dhkey, 32, prov_salt, "prsn", tmp); + if (!err) { + memcpy(nonce, tmp + 3, 13); + } + + return err; +} + +static inline int bt_mesh_dev_key(const u8_t dhkey[32], + const u8_t prov_salt[16], + u8_t dev_key[16]) +{ + return bt_mesh_k1(dhkey, 32, prov_salt, "prdk", dev_key); +} + +static inline int bt_mesh_prov_salt(const u8_t conf_salt[16], + const u8_t prov_rand[16], + const u8_t dev_rand[16], + u8_t prov_salt[16]) +{ + const u8_t prov_salt_key[16] = { 0 }; + struct bt_mesh_sg sg[] = { + { conf_salt, 16 }, + { prov_rand, 16 }, + { dev_rand, 16 }, + }; + + return bt_mesh_aes_cmac(prov_salt_key, sg, ARRAY_SIZE(sg), prov_salt); +} + +int bt_mesh_net_obfuscate(u8_t *pdu, u32_t iv_index, + const u8_t privacy_key[16]); + +int bt_mesh_net_encrypt(const u8_t key[16], struct os_mbuf *buf, + u32_t iv_index, bool proxy); + +int bt_mesh_net_decrypt(const u8_t key[16], struct os_mbuf *buf, + u32_t iv_index, bool proxy); + +int bt_mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf*buf, const u8_t *ad, + u16_t src, u16_t dst, u32_t seq_num, u32_t iv_index); + +int bt_mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf*buf, struct os_mbuf*out, + const u8_t *ad, u16_t src, u16_t dst, u32_t seq_num, + u32_t iv_index); + +u8_t bt_mesh_fcs_calc(const u8_t *data, u8_t data_len); + +bool bt_mesh_fcs_check(struct os_mbuf *buf, u8_t received_fcs); + +int bt_mesh_virtual_addr(const u8_t virtual_label[16], u16_t *addr); + +int bt_mesh_prov_conf_salt(const u8_t conf_inputs[145], u8_t salt[16]); + +int bt_mesh_prov_conf_key(const u8_t dhkey[32], const u8_t conf_salt[16], + u8_t conf_key[16]); + +int bt_mesh_prov_conf(const u8_t conf_key[16], const u8_t rand[16], + const u8_t auth[16], u8_t conf[16]); + +int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25 + 8], u8_t out[25]); + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/foundation.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/foundation.h new file mode 100644 index 000000000..2c257ee38 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/foundation.h @@ -0,0 +1,164 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FUNDATION_H__ +#define __FUNDATION_H__ + +#define OP_APP_KEY_ADD BT_MESH_MODEL_OP_1(0x00) +#define OP_APP_KEY_UPDATE BT_MESH_MODEL_OP_1(0x01) +#define OP_DEV_COMP_DATA_STATUS BT_MESH_MODEL_OP_1(0x02) +#define OP_MOD_PUB_SET BT_MESH_MODEL_OP_1(0x03) +#define OP_HEALTH_CURRENT_STATUS BT_MESH_MODEL_OP_1(0x04) +#define OP_HEALTH_FAULT_STATUS BT_MESH_MODEL_OP_1(0x05) +#define OP_HEARTBEAT_PUB_STATUS BT_MESH_MODEL_OP_1(0x06) +#define OP_APP_KEY_DEL BT_MESH_MODEL_OP_2(0x80, 0x00) +#define OP_APP_KEY_GET BT_MESH_MODEL_OP_2(0x80, 0x01) +#define OP_APP_KEY_LIST BT_MESH_MODEL_OP_2(0x80, 0x02) +#define OP_APP_KEY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x03) +#define OP_ATTENTION_GET BT_MESH_MODEL_OP_2(0x80, 0x04) +#define OP_ATTENTION_SET BT_MESH_MODEL_OP_2(0x80, 0x05) +#define OP_ATTENTION_SET_UNREL BT_MESH_MODEL_OP_2(0x80, 0x06) +#define OP_ATTENTION_STATUS BT_MESH_MODEL_OP_2(0x80, 0x07) +#define OP_DEV_COMP_DATA_GET BT_MESH_MODEL_OP_2(0x80, 0x08) +#define OP_BEACON_GET BT_MESH_MODEL_OP_2(0x80, 0x09) +#define OP_BEACON_SET BT_MESH_MODEL_OP_2(0x80, 0x0a) +#define OP_BEACON_STATUS BT_MESH_MODEL_OP_2(0x80, 0x0b) +#define OP_DEFAULT_TTL_GET BT_MESH_MODEL_OP_2(0x80, 0x0c) +#define OP_DEFAULT_TTL_SET BT_MESH_MODEL_OP_2(0x80, 0x0d) +#define OP_DEFAULT_TTL_STATUS BT_MESH_MODEL_OP_2(0x80, 0x0e) +#define OP_FRIEND_GET BT_MESH_MODEL_OP_2(0x80, 0x0f) +#define OP_FRIEND_SET BT_MESH_MODEL_OP_2(0x80, 0x10) +#define OP_FRIEND_STATUS BT_MESH_MODEL_OP_2(0x80, 0x11) +#define OP_GATT_PROXY_GET BT_MESH_MODEL_OP_2(0x80, 0x12) +#define OP_GATT_PROXY_SET BT_MESH_MODEL_OP_2(0x80, 0x13) +#define OP_GATT_PROXY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x14) +#define OP_KRP_GET BT_MESH_MODEL_OP_2(0x80, 0x15) +#define OP_KRP_SET BT_MESH_MODEL_OP_2(0x80, 0x16) +#define OP_KRP_STATUS BT_MESH_MODEL_OP_2(0x80, 0x17) +#define OP_MOD_PUB_GET BT_MESH_MODEL_OP_2(0x80, 0x18) +#define OP_MOD_PUB_STATUS BT_MESH_MODEL_OP_2(0x80, 0x19) +#define OP_MOD_PUB_VA_SET BT_MESH_MODEL_OP_2(0x80, 0x1a) +#define OP_MOD_SUB_ADD BT_MESH_MODEL_OP_2(0x80, 0x1b) +#define OP_MOD_SUB_DEL BT_MESH_MODEL_OP_2(0x80, 0x1c) +#define OP_MOD_SUB_DEL_ALL BT_MESH_MODEL_OP_2(0x80, 0x1d) +#define OP_MOD_SUB_OVERWRITE BT_MESH_MODEL_OP_2(0x80, 0x1e) +#define OP_MOD_SUB_STATUS BT_MESH_MODEL_OP_2(0x80, 0x1f) +#define OP_MOD_SUB_VA_ADD BT_MESH_MODEL_OP_2(0x80, 0x20) +#define OP_MOD_SUB_VA_DEL BT_MESH_MODEL_OP_2(0x80, 0x21) +#define OP_MOD_SUB_VA_OVERWRITE BT_MESH_MODEL_OP_2(0x80, 0x22) +#define OP_NET_TRANSMIT_GET BT_MESH_MODEL_OP_2(0x80, 0x23) +#define OP_NET_TRANSMIT_SET BT_MESH_MODEL_OP_2(0x80, 0x24) +#define OP_NET_TRANSMIT_STATUS BT_MESH_MODEL_OP_2(0x80, 0x25) +#define OP_RELAY_GET BT_MESH_MODEL_OP_2(0x80, 0x26) +#define OP_RELAY_SET BT_MESH_MODEL_OP_2(0x80, 0x27) +#define OP_RELAY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x28) +#define OP_MOD_SUB_GET BT_MESH_MODEL_OP_2(0x80, 0x29) +#define OP_MOD_SUB_LIST BT_MESH_MODEL_OP_2(0x80, 0x2a) +#define OP_MOD_SUB_GET_VND BT_MESH_MODEL_OP_2(0x80, 0x2b) +#define OP_MOD_SUB_LIST_VND BT_MESH_MODEL_OP_2(0x80, 0x2c) +#define OP_LPN_TIMEOUT_GET BT_MESH_MODEL_OP_2(0x80, 0x2d) +#define OP_LPN_TIMEOUT_STATUS BT_MESH_MODEL_OP_2(0x80, 0x2e) +#define OP_HEALTH_FAULT_CLEAR BT_MESH_MODEL_OP_2(0x80, 0x2f) +#define OP_HEALTH_FAULT_CLEAR_UNREL BT_MESH_MODEL_OP_2(0x80, 0x30) +#define OP_HEALTH_FAULT_GET BT_MESH_MODEL_OP_2(0x80, 0x31) +#define OP_HEALTH_FAULT_TEST BT_MESH_MODEL_OP_2(0x80, 0x32) +#define OP_HEALTH_FAULT_TEST_UNREL BT_MESH_MODEL_OP_2(0x80, 0x33) +#define OP_HEALTH_PERIOD_GET BT_MESH_MODEL_OP_2(0x80, 0x34) +#define OP_HEALTH_PERIOD_SET BT_MESH_MODEL_OP_2(0x80, 0x35) +#define OP_HEALTH_PERIOD_SET_UNREL BT_MESH_MODEL_OP_2(0x80, 0x36) +#define OP_HEALTH_PERIOD_STATUS BT_MESH_MODEL_OP_2(0x80, 0x37) +#define OP_HEARTBEAT_PUB_GET BT_MESH_MODEL_OP_2(0x80, 0x38) +#define OP_HEARTBEAT_PUB_SET BT_MESH_MODEL_OP_2(0x80, 0x39) +#define OP_HEARTBEAT_SUB_GET BT_MESH_MODEL_OP_2(0x80, 0x3a) +#define OP_HEARTBEAT_SUB_SET BT_MESH_MODEL_OP_2(0x80, 0x3b) +#define OP_HEARTBEAT_SUB_STATUS BT_MESH_MODEL_OP_2(0x80, 0x3c) +#define OP_MOD_APP_BIND BT_MESH_MODEL_OP_2(0x80, 0x3d) +#define OP_MOD_APP_STATUS BT_MESH_MODEL_OP_2(0x80, 0x3e) +#define OP_MOD_APP_UNBIND BT_MESH_MODEL_OP_2(0x80, 0x3f) +#define OP_NET_KEY_ADD BT_MESH_MODEL_OP_2(0x80, 0x40) +#define OP_NET_KEY_DEL BT_MESH_MODEL_OP_2(0x80, 0x41) +#define OP_NET_KEY_GET BT_MESH_MODEL_OP_2(0x80, 0x42) +#define OP_NET_KEY_LIST BT_MESH_MODEL_OP_2(0x80, 0x43) +#define OP_NET_KEY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x44) +#define OP_NET_KEY_UPDATE BT_MESH_MODEL_OP_2(0x80, 0x45) +#define OP_NODE_IDENTITY_GET BT_MESH_MODEL_OP_2(0x80, 0x46) +#define OP_NODE_IDENTITY_SET BT_MESH_MODEL_OP_2(0x80, 0x47) +#define OP_NODE_IDENTITY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x48) +#define OP_NODE_RESET BT_MESH_MODEL_OP_2(0x80, 0x49) +#define OP_NODE_RESET_STATUS BT_MESH_MODEL_OP_2(0x80, 0x4a) +#define OP_SIG_MOD_APP_GET BT_MESH_MODEL_OP_2(0x80, 0x4b) +#define OP_SIG_MOD_APP_LIST BT_MESH_MODEL_OP_2(0x80, 0x4c) +#define OP_VND_MOD_APP_GET BT_MESH_MODEL_OP_2(0x80, 0x4d) +#define OP_VND_MOD_APP_LIST BT_MESH_MODEL_OP_2(0x80, 0x4e) + +#define STATUS_SUCCESS 0x00 +#define STATUS_INVALID_ADDRESS 0x01 +#define STATUS_INVALID_MODEL 0x02 +#define STATUS_INVALID_APPKEY 0x03 +#define STATUS_INVALID_NETKEY 0x04 +#define STATUS_INSUFF_RESOURCES 0x05 +#define STATUS_IDX_ALREADY_STORED 0x06 +#define STATUS_NVAL_PUB_PARAM 0x07 +#define STATUS_NOT_SUB_MOD 0x08 +#define STATUS_STORAGE_FAIL 0x09 +#define STATUS_FEAT_NOT_SUPP 0x0a +#define STATUS_CANNOT_UPDATE 0x0b +#define STATUS_CANNOT_REMOVE 0x0c +#define STATUS_CANNOT_BIND 0x0d +#define STATUS_TEMP_STATE_CHG_FAIL 0x0e +#define STATUS_CANNOT_SET 0x0f +#define STATUS_UNSPECIFIED 0x10 +#define STATUS_INVALID_BINDING 0x11 + +int bt_mesh_cfg_srv_init(struct bt_mesh_model *model, bool primary); +int bt_mesh_health_srv_init(struct bt_mesh_model *model, bool primary); + +int bt_mesh_cfg_cli_init(struct bt_mesh_model *model, bool primary); +int bt_mesh_health_cli_init(struct bt_mesh_model *model, bool primary); + +void bt_mesh_cfg_reset(void); + +void bt_mesh_heartbeat(u16_t src, u16_t dst, u8_t hops, u16_t feat); + +void bt_mesh_attention(struct bt_mesh_model *model, u8_t time); + +u8_t *bt_mesh_label_uuid_get(u16_t addr); + +struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void); +void bt_mesh_hb_pub_disable(void); +struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void); + +u8_t bt_mesh_net_transmit_get(void); +u8_t bt_mesh_relay_get(void); +u8_t bt_mesh_friend_get(void); +u8_t bt_mesh_relay_retransmit_get(void); +u8_t bt_mesh_beacon_get(void); +u8_t bt_mesh_gatt_proxy_get(void); +u8_t bt_mesh_default_ttl_get(void); + +void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store); + +struct bt_mesh_app_key *bt_mesh_app_key_alloc(u16_t app_idx); +void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store); + +static inline void key_idx_pack(struct os_mbuf *buf, + u16_t idx1, u16_t idx2) +{ + net_buf_simple_add_le16(buf, idx1 | ((idx2 & 0x00f) << 12)); + net_buf_simple_add_u8(buf, idx2 >> 4); +} + +static inline void key_idx_unpack(struct os_mbuf *buf, + u16_t *idx1, u16_t *idx2) +{ + *idx1 = sys_get_le16(&buf->om_data[0]) & 0xfff; + *idx2 = sys_get_le16(&buf->om_data[1]) >> 4; + net_buf_simple_pull(buf, 3); +} + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.c new file mode 100644 index 000000000..a9f25f777 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.c @@ -0,0 +1,1350 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include "syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH_FRIEND) + +#include +#include +#include + +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_FRIEND)) +#include "host/ble_hs_log.h" + +#include "mesh/mesh.h" +#include "mesh/slist.h" +#include "mesh_priv.h" +#include "crypto.h" +#include "adv.h" +#include "net.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "friend.h" + +/* We reserve one extra buffer for each friendship, since we need to be able + * to resend the last sent PDU, which sits separately outside of the queue. + */ +#define FRIEND_BUF_COUNT ((MYNEWT_VAL(BLE_MESH_FRIEND_QUEUE_SIZE) + 1) * MYNEWT_VAL(BLE_MESH_FRIEND_LPN_COUNT)) + +static os_membuf_t friend_buf_mem[OS_MEMPOOL_SIZE( + FRIEND_BUF_COUNT, + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE)]; + +struct os_mbuf_pool friend_os_mbuf_pool; +static struct os_mempool friend_buf_mempool; + + +#define FRIEND_ADV(buf) CONTAINER_OF(BT_MESH_ADV(buf), \ + struct friend_adv, adv) + +/* PDUs from Friend to the LPN should only be transmitted once with the + * smallest possible interval (20ms). + */ +#define FRIEND_XMIT BT_MESH_TRANSMIT(0, 20) + +struct friend_pdu_info { + u16_t src; + u16_t dst; + + u8_t seq[3]; + + u8_t ttl:7, + ctl:1; + + u32_t iv_index; +}; + +static struct friend_adv { + struct bt_mesh_adv adv; + u64_t seq_auth; +} adv_pool[FRIEND_BUF_COUNT]; + +static struct bt_mesh_adv *adv_alloc(int id) +{ + return &adv_pool[id].adv; +} + +static void discard_buffer(void) +{ + struct bt_mesh_friend *frnd = &bt_mesh.frnd[0]; + struct os_mbuf *buf; + int i; + + /* Find the Friend context with the most queued buffers */ + for (i = 1; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + if (bt_mesh.frnd[i].queue_size > frnd->queue_size) { + frnd = &bt_mesh.frnd[i]; + } + } + + buf = net_buf_slist_get(&frnd->queue); + __ASSERT_NO_MSG(buf != NULL); + BT_WARN("Discarding buffer %p for LPN 0x%04x", buf, frnd->lpn); + net_buf_unref(buf); +} + +static struct os_mbuf *friend_buf_alloc(u16_t src) +{ + struct os_mbuf *buf; + + do { + buf = bt_mesh_adv_create_from_pool(&friend_os_mbuf_pool, adv_alloc, + BT_MESH_ADV_DATA, + FRIEND_XMIT, K_NO_WAIT); + if (!buf) { + discard_buffer(); + } + } while (!buf); + + BT_MESH_ADV(buf)->addr = src; + FRIEND_ADV(buf)->seq_auth = TRANS_SEQ_AUTH_NVAL; + + BT_DBG("allocated buf %p", buf); + + return buf; +} + +static bool is_lpn_unicast(struct bt_mesh_friend *frnd, u16_t addr) +{ + if (frnd->lpn == BT_MESH_ADDR_UNASSIGNED) { + return false; + } + + return (addr >= frnd->lpn && addr < (frnd->lpn + frnd->num_elem)); +} + +struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr, + bool valid, bool established) +{ + int i; + + BT_DBG("net_idx 0x%04x lpn_addr 0x%04x", net_idx, lpn_addr); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (valid && !frnd->valid) { + continue; + } + + if (established && !frnd->established) { + continue; + } + + if (net_idx != BT_MESH_KEY_ANY && frnd->net_idx != net_idx) { + continue; + } + + if (is_lpn_unicast(frnd, lpn_addr)) { + return frnd; + } + } + + return NULL; +} + +/* Intentionally start a little bit late into the ReceiveWindow when + * it's large enough. This may improve reliability with some platforms, + * like the PTS, where the receiver might not have sufficiently compensated + * for internal latencies required to start scanning. + */ +static s32_t recv_delay(struct bt_mesh_friend *frnd) +{ +#if CONFIG_BT_MESH_FRIEND_RECV_WIN > 50 + return (s32_t)frnd->recv_delay + (CONFIG_BT_MESH_FRIEND_RECV_WIN / 5); +#else + return frnd->recv_delay; +#endif +} + +static void friend_clear(struct bt_mesh_friend *frnd) +{ + int i; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + k_delayed_work_cancel(&frnd->timer); + + friend_cred_del(frnd->net_idx, frnd->lpn); + + if (frnd->last) { + /* Cancel the sending if necessary */ + if (frnd->pending_buf) { + BT_MESH_ADV(frnd->last)->busy = 0; + } + + net_buf_unref(frnd->last); + frnd->last = NULL; + } + + while (!net_buf_slist_is_empty(&frnd->queue)) { + net_buf_unref(net_buf_slist_get(&frnd->queue)); + } + + for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[i]; + + while (!net_buf_slist_is_empty(&seg->queue)) { + net_buf_unref(net_buf_slist_get(&seg->queue)); + } + } + + frnd->valid = 0; + frnd->established = 0; + frnd->pending_buf = 0; + frnd->fsn = 0; + frnd->queue_size = 0; + frnd->pending_req = 0; + memset(frnd->sub_list, 0, sizeof(frnd->sub_list)); +} + +void bt_mesh_friend_clear_net_idx(u16_t net_idx) +{ + int i; + + BT_DBG("net_idx 0x%04x", net_idx); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) { + friend_clear(frnd); + } + } +} + +void bt_mesh_friend_sec_update(u16_t net_idx) +{ + int i; + + BT_DBG("net_idx 0x%04x", net_idx); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) { + frnd->sec_update = 1; + } + } +} + +int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_clear *msg = (void *)buf->om_data; + struct bt_mesh_friend *frnd; + u16_t lpn_addr, lpn_counter; + struct bt_mesh_net_tx tx = { + .sub = rx->sub, + .ctx = &rx->ctx, + .src = bt_mesh_primary_addr(), + .xmit = bt_mesh_net_transmit_get(), + }; + struct bt_mesh_ctl_friend_clear_confirm cfm; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Clear"); + return -EINVAL; + } + + lpn_addr = sys_be16_to_cpu(msg->lpn_addr); + lpn_counter = sys_be16_to_cpu(msg->lpn_counter); + + BT_DBG("LPN addr 0x%04x counter 0x%04x", lpn_addr, lpn_counter); + + frnd = bt_mesh_friend_find(rx->sub->net_idx, lpn_addr, false, false); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", lpn_addr); + return 0; + } + + /* A Friend Clear message is considered valid if the result of the + * subtraction of the value of the LPNCounter field of the Friend + * Request message (the one that initiated the friendship) from the + * value of the LPNCounter field of the Friend Clear message, modulo + * 65536, is in the range 0 to 255 inclusive. + */ + if (lpn_counter - frnd->lpn_counter > 255) { + BT_WARN("LPN Counter out of range (old %u new %u)", + frnd->lpn_counter, lpn_counter); + return 0; + } + + tx.ctx->send_ttl = BT_MESH_TTL_MAX; + + cfm.lpn_addr = msg->lpn_addr; + cfm.lpn_counter = msg->lpn_counter; + + bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR_CFM, &cfm, + sizeof(cfm), NULL, NULL, NULL); + + friend_clear(frnd); + + return 0; +} + +static void friend_sub_add(struct bt_mesh_friend *frnd, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == BT_MESH_ADDR_UNASSIGNED) { + frnd->sub_list[i] = addr; + return; + } + } + + BT_WARN("No space in friend subscription list"); +} + +static void friend_sub_rem(struct bt_mesh_friend *frnd, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == addr) { + frnd->sub_list[i] = BT_MESH_ADDR_UNASSIGNED; + return; + } + } +} + +static struct os_mbuf *create_friend_pdu(struct bt_mesh_friend *frnd, + struct friend_pdu_info *info, + struct os_mbuf *sdu) +{ + struct bt_mesh_subnet *sub; + const u8_t *enc, *priv; + struct os_mbuf *buf; + u8_t nid; + + sub = bt_mesh_subnet_get(frnd->net_idx); + __ASSERT_NO_MSG(sub != NULL); + + buf = friend_buf_alloc(info->src); + + /* Friend Offer needs master security credentials */ + if (info->ctl && TRANS_CTL_OP(sdu->om_data) == TRANS_CTL_OP_FRIEND_OFFER) { + enc = sub->keys[sub->kr_flag].enc; + priv = sub->keys[sub->kr_flag].privacy; + nid = sub->keys[sub->kr_flag].nid; + } else { + if (friend_cred_get(sub, frnd->lpn, &nid, &enc, &priv)) { + BT_ERR("friend_cred_get failed"); + goto failed; + } + } + + net_buf_add_u8(buf, (nid | (info->iv_index & 1) << 7)); + + if (info->ctl) { + net_buf_add_u8(buf, info->ttl | 0x80); + } else { + net_buf_add_u8(buf, info->ttl); + } + + net_buf_add_mem(buf, info->seq, sizeof(info->seq)); + + net_buf_add_be16(buf, info->src); + net_buf_add_be16(buf, info->dst); + + net_buf_add_mem(buf, sdu->om_data, sdu->om_len); + + /* We re-encrypt and obfuscate using the received IVI rather than + * the normal TX IVI (which may be different) since the transport + * layer nonce includes the IVI. + */ + if (bt_mesh_net_encrypt(enc, buf, info->iv_index, false)) { + BT_ERR("Re-encrypting failed"); + goto failed; + } + + if (bt_mesh_net_obfuscate(buf->om_data, info->iv_index, priv)) { + BT_ERR("Re-obfuscating failed"); + goto failed; + } + + return buf; + +failed: + net_buf_unref(buf); + return NULL; +} + +static struct os_mbuf *encode_friend_ctl(struct bt_mesh_friend *frnd, + u8_t ctl_op, + struct os_mbuf *sdu) +{ + struct friend_pdu_info info; + u32_t seq; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + net_buf_simple_push_u8(sdu, TRANS_CTL_HDR(ctl_op, 0)); + + info.src = bt_mesh_primary_addr(); + info.dst = frnd->lpn; + + info.ctl = 1; + info.ttl = 0; + + seq = bt_mesh_next_seq(); + info.seq[0] = seq >> 16; + info.seq[1] = seq >> 8; + info.seq[2] = seq; + + info.iv_index = BT_MESH_NET_IVI_TX; + + return create_friend_pdu(frnd, &info, sdu); +} + +static struct os_mbuf *encode_update(struct bt_mesh_friend *frnd, u8_t md) +{ + struct bt_mesh_ctl_friend_update *upd; + struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*upd)); + struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx); + struct os_mbuf *buf; + + __ASSERT_NO_MSG(sub != NULL); + + BT_DBG("lpn 0x%04x md 0x%02x", frnd->lpn, md); + + net_buf_simple_init(sdu, 1); + + upd = net_buf_simple_add(sdu, sizeof(*upd)); + upd->flags = bt_mesh_net_flags(sub); + upd->iv_index = sys_cpu_to_be32(bt_mesh.iv_index); + upd->md = md; + + buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_UPDATE, sdu); + + os_mbuf_free_chain(sdu); + return buf; +} + +static void enqueue_sub_cfm(struct bt_mesh_friend *frnd, u8_t xact) +{ + struct bt_mesh_ctl_friend_sub_confirm *cfm; + struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*cfm)); + struct os_mbuf *buf; + + BT_DBG("lpn 0x%04x xact 0x%02x", frnd->lpn, xact); + + net_buf_simple_init(sdu, 1); + + cfm = net_buf_simple_add(sdu, sizeof(*cfm)); + cfm->xact = xact; + + buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_SUB_CFM, sdu); + if (!buf) { + BT_ERR("Unable to encode Subscription List Confirmation"); + goto done; + } + + if (frnd->last) { + BT_DBG("Discarding last PDU"); + net_buf_unref(frnd->last); + } + + frnd->last = buf; + frnd->send_last = 1; + +done: + os_mbuf_free_chain(sdu); +} + +static void friend_recv_delay(struct bt_mesh_friend *frnd) +{ + frnd->pending_req = 1; + k_delayed_work_submit(&frnd->timer, recv_delay(frnd)); + BT_DBG("Waiting RecvDelay of %d ms", (int) recv_delay(frnd)); +} + +int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_friend *frnd; + u8_t xact; + + if (buf->om_len < BT_MESH_FRIEND_SUB_MIN_LEN) { + BT_WARN("Too short Friend Subscription Add"); + return -EINVAL; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, true); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); + return 0; + } + + if (frnd->pending_buf) { + BT_WARN("Previous buffer not yet sent!"); + return 0; + } + + friend_recv_delay(frnd); + + xact = net_buf_simple_pull_u8(buf); + + while (buf->om_len >= 2) { + friend_sub_add(frnd, net_buf_simple_pull_be16(buf)); + } + + enqueue_sub_cfm(frnd, xact); + + return 0; +} + +int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_friend *frnd; + u8_t xact; + + if (buf->om_len < BT_MESH_FRIEND_SUB_MIN_LEN) { + BT_WARN("Too short Friend Subscription Remove"); + return -EINVAL; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, true); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); + return 0; + } + + if (frnd->pending_buf) { + BT_WARN("Previous buffer not yet sent!"); + return 0; + } + + friend_recv_delay(frnd); + + xact = net_buf_simple_pull_u8(buf); + + while (buf->om_len >= 2) { + friend_sub_rem(frnd, net_buf_simple_pull_be16(buf)); + } + + enqueue_sub_cfm(frnd, xact); + + return 0; +} + +static void enqueue_buf(struct bt_mesh_friend *frnd, struct os_mbuf *buf) +{ + net_buf_slist_put(&frnd->queue, buf); + frnd->queue_size++; +} + +static void enqueue_update(struct bt_mesh_friend *frnd, u8_t md) +{ + struct os_mbuf *buf; + + buf = encode_update(frnd, md); + if (!buf) { + BT_ERR("Unable to encode Friend Update"); + return; + } + + frnd->sec_update = 0; + enqueue_buf(frnd, buf); +} + +int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_poll *msg = (void *)buf->om_data; + struct bt_mesh_friend *frnd; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Poll"); + return -EINVAL; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, false); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); + return 0; + } + + if (msg->fsn & ~1) { + BT_WARN("Prohibited (non-zero) padding bits"); + return -EINVAL; + } + + if (frnd->pending_buf) { + BT_WARN("Previous buffer not yet sent"); + return 0; + } + + BT_DBG("msg->fsn %u frnd->fsn %u", (msg->fsn & 1), frnd->fsn); + + friend_recv_delay(frnd); + + if (!frnd->established) { + BT_DBG("Friendship established with 0x%04x", frnd->lpn); + frnd->established = 1; + } + + if (msg->fsn == frnd->fsn && frnd->last) { + BT_DBG("Re-sending last PDU"); + frnd->send_last = 1; + } else { + if (frnd->last) { + net_buf_unref(frnd->last); + frnd->last = NULL; + } + + frnd->fsn = msg->fsn; + + if (net_buf_slist_is_empty(&frnd->queue)) { + enqueue_update(frnd, 0); + BT_DBG("Enqueued Friend Update to empty queue"); + } + } + + return 0; +} + +static struct bt_mesh_friend *find_clear(u16_t prev_friend) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->clear.frnd == prev_friend) { + return frnd; + } + } + + return NULL; +} + +static void friend_clear_sent(int err, void *user_data) +{ + struct bt_mesh_friend *frnd = user_data; + + k_delayed_work_submit(&frnd->clear.timer, + K_SECONDS(frnd->clear.repeat_sec)); + frnd->clear.repeat_sec *= 2; +} + +static const struct bt_mesh_send_cb clear_sent_cb = { + .end = friend_clear_sent, +}; + +static void send_friend_clear(struct bt_mesh_friend *frnd) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = frnd->net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = frnd->clear.frnd, + .send_ttl = BT_MESH_TTL_MAX, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = bt_mesh_net_transmit_get(), + }; + struct bt_mesh_ctl_friend_clear req = { + .lpn_addr = sys_cpu_to_be16(frnd->lpn), + .lpn_counter = sys_cpu_to_be16(frnd->lpn_counter), + }; + + BT_DBG(""); + + bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req, + sizeof(req), NULL, &clear_sent_cb, frnd); +} + +static void clear_timeout(struct ble_npl_event *work) +{ + struct bt_mesh_friend *frnd = ble_npl_event_get_arg(work); + u32_t duration; + + BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd); + + duration = k_uptime_get_32() - frnd->clear.start; + if (duration > 2 * frnd->poll_to) { + BT_DBG("Clear Procedure timer expired"); + frnd->clear.frnd = BT_MESH_ADDR_UNASSIGNED; + return; + } + + send_friend_clear(frnd); +} + +static void clear_procedure_start(struct bt_mesh_friend *frnd) +{ + BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd); + + frnd->clear.start = k_uptime_get_32() + (2 * frnd->poll_to); + frnd->clear.repeat_sec = 1; + + send_friend_clear(frnd); +} + +int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_clear_confirm *msg = (void *)buf->om_data; + struct bt_mesh_friend *frnd; + u16_t lpn_addr, lpn_counter; + + BT_DBG(""); + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Clear Confirm"); + return -EINVAL; + } + + frnd = find_clear(rx->ctx.addr); + if (!frnd) { + BT_WARN("No pending clear procedure for 0x%02x", rx->ctx.addr); + return 0; + } + + lpn_addr = sys_be16_to_cpu(msg->lpn_addr); + if (lpn_addr != frnd->lpn) { + BT_WARN("LPN address mismatch (0x%04x != 0x%04x)", + lpn_addr, frnd->lpn); + return 0; + } + + lpn_counter = sys_be16_to_cpu(msg->lpn_counter); + if (lpn_counter != frnd->lpn_counter) { + BT_WARN("LPN counter mismatch (0x%04x != 0x%04x)", + lpn_counter, frnd->lpn_counter); + return 0; + } + + k_delayed_work_cancel(&frnd->clear.timer); + frnd->clear.frnd = BT_MESH_ADDR_UNASSIGNED; + + return 0; +} + +static void enqueue_offer(struct bt_mesh_friend *frnd, s8_t rssi) +{ + struct bt_mesh_ctl_friend_offer *off; + struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*off)); + struct os_mbuf *buf; + + BT_DBG(""); + + net_buf_simple_init(sdu, 1); + + off = net_buf_simple_add(sdu, sizeof(*off)); + + off->recv_win = CONFIG_BT_MESH_FRIEND_RECV_WIN, + off->queue_size = CONFIG_BT_MESH_FRIEND_QUEUE_SIZE, + off->sub_list_size = ARRAY_SIZE(frnd->sub_list), + off->rssi = rssi, + off->frnd_counter = sys_cpu_to_be16(frnd->counter); + + buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_OFFER, sdu); + if (!buf) { + BT_ERR("Unable to encode Friend Offer"); + goto done; + } + + frnd->counter++; + + if (frnd->last) { + net_buf_unref(frnd->last); + } + + frnd->last = buf; + frnd->send_last = 1; + +done: + os_mbuf_free_chain(sdu); +} + +#define RECV_WIN CONFIG_BT_MESH_FRIEND_RECV_WIN +#define RSSI_FACT(crit) (((crit) >> 5) & (u8_t)BIT_MASK(2)) +#define RECV_WIN_FACT(crit) (((crit) >> 3) & (u8_t)BIT_MASK(2)) +#define MIN_QUEUE_SIZE_LOG(crit) ((crit) & (u8_t)BIT_MASK(3)) +#define MIN_QUEUE_SIZE(crit) ((u32_t)BIT(MIN_QUEUE_SIZE_LOG(crit))) + +static s32_t offer_delay(struct bt_mesh_friend *frnd, s8_t rssi, u8_t crit) +{ + /* Scaling factors. The actual values are 1, 1.5, 2 & 2.5, but we + * want to avoid floating-point arithmetic. + */ + static const u8_t fact[] = { 10, 15, 20, 25 }; + s32_t delay; + + BT_DBG("ReceiveWindowFactor %u ReceiveWindow %u RSSIFactor %u RSSI %d", + fact[RECV_WIN_FACT(crit)], RECV_WIN, + fact[RSSI_FACT(crit)], rssi); + + /* Delay = ReceiveWindowFactor * ReceiveWindow - RSSIFactor * RSSI */ + delay = (s32_t)fact[RECV_WIN_FACT(crit)] * RECV_WIN; + delay -= (s32_t)fact[RSSI_FACT(crit)] * rssi; + delay /= 10; + + BT_DBG("Local Delay calculated as %d ms", (int) delay); + + if (delay < 100) { + return K_MSEC(100); + } + + return K_MSEC(delay); +} + +int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_req *msg = (void *)buf->om_data; + struct bt_mesh_friend *frnd = NULL; + u32_t poll_to; + int i; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Request"); + return -EINVAL; + } + + if (msg->recv_delay <= 0x09) { + BT_WARN("Prohibited ReceiveDelay (0x%02x)", msg->recv_delay); + return -EINVAL; + } + + poll_to = (((u32_t)msg->poll_to[0] << 16) | + ((u32_t)msg->poll_to[1] << 8) | + ((u32_t)msg->poll_to[2])); + + if (poll_to <= 0x000009 || poll_to >= 0x34bc00) { + BT_WARN("Prohibited PollTimeout (0x%06x)", (unsigned) poll_to); + return -EINVAL; + } + + if (msg->num_elem == 0x00) { + BT_WARN("Prohibited NumElements value (0x00)"); + return -EINVAL; + } + + if (!BT_MESH_ADDR_IS_UNICAST(rx->ctx.addr + msg->num_elem - 1)) { + BT_WARN("LPN elements stretch outside of unicast range"); + return -EINVAL; + } + + if (!MIN_QUEUE_SIZE_LOG(msg->criteria)) { + BT_WARN("Prohibited Minimum Queue Size in Friend Request"); + return -EINVAL; + } + + if (CONFIG_BT_MESH_FRIEND_QUEUE_SIZE < MIN_QUEUE_SIZE(msg->criteria)) { + BT_WARN("We have a too small Friend Queue size (%u < %u)", + CONFIG_BT_MESH_FRIEND_QUEUE_SIZE, + (unsigned) MIN_QUEUE_SIZE(msg->criteria)); + return 0; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, false); + if (frnd) { + BT_WARN("Existing LPN re-requesting Friendship"); + friend_clear(frnd); + goto init_friend; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + if (!bt_mesh.frnd[i].valid) { + frnd = &bt_mesh.frnd[i]; + frnd->valid = 1; + break; + } + } + + if (!frnd) { + BT_WARN("No free Friend contexts for new LPN"); + return -ENOMEM; + } + +init_friend: + frnd->lpn = rx->ctx.addr; + frnd->num_elem = msg->num_elem; + frnd->net_idx = rx->sub->net_idx; + frnd->recv_delay = msg->recv_delay; + frnd->poll_to = poll_to * 100; + frnd->lpn_counter = sys_be16_to_cpu(msg->lpn_counter); + frnd->clear.frnd = sys_be16_to_cpu(msg->prev_addr); + + BT_DBG("LPN 0x%04x rssi %d recv_delay %u poll_to %ums", + frnd->lpn, rx->rssi, frnd->recv_delay, + (unsigned) frnd->poll_to); + + if (BT_MESH_ADDR_IS_UNICAST(frnd->clear.frnd) && + !bt_mesh_elem_find(frnd->clear.frnd)) { + clear_procedure_start(frnd); + } + + k_delayed_work_submit(&frnd->timer, + offer_delay(frnd, rx->rssi, msg->criteria)); + + friend_cred_create(rx->sub, frnd->lpn, frnd->lpn_counter, + frnd->counter); + + enqueue_offer(frnd, rx->rssi); + + return 0; +} + +static struct bt_mesh_friend_seg *get_seg(struct bt_mesh_friend *frnd, + u16_t src, u64_t *seq_auth) +{ + struct bt_mesh_friend_seg *unassigned = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[i]; + struct os_mbuf *buf = (void *)net_buf_slist_peek_head(&seg->queue); + + if (buf && BT_MESH_ADV(buf)->addr == src && + FRIEND_ADV(buf)->seq_auth == *seq_auth) { + return seg; + } + + if (!unassigned && !buf) { + unassigned = seg; + } + } + + return unassigned; +} + +static void enqueue_friend_pdu(struct bt_mesh_friend *frnd, + enum bt_mesh_friend_pdu_type type, + struct os_mbuf *buf) +{ + struct bt_mesh_friend_seg *seg; + struct friend_adv *adv; + + BT_DBG("type %u", type); + + if (type == BT_MESH_FRIEND_PDU_SINGLE) { + if (frnd->sec_update) { + enqueue_update(frnd, 1); + } + + enqueue_buf(frnd, buf); + return; + } + + adv = FRIEND_ADV(buf); + seg = get_seg(frnd, BT_MESH_ADV(buf)->addr, &adv->seq_auth); + if (!seg) { + BT_ERR("No free friend segment RX contexts for 0x%04x", + BT_MESH_ADV(buf)->addr); + net_buf_unref(buf); + return; + } + + net_buf_slist_put(&seg->queue, buf); + + if (type == BT_MESH_FRIEND_PDU_COMPLETE) { + if (frnd->sec_update) { + enqueue_update(frnd, 1); + } + + /* Only acks should have a valid SeqAuth in the Friend queue + * (otherwise we can't easily detect them there), so clear + * the SeqAuth information from the segments before merging. + */ + struct os_mbuf *m; + struct os_mbuf_pkthdr *pkthdr; + NET_BUF_SLIST_FOR_EACH_NODE(&seg->queue, pkthdr) { + m = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + FRIEND_ADV(m)->seq_auth = TRANS_SEQ_AUTH_NVAL; + frnd->queue_size++; + } + + net_buf_slist_merge_slist(&frnd->queue, &seg->queue); + } +} + +static void buf_send_start(u16_t duration, int err, void *user_data) +{ + struct bt_mesh_friend *frnd = user_data; + + BT_DBG("err %d", err); + + frnd->pending_buf = 0; + + /* Friend Offer doesn't follow the re-sending semantics */ + if (!frnd->established) { + net_buf_unref(frnd->last); + frnd->last = NULL; + } +} + +static void buf_send_end(int err, void *user_data) +{ + struct bt_mesh_friend *frnd = user_data; + + BT_DBG("err %d", err); + + if (frnd->pending_req) { + BT_WARN("Another request before previous completed sending"); + return; + } + + if (frnd->established) { + k_delayed_work_submit(&frnd->timer, frnd->poll_to); + BT_DBG("Waiting %u ms for next poll", + (unsigned) frnd->poll_to); + } else { + /* Friend offer timeout is 1 second */ + k_delayed_work_submit(&frnd->timer, K_SECONDS(1)); + BT_DBG("Waiting for first poll"); + } +} + +static void friend_timeout(struct ble_npl_event *work) +{ + struct bt_mesh_friend *frnd = ble_npl_event_get_arg(work); + static const struct bt_mesh_send_cb buf_sent_cb = { + .start = buf_send_start, + .end = buf_send_end, + }; + + __ASSERT_NO_MSG(frnd->pending_buf == 0); + + BT_DBG("lpn 0x%04x send_last %u last %p", frnd->lpn, + frnd->send_last, frnd->last); + + if (frnd->send_last && frnd->last) { + BT_DBG("Sending frnd->last %p", frnd->last); + frnd->send_last = 0; + goto send_last; + } + + if (frnd->established && !frnd->pending_req) { + BT_WARN("Friendship lost with 0x%04x", frnd->lpn); + friend_clear(frnd); + return; + } + + frnd->last = net_buf_slist_get(&frnd->queue); + if (!frnd->last) { + BT_WARN("Friendship not established with 0x%04x", frnd->lpn); + friend_clear(frnd); + return; + } + + BT_DBG("Sending buf %p from Friend Queue of LPN 0x%04x", + frnd->last, frnd->lpn); + frnd->queue_size--; + +send_last: + frnd->pending_req = 0; + frnd->pending_buf = 1; + bt_mesh_adv_send(frnd->last, &buf_sent_cb, frnd); +} + +int bt_mesh_friend_init(void) +{ + int rc; + int i; + + rc = os_mempool_init(&friend_buf_mempool, FRIEND_BUF_COUNT, + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE, + friend_buf_mem, "friend_buf_pool"); + assert(rc == 0); + + rc = os_mbuf_pool_init(&friend_os_mbuf_pool, &friend_buf_mempool, + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE, + FRIEND_BUF_COUNT); + assert(rc == 0); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + int j; + + frnd->net_idx = BT_MESH_KEY_UNUSED; + + net_buf_slist_init(&frnd->queue); + + k_delayed_work_init(&frnd->timer, friend_timeout); + k_delayed_work_add_arg(&frnd->timer, frnd); + k_delayed_work_init(&frnd->clear.timer, clear_timeout); + k_delayed_work_add_arg(&frnd->clear.timer, frnd); + + for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) { + net_buf_slist_init(&frnd->seg[j].queue); + } + } + + return 0; +} + +static void friend_purge_old_ack(struct bt_mesh_friend *frnd, u64_t *seq_auth, + u16_t src) +{ + struct os_mbuf *cur, *prev = NULL; + + BT_DBG("SeqAuth %llx src 0x%04x", *seq_auth, src); + + for (cur = net_buf_slist_peek_head(&frnd->queue); + cur != NULL; prev = cur, cur = net_buf_slist_peek_next(cur)) { + struct os_mbuf *buf = (void *)cur; + + if (BT_MESH_ADV(buf)->addr == src && + FRIEND_ADV(buf)->seq_auth == *seq_auth) { + BT_DBG("Removing old ack from Friend Queue"); + + net_buf_slist_remove(&frnd->queue, prev, cur); + frnd->queue_size--; + + net_buf_unref(buf); + break; + } + } +} + +static void friend_lpn_enqueue_rx(struct bt_mesh_friend *frnd, + struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct os_mbuf *sbuf) +{ + struct friend_pdu_info info; + struct os_mbuf *buf; + + BT_DBG("LPN 0x%04x queue_size %u", frnd->lpn, + (unsigned) frnd->queue_size); + + if (type == BT_MESH_FRIEND_PDU_SINGLE && seq_auth) { + friend_purge_old_ack(frnd, seq_auth, rx->ctx.addr); + } + + info.src = rx->ctx.addr; + info.dst = rx->ctx.recv_dst; + + if (rx->net_if == BT_MESH_NET_IF_LOCAL) { + info.ttl = rx->ctx.recv_ttl; + } else { + info.ttl = rx->ctx.recv_ttl - 1; + } + + info.ctl = rx->ctl; + + info.seq[0] = (rx->seq >> 16); + info.seq[1] = (rx->seq >> 8); + info.seq[2] = rx->seq; + + info.iv_index = BT_MESH_NET_IVI_RX(rx); + + buf = create_friend_pdu(frnd, &info, sbuf); + if (!buf) { + BT_ERR("Failed to encode Friend buffer"); + return; + } + + if (seq_auth) { + FRIEND_ADV(buf)->seq_auth = *seq_auth; + } + + enqueue_friend_pdu(frnd, type, buf); + + BT_DBG("Queued message for LPN 0x%04x, queue_size %u", + frnd->lpn, (unsigned) frnd->queue_size); +} + +static void friend_lpn_enqueue_tx(struct bt_mesh_friend *frnd, + struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct os_mbuf *sbuf) +{ + struct friend_pdu_info info; + struct os_mbuf *buf; + u32_t seq; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + if (type == BT_MESH_FRIEND_PDU_SINGLE && seq_auth) { + friend_purge_old_ack(frnd, seq_auth, tx->src); + } + + info.src = tx->src; + info.dst = tx->ctx->addr; + + info.ttl = tx->ctx->send_ttl; + info.ctl = (tx->ctx->app_idx == BT_MESH_KEY_UNUSED); + + seq = bt_mesh_next_seq(); + info.seq[0] = seq >> 16; + info.seq[1] = seq >> 8; + info.seq[2] = seq; + + info.iv_index = BT_MESH_NET_IVI_TX; + + buf = create_friend_pdu(frnd, &info, sbuf); + if (!buf) { + BT_ERR("Failed to encode Friend buffer"); + return; + } + + if (seq_auth) { + FRIEND_ADV(buf)->seq_auth = *seq_auth; + } + + enqueue_friend_pdu(frnd, type, buf); + + BT_DBG("Queued message for LPN 0x%04x", frnd->lpn); +} + +static bool friend_lpn_matches(struct bt_mesh_friend *frnd, u16_t net_idx, + u16_t addr) +{ + int i; + + if (!frnd->established) { + return false; + } + + if (net_idx != frnd->net_idx) { + return false; + } + + if (BT_MESH_ADDR_IS_UNICAST(addr)) { + return is_lpn_unicast(frnd, addr); + } + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == addr) { + return true; + } + } + + return false; +} + +bool bt_mesh_friend_match(u16_t net_idx, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (friend_lpn_matches(frnd, net_idx, addr)) { + BT_DBG("LPN 0x%04x matched address 0x%04x", + frnd->lpn, addr); + return true; + } + } + + BT_DBG("No matching LPN for address 0x%04x", addr); + + return false; +} + +void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct os_mbuf *sbuf) +{ + int i; + + if (!rx->friend_match || + (rx->ctx.recv_ttl <= 1 && rx->net_if != BT_MESH_NET_IF_LOCAL) || + bt_mesh_friend_get() != BT_MESH_FRIEND_ENABLED) { + return; + } + + BT_DBG("recv_ttl %u net_idx 0x%04x src 0x%04x dst 0x%04x", + rx->ctx.recv_ttl, rx->sub->net_idx, rx->ctx.addr, + rx->ctx.recv_dst); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (friend_lpn_matches(frnd, rx->sub->net_idx, + rx->ctx.recv_dst)) { + friend_lpn_enqueue_rx(frnd, rx, type, seq_auth, sbuf); + } + } +} + +bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct os_mbuf *sbuf) +{ + bool matched = false; + int i; + + if (!bt_mesh_friend_match(tx->sub->net_idx, tx->ctx->addr) || + bt_mesh_friend_get() != BT_MESH_FRIEND_ENABLED) { + return matched; + } + + BT_DBG("net_idx 0x%04x dst 0x%04x src 0x%04x", tx->sub->net_idx, + tx->ctx->addr, tx->src); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (friend_lpn_matches(frnd, tx->sub->net_idx, tx->ctx->addr)) { + friend_lpn_enqueue_tx(frnd, tx, type, seq_auth, sbuf); + matched = true; + } + } + + return matched; +} + +void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src, + u16_t dst, u64_t *seq_auth) +{ + int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + int j; + + if (!friend_lpn_matches(frnd, sub->net_idx, dst)) { + continue; + } + + for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[j]; + struct os_mbuf *buf; + + buf = (void *)net_buf_slist_peek_head(&seg->queue); + if (!buf) { + continue; + } + + if (BT_MESH_ADV(buf)->addr != src) { + continue; + } + + if (FRIEND_ADV(buf)->seq_auth != *seq_auth) { + continue; + } + + BT_WARN("Clearing incomplete segments for 0x%04x", src); + + while (!net_buf_slist_is_empty(&seg->queue)) { + net_buf_unref(net_buf_slist_get(&seg->queue)); + } + } + } +} + +#endif /* MYNEWT_VAL(BLE_MESH_FRIEND) */ diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.h new file mode 100644 index 000000000..053de146c --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.h @@ -0,0 +1,51 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FRIEND_H__ +#define __FRIEND_H__ + +#include "mesh/mesh.h" + +enum bt_mesh_friend_pdu_type { + BT_MESH_FRIEND_PDU_SINGLE, + BT_MESH_FRIEND_PDU_PARTIAL, + BT_MESH_FRIEND_PDU_COMPLETE, +}; + +bool bt_mesh_friend_match(u16_t net_idx, u16_t addr); + +struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr, + bool valid, bool established); + +void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct os_mbuf *sbuf); +bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct os_mbuf *sbuf); + +void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src, + u16_t dst, u64_t *seq_auth); + +void bt_mesh_friend_sec_update(u16_t net_idx); + +void bt_mesh_friend_clear_net_idx(u16_t net_idx); + +int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct os_mbuf *buf); +int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct os_mbuf *buf); +int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct os_mbuf *buf); +int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); +int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); +int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); + +int bt_mesh_friend_init(void); + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/glue.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/glue.c new file mode 100644 index 000000000..3b765ee65 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/glue.c @@ -0,0 +1,904 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "mesh/glue.h" +#include "adv.h" +#ifndef MYNEWT +#include "nimble/nimble_port.h" +#endif + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) +#include "base64/base64.h" +#endif + +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG)) + +#if MYNEWT_VAL(BLE_EXT_ADV) +#define BT_MESH_ADV_INST (MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES)) + +#if MYNEWT_VAL(BLE_MESH_PROXY) +/* Note that BLE_MULTI_ADV_INSTANCES contains number of additional instances. + * Instance 0 is always there + */ +#if MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES) < 1 +#error "Mesh needs at least BLE_MULTI_ADV_INSTANCES set to 1" +#endif +#define BT_MESH_ADV_GATT_INST (MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES) - 1) +#endif /* BLE_MESH_PROXY */ +#endif /* BLE_EXT_ADV */ + +extern u8_t g_mesh_addr_type; + +#if MYNEWT_VAL(BLE_EXT_ADV) +/* Store configuration for different bearers */ +#define BT_MESH_ADV_IDX (0) +#define BT_MESH_GATT_IDX (1) +static struct ble_gap_adv_params ble_adv_cur_conf[2]; +#endif + +const char * +bt_hex(const void *buf, size_t len) +{ + static const char hex[] = "0123456789abcdef"; + static char hexbufs[4][137]; + static u8_t curbuf; + const u8_t *b = buf; + char *str; + int i; + + str = hexbufs[curbuf++]; + curbuf %= ARRAY_SIZE(hexbufs); + + len = min(len, (sizeof(hexbufs[0]) - 1) / 2); + + for (i = 0; i < len; i++) { + str[i * 2] = hex[b[i] >> 4]; + str[i * 2 + 1] = hex[b[i] & 0xf]; + } + + str[i * 2] = '\0'; + + return str; +} + +void +net_buf_put(struct ble_npl_eventq *fifo, struct os_mbuf *om) +{ + struct ble_npl_event *ev; + + assert(OS_MBUF_IS_PKTHDR(om)); + ev = &BT_MESH_ADV(om)->ev; + assert(ev); + assert(ble_npl_event_get_arg(ev)); + + ble_npl_eventq_put(fifo, ev); +} + +void * +net_buf_ref(struct os_mbuf *om) +{ + struct bt_mesh_adv *adv; + + /* For bufs with header we count refs*/ + if (OS_MBUF_USRHDR_LEN(om) == 0) { + return om; + } + + adv = BT_MESH_ADV(om); + adv->ref_cnt++; + + return om; +} + +void +net_buf_unref(struct os_mbuf *om) +{ + struct bt_mesh_adv *adv; + + /* For bufs with header we count refs*/ + if (OS_MBUF_USRHDR_LEN(om) == 0) { + goto free; + } + + adv = BT_MESH_ADV(om); + if (--adv->ref_cnt > 0) { + return; + } + +free: + os_mbuf_free_chain(om); +} + +#if MYNEWT_VAL(BLE_CRYPTO_STACK_MBEDTLS) +int +bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data) +{ + mbedtls_aes_context s = {0}; + mbedtls_aes_init(&s); + + if (mbedtls_aes_setkey_enc(&s, key, 128) != 0) { + return BLE_HS_EUNKNOWN; + } + + if (mbedtls_aes_crypt_ecb(&s, MBEDTLS_AES_ENCRYPT, plaintext, enc_data) != 0) { + return BLE_HS_EUNKNOWN; + } + + return 0; +} + +#else +int +bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data) +{ + struct tc_aes_key_sched_struct s = {0}; + + if (tc_aes128_set_encrypt_key(&s, key) == TC_CRYPTO_FAIL) { + return BLE_HS_EUNKNOWN; + } + + if (tc_aes_encrypt(enc_data, plaintext, &s) == TC_CRYPTO_FAIL) { + return BLE_HS_EUNKNOWN; + } + + return 0; +} +#endif + +uint16_t +net_buf_simple_pull_le16(struct os_mbuf *om) +{ + uint16_t val; + struct os_mbuf *old = om; + + om = os_mbuf_pullup(om, sizeof(val)); + assert(om == old); + val = get_le16(om->om_data); + os_mbuf_adj(om, sizeof(val)); + + return val; +} + +uint16_t +net_buf_simple_pull_be16(struct os_mbuf *om) +{ + uint16_t val; + struct os_mbuf *old = om; + + om = os_mbuf_pullup(om, sizeof(val)); + assert(om == old); + val = get_be16(om->om_data); + os_mbuf_adj(om, sizeof(val)); + + return val; +} + +uint32_t +net_buf_simple_pull_be32(struct os_mbuf *om) +{ + uint32_t val; + struct os_mbuf *old = om; + + om = os_mbuf_pullup(om, sizeof(val)); + assert(om == old); + val = get_be32(om->om_data); + os_mbuf_adj(om, sizeof(val)); + + return val; +} + +uint32_t +net_buf_simple_pull_le32(struct os_mbuf *om) +{ + uint32_t val; + struct os_mbuf *old = om; + + om = os_mbuf_pullup(om, sizeof(val)); + assert(om == old); + val = get_le32(om->om_data); + os_mbuf_adj(om, sizeof(val)); + + return val; +} + +uint8_t +net_buf_simple_pull_u8(struct os_mbuf *om) +{ + uint8_t val; + struct os_mbuf *old = om; + + om = os_mbuf_pullup(om, sizeof(val)); + assert(om == old); + val = om->om_data[0]; + os_mbuf_adj(om, 1); + + return val; +} + +void +net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val) +{ + val = htole16(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val) +{ + val = htobe16(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val) +{ + val = htobe32(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_le32(struct os_mbuf *om, uint32_t val) +{ + val = htole32(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val) +{ + os_mbuf_append(om, &val, 1); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_push_le16(struct os_mbuf *om, uint16_t val) +{ + uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len]; + + assert(headroom >= 2); + om->om_data -= 2; + put_le16(om->om_data, val); + om->om_len += 2; + + if (om->om_pkthdr_len) { + OS_MBUF_PKTHDR(om)->omp_len += 2; + } + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_push_be16(struct os_mbuf *om, uint16_t val) +{ + uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len]; + + assert(headroom >= 2); + om->om_data -= 2; + put_be16(om->om_data, val); + om->om_len += 2; + + if (om->om_pkthdr_len) { + OS_MBUF_PKTHDR(om)->omp_len += 2; + } + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_push_u8(struct os_mbuf *om, uint8_t val) +{ + uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len]; + + assert(headroom >= 1); + om->om_data -= 1; + om->om_data[0] = val; + om->om_len += 1; + + if (om->om_pkthdr_len) { + OS_MBUF_PKTHDR(om)->omp_len += 1; + } + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_add_zeros(struct os_mbuf *om, uint8_t len) +{ + uint8_t z[len]; + int rc; + + memset(z, 0, len); + + rc = os_mbuf_append(om, z, len); + if(rc) { + assert(0); + } + ASSERT_NOT_CHAIN(om); +} + +void * +net_buf_simple_pull(struct os_mbuf *om, uint8_t len) +{ + os_mbuf_adj(om, len); + return om->om_data; +} + +void * +net_buf_simple_pull_mem(struct os_mbuf *om, uint8_t len) +{ + void *data = om->om_data; + + net_buf_simple_pull(om, len); + return data; +} + +void* +net_buf_simple_add(struct os_mbuf *om, uint8_t len) +{ + void * tmp; + + tmp = os_mbuf_extend(om, len); + ASSERT_NOT_CHAIN(om); + + return tmp; +} + +bool +k_fifo_is_empty(struct ble_npl_eventq *q) +{ + return ble_npl_eventq_is_empty(q); +} + +void * net_buf_get(struct ble_npl_eventq *fifo, s32_t t) +{ + struct ble_npl_event *ev = ble_npl_eventq_get(fifo, 0); + + if (ev) { + return ble_npl_event_get_arg(ev); + } + + return NULL; +} + +uint8_t * +net_buf_simple_push(struct os_mbuf *om, uint8_t len) +{ + uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len]; + + assert(headroom >= len); + om->om_data -= len; + om->om_len += len; + + return om->om_data; +} + +void +net_buf_reserve(struct os_mbuf *om, size_t reserve) +{ + /* We need reserve to be done on fresh buf */ + assert(om->om_len == 0); + om->om_data += reserve; +} + +void +k_work_init(struct ble_npl_callout *work, ble_npl_event_fn handler) +{ +#ifndef MYNEWT + ble_npl_callout_init(work, nimble_port_get_dflt_eventq(), handler, NULL); +#else + ble_npl_callout_init(work, ble_npl_eventq_dflt_get(), handler, NULL); +#endif +} + +void +k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f) +{ +#ifndef MYNEWT + ble_npl_callout_init(&w->work, nimble_port_get_dflt_eventq(), f, NULL); +#else + ble_npl_callout_init(&w->work, ble_npl_eventq_dflt_get(), f, NULL); +#endif +} + +void +k_delayed_work_cancel(struct k_delayed_work *w) +{ + ble_npl_callout_stop(&w->work); +} + +void +k_delayed_work_submit(struct k_delayed_work *w, uint32_t ms) +{ + uint32_t ticks; + + if (ble_npl_time_ms_to_ticks(ms, &ticks) != 0) { + assert(0); + } + ble_npl_callout_reset(&w->work, ticks); +} + +void +k_work_submit(struct ble_npl_callout *w) +{ + ble_npl_callout_reset(w, 0); +} + +void +k_work_add_arg(struct ble_npl_callout *w, void *arg) +{ + ble_npl_callout_set_arg(w, arg); +} + +void +k_delayed_work_add_arg(struct k_delayed_work *w, void *arg) +{ + k_work_add_arg(&w->work, arg); +} + +uint32_t +k_delayed_work_remaining_get (struct k_delayed_work *w) +{ + int sr; + ble_npl_time_t t; + + OS_ENTER_CRITICAL(sr); + + t = ble_npl_callout_remaining_ticks(&w->work, ble_npl_time_get()); + + OS_EXIT_CRITICAL(sr); + + return ble_npl_time_ticks_to_ms32(t); +} + +int64_t k_uptime_get(void) +{ + /* We should return ms */ + return ble_npl_time_ticks_to_ms32(ble_npl_time_get()); +} + +u32_t k_uptime_get_32(void) +{ + return k_uptime_get(); +} + +void k_sleep(int32_t duration) +{ + uint32_t ticks; + + ticks = ble_npl_time_ms_to_ticks32(duration); + + ble_npl_time_delay(ticks); +} + +static uint8_t pub[64]; +static uint8_t priv[32]; +static bool has_pub = false; + +int +bt_dh_key_gen(const u8_t remote_pk[64], bt_dh_key_cb_t cb) +{ + uint8_t dh[32]; + + if (ble_sm_alg_gen_dhkey((uint8_t *)&remote_pk[0], (uint8_t *)&remote_pk[32], + priv, dh)) { + return -1; + } + + cb(dh); + return 0; +} + +int +bt_rand(void *buf, size_t len) +{ + int rc; + rc = ble_hs_hci_util_rand(buf, len); + if (rc != 0) { + return -1; + } + + return 0; +} + +int +bt_pub_key_gen(struct bt_pub_key_cb *new_cb) +{ + + if (ble_sm_alg_gen_key_pair(pub, priv)) { + assert(0); + return -1; + } + + new_cb->func(pub); + has_pub = true; + + return 0; +} + +uint8_t * +bt_pub_key_get(void) +{ + if (!has_pub) { + return NULL; + } + + return pub; +} + +static int +set_ad(const struct bt_data *ad, size_t ad_len, u8_t *buf, u8_t *buf_len) +{ + int i; + + for (i = 0; i < ad_len; i++) { + buf[(*buf_len)++] = ad[i].data_len + 1; + buf[(*buf_len)++] = ad[i].type; + + memcpy(&buf[*buf_len], ad[i].data, + ad[i].data_len); + *buf_len += ad[i].data_len; + } + + return 0; +} + +#if MYNEWT_VAL(BLE_EXT_ADV) +static void +ble_adv_copy_to_ext_param(struct ble_gap_ext_adv_params *ext_param, + const struct ble_gap_adv_params *param) +{ + memset(ext_param, 0, sizeof(*ext_param)); + + ext_param->legacy_pdu = 1; + + if (param->conn_mode != BLE_GAP_CONN_MODE_NON) { + ext_param->connectable = 1; + ext_param->scannable = 1; + } + + ext_param->itvl_max = param->itvl_max; + ext_param->itvl_min = param->itvl_min; + ext_param->channel_map = param->channel_map; + ext_param->high_duty_directed = param->high_duty_cycle; + ext_param->own_addr_type = g_mesh_addr_type; +} + +static int +ble_adv_conf_adv_instance(const struct ble_gap_adv_params *param, int *instance) +{ + struct ble_gap_ext_adv_params ext_params; + struct ble_gap_adv_params *cur_conf; + int err = 0; + + if (param->conn_mode == BLE_GAP_CONN_MODE_NON) { + *instance = BT_MESH_ADV_INST; + cur_conf = &ble_adv_cur_conf[BT_MESH_ADV_IDX]; + } else { +#if MYNEWT_VAL(BLE_MESH_PROXY) + *instance = BT_MESH_ADV_GATT_INST; + cur_conf = &ble_adv_cur_conf[BT_MESH_GATT_IDX]; +#else + assert(0); +#endif + } + + /* Checking interval max as it has to be in place if instance was configured + * before. + */ + if (cur_conf->itvl_max == 0) { + goto configure; + } + + if (memcmp(param, cur_conf, sizeof(*cur_conf)) == 0) { + /* Same parameters - skip reconfiguring */ + goto done; + } + + ble_gap_ext_adv_stop(*instance); + err = ble_gap_ext_adv_remove(*instance); + if (err) { + assert(0); + goto done; + } + +configure: + ble_adv_copy_to_ext_param(&ext_params, param); + + err = ble_gap_ext_adv_configure(*instance, &ext_params, 0, + ble_adv_gap_mesh_cb, NULL); + if (!err) { + memcpy(cur_conf, param, sizeof(*cur_conf)); + } + +done: + return err; +} + +int +bt_le_adv_start(const struct ble_gap_adv_params *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len) +{ + struct os_mbuf *data; + int instance; + int err; + uint8_t buf[BLE_HS_ADV_MAX_SZ]; + uint8_t buf_len = 0; + + err = ble_adv_conf_adv_instance(param, &instance); + if (err) { + return err; + } + + if (ad_len > 0) { + err = set_ad(ad, ad_len, buf, &buf_len); + if (err) { + return err; + } + + /* For now let's use msys pool. We are not putting more then legacy */ + data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_SZ, 0); + if (!data) { + return OS_ENOMEM; + } + + err = os_mbuf_append(data, buf, buf_len); + if (err) { + goto error; + } + + err = ble_gap_ext_adv_set_data(instance, data); + if (err) { + return err; + } + + data = NULL; + } + + if (sd_len > 0) { + buf_len = 0; + + err = set_ad(sd, sd_len, buf, &buf_len); + if (err) { + return err; + } + + /* For now let's use msys pool. We are not putting more then legace*/ + data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_SZ, 0); + if (!data) { + return OS_ENOMEM; + } + + err = os_mbuf_append(data, buf, buf_len); + if (err) { + goto error; + } + + err = ble_gap_ext_adv_rsp_set_data(instance, data); + if (err) { + goto error; + } + } + + /*TODO: We could use duration and max events in the future */ + err = ble_gap_ext_adv_start(instance, 0, 0); + return err; + +error: + if (data) { + os_mbuf_free_chain(data); + } + + return err; +} + +int bt_le_adv_stop(bool proxy) +{ +#if MYNEWT_VAL(BLE_MESH_PROXY) + int rc; + + if (proxy) { + rc = ble_gap_ext_adv_stop(BT_MESH_ADV_GATT_INST); + } else { + rc = ble_gap_ext_adv_stop(BT_MESH_ADV_INST); + } + + return rc; +#else + return ble_gap_ext_adv_stop(BT_MESH_ADV_INST); +#endif +} + +#else + +int +bt_le_adv_start(const struct ble_gap_adv_params *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len) +{ + uint8_t buf[BLE_HS_ADV_MAX_SZ]; + uint8_t buf_len = 0; + int err; + + err = set_ad(ad, ad_len, buf, &buf_len); + if (err) { + return err; + } + + err = ble_gap_adv_set_data(buf, buf_len); + if (err != 0) { + return err; + } + + if (sd) { + buf_len = 0; + + err = set_ad(sd, sd_len, buf, &buf_len); + if (err) { + BT_ERR("Advertising failed: err %d", err); + return err; + } + + err = ble_gap_adv_rsp_set_data(buf, buf_len); + if (err != 0) { + BT_ERR("Advertising failed: err %d", err); + return err; + } + } + + err = ble_gap_adv_start(g_mesh_addr_type, NULL, BLE_HS_FOREVER, param, + NULL, NULL); + if (err) { + BT_ERR("Advertising failed: err %d", err); + return err; + } + + return 0; +} + +int bt_le_adv_stop(bool proxy) +{ + return ble_gap_adv_stop(); +} + +#endif + +#if MYNEWT_VAL(BLE_MESH_PROXY) +int bt_mesh_proxy_svcs_register(void); +#endif + +void +bt_mesh_register_gatt(void) +{ +#if MYNEWT_VAL(BLE_MESH_PROXY) + bt_mesh_proxy_svcs_register(); +#endif +} + +void net_buf_slist_init(struct net_buf_slist_t *list) +{ + STAILQ_INIT(list); +} + +bool net_buf_slist_is_empty(struct net_buf_slist_t *list) +{ + return STAILQ_EMPTY(list); +} + +struct os_mbuf *net_buf_slist_peek_head(struct net_buf_slist_t *list) +{ + struct os_mbuf_pkthdr *pkthdr; + + /* Get mbuf pointer from packet header pointer */ + pkthdr = STAILQ_FIRST(list); + if (!pkthdr) { + return NULL; + } + + return OS_MBUF_PKTHDR_TO_MBUF(pkthdr); +} + +struct os_mbuf *net_buf_slist_peek_next(struct os_mbuf *buf) +{ + struct os_mbuf_pkthdr *pkthdr; + + /* Get mbuf pointer from packet header pointer */ + pkthdr = OS_MBUF_PKTHDR(buf); + pkthdr = STAILQ_NEXT(pkthdr, omp_next); + if (!pkthdr) { + return NULL; + } + + return OS_MBUF_PKTHDR_TO_MBUF(pkthdr); +} + +struct os_mbuf *net_buf_slist_get(struct net_buf_slist_t *list) +{ + os_sr_t sr; + struct os_mbuf *m; + + m = net_buf_slist_peek_head(list); + if (!m) { + return NULL; + } + + /* Remove from queue */ + OS_ENTER_CRITICAL(sr); + STAILQ_REMOVE_HEAD(list, omp_next); + OS_EXIT_CRITICAL(sr); + return m; +} + +void net_buf_slist_put(struct net_buf_slist_t *list, struct os_mbuf *buf) +{ + struct os_mbuf_pkthdr *pkthdr; + + pkthdr = OS_MBUF_PKTHDR(buf); + STAILQ_INSERT_TAIL(list, pkthdr, omp_next); +} + +void net_buf_slist_remove(struct net_buf_slist_t *list, struct os_mbuf *prev, + struct os_mbuf *cur) +{ + struct os_mbuf_pkthdr *pkthdr, *cur_pkthdr; + + cur_pkthdr = OS_MBUF_PKTHDR(cur); + + STAILQ_FOREACH(pkthdr, list, omp_next) { + if (cur_pkthdr == pkthdr) { + STAILQ_REMOVE(list, cur_pkthdr, os_mbuf_pkthdr, omp_next); + break; + } + } +} + +void net_buf_slist_merge_slist(struct net_buf_slist_t *list, + struct net_buf_slist_t *list_to_append) +{ + struct os_mbuf_pkthdr *pkthdr; + + STAILQ_FOREACH(pkthdr, list_to_append, omp_next) { + STAILQ_INSERT_TAIL(list, pkthdr, omp_next); + } + + STAILQ_INIT(list); +} + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) + +int settings_bytes_from_str(char *val_str, void *vp, int *len) +{ + *len = base64_decode(val_str, vp); + return 0; +} + +char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len) +{ + if (BASE64_ENCODE_SIZE(vp_len) > buf_len) { + return NULL; + } + + base64_encode(vp, vp_len, buf, 1); + + return buf; +} + +#endif /* MYNEWT_VAL(BLE_MESH_SETTINGS) */ + diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_cli.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_cli.c new file mode 100644 index 000000000..68543415c --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_cli.c @@ -0,0 +1,553 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "syscfg/syscfg.h" +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_MODEL)) +#include "host/ble_hs_log.h" + +#include "mesh/mesh.h" +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "transport.h" +#include "foundation.h" +#include "mesh/health_cli.h" + +static s32_t msg_timeout = K_SECONDS(5); + +static struct bt_mesh_health_cli *health_cli; + +struct health_fault_param { + u16_t cid; + u8_t *expect_test_id; + u8_t *test_id; + u8_t *faults; + size_t *fault_count; +}; + +static void health_fault_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct health_fault_param *param; + u8_t test_id; + u16_t cid; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (health_cli->op_pending != OP_HEALTH_FAULT_STATUS) { + BT_WARN("Unexpected Health Fault Status message"); + return; + } + + param = health_cli->op_param; + + test_id = net_buf_simple_pull_u8(buf); + if (param->expect_test_id && test_id != *param->expect_test_id) { + BT_WARN("Health fault with unexpected Test ID"); + return; + } + + cid = net_buf_simple_pull_le16(buf); + if (cid != param->cid) { + BT_WARN("Health fault with unexpected Company ID"); + return; + } + + if (param->test_id) { + *param->test_id = test_id; + } + + if (buf->om_len > *param->fault_count) { + BT_WARN("Got more faults than there's space for"); + } else { + *param->fault_count = buf->om_len; + } + + memcpy(param->faults, buf->om_data, *param->fault_count); + + k_sem_give(&health_cli->op_sync); +} + +static void health_current_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_health_cli *cli = model->user_data; + u8_t test_id; + u16_t cid; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + test_id = net_buf_simple_pull_u8(buf); + cid = net_buf_simple_pull_le16(buf); + + BT_DBG("Test ID 0x%02x Company ID 0x%04x Fault Count %u", + test_id, cid, buf->om_len); + + if (!cli->current_status) { + BT_WARN("No Current Status callback available"); + return; + } + + cli->current_status(cli, ctx->addr, test_id, cid, buf->om_data, buf->om_len); +} + +struct health_period_param { + u8_t *divisor; +}; + +static void health_period_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct health_period_param *param; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (health_cli->op_pending != OP_HEALTH_PERIOD_STATUS) { + BT_WARN("Unexpected Health Period Status message"); + return; + } + + param = health_cli->op_param; + + *param->divisor = net_buf_simple_pull_u8(buf); + + k_sem_give(&health_cli->op_sync); +} + +struct health_attention_param { + u8_t *attention; +}; + +static void health_attention_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct health_attention_param *param; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (health_cli->op_pending != OP_ATTENTION_STATUS) { + BT_WARN("Unexpected Health Attention Status message"); + return; + } + + param = health_cli->op_param; + + if (param->attention) { + *param->attention = net_buf_simple_pull_u8(buf); + } + + k_sem_give(&health_cli->op_sync); +} + +const struct bt_mesh_model_op bt_mesh_health_cli_op[] = { + { OP_HEALTH_FAULT_STATUS, 3, health_fault_status }, + { OP_HEALTH_CURRENT_STATUS, 3, health_current_status }, + { OP_HEALTH_PERIOD_STATUS, 1, health_period_status }, + { OP_ATTENTION_STATUS, 1, health_attention_status }, + BT_MESH_MODEL_OP_END, +}; + +static int cli_prepare(void *param, u32_t op) +{ + if (!health_cli) { + BT_ERR("No available Health Client context!"); + return -EINVAL; + } + + if (health_cli->op_pending) { + BT_WARN("Another synchronous operation pending"); + return -EBUSY; + } + + health_cli->op_param = param; + health_cli->op_pending = op; + + return 0; +} + +static void cli_reset(void) +{ + health_cli->op_pending = 0; + health_cli->op_param = NULL; +} + +static int cli_wait(void) +{ + int err; + + err = k_sem_take(&health_cli->op_sync, msg_timeout); + + cli_reset(); + + return err; +} + +int bt_mesh_health_attention_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *attention) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_attention_param param = { + .attention = attention, + }; + int err; + + err = cli_prepare(¶m, OP_ATTENTION_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_ATTENTION_GET); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_attention_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t attention, u8_t *updated_attention) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_attention_param param = { + .attention = updated_attention, + }; + int err; + + err = cli_prepare(¶m, OP_ATTENTION_STATUS); + if (err) { + goto done; + } + + if (updated_attention) { + bt_mesh_model_msg_init(msg, OP_ATTENTION_SET); + } else { + bt_mesh_model_msg_init(msg, OP_ATTENTION_SET_UNREL); + } + + net_buf_simple_add_u8(msg, attention); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!updated_attention) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_period_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *divisor) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_period_param param = { + .divisor = divisor, + }; + int err; + + err = cli_prepare(¶m, OP_HEALTH_PERIOD_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_GET); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_period_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t divisor, u8_t *updated_divisor) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_period_param param = { + .divisor = updated_divisor, + }; + int err; + + err = cli_prepare(¶m, OP_HEALTH_PERIOD_STATUS); + if (err) { + goto done; + } + + if (updated_divisor) { + bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_SET); + } else { + bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_SET_UNREL); + } + + net_buf_simple_add_u8(msg, divisor); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!updated_divisor) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_fault_test(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t test_id, u8_t *faults, + size_t *fault_count) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 3 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_fault_param param = { + .cid = cid, + .expect_test_id = &test_id, + .faults = faults, + .fault_count = fault_count, + }; + int err; + + err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS); + if (err) { + goto done; + } + + if (faults) { + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_TEST); + } else { + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_TEST_UNREL); + } + + net_buf_simple_add_u8(msg, test_id); + net_buf_simple_add_le16(msg, cid); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!faults) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_fault_clear(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t *test_id, u8_t *faults, + size_t *fault_count) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_fault_param param = { + .cid = cid, + .test_id = test_id, + .faults = faults, + .fault_count = fault_count, + }; + int err; + + err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS); + if (err) { + goto done; + } + + if (test_id) { + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_CLEAR); + } else { + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_CLEAR_UNREL); + } + + net_buf_simple_add_le16(msg, cid); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!test_id) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_fault_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t *test_id, u8_t *faults, + size_t *fault_count) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_fault_param param = { + .cid = cid, + .test_id = test_id, + .faults = faults, + .fault_count = fault_count, + }; + int err; + + err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_GET); + net_buf_simple_add_le16(msg, cid); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +s32_t bt_mesh_health_cli_timeout_get(void) +{ + return msg_timeout; +} + +void bt_mesh_health_cli_timeout_set(s32_t timeout) +{ + msg_timeout = timeout; +} + +int bt_mesh_health_cli_set(struct bt_mesh_model *model) +{ + if (!model->user_data) { + BT_ERR("No Health Client context for given model"); + return -EINVAL; + } + + health_cli = model->user_data; + + return 0; +} + +int bt_mesh_health_cli_init(struct bt_mesh_model *model, bool primary) +{ + struct bt_mesh_health_cli *cli = model->user_data; + + BT_DBG("primary %u", primary); + + if (!cli) { + BT_ERR("No Health Client context provided"); + return -EINVAL; + } + + cli = model->user_data; + cli->model = model; + + k_sem_init(&cli->op_sync, 0, 1); + + /* Set the default health client pointer */ + if (!health_cli) { + health_cli = cli; + } + + return 0; +} diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_srv.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_srv.c new file mode 100644 index 000000000..97e3e043b --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_srv.c @@ -0,0 +1,441 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "syscfg/syscfg.h" +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_MODEL)) +#include "host/ble_hs_log.h" + +#include "mesh/mesh.h" +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" + +#define HEALTH_TEST_STANDARD 0x00 + +/* Health Server context of the primary element */ +struct bt_mesh_health_srv *health_srv; + +static void health_get_registered(struct bt_mesh_model *mod, + u16_t company_id, + struct os_mbuf *msg) +{ + struct bt_mesh_health_srv *srv = mod->user_data; + u8_t *test_id; + + BT_DBG("Company ID 0x%04x", company_id); + + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_STATUS); + + test_id = net_buf_simple_add(msg, 1); + net_buf_simple_add_le16(msg, company_id); + + if (srv->cb && srv->cb->fault_get_reg) { + u8_t fault_count = net_buf_simple_tailroom(msg) - 4; + int err; + + err = srv->cb->fault_get_reg(mod, company_id, test_id, + net_buf_simple_tail(msg), + &fault_count); + if (err) { + BT_ERR("Failed to get faults (err %d)", err); + *test_id = HEALTH_TEST_STANDARD; + } else { + net_buf_simple_add(msg, fault_count); + } + } else { + BT_WARN("No callback for getting faults"); + *test_id = HEALTH_TEST_STANDARD; + } +} + +static size_t health_get_current(struct bt_mesh_model *mod, + struct os_mbuf *msg) +{ + struct bt_mesh_health_srv *srv = mod->user_data; + const struct bt_mesh_comp *comp; + u8_t *test_id, *company_ptr; + u16_t company_id; + u8_t fault_count; + int err; + + bt_mesh_model_msg_init(msg, OP_HEALTH_CURRENT_STATUS); + + test_id = net_buf_simple_add(msg, 1); + company_ptr = net_buf_simple_add(msg, sizeof(company_id)); + comp = bt_mesh_comp_get(); + + if (srv->cb && srv->cb->fault_get_cur) { + fault_count = net_buf_simple_tailroom(msg); + err = srv->cb->fault_get_cur(mod, test_id, &company_id, + net_buf_simple_tail(msg), + &fault_count); + if (err) { + BT_ERR("Failed to get faults (err %d)", err); + sys_put_le16(comp->cid, company_ptr); + *test_id = HEALTH_TEST_STANDARD; + fault_count = 0; + } else { + sys_put_le16(company_id, company_ptr); + net_buf_simple_add(msg, fault_count); + } + } else { + BT_WARN("No callback for getting faults"); + sys_put_le16(comp->cid, company_ptr); + *test_id = HEALTH_TEST_STANDARD; + fault_count = 0; + } + + return fault_count; +} + +static void health_fault_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + u16_t company_id; + + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("company_id 0x%04x", company_id); + + health_get_registered(model, company_id, sdu); + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("Unable to send Health Current Status response"); + } + + os_mbuf_free_chain(sdu); +} + +static void health_fault_clear_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_health_srv *srv = model->user_data; + u16_t company_id; + + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("company_id 0x%04x", company_id); + + if (srv->cb && srv->cb->fault_clear) { + srv->cb->fault_clear(model, company_id); + } +} + +static void health_fault_clear(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + struct bt_mesh_health_srv *srv = model->user_data; + u16_t company_id; + + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("company_id 0x%04x", company_id); + + if (srv->cb && srv->cb->fault_clear) { + srv->cb->fault_clear(model, company_id); + } + + health_get_registered(model, company_id, sdu); + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("Unable to send Health Current Status response"); + } + + os_mbuf_free_chain(sdu); +} + +static void health_fault_test_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_health_srv *srv = model->user_data; + u16_t company_id; + u8_t test_id; + + test_id = net_buf_simple_pull_u8(buf); + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("test 0x%02x company 0x%04x", test_id, company_id); + + if (srv->cb && srv->cb->fault_test) { + srv->cb->fault_test(model, test_id, company_id); + } +} + +static void health_fault_test(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + struct bt_mesh_health_srv *srv = model->user_data; + u16_t company_id; + u8_t test_id; + + BT_DBG(""); + + test_id = net_buf_simple_pull_u8(buf); + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("test 0x%02x company 0x%04x", test_id, company_id); + + if (srv->cb && srv->cb->fault_test) { + int err; + + err = srv->cb->fault_test(model, test_id, company_id); + if (err) { + BT_WARN("Running fault test failed with err %d", err); + goto done; + } + } + + health_get_registered(model, company_id, sdu); + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("Unable to send Health Current Status response"); + } + +done: + os_mbuf_free_chain(sdu); +} + +static void send_attention_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct bt_mesh_health_srv *srv = model->user_data; + u8_t time; + + time = k_delayed_work_remaining_get(&srv->attn_timer) / 1000; + BT_DBG("%u second%s", time, (time == 1) ? "" : "s"); + + bt_mesh_model_msg_init(msg, OP_ATTENTION_STATUS); + + net_buf_simple_add_u8(msg, time); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Attention Status"); + } + + os_mbuf_free_chain(msg); +} + +static void attention_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + send_attention_status(model, ctx); +} + +static void attention_set_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t time; + + time = net_buf_simple_pull_u8(buf); + + BT_DBG("%u second%s", time, (time == 1) ? "" : "s"); + + bt_mesh_attention(model, time); +} + +static void attention_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + attention_set_unrel(model, ctx, buf); + + send_attention_status(model, ctx); +} + +static void send_health_period_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + + bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_STATUS); + + net_buf_simple_add_u8(msg, model->pub->period_div); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Health Period Status"); + } + + os_mbuf_free_chain(msg); +} + +static void health_period_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + send_health_period_status(model, ctx); +} + +static void health_period_set_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t period; + + period = net_buf_simple_pull_u8(buf); + if (period > 15) { + BT_WARN("Prohibited period value %u", period); + return; + } + + BT_DBG("period %u", period); + + model->pub->period_div = period; +} + +static void health_period_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + health_period_set_unrel(model, ctx, buf); + + send_health_period_status(model, ctx); +} + +const struct bt_mesh_model_op bt_mesh_health_srv_op[] = { + { OP_HEALTH_FAULT_GET, 2, health_fault_get }, + { OP_HEALTH_FAULT_CLEAR, 2, health_fault_clear }, + { OP_HEALTH_FAULT_CLEAR_UNREL, 2, health_fault_clear_unrel }, + { OP_HEALTH_FAULT_TEST, 3, health_fault_test }, + { OP_HEALTH_FAULT_TEST_UNREL, 3, health_fault_test_unrel }, + { OP_HEALTH_PERIOD_GET, 0, health_period_get }, + { OP_HEALTH_PERIOD_SET, 1, health_period_set }, + { OP_HEALTH_PERIOD_SET_UNREL, 1, health_period_set_unrel }, + { OP_ATTENTION_GET, 0, attention_get }, + { OP_ATTENTION_SET, 1, attention_set }, + { OP_ATTENTION_SET_UNREL, 1, attention_set_unrel }, + BT_MESH_MODEL_OP_END, +}; + +static int health_pub_update(struct bt_mesh_model *mod) +{ + struct bt_mesh_model_pub *pub = mod->pub; + size_t count; + + BT_DBG(""); + + count = health_get_current(mod, pub->msg); + if (!count) { + pub->period_div = 0; + } + + return 0; +} + +int bt_mesh_fault_update(struct bt_mesh_elem *elem) +{ + struct bt_mesh_model *mod; + + mod = bt_mesh_model_find(elem, BT_MESH_MODEL_ID_HEALTH_SRV); + if (!mod) { + return -EINVAL; + } + + return bt_mesh_model_publish(mod); +} + +static void attention_off(struct ble_npl_event *work) +{ + struct bt_mesh_health_srv *srv = ble_npl_event_get_arg(work); + BT_DBG(""); + + if (srv->cb && srv->cb->attn_off) { + srv->cb->attn_off(srv->model); + } +} + +int bt_mesh_health_srv_init(struct bt_mesh_model *model, bool primary) +{ + struct bt_mesh_health_srv *srv = model->user_data; + + if (!srv) { + if (!primary) { + return 0; + } + + BT_ERR("No Health Server context provided"); + return -EINVAL; + } + + if (!model->pub) { + BT_ERR("Health Server has no publication support"); + return -EINVAL; + } + + model->pub->update = health_pub_update; + + k_delayed_work_init(&srv->attn_timer, attention_off); + k_delayed_work_add_arg(&srv->attn_timer, srv); + + srv->model = model; + + if (primary) { + health_srv = srv; + } + + return 0; +} + +void bt_mesh_attention(struct bt_mesh_model *model, u8_t time) +{ + struct bt_mesh_health_srv *srv; + + BT_DBG("bt_mesh_attention"); + if (!model) { + srv = health_srv; + if (!srv) { + BT_WARN("No Health Server available"); + return; + } + + model = srv->model; + } else { + srv = model->user_data; + } + + if (time) { + if (srv->cb && srv->cb->attn_on) { + srv->cb->attn_on(model); + } + + k_delayed_work_submit(&srv->attn_timer, time * 1000); + } else { + k_delayed_work_cancel(&srv->attn_timer); + + if (srv->cb && srv->cb->attn_off) { + srv->cb->attn_off(model); + } + } +} diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.c new file mode 100644 index 000000000..b6d838188 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.c @@ -0,0 +1,58 @@ + +#include "syscfg/syscfg.h" + +#include "mesh/mesh.h" +#include "console/console.h" +#include "light_model.h" + + +static u8_t gen_onoff_state; +static s16_t gen_level_state; + +static void update_light_state(void) +{ + console_printf("Light state: onoff=%d lvl=0x%04x\n", gen_onoff_state, (u16_t)gen_level_state); +} + +int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state) +{ + *state = gen_onoff_state; + return 0; +} + +int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state) +{ + gen_onoff_state = state; + update_light_state(); + return 0; +} + +int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level) +{ + *level = gen_level_state; + return 0; +} + +int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level) +{ + gen_level_state = level; + if ((u16_t)gen_level_state > 0x0000) { + gen_onoff_state = 1; + } + if ((u16_t)gen_level_state == 0x0000) { + gen_onoff_state = 0; + } + update_light_state(); + return 0; +} + +int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness) +{ + return light_model_gen_level_get(model, lightness); +} + +int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness) +{ + return light_model_gen_level_set(model, lightness); +} + diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.h new file mode 100644 index 000000000..95fcdb786 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_LIGHT_MODEL_H +#define __BT_MESH_LIGHT_MODEL_H + +#include "syscfg/syscfg.h" +#include "mesh/mesh.h" + +int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state); +int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state); +int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level); +int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level); +int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness); +int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness); + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.c new file mode 100644 index 000000000..0dd00ac6c --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.c @@ -0,0 +1,1046 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) + +#include + +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_LOW_POWER)) +#include "host/ble_hs_log.h" + +#include "mesh/mesh.h" +#include "mesh_priv.h" +#include "crypto.h" +#include "adv.h" +#include "net.h" +#include "transport.h" +#include "access.h" +#include "beacon.h" +#include "foundation.h" +#include "lpn.h" + +#if MYNEWT_VAL(BLE_MESH_LPN_AUTO) +#define LPN_AUTO_TIMEOUT K_SECONDS(MYNEWT_VAL(BLE_MESH_LPN_AUTO_TIMEOUT)) +#else +#define LPN_AUTO_TIMEOUT 0 +#endif + +#define LPN_RECV_DELAY MYNEWT_VAL(BLE_MESH_LPN_RECV_DELAY) +#define SCAN_LATENCY min(MYNEWT_VAL(BLE_MESH_LPN_SCAN_LATENCY), \ + LPN_RECV_DELAY) + +#define FRIEND_REQ_RETRY_TIMEOUT K_SECONDS(MYNEWT_VAL(BLE_MESH_LPN_RETRY_TIMEOUT)) + +#define FRIEND_REQ_WAIT K_MSEC(100) +#define FRIEND_REQ_SCAN K_SECONDS(1) +#define FRIEND_REQ_TIMEOUT (FRIEND_REQ_WAIT + FRIEND_REQ_SCAN) + +#define POLL_RETRY_TIMEOUT K_MSEC(100) + +#define REQ_RETRY_DURATION(lpn) (4 * (LPN_RECV_DELAY + (lpn)->adv_duration + \ + (lpn)->recv_win + POLL_RETRY_TIMEOUT)) + +#define POLL_TIMEOUT_INIT (MYNEWT_VAL(BLE_MESH_LPN_INIT_POLL_TIMEOUT) * 100) +#define POLL_TIMEOUT_MAX(lpn) ((MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT) * 100) - \ + REQ_RETRY_DURATION(lpn)) +#define REQ_ATTEMPTS(lpn) (POLL_TIMEOUT_MAX(lpn) < K_SECONDS(3) ? 2 : 4) + +#define CLEAR_ATTEMPTS 2 + +#define LPN_CRITERIA ((MYNEWT_VAL(BLE_MESH_LPN_MIN_QUEUE_SIZE)) | \ + (MYNEWT_VAL(BLE_MESH_LPN_RSSI_FACTOR) << 3) | \ + (MYNEWT_VAL(BLE_MESH_LPN_RECV_WIN_FACTOR) << 5)) + +#define POLL_TO(to) { (u8_t)((to) >> 16), (u8_t)((to) >> 8), (u8_t)(to) } +#define LPN_POLL_TO POLL_TO(MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT)) + +/* 2 transmissions, 20ms interval */ +#define POLL_XMIT BT_MESH_TRANSMIT(1, 20) + +static void (*lpn_cb)(u16_t friend_addr, bool established); + +#if MYNEWT_VAL(BLE_MESH_DEBUG_LOW_POWER) +static const char *state2str(int state) +{ + switch (state) { + case BT_MESH_LPN_DISABLED: + return "disabled"; + case BT_MESH_LPN_CLEAR: + return "clear"; + case BT_MESH_LPN_TIMER: + return "timer"; + case BT_MESH_LPN_ENABLED: + return "enabled"; + case BT_MESH_LPN_REQ_WAIT: + return "req wait"; + case BT_MESH_LPN_WAIT_OFFER: + return "wait offer"; + case BT_MESH_LPN_ESTABLISHED: + return "established"; + case BT_MESH_LPN_RECV_DELAY: + return "recv delay"; + case BT_MESH_LPN_WAIT_UPDATE: + return "wait update"; + default: + return "(unknown)"; + } +} +#endif /* CONFIG_BLUETOOTH_MESH_DEBUG_LPN */ + +static inline void lpn_set_state(int state) +{ +#if MYNEWT_VAL(BLE_MESH_DEBUG_LOW_POWER) + BT_DBG("%s -> %s", state2str(bt_mesh.lpn.state), state2str(state)); +#endif + bt_mesh.lpn.state = state; +} + +static inline void group_zero(atomic_t *target) +{ +#if CONFIG_BT_MESH_LPN_GROUPS > 32 + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + atomic_set(&target[i], 0); + } +#else + atomic_set(target, 0); +#endif +} + +static inline void group_set(atomic_t *target, atomic_t *source) +{ +#if CONFIG_BT_MESH_LPN_GROUPS > 32 + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + atomic_or(&target[i], atomic_get(&source[i])); + } +#else + atomic_or(target, atomic_get(source)); +#endif +} + +static inline void group_clear(atomic_t *target, atomic_t *source) +{ +#if CONFIG_BT_MESH_LPN_GROUPS > 32 + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + atomic_and(&target[i], ~atomic_get(&source[i])); + } +#else + atomic_and(target, ~atomic_get(source)); +#endif +} + +static void clear_friendship(bool force, bool disable); + +static void friend_clear_sent(int err, void *user_data) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + /* We're switching away from Low Power behavior, so permanently + * enable scanning. + */ + bt_mesh_scan_enable(); + + lpn->req_attempts++; + + if (err) { + BT_ERR("Sending Friend Request failed (err %d)", err); + lpn_set_state(BT_MESH_LPN_ENABLED); + clear_friendship(false, lpn->disable); + return; + } + + lpn_set_state(BT_MESH_LPN_CLEAR); + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_TIMEOUT); +} + +static const struct bt_mesh_send_cb clear_sent_cb = { + .end = friend_clear_sent, +}; + +static int send_friend_clear(void) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = bt_mesh.lpn.frnd, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = bt_mesh_net_transmit_get(), + }; + struct bt_mesh_ctl_friend_clear req = { + .lpn_addr = sys_cpu_to_be16(tx.src), + .lpn_counter = sys_cpu_to_be16(bt_mesh.lpn.counter), + }; + + BT_DBG(""); + + return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req, + sizeof(req), NULL, &clear_sent_cb, NULL); +} + +static void clear_friendship(bool force, bool disable) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + BT_DBG("force %u disable %u", force, disable); + + if (!force && lpn->established && !lpn->clear_success && + lpn->req_attempts < CLEAR_ATTEMPTS) { + send_friend_clear(); + lpn->disable = disable; + return; + } + + bt_mesh_rx_reset(); + + k_delayed_work_cancel(&lpn->timer); + + friend_cred_del(bt_mesh.sub[0].net_idx, lpn->frnd); + + if (lpn->clear_success) { + lpn->old_friend = BT_MESH_ADDR_UNASSIGNED; + } else { + lpn->old_friend = lpn->frnd; + } + + if (lpn_cb && lpn->frnd != BT_MESH_ADDR_UNASSIGNED) { + lpn_cb(lpn->frnd, false); + } + + lpn->frnd = BT_MESH_ADDR_UNASSIGNED; + lpn->fsn = 0; + lpn->req_attempts = 0; + lpn->recv_win = 0; + lpn->queue_size = 0; + lpn->disable = 0; + lpn->sent_req = 0; + lpn->established = 0; + lpn->clear_success = 0; + + group_zero(lpn->added); + group_zero(lpn->pending); + group_zero(lpn->to_remove); + + /* Set this to 1 to force group subscription when the next + * Friendship is created, in case lpn->groups doesn't get + * modified meanwhile. + */ + lpn->groups_changed = 1; + + if (disable) { + lpn_set_state(BT_MESH_LPN_DISABLED); + return; + } + + lpn_set_state(BT_MESH_LPN_ENABLED); + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT); +} + +static void friend_req_sent(u16_t duration, int err, void *user_data) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (err) { + BT_ERR("Sending Friend Request failed (err %d)", err); + return; + } + + lpn->adv_duration = duration; + + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_WAIT); + lpn_set_state(BT_MESH_LPN_REQ_WAIT); + } else { + k_delayed_work_submit(&lpn->timer, + duration + FRIEND_REQ_TIMEOUT); + lpn_set_state(BT_MESH_LPN_WAIT_OFFER); + } +} + +static const struct bt_mesh_send_cb friend_req_sent_cb = { + .start = friend_req_sent, +}; + +static int send_friend_req(struct bt_mesh_lpn *lpn) +{ + const struct bt_mesh_comp *comp = bt_mesh_comp_get(); + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = BT_MESH_ADDR_FRIENDS, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = POLL_XMIT, + }; + struct bt_mesh_ctl_friend_req req = { + .criteria = LPN_CRITERIA, + .recv_delay = LPN_RECV_DELAY, + .poll_to = LPN_POLL_TO, + .prev_addr = lpn->old_friend, + .num_elem = comp->elem_count, + .lpn_counter = sys_cpu_to_be16(lpn->counter), + }; + + BT_DBG(""); + + return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_REQ, &req, + sizeof(req), NULL, &friend_req_sent_cb, NULL); +} + +static void req_sent(u16_t duration, int err, void *user_data) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + +#if BT_DBG_ENABLED + BT_DBG("req 0x%02x duration %u err %d state %s", + lpn->sent_req, duration, err, state2str(lpn->state)); +#endif + + if (err) { + BT_ERR("Sending request failed (err %d)", err); + lpn->sent_req = 0; + group_zero(lpn->pending); + return; + } + + lpn->req_attempts++; + lpn->adv_duration = duration; + + if (lpn->established || IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + lpn_set_state(BT_MESH_LPN_RECV_DELAY); + /* We start scanning a bit early to elimitate risk of missing + * response data due to HCI and other latencies. + */ + k_delayed_work_submit(&lpn->timer, + LPN_RECV_DELAY - SCAN_LATENCY); + } else { + k_delayed_work_submit(&lpn->timer, + LPN_RECV_DELAY + duration + + lpn->recv_win); + } +} + +static const struct bt_mesh_send_cb req_sent_cb = { + .start = req_sent, +}; + +static int send_friend_poll(void) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = bt_mesh.lpn.frnd, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = POLL_XMIT, + .friend_cred = true, + }; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + u8_t fsn = lpn->fsn; + int err; + + BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req); + + if (lpn->sent_req) { + if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) { + lpn->pending_poll = 1; + } + + return 0; + } + + err = bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_POLL, &fsn, 1, + NULL, &req_sent_cb, NULL); + if (err == 0) { + lpn->pending_poll = 0; + lpn->sent_req = TRANS_CTL_OP_FRIEND_POLL; + } + + return err; +} + +void bt_mesh_lpn_disable(bool force) +{ + if (bt_mesh.lpn.state == BT_MESH_LPN_DISABLED) { + return; + } + + clear_friendship(force, true); +} + +int bt_mesh_lpn_set(bool enable) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (enable) { + if (lpn->state != BT_MESH_LPN_DISABLED) { + return 0; + } + } else { + if (lpn->state == BT_MESH_LPN_DISABLED) { + return 0; + } + } + + if (!bt_mesh_is_provisioned()) { + if (enable) { + lpn_set_state(BT_MESH_LPN_ENABLED); + } else { + lpn_set_state(BT_MESH_LPN_DISABLED); + } + + return 0; + } + + if (enable) { + lpn_set_state(BT_MESH_LPN_ENABLED); + + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + + send_friend_req(lpn); + } else { + if (IS_ENABLED(CONFIG_BT_MESH_LPN_AUTO) && + lpn->state == BT_MESH_LPN_TIMER) { + k_delayed_work_cancel(&lpn->timer); + lpn_set_state(BT_MESH_LPN_DISABLED); + } else { + bt_mesh_lpn_disable(false); + } + } + + return 0; +} + +static void friend_response_received(struct bt_mesh_lpn *lpn) +{ + BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req); + + if (lpn->sent_req == TRANS_CTL_OP_FRIEND_POLL) { + lpn->fsn++; + } + + k_delayed_work_cancel(&lpn->timer); + bt_mesh_scan_disable(); + lpn_set_state(BT_MESH_LPN_ESTABLISHED); + lpn->req_attempts = 0; + lpn->sent_req = 0; +} + +void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (lpn->state == BT_MESH_LPN_TIMER) { + BT_DBG("Restarting establishment timer"); + k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT); + return; + } + + if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) { + BT_WARN("Unexpected message withouth a preceding Poll"); + return; + } + + friend_response_received(lpn); + + BT_DBG("Requesting more messages from Friend"); + + send_friend_poll(); +} + +int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_offer *msg = (void *)buf->om_data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + struct bt_mesh_subnet *sub = rx->sub; + struct friend_cred *cred; + u16_t frnd_counter; + int err; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Offer"); + return -EINVAL; + } + + if (lpn->state != BT_MESH_LPN_WAIT_OFFER) { + BT_WARN("Ignoring unexpected Friend Offer"); + return 0; + } + + if (!msg->recv_win) { + BT_WARN("Prohibited ReceiveWindow value"); + return -EINVAL; + } + + frnd_counter = sys_be16_to_cpu(msg->frnd_counter); + + BT_DBG("recv_win %u queue_size %u sub_list_size %u rssi %d counter %u", + msg->recv_win, msg->queue_size, msg->sub_list_size, msg->rssi, + frnd_counter); + + lpn->frnd = rx->ctx.addr; + + cred = friend_cred_create(sub, lpn->frnd, lpn->counter, frnd_counter); + if (!cred) { + lpn->frnd = BT_MESH_ADDR_UNASSIGNED; + return -ENOMEM; + } + + /* TODO: Add offer acceptance criteria check */ + + k_delayed_work_cancel(&lpn->timer); + + lpn->recv_win = msg->recv_win; + lpn->queue_size = msg->queue_size; + + err = send_friend_poll(); + if (err) { + friend_cred_clear(cred); + lpn->frnd = BT_MESH_ADDR_UNASSIGNED; + lpn->recv_win = 0; + lpn->queue_size = 0; + return err; + } + + lpn->counter++; + + return 0; +} + +int bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_clear_confirm *msg = (void *)buf->om_data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + u16_t addr, counter; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Clear Confirm"); + return -EINVAL; + } + + if (lpn->state != BT_MESH_LPN_CLEAR) { + BT_WARN("Ignoring unexpected Friend Clear Confirm"); + return 0; + } + + addr = sys_be16_to_cpu(msg->lpn_addr); + counter = sys_be16_to_cpu(msg->lpn_counter); + + BT_DBG("LPNAddress 0x%04x LPNCounter 0x%04x", addr, counter); + + if (addr != bt_mesh_primary_addr() || counter != lpn->counter) { + BT_WARN("Invalid parameters in Friend Clear Confirm"); + return 0; + } + + lpn->clear_success = 1; + clear_friendship(false, lpn->disable); + + return 0; +} + +static void lpn_group_add(u16_t group) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + u16_t *free_slot = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (lpn->groups[i] == group) { + atomic_clear_bit(lpn->to_remove, i); + return; + } + + if (!free_slot && lpn->groups[i] == BT_MESH_ADDR_UNASSIGNED) { + free_slot = &lpn->groups[i]; + } + } + + if (!free_slot) { + BT_WARN("Friend Subscription List exceeded!"); + return; + } + + *free_slot = group; + lpn->groups_changed = 1; +} + +static void lpn_group_del(u16_t group) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + int i; + + for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (lpn->groups[i] == group) { + if (atomic_test_bit(lpn->added, i) || + atomic_test_bit(lpn->pending, i)) { + atomic_set_bit(lpn->to_remove, i); + lpn->groups_changed = 1; + } else { + lpn->groups[i] = BT_MESH_ADDR_UNASSIGNED; + } + } + } +} + +static inline int group_popcount(atomic_t *target) +{ +#if CONFIG_BT_MESH_LPN_GROUPS > 32 + int i, count = 0; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + count += popcount(atomic_get(&target[i])); + } +#else + return popcount(atomic_get(target)); +#endif +} + +static bool sub_update(u8_t op) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + int added_count = group_popcount(lpn->added); + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = lpn->frnd, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = POLL_XMIT, + .friend_cred = true, + }; + struct bt_mesh_ctl_friend_sub req; + size_t i, g; + + BT_DBG("op 0x%02x sent_req 0x%02x", op, lpn->sent_req); + + if (lpn->sent_req) { + return false; + } + + for (i = 0, g = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (lpn->groups[i] == BT_MESH_ADDR_UNASSIGNED) { + continue; + } + + if (op == TRANS_CTL_OP_FRIEND_SUB_ADD) { + if (atomic_test_bit(lpn->added, i)) { + continue; + } + } else { + if (!atomic_test_bit(lpn->to_remove, i)) { + continue; + } + } + + if (added_count + g >= lpn->queue_size) { + BT_WARN("Friend Queue Size exceeded"); + break; + } + + req.addr_list[g++] = sys_cpu_to_be16(lpn->groups[i]); + atomic_set_bit(lpn->pending, i); + + if (g == ARRAY_SIZE(req.addr_list)) { + break; + } + } + + if (g == 0) { + group_zero(lpn->pending); + return false; + } + + req.xact = lpn->xact_next++; + + if (bt_mesh_ctl_send(&tx, op, &req, 1 + g * 2, NULL, + &req_sent_cb, NULL) < 0) { + group_zero(lpn->pending); + return false; + } + + lpn->xact_pending = req.xact; + lpn->sent_req = op; + return true; +} + +static void update_timeout(struct bt_mesh_lpn *lpn) +{ + if (lpn->established) { + BT_WARN("No response from Friend during ReceiveWindow"); + bt_mesh_scan_disable(); + lpn_set_state(BT_MESH_LPN_ESTABLISHED); + k_delayed_work_submit(&lpn->timer, POLL_RETRY_TIMEOUT); + } else { + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + + if (lpn->req_attempts < 6) { + BT_WARN("Retrying first Friend Poll"); + lpn->sent_req = 0; + if (send_friend_poll() == 0) { + return; + } + } + + BT_ERR("Timed out waiting for first Friend Update"); + clear_friendship(false, false); + } +} + +static void lpn_timeout(struct ble_npl_event *work) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + +#if MYNEWT_VAL(BLE_MESH_DEBUG_LOW_POWER) + BT_DBG("state: %s", state2str(lpn->state)); +#endif + + switch (lpn->state) { + case BT_MESH_LPN_DISABLED: + break; + case BT_MESH_LPN_CLEAR: + clear_friendship(false, bt_mesh.lpn.disable); + break; + case BT_MESH_LPN_TIMER: + BT_DBG("Starting to look for Friend nodes"); + lpn_set_state(BT_MESH_LPN_ENABLED); + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + /* fall through */ + case BT_MESH_LPN_ENABLED: + send_friend_req(lpn); + break; + case BT_MESH_LPN_REQ_WAIT: + bt_mesh_scan_enable(); + k_delayed_work_submit(&lpn->timer, + lpn->adv_duration + FRIEND_REQ_SCAN); + lpn_set_state(BT_MESH_LPN_WAIT_OFFER); + break; + case BT_MESH_LPN_WAIT_OFFER: + BT_WARN("No acceptable Friend Offers received"); + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + lpn->counter++; + lpn_set_state(BT_MESH_LPN_ENABLED); + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT); + break; + case BT_MESH_LPN_ESTABLISHED: + if (lpn->req_attempts < REQ_ATTEMPTS(lpn)) { + u8_t req = lpn->sent_req; + + lpn->sent_req = 0; + + if (!req || req == TRANS_CTL_OP_FRIEND_POLL) { + send_friend_poll(); + } else { + sub_update(req); + } + + break; + } + + BT_ERR("No response from Friend after %u retries", + lpn->req_attempts); + lpn->req_attempts = 0; + clear_friendship(false, false); + break; + case BT_MESH_LPN_RECV_DELAY: + k_delayed_work_submit(&lpn->timer, + lpn->adv_duration + SCAN_LATENCY + + lpn->recv_win); + bt_mesh_scan_enable(); + lpn_set_state(BT_MESH_LPN_WAIT_UPDATE); + break; + case BT_MESH_LPN_WAIT_UPDATE: + update_timeout(lpn); + break; + default: + __ASSERT(0, "Unhandled LPN state"); + break; + } +} + +void bt_mesh_lpn_group_add(u16_t group) +{ + BT_DBG("group 0x%04x", group); + + lpn_group_add(group); + + if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) { + return; + } + + sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD); +} + +void bt_mesh_lpn_group_del(u16_t *groups, size_t group_count) +{ + int i; + + for (i = 0; i < group_count; i++) { + if (groups[i] != BT_MESH_ADDR_UNASSIGNED) { + BT_DBG("group 0x%04x", groups[i]); + lpn_group_del(groups[i]); + } + } + + if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) { + return; + } + + sub_update(TRANS_CTL_OP_FRIEND_SUB_REM); +} + +static s32_t poll_timeout(struct bt_mesh_lpn *lpn) +{ + /* If we're waiting for segment acks keep polling at high freq */ + if (bt_mesh_tx_in_progress()) { + return min(POLL_TIMEOUT_MAX(lpn), K_SECONDS(1)); + } + + if (lpn->poll_timeout < POLL_TIMEOUT_MAX(lpn)) { + lpn->poll_timeout *= 2; + lpn->poll_timeout = min(lpn->poll_timeout, + POLL_TIMEOUT_MAX(lpn)); + } + + BT_DBG("Poll Timeout is %ums", (unsigned) lpn->poll_timeout); + + return lpn->poll_timeout; +} + +int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_sub_confirm *msg = (void *)buf->om_data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Subscription Confirm"); + return -EINVAL; + } + + BT_DBG("xact 0x%02x", msg->xact); + + if (!lpn->sent_req) { + BT_WARN("No pending subscription list message"); + return 0; + } + + if (msg->xact != lpn->xact_pending) { + BT_WARN("Transaction mismatch (0x%02x != 0x%02x)", + msg->xact, lpn->xact_pending); + return 0; + } + + if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_ADD) { + group_set(lpn->added, lpn->pending); + group_zero(lpn->pending); + } else if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_REM) { + int i; + + group_clear(lpn->added, lpn->pending); + + for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (atomic_test_and_clear_bit(lpn->pending, i) && + atomic_test_and_clear_bit(lpn->to_remove, i)) { + lpn->groups[i] = BT_MESH_ADDR_UNASSIGNED; + } + } + } else { + BT_WARN("Unexpected Friend Subscription Confirm"); + return 0; + } + + friend_response_received(lpn); + + if (lpn->groups_changed) { + sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD); + sub_update(TRANS_CTL_OP_FRIEND_SUB_REM); + + if (!lpn->sent_req) { + lpn->groups_changed = 0; + } + } + + if (lpn->pending_poll) { + send_friend_poll(); + } + + if (!lpn->sent_req) { + k_delayed_work_submit(&lpn->timer, poll_timeout(lpn)); + } + + return 0; +} + +int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_update *msg = (void *)buf->om_data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + struct bt_mesh_subnet *sub = rx->sub; + u32_t iv_index; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Update"); + return -EINVAL; + } + + if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) { + BT_WARN("Unexpected friend update"); + return 0; + } + + if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !rx->new_key) { + BT_WARN("Ignoring Phase 2 KR Update secured using old key"); + return 0; + } + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) && + (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) == + BT_MESH_IV_UPDATE(msg->flags))) { + bt_mesh_beacon_ivu_initiator(false); + } + + if (!lpn->established) { + /* This is normally checked on the transport layer, however + * in this state we're also still accepting master + * credentials so we need to ensure the right ones (Friend + * Credentials) were used for this message. + */ + if (!rx->friend_cred) { + BT_WARN("Friend Update with wrong credentials"); + return -EINVAL; + } + + lpn->established = 1; + + BT_INFO("Friendship established with 0x%04x", lpn->frnd); + + if (lpn_cb) { + lpn_cb(lpn->frnd, true); + } + + /* Set initial poll timeout */ + lpn->poll_timeout = min(POLL_TIMEOUT_MAX(lpn), + POLL_TIMEOUT_INIT); + } + + friend_response_received(lpn); + + iv_index = sys_be32_to_cpu(msg->iv_index); + + BT_DBG("flags 0x%02x iv_index 0x%08x md %u", msg->flags, + (unsigned) iv_index, msg->md); + + if (bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(msg->flags), + rx->new_key)) { + bt_mesh_net_beacon_update(sub); + } + + bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(msg->flags)); + + if (lpn->groups_changed) { + sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD); + sub_update(TRANS_CTL_OP_FRIEND_SUB_REM); + + if (!lpn->sent_req) { + lpn->groups_changed = 0; + } + } + + if (msg->md) { + BT_DBG("Requesting for more messages"); + send_friend_poll(); + } + + if (!lpn->sent_req) { + k_delayed_work_submit(&lpn->timer, poll_timeout(lpn)); + } + + return 0; +} + +int bt_mesh_lpn_poll(void) +{ + if (!bt_mesh.lpn.established) { + return -EAGAIN; + } + + BT_DBG("Requesting more messages"); + + return send_friend_poll(); +} + +void bt_mesh_lpn_set_cb(void (*cb)(u16_t friend_addr, bool established)) +{ + lpn_cb = cb; +} + +int bt_mesh_lpn_init(void) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + BT_DBG(""); + + k_delayed_work_init(&lpn->timer, lpn_timeout); + + if (lpn->state == BT_MESH_LPN_ENABLED) { + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } else { + bt_mesh_scan_enable(); + } + + send_friend_req(lpn); + } else { + bt_mesh_scan_enable(); + + if (IS_ENABLED(CONFIG_BT_MESH_LPN_AUTO)) { + BT_DBG("Waiting %u ms for messages", LPN_AUTO_TIMEOUT); + lpn_set_state(BT_MESH_LPN_TIMER); + k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT); + } + } + + return 0; +} + +#endif /* MYNEWT_VAL(BLE_MESH_LOW_POWER) */ diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.h new file mode 100644 index 000000000..0ff6c9cfd --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.h @@ -0,0 +1,68 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __LPN_H__ +#define __LPN_H__ + +#include "mesh/mesh.h" + +int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); +int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); +int bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); +int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); + +static inline bool bt_mesh_lpn_established(void) +{ +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + return bt_mesh.lpn.established; +#else + return false; +#endif +} + +static inline bool bt_mesh_lpn_match(u16_t addr) +{ +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + if (bt_mesh_lpn_established()) { + return (addr == bt_mesh.lpn.frnd); + } +#endif + return false; +} + +static inline bool bt_mesh_lpn_waiting_update(void) +{ +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + return (bt_mesh.lpn.state == BT_MESH_LPN_WAIT_UPDATE); +#else + return false; +#endif +} + +static inline bool bt_mesh_lpn_timer(void) +{ +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) && MYNEWT_VAL(BLE_MESH_LPN_AUTO) + return (bt_mesh.lpn.state == BT_MESH_LPN_TIMER); +#else + return false; +#endif +} + +void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx); + +void bt_mesh_lpn_group_add(u16_t group); +void bt_mesh_lpn_group_del(u16_t *groups, size_t group_count); + +void bt_mesh_lpn_disable(bool force); + +int bt_mesh_lpn_init(void); + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh.c new file mode 100644 index 000000000..80b638b1f --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh.c @@ -0,0 +1,345 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "os/os_mbuf.h" +#include "mesh/mesh.h" + +#include "syscfg/syscfg.h" +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG)) +#include "host/ble_hs_log.h" +#include "host/ble_uuid.h" + +#include "adv.h" +#include "prov.h" +#include "net.h" +#include "beacon.h" +#include "lpn.h" +#include "friend.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "proxy.h" +#include "shell.h" +#include "mesh_priv.h" +#include "settings.h" + +u8_t g_mesh_addr_type; +static struct ble_gap_event_listener mesh_event_listener; + +int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx, + u8_t flags, u32_t iv_index, u16_t addr, + const u8_t dev_key[16]) +{ + bool pb_gatt_enabled; + int err; + + BT_INFO("Primary Element: 0x%04x", addr); + BT_DBG("net_idx 0x%04x flags 0x%02x iv_index 0x%04x", + net_idx, flags, (unsigned) iv_index); + + if (atomic_test_and_set_bit(bt_mesh.flags, BT_MESH_VALID)) { + return -EALREADY; + } + + if ((MYNEWT_VAL(BLE_MESH_PB_GATT))) { + if (bt_mesh_proxy_prov_disable() == 0) { + pb_gatt_enabled = true; + } else { + pb_gatt_enabled = false; + } + } else { + pb_gatt_enabled = false; + } + + err = bt_mesh_net_create(net_idx, flags, net_key, iv_index); + if (err) { + atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID); + + if (MYNEWT_VAL(BLE_MESH_PB_GATT) && pb_gatt_enabled) { + bt_mesh_proxy_prov_enable(); + } + + return err; + } + + bt_mesh.seq = 0; + + bt_mesh_comp_provision(addr); + + memcpy(bt_mesh.dev_key, dev_key, 16); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + BT_DBG("Storing network information persistently"); + bt_mesh_store_net(); + bt_mesh_store_subnet(&bt_mesh.sub[0]); + bt_mesh_store_iv(false); + } + + bt_mesh_net_start(); + + return 0; +} + +void bt_mesh_reset(void) +{ + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + return; + } + + bt_mesh.iv_index = 0U; + bt_mesh.seq = 0U; + + memset(bt_mesh.flags, 0, sizeof(bt_mesh.flags)); + + k_delayed_work_cancel(&bt_mesh.ivu_timer); + + bt_mesh_cfg_reset(); + + bt_mesh_rx_reset(); + bt_mesh_tx_reset(); + + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) { + bt_mesh_lpn_disable(true); + } + + if ((MYNEWT_VAL(BLE_MESH_FRIEND))) { + bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY); + } + + if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) { + bt_mesh_proxy_gatt_disable(); + } + + if ((MYNEWT_VAL(BLE_MESH_PB_GATT))) { + bt_mesh_proxy_prov_enable(); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_clear_net(); + } + + memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key)); + + bt_mesh_scan_disable(); + bt_mesh_beacon_disable(); + + bt_mesh_comp_unprovision(); + + if (IS_ENABLED(CONFIG_BT_MESH_PROV)) { + bt_mesh_prov_reset(); + } +} + +bool bt_mesh_is_provisioned(void) +{ + return atomic_test_bit(bt_mesh.flags, BT_MESH_VALID); +} + +int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers) +{ + if (bt_mesh_is_provisioned()) { + return -EALREADY; + } + + if (MYNEWT_VAL(BLE_MESH_DEBUG)) { + char uuid_buf[BLE_UUID_STR_LEN]; + const struct bt_mesh_prov *prov = bt_mesh_prov_get(); + ble_uuid_t *uuid = BLE_UUID128_DECLARE(); + + memcpy(BLE_UUID128(uuid)->value, prov->uuid, 16); + BT_INFO("Device UUID: %s", ble_uuid_to_str(uuid, uuid_buf)); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) && + (bearers & BT_MESH_PROV_ADV)) { + /* Make sure we're scanning for provisioning inviations */ + bt_mesh_scan_enable(); + /* Enable unprovisioned beacon sending */ + bt_mesh_beacon_enable(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) && + (bearers & BT_MESH_PROV_GATT)) { + bt_mesh_proxy_prov_enable(); + bt_mesh_adv_update(); + } + + return 0; +} + +int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers) +{ + if (bt_mesh_is_provisioned()) { + return -EALREADY; + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) && + (bearers & BT_MESH_PROV_ADV)) { + bt_mesh_beacon_disable(); + bt_mesh_scan_disable(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) && + (bearers & BT_MESH_PROV_GATT)) { + bt_mesh_proxy_prov_disable(); + bt_mesh_adv_update(); + } + + return 0; +} + +static int bt_mesh_gap_event(struct ble_gap_event *event, void *arg) +{ + ble_adv_gap_mesh_cb(event, arg); + +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + ble_mesh_proxy_gap_event(event, arg); +#endif + + return 0; +} + +static void model_suspend(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (mod->pub && mod->pub->update) { + mod->pub->count = 0; + k_delayed_work_cancel(&mod->pub->timer); + } +} + +int bt_mesh_suspend(void) +{ + int err; + + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + return -EINVAL; + } + + if (atomic_test_and_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { + return -EALREADY; + } + + err = bt_mesh_scan_disable(); + if (err) { + atomic_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED); + BT_WARN("Disabling scanning failed (err %d)", err); + return err; + } + + bt_mesh_hb_pub_disable(); + + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) { + bt_mesh_beacon_disable(); + } + + bt_mesh_model_foreach(model_suspend, NULL); + + return 0; +} + +static void model_resume(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (mod->pub && mod->pub->update) { + s32_t period_ms = bt_mesh_model_pub_period_get(mod); + + if (period_ms) { + k_delayed_work_submit(&mod->pub->timer, period_ms); + } + } +} + +int bt_mesh_resume(void) +{ + int err; + + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + return -EINVAL; + } + + if (!atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { + return -EALREADY; + } + + err = bt_mesh_scan_enable(); + if (err) { + BT_WARN("Re-enabling scanning failed (err %d)", err); + atomic_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED); + return err; + } + + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) { + bt_mesh_beacon_enable(); + } + + bt_mesh_model_foreach(model_resume, NULL); + + return err; +} + +int bt_mesh_init(uint8_t own_addr_type, const struct bt_mesh_prov *prov, + const struct bt_mesh_comp *comp) +{ + int err; + + g_mesh_addr_type = own_addr_type; + + /* initialize SM alg ECC subsystem (it is used directly from mesh code) */ + ble_sm_alg_ecc_init(); + + err = bt_mesh_comp_register(comp); + if (err) { + return err; + } + +#if (MYNEWT_VAL(BLE_MESH_PROV)) + err = bt_mesh_prov_init(prov); + if (err) { + return err; + } +#endif + +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + bt_mesh_proxy_init(); +#endif + +#if (MYNEWT_VAL(BLE_MESH_PROV)) + /* Need this to proper link.rx.buf allocation */ + bt_mesh_prov_reset_link(); +#endif + + bt_mesh_net_init(); + bt_mesh_trans_init(); + bt_mesh_beacon_init(); + bt_mesh_adv_init(); + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + /* Make sure we're scanning for provisioning inviations */ + bt_mesh_scan_enable(); + /* Enable unprovisioned beacon sending */ + + bt_mesh_beacon_enable(); +#endif + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + bt_mesh_proxy_prov_enable(); +#endif + + ble_gap_event_listener_register(&mesh_event_listener, + bt_mesh_gap_event, NULL); + +#if (MYNEWT_VAL(BLE_MESH_SETTINGS)) + bt_mesh_settings_init(); +#endif + + return 0; +} diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh_priv.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh_priv.h new file mode 100644 index 000000000..0a26c903b --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh_priv.h @@ -0,0 +1,38 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __MESH_PRIV_H +#define __MESH_PRIV_H + +#define BT_MESH_KEY_PRIMARY 0x0000 +#define BT_MESH_KEY_ANY 0xffff + +#define BT_MESH_ADDR_IS_UNICAST(addr) ((addr) && (addr) < 0x8000) +#define BT_MESH_ADDR_IS_GROUP(addr) ((addr) >= 0xc000 && (addr) <= 0xff00) +#define BT_MESH_ADDR_IS_VIRTUAL(addr) ((addr) >= 0x8000 && (addr) < 0xc000) +#define BT_MESH_ADDR_IS_RFU(addr) ((addr) >= 0xff00 && (addr) <= 0xfffb) +struct bt_mesh_net; + +#define OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01) +#define OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02) +#define OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03) +#define OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04) +#define OP_GEN_LEVEL_GET BT_MESH_MODEL_OP_2(0x82, 0x05) +#define OP_GEN_LEVEL_SET BT_MESH_MODEL_OP_2(0x82, 0x06) +#define OP_GEN_LEVEL_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x07) +#define OP_GEN_LEVEL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x08) +#define OP_GEN_DELTA_SET BT_MESH_MODEL_OP_2(0x82, 0x09) +#define OP_GEN_DELTA_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0a) +#define OP_GEN_MOVE_SET BT_MESH_MODEL_OP_2(0x82, 0x0b) +#define OP_GEN_MOVE_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0c) +#define OP_LIGHT_LIGHTNESS_GET BT_MESH_MODEL_OP_2(0x82, 0x4b) +#define OP_LIGHT_LIGHTNESS_SET BT_MESH_MODEL_OP_2(0x82, 0x4c) +#define OP_LIGHT_LIGHTNESS_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x4d) + +bool bt_mesh_is_provisioned(void); + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_cli.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_cli.c new file mode 100644 index 000000000..c9e9cf622 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_cli.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_MODEL)) +#include "mesh/model_cli.h" +#include "mesh_priv.h" + +static s32_t msg_timeout = K_SECONDS(5); + +struct bt_mesh_gen_model_cli gen_onoff_cli; +struct bt_mesh_gen_model_cli gen_level_cli; + +static u8_t transaction_id = 0; + +struct gen_onoff_param { + u8_t *state; +}; + +struct gen_level_param { + s16_t *level; +}; + +static void gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_gen_model_cli *cli = model->user_data; + struct gen_onoff_param *param; + u8_t state; + + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_GEN_ONOFF_STATUS) { + BT_WARN("Unexpected Generic OnOff Status message"); + return; + } + + param = cli->op_param; + + state = net_buf_simple_pull_u8(buf); + if (param->state) { + *param->state = state; + } + + BT_DBG("state: %d", state); + + k_sem_give(&cli->op_sync); +} + +static void gen_level_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_gen_model_cli *cli = model->user_data; + struct gen_level_param *param; + s16_t level; + + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_GEN_LEVEL_STATUS) { + BT_WARN("Unexpected Generic LEVEL Status message"); + return; + } + + param = cli->op_param; + + level = net_buf_simple_pull_le16(buf); + if (param->level) { + *param->level = level; + } + + BT_DBG("level: %d", level); + + k_sem_give(&cli->op_sync); +} + +const struct bt_mesh_model_op gen_onoff_cli_op[] = { + { OP_GEN_ONOFF_STATUS, 1, gen_onoff_status }, + BT_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op gen_level_cli_op[] = { + { OP_GEN_LEVEL_STATUS, 2, gen_level_status }, + BT_MESH_MODEL_OP_END, +}; + +static int cli_wait(struct bt_mesh_gen_model_cli *cli, void *param, u32_t op) +{ + int err; + + BT_DBG(""); + + cli->op_param = param; + cli->op_pending = op; + + err = k_sem_take(&cli->op_sync, msg_timeout); + + cli->op_pending = 0; + cli->op_param = NULL; + + return err; +} + +int bt_mesh_gen_onoff_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *state) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct gen_onoff_param param = { + .state = state, + }; + int err; + + bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_GET); + + err = bt_mesh_model_send(gen_onoff_cli.model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + goto done; + } + + err = cli_wait(&gen_onoff_cli, ¶m, OP_GEN_ONOFF_STATUS); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_gen_onoff_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t val, u8_t *state) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct gen_onoff_param param = { + .state = state, + }; + int err; + + if (state) { + bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_SET); + } else { + bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_SET_UNACK); + } + + net_buf_simple_add_u8(msg, val); + net_buf_simple_add_u8(msg, transaction_id); + + err = bt_mesh_model_send(gen_onoff_cli.model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + goto done; + } + + if (!state) { + goto done; + } + + err = cli_wait(&gen_onoff_cli, ¶m, OP_GEN_ONOFF_STATUS); +done: + if (err == 0) { + transaction_id++; + } + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_gen_level_get(u16_t net_idx, u16_t addr, u16_t app_idx, + s16_t *level) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct gen_level_param param = { + .level = level, + }; + int err; + + bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_GET); + + err = bt_mesh_model_send(gen_level_cli.model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + goto done; + } + + err = cli_wait(&gen_level_cli, ¶m, OP_GEN_LEVEL_STATUS); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_gen_level_set(u16_t net_idx, u16_t addr, u16_t app_idx, + s16_t val, s16_t *state) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 3 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct gen_level_param param = { + .level = state, + }; + int err; + + if (state) { + bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_SET); + } else { + bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_SET_UNACK); + } + + net_buf_simple_add_le16(msg, val); + net_buf_simple_add_u8(msg, transaction_id); + + err = bt_mesh_model_send(gen_level_cli.model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + goto done; + } + + if (!state) { + goto done; + } + + err = cli_wait(&gen_level_cli, ¶m, OP_GEN_LEVEL_STATUS); +done: + if (err == 0) { + transaction_id++; + } + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_gen_model_cli_init(struct bt_mesh_model *model, bool primary) +{ + struct bt_mesh_gen_model_cli *cli = model->user_data; + + BT_DBG("primary %u", primary); + + if (!cli) { + BT_ERR("No Generic Model Client context provided"); + return -EINVAL; + } + + cli->model = model; + + k_sem_init(&cli->op_sync, 0, 1); + + return 0; +} + diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_srv.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_srv.c new file mode 100644 index 000000000..1420b1ccc --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_srv.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mesh/mesh.h" + +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_MODEL)) + +#include "mesh/model_srv.h" +#include "mesh_priv.h" + +static void gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct bt_mesh_gen_onoff_srv_cb *cb = model->user_data; + struct os_mbuf *msg = NET_BUF_SIMPLE(3); + u8_t *state; + + bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_STATUS); + state = net_buf_simple_add(msg, 1); + if (cb && cb->get) { + cb->get(model, state); + } + + BT_DBG("state: %d", *state); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Send status failed"); + } + + os_mbuf_free_chain(msg); +} + +static void gen_onoff_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + gen_onoff_status(model, ctx); +} + +static void gen_onoff_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_gen_onoff_srv_cb *cb = model->user_data; + u8_t state; + + state = buf->om_data[0]; + + BT_DBG("state: %d", state); + + if (cb && cb->set) { + cb->set(model, state); + } +} + +static void gen_onoff_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + gen_onoff_set_unack(model, ctx, buf); + gen_onoff_status(model, ctx); +} + +static void gen_level_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct bt_mesh_gen_level_srv_cb *cb = model->user_data; + struct os_mbuf *msg = NET_BUF_SIMPLE(4); + s16_t *level; + + bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_STATUS); + level = net_buf_simple_add(msg, 2); + if (cb && cb->get) { + cb->get(model, level); + } + + BT_DBG("level: %d", *level); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Send status failed"); + } + + os_mbuf_free_chain(msg); +} + +static void gen_level_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + gen_level_status(model, ctx); +} + +static void gen_level_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) { + struct bt_mesh_gen_level_srv_cb *cb = model->user_data; + s16_t level; + + level = (s16_t) net_buf_simple_pull_le16(buf); + BT_DBG("level: %d", level); + + if (cb && cb->set) { + cb->set(model, level); + } +} + +static void gen_level_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + gen_level_set_unack(model, ctx, buf); + gen_level_status(model, ctx); +} + +static void light_lightness_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct bt_mesh_light_lightness_srv_cb *cb = model->user_data; + struct os_mbuf *msg = NET_BUF_SIMPLE(4); + s16_t *lightness; + + bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_STATUS); + lightness = net_buf_simple_add(msg, 2); + if (cb && cb->get) { + cb->get(model, lightness); + } + + BT_DBG("lightness: %d", *lightness); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Send status failed"); + } + + os_mbuf_free_chain(msg); +} + +static void light_lightness_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + light_lightness_status(model, ctx); +} + +static void light_lightness_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) { + struct bt_mesh_light_lightness_srv_cb *cb = model->user_data; + s16_t lightness; + + lightness = (s16_t) net_buf_simple_pull_le16(buf); + BT_DBG("lightness: %d", lightness); + + if (cb && cb->set) { + cb->set(model, lightness); + } +} + +static void light_lightness_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + light_lightness_set_unack(model, ctx, buf); + light_lightness_status(model, ctx); +} + +const struct bt_mesh_model_op gen_onoff_srv_op[] = { + { OP_GEN_ONOFF_GET, 0, gen_onoff_get }, + { OP_GEN_ONOFF_SET, 2, gen_onoff_set }, + { OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op gen_level_srv_op[] = { + { OP_GEN_LEVEL_GET, 0, gen_level_get }, + { OP_GEN_LEVEL_SET, 3, gen_level_set }, + { OP_GEN_LEVEL_SET_UNACK, 3, gen_level_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op light_lightness_srv_op[] = { + { OP_LIGHT_LIGHTNESS_GET, 0, light_lightness_get }, + { OP_LIGHT_LIGHTNESS_SET, 3, light_lightness_set }, + { OP_LIGHT_LIGHTNESS_SET_UNACK, 3, light_lightness_set_unack }, + BT_MESH_MODEL_OP_END, +}; diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.c new file mode 100644 index 000000000..455893354 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.c @@ -0,0 +1,1410 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "os/os_mbuf.h" +#include "mesh/mesh.h" + +#include "syscfg/syscfg.h" +#define BT_DBG_ENABLED MYNEWT_VAL(BLE_MESH_DEBUG_NET) +#include "host/ble_hs_log.h" + +#include "crypto.h" +#include "adv.h" +#include "mesh_priv.h" +#include "net.h" +#include "lpn.h" +#include "friend.h" +#include "proxy.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "beacon.h" +#include "settings.h" +#include "prov.h" + +/* Minimum valid Mesh Network PDU length. The Network headers + * themselves take up 9 bytes. After that there is a minumum of 1 byte + * payload for both CTL=1 and CTL=0 PDUs (smallest OpCode is 1 byte). CTL=1 + * PDUs must use a 64-bit (8 byte) NetMIC, whereas CTL=0 PDUs have at least + * a 32-bit (4 byte) NetMIC and AppMIC giving again a total of 8 bytes. + */ +#define BT_MESH_NET_MIN_PDU_LEN (BT_MESH_NET_HDR_LEN + 1 + 8) + +/* Seq limit after IV Update is triggered */ +#define IV_UPDATE_SEQ_LIMIT 8000000 + +#define IVI(pdu) ((pdu)[0] >> 7) +#define NID(pdu) ((pdu)[0] & 0x7f) +#define CTL(pdu) ((pdu)[1] >> 7) +#define TTL(pdu) ((pdu)[1] & 0x7f) +#define SEQ(pdu) (((u32_t)(pdu)[2] << 16) | \ + ((u32_t)(pdu)[3] << 8) | (u32_t)(pdu)[4]); +#define SRC(pdu) (sys_get_be16(&(pdu)[5])) +#define DST(pdu) (sys_get_be16(&(pdu)[7])) + +/* Determine how many friendship credentials we need */ +#if (MYNEWT_VAL(BLE_MESH_FRIEND)) +#define FRIEND_CRED_COUNT MYNEWT_VAL(BLE_MESH_FRIEND_LPN_COUNT) +#elif (MYNEWT_VAL(BLE_MESH_LOW_POWER)) +#define FRIEND_CRED_COUNT MYNEWT_VAL(BLE_MESH_SUBNET_COUNT) +#else +#define FRIEND_CRED_COUNT 0 +#endif + +#if FRIEND_CRED_COUNT > 0 +static struct friend_cred friend_cred[FRIEND_CRED_COUNT]; +#endif + +static u64_t msg_cache[MYNEWT_VAL(BLE_MESH_MSG_CACHE_SIZE)]; +static u16_t msg_cache_next; + +/* Singleton network context (the implementation only supports one) */ +struct bt_mesh_net bt_mesh = { + .local_queue = STAILQ_HEAD_INITIALIZER(bt_mesh.local_queue), + .sub = { + [0 ... (MYNEWT_VAL(BLE_MESH_SUBNET_COUNT) - 1)] = { + .net_idx = BT_MESH_KEY_UNUSED, + } + }, + .app_keys = { + [0 ... (MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT) - 1)] = { + .net_idx = BT_MESH_KEY_UNUSED, + } + }, +}; + +static u32_t dup_cache[4]; +static int dup_cache_next; + +static bool check_dup(struct os_mbuf *data) +{ + const u8_t *tail = net_buf_simple_tail(data); + u32_t val; + int i; + + val = sys_get_be32(tail - 4) ^ sys_get_be32(tail - 8); + + for (i = 0; i < ARRAY_SIZE(dup_cache); i++) { + if (dup_cache[i] == val) { + return true; + } + } + + dup_cache[dup_cache_next++] = val; + dup_cache_next %= ARRAY_SIZE(dup_cache); + + return false; +} + +static u64_t msg_hash(struct bt_mesh_net_rx *rx, struct os_mbuf *pdu) +{ + u32_t hash1, hash2; + + /* Three least significant bytes of IVI + first byte of SEQ */ + hash1 = (BT_MESH_NET_IVI_RX(rx) << 8) | pdu->om_data[2]; + + /* Two last bytes of SEQ + SRC */ + memcpy(&hash2, &pdu->om_data[3], 4); + + return (u64_t)hash1 << 32 | (u64_t)hash2; +} + +static bool msg_cache_match(struct bt_mesh_net_rx *rx, + struct os_mbuf *pdu) +{ + u64_t hash = msg_hash(rx, pdu); + u16_t i; + + for (i = 0; i < ARRAY_SIZE(msg_cache); i++) { + if (msg_cache[i] == hash) { + return true; + } + } + + /* Add to the cache */ + msg_cache[msg_cache_next++] = hash; + msg_cache_next %= ARRAY_SIZE(msg_cache); + + return false; +} + +struct bt_mesh_subnet *bt_mesh_subnet_get(u16_t net_idx) +{ + int i; + + if (net_idx == BT_MESH_KEY_ANY) { + return &bt_mesh.sub[0]; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx == net_idx) { + return &bt_mesh.sub[i]; + } + } + + return NULL; +} + +int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys, + const u8_t key[16]) +{ + u8_t p[] = { 0 }; + u8_t nid; + int err; + + err = bt_mesh_k2(key, p, sizeof(p), &nid, keys->enc, keys->privacy); + if (err) { + BT_ERR("Unable to generate NID, EncKey & PrivacyKey"); + return err; + } + + memcpy(keys->net, key, 16); + + keys->nid = nid; + + BT_DBG("NID 0x%02x EncKey %s", keys->nid, bt_hex(keys->enc, 16)); + BT_DBG("PrivacyKey %s", bt_hex(keys->privacy, 16)); + + err = bt_mesh_k3(key, keys->net_id); + if (err) { + BT_ERR("Unable to generate Net ID"); + return err; + } + + BT_DBG("NetID %s", bt_hex(keys->net_id, 8)); + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + err = bt_mesh_identity_key(key, keys->identity); + if (err) { + BT_ERR("Unable to generate IdentityKey"); + return err; + } + + BT_DBG("IdentityKey %s", bt_hex(keys->identity, 16)); +#endif /* GATT_PROXY */ + + err = bt_mesh_beacon_key(key, keys->beacon); + if (err) { + BT_ERR("Unable to generate beacon key"); + return err; + } + + BT_DBG("BeaconKey %s", bt_hex(keys->beacon, 16)); + + return 0; +} + +#if ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) || \ + (MYNEWT_VAL(BLE_MESH_FRIEND))) +int friend_cred_set(struct friend_cred *cred, u8_t idx, const u8_t net_key[16]) +{ + u16_t lpn_addr, frnd_addr; + int err; + u8_t p[9]; + +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + if (cred->addr == bt_mesh.lpn.frnd) { + lpn_addr = bt_mesh_primary_addr(); + frnd_addr = cred->addr; + } else { + lpn_addr = cred->addr; + frnd_addr = bt_mesh_primary_addr(); + } +#else + lpn_addr = cred->addr; + frnd_addr = bt_mesh_primary_addr(); +#endif + + BT_DBG("LPNAddress 0x%04x FriendAddress 0x%04x", lpn_addr, frnd_addr); + BT_DBG("LPNCounter 0x%04x FriendCounter 0x%04x", cred->lpn_counter, + cred->frnd_counter); + + p[0] = 0x01; + sys_put_be16(lpn_addr, p + 1); + sys_put_be16(frnd_addr, p + 3); + sys_put_be16(cred->lpn_counter, p + 5); + sys_put_be16(cred->frnd_counter, p + 7); + + err = bt_mesh_k2(net_key, p, sizeof(p), &cred->cred[idx].nid, + cred->cred[idx].enc, cred->cred[idx].privacy); + if (err) { + BT_ERR("Unable to generate NID, EncKey & PrivacyKey"); + return err; + } + + BT_DBG("Friend NID 0x%02x EncKey %s", cred->cred[idx].nid, + bt_hex(cred->cred[idx].enc, 16)); + BT_DBG("Friend PrivacyKey %s", bt_hex(cred->cred[idx].privacy, 16)); + + return 0; +} + +void friend_cred_refresh(u16_t net_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->addr != BT_MESH_ADDR_UNASSIGNED && + cred->net_idx == net_idx) { + memcpy(&cred->cred[0], &cred->cred[1], + sizeof(cred->cred[0])); + } + } +} + +int friend_cred_update(struct bt_mesh_subnet *sub) +{ + int err, i; + + BT_DBG("net_idx 0x%04x", sub->net_idx); + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->addr == BT_MESH_ADDR_UNASSIGNED || + cred->net_idx != sub->net_idx) { + continue; + } + + err = friend_cred_set(cred, 1, sub->keys[1].net); + if (err) { + return err; + } + } + + return 0; +} + +struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, u16_t addr, + u16_t lpn_counter, u16_t frnd_counter) +{ + struct friend_cred *cred; + int i, err; + + BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr); + + for (cred = NULL, i = 0; i < ARRAY_SIZE(friend_cred); i++) { + if ((friend_cred[i].addr == BT_MESH_ADDR_UNASSIGNED) || + (friend_cred[i].addr == addr && + friend_cred[i].net_idx == sub->net_idx)) { + cred = &friend_cred[i]; + break; + } + } + + if (!cred) { + BT_WARN("No free friend credential slots"); + return NULL; + } + + cred->net_idx = sub->net_idx; + cred->addr = addr; + cred->lpn_counter = lpn_counter; + cred->frnd_counter = frnd_counter; + + err = friend_cred_set(cred, 0, sub->keys[0].net); + if (err) { + friend_cred_clear(cred); + return NULL; + } + + if (sub->kr_flag) { + err = friend_cred_set(cred, 1, sub->keys[1].net); + if (err) { + friend_cred_clear(cred); + return NULL; + } + } + + return cred; +} + +void friend_cred_clear(struct friend_cred *cred) +{ + cred->net_idx = BT_MESH_KEY_UNUSED; + cred->addr = BT_MESH_ADDR_UNASSIGNED; + cred->lpn_counter = 0; + cred->frnd_counter = 0; + memset(cred->cred, 0, sizeof(cred->cred)); +} + +int friend_cred_del(u16_t net_idx, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->addr == addr && cred->net_idx == net_idx) { + friend_cred_clear(cred); + return 0; + } + } + + return -ENOENT; +} + +int friend_cred_get(struct bt_mesh_subnet *sub, u16_t addr, u8_t *nid, + const u8_t **enc, const u8_t **priv) +{ + int i; + + BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr); + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->net_idx != sub->net_idx) { + continue; + } + + if (addr != BT_MESH_ADDR_UNASSIGNED && cred->addr != addr) { + continue; + } + + if (nid) { + *nid = cred->cred[sub->kr_flag].nid; + } + + if (enc) { + *enc = cred->cred[sub->kr_flag].enc; + } + + if (priv) { + *priv = cred->cred[sub->kr_flag].privacy; + } + + return 0; + } + + return -ENOENT; +} +#else +int friend_cred_get(struct bt_mesh_subnet *sub, u16_t addr, u8_t *nid, + const u8_t **enc, const u8_t **priv) +{ + return -ENOENT; +} +#endif /* FRIEND || LOW_POWER */ + +u8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub) +{ + u8_t flags = 0x00; + + if (sub && sub->kr_flag) { + flags |= BT_MESH_NET_FLAG_KR; + } + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) { + flags |= BT_MESH_NET_FLAG_IVU; + } + + return flags; +} + +int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub) +{ + u8_t flags = bt_mesh_net_flags(sub); + struct bt_mesh_subnet_keys *keys; + + if (sub->kr_flag) { + BT_DBG("NetIndex %u Using new key", sub->net_idx); + keys = &sub->keys[1]; + } else { + BT_DBG("NetIndex %u Using current key", sub->net_idx); + keys = &sub->keys[0]; + } + + BT_DBG("flags 0x%02x, IVI 0x%08x", flags, (unsigned) bt_mesh.iv_index); + + return bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, + bt_mesh.iv_index, sub->auth); +} + +int bt_mesh_net_create(u16_t idx, u8_t flags, const u8_t key[16], + u32_t iv_index) +{ + struct bt_mesh_subnet *sub; + int err; + + BT_DBG("idx %u flags 0x%02x iv_index %u", idx, flags, + (unsigned) iv_index); + + BT_DBG("NetKey %s", bt_hex(key, 16)); + + (void)memset(msg_cache, 0, sizeof(msg_cache)); + msg_cache_next = 0U; + + sub = &bt_mesh.sub[0]; + + sub->kr_flag = BT_MESH_KEY_REFRESH(flags); + if (sub->kr_flag) { + err = bt_mesh_net_keys_create(&sub->keys[1], key); + if (err) { + return -EIO; + } + + sub->kr_phase = BT_MESH_KR_PHASE_2; + } else { + err = bt_mesh_net_keys_create(&sub->keys[0], key); + if (err) { + return -EIO; + } + } + + sub->net_idx = idx; + + if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) { + sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED; + } else { + sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED; + } + + bt_mesh.iv_index = iv_index; + atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, + BT_MESH_IV_UPDATE(flags)); + + /* Set minimum required hours, since the 96-hour minimum requirement + * doesn't apply straight after provisioning (since we can't know how + * long has actually passed since the network changed its state). + */ + bt_mesh.ivu_duration = BT_MESH_IVU_MIN_HOURS; + + /* Make sure we have valid beacon data to be sent */ + bt_mesh_net_beacon_update(sub); + + return 0; +} + +void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub) +{ + int i; + + BT_DBG("idx 0x%04x", sub->net_idx); + + memcpy(&sub->keys[0], &sub->keys[1], sizeof(sub->keys[0])); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx != sub->net_idx || !key->updated) { + continue; + } + + memcpy(&key->keys[0], &key->keys[1], sizeof(key->keys[0])); + key->updated = false; + } +} + +bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, u8_t new_kr, bool new_key) +{ + if (new_kr != sub->kr_flag && sub->kr_phase == BT_MESH_KR_NORMAL) { + BT_WARN("KR change in normal operation. Are we blacklisted?"); + return false; + } + + sub->kr_flag = new_kr; + + if (sub->kr_flag) { + if (sub->kr_phase == BT_MESH_KR_PHASE_1) { + BT_DBG("Phase 1 -> Phase 2"); + sub->kr_phase = BT_MESH_KR_PHASE_2; + return true; + } + } else { + switch (sub->kr_phase) { + case BT_MESH_KR_PHASE_1: + if (!new_key) { + /* Ignore */ + break; + } + /* Upon receiving a Secure Network beacon with the KR flag set + * to 0 using the new NetKey in Phase 1, the node shall + * immediately transition to Phase 3, which effectively skips + * Phase 2. + * + */ + /* falls through */ + case BT_MESH_KR_PHASE_2: + BT_DBG("KR Phase 0x%02x -> Normal", sub->kr_phase); + bt_mesh_net_revoke_keys(sub); + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) || + (MYNEWT_VAL(BLE_MESH_FRIEND))) { + friend_cred_refresh(sub->net_idx); + } + sub->kr_phase = BT_MESH_KR_NORMAL; + return true; + } + } + + return false; +} + +void bt_mesh_rpl_reset(void) +{ + int i; + + /* Discard "old old" IV Index entries from RPL and flag + * any other ones (which are valid) as old. + */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i]; + + if (rpl->src) { + if (rpl->old_iv) { + memset(rpl, 0, sizeof(*rpl)); + } else { + rpl->old_iv = true; + } + } + } +} + +#if MYNEWT_VAL(BLE_MESH_IV_UPDATE_TEST) +void bt_mesh_iv_update_test(bool enable) +{ + atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_TEST, enable); + /* Reset the duration variable - needed for some PTS tests */ + bt_mesh.ivu_duration = 0; +} + +bool bt_mesh_iv_update(void) +{ + if (!bt_mesh_is_provisioned()) { + BT_ERR("Not yet provisioned"); + return false; + } + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) { + bt_mesh_net_iv_update(bt_mesh.iv_index, false); + } else { + bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true); + } + + bt_mesh_net_sec_update(NULL); + + return atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS); +} +#endif /* CONFIG_BT_MESH_IV_UPDATE_TEST */ + +/* Used for sending immediate beacons to Friend queues and GATT clients */ +void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub) +{ + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + bt_mesh_friend_sec_update(sub ? sub->net_idx : BT_MESH_KEY_ANY); + } + + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && + bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) { + bt_mesh_proxy_beacon_send(sub); + } +} + +bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update) +{ + int i; + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) { + /* We're currently in IV Update mode */ + + if (iv_index != bt_mesh.iv_index) { + BT_WARN("IV Index mismatch: 0x%08x != 0x%08x", + (unsigned) iv_index, + (unsigned) bt_mesh.iv_index); + return false; + } + + if (iv_update) { + /* Nothing to do */ + BT_DBG("Already in IV Update in Progress state"); + return false; + } + } else { + /* We're currently in Normal mode */ + + if (iv_index == bt_mesh.iv_index) { + BT_DBG("Same IV Index in normal mode"); + return false; + } + + if (iv_index < bt_mesh.iv_index || + iv_index > bt_mesh.iv_index + 42) { + BT_ERR("IV Index out of sync: 0x%08x != 0x%08x", + (unsigned) iv_index, + (unsigned) bt_mesh.iv_index); + return false; + } + + if (iv_index > bt_mesh.iv_index + 1) { + BT_WARN("Performing IV Index Recovery"); + memset(bt_mesh.rpl, 0, sizeof(bt_mesh.rpl)); + bt_mesh.iv_index = iv_index; + bt_mesh.seq = 0; + goto do_update; + } + + if (iv_index == bt_mesh.iv_index + 1 && !iv_update) { + BT_WARN("Ignoring new index in normal mode"); + return false; + } + + if (!iv_update) { + /* Nothing to do */ + BT_DBG("Already in Normal state"); + return false; + } + } + + if (!(IS_ENABLED(CONFIG_BT_MESH_IV_UPDATE_TEST) && + atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_TEST))) { + if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) { + BT_WARN("IV Update before minimum duration"); + return false; + } + } + + /* Defer change to Normal Operation if there are pending acks */ + if (!iv_update && bt_mesh_tx_in_progress()) { + BT_WARN("IV Update deferred because of pending transfer"); + atomic_set_bit(bt_mesh.flags, BT_MESH_IVU_PENDING); + return false; + } + +do_update: + atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv_update); + bt_mesh.ivu_duration = 0U; + + if (iv_update) { + bt_mesh.iv_index = iv_index; + BT_DBG("IV Update state entered. New index 0x%08x", + (unsigned) bt_mesh.iv_index); + + bt_mesh_rpl_reset(); + } else { + BT_DBG("Normal mode entered"); + bt_mesh.seq = 0; + } + + k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) { + bt_mesh_net_beacon_update(&bt_mesh.sub[i]); + } + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_iv(false); + } + + return true; +} + +u32_t bt_mesh_next_seq(void) +{ + u32_t seq = bt_mesh.seq++; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_seq(); + } + + return seq; +} + +int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct os_mbuf *buf, + bool new_key, const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + const u8_t *enc, *priv; + u32_t seq; + int err; + + BT_DBG("net_idx 0x%04x new_key %u len %u", sub->net_idx, new_key, + buf->om_len); + + enc = sub->keys[new_key].enc; + priv = sub->keys[new_key].privacy; + + err = bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv); + if (err) { + BT_ERR("deobfuscate failed (err %d)", err); + return err; + } + + err = bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_TX, false); + if (err) { + BT_ERR("decrypt failed (err %d)", err); + return err; + } + + seq = bt_mesh_next_seq(); + buf->om_data[2] = seq >> 16; + buf->om_data[3] = seq >> 8; + buf->om_data[4] = seq; + + err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, false); + if (err) { + BT_ERR("encrypt failed (err %d)", err); + return err; + } + + err = bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv); + if (err) { + BT_ERR("obfuscate failed (err %d)", err); + return err; + } + + bt_mesh_adv_send(buf, cb, cb_data); + + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) && + bt_mesh.seq > IV_UPDATE_SEQ_LIMIT) { + bt_mesh_beacon_ivu_initiator(true); + bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true); + bt_mesh_net_sec_update(NULL); + } + + return 0; +} + +static void bt_mesh_net_local(struct ble_npl_event *work) +{ + struct os_mbuf *buf; + + while ((buf = net_buf_slist_get(&bt_mesh.local_queue))) { + BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + bt_mesh_net_recv(buf, 0, BT_MESH_NET_IF_LOCAL); + net_buf_unref(buf); + } +} + +int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct os_mbuf *buf, + bool proxy) +{ + const bool ctl = (tx->ctx->app_idx == BT_MESH_KEY_UNUSED); + u32_t seq_val; + u8_t nid; + const u8_t *enc, *priv; + u8_t *seq; + int err; + + if (ctl && net_buf_simple_tailroom(buf) < 8) { + BT_ERR("Insufficient MIC space for CTL PDU"); + return -EINVAL; + } else if (net_buf_simple_tailroom(buf) < 4) { + BT_ERR("Insufficient MIC space for PDU"); + return -EINVAL; + } + + BT_DBG("src 0x%04x dst 0x%04x ctl %u seq 0x%06x", + tx->src, tx->ctx->addr, ctl, bt_mesh.seq); + + net_buf_simple_push_be16(buf, tx->ctx->addr); + net_buf_simple_push_be16(buf, tx->src); + + seq = net_buf_simple_push(buf, 3); + seq_val = bt_mesh_next_seq(); + seq[0] = seq_val >> 16; + seq[1] = seq_val >> 8; + seq[2] = seq_val; + + if (ctl) { + net_buf_simple_push_u8(buf, tx->ctx->send_ttl | 0x80); + } else { + net_buf_simple_push_u8(buf, tx->ctx->send_ttl); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && tx->friend_cred) { + if (friend_cred_get(tx->sub, BT_MESH_ADDR_UNASSIGNED, + &nid, &enc, &priv)) { + BT_WARN("Falling back to master credentials"); + + tx->friend_cred = 0; + + nid = tx->sub->keys[tx->sub->kr_flag].nid; + enc = tx->sub->keys[tx->sub->kr_flag].enc; + priv = tx->sub->keys[tx->sub->kr_flag].privacy; + } + } else { + tx->friend_cred = 0; + nid = tx->sub->keys[tx->sub->kr_flag].nid; + enc = tx->sub->keys[tx->sub->kr_flag].enc; + priv = tx->sub->keys[tx->sub->kr_flag].privacy; + } + + net_buf_simple_push_u8(buf, (nid | (BT_MESH_NET_IVI_TX & 1) << 7)); + + err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, proxy); + if (err) { + return err; + } + + return bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv); +} + +int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + int err; + + BT_DBG("src 0x%04x dst 0x%04x len %u headroom %zu tailroom %zu", + tx->src, tx->ctx->addr, buf->om_len, net_buf_headroom(buf), + net_buf_tailroom(buf)); + BT_DBG("Payload len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + BT_DBG("Seq 0x%06x", bt_mesh.seq); + + if (tx->ctx->send_ttl == BT_MESH_TTL_DEFAULT) { + tx->ctx->send_ttl = bt_mesh_default_ttl_get(); + } + + err = bt_mesh_net_encode(tx, buf, false); + if (err) { + goto done; + } + + BT_DBG("encoded %u bytes: %s", buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + /* Deliver to GATT Proxy Clients if necessary. Mesh spec 3.4.5.2: + * "The output filter of the interface connected to advertising or + * GATT bearers shall drop all messages with TTL value set to 1." + */ + if (MYNEWT_VAL(BLE_MESH_GATT_PROXY) && + tx->ctx->send_ttl != 1) { + if (bt_mesh_proxy_relay(buf, tx->ctx->addr) && + BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) { + /* Notify completion if this only went + * through the Mesh Proxy. + */ + if (cb) { + if (cb->start) { + cb->start(0, 0, cb_data); + } + + if (cb->end) { + cb->end(0, cb_data); + } + } + + err = 0; + goto done; + } + } + + /* Deliver to local network interface if necessary */ + if (bt_mesh_fixed_group_match(tx->ctx->addr) || + bt_mesh_elem_find(tx->ctx->addr)) { + if (cb && cb->start) { + cb->start(0, 0, cb_data); + } + net_buf_slist_put(&bt_mesh.local_queue, net_buf_ref(buf)); + if (cb && cb->end) { + cb->end(0, cb_data); + } + k_work_submit(&bt_mesh.local_work); + } else if (tx->ctx->send_ttl != 1) { + /* Deliver to to the advertising network interface. Mesh spec + * 3.4.5.2: "The output filter of the interface connected to + * advertising or GATT bearers shall drop all messages with + * TTL value set to 1." + */ + bt_mesh_adv_send(buf, cb, cb_data); + } + +done: + net_buf_unref(buf); + return err; +} + +static bool auth_match(struct bt_mesh_subnet_keys *keys, + const u8_t net_id[8], u8_t flags, + u32_t iv_index, const u8_t auth[8]) +{ + u8_t net_auth[8]; + + if (memcmp(net_id, keys->net_id, 8)) { + return false; + } + + bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, iv_index, + net_auth); + + if (memcmp(auth, net_auth, 8)) { + BT_WARN("Authentication Value %s != %s", + bt_hex(auth, 8), bt_hex(net_auth, 8)); + return false; + } + + return true; +} + +struct bt_mesh_subnet *bt_mesh_subnet_find(const u8_t net_id[8], u8_t flags, + u32_t iv_index, const u8_t auth[8], + bool *new_key) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (auth_match(&sub->keys[0], net_id, flags, iv_index, auth)) { + *new_key = false; + return sub; + } + + if (sub->kr_phase == BT_MESH_KR_NORMAL) { + continue; + } + + if (auth_match(&sub->keys[1], net_id, flags, iv_index, auth)) { + *new_key = true; + return sub; + } + } + + return NULL; +} + +static int net_decrypt(struct bt_mesh_subnet *sub, const u8_t *enc, + const u8_t *priv, const u8_t *data, + size_t data_len, struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx); + BT_DBG("IVI %u net->iv_index 0x%08x", IVI(data), + (unsigned) bt_mesh.iv_index); + + rx->old_iv = (IVI(data) != (bt_mesh.iv_index & 0x01)); + + net_buf_simple_init(buf, 0); + memcpy(net_buf_simple_add(buf, data_len), data, data_len); + + if (bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_RX(rx), priv)) { + return -ENOENT; + } + + if (rx->net_if == BT_MESH_NET_IF_ADV && msg_cache_match(rx, buf)) { + BT_WARN("Duplicate found in Network Message Cache"); + return -EALREADY; + } + + rx->ctx.addr = SRC(buf->om_data); + if (!BT_MESH_ADDR_IS_UNICAST(rx->ctx.addr)) { + BT_WARN("Ignoring non-unicast src addr 0x%04x", rx->ctx.addr); + return -EINVAL; + } + + BT_DBG("src 0x%04x", rx->ctx.addr); + + if ((MYNEWT_VAL(BLE_MESH_PROXY)) && + rx->net_if == BT_MESH_NET_IF_PROXY_CFG) { + return bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), + true); + } + + return bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), false); +} + +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER) || \ + MYNEWT_VAL(BLE_MESH_FRIEND)) +static int friend_decrypt(struct bt_mesh_subnet *sub, const u8_t *data, + size_t data_len, struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + int i; + + BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx); + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->net_idx != sub->net_idx) { + continue; + } + + if (NID(data) == cred->cred[0].nid && + !net_decrypt(sub, cred->cred[0].enc, cred->cred[0].privacy, + data, data_len, rx, buf)) { + return 0; + } + + if (sub->kr_phase == BT_MESH_KR_NORMAL) { + continue; + } + + if (NID(data) == cred->cred[1].nid && + !net_decrypt(sub, cred->cred[1].enc, cred->cred[1].privacy, + data, data_len, rx, buf)) { + rx->new_key = 1; + return 0; + } + } + + return -ENOENT; +} +#endif + +static bool net_find_and_decrypt(const u8_t *data, size_t data_len, + struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + unsigned int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + sub = &bt_mesh.sub[i]; + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER) || \ + MYNEWT_VAL(BLE_MESH_FRIEND)) + if (!friend_decrypt(sub, data, data_len, rx, buf)) { + rx->friend_cred = 1; + rx->ctx.net_idx = sub->net_idx; + rx->sub = sub; + return true; + } +#endif + + if (NID(data) == sub->keys[0].nid && + !net_decrypt(sub, sub->keys[0].enc, sub->keys[0].privacy, + data, data_len, rx, buf)) { + rx->ctx.net_idx = sub->net_idx; + rx->sub = sub; + return true; + } + + if (sub->kr_phase == BT_MESH_KR_NORMAL) { + continue; + } + + if (NID(data) == sub->keys[1].nid && + !net_decrypt(sub, sub->keys[1].enc, sub->keys[1].privacy, + data, data_len, rx, buf)) { + rx->new_key = 1; + rx->ctx.net_idx = sub->net_idx; + rx->sub = sub; + return true; + } + } + + return false; +} + +/* Relaying from advertising to the advertising bearer should only happen + * if the Relay state is set to enabled. Locally originated packets always + * get sent to the advertising bearer. If the packet came in through GATT, + * then we should only relay it if the GATT Proxy state is enabled. + */ +static bool relay_to_adv(enum bt_mesh_net_if net_if) +{ + switch (net_if) { + case BT_MESH_NET_IF_LOCAL: + return true; + case BT_MESH_NET_IF_ADV: + return (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED); + case BT_MESH_NET_IF_PROXY: + return (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED); + default: + return false; + } +} + +static void bt_mesh_net_relay(struct os_mbuf *sbuf, + struct bt_mesh_net_rx *rx) +{ + const u8_t *enc, *priv; + struct os_mbuf *buf; + u8_t nid, transmit; + + if (rx->net_if == BT_MESH_NET_IF_LOCAL) { + /* Locally originated PDUs with TTL=1 will only be delivered + * to local elements as per Mesh Profile 1.0 section 3.4.5.2: + * "The output filter of the interface connected to + * advertising or GATT bearers shall drop all messages with + * TTL value set to 1." + */ + if (rx->ctx.recv_ttl == 1) { + return; + } + } else { + if (rx->ctx.recv_ttl <= 1) { + return; + } + } + + if (rx->net_if == BT_MESH_NET_IF_ADV && + bt_mesh_relay_get() != BT_MESH_RELAY_ENABLED && + bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_ENABLED) { + return; + } + + BT_DBG("TTL %u CTL %u dst 0x%04x", rx->ctx.recv_ttl, rx->ctl, + rx->ctx.recv_dst); + + /* The Relay Retransmit state is only applied to adv-adv relaying. + * Anything else (like GATT to adv, or locally originated packets) + * use the Network Transmit state. + */ + if (rx->net_if == BT_MESH_NET_IF_ADV) { + transmit = bt_mesh_relay_retransmit_get(); + } else { + transmit = bt_mesh_net_transmit_get(); + } + + buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, transmit, K_NO_WAIT); + if (!buf) { + BT_ERR("Out of relay buffers"); + return; + } + + /* Only decrement TTL for non-locally originated packets */ + if (rx->net_if != BT_MESH_NET_IF_LOCAL) { + /* Leave CTL bit intact */ + sbuf->om_data[1] &= 0x80; + sbuf->om_data[1] |= rx->ctx.recv_ttl - 1; + } + + net_buf_add_mem(buf, sbuf->om_data, sbuf->om_len); + + enc = rx->sub->keys[rx->sub->kr_flag].enc; + priv = rx->sub->keys[rx->sub->kr_flag].privacy; + nid = rx->sub->keys[rx->sub->kr_flag].nid; + + BT_DBG("Relaying packet. TTL is now %u", TTL(buf->om_data)); + + /* Update NID if RX or RX was with friend credentials */ + if (rx->friend_cred) { + buf->om_data[0] &= 0x80; /* Clear everything except IVI */ + buf->om_data[0] |= nid; + } + + /* We re-encrypt and obfuscate using the received IVI rather than + * the normal TX IVI (which may be different) since the transport + * layer nonce includes the IVI. + */ + if (bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), false)) { + BT_ERR("Re-encrypting failed"); + goto done; + } + + if (bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_RX(rx), priv)) { + BT_ERR("Re-obfuscating failed"); + goto done; + } + + BT_DBG("encoded %u bytes: %s", buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + /* Sending to the GATT bearer should only happen if GATT Proxy + * is enabled or the message originates from the local node. + */ + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && + (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED || + rx->net_if == BT_MESH_NET_IF_LOCAL)) { + if (bt_mesh_proxy_relay(buf, rx->ctx.recv_dst) && + BT_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) { + goto done; + } + } + + if (relay_to_adv(rx->net_if)) { + bt_mesh_adv_send(buf, NULL, NULL); + } + +done: + net_buf_unref(buf); +} + +int bt_mesh_net_decode(struct os_mbuf *data, enum bt_mesh_net_if net_if, + struct bt_mesh_net_rx *rx, struct os_mbuf *buf) +{ + if (data->om_len < BT_MESH_NET_MIN_PDU_LEN) { + BT_WARN("Dropping too short mesh packet (len %u)", data->om_len); + BT_WARN("%s", bt_hex(data->om_data, data->om_len)); + return -EINVAL; + } + + if (net_if == BT_MESH_NET_IF_ADV && check_dup(data)) { + BT_DBG("duplicate packet; dropping %u bytes: %s", data->om_len, + bt_hex(data->om_data, data->om_len)); + return -EINVAL; + } + + BT_DBG("%u bytes: %s", data->om_len, bt_hex(data->om_data, data->om_len)); + + rx->net_if = net_if; + + if (!net_find_and_decrypt(data->om_data, data->om_len, rx, buf)) { + BT_DBG("Unable to find matching net for packet"); + return -ENOENT; + } + + /* Initialize AppIdx to a sane value */ + rx->ctx.app_idx = BT_MESH_KEY_UNUSED; + + rx->ctx.recv_ttl = TTL(buf->om_data); + + /* Default to responding with TTL 0 for non-routed messages */ + if (rx->ctx.recv_ttl == 0) { + rx->ctx.send_ttl = 0; + } else { + rx->ctx.send_ttl = BT_MESH_TTL_DEFAULT; + } + + rx->ctl = CTL(buf->om_data); + rx->seq = SEQ(buf->om_data); + rx->ctx.recv_dst = DST(buf->om_data); + + BT_DBG("Decryption successful. Payload len %u: %s", buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (net_if != BT_MESH_NET_IF_PROXY_CFG && + rx->ctx.recv_dst == BT_MESH_ADDR_UNASSIGNED) { + BT_ERR("Destination address is unassigned; dropping packet"); + return -EBADMSG; + } + + if (BT_MESH_ADDR_IS_RFU(rx->ctx.recv_dst)) { + BT_ERR("Destination address is RFU; dropping packet"); + return -EBADMSG; + } + + if (net_if != BT_MESH_NET_IF_LOCAL && bt_mesh_elem_find(rx->ctx.addr)) { + BT_DBG("Dropping locally originated packet"); + return -EBADMSG; + } + + BT_DBG("src 0x%04x dst 0x%04x ttl %u", rx->ctx.addr, rx->ctx.recv_dst, + rx->ctx.recv_ttl); + BT_DBG("PDU: %s", bt_hex(buf->om_data, buf->om_len)); + + return 0; +} + +void bt_mesh_net_recv(struct os_mbuf *data, s8_t rssi, + enum bt_mesh_net_if net_if) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(29); + struct bt_mesh_net_rx rx = { .rssi = rssi }; + struct net_buf_simple_state state; + + BT_DBG("rssi %d net_if %u", rssi, net_if); + + if (!bt_mesh_is_provisioned()) { + BT_ERR("Not provisioned; dropping packet"); + goto done; + } + + if (bt_mesh_net_decode(data, net_if, &rx, buf)) { + goto done; + } + + /* Save the state so the buffer can later be relayed */ + net_buf_simple_save(buf, &state); + + if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY)) && + net_if == BT_MESH_NET_IF_PROXY) { + bt_mesh_proxy_addr_add(data, rx.ctx.addr); + } + + rx.local_match = (bt_mesh_fixed_group_match(rx.ctx.recv_dst) || + bt_mesh_elem_find(rx.ctx.recv_dst)); + + bt_mesh_trans_recv(buf, &rx); + + /* Relay if this was a group/virtual address, or if the destination + * was neither a local element nor an LPN we're Friends for. + */ + if (!BT_MESH_ADDR_IS_UNICAST(rx.ctx.recv_dst) || + (!rx.local_match && !rx.friend_match)) { + net_buf_simple_restore(buf, &state); + bt_mesh_net_relay(buf, &rx); + } + +done: + os_mbuf_free_chain(buf); +} + +static void ivu_refresh(struct ble_npl_event *work) +{ + bt_mesh.ivu_duration += BT_MESH_IVU_HOURS; + + BT_DBG("%s for %u hour%s", + atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ? + "IVU in Progress" : "IVU Normal mode", + bt_mesh.ivu_duration, bt_mesh.ivu_duration == 1 ? "" : "s"); + + if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) { + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_iv(true); + } + + k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT); + return; + } + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) { + bt_mesh_beacon_ivu_initiator(true); + bt_mesh_net_iv_update(bt_mesh.iv_index, false); + } else if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_iv(true); + } +} + +void bt_mesh_net_start(void) +{ + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) { + bt_mesh_beacon_enable(); + } else { + bt_mesh_beacon_disable(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && + bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_NOT_SUPPORTED) { + bt_mesh_proxy_gatt_enable(); + bt_mesh_adv_update(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_init(); + } else { + bt_mesh_scan_enable(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + bt_mesh_friend_init(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PROV)) { + u16_t net_idx = bt_mesh.sub[0].net_idx; + u16_t addr = bt_mesh_primary_addr(); + + bt_mesh_prov_complete(net_idx, addr); + } +} + +void bt_mesh_net_init(void) +{ + k_delayed_work_init(&bt_mesh.ivu_timer, ivu_refresh); + + k_work_init(&bt_mesh.local_work, bt_mesh_net_local); + net_buf_slist_init(&bt_mesh.local_queue); +} diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.h new file mode 100644 index 000000000..e55102c06 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.h @@ -0,0 +1,375 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __NET_H__ +#define __NET_H__ + +#define BT_MESH_NET_FLAG_KR BIT(0) +#define BT_MESH_NET_FLAG_IVU BIT(1) + +#define BT_MESH_KR_NORMAL 0x00 +#define BT_MESH_KR_PHASE_1 0x01 +#define BT_MESH_KR_PHASE_2 0x02 +#define BT_MESH_KR_PHASE_3 0x03 + +#define BT_MESH_IV_UPDATE(flags) ((flags >> 1) & 0x01) +#define BT_MESH_KEY_REFRESH(flags) (flags & 0x01) + +#include +#include "atomic.h" +#include "mesh/mesh.h" +#include "mesh/glue.h" + +/* How many hours in between updating IVU duration */ +#define BT_MESH_IVU_MIN_HOURS 96 +#define BT_MESH_IVU_HOURS (BT_MESH_IVU_MIN_HOURS / \ + CONFIG_BT_MESH_IVU_DIVIDER) +#define BT_MESH_IVU_TIMEOUT K_HOURS(BT_MESH_IVU_HOURS) + +struct bt_mesh_app_key { + u16_t net_idx; + u16_t app_idx; + bool updated; + struct bt_mesh_app_keys { + u8_t id; + u8_t val[16]; + } keys[2]; +}; + +struct bt_mesh_subnet { + u32_t beacon_sent; /* Timestamp of last sent beacon */ + u8_t beacons_last; /* Number of beacons during last + * observation window + */ + u8_t beacons_cur; /* Number of beaconds observed during + * currently ongoing window. + */ + + u8_t beacon_cache[21]; /* Cached last authenticated beacon */ + + u16_t net_idx; /* NetKeyIndex */ + + bool kr_flag; /* Key Refresh Flag */ + u8_t kr_phase; /* Key Refresh Phase */ + + u8_t node_id; /* Node Identity State */ + u32_t node_id_start; /* Node Identity started timestamp */ + + u8_t auth[8]; /* Beacon Authentication Value */ + + struct bt_mesh_subnet_keys { + u8_t net[16]; /* NetKey */ + u8_t nid; /* NID */ + u8_t enc[16]; /* EncKey */ + u8_t net_id[8]; /* Network ID */ +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + u8_t identity[16]; /* IdentityKey */ +#endif + u8_t privacy[16]; /* PrivacyKey */ + u8_t beacon[16]; /* BeaconKey */ + } keys[2]; +}; + +struct bt_mesh_rpl { + u16_t src; + bool old_iv; +#if defined(CONFIG_BT_SETTINGS) + bool store; +#endif + u32_t seq; +}; + +#if MYNEWT_VAL(BLE_MESH_FRIEND) +#define FRIEND_SEG_RX MYNEWT_VAL(BLE_MESH_FRIEND_SEG_RX) +#define FRIEND_SUB_LIST_SIZE MYNEWT_VAL(BLE_MESH_FRIEND_SUB_LIST_SIZE) +#else +#define FRIEND_SEG_RX 0 +#define FRIEND_SUB_LIST_SIZE 0 +#endif + +struct bt_mesh_friend { + u16_t lpn; + u8_t recv_delay; + u8_t fsn:1, + send_last:1, + pending_req:1, + sec_update:1, + pending_buf:1, + valid:1, + established:1; + s32_t poll_to; + u8_t num_elem; + u16_t lpn_counter; + u16_t counter; + + u16_t net_idx; + + u16_t sub_list[FRIEND_SUB_LIST_SIZE]; + + struct k_delayed_work timer; + + struct bt_mesh_friend_seg { + struct net_buf_slist_t queue; + } seg[FRIEND_SEG_RX]; + + struct os_mbuf *last; + + struct net_buf_slist_t queue; + u32_t queue_size; + + /* Friend Clear Procedure */ + struct { + u32_t start; /* Clear Procedure start */ + u16_t frnd; /* Previous Friend's address */ + u16_t repeat_sec; /* Repeat timeout in seconds */ + struct k_delayed_work timer; /* Repeat timer */ + } clear; +}; + +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) +#define LPN_GROUPS CONFIG_BT_MESH_LPN_GROUPS +#else +#define LPN_GROUPS 0 +#endif + +/* Low Power Node state */ +struct bt_mesh_lpn { + enum __packed { + BT_MESH_LPN_DISABLED, /* LPN feature is disabled */ + BT_MESH_LPN_CLEAR, /* Clear in progress */ + BT_MESH_LPN_TIMER, /* Waiting for auto timer expiry */ + BT_MESH_LPN_ENABLED, /* LPN enabled, but no Friend */ + BT_MESH_LPN_REQ_WAIT, /* Wait before scanning for offers */ + BT_MESH_LPN_WAIT_OFFER, /* Friend Req sent */ + BT_MESH_LPN_ESTABLISHED, /* Friendship established */ + BT_MESH_LPN_RECV_DELAY, /* Poll sent, waiting ReceiveDelay */ + BT_MESH_LPN_WAIT_UPDATE, /* Waiting for Update or message */ + } state; + + /* Transaction Number (used for subscription list) */ + u8_t xact_next; + u8_t xact_pending; + u8_t sent_req; + + /* Address of our Friend when we're a LPN. Unassigned if we don't + * have a friend yet. + */ + u16_t frnd; + + /* Value from the friend offer */ + u8_t recv_win; + + u8_t req_attempts; /* Number of Request attempts */ + + s32_t poll_timeout; + + u8_t groups_changed:1, /* Friend Subscription List needs updating */ + pending_poll:1, /* Poll to be sent after subscription */ + disable:1, /* Disable LPN after clearing */ + fsn:1, /* Friend Sequence Number */ + established:1, /* Friendship established */ + clear_success:1; /* Friend Clear Confirm received */ + + /* Friend Queue Size */ + u8_t queue_size; + + /* LPNCounter */ + u16_t counter; + + /* Previous Friend of this LPN */ + u16_t old_friend; + + /* Duration reported for last advertising packet */ + u16_t adv_duration; + + /* Next LPN related action timer */ + struct k_delayed_work timer; + + /* Subscribed groups */ + u16_t groups[LPN_GROUPS]; + + /* Bit fields for tracking which groups the Friend knows about */ + ATOMIC_DEFINE(added, LPN_GROUPS); + ATOMIC_DEFINE(pending, LPN_GROUPS); + ATOMIC_DEFINE(to_remove, LPN_GROUPS); +}; + +/* bt_mesh_net.flags */ +enum { + BT_MESH_VALID, /* We have been provisioned */ + BT_MESH_SUSPENDED, /* Network is temporarily suspended */ + BT_MESH_IVU_IN_PROGRESS, /* IV Update in Progress */ + BT_MESH_IVU_INITIATOR, /* IV Update initiated by us */ + BT_MESH_IVU_TEST, /* IV Update test mode */ + BT_MESH_IVU_PENDING, /* Update blocked by SDU in progress */ + + /* pending storage actions */ + BT_MESH_RPL_PENDING, + BT_MESH_KEYS_PENDING, + BT_MESH_NET_PENDING, + BT_MESH_IV_PENDING, + BT_MESH_SEQ_PENDING, + BT_MESH_HB_PUB_PENDING, + BT_MESH_CFG_PENDING, + BT_MESH_MOD_PENDING, + + /* Don't touch - intentionally last */ + BT_MESH_FLAG_COUNT, +}; + +struct bt_mesh_net { + u32_t iv_index; /* Current IV Index */ + u32_t seq; /* Next outgoing sequence number (24 bits) */ + + ATOMIC_DEFINE(flags, BT_MESH_FLAG_COUNT); + + /* Local network interface */ + struct ble_npl_callout local_work; + struct net_buf_slist_t local_queue; + +#if MYNEWT_VAL(BLE_MESH_FRIEND) + /* Friend state, unique for each LPN that we're Friends for */ + struct bt_mesh_friend frnd[MYNEWT_VAL(BLE_MESH_FRIEND_LPN_COUNT)]; +#endif + +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + struct bt_mesh_lpn lpn; /* Low Power Node state */ +#endif + + /* Number of hours in current IV Update state */ + u8_t ivu_duration; + + /* Timer to track duration in current IV Update state */ + struct k_delayed_work ivu_timer; + + u8_t dev_key[16]; + + struct bt_mesh_app_key app_keys[MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT)]; + + struct bt_mesh_subnet sub[MYNEWT_VAL(BLE_MESH_SUBNET_COUNT)]; + + struct bt_mesh_rpl rpl[MYNEWT_VAL(BLE_MESH_CRPL)]; +}; + +/* Network interface */ +enum bt_mesh_net_if { + BT_MESH_NET_IF_ADV, + BT_MESH_NET_IF_LOCAL, + BT_MESH_NET_IF_PROXY, + BT_MESH_NET_IF_PROXY_CFG, +}; + +/* Decoding context for Network/Transport data */ +struct bt_mesh_net_rx { + struct bt_mesh_subnet *sub; + struct bt_mesh_msg_ctx ctx; + u32_t seq; /* Sequence Number */ + u8_t old_iv:1, /* iv_index - 1 was used */ + new_key:1, /* Data was encrypted with updated key */ + friend_cred:1, /* Data was encrypted with friend cred */ + ctl:1, /* Network Control */ + net_if:2, /* Network interface */ + local_match:1, /* Matched a local element */ + friend_match:1; /* Matched an LPN we're friends for */ + s8_t rssi; +}; + +/* Encoding context for Network/Transport data */ +struct bt_mesh_net_tx { + struct bt_mesh_subnet *sub; + struct bt_mesh_msg_ctx *ctx; + u16_t src; + u8_t xmit; + u8_t friend_cred:1, + aszmic:1, + aid:6; +}; + +extern struct bt_mesh_net bt_mesh; + +#define BT_MESH_NET_IVI_TX (bt_mesh.iv_index - \ + atomic_test_bit(bt_mesh.flags, \ + BT_MESH_IVU_IN_PROGRESS)) +#define BT_MESH_NET_IVI_RX(rx) (bt_mesh.iv_index - (rx)->old_iv) + +#define BT_MESH_NET_HDR_LEN 9 + +int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys, + const u8_t key[16]); + +int bt_mesh_net_create(u16_t idx, u8_t flags, const u8_t key[16], + u32_t iv_index); + +u8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub); + +bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, u8_t new_kr, bool new_key); + +void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub); + +int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub); + +void bt_mesh_rpl_reset(void); + +bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update); + +void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub); + +struct bt_mesh_subnet *bt_mesh_subnet_get(u16_t net_idx); + +struct bt_mesh_subnet *bt_mesh_subnet_find(const u8_t net_id[8], u8_t flags, + u32_t iv_index, const u8_t auth[8], + bool *new_key); + +int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct os_mbuf *buf, + bool proxy); + +int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf, + const struct bt_mesh_send_cb *cb, void *cb_data); + +int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct os_mbuf *buf, + bool new_key, const struct bt_mesh_send_cb *cb, + void *cb_data); + +int bt_mesh_net_decode(struct os_mbuf *data, enum bt_mesh_net_if net_if, + struct bt_mesh_net_rx *rx, struct os_mbuf *buf); + +void bt_mesh_net_recv(struct os_mbuf *data, s8_t rssi, + enum bt_mesh_net_if net_if); + +u32_t bt_mesh_next_seq(void); + +void bt_mesh_net_start(void); + +void bt_mesh_net_init(void); + +/* Friendship Credential Management */ +struct friend_cred { + u16_t net_idx; + u16_t addr; + + u16_t lpn_counter; + u16_t frnd_counter; + + struct { + u8_t nid; /* NID */ + u8_t enc[16]; /* EncKey */ + u8_t privacy[16]; /* PrivacyKey */ + } cred[2]; +}; + +int friend_cred_get(struct bt_mesh_subnet *sub, u16_t addr, u8_t *nid, + const u8_t **enc, const u8_t **priv); +int friend_cred_set(struct friend_cred *cred, u8_t idx, const u8_t net_key[16]); +void friend_cred_refresh(u16_t net_idx); +int friend_cred_update(struct bt_mesh_subnet *sub); +struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, u16_t addr, + u16_t lpn_counter, u16_t frnd_counter); +void friend_cred_clear(struct friend_cred *cred); +int friend_cred_del(u16_t net_idx, u16_t addr); + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.c new file mode 100644 index 000000000..5c519e75a --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.c @@ -0,0 +1,1665 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH_PROV) + +#include + +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_PROV)) +#include "host/ble_hs_log.h" + +#include "mesh/mesh.h" +#include "mesh_priv.h" + +#include "crypto.h" +#include "atomic.h" +#include "adv.h" +#include "net.h" +#include "access.h" +#include "foundation.h" +#include "proxy.h" +#include "prov.h" +#include "testing.h" + +/* 3 transmissions, 20ms interval */ +#define PROV_XMIT BT_MESH_TRANSMIT(2, 20) + +#define AUTH_METHOD_NO_OOB 0x00 +#define AUTH_METHOD_STATIC 0x01 +#define AUTH_METHOD_OUTPUT 0x02 +#define AUTH_METHOD_INPUT 0x03 + +#define OUTPUT_OOB_BLINK 0x00 +#define OUTPUT_OOB_BEEP 0x01 +#define OUTPUT_OOB_VIBRATE 0x02 +#define OUTPUT_OOB_NUMBER 0x03 +#define OUTPUT_OOB_STRING 0x04 + +#define INPUT_OOB_PUSH 0x00 +#define INPUT_OOB_TWIST 0x01 +#define INPUT_OOB_NUMBER 0x02 +#define INPUT_OOB_STRING 0x03 + +#define PUB_KEY_NO_OOB 0x00 +#define PUB_KEY_OOB 0x01 + +#define PROV_ERR_NONE 0x00 +#define PROV_ERR_NVAL_PDU 0x01 +#define PROV_ERR_NVAL_FMT 0x02 +#define PROV_ERR_UNEXP_PDU 0x03 +#define PROV_ERR_CFM_FAILED 0x04 +#define PROV_ERR_RESOURCES 0x05 +#define PROV_ERR_DECRYPT 0x06 +#define PROV_ERR_UNEXP_ERR 0x07 +#define PROV_ERR_ADDR 0x08 + +#define PROV_INVITE 0x00 +#define PROV_CAPABILITIES 0x01 +#define PROV_START 0x02 +#define PROV_PUB_KEY 0x03 +#define PROV_INPUT_COMPLETE 0x04 +#define PROV_CONFIRM 0x05 +#define PROV_RANDOM 0x06 +#define PROV_DATA 0x07 +#define PROV_COMPLETE 0x08 +#define PROV_FAILED 0x09 + +#define PROV_ALG_P256 0x00 + +#define GPCF(gpc) (gpc & 0x03) +#define GPC_START(last_seg) (((last_seg) << 2) | 0x00) +#define GPC_ACK 0x01 +#define GPC_CONT(seg_id) (((seg_id) << 2) | 0x02) +#define GPC_CTL(op) (((op) << 2) | 0x03) + +#define START_PAYLOAD_MAX 20 +#define CONT_PAYLOAD_MAX 23 + +#define START_LAST_SEG(gpc) (gpc >> 2) +#define CONT_SEG_INDEX(gpc) (gpc >> 2) + +#define BEARER_CTL(gpc) (gpc >> 2) +#define LINK_OPEN 0x00 +#define LINK_ACK 0x01 +#define LINK_CLOSE 0x02 + +#define CLOSE_REASON_SUCCESS 0x00 +#define CLOSE_REASON_TIMEOUT 0x01 +#define CLOSE_REASON_FAILED 0x02 + +#define XACT_SEG_DATA(_seg) (&link.rx.buf->om_data[20 + ((_seg - 1) * 23)]) +#define XACT_SEG_RECV(_seg) (link.rx.seg &= ~(1 << (_seg))) + +#define XACT_NVAL 0xff + +enum { + REMOTE_PUB_KEY, /* Remote key has been received */ + LINK_ACTIVE, /* Link has been opened */ + HAVE_DHKEY, /* DHKey has been calculated */ + SEND_CONFIRM, /* Waiting to send Confirm value */ + WAIT_NUMBER, /* Waiting for number input from user */ + WAIT_STRING, /* Waiting for string input from user */ + LINK_INVALID, /* Error occurred during provisioning */ + + NUM_FLAGS, +}; + +struct prov_link { + ATOMIC_DEFINE(flags, NUM_FLAGS); +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + uint16_t conn_handle; /* GATT connection */ +#endif + u8_t dhkey[32]; /* Calculated DHKey */ + u8_t expect; /* Next expected PDU */ + + u8_t oob_method; + u8_t oob_action; + u8_t oob_size; + + u8_t conf[16]; /* Remote Confirmation */ + u8_t rand[16]; /* Local Random */ + u8_t auth[16]; /* Authentication Value */ + + u8_t conf_salt[16]; /* ConfirmationSalt */ + u8_t conf_key[16]; /* ConfirmationKey */ + u8_t conf_inputs[145]; /* ConfirmationInputs */ + u8_t prov_salt[16]; /* Provisioning Salt */ + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + u32_t id; /* Link ID */ + + struct { + u8_t id; /* Transaction ID */ + u8_t prev_id; /* Previous Transaction ID */ + u8_t seg; /* Bit-field of unreceived segments */ + u8_t last_seg; /* Last segment (to check length) */ + u8_t fcs; /* Expected FCS value */ + struct os_mbuf *buf; + } rx; + + struct { + /* Start timestamp of the transaction */ + s64_t start; + + /* Transaction id*/ + u8_t id; + + /* Pending outgoing buffer(s) */ + struct os_mbuf *buf[3]; + + /* Retransmit timer */ + struct k_delayed_work retransmit; + } tx; +#endif + + struct k_delayed_work prot_timer; +}; + +struct prov_rx { + u32_t link_id; + u8_t xact_id; + u8_t gpc; +}; + +#define RETRANSMIT_TIMEOUT K_MSEC(500) +#define BUF_TIMEOUT K_MSEC(400) +#define TRANSACTION_TIMEOUT K_SECONDS(30) +#define PROTOCOL_TIMEOUT K_SECONDS(60) + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +#define PROV_BUF_HEADROOM 5 +#else +#define PROV_BUF_HEADROOM 0 +static struct os_mbuf *rx_buf; +#endif + +#define PROV_BUF(len) NET_BUF_SIMPLE(PROV_BUF_HEADROOM + len) + +static struct prov_link link; + +static const struct bt_mesh_prov *prov; + +static void pub_key_ready(const u8_t *pkey); + +static int reset_state(void) +{ + static struct bt_pub_key_cb pub_key_cb = { + .func = pub_key_ready, + }; + int err; + + k_delayed_work_cancel(&link.prot_timer); + + /* Disable Attention Timer if it was set */ + if (link.conf_inputs[0]) { + bt_mesh_attention(NULL, 0); + } + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + /* Clear everything except the retransmit and protocol timer + * delayed work objects. + */ + (void)memset(&link, 0, offsetof(struct prov_link, tx.retransmit)); + link.rx.prev_id = XACT_NVAL; + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + link.rx.buf = bt_mesh_proxy_get_buf(); +#else + net_buf_simple_init(rx_buf, 0); + link.rx.buf = rx_buf; +#endif /* PB_GATT */ + +#else /* !PB_ADV */ + /* Clear everything except the protocol timer (k_delayed_work) */ + (void)memset(&link, 0, offsetof(struct prov_link, prot_timer)); +#endif /* PB_ADV */ + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + link.conn_handle = BLE_HS_CONN_HANDLE_NONE; +#endif + + err = bt_pub_key_gen(&pub_key_cb); + if (err) { + BT_ERR("Failed to generate public key (%d)", err); + return err; + } + + return 0; +} + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) +static void buf_sent(int err, void *user_data) +{ + BT_DBG("buf_sent"); + + if (!link.tx.buf[0]) { + return; + } + + k_delayed_work_submit(&link.tx.retransmit, RETRANSMIT_TIMEOUT); +} + +static struct bt_mesh_send_cb buf_sent_cb = { + .end = buf_sent, +}; + +static void free_segments(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct os_mbuf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + link.tx.buf[i] = NULL; + /* Mark as canceled */ + BT_MESH_ADV(buf)->busy = 0; + net_buf_unref(buf); + } +} + +static void prov_clear_tx(void) +{ + BT_DBG(""); + + k_delayed_work_cancel(&link.tx.retransmit); + + free_segments(); +} + +static void reset_adv_link(void) +{ + prov_clear_tx(); + + if (prov->link_close) { + prov->link_close(BT_MESH_PROV_ADV); + } + + reset_state(); +} + +static struct os_mbuf *adv_buf_create(void) +{ + struct os_mbuf *buf; + + buf = bt_mesh_adv_create(BT_MESH_ADV_PROV, PROV_XMIT, BUF_TIMEOUT); + if (!buf) { + BT_ERR("Out of provisioning buffers"); + assert(0); + return NULL; + } + + return buf; +} + +static u8_t pending_ack = XACT_NVAL; + +static void ack_complete(u16_t duration, int err, void *user_data) +{ + BT_DBG("xact %u complete", (u8_t)pending_ack); + pending_ack = XACT_NVAL; +} + +static void gen_prov_ack_send(u8_t xact_id) +{ + static const struct bt_mesh_send_cb cb = { + .start = ack_complete, + }; + const struct bt_mesh_send_cb *complete; + struct os_mbuf *buf; + + BT_DBG("xact_id %u", xact_id); + + if (pending_ack == xact_id) { + BT_DBG("Not sending duplicate ack"); + return; + } + + buf = adv_buf_create(); + if (!buf) { + return; + } + + if (pending_ack == XACT_NVAL) { + pending_ack = xact_id; + complete = &cb; + } else { + complete = NULL; + } + + net_buf_add_be32(buf, link.id); + net_buf_add_u8(buf, xact_id); + net_buf_add_u8(buf, GPC_ACK); + + bt_mesh_adv_send(buf, complete, NULL); + net_buf_unref(buf); +} + +static void send_reliable(void) +{ + int i; + + link.tx.start = k_uptime_get(); + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct os_mbuf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { + bt_mesh_adv_send(buf, NULL, NULL); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, NULL); + } + } +} + +static int bearer_ctl_send(u8_t op, void *data, u8_t data_len) +{ + struct os_mbuf *buf; + + BT_DBG("op 0x%02x data_len %u", op, data_len); + + prov_clear_tx(); + + buf = adv_buf_create(); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_be32(buf, link.id); + /* Transaction ID, always 0 for Bearer messages */ + net_buf_add_u8(buf, 0x00); + net_buf_add_u8(buf, GPC_CTL(op)); + net_buf_add_mem(buf, data, data_len); + + link.tx.buf[0] = buf; + send_reliable(); + + return 0; +} + +static u8_t last_seg(u8_t len) +{ + if (len <= START_PAYLOAD_MAX) { + return 0; + } + + len -= START_PAYLOAD_MAX; + + return 1 + (len / CONT_PAYLOAD_MAX); +} + +static inline u8_t next_transaction_id(void) +{ + if (link.tx.id != 0 && link.tx.id != 0xFF) { + return ++link.tx.id; + } + + link.tx.id = 0x80; + return link.tx.id; +} + +static int prov_send_adv(struct os_mbuf *msg) +{ + struct os_mbuf *start, *buf; + u8_t seg_len, seg_id; + u8_t xact_id; + + BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len)); + + prov_clear_tx(); + + start = adv_buf_create(); + if (!start) { + return -ENOBUFS; + } + + xact_id = next_transaction_id(); + net_buf_add_be32(start, link.id); + net_buf_add_u8(start, xact_id); + + net_buf_add_u8(start, GPC_START(last_seg(msg->om_len))); + net_buf_add_be16(start, msg->om_len); + net_buf_add_u8(start, bt_mesh_fcs_calc(msg->om_data, msg->om_len)); + + link.tx.buf[0] = start; + + seg_len = min(msg->om_len, START_PAYLOAD_MAX); + BT_DBG("seg 0 len %u: %s", seg_len, bt_hex(msg->om_data, seg_len)); + net_buf_add_mem(start, msg->om_data, seg_len); + net_buf_simple_pull(msg, seg_len); + + buf = start; + for (seg_id = 1; msg->om_len > 0; seg_id++) { + if (seg_id >= ARRAY_SIZE(link.tx.buf)) { + BT_ERR("Too big message"); + free_segments(); + return -E2BIG; + } + + buf = adv_buf_create(); + if (!buf) { + free_segments(); + return -ENOBUFS; + } + + link.tx.buf[seg_id] = buf; + + seg_len = min(msg->om_len, CONT_PAYLOAD_MAX); + + BT_DBG("seg_id %u len %u: %s", seg_id, seg_len, + bt_hex(msg->om_data, seg_len)); + + net_buf_add_be32(buf, link.id); + net_buf_add_u8(buf, xact_id); + net_buf_add_u8(buf, GPC_CONT(seg_id)); + net_buf_add_mem(buf, msg->om_data, seg_len); + net_buf_simple_pull(msg, seg_len); + } + + send_reliable(); + + return 0; +} + +#endif /* MYNEWT_VAL(BLE_MESH_PB_ADV) */ + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static int prov_send_gatt(struct os_mbuf *msg) +{ + if (link.conn_handle == BLE_HS_CONN_HANDLE_NONE) { + BT_ERR("No connection handle!?"); + return -ENOTCONN; + } + + return bt_mesh_proxy_send(link.conn_handle, BT_MESH_PROXY_PROV, msg); +} +#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */ + +static inline int prov_send(struct os_mbuf *buf) +{ + k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + if (link.conn_handle != BLE_HS_CONN_HANDLE_NONE) { + return prov_send_gatt(buf); + } +#endif +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + return prov_send_adv(buf); +#else + return 0; +#endif +} + +static void prov_buf_init(struct os_mbuf *buf, u8_t type) +{ + net_buf_simple_init(buf, PROV_BUF_HEADROOM); + net_buf_simple_add_u8(buf, type); +} + +static void prov_send_fail_msg(u8_t err) +{ + struct os_mbuf *buf = PROV_BUF(2); + + prov_buf_init(buf, PROV_FAILED); + net_buf_simple_add_u8(buf, err); + + if (prov_send(buf)) { + BT_ERR("Failed to send Provisioning Failed message"); + } + + atomic_set_bit(link.flags, LINK_INVALID); + + os_mbuf_free_chain(buf); +} + +static void prov_invite(const u8_t *data) +{ + struct os_mbuf *buf = PROV_BUF(12); + + BT_DBG("Attention Duration: %u seconds", data[0]); + + if (data[0]) { + bt_mesh_attention(NULL, data[0]); + } + + link.conf_inputs[0] = data[0]; + + prov_buf_init(buf, PROV_CAPABILITIES); + + /* Number of Elements supported */ + net_buf_simple_add_u8(buf, bt_mesh_elem_count()); + + /* Supported algorithms - FIPS P-256 Eliptic Curve */ + net_buf_simple_add_be16(buf, BIT(PROV_ALG_P256)); + + /* Public Key Type, Only "No OOB" Public Key is supported*/ + net_buf_simple_add_u8(buf, PUB_KEY_NO_OOB); + + /* Static OOB Type */ + net_buf_simple_add_u8(buf, prov->static_val ? BIT(0) : 0x00); + + /* Output OOB Size */ + net_buf_simple_add_u8(buf, prov->output_size); + + /* Output OOB Action */ + net_buf_simple_add_be16(buf, prov->output_actions); + + /* Input OOB Size */ + net_buf_simple_add_u8(buf, prov->input_size); + + /* Input OOB Action */ + net_buf_simple_add_be16(buf, prov->input_actions); + + memcpy(&link.conf_inputs[1], &buf->om_data[1], 11); + + if (prov_send(buf)) { + BT_ERR("Failed to send capabilities"); + goto done; + } + + link.expect = PROV_START; + +done: + os_mbuf_free_chain(buf); +} + +static void prov_capabilities(const u8_t *data) +{ + u16_t algorithms, output_action, input_action; + + BT_DBG("Elements: %u", data[0]); + + algorithms = sys_get_be16(&data[1]); + BT_DBG("Algorithms: %u", algorithms); + + BT_DBG("Public Key Type: 0x%02x", data[3]); + BT_DBG("Static OOB Type: 0x%02x", data[4]); + BT_DBG("Output OOB Size: %u", data[5]); + + output_action = sys_get_be16(&data[6]); + BT_DBG("Output OOB Action: 0x%04x", output_action); + + BT_DBG("Input OOB Size: %u", data[8]); + + input_action = sys_get_be16(&data[9]); + BT_DBG("Input OOB Action: 0x%04x", input_action); +} + +static bt_mesh_output_action_t output_action(u8_t action) +{ + switch (action) { + case OUTPUT_OOB_BLINK: + return BT_MESH_BLINK; + case OUTPUT_OOB_BEEP: + return BT_MESH_BEEP; + case OUTPUT_OOB_VIBRATE: + return BT_MESH_VIBRATE; + case OUTPUT_OOB_NUMBER: + return BT_MESH_DISPLAY_NUMBER; + case OUTPUT_OOB_STRING: + return BT_MESH_DISPLAY_STRING; + default: + return BT_MESH_NO_OUTPUT; + } +} + +static bt_mesh_input_action_t input_action(u8_t action) +{ + switch (action) { + case INPUT_OOB_PUSH: + return BT_MESH_PUSH; + case INPUT_OOB_TWIST: + return BT_MESH_TWIST; + case INPUT_OOB_NUMBER: + return BT_MESH_ENTER_NUMBER; + case INPUT_OOB_STRING: + return BT_MESH_ENTER_STRING; + default: + return BT_MESH_NO_INPUT; + } +} + +static int prov_auth(u8_t method, u8_t action, u8_t size) +{ + bt_mesh_output_action_t output; + bt_mesh_input_action_t input; + + switch (method) { + case AUTH_METHOD_NO_OOB: + if (action || size) { + return -EINVAL; + } + + memset(link.auth, 0, sizeof(link.auth)); + return 0; + case AUTH_METHOD_STATIC: + if (action || size) { + return -EINVAL; + } + + memcpy(link.auth + 16 - prov->static_val_len, + prov->static_val, prov->static_val_len); + memset(link.auth, 0, sizeof(link.auth) - prov->static_val_len); + return 0; + + case AUTH_METHOD_OUTPUT: + output = output_action(action); + if (!output) { + return -EINVAL; + } + + if (!(prov->output_actions & output)) { + return -EINVAL; + } + + if (size > prov->output_size) { + return -EINVAL; + } + + if (output == BT_MESH_DISPLAY_STRING) { + unsigned char str[9]; + u8_t i; + + bt_rand(str, size); + + /* Normalize to '0' .. '9' & 'A' .. 'Z' */ + for (i = 0; i < size; i++) { + str[i] %= 36; + if (str[i] < 10) { + str[i] += '0'; + } else { + str[i] += 'A' - 10; + } + } + str[size] = '\0'; + + memcpy(link.auth, str, size); + memset(link.auth + size, 0, sizeof(link.auth) - size); + + return prov->output_string((char *)str); + } else { + u32_t div[8] = { 10, 100, 1000, 10000, 100000, + 1000000, 10000000, 100000000 }; + u32_t num; + + bt_rand(&num, sizeof(num)); + num %= div[size - 1]; + + sys_put_be32(num, &link.auth[12]); + memset(link.auth, 0, 12); + + return prov->output_number(output, num); + } + + case AUTH_METHOD_INPUT: + input = input_action(action); + if (!input) { + return -EINVAL; + } + + if (!(prov->input_actions & input)) { + return -EINVAL; + } + + if (size > prov->input_size) { + return -EINVAL; + } + + if (input == BT_MESH_ENTER_STRING) { + atomic_set_bit(link.flags, WAIT_STRING); + } else { + atomic_set_bit(link.flags, WAIT_NUMBER); + } + + return prov->input(input, size); + + default: + return -EINVAL; + } +} + +static void prov_start(const u8_t *data) +{ + BT_DBG("Algorithm: 0x%02x", data[0]); + BT_DBG("Public Key: 0x%02x", data[1]); + BT_DBG("Auth Method: 0x%02x", data[2]); + BT_DBG("Auth Action: 0x%02x", data[3]); + BT_DBG("Auth Size: 0x%02x", data[4]); + + if (data[0] != PROV_ALG_P256) { + BT_ERR("Unknown algorithm 0x%02x", data[0]); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + if (data[1] != PUB_KEY_NO_OOB) { + BT_ERR("Invalid public key type: 0x%02x", data[1]); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + memcpy(&link.conf_inputs[12], data, 5); + + /* TODO: reset link when auth fails? */ + link.expect = PROV_PUB_KEY; + + if (prov_auth(data[2], data[3], data[4]) < 0) { + BT_ERR("Invalid authentication method: 0x%02x; " + "action: 0x%02x; size: 0x%02x", data[2], data[3], + data[4]); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + } +} + +static void send_confirm(void) +{ + struct os_mbuf *cfm = PROV_BUF(17); + + BT_DBG("ConfInputs[0] %s", bt_hex(link.conf_inputs, 64)); + BT_DBG("ConfInputs[64] %s", bt_hex(&link.conf_inputs[64], 64)); + BT_DBG("ConfInputs[128] %s", bt_hex(&link.conf_inputs[128], 17)); + + if (bt_mesh_prov_conf_salt(link.conf_inputs, link.conf_salt)) { + BT_ERR("Unable to generate confirmation salt"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("ConfirmationSalt: %s", bt_hex(link.conf_salt, 16)); + + if (bt_mesh_prov_conf_key(link.dhkey, link.conf_salt, link.conf_key)) { + BT_ERR("Unable to generate confirmation key"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("ConfirmationKey: %s", bt_hex(link.conf_key, 16)); + + if (bt_rand(link.rand, 16)) { + BT_ERR("Unable to generate random number"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("LocalRandom: %s", bt_hex(link.rand, 16)); + + prov_buf_init(cfm, PROV_CONFIRM); + + if (bt_mesh_prov_conf(link.conf_key, link.rand, link.auth, + net_buf_simple_add(cfm, 16))) { + BT_ERR("Unable to generate confirmation value"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + if (prov_send(cfm)) { + BT_ERR("Failed to send Provisioning Confirm"); + goto done; + } + + link.expect = PROV_RANDOM; + +done: + os_mbuf_free_chain(cfm); +} + +static void send_input_complete(void) +{ + struct os_mbuf *buf = PROV_BUF(1); + + prov_buf_init(buf, PROV_INPUT_COMPLETE); + if (prov_send(buf)) { + BT_ERR("Failed to send Provisioning Input Complete"); + } + + os_mbuf_free_chain(buf); +} + +int bt_mesh_input_number(u32_t num) +{ + BT_DBG("%u", (unsigned) num); + + if (!atomic_test_and_clear_bit(link.flags, WAIT_NUMBER)) { + return -EINVAL; + } + + sys_put_be32(num, &link.auth[12]); + + send_input_complete(); + + if (!atomic_test_bit(link.flags, HAVE_DHKEY)) { + return 0; + } + + if (atomic_test_and_clear_bit(link.flags, SEND_CONFIRM)) { + send_confirm(); + } + + return 0; +} + +int bt_mesh_input_string(const char *str) +{ + BT_DBG("%s", str); + + if (!atomic_test_and_clear_bit(link.flags, WAIT_STRING)) { + return -EINVAL; + } + + strncpy((char *)link.auth, str, prov->input_size); + + send_input_complete(); + + if (!atomic_test_bit(link.flags, HAVE_DHKEY)) { + return 0; + } + + if (atomic_test_and_clear_bit(link.flags, SEND_CONFIRM)) { + send_confirm(); + } + + return 0; +} + +static void prov_dh_key_cb(const u8_t key[32]) +{ + BT_DBG("%p", key); + + if (!key) { + BT_ERR("DHKey generation failed"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + return; + } + + sys_memcpy_swap(link.dhkey, key, 32); + + BT_DBG("DHkey: %s", bt_hex(link.dhkey, 32)); + + atomic_set_bit(link.flags, HAVE_DHKEY); + + if (atomic_test_bit(link.flags, WAIT_NUMBER) || + atomic_test_bit(link.flags, WAIT_STRING)) { + return; + } + + if (atomic_test_and_clear_bit(link.flags, SEND_CONFIRM)) { + send_confirm(); + } +} + +static void send_pub_key(void) +{ + struct os_mbuf *buf = PROV_BUF(65); + const u8_t *key; + + key = bt_pub_key_get(); + if (!key) { + BT_ERR("No public key available"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("Local Public Key: %s", bt_hex(key, 64)); + + /* Copy remote key in little-endian for bt_dh_key_gen(). + * X and Y halves are swapped independently. Use response + * buffer as a temporary storage location. The bt_dh_key_gen() + * will also take care of validating the remote public key. + */ + sys_memcpy_swap(buf->om_data, &link.conf_inputs[17], 32); + sys_memcpy_swap(&buf->om_data[32], &link.conf_inputs[49], 32); + + if (bt_dh_key_gen(buf->om_data, prov_dh_key_cb)) { + BT_ERR("Failed to generate DHKey"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + return; + } + + prov_buf_init(buf, PROV_PUB_KEY); + + /* Swap X and Y halves independently to big-endian */ + sys_memcpy_swap(net_buf_simple_add(buf, 32), key, 32); + sys_memcpy_swap(net_buf_simple_add(buf, 32), &key[32], 32); + + memcpy(&link.conf_inputs[81], &buf->om_data[1], 64); + + if (prov_send(buf)) { + BT_ERR("Failed to send Public Key"); + return; + } + + link.expect = PROV_CONFIRM; + +done: + os_mbuf_free_chain(buf); +} + +static void prov_pub_key(const u8_t *data) +{ + BT_DBG("Remote Public Key: %s", bt_hex(data, 64)); + + memcpy(&link.conf_inputs[17], data, 64); + + if (!bt_pub_key_get()) { + /* Clear retransmit timer */ +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + prov_clear_tx(); +#endif + atomic_set_bit(link.flags, REMOTE_PUB_KEY); + BT_WARN("Waiting for local public key"); + return; + } + + send_pub_key(); +} + +static void pub_key_ready(const u8_t *pkey) +{ + if (!pkey) { + BT_WARN("Public key not available"); + return; + } + + BT_DBG("Local public key ready"); + + if (atomic_test_and_clear_bit(link.flags, REMOTE_PUB_KEY)) { + send_pub_key(); + } +} + +static void prov_input_complete(const u8_t *data) +{ + BT_DBG(""); +} + +static void prov_confirm(const u8_t *data) +{ + BT_DBG("Remote Confirm: %s", bt_hex(data, 16)); + + memcpy(link.conf, data, 16); + + if (!atomic_test_bit(link.flags, HAVE_DHKEY)) { +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + prov_clear_tx(); +#endif + atomic_set_bit(link.flags, SEND_CONFIRM); + } else { + send_confirm(); + } +} + +static void prov_random(const u8_t *data) +{ + struct os_mbuf *rnd = PROV_BUF(16); + u8_t conf_verify[16]; + + BT_DBG("Remote Random: %s", bt_hex(data, 16)); + + if (bt_mesh_prov_conf(link.conf_key, data, link.auth, conf_verify)) { + BT_ERR("Unable to calculate confirmation verification"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + if (memcmp(conf_verify, link.conf, 16)) { + BT_ERR("Invalid confirmation value"); + BT_DBG("Received: %s", bt_hex(link.conf, 16)); + BT_DBG("Calculated: %s", bt_hex(conf_verify, 16)); + prov_send_fail_msg(PROV_ERR_CFM_FAILED); + goto done; + } + + prov_buf_init(rnd, PROV_RANDOM); + net_buf_simple_add_mem(rnd, link.rand, 16); + + if (prov_send(rnd)) { + BT_ERR("Failed to send Provisioning Random"); + goto done; + } + + if (bt_mesh_prov_salt(link.conf_salt, data, link.rand, + link.prov_salt)) { + BT_ERR("Failed to generate provisioning salt"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("ProvisioningSalt: %s", bt_hex(link.prov_salt, 16)); + + link.expect = PROV_DATA; + +done: + os_mbuf_free_chain(rnd); +} + +static inline bool is_pb_gatt(void) +{ +#if MYNEWT_VAL(BLE_MESH_PB_GATT) + return (link.conn_handle != BLE_HS_CONN_HANDLE_NONE); +#else + return false; +#endif +} + +static void prov_data(const u8_t *data) +{ + struct os_mbuf *msg = PROV_BUF(1); + u8_t session_key[16]; + u8_t nonce[13]; + u8_t dev_key[16]; + u8_t pdu[25]; + u8_t flags; + u32_t iv_index; + u16_t addr; + u16_t net_idx; + int err; + bool identity_enable; + + BT_DBG(""); + + err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key); + if (err) { + BT_ERR("Unable to generate session key"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("SessionKey: %s", bt_hex(session_key, 16)); + + err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce); + if (err) { + BT_ERR("Unable to generate session nonce"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("Nonce: %s", bt_hex(nonce, 13)); + + err = bt_mesh_prov_decrypt(session_key, nonce, data, pdu); + if (err) { + BT_ERR("Unable to decrypt provisioning data"); + prov_send_fail_msg(PROV_ERR_DECRYPT); + goto done; + } + + err = bt_mesh_dev_key(link.dhkey, link.prov_salt, dev_key); + if (err) { + BT_ERR("Unable to generate device key"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("DevKey: %s", bt_hex(dev_key, 16)); + + net_idx = sys_get_be16(&pdu[16]); + flags = pdu[18]; + iv_index = sys_get_be32(&pdu[19]); + addr = sys_get_be16(&pdu[23]); + + BT_DBG("net_idx %u iv_index 0x%08x, addr 0x%04x", + net_idx, (unsigned) iv_index, addr); + + prov_buf_init(msg, PROV_COMPLETE); + if (prov_send(msg)) { + BT_ERR("Failed to send Provisioning Complete"); + goto done; + } + + /* Ignore any further PDUs on this link */ + link.expect = 0; + + /* Store info, since bt_mesh_provision() will end up clearing it */ + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { + identity_enable = is_pb_gatt(); + } else { + identity_enable = false; + } + + err = bt_mesh_provision(pdu, net_idx, flags, iv_index, addr, dev_key); + if (err) { + BT_ERR("Failed to provision (err %d)", err); + goto done; + } + + /* After PB-GATT provisioning we should start advertising + * using Node Identity. + */ + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && identity_enable) { + bt_mesh_proxy_identity_enable(); + } + +done: + os_mbuf_free_chain(msg); +} + +static void prov_complete(const u8_t *data) +{ + BT_DBG(""); +} + +static void prov_failed(const u8_t *data) +{ + BT_WARN("Error: 0x%02x", data[0]); +} + +static const struct { + void (*func)(const u8_t *data); + u16_t len; +} prov_handlers[] = { + { prov_invite, 1 }, + { prov_capabilities, 11 }, + { prov_start, 5, }, + { prov_pub_key, 64 }, + { prov_input_complete, 0 }, + { prov_confirm, 16 }, + { prov_random, 16 }, + { prov_data, 33 }, + { prov_complete, 0 }, + { prov_failed, 1 }, +}; + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) +static void prov_retransmit(struct ble_npl_event *work) +{ + int i; + + BT_DBG(""); + + if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { + BT_WARN("Link not active"); + return; + } + + if (k_uptime_get() - link.tx.start > TRANSACTION_TIMEOUT) { + BT_WARN("Giving up transaction"); + reset_adv_link(); + return; + } + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct os_mbuf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + if (BT_MESH_ADV(buf)->busy) { + continue; + } + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { + bt_mesh_adv_send(buf, NULL, NULL); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, NULL); + } + + } +} + +static void link_open(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("link open: len %u", buf->om_len); + + if (buf->om_len < 16) { + BT_ERR("Too short bearer open message (len %u)", buf->om_len); + return; + } + + if (atomic_test_bit(link.flags, LINK_ACTIVE)) { + /* Send another link ack if the provisioner missed the last */ + if (link.id == rx->link_id && link.expect == PROV_INVITE) { + BT_DBG("Resending link ack"); + bearer_ctl_send(LINK_ACK, NULL, 0); + } else { + BT_WARN("Ignoring bearer open: link already active"); + } + + return; + } + + if (memcmp(buf->om_data, prov->uuid, 16)) { + BT_DBG("Bearer open message not for us"); + return; + } + + if (prov->link_open) { + prov->link_open(BT_MESH_PROV_ADV); + } + + link.id = rx->link_id; + atomic_set_bit(link.flags, LINK_ACTIVE); + net_buf_simple_init(link.rx.buf, 0); + + bearer_ctl_send(LINK_ACK, NULL, 0); + + link.expect = PROV_INVITE; + +} + +static void link_ack(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("Link ack: len %u", buf->om_len); +} + +static void link_close(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("Link close: len %u", buf->om_len); + + reset_adv_link(); +} + +static void gen_prov_ctl(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("op 0x%02x len %u", BEARER_CTL(rx->gpc), buf->om_len); + + switch (BEARER_CTL(rx->gpc)) { + case LINK_OPEN: + link_open(rx, buf); + break; + case LINK_ACK: + if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { + return; + } + + link_ack(rx, buf); + break; + case LINK_CLOSE: + if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { + return; + } + + link_close(rx, buf); + break; + default: + BT_ERR("Unknown bearer opcode: 0x%02x", BEARER_CTL(rx->gpc)); + + if (IS_ENABLED(CONFIG_BT_TESTING)) { + bt_test_mesh_prov_invalid_bearer(BEARER_CTL(rx->gpc)); + } + + return; + } +} + +static void prov_msg_recv(void) +{ + u8_t type = link.rx.buf->om_data[0]; + + BT_DBG("type 0x%02x len %u", type, link.rx.buf->om_len); + + k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); + + if (!bt_mesh_fcs_check(link.rx.buf, link.rx.fcs)) { + BT_ERR("Incorrect FCS"); + return; + } + + gen_prov_ack_send(link.rx.id); + link.rx.prev_id = link.rx.id; + link.rx.id = 0; + + if (atomic_test_bit(link.flags, LINK_INVALID)) { + BT_WARN("Unexpected msg 0x%02x on invalidated link", type); + prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + return; + } + + if (type != PROV_FAILED && type != link.expect) { + BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); + prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + return; + } + + if (type >= ARRAY_SIZE(prov_handlers)) { + BT_ERR("Unknown provisioning PDU type 0x%02x", type); + prov_send_fail_msg(PROV_ERR_NVAL_PDU); + return; + } + + if (1 + prov_handlers[type].len != link.rx.buf->om_len) { + BT_ERR("Invalid length %u for type 0x%02x", + link.rx.buf->om_len, type); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + prov_handlers[type].func(&link.rx.buf->om_data[1]); +} + +static void gen_prov_cont(struct prov_rx *rx, struct os_mbuf *buf) +{ + u8_t seg = CONT_SEG_INDEX(rx->gpc); + + BT_DBG("len %u, seg_index %u", buf->om_len, seg); + + if (!link.rx.seg && link.rx.prev_id == rx->xact_id) { + BT_WARN("Resending ack"); + gen_prov_ack_send(rx->xact_id); + return; + } + + if (rx->xact_id != link.rx.id) { + BT_WARN("Data for unknown transaction (%u != %u)", + rx->xact_id, link.rx.id); + return; + } + + if (seg > link.rx.last_seg) { + BT_ERR("Invalid segment index %u", seg); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } else if (seg == link.rx.last_seg) { + u8_t expect_len; + + expect_len = (link.rx.buf->om_len - 20 - + ((link.rx.last_seg - 1) * 23)); + if (expect_len != buf->om_len) { + BT_ERR("Incorrect last seg len: %u != %u", + expect_len, buf->om_len); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + } + + if (!(link.rx.seg & BIT(seg))) { + BT_WARN("Ignoring already received segment"); + return; + } + + memcpy(XACT_SEG_DATA(seg), buf->om_data, buf->om_len); + XACT_SEG_RECV(seg); + + if (!link.rx.seg) { + prov_msg_recv(); + } +} + +static void gen_prov_ack(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("len %u", buf->om_len); + + if (!link.tx.buf[0]) { + return; + } + + if (rx->xact_id == link.tx.id) { + prov_clear_tx(); + } +} + +static void gen_prov_start(struct prov_rx *rx, struct os_mbuf *buf) +{ + u16_t trailing_space = 0; + + if (link.rx.seg) { + BT_WARN("Got Start while there are unreceived segments"); + return; + } + + if (link.rx.prev_id == rx->xact_id) { + BT_WARN("Resending ack"); + gen_prov_ack_send(rx->xact_id); + return; + } + + trailing_space = OS_MBUF_TRAILINGSPACE(link.rx.buf); + + link.rx.buf->om_len = net_buf_simple_pull_be16(buf); + link.rx.id = rx->xact_id; + link.rx.fcs = net_buf_simple_pull_u8(buf); + + BT_DBG("len %u last_seg %u total_len %u fcs 0x%02x", buf->om_len, + START_LAST_SEG(rx->gpc), link.rx.buf->om_len, link.rx.fcs); + + if (link.rx.buf->om_len < 1) { + BT_ERR("Ignoring zero-length provisioning PDU"); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + if (link.rx.buf->om_len > trailing_space) { + BT_ERR("Too large provisioning PDU (%u bytes)", + link.rx.buf->om_len); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + if (START_LAST_SEG(rx->gpc) > 0 && link.rx.buf->om_len <= 20) { + BT_ERR("Too small total length for multi-segment PDU"); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + link.rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1; + link.rx.last_seg = START_LAST_SEG(rx->gpc); + memcpy(link.rx.buf->om_data, buf->om_data, buf->om_len); + XACT_SEG_RECV(0); + + if (!link.rx.seg) { + prov_msg_recv(); + } +} + +static const struct { + void (*const func)(struct prov_rx *rx, struct os_mbuf *buf); + const u8_t require_link; + const u8_t min_len; +} gen_prov[] = { + { gen_prov_start, true, 3 }, + { gen_prov_ack, true, 0 }, + { gen_prov_cont, true, 0 }, + { gen_prov_ctl, false, 0 }, +}; + +static void gen_prov_recv(struct prov_rx *rx, struct os_mbuf *buf) +{ + if (buf->om_len < gen_prov[GPCF(rx->gpc)].min_len) { + BT_ERR("Too short GPC message type %u", GPCF(rx->gpc)); + return; + } + + if (!atomic_test_bit(link.flags, LINK_ACTIVE) && + gen_prov[GPCF(rx->gpc)].require_link) { + BT_DBG("Ignoring message that requires active link"); + return; + } + + BT_DBG("prov_action: %d", GPCF(rx->gpc)); + gen_prov[GPCF(rx->gpc)].func(rx, buf); +} + +void bt_mesh_pb_adv_recv(struct os_mbuf *buf) +{ + struct prov_rx rx; + + if (!bt_prov_active() && bt_mesh_is_provisioned()) { + BT_DBG("Ignoring provisioning PDU - already provisioned"); + return; + } + + if (buf->om_len < 6) { + BT_WARN("Too short provisioning packet (len %u)", buf->om_len); + return; + } + + rx.link_id = net_buf_simple_pull_be32(buf); + rx.xact_id = net_buf_simple_pull_u8(buf); + rx.gpc = net_buf_simple_pull_u8(buf); + + BT_DBG("link_id 0x%08x xact_id %u", (unsigned) rx.link_id, rx.xact_id); + + if (atomic_test_bit(link.flags, LINK_ACTIVE) && link.id != rx.link_id) { + BT_DBG("Ignoring mesh beacon for unknown link"); + return; + } + + gen_prov_recv(&rx, buf); +} +#endif /* MYNEWT_VAL(BLE_MESH_PB_ADV) */ + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +int bt_mesh_pb_gatt_recv(uint16_t conn_handle, struct os_mbuf *buf) +{ + u8_t type; + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (link.conn_handle != conn_handle) { + BT_WARN("Data for unexpected connection"); + return -ENOTCONN; + } + + if (buf->om_len < 1) { + BT_WARN("Too short provisioning packet (len %u)", buf->om_len); + return -EINVAL; + } + + type = net_buf_simple_pull_u8(buf); + if (type != PROV_FAILED && type != link.expect) { + BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); + prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + return -EINVAL; + } + + if (type >= ARRAY_SIZE(prov_handlers)) { + BT_ERR("Unknown provisioning PDU type 0x%02x", type); + return -EINVAL; + } + + if (prov_handlers[type].len != buf->om_len) { + BT_ERR("Invalid length %u for type 0x%02x", buf->om_len, type); + return -EINVAL; + } + + prov_handlers[type].func(buf->om_data); + + return 0; +} + +int bt_mesh_pb_gatt_open(uint16_t conn_handle) +{ + BT_DBG("conn_handle %d", conn_handle); + + if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) { + BT_ERR("Link already opened?"); + return -EBUSY; + } + + link.conn_handle = conn_handle; + link.expect = PROV_INVITE; + + if (prov->link_open) { + prov->link_open(BT_MESH_PROV_GATT); + } + + return 0; +} + +int bt_mesh_pb_gatt_close(uint16_t conn_handle) +{ + BT_DBG("conn_handle %d", conn_handle); + + if (link.conn_handle != conn_handle) { + BT_ERR("Not connected"); + return -ENOTCONN; + } + + if (prov->link_close) { + prov->link_close(BT_MESH_PROV_GATT); + } + + return reset_state(); +} +#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */ + +const struct bt_mesh_prov *bt_mesh_prov_get(void) +{ + return prov; +} + +bool bt_prov_active(void) +{ + return atomic_test_bit(link.flags, LINK_ACTIVE); +} + +static void protocol_timeout(struct ble_npl_event *work) +{ + BT_DBG("Protocol timeout"); + +#if MYNEWT_VAL(BLE_MESH_PB_GATT) + if (link.conn_handle != BLE_HS_CONN_HANDLE_NONE) { + bt_mesh_pb_gatt_close(link.conn_handle); + return; + } +#endif + +#if MYNEWT_VAL(BLE_MESH_PB_ADV) + u8_t reason = CLOSE_REASON_TIMEOUT; + + link.rx.seg = 0U; + bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason)); + + reset_state(); +#endif +} + +int bt_mesh_prov_init(const struct bt_mesh_prov *prov_info) +{ + if (!prov_info) { + BT_ERR("No provisioning context provided"); + return -EINVAL; + } + + k_delayed_work_init(&link.prot_timer, protocol_timeout); + + prov = prov_info; + +#if MYNEWT_VAL(BLE_MESH_PB_ADV) + k_delayed_work_init(&link.tx.retransmit, prov_retransmit); +#endif + + return reset_state(); +} + +void bt_mesh_prov_reset_link(void) +{ +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + link.rx.buf = bt_mesh_proxy_get_buf(); +#else + net_buf_simple_init(rx_buf, 0); + link.rx.buf = rx_buf; +#endif +#endif +} + +void bt_mesh_prov_complete(u16_t net_idx, u16_t addr) +{ + if (prov->complete) { + prov->complete(net_idx, addr); + } +} + +void bt_mesh_prov_reset(void) +{ + if (prov->reset) { + prov->reset(); + } +} + +#endif /* MYNEWT_VAL(BLE_MESH_PROV) */ diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.h new file mode 100644 index 000000000..3675b6cc7 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.h @@ -0,0 +1,33 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __PROV_H__ +#define __PROV_H__ + +#include "os/os_mbuf.h" +#include "mesh/mesh.h" +#include "../src/ble_hs_conn_priv.h" + +void bt_mesh_pb_adv_recv(struct os_mbuf *buf); + +bool bt_prov_active(void); + +int bt_mesh_pb_gatt_open(uint16_t conn_handle); +int bt_mesh_pb_gatt_close(uint16_t conn_handle); +int bt_mesh_pb_gatt_recv(uint16_t conn_handle, struct os_mbuf *buf); + +const struct bt_mesh_prov *bt_mesh_prov_get(void); + +int bt_mesh_prov_init(const struct bt_mesh_prov *prov); + +void bt_mesh_prov_reset_link(void); + +void bt_mesh_prov_complete(u16_t net_idx, u16_t addr); +void bt_mesh_prov_reset(void); + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.c new file mode 100644 index 000000000..609da54d2 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.c @@ -0,0 +1,1464 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_MESH_PROXY) + +#include "mesh/mesh.h" + +#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_PROXY)) +#include "host/ble_hs_log.h" +#include "host/ble_att.h" +#include "services/gatt/ble_svc_gatt.h" +#include "../../host/src/ble_hs_priv.h" + +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "prov.h" +#include "beacon.h" +#include "foundation.h" +#include "access.h" +#include "proxy.h" + +#define PDU_TYPE(data) (data[0] & BIT_MASK(6)) +#define PDU_SAR(data) (data[0] >> 6) + +/* Mesh Profile 1.0 Section 6.6: + * "The timeout for the SAR transfer is 20 seconds. When the timeout + * expires, the Proxy Server shall disconnect." + */ +#define PROXY_SAR_TIMEOUT K_SECONDS(20) + +#define SAR_COMPLETE 0x00 +#define SAR_FIRST 0x01 +#define SAR_CONT 0x02 +#define SAR_LAST 0x03 + +#define CFG_FILTER_SET 0x00 +#define CFG_FILTER_ADD 0x01 +#define CFG_FILTER_REMOVE 0x02 +#define CFG_FILTER_STATUS 0x03 + +/** @def BT_UUID_MESH_PROV + * @brief Mesh Provisioning Service + */ +ble_uuid16_t BT_UUID_MESH_PROV = BLE_UUID16_INIT(0x1827); +#define BT_UUID_MESH_PROV_VAL 0x1827 +/** @def BT_UUID_MESH_PROXY + * @brief Mesh Proxy Service + */ +ble_uuid16_t BT_UUID_MESH_PROXY = BLE_UUID16_INIT(0x1828); +#define BT_UUID_MESH_PROXY_VAL 0x1828 +/** @def BT_UUID_GATT_CCC + * @brief GATT Client Characteristic Configuration + */ +ble_uuid16_t BT_UUID_GATT_CCC = BLE_UUID16_INIT(0x2902); +#define BT_UUID_GATT_CCC_VAL 0x2902 +/** @def BT_UUID_MESH_PROV_DATA_IN + * @brief Mesh Provisioning Data In + */ +ble_uuid16_t BT_UUID_MESH_PROV_DATA_IN = BLE_UUID16_INIT(0x2adb); +#define BT_UUID_MESH_PROV_DATA_IN_VAL 0x2adb +/** @def BT_UUID_MESH_PROV_DATA_OUT + * @brief Mesh Provisioning Data Out + */ +ble_uuid16_t BT_UUID_MESH_PROV_DATA_OUT = BLE_UUID16_INIT(0x2adc); +#define BT_UUID_MESH_PROV_DATA_OUT_VAL 0x2adc +/** @def BT_UUID_MESH_PROXY_DATA_IN + * @brief Mesh Proxy Data In + */ +ble_uuid16_t BT_UUID_MESH_PROXY_DATA_IN = BLE_UUID16_INIT(0x2add); +#define BT_UUID_MESH_PROXY_DATA_IN_VAL 0x2add +/** @def BT_UUID_MESH_PROXY_DATA_OUT + * @brief Mesh Proxy Data Out + */ +ble_uuid16_t BT_UUID_MESH_PROXY_DATA_OUT = BLE_UUID16_INIT(0x2ade); +#define BT_UUID_MESH_PROXY_DATA_OUT_VAL 0x2ade + +#define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6))) + +#define CLIENT_BUF_SIZE 68 + +static const struct ble_gap_adv_params slow_adv_param = { + .conn_mode = (BLE_GAP_CONN_MODE_UND), + .disc_mode = (BLE_GAP_DISC_MODE_GEN), + .itvl_min = BT_GAP_ADV_SLOW_INT_MIN, + .itvl_max = BT_GAP_ADV_SLOW_INT_MAX, +}; + +static const struct ble_gap_adv_params fast_adv_param = { + .conn_mode = (BLE_GAP_CONN_MODE_UND), + .disc_mode = (BLE_GAP_DISC_MODE_GEN), + .itvl_min = BT_GAP_ADV_FAST_INT_MIN_2, + .itvl_max = BT_GAP_ADV_FAST_INT_MAX_2, +}; + +static bool proxy_adv_enabled; + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) +static void proxy_send_beacons(struct ble_npl_event *work); +#endif + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static bool prov_fast_adv; +#endif + +static struct bt_mesh_proxy_client { + uint16_t conn_handle; + u16_t filter[MYNEWT_VAL(BLE_MESH_PROXY_FILTER_SIZE)]; + enum __packed { + NONE, + WHITELIST, + BLACKLIST, + PROV, + } filter_type; + u8_t msg_type; +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + struct ble_npl_callout send_beacons; +#endif + struct k_delayed_work sar_timer; + struct os_mbuf *buf; +} clients[MYNEWT_VAL(BLE_MAX_CONNECTIONS)] = { + [0 ... (MYNEWT_VAL(BLE_MAX_CONNECTIONS) - 1)] = { 0 }, +}; + +/* Track which service is enabled */ +static enum { + MESH_GATT_NONE, + MESH_GATT_PROV, + MESH_GATT_PROXY, +} gatt_svc = MESH_GATT_NONE; + +static struct { + uint16_t proxy_h; + uint16_t proxy_data_out_h; + uint16_t prov_h; + uint16_t prov_data_in_h; + uint16_t prov_data_out_h; +} svc_handles; + +static void resolve_svc_handles(void) +{ + int rc; + + /* Either all handles are already resolved, or none of them */ + if (svc_handles.prov_data_out_h) { + return; + } + + /* + * We assert if attribute is not found since at this stage all attributes + * shall be already registered and thus shall be found. + */ + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), + &svc_handles.proxy_h); + assert(rc == 0); + + rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), + BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_OUT_VAL), + NULL, &svc_handles.proxy_data_out_h); + assert(rc == 0); + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), + &svc_handles.prov_h); + assert(rc == 0); + + rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), + BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_IN_VAL), + NULL, &svc_handles.prov_data_in_h); + assert(rc == 0); + + rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), + BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_OUT_VAL), + NULL, &svc_handles.prov_data_out_h); + assert(rc == 0); +} + +static struct bt_mesh_proxy_client *find_client(uint16_t conn_handle) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn_handle == conn_handle) { + return &clients[i]; + } + } + + return NULL; +} + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) +/* Next subnet in queue to be advertised */ +static int next_idx; + +static int proxy_segment_and_send(uint16_t conn_handle, u8_t type, + struct os_mbuf *msg); + +static int filter_set(struct bt_mesh_proxy_client *client, + struct os_mbuf *buf) +{ + u8_t type; + + if (buf->om_len < 1) { + BT_WARN("Too short Filter Set message"); + return -EINVAL; + } + + type = net_buf_simple_pull_u8(buf); + BT_DBG("type 0x%02x", type); + + switch (type) { + case 0x00: + memset(client->filter, 0, sizeof(client->filter)); + client->filter_type = WHITELIST; + break; + case 0x01: + memset(client->filter, 0, sizeof(client->filter)); + client->filter_type = BLACKLIST; + break; + default: + BT_WARN("Prohibited Filter Type 0x%02x", type); + return -EINVAL; + } + + return 0; +} + +static void filter_add(struct bt_mesh_proxy_client *client, u16_t addr) +{ + int i; + + BT_DBG("addr 0x%04x", addr); + + if (addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + return; + } + } + + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == BT_MESH_ADDR_UNASSIGNED) { + client->filter[i] = addr; + return; + } + } +} + +static void filter_remove(struct bt_mesh_proxy_client *client, u16_t addr) +{ + int i; + + BT_DBG("addr 0x%04x", addr); + + if (addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + client->filter[i] = BT_MESH_ADDR_UNASSIGNED; + return; + } + } +} + +static void send_filter_status(struct bt_mesh_proxy_client *client, + struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_net_tx tx = { + .sub = rx->sub, + .ctx = &rx->ctx, + .src = bt_mesh_primary_addr(), + }; + u16_t filter_size; + int i, err; + + /* Configuration messages always have dst unassigned */ + tx.ctx->addr = BT_MESH_ADDR_UNASSIGNED; + + net_buf_simple_init(buf, 10); + + net_buf_simple_add_u8(buf, CFG_FILTER_STATUS); + + if (client->filter_type == WHITELIST) { + net_buf_simple_add_u8(buf, 0x00); + } else { + net_buf_simple_add_u8(buf, 0x01); + } + + for (filter_size = 0, i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] != BT_MESH_ADDR_UNASSIGNED) { + filter_size++; + } + } + + net_buf_simple_add_be16(buf, filter_size); + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + err = bt_mesh_net_encode(&tx, buf, true); + if (err) { + BT_ERR("Encoding Proxy cfg message failed (err %d)", err); + return; + } + + err = proxy_segment_and_send(client->conn_handle, BT_MESH_PROXY_CONFIG, buf); + if (err) { + BT_ERR("Failed to send proxy cfg message (err %d)", err); + } +} + +static void proxy_cfg(struct bt_mesh_proxy_client *client) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(29); + struct bt_mesh_net_rx rx; + u8_t opcode; + int err; + + err = bt_mesh_net_decode(client->buf, BT_MESH_NET_IF_PROXY_CFG, + &rx, buf); + if (err) { + BT_ERR("Failed to decode Proxy Configuration (err %d)", err); + goto done; + } + + /* Remove network headers */ + net_buf_simple_pull(buf, BT_MESH_NET_HDR_LEN); + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (buf->om_len < 1) { + BT_WARN("Too short proxy configuration PDU"); + goto done; + } + + opcode = net_buf_simple_pull_u8(buf); + switch (opcode) { + case CFG_FILTER_SET: + filter_set(client, buf); + send_filter_status(client, &rx, buf); + break; + case CFG_FILTER_ADD: + while (buf->om_len >= 2) { + u16_t addr; + + addr = net_buf_simple_pull_be16(buf); + filter_add(client, addr); + } + send_filter_status(client, &rx, buf); + break; + case CFG_FILTER_REMOVE: + while (buf->om_len >= 2) { + u16_t addr; + + addr = net_buf_simple_pull_be16(buf); + filter_remove(client, addr); + } + send_filter_status(client, &rx, buf); + break; + default: + BT_WARN("Unhandled configuration OpCode 0x%02x", opcode); + break; + } + +done: + os_mbuf_free_chain(buf); +} + +static int beacon_send(uint16_t conn_handle, struct bt_mesh_subnet *sub) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(23); + int rc; + + net_buf_simple_init(buf, 1); + bt_mesh_beacon_create(sub, buf); + + rc = proxy_segment_and_send(conn_handle, BT_MESH_PROXY_BEACON, buf); + os_mbuf_free_chain(buf); + return rc; +} + +static void proxy_send_beacons(struct ble_npl_event *work) +{ + struct bt_mesh_proxy_client *client; + int i; + + + client = ble_npl_event_get_arg(work); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx != BT_MESH_KEY_UNUSED) { + beacon_send(client->conn_handle, sub); + } + } +} + +static void proxy_sar_timeout(struct ble_npl_event *work) +{ + struct bt_mesh_proxy_client *client; + int rc; + + BT_WARN("Proxy SAR timeout"); + + client = ble_npl_event_get_arg(work); + assert(client != NULL); + + if ((client->conn_handle != BLE_HS_CONN_HANDLE_NONE)) { + rc = ble_gap_terminate(client->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + assert(rc == 0); + } +} + +void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub) +{ + int i; + + if (!sub) { + /* NULL means we send on all subnets */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) { + bt_mesh_proxy_beacon_send(&bt_mesh.sub[i]); + } + } + + return; + } + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) { + beacon_send(clients[i].conn_handle, sub); + } + } +} + +void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub) +{ + sub->node_id = BT_MESH_NODE_IDENTITY_RUNNING; + sub->node_id_start = k_uptime_get_32(); + + /* Prioritize the recently enabled subnet */ + next_idx = sub - bt_mesh.sub; +} + +void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub) +{ + sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED; + sub->node_id_start = 0; +} + +int bt_mesh_proxy_identity_enable(void) +{ + int i, count = 0; + + BT_DBG(""); + + if (!bt_mesh_is_provisioned()) { + return -EAGAIN; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (sub->node_id == BT_MESH_NODE_IDENTITY_NOT_SUPPORTED) { + continue; + } + + bt_mesh_proxy_identity_start(sub); + count++; + } + + if (count) { + bt_mesh_adv_update(); + } + + return 0; +} + +#endif /* GATT_PROXY */ + +static void proxy_complete_pdu(struct bt_mesh_proxy_client *client) +{ + switch (client->msg_type) { +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + case BT_MESH_PROXY_NET_PDU: + BT_INFO("Mesh Network PDU"); + bt_mesh_net_recv(client->buf, 0, BT_MESH_NET_IF_PROXY); + break; + case BT_MESH_PROXY_BEACON: + BT_INFO("Mesh Beacon PDU"); + bt_mesh_beacon_recv(client->buf); + break; + case BT_MESH_PROXY_CONFIG: + BT_INFO("Mesh Configuration PDU"); + proxy_cfg(client); + break; +#endif +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + case BT_MESH_PROXY_PROV: + BT_INFO("Mesh Provisioning PDU"); + bt_mesh_pb_gatt_recv(client->conn_handle, client->buf); + break; +#endif + default: + BT_WARN("Unhandled Message Type 0x%02x", client->msg_type); + break; + } + + net_buf_simple_init(client->buf, 0); +} + +static int proxy_recv(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + struct bt_mesh_proxy_client *client; + const u8_t *data = ctxt->om->om_data; + u16_t len = ctxt->om->om_len; + + client = find_client(conn_handle); + + if (!client) { + return -ENOTCONN; + } + + if (len < 1) { + BT_WARN("Too small Proxy PDU"); + return -EINVAL; + } + + if ((attr_handle == svc_handles.prov_data_in_h) != + (PDU_TYPE(data) == BT_MESH_PROXY_PROV)) { + BT_WARN("Proxy PDU type doesn't match GATT service"); + return -EINVAL; + } + + if (len - 1 > net_buf_simple_tailroom(client->buf)) { + BT_WARN("Too big proxy PDU"); + return -EINVAL; + } + + switch (PDU_SAR(data)) { + case SAR_COMPLETE: + if (client->buf->om_len) { + BT_WARN("Complete PDU while a pending incomplete one"); + return -EINVAL; + } + + client->msg_type = PDU_TYPE(data); + net_buf_simple_add_mem(client->buf, data + 1, len - 1); + proxy_complete_pdu(client); + break; + + case SAR_FIRST: + if (client->buf->om_len) { + BT_WARN("First PDU while a pending incomplete one"); + return -EINVAL; + } + + k_delayed_work_submit(&client->sar_timer, PROXY_SAR_TIMEOUT); + client->msg_type = PDU_TYPE(data); + net_buf_simple_add_mem(client->buf, data + 1, len - 1); + break; + + case SAR_CONT: + if (!client->buf->om_len) { + BT_WARN("Continuation with no prior data"); + return -EINVAL; + } + + if (client->msg_type != PDU_TYPE(data)) { + BT_WARN("Unexpected message type in continuation"); + return -EINVAL; + } + + k_delayed_work_submit(&client->sar_timer, PROXY_SAR_TIMEOUT); + net_buf_simple_add_mem(client->buf, data + 1, len - 1); + break; + + case SAR_LAST: + if (!client->buf->om_len) { + BT_WARN("Last SAR PDU with no prior data"); + return -EINVAL; + } + + if (client->msg_type != PDU_TYPE(data)) { + BT_WARN("Unexpected message type in last SAR PDU"); + return -EINVAL; + } + + k_delayed_work_cancel(&client->sar_timer); + net_buf_simple_add_mem(client->buf, data + 1, len - 1); + proxy_complete_pdu(client); + break; + } + + return len; +} + +static int conn_count; + +static void proxy_connected(uint16_t conn_handle) +{ + struct bt_mesh_proxy_client *client; + int i; + + BT_INFO("conn_handle %d", conn_handle); + + conn_count++; + + /* Since we use ADV_OPT_ONE_TIME */ + proxy_adv_enabled = false; + + /* Try to re-enable advertising in case it's possible */ + if (conn_count < CONFIG_BT_MAX_CONN) { + bt_mesh_adv_update(); + } + + for (client = NULL, i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn_handle == BLE_HS_CONN_HANDLE_NONE) { + client = &clients[i]; + break; + } + } + + if (!client) { + BT_ERR("No free Proxy Client objects"); + return; + } + + client->conn_handle = conn_handle; + client->filter_type = NONE; + memset(client->filter, 0, sizeof(client->filter)); + net_buf_simple_init(client->buf, 0); +} + +static void proxy_disconnected(uint16_t conn_handle, int reason) +{ + int i; + + BT_INFO("conn_handle %d reason %d", conn_handle, reason); + + conn_count--; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + + if (client->conn_handle == conn_handle) { + if ((MYNEWT_VAL(BLE_MESH_PB_GATT)) && + client->filter_type == PROV) { + bt_mesh_pb_gatt_close(conn_handle); + } + + k_delayed_work_cancel(&client->sar_timer); + client->conn_handle = BLE_HS_CONN_HANDLE_NONE; + break; + } + } + + bt_mesh_adv_update(); +} + +struct os_mbuf *bt_mesh_proxy_get_buf(void) +{ + struct os_mbuf *buf = clients[0].buf; + + if (buf != NULL) { + net_buf_simple_init(buf, 0); + } + + return buf; +} + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static void prov_ccc_write(uint16_t conn_handle) +{ + struct bt_mesh_proxy_client *client; + + BT_DBG("conn_handle %d", conn_handle); + + /* If a connection exists there must be a client */ + client = find_client(conn_handle); + __ASSERT(client, "No client for connection"); + + if (client->filter_type == NONE) { + client->filter_type = PROV; + bt_mesh_pb_gatt_open(conn_handle); + } +} + +int bt_mesh_proxy_prov_enable(void) +{ + uint16_t handle; + int rc; + int i; + + BT_DBG(""); + + if (gatt_svc == MESH_GATT_PROV) { + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_NONE) { + return -EBUSY; + } + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), &handle); + assert(rc == 0); + ble_gatts_svc_set_visibility(handle, 1); + /* FIXME: figure out end handle */ + ble_svc_gatt_changed(svc_handles.prov_h, 0xffff); + + gatt_svc = MESH_GATT_PROV; + prov_fast_adv = true; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) { + clients[i].filter_type = PROV; + } + } + + + return 0; +} + +int bt_mesh_proxy_prov_disable(void) +{ + uint16_t handle; + int rc; + int i; + + BT_DBG(""); + + if (gatt_svc == MESH_GATT_NONE) { + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_PROV) { + return -EBUSY; + } + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), &handle); + assert(rc == 0); + ble_gatts_svc_set_visibility(handle, 0); + /* FIXME: figure out end handle */ + ble_svc_gatt_changed(svc_handles.prov_h, 0xffff); + + gatt_svc = MESH_GATT_NONE; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + + if ((client->conn_handle != BLE_HS_CONN_HANDLE_NONE) + && (client->filter_type == PROV)) { + bt_mesh_pb_gatt_close(client->conn_handle); + client->filter_type = NONE; + } + } + + return 0; +} +#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */ + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) +static void proxy_ccc_write(uint16_t conn_handle) +{ + struct bt_mesh_proxy_client *client; + + BT_DBG("conn_handle %d", conn_handle); + + client = find_client(conn_handle); + __ASSERT(client, "No client for connection"); + + if (client->filter_type == NONE) { + client->filter_type = WHITELIST; + k_work_add_arg(&client->send_beacons, client); + k_work_submit(&client->send_beacons); + } +} + +int bt_mesh_proxy_gatt_enable(void) +{ + uint16_t handle; + int rc; + int i; + + BT_DBG(""); + + if (gatt_svc == MESH_GATT_PROXY) { + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_NONE) { + return -EBUSY; + } + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), &handle); + assert(rc == 0); + ble_gatts_svc_set_visibility(handle, 1); + /* FIXME: figure out end handle */ + ble_svc_gatt_changed(svc_handles.proxy_h, 0xffff); + + gatt_svc = MESH_GATT_PROXY; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) { + clients[i].filter_type = WHITELIST; + } + } + + return 0; +} + +void bt_mesh_proxy_gatt_disconnect(void) +{ + int rc; + int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + + if ((client->conn_handle != BLE_HS_CONN_HANDLE_NONE) && + (client->filter_type == WHITELIST || + client->filter_type == BLACKLIST)) { + client->filter_type = NONE; + rc = ble_gap_terminate(client->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + assert(rc == 0); + } + } +} + +int bt_mesh_proxy_gatt_disable(void) +{ + uint16_t handle; + int rc; + + BT_DBG(""); + + if (gatt_svc == MESH_GATT_NONE) { + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_PROXY) { + return -EBUSY; + } + + bt_mesh_proxy_gatt_disconnect(); + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), &handle); + assert(rc == 0); + ble_gatts_svc_set_visibility(handle, 0); + /* FIXME: figure out end handle */ + ble_svc_gatt_changed(svc_handles.proxy_h, 0xffff); + + gatt_svc = MESH_GATT_NONE; + + return 0; +} + +void bt_mesh_proxy_addr_add(struct os_mbuf *buf, u16_t addr) +{ + struct bt_mesh_proxy_client *client = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + client = &clients[i]; + if (client->buf == buf) { + break; + } + } + + assert(client); + + BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr); + + if (client->filter_type == WHITELIST) { + filter_add(client, addr); + } else if (client->filter_type == BLACKLIST) { + filter_remove(client, addr); + } +} + +static bool client_filter_match(struct bt_mesh_proxy_client *client, + u16_t addr) +{ + int i; + + BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr); + + if (client->filter_type == WHITELIST) { + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + return true; + } + } + + return false; + } + + if (client->filter_type == BLACKLIST) { + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + return false; + } + } + + return true; + } + + return false; +} + +bool bt_mesh_proxy_relay(struct os_mbuf *buf, u16_t dst) +{ + bool relayed = false; + int i; + + BT_DBG("%u bytes to dst 0x%04x", buf->om_len, dst); + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + struct os_mbuf *msg; + + if (client->conn_handle == BLE_HS_CONN_HANDLE_NONE) { + continue; + } + + if (!client_filter_match(client, dst)) { + continue; + } + + /* Proxy PDU sending modifies the original buffer, + * so we need to make a copy. + */ + msg = NET_BUF_SIMPLE(32); + net_buf_simple_init(msg, 1); + net_buf_simple_add_mem(msg, buf->om_data, buf->om_len); + + bt_mesh_proxy_send(client->conn_handle, BT_MESH_PROXY_NET_PDU, msg); + os_mbuf_free_chain(msg); + relayed = true; + } + + return relayed; +} + +#endif /* MYNEWT_VAL(BLE_MESH_GATT_PROXY) */ + +static int proxy_send(uint16_t conn_handle, const void *data, u16_t len) +{ + struct os_mbuf *om; + + BT_DBG("%u bytes: %s", len, bt_hex(data, len)); + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + if (gatt_svc == MESH_GATT_PROXY) { + om = ble_hs_mbuf_from_flat(data, len); + assert(om); + ble_gattc_notify_custom(conn_handle, svc_handles.proxy_data_out_h, om); + } +#endif + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + if (gatt_svc == MESH_GATT_PROV) { + om = ble_hs_mbuf_from_flat(data, len); + assert(om); + ble_gattc_notify_custom(conn_handle, svc_handles.prov_data_out_h, om); + } +#endif + + return 0; +} + +static int proxy_segment_and_send(uint16_t conn_handle, u8_t type, + struct os_mbuf *msg) +{ + u16_t mtu; + + BT_DBG("conn_handle %d type 0x%02x len %u: %s", conn_handle, type, msg->om_len, + bt_hex(msg->om_data, msg->om_len)); + + /* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */ + mtu = ble_att_mtu(conn_handle) - 3; + if (mtu > msg->om_len) { + net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type)); + return proxy_send(conn_handle, msg->om_data, msg->om_len); + } + + net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type)); + proxy_send(conn_handle, msg->om_data, mtu); + net_buf_simple_pull(msg, mtu); + + while (msg->om_len) { + if (msg->om_len + 1 < mtu) { + net_buf_simple_push_u8(msg, PDU_HDR(SAR_LAST, type)); + proxy_send(conn_handle, msg->om_data, msg->om_len); + break; + } + + net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type)); + proxy_send(conn_handle, msg->om_data, mtu); + net_buf_simple_pull(msg, mtu); + } + + return 0; +} + +int bt_mesh_proxy_send(uint16_t conn_handle, u8_t type, + struct os_mbuf *msg) +{ + struct bt_mesh_proxy_client *client = find_client(conn_handle); + + if (!client) { + BT_ERR("No Proxy Client found"); + return -ENOTCONN; + } + + if ((client->filter_type == PROV) != (type == BT_MESH_PROXY_PROV)) { + BT_ERR("Invalid PDU type for Proxy Client"); + return -EINVAL; + } + + return proxy_segment_and_send(conn_handle, type, msg); +} + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static u8_t prov_svc_data[20] = { 0x27, 0x18, }; + +static const struct bt_data prov_ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x27, 0x18), + BT_DATA(BT_DATA_SVC_DATA16, prov_svc_data, sizeof(prov_svc_data)), +}; +#endif /* PB_GATT */ + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + +#define ID_TYPE_NET 0x00 +#define ID_TYPE_NODE 0x01 + +#define NODE_ID_LEN 19 +#define NET_ID_LEN 11 + +#define NODE_ID_TIMEOUT K_SECONDS(CONFIG_BT_MESH_NODE_ID_TIMEOUT) + +static u8_t proxy_svc_data[NODE_ID_LEN] = { 0x28, 0x18, }; + +static const struct bt_data node_id_ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x28, 0x18), + BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NODE_ID_LEN), +}; + +static const struct bt_data net_id_ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x28, 0x18), + BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NET_ID_LEN), +}; + +static int node_id_adv(struct bt_mesh_subnet *sub) +{ + u8_t tmp[16]; + int err; + + BT_DBG(""); + + proxy_svc_data[2] = ID_TYPE_NODE; + + err = bt_rand(proxy_svc_data + 11, 8); + if (err) { + return err; + } + + memset(tmp, 0, 6); + memcpy(tmp + 6, proxy_svc_data + 11, 8); + sys_put_be16(bt_mesh_primary_addr(), tmp + 14); + + err = bt_encrypt_be(sub->keys[sub->kr_flag].identity, tmp, tmp); + if (err) { + return err; + } + + memcpy(proxy_svc_data + 3, tmp + 8, 8); + + err = bt_le_adv_start(&fast_adv_param, node_id_ad, + ARRAY_SIZE(node_id_ad), NULL, 0); + if (err) { + BT_WARN("Failed to advertise using Node ID (err %d)", err); + return err; + } + + proxy_adv_enabled = true; + + return 0; +} + +static int net_id_adv(struct bt_mesh_subnet *sub) +{ + int err; + + BT_DBG(""); + + proxy_svc_data[2] = ID_TYPE_NET; + + BT_DBG("Advertising with NetId %s", + bt_hex(sub->keys[sub->kr_flag].net_id, 8)); + + memcpy(proxy_svc_data + 3, sub->keys[sub->kr_flag].net_id, 8); + + err = bt_le_adv_start(&slow_adv_param, net_id_ad, + ARRAY_SIZE(net_id_ad), NULL, 0); + if (err) { + BT_WARN("Failed to advertise using Network ID (err %d)", err); + return err; + } + + proxy_adv_enabled = true; + + return 0; +} + +static bool advertise_subnet(struct bt_mesh_subnet *sub) +{ + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + return false; + } + + return (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING || + bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED); +} + +static struct bt_mesh_subnet *next_sub(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub; + + sub = &bt_mesh.sub[(i + next_idx) % ARRAY_SIZE(bt_mesh.sub)]; + if (advertise_subnet(sub)) { + next_idx = (next_idx + 1) % ARRAY_SIZE(bt_mesh.sub); + return sub; + } + } + + return NULL; +} + +static int sub_count(void) +{ + int i, count = 0; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (advertise_subnet(sub)) { + count++; + } + } + + return count; +} + +static s32_t gatt_proxy_advertise(struct bt_mesh_subnet *sub) +{ + s32_t remaining = K_FOREVER; + int subnet_count; + + BT_DBG(""); + + if (conn_count == CONFIG_BT_MAX_CONN) { + BT_WARN("Connectable advertising deferred (max connections)"); + return remaining; + } + + if (!sub) { + BT_WARN("No subnets to advertise on"); + return remaining; + } + + if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) { + u32_t active = k_uptime_get_32() - sub->node_id_start; + + if (active < NODE_ID_TIMEOUT) { + remaining = NODE_ID_TIMEOUT - active; + BT_DBG("Node ID active for %u ms, %d ms remaining", + (unsigned) active, (int) remaining); + node_id_adv(sub); + } else { + bt_mesh_proxy_identity_stop(sub); + BT_DBG("Node ID stopped"); + } + } + + if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED) { + if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) { + net_id_adv(sub); + } else { + return gatt_proxy_advertise(next_sub()); + } + } + + subnet_count = sub_count(); + BT_DBG("sub_count %u", subnet_count); + if (subnet_count > 1) { + s32_t max_timeout; + + /* We use NODE_ID_TIMEOUT as a starting point since it may + * be less than 60 seconds. Divide this period into at least + * 6 slices, but make sure that a slice is at least one + * second long (to avoid excessive rotation). + */ + max_timeout = NODE_ID_TIMEOUT / max(subnet_count, 6); + max_timeout = max(max_timeout, K_SECONDS(1)); + + if (remaining > max_timeout || remaining < 0) { + remaining = max_timeout; + } + } + + BT_DBG("Advertising %d ms for net_idx 0x%04x", + (int) remaining, sub->net_idx); + + return remaining; +} +#endif /* GATT_PROXY */ + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static size_t gatt_prov_adv_create(struct bt_data prov_sd[2]) +{ + const struct bt_mesh_prov *prov = bt_mesh_prov_get(); + const char *name = CONFIG_BT_DEVICE_NAME; + size_t name_len = strlen(name); + size_t prov_sd_len = 0; + size_t sd_space = 31; + + memcpy(prov_svc_data + 2, prov->uuid, 16); + sys_put_be16(prov->oob_info, prov_svc_data + 18); + + if (prov->uri) { + size_t uri_len = strlen(prov->uri); + + if (uri_len > 29) { + /* There's no way to shorten an URI */ + BT_WARN("Too long URI to fit advertising packet"); + } else { + prov_sd[0].type = BT_DATA_URI; + prov_sd[0].data_len = uri_len; + prov_sd[0].data = (void *)prov->uri; + sd_space -= 2 + uri_len; + prov_sd_len++; + } + } + + if (sd_space > 2 && name_len > 0) { + sd_space -= 2; + + if (sd_space < name_len) { + prov_sd[prov_sd_len].type = BT_DATA_NAME_SHORTENED; + prov_sd[prov_sd_len].data_len = sd_space; + } else { + prov_sd[prov_sd_len].type = BT_DATA_NAME_COMPLETE; + prov_sd[prov_sd_len].data_len = name_len; + } + + prov_sd[prov_sd_len].data = (void *)name; + prov_sd_len++; + } + + return prov_sd_len; +} +#endif /* PB_GATT */ + +s32_t bt_mesh_proxy_adv_start(void) +{ + BT_DBG(""); + + if (gatt_svc == MESH_GATT_NONE) { + return K_FOREVER; + } + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + if (!bt_mesh_is_provisioned()) { + const struct ble_gap_adv_params *param; + struct bt_data prov_sd[2]; + size_t prov_sd_len; + + if (prov_fast_adv) { + param = &fast_adv_param; + } else { + param = &slow_adv_param; + } + + prov_sd_len = gatt_prov_adv_create(prov_sd); + + if (bt_le_adv_start(param, prov_ad, ARRAY_SIZE(prov_ad), + prov_sd, prov_sd_len) == 0) { + proxy_adv_enabled = true; + + /* Advertise 60 seconds using fast interval */ + if (prov_fast_adv) { + prov_fast_adv = false; + return K_SECONDS(60); + } + } + } +#endif /* PB_GATT */ + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + if (bt_mesh_is_provisioned()) { + return gatt_proxy_advertise(next_sub()); + } +#endif /* GATT_PROXY */ + + return K_FOREVER; +} + +void bt_mesh_proxy_adv_stop(void) +{ + int err; + + BT_DBG("adv_enabled %u", proxy_adv_enabled); + + if (!proxy_adv_enabled) { + return; + } + + err = bt_le_adv_stop(true); + if (err) { + BT_ERR("Failed to stop advertising (err %d)", err); + } else { + proxy_adv_enabled = false; + } +} + +int +ble_mesh_proxy_gap_event(struct ble_gap_event *event, void *arg) +{ + + if (event->type == BLE_GAP_EVENT_CONNECT) { + proxy_connected(event->connect.conn_handle); + } else if (event->type == BLE_GAP_EVENT_DISCONNECT) { + proxy_disconnected(event->disconnect.conn.conn_handle, + event->disconnect.reason); + } else if (event->type == BLE_GAP_EVENT_SUBSCRIBE) { + if (event->subscribe.attr_handle == svc_handles.proxy_data_out_h) { +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + proxy_ccc_write(event->subscribe.conn_handle); +#endif + } else if (event->subscribe.attr_handle == + svc_handles.prov_data_out_h) { +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + prov_ccc_write(event->subscribe.conn_handle); +#endif + } + } + + return 0; +} + +static int +dummy_access_cb(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + /* + * We should never never enter this callback - it's attached to notify-only + * characteristic which are notified directly from mbuf. And we can't pass + * NULL as access_cb because gatts will assert on init... + */ + BLE_HS_DBG_ASSERT(0); + return 0; +} + +static const struct ble_gatt_svc_def svc_defs [] = { + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_IN_VAL), + .access_cb = proxy_recv, + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, + }, { + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_OUT_VAL), + .access_cb = dummy_access_cb, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_IN_VAL), + .access_cb = proxy_recv, + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, + }, { + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_OUT_VAL), + .access_cb = dummy_access_cb, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, { + 0, /* No more services. */ + }, +}; + +int bt_mesh_proxy_svcs_register(void) +{ + int rc; + + rc = ble_gatts_count_cfg(svc_defs); + assert(rc == 0); + + rc = ble_gatts_add_svcs(svc_defs); + assert(rc == 0); + + return 0; +} + +int bt_mesh_proxy_init(void) +{ + int i; + + for (i = 0; i < MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) { +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + k_work_init(&clients[i].send_beacons, proxy_send_beacons); +#endif + clients[i].buf = NET_BUF_SIMPLE(CLIENT_BUF_SIZE); + clients[i].conn_handle = BLE_HS_CONN_HANDLE_NONE; + + k_delayed_work_init(&clients[i].sar_timer, proxy_sar_timeout); + k_delayed_work_add_arg(&clients[i].sar_timer, &clients[i]); + } + + resolve_svc_handles(); + + ble_gatts_svc_set_visibility(svc_handles.proxy_h, 0); + ble_gatts_svc_set_visibility(svc_handles.prov_h, 0); + + return 0; +} + +#endif /* MYNEWT_VAL(BLE_MESH_PROXY) */ diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.h new file mode 100644 index 000000000..9f19be07b --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.h @@ -0,0 +1,45 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __PROXY_H__ +#define __PROXY_H__ + +#define BT_MESH_PROXY_NET_PDU 0x00 +#define BT_MESH_PROXY_BEACON 0x01 +#define BT_MESH_PROXY_CONFIG 0x02 +#define BT_MESH_PROXY_PROV 0x03 + +#include "mesh/mesh.h" + +int bt_mesh_proxy_send(uint16_t conn_handle, u8_t type, struct os_mbuf *msg); + +int bt_mesh_proxy_prov_enable(void); +int bt_mesh_proxy_prov_disable(void); + +int bt_mesh_proxy_gatt_enable(void); +int bt_mesh_proxy_gatt_disable(void); +void bt_mesh_proxy_gatt_disconnect(void); + +void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub); + +struct os_mbuf *bt_mesh_proxy_get_buf(void); + +s32_t bt_mesh_proxy_adv_start(void); +void bt_mesh_proxy_adv_stop(void); + +void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub); +void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub); + +bool bt_mesh_proxy_relay(struct os_mbuf *buf, u16_t dst); +void bt_mesh_proxy_addr_add(struct os_mbuf *buf, u16_t addr); + +int bt_mesh_proxy_init(void); + +int ble_mesh_proxy_gap_event(struct ble_gap_event *event, void *arg); + +#endif diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.c new file mode 100644 index 000000000..f0f4a27ea --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.c @@ -0,0 +1,1575 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) + +#define BT_DBG_ENABLED MYNEWT_VAL(BLE_MESH_DEBUG_SETTINGS) + +#include "mesh/mesh.h" +#include "mesh/glue.h" +#include "net.h" +#include "crypto.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "proxy.h" +#include "settings.h" + +#include "config/config.h" + +/* Tracking of what storage changes are pending for App and Net Keys. We + * track this in a separate array here instead of within the respective + * bt_mesh_app_key and bt_mesh_subnet structs themselves, since once a key + * gets deleted its struct becomes invalid and may be reused for other keys. + */ +static struct key_update { + u16_t key_idx:12, /* AppKey or NetKey Index */ + valid:1, /* 1 if this entry is valid, 0 if not */ + app_key:1, /* 1 if this is an AppKey, 0 if a NetKey */ + clear:1; /* 1 if key needs clearing, 0 if storing */ +} key_updates[CONFIG_BT_MESH_APP_KEY_COUNT + CONFIG_BT_MESH_SUBNET_COUNT]; + +static struct k_delayed_work pending_store; + +/* Mesh network storage information */ +struct net_val { + u16_t primary_addr; + u8_t dev_key[16]; +} __packed; + +/* Sequence number storage */ +struct seq_val { + u8_t val[3]; +} __packed; + +/* Heartbeat Publication storage */ +struct hb_pub_val { + u16_t dst; + u8_t period; + u8_t ttl; + u16_t feat; + u16_t net_idx:12, + indefinite:1; +}; + +/* Miscelaneous configuration server model states */ +struct cfg_val { + u8_t net_transmit; + u8_t relay; + u8_t relay_retransmit; + u8_t beacon; + u8_t gatt_proxy; + u8_t frnd; + u8_t default_ttl; +}; + +/* IV Index & IV Update storage */ +struct iv_val { + u32_t iv_index; + u8_t iv_update:1, + iv_duration:7; +} __packed; + +/* Replay Protection List storage */ +struct rpl_val { + u32_t seq:24, + old_iv:1; +}; + +/* NetKey storage information */ +struct net_key_val { + u8_t kr_flag:1, + kr_phase:7; + u8_t val[2][16]; +} __packed; + +/* AppKey storage information */ +struct app_key_val { + u16_t net_idx; + bool updated; + u8_t val[2][16]; +} __packed; + +struct mod_pub_val { + u16_t addr; + u16_t key; + u8_t ttl; + u8_t retransmit; + u8_t period; + u8_t period_div:4, + cred:1; +}; + +/* We need this so we don't overwrite app-hardcoded values in case FCB + * contains a history of changes but then has a NULL at the end. + */ +static struct { + bool valid; + struct cfg_val cfg; +} stored_cfg; + +static int net_set(int argc, char **argv, char *val) +{ + struct net_val net; + int len, err; + + BT_DBG("val %s", val ? val : "(null)"); + + if (!val) { + bt_mesh_comp_unprovision(); + memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key)); + return 0; + } + + len = sizeof(net); + err = settings_bytes_from_str(val, &net, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(net)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(net)); + return -EINVAL; + } + + memcpy(bt_mesh.dev_key, net.dev_key, sizeof(bt_mesh.dev_key)); + bt_mesh_comp_provision(net.primary_addr); + + BT_DBG("Provisioned with primary address 0x%04x", net.primary_addr); + BT_DBG("Recovered DevKey %s", bt_hex(bt_mesh.dev_key, 16)); + + return 0; +} + +static int iv_set(int argc, char **argv, char *val) +{ + struct iv_val iv; + int len, err; + + BT_DBG("val %s", val ? val : "(null)"); + + if (!val) { + bt_mesh.iv_index = 0U; + atomic_clear_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS); + return 0; + } + + len = sizeof(iv); + err = settings_bytes_from_str(val, &iv, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(iv)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(iv)); + return -EINVAL; + } + + bt_mesh.iv_index = iv.iv_index; + atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv.iv_update); + bt_mesh.ivu_duration = iv.iv_duration; + + BT_DBG("IV Index 0x%04x (IV Update Flag %u) duration %u hours", + (unsigned) iv.iv_index, iv.iv_update, iv.iv_duration); + + return 0; +} + +static int seq_set(int argc, char **argv, char *val) +{ + struct seq_val seq; + int len, err; + + BT_DBG("val %s", val ? val : "(null)"); + + if (!val) { + bt_mesh.seq = 0; + return 0; + } + + len = sizeof(seq); + err = settings_bytes_from_str(val, &seq, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(seq)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(seq)); + return -EINVAL; + } + + bt_mesh.seq = ((u32_t)seq.val[0] | ((u32_t)seq.val[1] << 8) | + ((u32_t)seq.val[2] << 16)); + + if (CONFIG_BT_MESH_SEQ_STORE_RATE > 0) { + /* Make sure we have a large enough sequence number. We + * subtract 1 so that the first transmission causes a write + * to the settings storage. + */ + bt_mesh.seq += (CONFIG_BT_MESH_SEQ_STORE_RATE - + (bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)); + bt_mesh.seq--; + } + + BT_DBG("Sequence Number 0x%06x", bt_mesh.seq); + + return 0; +} + +static struct bt_mesh_rpl *rpl_find(u16_t src) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + if (bt_mesh.rpl[i].src == src) { + return &bt_mesh.rpl[i]; + } + } + + return NULL; +} + +static struct bt_mesh_rpl *rpl_alloc(u16_t src) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + if (!bt_mesh.rpl[i].src) { + bt_mesh.rpl[i].src = src; + return &bt_mesh.rpl[i]; + } + } + + return NULL; +} + +static int rpl_set(int argc, char **argv, char *val) +{ + struct bt_mesh_rpl *entry; + struct rpl_val rpl; + int len, err; + u16_t src; + + if (argc < 1) { + BT_ERR("Invalid argc (%d)", argc); + return -ENOENT; + } + + BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); + + src = strtol(argv[0], NULL, 16); + entry = rpl_find(src); + + if (!val) { + if (entry) { + memset(entry, 0, sizeof(*entry)); + } else { + BT_WARN("Unable to find RPL entry for 0x%04x", src); + } + + return 0; + } + + if (!entry) { + entry = rpl_alloc(src); + if (!entry) { + BT_ERR("Unable to allocate RPL entry for 0x%04x", src); + return -ENOMEM; + } + } + + len = sizeof(rpl); + err = settings_bytes_from_str(val, &rpl, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(rpl)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(rpl)); + return -EINVAL; + } + + entry->seq = rpl.seq; + entry->old_iv = rpl.old_iv; + + BT_DBG("RPL entry for 0x%04x: Seq 0x%06x old_iv %u", entry->src, + (unsigned) entry->seq, entry->old_iv); + + return 0; +} + +static int net_key_set(int argc, char **argv, char *val) +{ + struct bt_mesh_subnet *sub; + struct net_key_val key; + int len, i, err; + u16_t net_idx; + + BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); + + net_idx = strtol(argv[0], NULL, 16); + sub = bt_mesh_subnet_get(net_idx); + + if (!val) { + if (!sub) { + BT_ERR("No subnet with NetKeyIndex 0x%03x", net_idx); + return -ENOENT; + } + + BT_DBG("Deleting NetKeyIndex 0x%03x", net_idx); + bt_mesh_subnet_del(sub, false); + return 0; + } + + len = sizeof(key); + err = settings_bytes_from_str(val, &key, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(key)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(key)); + return -EINVAL; + } + + if (sub) { + BT_DBG("Updating existing NetKeyIndex 0x%03x", net_idx); + + sub->kr_flag = key.kr_flag; + sub->kr_phase = key.kr_phase; + memcpy(sub->keys[0].net, &key.val[0], 16); + memcpy(sub->keys[1].net, &key.val[1], 16); + + return 0; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) { + sub = &bt_mesh.sub[i]; + break; + } + } + + if (!sub) { + BT_ERR("No space to allocate a new subnet"); + return -ENOMEM; + } + + sub->net_idx = net_idx; + sub->kr_flag = key.kr_flag; + sub->kr_phase = key.kr_phase; + memcpy(sub->keys[0].net, &key.val[0], 16); + memcpy(sub->keys[1].net, &key.val[1], 16); + + BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx); + + return 0; +} + +static int app_key_set(int argc, char **argv, char *val) +{ + struct bt_mesh_app_key *app; + struct app_key_val key; + u16_t app_idx; + int len, err; + + BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); + + app_idx = strtol(argv[0], NULL, 16); + + if (!val) { + BT_DBG("Deleting AppKeyIndex 0x%03x", app_idx); + + app = bt_mesh_app_key_find(app_idx); + if (app) { + bt_mesh_app_key_del(app, false); + } + + return 0; + } + + len = sizeof(key); + err = settings_bytes_from_str(val, &key, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(key)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(key)); + return -EINVAL; + } + + app = bt_mesh_app_key_find(app_idx); + if (!app) { + app = bt_mesh_app_key_alloc(app_idx); + } + + if (!app) { + BT_ERR("No space for a new app key"); + return -ENOMEM; + } + + app->net_idx = key.net_idx; + app->app_idx = app_idx; + app->updated = key.updated; + memcpy(app->keys[0].val, key.val[0], 16); + memcpy(app->keys[1].val, key.val[1], 16); + + bt_mesh_app_id(app->keys[0].val, &app->keys[0].id); + bt_mesh_app_id(app->keys[1].val, &app->keys[1].id); + + BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx); + + return 0; +} + +static int hb_pub_set(int argc, char **argv, char *val) +{ + struct bt_mesh_hb_pub *pub = bt_mesh_hb_pub_get(); + struct hb_pub_val hb_val; + int len, err; + + BT_DBG("val %s", val ? val : "(null)"); + + if (!pub) { + return -ENOENT; + } + + if (!val) { + pub->dst = BT_MESH_ADDR_UNASSIGNED; + pub->count = 0; + pub->ttl = 0; + pub->period = 0; + pub->feat = 0; + + BT_DBG("Cleared heartbeat publication"); + return 0; + } + + len = sizeof(hb_val); + err = settings_bytes_from_str(val, &hb_val, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(hb_val)) { + BT_ERR("Unexpected value length (%d != %zu)", len, + sizeof(hb_val)); + return -EINVAL; + } + + pub->dst = hb_val.dst; + pub->period = hb_val.period; + pub->ttl = hb_val.ttl; + pub->feat = hb_val.feat; + pub->net_idx = hb_val.net_idx; + + if (hb_val.indefinite) { + pub->count = 0xffff; + } else { + pub->count = 0; + } + + BT_DBG("Restored heartbeat publication"); + + return 0; +} + +static int cfg_set(int argc, char **argv, char *val) +{ + struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get(); + int len, err; + + BT_DBG("val %s", val ? val : "(null)"); + + if (!cfg) { + return -ENOENT; + } + + if (!val) { + stored_cfg.valid = false; + BT_DBG("Cleared configuration state"); + return 0; + } + + len = sizeof(stored_cfg.cfg); + err = settings_bytes_from_str(val, &stored_cfg.cfg, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(stored_cfg.cfg)) { + BT_ERR("Unexpected value length (%d != %zu)", len, + sizeof(stored_cfg.cfg)); + return -EINVAL; + } + + stored_cfg.valid = true; + BT_DBG("Restored configuration state"); + + return 0; +} + +static int mod_set_bind(struct bt_mesh_model *mod, char *val) +{ + int len, err, i; + + /* Start with empty array regardless of cleared or set value */ + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + mod->keys[i] = BT_MESH_KEY_UNUSED; + } + + if (!val) { + BT_DBG("Cleared bindings for model"); + return 0; + } + + len = sizeof(mod->keys); + err = settings_bytes_from_str(val, mod->keys, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return -EINVAL; + } + + BT_DBG("Decoded %u bound keys for model", len / sizeof(mod->keys[0])); + return 0; +} + +static int mod_set_sub(struct bt_mesh_model *mod, char *val) +{ + int len, err; + + /* Start with empty array regardless of cleared or set value */ + memset(mod->groups, 0, sizeof(mod->groups)); + + if (!val) { + BT_DBG("Cleared subscriptions for model"); + return 0; + } + + len = sizeof(mod->groups); + err = settings_bytes_from_str(val, mod->groups, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return -EINVAL; + } + + BT_DBG("Decoded %u subscribed group addresses for model", + len / sizeof(mod->groups[0])); + return 0; +} + +static int mod_set_pub(struct bt_mesh_model *mod, char *val) +{ + struct mod_pub_val pub; + int len, err; + + if (!mod->pub) { + BT_WARN("Model has no publication context!"); + return -EINVAL; + } + + if (!val) { + mod->pub->addr = BT_MESH_ADDR_UNASSIGNED; + mod->pub->key = 0; + mod->pub->cred = 0; + mod->pub->ttl = 0; + mod->pub->period = 0; + mod->pub->retransmit = 0; + mod->pub->count = 0; + + BT_DBG("Cleared publication for model"); + return 0; + } + + len = sizeof(pub); + err = settings_bytes_from_str(val, &pub, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return -EINVAL; + } + + if (len != sizeof(pub)) { + BT_ERR("Invalid length for model publication"); + return -EINVAL; + } + + mod->pub->addr = pub.addr; + mod->pub->key = pub.key; + mod->pub->cred = pub.cred; + mod->pub->ttl = pub.ttl; + mod->pub->period = pub.period; + mod->pub->retransmit = pub.retransmit; + mod->pub->count = 0; + + BT_DBG("Restored model publication, dst 0x%04x app_idx 0x%03x", + pub.addr, pub.key); + + return 0; +} + +static int mod_set(bool vnd, int argc, char **argv, char *val) +{ + struct bt_mesh_model *mod; + u8_t elem_idx, mod_idx; + u16_t mod_key; + + if (argc < 2) { + BT_ERR("Too small argc (%d)", argc); + return -ENOENT; + } + + mod_key = strtol(argv[0], NULL, 16); + elem_idx = mod_key >> 8; + mod_idx = mod_key; + + BT_DBG("Decoded mod_key 0x%04x as elem_idx %u mod_idx %u", + mod_key, elem_idx, mod_idx); + + mod = bt_mesh_model_get(vnd, elem_idx, mod_idx); + if (!mod) { + BT_ERR("Failed to get model for elem_idx %u mod_idx %u", + elem_idx, mod_idx); + return -ENOENT; + } + + if (!strcmp(argv[1], "bind")) { + return mod_set_bind(mod, val); + } + + if (!strcmp(argv[1], "sub")) { + return mod_set_sub(mod, val); + } + + if (!strcmp(argv[1], "pub")) { + return mod_set_pub(mod, val); + } + + BT_WARN("Unknown module key %s", argv[1]); + return -ENOENT; +} + +static int sig_mod_set(int argc, char **argv, char *val) +{ + return mod_set(false, argc, argv, val); +} + +static int vnd_mod_set(int argc, char **argv, char *val) +{ + return mod_set(true, argc, argv, val); +} + +const struct mesh_setting { + const char *name; + int (*func)(int argc, char **argv, char *val); +} settings[] = { + { "Net", net_set }, + { "IV", iv_set }, + { "Seq", seq_set }, + { "RPL", rpl_set }, + { "NetKey", net_key_set }, + { "AppKey", app_key_set }, + { "HBPub", hb_pub_set }, + { "Cfg", cfg_set }, + { "s", sig_mod_set }, + { "v", vnd_mod_set }, +}; + +static int mesh_set(int argc, char **argv, char *val) +{ + int i; + + if (argc < 1) { + BT_ERR("Insufficient number of arguments"); + return -EINVAL; + } + + BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); + + for (i = 0; i < ARRAY_SIZE(settings); i++) { + if (!strcmp(settings[i].name, argv[0])) { + argc--; + argv++; + + return settings[i].func(argc, argv, val); + } + } + + BT_WARN("No matching handler for key %s", argv[0]); + + return -ENOENT; +} + +static int subnet_init(struct bt_mesh_subnet *sub) +{ + int err; + + err = bt_mesh_net_keys_create(&sub->keys[0], sub->keys[0].net); + if (err) { + BT_ERR("Unable to generate keys for subnet"); + return -EIO; + } + + if (sub->kr_phase != BT_MESH_KR_NORMAL) { + err = bt_mesh_net_keys_create(&sub->keys[1], sub->keys[1].net); + if (err) { + BT_ERR("Unable to generate keys for subnet"); + memset(&sub->keys[0], 0, sizeof(sub->keys[0])); + return -EIO; + } + } + + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { + sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED; + } else { + sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED; + } + + /* Make sure we have valid beacon data to be sent */ + bt_mesh_net_beacon_update(sub); + + return 0; +} + +static void commit_mod(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (mod->pub && mod->pub->update && + mod->pub->addr != BT_MESH_ADDR_UNASSIGNED) { + s32_t ms = bt_mesh_model_pub_period_get(mod); + if (ms) { + BT_DBG("Starting publish timer (period %u ms)", + (unsigned) ms); + k_delayed_work_submit(&mod->pub->timer, ms); + } + } +} + +static int mesh_commit(void) +{ + struct bt_mesh_hb_pub *hb_pub; + struct bt_mesh_cfg_srv *cfg; + int i; + + BT_DBG("sub[0].net_idx 0x%03x", bt_mesh.sub[0].net_idx); + + if (bt_mesh.sub[0].net_idx == BT_MESH_KEY_UNUSED) { + /* Nothing to do since we're not yet provisioned */ + return 0; + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) { + bt_mesh_proxy_prov_disable(); + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + int err; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + err = subnet_init(sub); + if (err) { + BT_ERR("Failed to init subnet 0x%03x", sub->net_idx); + } + } + + if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) { + k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT); + } + + bt_mesh_model_foreach(commit_mod, NULL); + + hb_pub = bt_mesh_hb_pub_get(); + if (hb_pub && hb_pub->dst != BT_MESH_ADDR_UNASSIGNED && + hb_pub->count && hb_pub->period) { + BT_DBG("Starting heartbeat publication"); + k_work_submit(&hb_pub->timer.work); + } + + cfg = bt_mesh_cfg_get(); + if (cfg && stored_cfg.valid) { + cfg->net_transmit = stored_cfg.cfg.net_transmit; + cfg->relay = stored_cfg.cfg.relay; + cfg->relay_retransmit = stored_cfg.cfg.relay_retransmit; + cfg->beacon = stored_cfg.cfg.beacon; + cfg->gatt_proxy = stored_cfg.cfg.gatt_proxy; + cfg->frnd = stored_cfg.cfg.frnd; + cfg->default_ttl = stored_cfg.cfg.default_ttl; + } + + atomic_set_bit(bt_mesh.flags, BT_MESH_VALID); + + bt_mesh_net_start(); + + return 0; +} + +static void schedule_store(int flag) +{ + s32_t timeout; + + atomic_set_bit(bt_mesh.flags, flag); + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_NET_PENDING) || + atomic_test_bit(bt_mesh.flags, BT_MESH_IV_PENDING) || + atomic_test_bit(bt_mesh.flags, BT_MESH_SEQ_PENDING)) { + timeout = K_NO_WAIT; + } else if (atomic_test_bit(bt_mesh.flags, BT_MESH_RPL_PENDING) && + (CONFIG_BT_MESH_RPL_STORE_TIMEOUT < + CONFIG_BT_MESH_STORE_TIMEOUT)) { + timeout = K_SECONDS(CONFIG_BT_MESH_RPL_STORE_TIMEOUT); + } else { + timeout = K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT); + } + + BT_DBG("Waiting %d seconds", (int) (timeout / MSEC_PER_SEC)); + + k_delayed_work_submit(&pending_store, timeout); +} + +static void clear_iv(void) +{ + BT_DBG("Clearing IV"); + settings_save_one("bt_mesh/IV", NULL); +} + +static void clear_net(void) +{ + BT_DBG("Clearing Network"); + settings_save_one("bt_mesh/Net", NULL); +} + +static void store_pending_net(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct net_val))]; + struct net_val net; + char *str; + + BT_DBG("addr 0x%04x DevKey %s", bt_mesh_primary_addr(), + bt_hex(bt_mesh.dev_key, 16)); + + net.primary_addr = bt_mesh_primary_addr(); + memcpy(net.dev_key, bt_mesh.dev_key, 16); + + str = settings_str_from_bytes(&net, sizeof(net), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode Network as value"); + return; + } + + BT_DBG("Saving Network as value %s", str); + settings_save_one("bt_mesh/Net", str); +} + +void bt_mesh_store_net(void) +{ + schedule_store(BT_MESH_NET_PENDING); +} + +static void store_pending_iv(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct iv_val))]; + struct iv_val iv; + char *str; + + iv.iv_index = bt_mesh.iv_index; + iv.iv_update = atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS); + iv.iv_duration = bt_mesh.ivu_duration; + + str = settings_str_from_bytes(&iv, sizeof(iv), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode IV as value"); + return; + } + + BT_DBG("Saving IV as value %s", str); + settings_save_one("bt_mesh/IV", str); +} + +void bt_mesh_store_iv(bool only_duration) +{ + schedule_store(BT_MESH_IV_PENDING); + + if (!only_duration) { + /* Always update Seq whenever IV changes */ + schedule_store(BT_MESH_SEQ_PENDING); + } +} + +static void store_pending_seq(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct seq_val))]; + struct seq_val seq; + char *str; + + seq.val[0] = bt_mesh.seq; + seq.val[1] = bt_mesh.seq >> 8; + seq.val[2] = bt_mesh.seq >> 16; + + str = settings_str_from_bytes(&seq, sizeof(seq), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode Seq as value"); + return; + } + + BT_DBG("Saving Seq as value %s", str); + settings_save_one("bt_mesh/Seq", str); +} + +void bt_mesh_store_seq(void) +{ + if (CONFIG_BT_MESH_SEQ_STORE_RATE && + (bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)) { + return; + } + + schedule_store(BT_MESH_SEQ_PENDING); +} + +static void store_rpl(struct bt_mesh_rpl *entry) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct rpl_val))]; + struct rpl_val rpl; + char path[18]; + char *str; + + BT_DBG("src 0x%04x seq 0x%06x old_iv %u", entry->src, + (unsigned) entry->seq, entry->old_iv); + + rpl.seq = entry->seq; + rpl.old_iv = entry->old_iv; + + str = settings_str_from_bytes(&rpl, sizeof(rpl), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode RPL as value"); + return; + } + + snprintk(path, sizeof(path), "bt_mesh/RPL/%x", entry->src); + + BT_DBG("Saving RPL %s as value %s", path, str); + settings_save_one(path, str); +} + +static void clear_rpl(void) +{ + int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i]; + char path[18]; + + if (!rpl->src) { + continue; + } + + snprintk(path, sizeof(path), "bt_mesh/RPL/%x", rpl->src); + settings_save_one(path, NULL); + + memset(rpl, 0, sizeof(*rpl)); + } +} + +static void store_pending_rpl(void) +{ + int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i]; + + if (rpl->store) { + rpl->store = false; + store_rpl(rpl); + } + } +} + +static void store_pending_hb_pub(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct hb_pub_val))]; + struct bt_mesh_hb_pub *pub = bt_mesh_hb_pub_get(); + struct hb_pub_val val; + char *str; + + if (!pub) { + return; + } + + if (pub->dst == BT_MESH_ADDR_UNASSIGNED) { + str = NULL; + } else { + val.indefinite = (pub->count == 0xffff); + val.dst = pub->dst; + val.period = pub->period; + val.ttl = pub->ttl; + val.feat = pub->feat; + val.net_idx = pub->net_idx; + + str = settings_str_from_bytes(&val, sizeof(val), + buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode hb pub as value"); + return; + } + } + + BT_DBG("Saving Heartbeat Publication as value %s", + str ? str : "(null)"); + settings_save_one("bt_mesh/HBPub", str); +} + +static void store_pending_cfg(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct cfg_val))]; + struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get(); + struct cfg_val val; + char *str; + + if (!cfg) { + return; + } + + val.net_transmit = cfg->net_transmit; + val.relay = cfg->relay; + val.relay_retransmit = cfg->relay_retransmit; + val.beacon = cfg->beacon; + val.gatt_proxy = cfg->gatt_proxy; + val.frnd = cfg->frnd; + val.default_ttl = cfg->default_ttl; + + str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode configuration as value"); + return; + } + + BT_DBG("Saving configuration as value %s", str); + settings_save_one("bt_mesh/Cfg", str); +} + +static void clear_cfg(void) +{ + BT_DBG("Clearing configuration"); + settings_save_one("bt_mesh/Cfg", NULL); +} + +static void clear_app_key(u16_t app_idx) +{ + char path[20]; + + BT_DBG("AppKeyIndex 0x%03x", app_idx); + + snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app_idx); + settings_save_one(path, NULL); +} + +static void clear_net_key(u16_t net_idx) +{ + char path[20]; + + BT_DBG("NetKeyIndex 0x%03x", net_idx); + + snprintk(path, sizeof(path), "bt_mesh/NetKey/%x", net_idx); + settings_save_one(path, NULL); +} + +static void store_net_key(struct bt_mesh_subnet *sub) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct net_key_val))]; + struct net_key_val key; + char path[20]; + char *str; + + BT_DBG("NetKeyIndex 0x%03x NetKey %s", sub->net_idx, + bt_hex(sub->keys[0].net, 16)); + + memcpy(&key.val[0], sub->keys[0].net, 16); + memcpy(&key.val[1], sub->keys[1].net, 16); + key.kr_flag = sub->kr_flag; + key.kr_phase = sub->kr_phase; + + str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode NetKey as value"); + return; + } + + snprintk(path, sizeof(path), "bt_mesh/NetKey/%x", sub->net_idx); + + BT_DBG("Saving NetKey %s as value %s", path, str); + settings_save_one(path, str); +} + +static void store_app_key(struct bt_mesh_app_key *app) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))]; + struct app_key_val key; + char path[20]; + char *str; + + key.net_idx = app->net_idx; + key.updated = app->updated; + memcpy(key.val[0], app->keys[0].val, 16); + memcpy(key.val[1], app->keys[1].val, 16); + + str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode AppKey as value"); + return; + } + + snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app->app_idx); + + BT_DBG("Saving AppKey %s as value %s", path, str); + settings_save_one(path, str); +} + +static void store_pending_keys(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(key_updates); i++) { + struct key_update *update = &key_updates[i]; + + if (!update->valid) { + continue; + } + + if (update->clear) { + if (update->app_key) { + clear_app_key(update->key_idx); + } else { + clear_net_key(update->key_idx); + } + } else { + if (update->app_key) { + struct bt_mesh_app_key *key; + + key = bt_mesh_app_key_find(update->key_idx); + if (key) { + store_app_key(key); + } else { + BT_WARN("AppKeyIndex 0x%03x not found", + update->key_idx); + } + + } else { + struct bt_mesh_subnet *sub; + + sub = bt_mesh_subnet_get(update->key_idx); + if (sub) { + store_net_key(sub); + } else { + BT_WARN("NetKeyIndex 0x%03x not found", + update->key_idx); + } + } + } + + update->valid = 0; + } +} + +static void encode_mod_path(struct bt_mesh_model *mod, bool vnd, + const char *key, char *path, size_t path_len) +{ + u16_t mod_key = (((u16_t)mod->elem_idx << 8) | mod->mod_idx); + + if (vnd) { + snprintk(path, path_len, "bt_mesh/v/%x/%s", mod_key, key); + } else { + snprintk(path, path_len, "bt_mesh/s/%x/%s", mod_key, key); + } +} + +static void store_pending_mod_bind(struct bt_mesh_model *mod, bool vnd) +{ + u16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT]; + char buf[BT_SETTINGS_SIZE(sizeof(keys))]; + char path[20]; + int i, count; + char *val; + + for (i = 0, count = 0; i < ARRAY_SIZE(mod->keys); i++) { + if (mod->keys[i] != BT_MESH_KEY_UNUSED) { + keys[count++] = mod->keys[i]; + } + } + + if (count) { + val = settings_str_from_bytes(keys, count * sizeof(keys[0]), + buf, sizeof(buf)); + if (!val) { + BT_ERR("Unable to encode model bindings as value"); + return; + } + } else { + val = NULL; + } + + encode_mod_path(mod, vnd, "bind", path, sizeof(path)); + + BT_DBG("Saving %s as %s", path, val ? val : "(null)"); + settings_save_one(path, val); +} + +static void store_pending_mod_sub(struct bt_mesh_model *mod, bool vnd) +{ + u16_t groups[CONFIG_BT_MESH_MODEL_GROUP_COUNT]; + char buf[BT_SETTINGS_SIZE(sizeof(groups))]; + char path[20]; + int i, count; + char *val; + + for (i = 0, count = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { + groups[count++] = mod->groups[i]; + } + } + + if (count) { + val = settings_str_from_bytes(groups, count * sizeof(groups[0]), + buf, sizeof(buf)); + if (!val) { + BT_ERR("Unable to encode model subscription as value"); + return; + } + } else { + val = NULL; + } + + encode_mod_path(mod, vnd, "sub", path, sizeof(path)); + + BT_DBG("Saving %s as %s", path, val ? val : "(null)"); + settings_save_one(path, val); +} + +static void store_pending_mod_pub(struct bt_mesh_model *mod, bool vnd) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))]; + struct mod_pub_val pub; + char path[20]; + char *val; + + if (!mod->pub || mod->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + val = NULL; + } else { + pub.addr = mod->pub->addr; + pub.key = mod->pub->key; + pub.ttl = mod->pub->ttl; + pub.retransmit = mod->pub->retransmit; + pub.period = mod->pub->period; + pub.period_div = mod->pub->period_div; + pub.cred = mod->pub->cred; + + val = settings_str_from_bytes(&pub, sizeof(pub), + buf, sizeof(buf)); + if (!val) { + BT_ERR("Unable to encode model publication as value"); + return; + } + } + + encode_mod_path(mod, vnd, "pub", path, sizeof(path)); + + BT_DBG("Saving %s as %s", path, val ? val : "(null)"); + settings_save_one(path, val); +} + +static void store_pending_mod(struct bt_mesh_model *mod, + struct bt_mesh_elem *elem, bool vnd, + bool primary, void *user_data) +{ + if (!mod->flags) { + return; + } + + if (mod->flags & BT_MESH_MOD_BIND_PENDING) { + mod->flags &= ~BT_MESH_MOD_BIND_PENDING; + store_pending_mod_bind(mod, vnd); + } + + if (mod->flags & BT_MESH_MOD_SUB_PENDING) { + mod->flags &= ~BT_MESH_MOD_SUB_PENDING; + store_pending_mod_sub(mod, vnd); + } + + if (mod->flags & BT_MESH_MOD_PUB_PENDING) { + mod->flags &= ~BT_MESH_MOD_PUB_PENDING; + store_pending_mod_pub(mod, vnd); + } +} + +static void store_pending(struct ble_npl_event *work) +{ + BT_DBG(""); + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_RPL_PENDING)) { + if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + store_pending_rpl(); + } else { + clear_rpl(); + } + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_KEYS_PENDING)) { + store_pending_keys(); + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_NET_PENDING)) { + if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + store_pending_net(); + } else { + clear_net(); + } + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_IV_PENDING)) { + if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + store_pending_iv(); + } else { + clear_iv(); + } + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_SEQ_PENDING)) { + store_pending_seq(); + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_HB_PUB_PENDING)) { + store_pending_hb_pub(); + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_CFG_PENDING)) { + if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + store_pending_cfg(); + } else { + clear_cfg(); + } + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_MOD_PENDING)) { + bt_mesh_model_foreach(store_pending_mod, NULL); + } +} + +void bt_mesh_store_rpl(struct bt_mesh_rpl *entry) +{ + entry->store = true; + schedule_store(BT_MESH_RPL_PENDING); +} + +static struct key_update *key_update_find(bool app_key, u16_t key_idx, + struct key_update **free_slot) +{ + struct key_update *match; + int i; + + match = NULL; + *free_slot = NULL; + + for (i = 0; i < ARRAY_SIZE(key_updates); i++) { + struct key_update *update = &key_updates[i]; + + if (!update->valid) { + *free_slot = update; + continue; + } + + if (update->app_key != app_key) { + continue; + } + + if (update->key_idx == key_idx) { + match = update; + } + } + + return match; +} + +void bt_mesh_store_subnet(struct bt_mesh_subnet *sub) +{ + struct key_update *update, *free_slot; + + BT_DBG("NetKeyIndex 0x%03x", sub->net_idx); + + update = key_update_find(false, sub->net_idx, &free_slot); + if (update) { + update->clear = 0; + schedule_store(BT_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + store_net_key(sub); + return; + } + + free_slot->valid = 1; + free_slot->key_idx = sub->net_idx; + free_slot->app_key = 0; + free_slot->clear = 0; + + schedule_store(BT_MESH_KEYS_PENDING); +} + +void bt_mesh_store_app_key(struct bt_mesh_app_key *key) +{ + struct key_update *update, *free_slot; + + BT_DBG("AppKeyIndex 0x%03x", key->app_idx); + + update = key_update_find(true, key->app_idx, &free_slot); + if (update) { + update->clear = 0; + schedule_store(BT_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + store_app_key(key); + return; + } + + free_slot->valid = 1; + free_slot->key_idx = key->app_idx; + free_slot->app_key = 1; + free_slot->clear = 0; + + schedule_store(BT_MESH_KEYS_PENDING); +} + +void bt_mesh_store_hb_pub(void) +{ + schedule_store(BT_MESH_HB_PUB_PENDING); +} + +void bt_mesh_store_cfg(void) +{ + schedule_store(BT_MESH_CFG_PENDING); +} + +void bt_mesh_clear_net(void) +{ + schedule_store(BT_MESH_NET_PENDING); + schedule_store(BT_MESH_IV_PENDING); + schedule_store(BT_MESH_CFG_PENDING); +} + +void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub) +{ + struct key_update *update, *free_slot; + + BT_DBG("NetKeyIndex 0x%03x", sub->net_idx); + + update = key_update_find(false, sub->net_idx, &free_slot); + if (update) { + update->clear = 1; + schedule_store(BT_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + clear_net_key(sub->net_idx); + return; + } + + free_slot->valid = 1; + free_slot->key_idx = sub->net_idx; + free_slot->app_key = 0; + free_slot->clear = 1; + + schedule_store(BT_MESH_KEYS_PENDING); +} + +void bt_mesh_clear_app_key(struct bt_mesh_app_key *key) +{ + struct key_update *update, *free_slot; + + BT_DBG("AppKeyIndex 0x%03x", key->app_idx); + + update = key_update_find(true, key->app_idx, &free_slot); + if (update) { + update->clear = 1; + schedule_store(BT_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + clear_app_key(key->app_idx); + return; + } + + free_slot->valid = 1; + free_slot->key_idx = key->app_idx; + free_slot->app_key = 1; + free_slot->clear = 1; + + schedule_store(BT_MESH_KEYS_PENDING); +} + +void bt_mesh_clear_rpl(void) +{ + schedule_store(BT_MESH_RPL_PENDING); +} + +void bt_mesh_store_mod_bind(struct bt_mesh_model *mod) +{ + mod->flags |= BT_MESH_MOD_BIND_PENDING; + schedule_store(BT_MESH_MOD_PENDING); +} + +void bt_mesh_store_mod_sub(struct bt_mesh_model *mod) +{ + mod->flags |= BT_MESH_MOD_SUB_PENDING; + schedule_store(BT_MESH_MOD_PENDING); +} + +void bt_mesh_store_mod_pub(struct bt_mesh_model *mod) +{ + mod->flags |= BT_MESH_MOD_PUB_PENDING; + schedule_store(BT_MESH_MOD_PENDING); +} + +static struct conf_handler bt_mesh_settings_conf_handler = { + .ch_name = "bt_mesh", + .ch_get = NULL, + .ch_set = mesh_set, + .ch_commit = mesh_commit, + .ch_export = NULL, +}; + +void bt_mesh_settings_init(void) +{ + int rc; + + rc = conf_register(&bt_mesh_settings_conf_handler); + + SYSINIT_PANIC_ASSERT_MSG(rc == 0, + "Failed to register bt_mesh_settings conf"); + + k_delayed_work_init(&pending_store, store_pending); +} + +#endif /* MYNEWT_VAL(BLE_MESH_SETTINGS) */ diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.h new file mode 100644 index 000000000..7bd028477 --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void bt_mesh_store_net(void); +void bt_mesh_store_iv(bool only_duration); +void bt_mesh_store_seq(void); +void bt_mesh_store_rpl(struct bt_mesh_rpl *rpl); +void bt_mesh_store_subnet(struct bt_mesh_subnet *sub); +void bt_mesh_store_app_key(struct bt_mesh_app_key *key); +void bt_mesh_store_hb_pub(void); +void bt_mesh_store_cfg(void); +void bt_mesh_store_mod_bind(struct bt_mesh_model *mod); +void bt_mesh_store_mod_sub(struct bt_mesh_model *mod); +void bt_mesh_store_mod_pub(struct bt_mesh_model *mod); + +void bt_mesh_clear_net(void); +void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub); +void bt_mesh_clear_app_key(struct bt_mesh_app_key *key); +void bt_mesh_clear_rpl(void); + +void bt_mesh_settings_init(void); diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.c b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.c new file mode 100644 index 000000000..94a7b58be --- /dev/null +++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.c @@ -0,0 +1,2752 @@ +/** @file + * @brief Bluetooth Mesh shell + * + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_MESH_SHELL) + +#include +#include +#include +#include "shell/shell.h" +#include "console/console.h" +#include "mesh/mesh.h" +#include "mesh/main.h" +#include "mesh/glue.h" +#include "mesh/testing.h" + +/* Private includes for raw Network & Transport layer access */ +#include "net.h" +#include "access.h" +#include "mesh_priv.h" +#include "lpn.h" +#include "transport.h" +#include "foundation.h" +#include "testing.h" +#include "settings.h" + +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) +#include "mesh/model_srv.h" +#include "mesh/model_cli.h" +#include "light_model.h" +#endif + +/* This should be higher priority (lower value) than main task priority */ +#define BLE_MESH_SHELL_TASK_PRIO 126 +#define BLE_MESH_SHELL_STACK_SIZE 768 + +OS_TASK_STACK_DEFINE(g_blemesh_shell_stack, BLE_MESH_SHELL_STACK_SIZE); + +struct os_task mesh_shell_task; +static struct os_eventq mesh_shell_queue; + +#define CID_NVAL 0xffff +#define CID_VENDOR 0x05C3 + +/* Vendor Model data */ +#define VND_MODEL_ID_1 0x1234 + +/* Default net, app & dev key values, unless otherwise specified */ +static const u8_t default_key[16] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, +}; + +static struct { + u16_t local; + u16_t dst; + u16_t net_idx; + u16_t app_idx; +} net = { + .local = BT_MESH_ADDR_UNASSIGNED, + .dst = BT_MESH_ADDR_UNASSIGNED, +}; + +static struct bt_mesh_cfg_srv cfg_srv = { + .relay = BT_MESH_RELAY_DISABLED, + .beacon = BT_MESH_BEACON_ENABLED, +#if MYNEWT_VAL(BLE_MESH_FRIEND) + .frnd = BT_MESH_FRIEND_DISABLED, +#else + .frnd = BT_MESH_FRIEND_NOT_SUPPORTED, +#endif +#if MYNEWT_VAL(BLE_MESH_GATT_PROXY) + .gatt_proxy = BT_MESH_GATT_PROXY_DISABLED, +#else + .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + + .default_ttl = 7, + + /* 3 transmissions with 20ms interval */ + .net_transmit = BT_MESH_TRANSMIT(2, 20), + .relay_retransmit = BT_MESH_TRANSMIT(2, 20), +}; + +#define CUR_FAULTS_MAX 4 + +static u8_t cur_faults[CUR_FAULTS_MAX]; +static u8_t reg_faults[CUR_FAULTS_MAX * 2]; + +static void get_faults(u8_t *faults, u8_t faults_size, u8_t *dst, u8_t *count) +{ + u8_t i, limit = *count; + + for (i = 0, *count = 0; i < faults_size && *count < limit; i++) { + if (faults[i]) { + *dst++ = faults[i]; + (*count)++; + } + } +} + +static int fault_get_cur(struct bt_mesh_model *model, u8_t *test_id, + u16_t *company_id, u8_t *faults, u8_t *fault_count) +{ + printk("Sending current faults\n"); + + *test_id = 0x00; + *company_id = CID_VENDOR; + + get_faults(cur_faults, sizeof(cur_faults), faults, fault_count); + + return 0; +} + +static int fault_get_reg(struct bt_mesh_model *model, u16_t cid, + u8_t *test_id, u8_t *faults, u8_t *fault_count) +{ + if (cid != CID_VENDOR) { + printk("Faults requested for unknown Company ID 0x%04x\n", cid); + return -EINVAL; + } + + printk("Sending registered faults\n"); + + *test_id = 0x00; + + get_faults(reg_faults, sizeof(reg_faults), faults, fault_count); + + return 0; +} + +static int fault_clear(struct bt_mesh_model *model, uint16_t cid) +{ + if (cid != CID_VENDOR) { + return -EINVAL; + } + + memset(reg_faults, 0, sizeof(reg_faults)); + + return 0; +} + +static int fault_test(struct bt_mesh_model *model, uint8_t test_id, + uint16_t cid) +{ + if (cid != CID_VENDOR) { + return -EINVAL; + } + + if (test_id != 0x00) { + return -EINVAL; + } + + return 0; +} + +static const struct bt_mesh_health_srv_cb health_srv_cb = { + .fault_get_cur = fault_get_cur, + .fault_get_reg = fault_get_reg, + .fault_clear = fault_clear, + .fault_test = fault_test, +}; + +static struct bt_mesh_health_srv health_srv = { + .cb = &health_srv_cb, +}; + +static struct bt_mesh_model_pub health_pub; + +static void +health_pub_init(void) +{ + health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(CUR_FAULTS_MAX); +} +#if MYNEWT_VAL(BLE_MESH_CFG_CLI) + +static struct bt_mesh_cfg_cli cfg_cli = { +}; + +#endif /* MYNEWT_VAL(BLE_MESH_CFG_CLI) */ + +#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI) +void show_faults(u8_t test_id, u16_t cid, u8_t *faults, size_t fault_count) +{ + size_t i; + + if (!fault_count) { + printk("Health Test ID 0x%02x Company ID 0x%04x: no faults\n", + test_id, cid); + return; + } + + printk("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu:\n", + test_id, cid, fault_count); + + for (i = 0; i < fault_count; i++) { + printk("\t0x%02x\n", faults[i]); + } +} + +static void health_current_status(struct bt_mesh_health_cli *cli, u16_t addr, + u8_t test_id, u16_t cid, u8_t *faults, + size_t fault_count) +{ + printk("Health Current Status from 0x%04x\n", addr); + show_faults(test_id, cid, faults, fault_count); +} + +static struct bt_mesh_health_cli health_cli = { + .current_status = health_current_status, +}; + +#endif /* MYNEWT_VAL(BLE_MESH_HEALTH_CLI) */ + +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) +static struct bt_mesh_model_pub gen_onoff_pub; +static struct bt_mesh_model_pub gen_level_pub; +static struct bt_mesh_model_pub light_lightness_pub; +static struct bt_mesh_gen_onoff_srv_cb gen_onoff_srv_cb = { + .get = light_model_gen_onoff_get, + .set = light_model_gen_onoff_set, +}; +static struct bt_mesh_gen_level_srv_cb gen_level_srv_cb = { + .get = light_model_gen_level_get, + .set = light_model_gen_level_set, +}; +static struct bt_mesh_light_lightness_srv_cb light_lightness_srv_cb = { + .get = light_model_light_lightness_get, + .set = light_model_light_lightness_set, +}; + +void bt_mesh_set_gen_onoff_srv_cb(struct bt_mesh_gen_onoff_srv_cb *gen_onoff_cb) +{ + gen_onoff_srv_cb = *gen_onoff_cb; +} + +void bt_mesh_set_gen_level_srv_cb(struct bt_mesh_gen_level_srv_cb *gen_level_cb) +{ + gen_level_srv_cb = *gen_level_cb; +} + +void bt_mesh_set_light_lightness_srv_cb(struct bt_mesh_light_lightness_srv_cb *light_lightness_cb) +{ + light_lightness_srv_cb = *light_lightness_cb; +} + +#endif + +static struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV(&cfg_srv), + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), +#if MYNEWT_VAL(BLE_MESH_CFG_CLI) + BT_MESH_MODEL_CFG_CLI(&cfg_cli), +#endif +#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI) + BT_MESH_MODEL_HEALTH_CLI(&health_cli), +#endif +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) + BT_MESH_MODEL_GEN_ONOFF_SRV(&gen_onoff_srv_cb, &gen_onoff_pub), + BT_MESH_MODEL_GEN_ONOFF_CLI(), + BT_MESH_MODEL_GEN_LEVEL_SRV(&gen_level_srv_cb, &gen_level_pub), + BT_MESH_MODEL_GEN_LEVEL_CLI(), + BT_MESH_MODEL_LIGHT_LIGHTNESS_SRV(&light_lightness_srv_cb, &light_lightness_pub), +#endif +}; + +static struct bt_mesh_model vnd_models[] = { + BT_MESH_MODEL_VND(CID_VENDOR, VND_MODEL_ID_1, + BT_MESH_MODEL_NO_OPS, NULL, NULL), +}; + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, vnd_models), +}; + +static const struct bt_mesh_comp comp = { + .cid = CID_VENDOR, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + +static u8_t hex2val(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } else { + return 0; + } +} + +static size_t hex2bin(const char *hex, u8_t *bin, size_t bin_len) +{ + size_t len = 0; + + while (*hex && len < bin_len) { + bin[len] = hex2val(*hex++) << 4; + + if (!*hex) { + len++; + break; + } + + bin[len++] |= hex2val(*hex++); + } + + return len; +} + +static void prov_complete(u16_t net_idx, u16_t addr) +{ + printk("Local node provisioned, net_idx 0x%04x address 0x%04x\n", + net_idx, addr); + net.net_idx = net_idx, + net.local = addr; + net.dst = addr; +} + +static void prov_reset(void) +{ + printk("The local node has been reset and needs reprovisioning\n"); +} + +static int output_number(bt_mesh_output_action_t action, uint32_t number) +{ + printk("OOB Number: %lu\n", number); + return 0; +} + +static int output_string(const char *str) +{ + printk("OOB String: %s\n", str); + return 0; +} + +static bt_mesh_input_action_t input_act; +static u8_t input_size; + +static int cmd_input_num(int argc, char *argv[]) +{ + int err; + + if (argc < 2) { + return -EINVAL; + } + + if (input_act != BT_MESH_ENTER_NUMBER) { + printk("A number hasn't been requested!\n"); + return 0; + } + + if (strlen(argv[1]) < input_size) { + printk("Too short input (%u digits required)\n", + input_size); + return 0; + } + + err = bt_mesh_input_number(strtoul(argv[1], NULL, 10)); + if (err) { + printk("Numeric input failed (err %d)\n", err); + return 0; + } + + input_act = BT_MESH_NO_INPUT; + return 0; +} + +struct shell_cmd_help cmd_input_num_help = { + NULL, "", NULL +}; + +static int cmd_input_str(int argc, char *argv[]) +{ + int err; + + if (argc < 2) { + return -EINVAL; + } + + if (input_act != BT_MESH_ENTER_STRING) { + printk("A string hasn't been requested!\n"); + return 0; + } + + if (strlen(argv[1]) < input_size) { + printk("Too short input (%u characters required)\n", + input_size); + return 0; + } + + err = bt_mesh_input_string(argv[1]); + if (err) { + printk("String input failed (err %d)\n", err); + return 0; + } + + input_act = BT_MESH_NO_INPUT; + return 0; +} + +struct shell_cmd_help cmd_input_str_help = { + NULL, "", NULL +}; + +static int input(bt_mesh_input_action_t act, u8_t size) +{ + switch (act) { + case BT_MESH_ENTER_NUMBER: + printk("Enter a number (max %u digits) with: input-num \n", + size); + break; + case BT_MESH_ENTER_STRING: + printk("Enter a string (max %u chars) with: input-str \n", + size); + break; + default: + printk("Unknown input action %u (size %u) requested!\n", + act, size); + return -EINVAL; + } + + input_act = act; + input_size = size; + return 0; +} + +static const char *bearer2str(bt_mesh_prov_bearer_t bearer) +{ + switch (bearer) { + case BT_MESH_PROV_ADV: + return "PB-ADV"; + case BT_MESH_PROV_GATT: + return "PB-GATT"; + default: + return "unknown"; + } +} + +static void link_open(bt_mesh_prov_bearer_t bearer) +{ + printk("Provisioning link opened on %s\n", bearer2str(bearer)); +} + +static void link_close(bt_mesh_prov_bearer_t bearer) +{ + printk("Provisioning link closed on %s\n", bearer2str(bearer)); +} + +static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID); + +static u8_t static_val[16]; + +static struct bt_mesh_prov prov = { + .uuid = dev_uuid, + .link_open = link_open, + .link_close = link_close, + .complete = prov_complete, + .reset = prov_reset, + .static_val = NULL, + .static_val_len = 0, + .output_size = MYNEWT_VAL(BLE_MESH_OOB_OUTPUT_SIZE), + .output_actions = MYNEWT_VAL(BLE_MESH_OOB_OUTPUT_ACTIONS), + .output_number = output_number, + .output_string = output_string, + .input_size = MYNEWT_VAL(BLE_MESH_OOB_INPUT_SIZE), + .input_actions = MYNEWT_VAL(BLE_MESH_OOB_INPUT_ACTIONS), + .input = input, +}; + +static int cmd_static_oob(int argc, char *argv[]) +{ + if (argc < 2) { + prov.static_val = NULL; + prov.static_val_len = 0; + } else { + prov.static_val_len = hex2bin(argv[1], static_val, 16); + if (prov.static_val_len) { + prov.static_val = static_val; + } else { + prov.static_val = NULL; + } + } + + if (prov.static_val) { + printk("Static OOB value set (length %u)\n", + prov.static_val_len); + } else { + printk("Static OOB value cleared\n"); + } + + return 0; +} + +struct shell_cmd_help cmd_static_oob_help = { + NULL, "[val: 1-16 hex values]", NULL +}; + +static int cmd_uuid(int argc, char *argv[]) +{ + u8_t uuid[16]; + size_t len; + + if (argc < 2) { + return -EINVAL; + } + + len = hex2bin(argv[1], uuid, sizeof(uuid)); + if (len < 1) { + return -EINVAL; + } + + memcpy(dev_uuid, uuid, len); + memset(dev_uuid + len, 0, sizeof(dev_uuid) - len); + + printk("Device UUID set\n"); + + return 0; +} + +struct shell_cmd_help cmd_uuid_help = { + NULL, "", NULL +}; + +static int cmd_reset(int argc, char *argv[]) +{ + bt_mesh_reset(); + printk("Local node reset complete\n"); + return 0; +} + +static u8_t str2u8(const char *str) +{ + if (isdigit(str[0])) { + return strtoul(str, NULL, 0); + } + + return (!strcmp(str, "on") || !strcmp(str, "enable")); +} + +static bool str2bool(const char *str) +{ + return str2u8(str); +} + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) +static int cmd_lpn(int argc, char *argv[]) +{ + static bool enabled; + int err; + + if (argc < 2) { + printk("%s\n", enabled ? "enabled" : "disabled"); + return 0; + } + + if (str2bool(argv[1])) { + if (enabled) { + printk("LPN already enabled\n"); + return 0; + } + + err = bt_mesh_lpn_set(true); + if (err) { + printk("Enabling LPN failed (err %d)\n", err); + } else { + enabled = true; + } + } else { + if (!enabled) { + printk("LPN already disabled\n"); + return 0; + } + + err = bt_mesh_lpn_set(false); + if (err) { + printk("Enabling LPN failed (err %d)\n", err); + } else { + enabled = false; + } + } + + return 0; +} + +static int cmd_poll(int argc, char *argv[]) +{ + int err; + + err = bt_mesh_lpn_poll(); + if (err) { + printk("Friend Poll failed (err %d)\n", err); + } + + return 0; +} + +static void lpn_cb(u16_t friend_addr, bool established) +{ + if (established) { + printk("Friendship (as LPN) established to Friend 0x%04x\n", + friend_addr); + } else { + printk("Friendship (as LPN) lost with Friend 0x%04x\n", + friend_addr); + } +} + +struct shell_cmd_help cmd_lpn_help = { + NULL, "", NULL +}; + +#endif /* MESH_LOW_POWER */ + +static int check_pub_addr_unassigned(void) +{ +#ifdef ARCH_sim + return 0; +#else + uint8_t zero_addr[BLE_DEV_ADDR_LEN] = { 0 }; + + return memcmp(MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), + zero_addr, BLE_DEV_ADDR_LEN) == 0; +#endif +} + +int cmd_mesh_init(int argc, char *argv[]) +{ + int err; + ble_addr_t addr; + + if (check_pub_addr_unassigned()) { + /* Use NRPA */ + err = ble_hs_id_gen_rnd(1, &addr); + assert(err == 0); + err = ble_hs_id_set_rnd(addr.val); + assert(err == 0); + + err = bt_mesh_init(addr.type, &prov, &comp); + } + else { + err = bt_mesh_init(0, &prov, &comp); + } + + if (err) { + printk("Mesh initialization failed (err %d)\n", err); + } + + printk("Mesh initialized\n"); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + printk("Mesh network restored from flash\n"); + } else { + printk("Use \"pb-adv on\" or \"pb-gatt on\" to enable" + " advertising\n"); + } + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) + bt_mesh_lpn_set_cb(lpn_cb); +#endif + + return 0; +} + +#if MYNEWT_VAL(BLE_MESH_GATT_PROXY) +static int cmd_ident(int argc, char *argv[]) +{ + int err; + + err = bt_mesh_proxy_identity_enable(); + if (err) { + printk("Failed advertise using Node Identity (err %d)\n", err); + } + + return 0; +} +#endif /* MESH_GATT_PROXY */ + +static int cmd_dst(int argc, char *argv[]) +{ + if (argc < 2) { + printk("Destination address: 0x%04x%s\n", net.dst, + net.dst == net.local ? " (local)" : ""); + return 0; + } + + if (!strcmp(argv[1], "local")) { + net.dst = net.local; + } else { + net.dst = strtoul(argv[1], NULL, 0); + } + + printk("Destination address set to 0x%04x%s\n", net.dst, + net.dst == net.local ? " (local)" : ""); + return 0; +} + +struct shell_cmd_help cmd_dst_help = { + NULL, "[destination address]", NULL +}; + +static int cmd_netidx(int argc, char *argv[]) +{ + if (argc < 2) { + printk("NetIdx: 0x%04x\n", net.net_idx); + return 0; + } + + net.net_idx = strtoul(argv[1], NULL, 0); + printk("NetIdx set to 0x%04x\n", net.net_idx); + return 0; +} + +struct shell_cmd_help cmd_netidx_help = { + NULL, "[NetIdx]", NULL +}; + +static int cmd_appidx(int argc, char *argv[]) +{ + if (argc < 2) { + printk("AppIdx: 0x%04x\n", net.app_idx); + return 0; + } + + net.app_idx = strtoul(argv[1], NULL, 0); + printk("AppIdx set to 0x%04x\n", net.app_idx); + return 0; +} + +struct shell_cmd_help cmd_appidx_help = { + NULL, "[AppIdx]", NULL +}; + +static int cmd_net_send(int argc, char *argv[]) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(32); + struct bt_mesh_msg_ctx ctx = { + .send_ttl = BT_MESH_TTL_DEFAULT, + .net_idx = net.net_idx, + .addr = net.dst, + .app_idx = net.app_idx, + + }; + struct bt_mesh_net_tx tx = { + .ctx = &ctx, + .src = net.local, + .xmit = bt_mesh_net_transmit_get(), + .sub = bt_mesh_subnet_get(net.net_idx), + }; + size_t len; + int err = 0; + + if (argc < 2) { + err = -EINVAL; + goto done; + } + + if (!tx.sub) { + printk("No matching subnet for NetKey Index 0x%04x\n", + net.net_idx); + goto done; + } + + net_buf_simple_init(msg, 0); + len = hex2bin(argv[1], msg->om_data, net_buf_simple_tailroom(msg) - 4); + net_buf_simple_add(msg, len); + + err = bt_mesh_trans_send(&tx, msg, NULL, NULL); + if (err) { + printk("Failed to send (err %d)\n", err); + } + +done: + os_mbuf_free_chain(msg); + return err; +} + +struct shell_cmd_help cmd_net_send_help = { + NULL, "", NULL +}; + +static int cmd_iv_update(int argc, char *argv[]) +{ + if (bt_mesh_iv_update()) { + printk("Transitioned to IV Update In Progress state\n"); + } else { + printk("Transitioned to IV Update Normal state\n"); + } + + printk("IV Index is 0x%08lx\n", bt_mesh.iv_index); + + return 0; +} + +static int cmd_rpl_clear(int argc, char *argv[]) +{ + bt_mesh_rpl_clear(); + return 0; +} + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) +static int cmd_lpn_subscribe(int argc, char *argv[]) +{ + u16_t address; + + if (argc < 2) { + return -EINVAL; + } + + address = strtoul(argv[1], NULL, 0); + + printk("address 0x%04x", address); + + bt_mesh_lpn_group_add(address); + + return 0; +} + +struct shell_cmd_help cmd_lpn_subscribe_help = { + NULL, "", NULL +}; + +static int cmd_lpn_unsubscribe(int argc, char *argv[]) +{ + u16_t address; + + if (argc < 2) { + return -EINVAL; + } + + address = strtoul(argv[1], NULL, 0); + + printk("address 0x%04x", address); + + bt_mesh_lpn_group_del(&address, 1); + + return 0; +} + +struct shell_cmd_help cmd_lpn_unsubscribe_help = { + NULL, "", NULL +}; +#endif + +static int cmd_iv_update_test(int argc, char *argv[]) +{ + bool enable; + + if (argc < 2) { + return -EINVAL; + } + + enable = str2bool(argv[1]); + if (enable) { + printk("Enabling IV Update test mode\n"); + } else { + printk("Disabling IV Update test mode\n"); + } + + bt_mesh_iv_update_test(enable); + + return 0; +} + +struct shell_cmd_help cmd_iv_update_test_help = { + NULL, "", NULL +}; + +#if MYNEWT_VAL(BLE_MESH_CFG_CLI) + +int cmd_timeout(int argc, char *argv[]) +{ + s32_t timeout; + + if (argc < 2) { + timeout = bt_mesh_cfg_cli_timeout_get(); + if (timeout == K_FOREVER) { + printk("Message timeout: forever\n"); + } else { + printk("Message timeout: %lu seconds\n", + timeout / 1000); + } + + return 0; + } + + timeout = strtol(argv[1], NULL, 0); + if (timeout < 0 || timeout > (INT32_MAX / 1000)) { + timeout = K_FOREVER; + } else { + timeout = timeout * 1000; + } + + bt_mesh_cfg_cli_timeout_set(timeout); + if (timeout == K_FOREVER) { + printk("Message timeout: forever\n"); + } else { + printk("Message timeout: %lu seconds\n", + timeout / 1000); + } + + return 0; +} + +struct shell_cmd_help cmd_timeout_help = { + NULL, "[timeout in seconds]", NULL +}; + + +static int cmd_get_comp(int argc, char *argv[]) +{ + struct os_mbuf *comp = NET_BUF_SIMPLE(32); + u8_t status, page = 0x00; + int err = 0; + + if (argc > 1) { + page = strtol(argv[1], NULL, 0); + } + + net_buf_simple_init(comp, 0); + err = bt_mesh_cfg_comp_data_get(net.net_idx, net.dst, page, + &status, comp); + if (err) { + printk("Getting composition failed (err %d)\n", err); + goto done; + } + + if (status != 0x00) { + printk("Got non-success status 0x%02x\n", status); + goto done; + } + + printk("Got Composition Data for 0x%04x:\n", net.dst); + printk("\tCID 0x%04x\n", net_buf_simple_pull_le16(comp)); + printk("\tPID 0x%04x\n", net_buf_simple_pull_le16(comp)); + printk("\tVID 0x%04x\n", net_buf_simple_pull_le16(comp)); + printk("\tCRPL 0x%04x\n", net_buf_simple_pull_le16(comp)); + printk("\tFeatures 0x%04x\n", net_buf_simple_pull_le16(comp)); + + while (comp->om_len > 4) { + u8_t sig, vnd; + u16_t loc; + int i; + + loc = net_buf_simple_pull_le16(comp); + sig = net_buf_simple_pull_u8(comp); + vnd = net_buf_simple_pull_u8(comp); + + printk("\n\tElement @ 0x%04x:\n", loc); + + if (comp->om_len < ((sig * 2) + (vnd * 4))) { + printk("\t\t...truncated data!\n"); + break; + } + + if (sig) { + printk("\t\tSIG Models:\n"); + } else { + printk("\t\tNo SIG Models\n"); + } + + for (i = 0; i < sig; i++) { + u16_t mod_id = net_buf_simple_pull_le16(comp); + + printk("\t\t\t0x%04x\n", mod_id); + } + + if (vnd) { + printk("\t\tVendor Models:\n"); + } else { + printk("\t\tNo Vendor Models\n"); + } + + for (i = 0; i < vnd; i++) { + u16_t cid = net_buf_simple_pull_le16(comp); + u16_t mod_id = net_buf_simple_pull_le16(comp); + + printk("\t\t\tCompany 0x%04x: 0x%04x\n", cid, mod_id); + } + } + +done: + os_mbuf_free_chain(comp); + return err; +} + +struct shell_cmd_help cmd_get_comp_help = { + NULL, "[page]", NULL +}; + +static int cmd_beacon(int argc, char *argv[]) +{ + u8_t status; + int err; + + if (argc < 2) { + err = bt_mesh_cfg_beacon_get(net.net_idx, net.dst, &status); + } else { + u8_t val = str2u8(argv[1]); + + err = bt_mesh_cfg_beacon_set(net.net_idx, net.dst, val, + &status); + } + + if (err) { + printk("Unable to send Beacon Get/Set message (err %d)\n", err); + return 0; + } + + printk("Beacon state is 0x%02x\n", status); + + return 0; +} + +struct shell_cmd_help cmd_beacon_help = { + NULL, "[val: off, on]", NULL +}; + +static int cmd_ttl(int argc, char *argv[]) +{ + u8_t ttl; + int err; + + if (argc < 2) { + err = bt_mesh_cfg_ttl_get(net.net_idx, net.dst, &ttl); + } else { + u8_t val = strtoul(argv[1], NULL, 0); + + err = bt_mesh_cfg_ttl_set(net.net_idx, net.dst, val, &ttl); + } + + if (err) { + printk("Unable to send Default TTL Get/Set (err %d)\n", err); + return 0; + } + + printk("Default TTL is 0x%02x\n", ttl); + + return 0; +} + +struct shell_cmd_help cmd_ttl_help = { + NULL, "[ttl: 0x00, 0x02-0x7f]", NULL +}; + +static int cmd_friend(int argc, char *argv[]) +{ + u8_t frnd; + int err; + + if (argc < 2) { + err = bt_mesh_cfg_friend_get(net.net_idx, net.dst, &frnd); + } else { + u8_t val = str2u8(argv[1]); + + err = bt_mesh_cfg_friend_set(net.net_idx, net.dst, val, &frnd); + } + + if (err) { + printk("Unable to send Friend Get/Set (err %d)\n", err); + return 0; + } + + printk("Friend is set to 0x%02x\n", frnd); + + return 0; +} + +struct shell_cmd_help cmd_friend_help = { + NULL, "[val: off, on]", NULL +}; + +static int cmd_gatt_proxy(int argc, char *argv[]) +{ + u8_t proxy; + int err; + + if (argc < 2) { + err = bt_mesh_cfg_gatt_proxy_get(net.net_idx, net.dst, &proxy); + } else { + u8_t val = str2u8(argv[1]); + + err = bt_mesh_cfg_gatt_proxy_set(net.net_idx, net.dst, val, + &proxy); + } + + if (err) { + printk("Unable to send GATT Proxy Get/Set (err %d)\n", err); + return 0; + } + + printk("GATT Proxy is set to 0x%02x\n", proxy); + + return 0; +} + +struct shell_cmd_help cmd_gatt_proxy_help = { + NULL, "[val: off, on]", NULL +}; + +static int cmd_relay(int argc, char *argv[]) +{ + u8_t relay, transmit; + int err; + + if (argc < 2) { + err = bt_mesh_cfg_relay_get(net.net_idx, net.dst, &relay, + &transmit); + } else { + u8_t val = str2u8(argv[1]); + u8_t count, interval, new_transmit; + + if (val) { + if (argc > 2) { + count = strtoul(argv[2], NULL, 0); + } else { + count = 2; + } + + if (argc > 3) { + interval = strtoul(argv[3], NULL, 0); + } else { + interval = 20; + } + + new_transmit = BT_MESH_TRANSMIT(count, interval); + } else { + new_transmit = 0; + } + + err = bt_mesh_cfg_relay_set(net.net_idx, net.dst, val, + new_transmit, &relay, &transmit); + } + + if (err) { + printk("Unable to send Relay Get/Set (err %d)\n", err); + return 0; + } + + printk("Relay is 0x%02x, Transmit 0x%02x (count %u interval %ums)\n", + relay, transmit, BT_MESH_TRANSMIT_COUNT(transmit), + BT_MESH_TRANSMIT_INT(transmit)); + + return 0; +} + +struct shell_cmd_help cmd_relay_help = { + NULL, "[val: off, on] [count: 0-7] [interval: 0-32]", NULL +}; + +static int cmd_net_key_add(int argc, char *argv[]) +{ + u8_t key_val[16]; + u16_t key_net_idx; + u8_t status; + int err; + + if (argc < 2) { + return -EINVAL; + } + + key_net_idx = strtoul(argv[1], NULL, 0); + + if (argc > 2) { + size_t len; + + len = hex2bin(argv[3], key_val, sizeof(key_val)); + memset(key_val, 0, sizeof(key_val) - len); + } else { + memcpy(key_val, default_key, sizeof(key_val)); + } + + err = bt_mesh_cfg_net_key_add(net.net_idx, net.dst, key_net_idx, + key_val, &status); + if (err) { + printk("Unable to send NetKey Add (err %d)\n", err); + return 0; + } + + if (status) { + printk("NetKeyAdd failed with status 0x%02x\n", status); + } else { + printk("NetKey added with NetKey Index 0x%03x\n", key_net_idx); + } + + return 0; +} + +struct shell_cmd_help cmd_net_key_add_help = { + NULL, " [val]", NULL +}; + +static int cmd_app_key_add(int argc, char *argv[]) +{ + u8_t key_val[16]; + u16_t key_net_idx, key_app_idx; + u8_t status; + int err; + + if (argc < 3) { + return -EINVAL; + } + + key_net_idx = strtoul(argv[1], NULL, 0); + key_app_idx = strtoul(argv[2], NULL, 0); + + if (argc > 3) { + size_t len; + + len = hex2bin(argv[3], key_val, sizeof(key_val)); + memset(key_val, 0, sizeof(key_val) - len); + } else { + memcpy(key_val, default_key, sizeof(key_val)); + } + + err = bt_mesh_cfg_app_key_add(net.net_idx, net.dst, key_net_idx, + key_app_idx, key_val, &status); + if (err) { + printk("Unable to send App Key Add (err %d)\n", err); + return 0; + } + + if (status) { + printk("AppKeyAdd failed with status 0x%02x\n", status); + } else { + printk("AppKey added, NetKeyIndex 0x%04x AppKeyIndex 0x%04x\n", + key_net_idx, key_app_idx); + } + + return 0; +} + +struct shell_cmd_help cmd_app_key_add_help = { + NULL, " [val]", NULL +}; + +static int cmd_mod_app_bind(int argc, char *argv[]) +{ + u16_t elem_addr, mod_app_idx, mod_id, cid; + u8_t status; + int err; + + if (argc < 4) { + return -EINVAL; + } + + elem_addr = strtoul(argv[1], NULL, 0); + mod_app_idx = strtoul(argv[2], NULL, 0); + mod_id = strtoul(argv[3], NULL, 0); + + if (argc > 4) { + cid = strtoul(argv[4], NULL, 0); + err = bt_mesh_cfg_mod_app_bind_vnd(net.net_idx, net.dst, + elem_addr, mod_app_idx, + mod_id, cid, &status); + } else { + err = bt_mesh_cfg_mod_app_bind(net.net_idx, net.dst, elem_addr, + mod_app_idx, mod_id, &status); + } + + if (err) { + printk("Unable to send Model App Bind (err %d)\n", err); + return 0; + } + + if (status) { + printk("Model App Bind failed with status 0x%02x\n", status); + } else { + printk("AppKey successfully bound\n"); + } + + return 0; +} + +struct shell_cmd_help cmd_mod_app_bind_help = { + NULL, " [Company ID]", NULL +}; + +static int cmd_mod_sub_add(int argc, char *argv[]) +{ + u16_t elem_addr, sub_addr, mod_id, cid; + u8_t status; + int err; + + if (argc < 4) { + return -EINVAL; + } + + elem_addr = strtoul(argv[1], NULL, 0); + sub_addr = strtoul(argv[2], NULL, 0); + mod_id = strtoul(argv[3], NULL, 0); + + if (argc > 4) { + cid = strtoul(argv[4], NULL, 0); + err = bt_mesh_cfg_mod_sub_add_vnd(net.net_idx, net.dst, + elem_addr, sub_addr, mod_id, + cid, &status); + } else { + err = bt_mesh_cfg_mod_sub_add(net.net_idx, net.dst, elem_addr, + sub_addr, mod_id, &status); + } + + if (err) { + printk("Unable to send Model Subscription Add (err %d)\n", err); + return 0; + } + + if (status) { + printk("Model Subscription Add failed with status 0x%02x\n", + status); + } else { + printk("Model subscription was successful\n"); + } + + return 0; +} + +struct shell_cmd_help cmd_mod_sub_add_help = { + NULL, " [Company ID]", NULL +}; + +static int cmd_mod_sub_del(int argc, char *argv[]) +{ + u16_t elem_addr, sub_addr, mod_id, cid; + u8_t status; + int err; + + if (argc < 4) { + return -EINVAL; + } + + elem_addr = strtoul(argv[1], NULL, 0); + sub_addr = strtoul(argv[2], NULL, 0); + mod_id = strtoul(argv[3], NULL, 0); + + if (argc > 4) { + cid = strtoul(argv[4], NULL, 0); + err = bt_mesh_cfg_mod_sub_del_vnd(net.net_idx, net.dst, + elem_addr, sub_addr, mod_id, + cid, &status); + } else { + err = bt_mesh_cfg_mod_sub_del(net.net_idx, net.dst, elem_addr, + sub_addr, mod_id, &status); + } + + if (err) { + printk("Unable to send Model Subscription Delete (err %d)\n", + err); + return 0; + } + + if (status) { + printk("Model Subscription Delete failed with status 0x%02x\n", + status); + } else { + printk("Model subscription deltion was successful\n"); + } + + return 0; +} + +struct shell_cmd_help cmd_mod_sub_del_help = { + NULL, " [Company ID]", NULL +}; + +static int cmd_mod_sub_add_va(int argc, char *argv[]) +{ + u16_t elem_addr, sub_addr, mod_id, cid; + u8_t label[16]; + u8_t status; + size_t len; + int err; + + if (argc < 4) { + return -EINVAL; + } + + elem_addr = strtoul(argv[1], NULL, 0); + + len = hex2bin(argv[2], label, sizeof(label)); + memset(label + len, 0, sizeof(label) - len); + + mod_id = strtoul(argv[3], NULL, 0); + + if (argc > 4) { + cid = strtoul(argv[4], NULL, 0); + err = bt_mesh_cfg_mod_sub_va_add_vnd(net.net_idx, net.dst, + elem_addr, label, mod_id, + cid, &sub_addr, &status); + } else { + err = bt_mesh_cfg_mod_sub_va_add(net.net_idx, net.dst, + elem_addr, label, mod_id, + &sub_addr, &status); + } + + if (err) { + printk("Unable to send Mod Sub VA Add (err %d)\n", err); + return 0; + } + + if (status) { + printk("Mod Sub VA Add failed with status 0x%02x\n", + status); + } else { + printk("0x%04x subscribed to Label UUID %s (va 0x%04x)\n", + elem_addr, argv[2], sub_addr); + } + + return 0; +} + +struct shell_cmd_help cmd_mod_sub_add_va_help = { + NULL, "
, {m} = , {e} =
" "
")); WSContentSend_P(HTTP_TABLE100); - for (uint32_t i = 0; i < 17; i++) { - if ((i < 6) || ((i > 8) && (i != 11))) { // Ignore flash pins GPIO06, 7, 8 and 11 + for (uint32_t i = 0; i < MAX_GPIO_PIN; i++) { + if (!FlashPin(i)) { +#ifdef ESP8266 WSContentSend_P(PSTR("" D_GPIO "%d"), ((9==i)||(10==i)) ? WebColor(COL_TEXT_WARNING) : WebColor(COL_TEXT), i, (0==i) ? " style='width:200px'" : "", i); +#else // ESP32 + WSContentSend_P(PSTR("" D_GPIO "%d"), + ((9==i)||(10==i)) ? WebColor(COL_TEXT_WARNING) : WebColor(COL_TEXT), i, (0==i) ? " style='width:150px'" : "", i, i); + WSContentSend_P(PSTR(""), i); +#endif // ESP8266 } } +#ifdef ESP8266 WSContentSend_P(PSTR("" D_ADC "0"), WebColor(COL_TEXT)); +#endif WSContentSend_P(PSTR("")); + gpio_flag flag = ModuleFlag(); +#ifdef ESP8266 if (flag.data > ADC0_USER) { +#else // ESP32 + if (flag.data) { +#endif // ESP32 WSContentSend_P(HTTP_FORM_TEMPLATE_FLAG); } + WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); WSContentStop(); @@ -1494,29 +1554,41 @@ void HandleTemplateConfiguration(void) void TemplateSaveSettings(void) { - char tmp[sizeof(Settings.user_template.name)]; // WebGetArg NAME and GPIO/BASE/FLAG byte value + char tmp[TOPSZ]; // WebGetArg NAME and GPIO/BASE/FLAG byte value char webindex[5]; // WebGetArg name - char svalue[128]; // Template command string + char svalue[300]; // Template command string WebGetArg("s1", tmp, sizeof(tmp)); // NAME snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp); uint32_t j = 0; - for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { if (6 == i) { j = 9; } if (8 == i) { j = 12; } snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), j); WebGetArg(webindex, tmp, sizeof(tmp)); // GPIO - uint8_t gpio = atoi(tmp); + uint32_t gpio = atoi(tmp); +#ifdef ESP32 + char tmp2[8]; // WebGetArg numbers only + char webindex2[5]; // WebGetArg name + snprintf_P(webindex2, sizeof(webindex2), PSTR("h%d"), j); + WebGetArg(webindex2, tmp2, sizeof(tmp2)); + uint32_t value2 = (!strlen(tmp2)) ? 0 : atoi(tmp2) -1; + gpio += value2; +#endif // ESP32 snprintf_P(svalue, sizeof(svalue), PSTR("%s%s%d"), svalue, (i>0)?",":"", gpio); j++; } - WebGetArg("g17", tmp, sizeof(tmp)); // FLAG - ADC0 +#ifdef ESP8266 + WebGetArg("g" STR(ADC0_PIN), tmp, sizeof(tmp)); // FLAG - ADC0 uint32_t flag = atoi(tmp); +#else // ESP32 + uint32_t flag = 0; +#endif // ESP32 for (uint32_t i = 0; i < GPIO_FLAG_USED; i++) { snprintf_P(webindex, sizeof(webindex), PSTR("c%d"), i); - uint32_t state = WebServer->hasArg(webindex) << i +4; // FLAG + uint32_t state = Webserver->hasArg(webindex) << i +4; // FLAG flag += state; } WebGetArg("g99", tmp, sizeof(tmp)); // BASE @@ -1532,84 +1604,112 @@ void HandleModuleConfiguration(void) { if (!HttpCheckPriviledgedAccess()) { return; } - if (WebServer->hasArg("save")) { + if (Webserver->hasArg("save")) { ModuleSaveSettings(); WebRestart(1); return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MODULE); + char stemp[30]; // Sensor name uint32_t midx; myio cmodule; ModuleGpios(&cmodule); - if (WebServer->hasArg("m")) { - WSContentBegin(200, CT_PLAIN); - uint32_t vidx = 0; - for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { // "}2'%d'>%s (%d)}3" - "}2'255'>UserTemplate (0)}3" - "}2'0'>Sonoff Basic (1)}3" - if (0 == i) { - midx = USER_MODULE; - vidx = 0; - } else { - midx = pgm_read_byte(kModuleNiceList + i -1); - vidx = midx +1; - } - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); - } - WSContentEnd(); - return; - } - - if (WebServer->hasArg("g")) { - WSContentBegin(200, CT_PLAIN); - for (uint32_t j = 0; j < sizeof(kGpioNiceList); j++) { - midx = pgm_read_byte(kGpioNiceList + j); - if (!GetUsedInModule(midx, cmodule.io)) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); - } - } - WSContentEnd(); - return; - } - -#ifndef USE_ADC_VCC - if (WebServer->hasArg("a")) { - WSContentBegin(200, CT_PLAIN); - for (uint32_t j = 0; j < ADC0_END; j++) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, j, GetTextIndexed(stemp, sizeof(stemp), j, kAdc0Names), j); - } - WSContentEnd(); - return; - } -#endif // USE_ADC_VCC - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MODULE); - WSContentStart_P(S_CONFIGURE_MODULE); WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); - WSContentSend_P(HTTP_SCRIPT_MODULE1, Settings.module); - for (uint32_t i = 0; i < sizeof(cmodule); i++) { + + WSContentSend_P(PSTR("function sl(){os=\"")); + uint32_t vidx = 0; + for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { // "}2'%d'>%s (%d)}3" - "}2'255'>UserTemplate (0)}3" - "}2'0'>Sonoff Basic (1)}3" + if (0 == i) { + midx = USER_MODULE; + vidx = 0; + } else { + midx = pgm_read_byte(kModuleNiceList + i -1); + vidx = midx +1; + } +#ifdef ESP8266 + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); +#else // ESP32 + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_INDEX, midx, AnyModuleName(midx).c_str(), vidx); +#endif // ESP8266 - ESP32 + } + WSContentSend_P(PSTR("\";sk(%d,99);os=\""), Settings.module); + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { +#ifdef ESP8266 + midx = pgm_read_byte(kGpioNiceList + i); + uint32_t ridx = midx; + if (!GetUsedInModule(midx, cmodule.io)) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), ridx); + } +#else // ESP32 + uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0; + midx = ridx >> 5; + if (!GetUsedInModule(midx, cmodule.io)) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames)); + } +#endif // ESP8266 - ESP32 + } + WSContentSend_P(PSTR("\";")); + +#ifdef ESP32 + WSContentSend_P(PSTR("hs=[")); + bool first_done = false; + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { // hs=[36,68,100,132,168,200,232,264,292,324,356,388,421,453]; + midx = pgm_read_word(kGpioNiceList + i); + if (midx & 0x001F) { + if (first_done) { WSContentSend_P(PSTR(",")); } + WSContentSend_P(PSTR("%d"), midx); + first_done = true; + } + } + WSContentSend_P(PSTR("];")); +#endif // ESP32 + + for (uint32_t i = 0; i < ARRAY_SIZE(cmodule.io); i++) { if (ValidGPIO(i, cmodule.io[i])) { WSContentSend_P(PSTR("sk(%d,%d);"), my_module.io[i], i); // g0 - g16 } } - WSContentSend_P(HTTP_SCRIPT_MODULE2, Settings.my_adc0); + +#ifdef ESP8266 +#ifndef USE_ADC_VCC + WSContentSend_P(PSTR("os=\"")); + for (uint32_t j = 0; j < ADC0_END; j++) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, j, GetTextIndexed(stemp, sizeof(stemp), j, kAdc0Names), j); + } + WSContentSend_P(PSTR("\";sk(%d," STR(ADC0_PIN) ");"), Settings.my_adc0); +#endif // USE_ADC_VCC +#endif // ESP8266 - ESP32 + + WSContentSend_P(PSTR("}wl(sl);")); + WSContentSendStyle(); WSContentSend_P(HTTP_FORM_MODULE, AnyModuleName(MODULE).c_str()); - for (uint32_t i = 0; i < sizeof(cmodule); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(cmodule.io); i++) { if (ValidGPIO(i, cmodule.io[i])) { snprintf_P(stemp, 3, PINS_WEMOS +i*2); +#ifdef ESP8266 char sesp8285[40]; snprintf_P(sesp8285, sizeof(sesp8285), PSTR("ESP8285"), WebColor(COL_TEXT_WARNING)); WSContentSend_P(PSTR("%s " D_GPIO "%d %s"), (WEMOS==my_module_type)?stemp:"", i, (0==i)? D_SENSOR_BUTTON "1":(1==i)? D_SERIAL_OUT :(3==i)? D_SERIAL_IN :((9==i)||(10==i))? sesp8285 :(12==i)? D_SENSOR_RELAY "1":(13==i)? D_SENSOR_LED "1i":(14==i)? D_SENSOR :"", i); +#else // ESP32 + WSContentSend_P(PSTR("%s " D_GPIO "%d"), + (WEMOS==my_module_type)?stemp:"", i, i, i); + WSContentSend_P(PSTR(""), i); +#endif // ESP8266 } } +#ifdef ESP8266 #ifndef USE_ADC_VCC if (ValidAdc()) { WSContentSend_P(PSTR("%s " D_ADC "0"), (WEMOS==my_module_type)?"A0":""); } #endif // USE_ADC_VCC +#endif // ESP8266 WSContentSend_P(PSTR("")); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); @@ -1629,23 +1729,34 @@ void ModuleSaveSettings(void) myio cmodule; ModuleGpios(&cmodule); String gpios = ""; - for (uint32_t i = 0; i < sizeof(cmodule); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(cmodule.io); i++) { if (Settings.last_module != new_module) { Settings.my_gp.io[i] = GPIO_NONE; } else { if (ValidGPIO(i, cmodule.io[i])) { snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), i); WebGetArg(webindex, tmp, sizeof(tmp)); - Settings.my_gp.io[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - gpios += F(", " D_GPIO ); gpios += String(i); gpios += F(" "); gpios += String(Settings.my_gp.io[i]); + uint32_t value = (!strlen(tmp)) ? 0 : atoi(tmp); +#ifdef ESP32 + char tmp2[8]; // WebGetArg numbers only + char webindex2[5]; // WebGetArg name + snprintf_P(webindex2, sizeof(webindex2), PSTR("h%d"), i); + WebGetArg(webindex2, tmp2, sizeof(tmp2)); + uint32_t value2 = (!strlen(tmp2)) ? 0 : atoi(tmp2) -1; + value += value2; +#endif // ESP8266 - ESP32 + Settings.my_gp.io[i] = value; + gpios += F(", " D_GPIO ); gpios += String(i); gpios += F(" "); gpios += String(value); } } } +#ifdef ESP8266 #ifndef USE_ADC_VCC - WebGetArg("g17", tmp, sizeof(tmp)); + WebGetArg("g" STR(ADC0_PIN), tmp, sizeof(tmp)); Settings.my_adc0 = (!strlen(tmp)) ? 0 : atoi(tmp); gpios += F(", " D_ADC "0 "); gpios += String(Settings.my_adc0); #endif // USE_ADC_VCC +#endif // ESP8266 AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MODULE "%s " D_CMND_MODULE "%s"), ModuleName().c_str(), gpios.c_str()); } @@ -1680,7 +1791,7 @@ void HandleWifiConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI); - if (WebServer->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) { + if (Webserver->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) { WifiSaveSettings(); WebRestart(2); return; @@ -1691,7 +1802,7 @@ void HandleWifiConfiguration(void) WSContentSendStyle(); if (HTTP_MANAGER_RESET_ONLY != Web.state) { - if (WebServer->hasArg("scan")) { + if (Webserver->hasArg("scan")) { #ifdef USE_EMULATION UdpDisconnect(); #endif // USE_EMULATION @@ -1735,7 +1846,7 @@ void HandleWifiConfiguration(void) //display networks in page for (uint32_t i = 0; i < n; i++) { if (-1 == indices[i]) { continue; } // skip dups - int32_t rssi = WiFi.RSSI(indices[i]) + int32_t rssi = WiFi.RSSI(indices[i]); DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), rssi); int quality = WifiGetRssiAsQuality(rssi); @@ -1804,7 +1915,7 @@ void HandleLoggingConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_LOGGING); - if (WebServer->hasArg("save")) { + if (Webserver->hasArg("save")) { LoggingSaveSettings(); HandleConfiguration(); return; @@ -1869,7 +1980,7 @@ void HandleOtherConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_OTHER); - if (WebServer->hasArg("save")) { + if (Webserver->hasArg("save")) { OtherSaveSettings(); WebRestart(1); return; @@ -1933,7 +2044,7 @@ void OtherSaveSettings(void) WebGetArg("wp", tmp, sizeof(tmp)); SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp); - Settings.flag.mqtt_enabled = WebServer->hasArg("b1"); // SetOption3 - Enable MQTT + Settings.flag.mqtt_enabled = Webserver->hasArg("b1"); // SetOption3 - Enable MQTT #ifdef USE_EMULATION UdpDisconnect(); #if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) @@ -1954,7 +2065,7 @@ void OtherSaveSettings(void) /* // This sometimes provides intermittent watchdog - bool template_activate = WebServer->hasArg("t2"); // Try this to tackle intermittent watchdog after execution of Template command + bool template_activate = Webserver->hasArg("t2"); // Try this to tackle intermittent watchdog after execution of Template command WebGetArg("t1", tmp, sizeof(tmp)); if (strlen(tmp)) { // {"NAME":"12345678901234","GPIO":[255,255,255,255,255,255,255,255,255,255,255,255,255],"FLAG":255,"BASE":255} char svalue[128]; @@ -1970,7 +2081,7 @@ void OtherSaveSettings(void) */ WebGetArg("t1", tmp, sizeof(tmp)); if (strlen(tmp)) { // {"NAME":"12345678901234","GPIO":[255,255,255,255,255,255,255,255,255,255,255,255,255],"FLAG":255,"BASE":255} - snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (WebServer->hasArg("t2")) ? "; " D_CMND_MODULE " 0" : ""); + snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (Webserver->hasArg("t2")) ? "; " D_CMND_MODULE " 0" : ""); ExecuteWebCommand(message, SRC_WEBGUI); } } @@ -1985,8 +2096,8 @@ void HandleBackupConfiguration(void) if (!SettingsBufferAlloc()) { return; } - WiFiClient myClient = WebServer->client(); - WebServer->setContentLength(sizeof(Settings)); + WiFiClient myClient = Webserver->client(); + Webserver->setContentLength(sizeof(Settings)); char attachment[TOPSZ]; @@ -1996,7 +2107,7 @@ void HandleBackupConfiguration(void) char hostname[sizeof(my_hostname)]; snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(hostname, my_hostname), my_version); - WebServer->sendHeader(F("Content-Disposition"), attachment); + Webserver->sendHeader(F("Content-Disposition"), attachment); WSSend(200, CT_STREAM, ""); @@ -2075,7 +2186,7 @@ void HandleInformation(void) char stopic[TOPSZ]; - int freeMem = ESP.getFreeHeap(); + int freeMem = ESP_getFreeHeap(); WSContentStart_P(S_INFORMATION); // Save 1k of code space replacing table html with javascript replace codes @@ -2085,9 +2196,13 @@ void HandleInformation(void) WSContentSend_P(PSTR("
")); WSContentSend_P(PSTR(D_PROGRAM_VERSION "}2%s%s"), my_version, my_image); WSContentSend_P(PSTR("}1" D_BUILD_DATE_AND_TIME "}2%s"), GetBuildDateAndTime().c_str()); - WSContentSend_P(PSTR("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_ESP8266_RELEASE "/%s"), ESP.getSdkVersion()); + WSContentSend_P(PSTR("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_CORE_RELEASE "/%s"), ESP.getSdkVersion()); WSContentSend_P(PSTR("}1" D_UPTIME "}2%s"), GetUptime().c_str()); +#ifdef ESP8266 WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d at 0x%X"), Settings.save_flag, GetSettingsAddress()); +#else + WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d"), Settings.save_flag); +#endif WSContentSend_P(PSTR("}1" D_BOOT_COUNT "}2%d"), Settings.bootcount); WSContentSend_P(PSTR("}1" D_RESTART_REASON "}2%s"), GetResetReason().c_str()); uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; @@ -2126,8 +2241,13 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER)); WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), mqtt_client); WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC)); -// WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), SettingsText(SET_MQTT_GRP_TOPIC)); - WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), GetGroupTopic_P(stopic, "")); + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC " %d}2%s"), 1 +i, GetGroupTopic_P(stopic, "", real_index +i)); + } + } WSContentSend_P(PSTR("}1" D_MQTT_FULL_TOPIC "}2%s"), GetTopic_P(stopic, CMND, mqtt_topic, "")); WSContentSend_P(PSTR("}1" D_MQTT " " D_FALLBACK_TOPIC "}2%s"), GetFallbackTopic_P(stopic, "")); } else { @@ -2155,13 +2275,21 @@ void HandleInformation(void) #endif // USE_DISCOVERY WSContentSend_P(PSTR("}1}2 ")); // Empty line - WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP.getChipId()); + WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP_getChipId()); +#ifdef ESP8266 WSContentSend_P(PSTR("}1" D_FLASH_CHIP_ID "}20x%06X"), ESP.getFlashChipId()); +#endif WSContentSend_P(PSTR("}1" D_FLASH_CHIP_SIZE "}2%dkB"), ESP.getFlashChipRealSize() / 1024); WSContentSend_P(PSTR("}1" D_PROGRAM_FLASH_SIZE "}2%dkB"), ESP.getFlashChipSize() / 1024); - WSContentSend_P(PSTR("}1" D_PROGRAM_SIZE "}2%dkB"), ESP.getSketchSize() / 1024); + WSContentSend_P(PSTR("}1" D_PROGRAM_SIZE "}2%dkB"), ESP_getSketchSize() / 1024); WSContentSend_P(PSTR("}1" D_FREE_PROGRAM_SPACE "}2%dkB"), ESP.getFreeSketchSpace() / 1024); WSContentSend_P(PSTR("}1" D_FREE_MEMORY "}2%dkB"), freeMem / 1024); +#ifdef ESP32 + if (psramFound()) { + WSContentSend_P(PSTR("}1" D_PSR_MAX_MEMORY "}2%dkB"), ESP.getPsramSize() / 1024); + WSContentSend_P(PSTR("}1" D_PSR_FREE_MEMORY "}2%dkB"), ESP.getFreePsram() / 1024); + } +#endif WSContentSend_P(PSTR("
")); WSContentSend_P(HTTP_SCRIPT_INFO_END); @@ -2291,7 +2419,7 @@ void HandleUploadLoop(void) return; } - HTTPUpload& upload = WebServer->upload(); + HTTPUpload& upload = Webserver->upload(); if (UPLOAD_FILE_START == upload.status) { restart_flag = 60; @@ -2453,6 +2581,16 @@ void HandleUploadLoop(void) } else { valid_settings = (settings_buffer[0] == CONFIG_FILE_SIGN); } + + if (valid_settings) { +#ifdef ESP8266 + valid_settings = (0 == settings_buffer[0xF36]); // Settings.config_version +#endif // ESP8266 +#ifdef ESP32 + valid_settings = (1 == settings_buffer[0xF36]); // Settings.config_version +#endif // ESP32 + } + if (valid_settings) { SettingsDefaultSet2(); memcpy((char*)&Settings +16, settings_buffer +16, sizeof(Settings) -16); @@ -2504,8 +2642,8 @@ void HandleUploadLoop(void) void HandlePreflightRequest(void) { HttpHeaderCors(); - WebServer->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST")); - WebServer->sendHeader(F("Access-Control-Allow-Headers"), F("authorization")); + Webserver->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST")); + Webserver->sendHeader(F("Access-Control-Allow-Headers"), F("authorization")); WSSend(200, CT_HTML, ""); } @@ -2517,54 +2655,54 @@ void HandleHttpCommand(void) AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND)); - bool valid = true; if (strlen(SettingsText(SET_WEBPWD))) { char tmp1[33]; WebGetArg("user", tmp1, sizeof(tmp1)); char tmp2[strlen(SettingsText(SET_WEBPWD)) +1]; WebGetArg("password", tmp2, sizeof(tmp2)); - if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) { valid = false; } + if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) { + WSContentBegin(401, CT_JSON); + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_NEED_USER_AND_PASSWORD "\"}")); + WSContentEnd(); + return; + } } WSContentBegin(200, CT_JSON); - if (valid) { - uint32_t curridx = web_log_index; - String svalue = WebServer->arg("cmnd"); - if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { - ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCOMMAND); - if (web_log_index != curridx) { - uint32_t counter = curridx; - WSContentSend_P(PSTR("{")); - bool cflg = false; - do { - char* tmp; - size_t len; - GetLog(counter, &tmp, &len); - if (len) { - // [14:49:36 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}] > [{"POWER":"OFF"}] - char* JSON = (char*)memchr(tmp, '{', len); - if (JSON) { // Is it a JSON message (and not only [15:26:08 MQT: stat/wemos5/POWER = O]) - size_t JSONlen = len - (JSON - tmp); - if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); } - char stemp[JSONlen]; - strlcpy(stemp, JSON +1, JSONlen -2); - WSContentSend_P(PSTR("%s%s"), (cflg) ? "," : "", stemp); - cflg = true; - } + uint32_t curridx = web_log_index; + String svalue = Webserver->arg("cmnd"); + if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { + ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCOMMAND); + if (web_log_index != curridx) { + uint32_t counter = curridx; + WSContentSend_P(PSTR("{")); + bool cflg = false; + do { + char* tmp; + size_t len; + GetLog(counter, &tmp, &len); + if (len) { + // [14:49:36 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}] > [{"POWER":"OFF"}] + char* JSON = (char*)memchr(tmp, '{', len); + if (JSON) { // Is it a JSON message (and not only [15:26:08 MQT: stat/wemos5/POWER = O]) + size_t JSONlen = len - (JSON - tmp); + if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); } + char stemp[JSONlen]; + strlcpy(stemp, JSON +1, JSONlen -2); + WSContentSend_P(PSTR("%s%s"), (cflg) ? "," : "", stemp); + cflg = true; } - counter++; - counter &= 0xFF; - if (!counter) counter++; // Skip 0 as it is not allowed - } while (counter != web_log_index); - WSContentSend_P(PSTR("}")); - } else { - WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENABLE_WEBLOG_FOR_RESPONSE "\"}")); - } + } + counter++; + counter &= 0xFF; + if (!counter) counter++; // Skip 0 as it is not allowed + } while (counter != web_log_index); + WSContentSend_P(PSTR("}")); } else { - WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENTER_COMMAND " cmnd=\"}")); + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENABLE_WEBLOG_FOR_RESPONSE "\"}")); } } else { - WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_NEED_USER_AND_PASSWORD "\"}")); + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENTER_COMMAND " cmnd=\"}")); } WSContentEnd(); } @@ -2575,7 +2713,7 @@ void HandleConsole(void) { if (!HttpCheckPriviledgedAccess()) { return; } - if (WebServer->hasArg("c2")) { // Console refresh requested + if (Webserver->hasArg("c2")) { // Console refresh requested HandleConsoleRefresh(); return; } @@ -2595,7 +2733,7 @@ void HandleConsoleRefresh(void) bool cflg = true; uint32_t counter = 0; // Initial start, should never be 0 again - String svalue = WebServer->arg("c1"); + String svalue = Webserver->arg("c1"); if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str()); ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE); @@ -2640,13 +2778,13 @@ void HandleConsoleRefresh(void) void HandleNotFound(void) { -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Not found (%s)"), WebServer->uri().c_str()); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Not found (%s)"), Webserver->uri().c_str()); if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the error page. #ifdef USE_EMULATION #ifdef USE_EMULATION_HUE - String path = WebServer->uri(); + String path = Webserver->uri(); if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) { HandleHueApi(&path); } else @@ -2654,9 +2792,9 @@ void HandleNotFound(void) #endif // USE_EMULATION { WSContentBegin(404, CT_PLAIN); - WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), WebServer->uri().c_str(), (WebServer->method() == HTTP_GET) ? "GET" : "POST", WebServer->args()); - for (uint32_t i = 0; i < WebServer->args(); i++) { - WSContentSend_P(PSTR(" %s: %s\n"), WebServer->argName(i).c_str(), WebServer->arg(i).c_str()); + WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), Webserver->uri().c_str(), (Webserver->method() == HTTP_GET) ? "GET" : "POST", Webserver->args()); + for (uint32_t i = 0; i < Webserver->args(); i++) { + WSContentSend_P(PSTR(" %s: %s\n"), Webserver->argName(i).c_str(), Webserver->arg(i).c_str()); } WSContentEnd(); } @@ -2666,12 +2804,12 @@ void HandleNotFound(void) bool CaptivePortal(void) { // Possible hostHeader: connectivitycheck.gstatic.com or 192.168.4.1 - if ((WifiIsInManagerMode()) && !ValidIpAddress(WebServer->hostHeader().c_str())) { + if ((WifiIsInManagerMode()) && !ValidIpAddress(Webserver->hostHeader().c_str())) { AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED)); - WebServer->sendHeader(F("Location"), String("http://") + WebServer->client().localIP().toString(), true); + Webserver->sendHeader(F("Location"), String("http://") + Webserver->client().localIP().toString(), true); WSSend(302, CT_PLAIN, ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. - WebServer->client().stop(); // Stop is needed because we sent no content length + Webserver->client().stop(); // Stop is needed because we sent no content length return true; } return false; @@ -3002,11 +3140,7 @@ bool Xdrv01(uint8_t function) case FUNC_LOOP: PollDnsWebserver(); #ifdef USE_EMULATION -#ifdef USE_DEVICE_GROUPS - if (Settings.flag2.emulation || Settings.flag4.device_groups_enabled) { PollUdp(); } -#else // USE_DEVICE_GROUPS if (Settings.flag2.emulation) { PollUdp(); } -#endif // USE_DEVICE_GROUPS #endif // USE_EMULATION break; case FUNC_COMMAND: diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 1d084862b..eaa2562e8 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -374,7 +374,7 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retain MqttPublish(stopic, retained); #ifdef USE_MQTT_AWS_IOT - if ((prefix > 0) && (Settings.flag4.awsiot_shadow)) { // placeholder for SetOptionXX + if ((prefix > 0) && (Settings.flag4.awsiot_shadow) && (Mqtt.connected)) { // placeholder for SetOptionXX // compute the target topic char *topic = SettingsText(SET_MQTT_TOPIC); char topic2[strlen(topic)+1]; // save buffer onto stack @@ -440,9 +440,11 @@ void MqttPublishPowerState(uint32_t device) Response_P(S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1))); MqttPublish(stopic); - GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P(GetStateText(bitRead(power, device -1))); - MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN + if (!Settings.flag4.only_json_message) { // SetOption90 - Disable non-json MQTT response + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P(GetStateText(bitRead(power, device -1))); + MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN + } #ifdef USE_SONOFF_IFAN } #endif // USE_SONOFF_IFAN @@ -503,15 +505,23 @@ void MqttConnected(void) Response_P(PSTR(D_ONLINE)); MqttPublish(stopic, true); - // Satisfy iobroker (#299) - mqtt_data[0] = '\0'; - MqttPublishPrefixTopic_P(CMND, S_RSLT_POWER); + if (!Settings.flag4.only_json_message) { // SetOption90 - Disable non-json MQTT response + // Satisfy iobroker (#299) + mqtt_data[0] = '\0'; + MqttPublishPrefixTopic_P(CMND, S_RSLT_POWER); + } GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#")); MqttSubscribe(stopic); if (strstr_P(SettingsText(SET_MQTT_FULLTOPIC), MQTT_TOKEN_TOPIC) != nullptr) { - GetGroupTopic_P(stopic, PSTR("#")); // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# or SetOption75 1: cmnd/ - MqttSubscribe(stopic); + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + GetGroupTopic_P(stopic, PSTR("#"), real_index +i); // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# or SetOption75 1: cmnd/ + MqttSubscribe(stopic); + } + } GetFallbackTopic_P(stopic, PSTR("#")); MqttSubscribe(stopic); } @@ -520,30 +530,33 @@ void MqttConnected(void) } if (Mqtt.initial_connection_state) { - char stopic2[TOPSZ]; - Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), - ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "")); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); + if (ResetReason() != REASON_DEEP_SLEEP_AWAKE) { + char stopic2[TOPSZ]; + Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), + ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "", SET_MQTT_GRP_TOPIC)); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); #ifdef USE_WEBSERVER - if (Settings.webserver) { + if (Settings.webserver) { #if LWIP_IPV6 - Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str(),WifiGetIPv6().c_str()); + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str(),WifiGetIPv6().c_str()); #else - Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); #endif // LWIP_IPV6 = 1 - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); - } + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); + } #endif // USE_WEBSERVER - Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":")); - if (CrashFlag()) { - CrashDump(); - } else { - ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str()); + Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":")); + if (CrashFlag()) { + CrashDump(); + } else { + ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str()); + } + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); } - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); + MqttPublishAllPowerState(); if (Settings.tele_period) { tele_period = Settings.tele_period -5; // Enable TelePeriod in 5 seconds @@ -881,26 +894,52 @@ void CmndPublish(void) void CmndGroupTopic(void) { -#ifdef USE_DEVICE_GROUPS - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - uint32_t settings_text_index = (XdrvMailbox.index <= 1 ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2); -#endif // USE_DEVICE_GROUPS - if (XdrvMailbox.data_len > 0) { - MakeValidMqtt(0, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } -#ifdef USE_DEVICE_GROUPS - SettingsUpdateText(settings_text_index, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); -#else // USE_DEVICE_GROUPS - SettingsUpdateText(SET_MQTT_GRP_TOPIC, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); -#endif // USE_DEVICE_GROUPS - restart_flag = 2; + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_GROUP_TOPICS)) { + if (XdrvMailbox.data_len > 0) { + uint32_t settings_text_index = (1 == XdrvMailbox.index) ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2; + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); + + // Eliminate duplicates, have at least one and fill from index 1 + char stemp[MAX_GROUP_TOPICS][TOPSZ]; + uint32_t read_index = 0; + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + bool not_equal = true; + for (uint32_t j = 0; j < read_index; j++) { + if (!strcmp(SettingsText(real_index +i), stemp[j])) { // Topics are case-sensitive + not_equal = false; + } + } + if (not_equal) { + strncpy(stemp[read_index], SettingsText(real_index +i), sizeof(stemp[read_index])); + read_index++; + } + } + } + if (0 == read_index) { + SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); + } else { + uint32_t write_index = 0; + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (write_index < read_index) { + SettingsUpdateText(real_index +i, stemp[write_index]); + write_index++; + } else { + SettingsUpdateText(real_index +i, ""); + } + } + } + + restart_flag = 2; + } + ResponseCmndAll(SET_MQTT_GRP_TOPIC, MAX_GROUP_TOPICS); } -#ifdef USE_DEVICE_GROUPS - ResponseCmndChar(SettingsText(settings_text_index)); - } -#else // USE_DEVICE_GROUPS - ResponseCmndChar(SettingsText(SET_MQTT_GRP_TOPIC)); -#endif // USE_DEVICE_GROUPS } void CmndTopic(void) @@ -989,6 +1028,9 @@ void CmndPowerRetain(void) } } Settings.flag.mqtt_power_retain = XdrvMailbox.payload; // CMND_POWERRETAIN + if (Settings.flag.mqtt_power_retain) { + Settings.flag4.only_json_message = 0; // SetOption90 - Disable non-json MQTT response + } } ResponseCmndStateText(Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN } @@ -1203,7 +1245,7 @@ const char HTTP_FORM_MQTT1[] PROGMEM = "

" D_CLIENT " (%s)

"; const char HTTP_FORM_MQTT2[] PROGMEM = "

" D_USER " (" MQTT_USER ")

" - "

" D_PASSWORD "

" + "


" "

" D_TOPIC " = %%topic%% (%s)

" "

" D_FULL_TOPIC " (%s)

"; @@ -1213,7 +1255,7 @@ void HandleMqttConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT); - if (WebServer->hasArg("save")) { + if (Webserver->hasArg("save")) { MqttSaveSettings(); WebRestart(1); return; @@ -1295,7 +1337,7 @@ bool Xdrv02(uint8_t function) WSContentSend_P(HTTP_BTN_MENU_MQTT); break; case FUNC_WEB_ADD_HANDLER: - WebServer->on("/" WEB_HANDLE_MQTT, HandleMqttConfiguration); + Webserver->on("/" WEB_HANDLE_MQTT, HandleMqttConfiguration); break; #endif // USE_WEBSERVER case FUNC_COMMAND: diff --git a/tasmota/xdrv_03_energy.ino b/tasmota/xdrv_03_energy.ino index bb74b991d..4210cea1c 100644 --- a/tasmota/xdrv_03_energy.ino +++ b/tasmota/xdrv_03_energy.ino @@ -96,6 +96,7 @@ struct ENERGY { uint8_t phase_count = 1; // Number of phases active bool voltage_common = false; // Use single voltage and frequency + bool kWhtoday_offset_init = false; bool voltage_available = true; // Enable if voltage is measured bool current_available = true; // Enable if current is measured @@ -223,6 +224,12 @@ void Energy200ms(void) XnrgCall(FUNC_ENERGY_EVERY_SECOND); if (RtcTime.valid) { + + if (!Energy.kWhtoday_offset_init && (RtcTime.day_of_year == Settings.energy_kWhdoy)) { + Energy.kWhtoday_offset = Settings.energy_kWhtoday; + Energy.kWhtoday_offset_init = true; + } + if (LocalTime() == Midnight()) { Settings.energy_kWhyesterday = RtcSettings.energy_kWhtoday; @@ -827,14 +834,11 @@ void EnergySnsInit(void) XnrgCall(FUNC_INIT); if (energy_flg) { - if (RtcSettingsValid()) { + Energy.kWhtoday_offset = 0; + // Do not use at Power On as Rtc was invalid (but has been restored from Settings already) + if ((ResetReason() != REASON_DEFAULT_RST) && RtcSettingsValid()) { Energy.kWhtoday_offset = RtcSettings.energy_kWhtoday; - } - else if (RtcTime.day_of_year == Settings.energy_kWhdoy) { - Energy.kWhtoday_offset = Settings.energy_kWhtoday; - } - else { - Energy.kWhtoday_offset = 0; + Energy.kWhtoday_offset_init = true; } Energy.kWhtoday = 0; Energy.kWhtoday_delta = 0; diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 6b72da9b2..345951696 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -131,12 +131,26 @@ const uint8_t LIGHT_COLOR_SIZE = 25; // Char array scolor size const char kLightCommands[] PROGMEM = "|" // No prefix D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" - D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ; + D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR +#ifdef USE_LIGHT_PALETTE + "|" D_CMND_PALETTE +#endif // USE_LIGHT_PALETTE +#ifdef USE_DGR_LIGHT_SEQUENCE + "|" D_CMND_SEQUENCE_OFFSET +#endif // USE_DGR_LIGHT_SEQUENCE + "|UNDOCA" ; void (* const LightCommand[])(void) PROGMEM = { &CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndLedTable, &CmndFade, &CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration, - &CmndWhite, &CmndChannel, &CmndHsbColor, &CmndUndocA }; + &CmndWhite, &CmndChannel, &CmndHsbColor, +#ifdef USE_LIGHT_PALETTE + &CmndPalette, +#endif // USE_LIGHT_PALETTE +#ifdef USE_DGR_LIGHT_SEQUENCE + &CmndSequenceOffset, +#endif // USE_DGR_LIGHT_SEQUENCE + &CmndUndocA }; // Light color mode, either RGB alone, or white-CT alone, or both only available if ct_rgb_linked is false enum LightColorModes { @@ -273,11 +287,26 @@ struct LIGHT { bool fade_initialized = false; // dont't fade at startup bool fade_running = false; +#ifdef USE_DEVICE_GROUPS + uint8_t last_scheme = 0; + bool devgrp_no_channels_out = false; // don't share channels with device group (e.g. if scheme set by other device) +#ifdef USE_DGR_LIGHT_SEQUENCE + uint8_t sequence_offset = 0; // number of channel changes this light is behind the master + uint8_t * channels_fifo; +#endif // USE_DGR_LIGHT_SEQUENCE +#endif // USE_DEVICE_GROUPS +#ifdef USE_LIGHT_PALETTE + uint8_t palette_count = 0; // palette entry count + uint8_t * palette; // dynamically allocated palette color array +#endif // USE_LIGHT_PALETTE uint16_t fade_start_10[LST_MAX] = {0,0,0,0,0}; uint16_t fade_cur_10[LST_MAX]; uint16_t fade_end_10[LST_MAX]; // 10 bits resolution target channel values uint16_t fade_duration = 0; // duration of fade in milliseconds uint32_t fade_start = 0; // fade start time in milliseconds, compared to millis() + + uint16_t pwm_min = 0; // minimum value for PWM, from DimmerRange, 0..1023 + uint16_t pwm_max = 1023; // maxumum value for PWM, from DimmerRange, 0..1023 } Light; power_t LightPower(void) @@ -607,6 +636,7 @@ class LightStateClass { void setCW(uint8_t c, uint8_t w, bool free_range = false) { uint16_t max = (w > c) ? w : c; // 0..255 uint16_t sum = c + w; + if (sum <= 257) { free_range = false; } // if we don't allow free range or if sum is below 255 (with tolerance of 2) if (0 == max) { _briCT = 0; // brightness set to null @@ -702,6 +732,8 @@ class LightStateClass { AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels (%d %d %d %d %d)", channels[0], channels[1], channels[2], channels[3], channels[4]); AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels CT (%d) briRGB (%d) briCT (%d)", _ct, _briRGB, _briCT); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels Actuals (%d %d %d %d %d)", + _r, _g, _b, _wc, _ww); #endif } @@ -947,17 +979,24 @@ public: // only if non-multi channel // We apply dimmer in priority to RGB uint8_t bri = _state->DimmerToBri(Settings.light_dimmer); - if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) { - // The default values are #FFFFFFFFFF, in this case we avoid setting all channels - // at the same time, see #6534 - if ( (DEFAULT_LIGHT_COMPONENT == Settings.light_color[0]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[1]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[2]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[3]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[4]) && - (DEFAULT_LIGHT_DIMMER == Settings.light_dimmer) ) { - _state->setColorMode(LCM_RGB); + + // The default values are #FFFFFFFFFF, in this case we avoid setting all channels + // at the same time, see #6534 and #8120 + if ((DEFAULT_LIGHT_COMPONENT == Settings.light_color[0]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[1]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[2]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[3]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[4]) && + (DEFAULT_LIGHT_DIMMER == Settings.light_dimmer) ) { + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + _state->setCW(255, 0); // avoid having both white channels at 100%, zero second channel (#see 8120) } + _state->setBriCT(bri); + _state->setBriRGB(bri); + _state->setColorMode(LCM_RGB); + } + + if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) { _state->setBriRGB(bri); } else { _state->setBriCT(bri); @@ -1205,7 +1244,7 @@ bool LightModuleInit(void) if (Settings.flag.pwm_control) { // SetOption15 - Switch between commands PWM or COLOR/DIMMER/CT/CHANNEL for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 +i] < 99) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 + if (PinUsed(GPIO_PWM1, i)) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 } } @@ -1213,6 +1252,7 @@ bool LightModuleInit(void) if (XlgtCall(FUNC_MODULE_INIT)) { // serviced } +#ifdef ESP8266 else if (SONOFF_BN == my_module_type) { // PWM Single color led (White) light_type = LT_PWM1; } @@ -1231,6 +1271,7 @@ bool LightModuleInit(void) } light_type = LT_PWM2; } +#endif // ESP8266 if (light_type > LT_BASIC) { devices_present++; @@ -1249,6 +1290,25 @@ bool LightModuleInit(void) return (light_type > LT_BASIC); } +// compute actual PWM min/max values from DimmerRange +// must be called when DimmerRange is changed or LedTable +void LightCalcPWMRange(void) { + uint16_t pwm_min, pwm_max; + + pwm_min = change8to10(LightStateClass::DimmerToBri(Settings.dimmer_hw_min)); // default 0 + pwm_max = change8to10(LightStateClass::DimmerToBri(Settings.dimmer_hw_max)); // default 100 + if (Settings.light_correction) { + pwm_min = ledGamma10_10(pwm_min); // apply gamma correction + pwm_max = ledGamma10_10(pwm_max); // 0..1023 + } + pwm_min = pwm_min > 0 ? changeUIntScale(pwm_min, 1, 1023, 1, Settings.pwm_range) : 0; // adapt range but keep zero and non-zero values + pwm_max = changeUIntScale(pwm_max, 1, 1023, 1, Settings.pwm_range); // pwm_max cannot be zero + + Light.pwm_min = pwm_min; + Light.pwm_max = pwm_max; + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("LightCalcPWMRange %d %d - %d %d"), Settings.dimmer_hw_min, Settings.dimmer_hw_max, Light.pwm_min, Light.pwm_max); +} + void LightInit(void) { Light.device = devices_present; @@ -1270,6 +1330,7 @@ void LightInit(void) // if RGBW or RGBCW, and SetOption37 >= 128, we manage RGB and W separately Light.device--; // we take the last two devices as lights } + LightCalcPWMRange(); #ifdef DEBUG_LIGHT AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightInit Light.pwm_multi_channels=%d Light.subtype=%d Light.device=%d devices_present=%d", Light.pwm_multi_channels, Light.subtype, Light.device, devices_present); @@ -1278,6 +1339,7 @@ void LightInit(void) light_controller.setSubType(Light.subtype); light_controller.loadSettings(); light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); + light_controller.calcLevels(); // calculate the initial values (#8058) if (LST_SINGLE == Light.subtype) { Settings.light_color[0] = 255; // One channel only supports Dimmer but needs max color @@ -1285,14 +1347,14 @@ void LightInit(void) if (light_type < LT_PWM6) { // PWM for (uint32_t i = 0; i < light_type; i++) { Settings.pwm_value[i] = 0; // Disable direct PWM control - if (pin[GPIO_PWM1 +i] < 99) { - pinMode(pin[GPIO_PWM1 +i], OUTPUT); + if (PinUsed(GPIO_PWM1, i)) { + pinMode(Pin(GPIO_PWM1, i), OUTPUT); } } - if (pin[GPIO_ARIRFRCV] < 99) { - if (pin[GPIO_ARIRFSEL] < 99) { - pinMode(pin[GPIO_ARIRFSEL], OUTPUT); - digitalWrite(pin[GPIO_ARIRFSEL], 1); // Turn off RF + if (PinUsed(GPIO_ARIRFRCV)) { + if (PinUsed(GPIO_ARIRFSEL)) { + pinMode(Pin(GPIO_ARIRFSEL), OUTPUT); + digitalWrite(Pin(GPIO_ARIRFSEL), 1); // Turn off RF } } } @@ -1307,6 +1369,9 @@ void LightInit(void) Light.power = 0; Light.update = true; Light.wakeup_active = 0; + if (Settings.flag4.fade_at_startup) { + Light.fade_initialized = true; // consider fade intialized starting from black + } LightUpdateColorMapping(); } @@ -1438,7 +1503,9 @@ void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value) // convert channels to string, use Option 17 to foce decimal, unless force_hex char* LightGetColor(char* scolor, boolean force_hex = false) { - light_controller.calcLevels(); + if ((0 == Settings.light_scheme) || (!Light.pwm_multi_channels)) { + light_controller.calcLevels(); // recalculate levels only if Scheme 0, otherwise we mess up levels + } scolor[0] = '\0'; for (uint32_t i = 0; i < Light.subtype; i++) { if (!force_hex && Settings.flag.decimal_text) { // SetOption17 - Switch between decimal or hexadecimal output @@ -1610,11 +1677,47 @@ void LightPreparePower(power_t channels = 0xFFFFFFFF) { // 1 = only RGB, 2 = LightState(0); } +#ifdef USE_LIGHT_PALETTE +void LightSetPaletteEntry(void) +{ + uint8_t bri = light_state.getBri(); + uint8_t * palette_entry = &Light.palette[Light.wheel * LST_MAX]; + for (int i = 0; i < LST_MAX; i++) { + Light.new_color[i] = changeUIntScale(palette_entry[i], 0, 255, 0, bri); + } + light_state.setChannelsRaw(Light.new_color); + if (!Light.pwm_multi_channels) { + light_state.setCW(Light.new_color[3], Light.new_color[4], true); + if (Light.new_color[0] || Light.new_color[1] || Light.new_color[2]) light_state.addRGBMode(); + } +} +#endif // USE_LIGHT_PALETTE + void LightCycleColor(int8_t direction) { - if (Light.strip_timer_counter % (Settings.light_speed * 2)) { +// if (Light.strip_timer_counter % (Settings.light_speed * 2)) { return; } // Speed 1: 24sec, 2: 48sec, 3: 72sec, etc + if (Settings.light_speed > 3) { + if (Light.strip_timer_counter % (Settings.light_speed - 2)) { return; } // Speed 4: 24sec, 5: 36sec, 6: 48sec, etc + } + +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) { + if (!Light.fade_running) { + if (0 == direction) { + Light.wheel = random(Light.palette_count); + } + else { + Light.wheel += direction; + if (Light.wheel >= Light.palette_count) { + Light.wheel = 0; + if (direction < 0) Light.wheel = Light.palette_count - 1; + } + } + LightSetPaletteEntry(); + } return; } +#endif // USE_LIGHT_PALETTE if (0 == direction) { if (Light.random == Light.wheel) { @@ -1630,14 +1733,22 @@ void LightCycleColor(int8_t direction) // direction = (Light.random < Light.wheel) ? -1 : 1; direction = (Light.random &0x01) ? 1 : -1; } + +// if (Settings.light_speed < 3) { direction <<= (3 - Settings.light_speed); } // Speed 1: 12/4=3sec, 2: 12/2=6sec, 3: 12sec + if (Settings.light_speed < 3) { direction *= (4 - Settings.light_speed); } // Speed 1: 12/3=4sec, 2: 12/2=6sec, 3: 12sec Light.wheel += direction; uint16_t hue = changeUIntScale(Light.wheel, 0, 255, 0, 359); // Scale to hue to keep amount of steps low (max 255 instead of 359) // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: random %d, wheel %d, hue %d"), Light.random, Light.wheel, hue); + if (!Light.pwm_multi_channels) { uint8_t sat; light_state.getHSB(nullptr, &sat, nullptr); // Allow user control over Saturation light_state.setHS(hue, sat); + } else { + light_state.setHS(hue, 255); + light_state.setBri(255); // If multi-channel, force bri to max, it will be later dimmed to correct value + } light_controller.calcLevels(Light.new_color); } @@ -1688,12 +1799,12 @@ void LightAnimate(void) // or set a maximum of PWM_MAX_SLEEP if light is on or Fade is running if (Light.power || Light.fade_running) { if (Settings.sleep > PWM_MAX_SLEEP) { - sleep = PWM_MAX_SLEEP; // set a maxumum value of 50 milliseconds to ensure that animations are smooth + ssleep = PWM_MAX_SLEEP; // set a maxumum value of 50 milliseconds to ensure that animations are smooth } else { - sleep = Settings.sleep; // or keep the current sleep if it's lower than 50 + ssleep = Settings.sleep; // or keep the current sleep if it's lower than 50 } } else { - sleep = Settings.sleep; + ssleep = Settings.sleep; } if (!Light.power) { // All channels powered off @@ -1742,17 +1853,32 @@ void LightAnimate(void) } break; case LS_CYCLEUP: - LightCycleColor(1); - break; case LS_CYCLEDN: - LightCycleColor(-1); - break; case LS_RANDOM: - LightCycleColor(0); + if (LS_CYCLEUP == Settings.light_scheme) { + LightCycleColor(1); + } else if (LS_CYCLEDN == Settings.light_scheme) { + LightCycleColor(-1); + } else { + LightCycleColor(0); + } + if (Light.pwm_multi_channels) { // See #8058 + Light.new_color[0] = changeUIntScale(Light.new_color[0], 0, 255, 0, Settings.light_color[0]); + Light.new_color[1] = changeUIntScale(Light.new_color[1], 0, 255, 0, Settings.light_color[1]); + Light.new_color[2] = changeUIntScale(Light.new_color[2], 0, 255, 0, Settings.light_color[2]); + } break; default: XlgtCall(FUNC_SET_SCHEME); } + +#ifdef USE_DEVICE_GROUPS + if (Settings.light_scheme != Light.last_scheme) { + Light.last_scheme = Settings.light_scheme; + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); + Light.devgrp_no_channels_out = false; + } +#endif // USE_DEVICE_GROUPS } if ((Settings.light_scheme < LS_MAX) || power_off) { // exclude WS281X Neopixel schemes @@ -1771,7 +1897,7 @@ void LightAnimate(void) } if (Light.update) { #ifdef USE_DEVICE_GROUPS - if (Light.power) LightSendDeviceGroupStatus(); + if (Light.power) LightSendDeviceGroupStatus(false); #endif // USE_DEVICE_GROUPS uint16_t cur_col_10[LST_MAX]; // 10 bits resolution @@ -1823,13 +1949,9 @@ void LightAnimate(void) } // final adjusments for PMW, post-gamma correction - uint16_t min = 1; -#ifdef USE_PWM_DIMMER - if (PWM_DIMMER == my_module_type) min = Settings.dimmer_hw_min; -#endif // USE_PWM_DIMMER for (uint32_t i = 0; i < LST_MAX; i++) { // scale from 0..1023 to 0..pwm_range, but keep any non-zero value to at least 1 - cur_col_10[i] = (cur_col_10[i] > 0) ? changeUIntScale(cur_col_10[i], 1, 1023, min, Settings.pwm_range) : 0; + cur_col_10[i] = (cur_col_10[i] > 0) ? changeUIntScale(cur_col_10[i], 1, 1023, 1, Settings.pwm_range) : 0; } // apply port remapping on both 8 bits and 10 bits versions @@ -1875,14 +1997,26 @@ void LightAnimate(void) bool isChannelGammaCorrected(uint32_t channel) { if (!Settings.light_correction) { return false; } // Gamma correction not activated if (channel >= Light.subtype) { return false; } // Out of range - - if (PHILIPS == my_module_type) { +#ifdef ESP8266 + if ((PHILIPS == my_module_type) || (Settings.flag4.pwm_ct_mode)) { if ((LST_COLDWARM == Light.subtype) && (1 == channel)) { return false; } // PMW reserved for CT if ((LST_RGBCW == Light.subtype) && (4 == channel)) { return false; } // PMW reserved for CT } +#endif // ESP8266 return true; } +// is the channel a regular PWM or ColorTemp control +bool isChannelCT(uint32_t channel) { +#ifdef ESP8266 + if ((PHILIPS == my_module_type) || (Settings.flag4.pwm_ct_mode)) { + if ((LST_COLDWARM == Light.subtype) && (1 == channel)) { return true; } // PMW reserved for CT + if ((LST_RGBCW == Light.subtype) && (4 == channel)) { return true; } // PMW reserved for CT + } +#endif // ESP8266 + return false; +} + // Calculate the Gamma correction, if any, for fading, using the fast Gamma curve (10 bits in+out) uint16_t fadeGamma(uint32_t channel, uint16_t v) { if (isChannelGammaCorrected(channel)) { @@ -2001,9 +2135,13 @@ void LightSetOutputs(const uint16_t *cur_col_10) { // now apply the actual PWM values, adjusted and remapped 10-bits range if (light_type < LT_PWM6) { // only for direct PWM lights, not for Tuya, Armtronix... for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) { - if (pin[GPIO_PWM1 +i] < 99) { + if (PinUsed(GPIO_PWM1, i)) { //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d"), i, cur_col_10[i]); - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col_10[(i + Light.pwm_offset)] : cur_col_10[(i + Light.pwm_offset)]); + uint16_t cur_col = cur_col_10[i + Light.pwm_offset]; + if (!isChannelCT(i)) { // if CT don't use pwm_min and pwm_max + cur_col = cur_col > 0 ? changeUIntScale(cur_col, 0, Settings.pwm_range, Light.pwm_min, Light.pwm_max) : 0; // shrink to the range of pwm_min..pwm_max + } + analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col : cur_col); } } } @@ -2054,7 +2192,8 @@ void calcGammaBulbs(uint16_t cur_col_10[5]) { uint16_t white_bri10 = cur_col_10[cw0] + cur_col_10[cw1]; // cumulated brightness uint16_t white_bri10_1023 = (white_bri10 > 1023) ? 1023 : white_bri10; // max 1023 - if (PHILIPS == my_module_type) { // channel 1 is the color tone, mapped to cold channel (0..255) +#ifdef ESP8266 + if ((PHILIPS == my_module_type) || (Settings.flag4.pwm_ct_mode)) { // channel 1 is the color tone, mapped to cold channel (0..255) // Xiaomi Philips bulbs follow a different scheme: cur_col_10[cw1] = light_state.getCT10bits(); // channel 0=intensity, channel1=temperature @@ -2063,7 +2202,9 @@ void calcGammaBulbs(uint16_t cur_col_10[5]) { } else { cur_col_10[cw0] = white_bri10_1023; // no gamma, extend to 10 bits } - } else if (Settings.light_correction) { + } else +#endif // ESP8266 + if (Settings.light_correction) { // if sum of both channels is > 255, then channels are probably uncorrelated if (white_bri10 <= 1031) { // take a margin of 8 above 1023 to account for rounding errors // we calculate the gamma corrected sum of CW + WW @@ -2093,25 +2234,36 @@ void calcGammaBulbs(uint16_t cur_col_10[5]) { } #ifdef USE_DEVICE_GROUPS -void LightSendDeviceGroupStatus() +void LightSendDeviceGroupStatus(bool status) { - if (Light.subtype > LST_SINGLE) { - uint8_t channels[LST_MAX]; - light_state.getChannels(channels); - SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_LIGHT_CHANNELS, channels); + static uint8_t last_bri; + uint8_t bri = light_state.getBri(); + bool send_bri_update = (status || bri != last_bri); + if (Light.subtype > LST_SINGLE && !Light.devgrp_no_channels_out) { + static uint8_t channels[LST_MAX + 1] = { 0, 0, 0, 0, 0, 0 }; + if (status) { + light_state.getChannels(channels); + } + else { + memcpy(channels, Light.new_color, LST_MAX); + channels[LST_MAX]++; + } + SendLocalDeviceGroupMessage((send_bri_update ? DGR_MSGTYP_PARTIAL_UPDATE : DGR_MSGTYP_UPDATE), DGR_ITEM_LIGHT_CHANNELS, channels); + } + if (send_bri_update) { + last_bri = bri; + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, light_state.getBri()); } - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme, - DGR_ITEM_LIGHT_BRI, light_state.getBri()); } -void LightHandleDeviceGroupItem() +void LightHandleDevGroupItem(void) { static bool send_state = false; static bool restore_power = false; bool more_to_come; uint32_t value = XdrvMailbox.payload; #ifdef USE_PWM_DIMMER_REMOTE - if (XdrvMailbox.index & 0xff00) return; // Ignore updates from other device groups + if (*XdrvMailbox.topic) return; // Ignore updates from other device groups #endif // USE_PWM_DIMMER_REMOTE switch (XdrvMailbox.command_code) { case DGR_ITEM_EOL: @@ -2139,16 +2291,49 @@ void LightHandleDeviceGroupItem() break; case DGR_ITEM_LIGHT_SCHEME: if (Settings.light_scheme != value) { - Settings.light_scheme = value; + Light.last_scheme = Settings.light_scheme = value; + Light.devgrp_no_channels_out = (value != 0); send_state = true; } break; case DGR_ITEM_LIGHT_CHANNELS: - light_controller.changeChannels((uint8_t *)XdrvMailbox.data); +#ifdef USE_DGR_LIGHT_SEQUENCE + { + static uint8_t last_sequence = 0; + + // If a sequence offset is set, set the channels to the ones we received + // changes ago. + if (Light.sequence_offset) { + light_controller.changeChannels(Light.channels_fifo); + + // Shift the fifo down and load the newly received channels at the end for this update and + // any updates we missed. + int last_entry = (Light.sequence_offset - 1) * LST_MAX; + for (uint8_t sequence = (uint8_t)XdrvMailbox.data[LST_MAX]; (uint8_t)(sequence - last_sequence) > 0; last_sequence++) { + memmove(Light.channels_fifo, &Light.channels_fifo[LST_MAX], last_entry); + memcpy(&Light.channels_fifo[last_entry], XdrvMailbox.data, LST_MAX); + } + } + else { +#endif // USE_DGR_LIGHT_SEQUENCE + light_controller.changeChannels((uint8_t *)XdrvMailbox.data); +#ifdef USE_DGR_LIGHT_SEQUENCE + } + } +#endif // USE_DGR_LIGHT_SEQUENCE send_state = true; break; case DGR_ITEM_LIGHT_FIXED_COLOR: if (Light.subtype >= LST_RGBW) { + send_state = true; +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) { + Light.wheel = value % Light.palette_count; + LightSetPaletteEntry(); + break; + } +#endif // !USE_LIGHT_PALETTE + value = value % MAX_FIXED_COLOR; if (value) { bool save_decimal_text = Settings.flag.decimal_text; char str[16]; @@ -2158,6 +2343,7 @@ void LightHandleDeviceGroupItem() light_controller.changeChannels(Light.entry_color); light_controller.changeBri(old_bri); Settings.light_scheme = 0; + Light.devgrp_no_channels_out = false; } else { light_state.setColorMode(LCM_CT); @@ -2167,7 +2353,6 @@ void LightHandleDeviceGroupItem() Light.power = 0xff; restore_power = true; } - send_state = true; } break; case DGR_ITEM_LIGHT_FADE: @@ -2184,8 +2369,8 @@ void LightHandleDeviceGroupItem() break; case DGR_ITEM_STATUS: SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_LIGHT_FADE, Settings.light_fade, - DGR_ITEM_LIGHT_SPEED, Settings.light_speed); - LightSendDeviceGroupStatus(); + DGR_ITEM_LIGHT_SPEED, Settings.light_speed, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); + LightSendDeviceGroupStatus(true); break; } } @@ -2202,6 +2387,9 @@ bool LightColorEntry(char *buffer, uint32_t buffer_length) char *str; uint32_t entry_type = 0; // Invalid uint8_t value = Light.fixed_color_index; +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) value = Light.wheel; +#endif // USE_LIGHT_PALETTE if (buffer[0] == '#') { // Optional hexadecimal entry buffer++; @@ -2210,14 +2398,29 @@ bool LightColorEntry(char *buffer, uint32_t buffer_length) if (Light.subtype >= LST_RGB) { char option = (1 == buffer_length) ? buffer[0] : '\0'; - if (('+' == option) && (Light.fixed_color_index < MAX_FIXED_COLOR)) { - value++; + if ('+' == option) { +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count || Light.fixed_color_index < MAX_FIXED_COLOR) { +#else // USE_LIGHT_PALETTE + if (Light.fixed_color_index < MAX_FIXED_COLOR) { +#endif // !USE_LIGHT_PALETTE + value++; + } } - else if (('-' == option) && (Light.fixed_color_index > 1)) { - value--; + else if ('-' == option) { +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count || Light.fixed_color_index > 1) { +#else // USE_LIGHT_PALETTE + if (Light.fixed_color_index > 1) { +#endif // !USE_LIGHT_PALETTE + value--; + } } else { value = atoi(buffer); } +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) value = value % Light.palette_count; +#endif // USE_LIGHT_PALETTE } memset(&Light.entry_color, 0x00, sizeof(Light.entry_color)); @@ -2242,6 +2445,14 @@ bool LightColorEntry(char *buffer, uint32_t buffer_length) } entry_type = 1; // Hexadecimal } +#ifdef USE_LIGHT_PALETTE + else if (Light.palette_count) { + value--; + Light.wheel = value; + memcpy_P(&Light.entry_color, &Light.palette[value * LST_MAX], LST_MAX); + entry_type = 1; // Hexadecimal + } +#endif // USE_LIGHT_PALETTE else if ((Light.subtype >= LST_RGB) && (value > 0) && (value <= MAX_FIXED_COLOR)) { Light.fixed_color_index = value; memcpy_P(&Light.entry_color, &kFixedColor[value -1], 3); @@ -2278,14 +2489,22 @@ void CmndSupportColor(void) valid_entry = LightColorEntry(XdrvMailbox.data, XdrvMailbox.data_len); if (valid_entry) { if (XdrvMailbox.index <= 2) { // Color(1), 2 - uint32_t old_bri = light_state.getBri(); - // change all channels to specified values - light_controller.changeChannels(Light.entry_color); - if (2 == XdrvMailbox.index) { - // If Color2, set back old brightness - light_controller.changeBri(old_bri); +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count && XdrvMailbox.index == 2) { + LightSetPaletteEntry(); } - + else { +#endif // USE_LIGHT_PALETTE + uint32_t old_bri = light_state.getBri(); + // change all channels to specified values + light_controller.changeChannels(Light.entry_color); + if (2 == XdrvMailbox.index) { + // If Color2, set back old brightness + light_controller.changeBri(old_bri); + } +#ifdef USE_LIGHT_PALETTE + } +#endif // USE_LIGHT_PALETTE Settings.light_scheme = 0; coldim = true; } else { // Color3, 4, 5 and 6 @@ -2451,11 +2670,11 @@ void CmndScheme(void) uint32_t parm[2]; if (ParseParameters(2, parm) > 1) { Light.wheel = parm[1]; +#ifdef USE_LIGHT_PALETTE + Light.wheel--; +#endif // USE_LIGHT_PALETTE } Settings.light_scheme = XdrvMailbox.payload; -#ifdef USE_DEVICE_GROUPS - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); -#endif // USE_DEVICE_GROUPS if (LS_WAKEUP == Settings.light_scheme) { Light.wakeup_active = 3; } @@ -2558,8 +2777,11 @@ void CmndDimmer(void) } } #if defined(USE_PWM_DIMMER) && defined(USE_DEVICE_GROUPS) - Settings.bri_power_on = light_state.getBri();; - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on); + uint8_t bri = light_state.getBri(); + if (bri != Settings.bri_power_on) { + Settings.bri_power_on = bri; + SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on); + } #endif // USE_PWM_DIMMER && USE_DEVICE_GROUPS Light.update = true; if (skip_light_fade) LightAnimate(); @@ -2585,7 +2807,8 @@ void CmndDimmerRange(void) Settings.dimmer_hw_min = parm[1]; Settings.dimmer_hw_max = parm[0]; } - if (PWM_DIMMER != my_module_type) restart_flag = 2; + LightCalcPWMRange(); + Light.update = true; } Response_P(PSTR("{\"" D_CMND_DIMMER_RANGE "\":{\"Min\":%d,\"Max\":%d}}"), Settings.dimmer_hw_min, Settings.dimmer_hw_max); } @@ -2606,6 +2829,7 @@ void CmndLedTable(void) Settings.light_correction ^= 1; break; } + LightCalcPWMRange(); Light.update = true; } ResponseCmndStateText(Settings.light_correction); @@ -2689,6 +2913,84 @@ void CmndWakeupDuration(void) ResponseCmndNumber(Settings.light_wakeup); } +#ifdef USE_LIGHT_PALETTE +void CmndPalette(void) +{ + uint8_t * palette_entry; + char * p; + + // Palette Color[ ...] + if (XdrvMailbox.data_len) { + Light.wheel = 0; + Light.palette_count = 0; + if (Light.palette) { + free(Light.palette); + Light.palette = nullptr; + } + if (XdrvMailbox.data_len > 1 || XdrvMailbox.data[0] != '0') { + uint8_t palette_count = 0; + char * color = XdrvMailbox.data; + if (!(Light.palette = (uint8_t *)malloc(255 * Light.subtype))) return; + palette_entry = Light.palette; + for (;;) { + p = strchr(color, ' '); + if (p) *p = 0; + color = Trim(color); + if (*color && LightColorEntry(color, strlen(color))) { + memcpy(palette_entry, Light.entry_color, Light.subtype); + palette_entry += Light.subtype; + palette_count++; + } + if (!p) break; + color = p + 1; + } + if (!(Light.palette = (uint8_t *)realloc(Light.palette, palette_count * Light.subtype))) return; + Light.palette_count = palette_count; + } + } + + char palette_str[5 * Light.subtype * Light.palette_count + 3]; + p = palette_str; + *p++ = '['; + if (Light.palette_count) { + palette_entry = Light.palette; + for (int entry = 0; entry < Light.palette_count; entry++) { + if (Settings.flag.decimal_text) { // SetOption17 - Switch between decimal or hexadecimal output + *p++ = '"'; + for (uint32_t i = 0; i < Light.subtype; i++) { + p += sprintf_P(p, PSTR("%d,"), *palette_entry++); + } + *(p - 1) = '"'; + } + else { + for (uint32_t i = 0; i < Light.subtype; i++) { + p += sprintf_P(p, PSTR("%02X"), *palette_entry++); + } + } + *p++ = ','; + } + p--; + } + *p++ = ']'; + *p = 0; + ResponseCmndChar(palette_str); +} +#endif // USE_LIGHT_PALETTE + +#ifdef USE_DGR_LIGHT_SEQUENCE +void CmndSequenceOffset(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { + if (XdrvMailbox.payload != Light.sequence_offset) { + if (Light.sequence_offset) free(Light.channels_fifo); + Light.sequence_offset = XdrvMailbox.payload; + if (Light.sequence_offset) Light.channels_fifo = (uint8_t *)calloc(Light.sequence_offset, LST_MAX); + } + } + ResponseCmndNumber(Light.sequence_offset); +} +#endif // USE_DGR_LIGHT_SEQUENCE + void CmndUndocA(void) { // Theos legacy status @@ -2728,7 +3030,7 @@ bool Xdrv04(uint8_t function) break; #ifdef USE_DEVICE_GROUPS case FUNC_DEVICE_GROUP_ITEM: - LightHandleDeviceGroupItem(); + LightHandleDevGroupItem(); break; #endif // USE_DEVICE_GROUPS case FUNC_SET_POWER: diff --git a/tasmota/xdrv_05_irremote.ino b/tasmota/xdrv_05_irremote.ino index 53197b693..87a51de53 100644 --- a/tasmota/xdrv_05_irremote.ino +++ b/tasmota/xdrv_05_irremote.ino @@ -49,7 +49,7 @@ bool irsend_active = false; void IrSendInit(void) { - irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND + irsend = new IRsend(Pin(GPIO_IRSEND)); // an IR led is at GPIO_IRSEND irsend->begin(); } @@ -78,7 +78,7 @@ void IrReceiveUpdateThreshold(void) void IrReceiveInit(void) { // an IR led is at GPIO_IRRECV - irrecv = new IRrecv(pin[GPIO_IRRECV], IR_RCV_BUFFER_SIZE, IR_RCV_TIMEOUT, IR_RCV_SAVE_BUFFER); + irrecv = new IRrecv(Pin(GPIO_IRRECV), IR_RCV_BUFFER_SIZE, IR_RCV_TIMEOUT, IR_RCV_SAVE_BUFFER); irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); irrecv->enableIRIn(); // Start the receiver @@ -279,28 +279,28 @@ bool Xdrv05(uint8_t function) { bool result = false; - if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { + if (PinUsed(GPIO_IRSEND) || PinUsed(GPIO_IRRECV)) { switch (function) { case FUNC_PRE_INIT: - if (pin[GPIO_IRSEND] < 99) { + if (PinUsed(GPIO_IRSEND)) { IrSendInit(); } #ifdef USE_IR_RECEIVE - if (pin[GPIO_IRRECV] < 99) { + if (PinUsed(GPIO_IRRECV)) { IrReceiveInit(); } #endif // USE_IR_RECEIVE break; case FUNC_EVERY_50_MSECOND: #ifdef USE_IR_RECEIVE - if (pin[GPIO_IRRECV] < 99) { + if (PinUsed(GPIO_IRRECV)) { IrReceiveCheck(); // check if there's anything on IR side } #endif // USE_IR_RECEIVE irsend_active = false; // re-enable IR reception break; case FUNC_COMMAND: - if (pin[GPIO_IRSEND] < 99) { + if (PinUsed(GPIO_IRSEND)) { result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); } break; diff --git a/tasmota/xdrv_05_irremote_full.ino b/tasmota/xdrv_05_irremote_full.ino index 7d9987ae5..60e16b30d 100644 --- a/tasmota/xdrv_05_irremote_full.ino +++ b/tasmota/xdrv_05_irremote_full.ino @@ -48,7 +48,7 @@ bool irsend_active = false; void IrSendInit(void) { - irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND + irsend = new IRsend(Pin(GPIO_IRSEND)); // an IR led is at GPIO_IRSEND irsend->begin(); } @@ -105,7 +105,7 @@ void IrReceiveUpdateThreshold(void) void IrReceiveInit(void) { // an IR led is at GPIO_IRRECV - irrecv = new IRrecv(pin[GPIO_IRRECV], IR_FULL_BUFFER_SIZE, IR__FULL_RCV_TIMEOUT, IR_FULL_RCV_SAVE_BUFFER); + irrecv = new IRrecv(Pin(GPIO_IRRECV), IR_FULL_BUFFER_SIZE, IR__FULL_RCV_TIMEOUT, IR_FULL_RCV_SAVE_BUFFER); irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); irrecv->enableIRIn(); // Start the receiver } @@ -365,7 +365,7 @@ uint32_t IrRemoteCmndIrHvacJson(void) if (json[parm_uc]) { state.sleep = json[parm_uc]; } //if (json[D_JSON_IRHVAC_CLOCK]) { state.clock = json[D_JSON_IRHVAC_CLOCK]; } // not sure it's useful to support 'clock' - IRac ac(pin[GPIO_IRSEND]); + IRac ac(Pin(GPIO_IRSEND)); bool success = ac.sendAc(state, &prev); if (!success) { return IE_SYNTAX_IRHVAC; } @@ -635,24 +635,24 @@ bool Xdrv05(uint8_t function) { bool result = false; - if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { + if (PinUsed(GPIO_IRSEND) || PinUsed(GPIO_IRRECV)) { switch (function) { case FUNC_PRE_INIT: - if (pin[GPIO_IRSEND] < 99) { + if (PinUsed(GPIO_IRSEND)) { IrSendInit(); } - if (pin[GPIO_IRRECV] < 99) { + if (PinUsed(GPIO_IRRECV)) { IrReceiveInit(); } break; case FUNC_EVERY_50_MSECOND: - if (pin[GPIO_IRRECV] < 99) { + if (PinUsed(GPIO_IRRECV)) { IrReceiveCheck(); // check if there's anything on IR side } irsend_active = false; // re-enable IR reception break; case FUNC_COMMAND: - if (pin[GPIO_IRSEND] < 99) { + if (PinUsed(GPIO_IRSEND)) { result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); } break; diff --git a/tasmota/xdrv_06_snfbridge.ino b/tasmota/xdrv_06_snfbridge.ino index c8e9db67e..210ff87a3 100644 --- a/tasmota/xdrv_06_snfbridge.ino +++ b/tasmota/xdrv_06_snfbridge.ino @@ -481,11 +481,11 @@ void CmndRfKey(void) SnfBridge.learn_active = 0; if (2 == XdrvMailbox.payload) { // Learn RF data SonoffBridgeLearn(XdrvMailbox.index); - ResponseCmndIdxChar(D_JSON_START_LEARNING); + ResponseCmndIdxChar(PSTR(D_JSON_START_LEARNING)); } else if (3 == XdrvMailbox.payload) { // Unlearn RF data Settings.rf_code[XdrvMailbox.index][0] = 0; // Reset sync_time MSB - ResponseCmndIdxChar(D_JSON_SET_TO_DEFAULT); + ResponseCmndIdxChar(PSTR(D_JSON_SET_TO_DEFAULT)); } else if (4 == XdrvMailbox.payload) { // Save RF data provided by RFSync, RfLow, RfHigh and last RfCode for (uint32_t i = 0; i < 6; i++) { @@ -494,7 +494,7 @@ void CmndRfKey(void) Settings.rf_code[XdrvMailbox.index][6] = (SnfBridge.last_send_code >> 16) & 0xff; Settings.rf_code[XdrvMailbox.index][7] = (SnfBridge.last_send_code >> 8) & 0xff; Settings.rf_code[XdrvMailbox.index][8] = SnfBridge.last_send_code & 0xff; - ResponseCmndIdxChar(D_JSON_SAVED); + ResponseCmndIdxChar(PSTR(D_JSON_SAVED)); } else if (5 == XdrvMailbox.payload) { // Show default or learned RF data uint8_t key = XdrvMailbox.index; uint8_t index = (0 == Settings.rf_code[key][0]) ? 0 : key; // Use default if sync_time MSB = 0 @@ -513,10 +513,10 @@ void CmndRfKey(void) } else { if ((1 == XdrvMailbox.payload) || (0 == Settings.rf_code[XdrvMailbox.index][0])) { // Test sync_time MSB SonoffBridgeSend(0, XdrvMailbox.index); // Send default RF data - ResponseCmndIdxChar(D_JSON_DEFAULT_SENT); + ResponseCmndIdxChar(PSTR(D_JSON_DEFAULT_SENT)); } else { SonoffBridgeSend(XdrvMailbox.index, 0); // Send learned RF data - ResponseCmndIdxChar(D_JSON_LEARNED_SENT); + ResponseCmndIdxChar(PSTR(D_JSON_LEARNED_SENT)); } } } else { @@ -565,6 +565,7 @@ bool Xdrv06(uint8_t function) { bool result = false; +#ifdef ESP8266 if (SONOFF_BRIDGE == my_module_type) { switch (function) { case FUNC_SERIAL: @@ -582,6 +583,7 @@ bool Xdrv06(uint8_t function) break; } } +#endif // ESP8266 return result; } diff --git a/tasmota/xdrv_07_domoticz.ino b/tasmota/xdrv_07_domoticz.ino index 1fddf5890..556997bb9 100644 --- a/tasmota/xdrv_07_domoticz.ino +++ b/tasmota/xdrv_07_domoticz.ino @@ -110,7 +110,8 @@ void DomoticzUpdateFanState(void) void MqttPublishDomoticzPowerState(uint8_t device) { if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT - if ((device < 1) || (device > devices_present)) { device = 1; } + if (device < 1) { device = 1; } + if ((device > devices_present) || (device > MAX_DOMOTICZ_IDX)) { return; } if (Settings.domoticz_relay_idx[device -1]) { #ifdef USE_SHUTTER if (domoticz_is_shutter) { @@ -559,7 +560,7 @@ void HandleDomoticzConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_DOMOTICZ); - if (WebServer->hasArg("save")) { + if (Webserver->hasArg("save")) { DomoticzSaveSettings(); WebRestart(1); return; @@ -576,7 +577,7 @@ void HandleDomoticzConfiguration(void) i +1, i, Settings.domoticz_relay_idx[i], i +1, i, Settings.domoticz_key_idx[i]); } - if (pin[GPIO_SWT1 +i] < 99) { + if (PinUsed(GPIO_SWT1, i)) { WSContentSend_P(HTTP_FORM_DOMOTICZ_SWITCH, i +1, i, Settings.domoticz_switch_idx[i]); } @@ -651,7 +652,7 @@ bool Xdrv07(uint8_t function) WSContentSend_P(HTTP_BTN_MENU_DOMOTICZ); break; case FUNC_WEB_ADD_HANDLER: - WebServer->on("/" WEB_HANDLE_DOMOTICZ, HandleDomoticzConfiguration); + Webserver->on("/" WEB_HANDLE_DOMOTICZ, HandleDomoticzConfiguration); break; #endif // USE_WEBSERVER case FUNC_MQTT_SUBSCRIBE: diff --git a/tasmota/xdrv_08_serial_bridge.ino b/tasmota/xdrv_08_serial_bridge.ino index c4f530a21..a3ae5ae32 100644 --- a/tasmota/xdrv_08_serial_bridge.ino +++ b/tasmota/xdrv_08_serial_bridge.ino @@ -73,9 +73,9 @@ void SerialBridgeInput(void) char hex_char[(serial_bridge_in_byte_counter * 2) + 2]; bool assume_json = (!serial_bridge_raw && (serial_bridge_buffer[0] == '{')); Response_P(PSTR("{\"" D_JSON_SSERIALRECEIVED "\":%s%s%s}"), - (assume_json) ? "" : """", + (assume_json) ? "" : "\"", (serial_bridge_raw) ? ToHex_P((unsigned char*)serial_bridge_buffer, serial_bridge_in_byte_counter, hex_char, sizeof(hex_char)) : serial_bridge_buffer, - (assume_json) ? "" : """"); + (assume_json) ? "" : "\""); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); XdrvRulesProcess(); serial_bridge_in_byte_counter = 0; @@ -87,8 +87,8 @@ void SerialBridgeInput(void) void SerialBridgeInit(void) { serial_bridge_active = false; - if ((pin[GPIO_SBR_RX] < 99) && (pin[GPIO_SBR_TX] < 99)) { - SerialBridgeSerial = new TasmotaSerial(pin[GPIO_SBR_RX], pin[GPIO_SBR_TX]); + if (PinUsed(GPIO_SBR_RX) && PinUsed(GPIO_SBR_TX)) { + SerialBridgeSerial = new TasmotaSerial(Pin(GPIO_SBR_RX), Pin(GPIO_SBR_TX)); if (SerialBridgeSerial->begin(Settings.sbaudrate * 300)) { // Baud rate is stored div 300 so it fits into 16 bits if (SerialBridgeSerial->hardwareSerial()) { ClaimSerial(); diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/xdrv_09_timers.ino index 0804ce286..61d5bc737 100644 --- a/tasmota/xdrv_09_timers.ino +++ b/tasmota/xdrv_09_timers.ino @@ -68,61 +68,61 @@ const float pi2 = TWO_PI; const float pi = PI; const float RAD = DEG_TO_RAD; -float JulianischesDatum(void) -{ - // Gregorianischer Kalender - int Gregor; - int Jahr = RtcTime.year; - int Monat = RtcTime.month; - int Tag = RtcTime.day_of_month; +// Compute the Julian date from the Calendar date, using only unsigned ints for code compactness +// Warning this formula works only from 2000 to 2099, after 2100 we get 1 day off per century. If ever Tasmota survives until then. +uint32_t JulianDate(const struct TIME_T &now) { + // https://en.wikipedia.org/wiki/Julian_day - if (Monat <= 2) { - Monat += 12; - Jahr -= 1; + uint32_t Year = now.year; // Year ex:2020 + uint32_t Month = now.month; // 1..12 + uint32_t Day = now.day_of_month; // 1..31 + uint32_t Julian; // Julian day number + + if (Month <= 2) { + Month += 12; + Year -= 1; } - Gregor = (Jahr / 400) - (Jahr / 100) + (Jahr / 4); // Gregorianischer Kalender - return 2400000.5f + 365.0f*Jahr - 679004.0f + Gregor + (int)(30.6001f * (Monat +1)) + Tag + 0.5f; + // Warning, this formula works only for the 20th century, afterwards be are off by 1 day - which does not impact Sunrise much + // Julian = (1461 * Year + 6884472) / 4 + (153 * Month - 457) / 5 + Day -1 -13; + Julian = (1461 * Year + 6884416) / 4 + (153 * Month - 457) / 5 + Day; // -1 -13 included in 6884472 - 14*4 = 6884416 + return Julian; } +// Force value in the 0..pi2 range float InPi(float x) { - int n = (int)(x / pi2); - x = x - n*pi2; - if (x < 0) x += pi2; - return x; + return ModulusRangef(x, 0.0f, pi2); } -float eps(float T) -{ - // Neigung der Erdachse - return RAD * (23.43929111f + (-46.8150f*T - 0.00059f*T*T + 0.001813f*T*T*T)/3600.0f); -} +// Time formula +// Tdays is the number of days since Jan 1 2000, and replaces T as the Tropical Century. T = Tdays / 36525.0 +float TimeFormula(float *DK, uint32_t Tdays) { + float RA_Mean = 18.71506921f + (2400.0513369f / 36525.0f) * Tdays; // we keep only first order value as T is between 0.20 and 0.30 + float M = InPi( (pi2 * 0.993133f) + (pi2 * 99.997361f / 36525.0f) * Tdays); + float L = InPi( (pi2 * 0.7859453f) + M + (6893.0f * sinf(M) + 72.0f * sinf(M+M) + (6191.2f / 36525.0f) * Tdays) * (pi2 / 1296.0e3f)); -float BerechneZeitgleichung(float *DK,float T) -{ - float RA_Mittel = 18.71506921f + 2400.0513369f*T +(2.5862e-5f - 1.72e-9f*T)*T*T; - float M = InPi(pi2 * (0.993133f + 99.997361f*T)); - float L = InPi(pi2 * (0.7859453f + M/pi2 + (6893.0f*sinf(M)+72.0f*sinf(2.0f*M)+6191.2f*T) / 1296.0e3f)); - float e = eps(T); - float RA = atanf(tanf(L)*cosf(e)); - if (RA < 0.0) RA += pi; + float eps = 0.40904f; // we take this angle as constant over the next decade + float cos_eps = 0.91750f; // precompute cos(eps) + float sin_eps = 0.39773f; // precompute sin(eps) + + float RA = atanf(tanf(L) * cos_eps); + if (RA < 0.0f) RA += pi; if (L > pi) RA += pi; - RA = 24.0*RA/pi2; - *DK = asinf(sinf(e)*sinf(L)); - // Damit 0<=RA_Mittel<24 - RA_Mittel = 24.0f * InPi(pi2*RA_Mittel/24.0f)/pi2; - float dRA = RA_Mittel - RA; - if (dRA < -12.0f) dRA += 24.0f; - if (dRA > 12.0f) dRA -= 24.0f; + RA = RA * (24.0f/pi2); + *DK = asinf(sin_eps * sinf(L)); + RA_Mean = ModulusRangef(RA_Mean, 0.0f, 24.0f); + float dRA = ModulusRangef(RA_Mean - RA, -12.0f, 12.0f); dRA = dRA * 1.0027379f; return dRA; } void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down) { - float JD2000 = 2451545.0f; - float JD = JulianischesDatum(); - float T = (JD - JD2000) / 36525.0f; + const uint32_t JD2000 = 2451545; + uint32_t JD = JulianDate(RtcTime); + uint32_t Tdays = JD - JD2000; // number of days since Jan 1 2000 + + // ex 2458977 (2020 May 7) - 2451545 -> 7432 -> 0,2034 float DK; /* h (D) = -0.8333 normaler SA & SU-Gang @@ -130,56 +130,33 @@ void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8 h (D) = -12.0 nautische Dämmerung h (D) = -18.0 astronomische Dämmerung */ -// double h = -50/60.0*RAD; - float h = SUNRISE_DAWN_ANGLE *RAD; - float B = (((float)Settings.latitude)/1000000) * RAD; // geographische Breite + const float h = SUNRISE_DAWN_ANGLE * RAD; + const float sin_h = sinf(h); // let GCC pre-compute the sin() at compile time + + float B = Settings.latitude / (1000000.0f / RAD); // geographische Breite + //float B = (((float)Settings.latitude)/1000000) * RAD; // geographische Breite float GeographischeLaenge = ((float)Settings.longitude)/1000000; // double Zeitzone = 0; //Weltzeit // double Zeitzone = 1; //Winterzeit // double Zeitzone = 2.0; //Sommerzeit float Zeitzone = ((float)Rtc.time_timezone) / 60; - float Zeitgleichung = BerechneZeitgleichung(&DK, T); - float Zeitdifferenz = 12.0f*acosf((sinf(h) - sinf(B)*sinf(DK)) / (cosf(B)*cosf(DK)))/pi; + float Zeitgleichung = TimeFormula(&DK, Tdays); + float Zeitdifferenz = acosf((sin_h - sinf(B)*sinf(DK)) / (cosf(B)*cosf(DK))) * (12.0f / pi); float AufgangOrtszeit = 12.0f - Zeitdifferenz - Zeitgleichung; float UntergangOrtszeit = 12.0f + Zeitdifferenz - Zeitgleichung; float AufgangWeltzeit = AufgangOrtszeit - GeographischeLaenge / 15.0f; float UntergangWeltzeit = UntergangOrtszeit - GeographischeLaenge / 15.0f; - float Aufgang = AufgangWeltzeit + Zeitzone; // In Stunden - if (Aufgang < 0.0f) { - Aufgang += 24.0f; - } else { - if (Aufgang >= 24.0f) Aufgang -= 24.0f; - } - float Untergang = UntergangWeltzeit + Zeitzone; - if (Untergang < 0.0f) { - Untergang += 24.0f; - } else { - if (Untergang >= 24.0f) Untergang -= 24.0f; - } - int AufgangMinuten = (int)(60.0f*(Aufgang - (int)Aufgang)+0.5f); + float Aufgang = AufgangWeltzeit + Zeitzone + (1/120.0f); // In Stunden, with rounding to nearest minute (1/60 * .5) + + Aufgang = ModulusRangef(Aufgang, 0.0f, 24.0f); // force 0 <= x < 24.0 int AufgangStunden = (int)Aufgang; - if (AufgangMinuten >= 60.0f) { - AufgangMinuten -= 60.0f; - AufgangStunden++; - } else { - if (AufgangMinuten < 0.0f) { - AufgangMinuten += 60.0f; - AufgangStunden--; - if (AufgangStunden < 0.0f) AufgangStunden += 24.0f; - } - } - int UntergangMinuten = (int)(60.0f*(Untergang - (int)Untergang)+0.5f); + int AufgangMinuten = (int)(60.0f * fmodf(Aufgang, 1.0f)); + float Untergang = UntergangWeltzeit + Zeitzone; + + Untergang = ModulusRangef(Untergang, 0.0f, 24.0f); int UntergangStunden = (int)Untergang; - if (UntergangMinuten >= 60.0f) { - UntergangMinuten -= 60.0f; - UntergangStunden++; - } else { - if (UntergangMinuten<0) { - UntergangMinuten += 60.0f; - UntergangStunden--; - if (UntergangStunden < 0.0f) UntergangStunden += 24.0f; - } - } + int UntergangMinuten = (int)(60.0f * fmodf(Untergang, 1.0f)); + *hour_up = AufgangStunden; *minute_up = AufgangMinuten; *hour_down = UntergangStunden; @@ -648,7 +625,11 @@ const char HTTP_TIMER_SCRIPT6[] PROGMEM = "o=qs('#mw');for(i=0;i<=15;i++){ce((i<10)?('0'+i):i,o);}" // Create window minutes select options "o=qs('#d1');for(i=0;i<%d;i++){ce(i+1,o);}" // Create outputs "var a='" D_DAY3LIST "';" - "s='';for(i=0;i<7;i++){s+=\"\"+a.substring(i*3,(i*3)+3)+\" \"}" + +// "s='';for(i=0;i<7;i++){s+=\"\"+a.substring(i*3,(i*3)+3)+\" \"}" +// "s='';for(i=0;i<7;i++){s+=\" \"}" + "s='';for(i=0;i<7;i++){s+=\" \"}" + "eb('ds').innerHTML=s;" // Create weekdays "eb('dP').click();" // Get the element with id='dP' and click on it "}" @@ -659,22 +640,22 @@ const char HTTP_FORM_TIMER1[] PROGMEM = "
" " " D_TIMER_PARAMETERS " " "" - "
" D_TIMER_ENABLE "


" + "



" "



" "

" "
" - "" D_TIMER_ARM " " - "" D_TIMER_REPEAT "" + " " + "" "

" "
"; #ifdef USE_SUNRISE const char HTTP_FORM_TIMER3[] PROGMEM = "
" - "" D_TIMER_TIME "
" - "" D_SUNRISE " (%s)
" - "" D_SUNSET " (%s)
" + "
" + "
" + "
" "
" "

" "" @@ -698,7 +679,7 @@ void HandleTimerConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER); - if (WebServer->hasArg("save")) { + if (Webserver->hasArg("save")) { TimerSaveSettings(); HandleConfiguration(); return; @@ -736,7 +717,7 @@ void TimerSaveSettings(void) char message[LOGSZ]; Timer timer; - Settings.flag3.timers_enable = WebServer->hasArg("e0"); // CMND_TIMERS + Settings.flag3.timers_enable = Webserver->hasArg("e0"); // CMND_TIMERS WebGetArg("t0", tmp, sizeof(tmp)); char *p = tmp; snprintf_P(message, sizeof(message), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable); // CMND_TIMERS @@ -777,7 +758,7 @@ bool Xdrv09(uint8_t function) #endif // USE_RULES break; case FUNC_WEB_ADD_HANDLER: - WebServer->on("/" WEB_HANDLE_TIMER, HandleTimerConfiguration); + Webserver->on("/" WEB_HANDLE_TIMER, HandleTimerConfiguration); break; #endif // USE_TIMERS_WEB #endif // USE_WEBSERVER diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index 2509d6f6f..922014e49 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -47,6 +47,7 @@ * on button1#state do publish cmnd/ring2/power %value% endon on button2#state do publish cmnd/strip1/power %value% endon * on switch1#state do power2 %value% endon * on analog#a0div10 do publish cmnd/ring2/dimmer %value% endon + * on loadavg<50 do power 2 endon * * Notes: * Spaces after , around and before are mandatory @@ -66,6 +67,8 @@ #define XDRV_10 10 +#include + #define D_CMND_RULE "Rule" #define D_CMND_RULETIMER "RuleTimer" #define D_CMND_EVENT "Event" @@ -178,6 +181,230 @@ char rules_vars[MAX_RULE_VARS][33] = {{ 0 }}; #error MAX_RULE_MEMS is bigger than 16 #endif + +/*******************************************************************************************/ +/* + * Add Unishox compression to Rules + * + * New compression for Rules, depends on SetOption93 + * + * To avoid memory corruption when downgrading, the format is as follows: + * - If `SetOption93 0` + * Rule[x][] = 511 char max NULL terminated string (512 with trailing NULL) + * Rule[x][0] = 0 if the Rule is empty + * New: in case the string is empty we also enforce: + * Rule[x][1] = 0 (i.e. we have two conseutive NULLs) + * + * - If `SetOption93 1` + * If the rule is smaller than 511, it is stored uncompressed. Rule[x][0] is not null. + * If the rule is empty, Rule[x][0] = 0 and Rule[x][1] = 0; + * If the rule is bigger than 511, it is stored compressed + * The first byte of each Rule is always NULL. + * Rule[x][0] = 0, if firmware is downgraded, the rule will be considered as empty + * + * The second byte contains the size of uncompressed rule in 8-bytes blocks (i.e. (len+7)/8 ) + * Maximum rule size is 2KB (2048 bytes per rule), although there is little chances compression ratio will go down to 75% + * Rule[x][1] = size uncompressed in dwords. If zero, the rule is empty. + * + * The remaining bytes contain the compressed rule, NULL terminated + */ +/*******************************************************************************************/ + +#ifdef USE_RULES_COMPRESSION +// Statically allocate one String per rule +String k_rules[MAX_RULE_SETS] = { String(), String(), String() }; // Strings are created empty +#endif // USE_RULES_COMPRESSION + +// Returns whether the rule is uncompressed, which means the first byte is not NULL +inline bool IsRuleUncompressed(uint32_t idx) { +#ifdef USE_RULES_COMPRESSION + return Settings.rules[idx][0] ? true : false; // first byte not NULL, the rule is not empty and not compressed +#else + return true; +#endif +} + +// Returns whether the rule is empty, which requires two consecutive NULL +inline bool IsRuleEmpty(uint32_t idx) { +#ifdef USE_RULES_COMPRESSION + return (Settings.rules[idx][0] == 0) && (Settings.rules[idx][1] == 0) ? true : false; +#else + return (Settings.rules[idx][0] == 0) ? true : false; +#endif +} + +// Returns the approximate (+3-0) length of the rule, not counting the trailing NULL +size_t GetRuleLen(uint32_t idx) { + // no need to use #ifdef USE_RULES_COMPRESSION, the compiler will optimize since first test is always true + if (IsRuleUncompressed(idx)) { + return strlen(Settings.rules[idx]); + } else { // either empty or compressed + return Settings.rules[idx][1] * 8; // cheap calculation, but not byte accurate (may overshoot by 7) + } +} + +// Returns the actual Flash storage for the Rule, including trailing NULL +size_t GetRuleLenStorage(uint32_t idx) { +#ifdef USE_RULES_COMPRESSION + if (Settings.rules[idx][0] || !Settings.rules[idx][1]) { // if first byte is non-NULL it is uncompressed, if second byte is NULL, then it's either uncompressed or empty + return 1 + strlen(Settings.rules[idx]); // uncompressed or empty + } else { + return 2 + strlen(&Settings.rules[idx][1]); // skip first byte and get len of the compressed rule + } +#else + return 1 + strlen(Settings.rules[idx]); +#endif +} + +// internal function, do the actual decompression +void GetRule_decompress(String &rule, const char *rule_head) { + size_t buf_len = 1 + *rule_head * 8; // the first byte contains size of buffer for uncompressed rule / 8, buf_len may overshoot by 7 + rule_head++; // advance to the actual compressed buffer + + // We use a nasty trick here. To avoid allocating twice the buffer, + // we first extend the buffer of the String object to the target size (maybe overshooting by 7 bytes) + // then we decompress in this buffer, + // and finally assign the raw string to the String, which happens to work: String uses memmove(), so overlapping works + rule.reserve(buf_len); + char* buf = rule.begin(); + + int32_t len_decompressed = unishox_decompress(rule_head, strlen(rule_head), buf, buf_len); + buf[len_decompressed] = 0; // add NULL terminator + + // AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Rawdecompressed: %d"), len_decompressed); + rule = buf; // assign the raw string to the String object (in reality re-writing the same data in the same place) +} + +// +// Read rule in memory, uncompress if needed +// +// Returns: String() object containing a copy of the rule (rule processing is destructive and will change the String) +String GetRule(uint32_t idx) { + if (IsRuleUncompressed(idx)) { + return String(Settings.rules[idx]); + } else { +#ifdef USE_RULES_COMPRESSION // we still do #ifdef to make sure we don't link unnecessary code + + String rule(""); + if (Settings.rules[idx][1] == 0) { return rule; } // the rule is empty + + // If the cache is empty, we need to decompress from Settings + if (0 == k_rules[idx].length() ) { + GetRule_decompress(rule, &Settings.rules[idx][1]); + if (!Settings.flag4.compress_rules_cpu) { + k_rules[idx] = rule; // keep a copy for next time + } + } else { + // we have a valid copy + rule = k_rules[idx]; + } + return rule; +#endif + } +} + +#ifdef USE_RULES_COMPRESSION +// internal function, comrpess rule and store a cached version uncompressed (except if SetOption94 1) +// If out == nullptr, we are in dry-run mode, so don't keep rule in cache +int32_t SetRule_compress(uint32_t idx, const char *in, size_t in_len, char *out, size_t out_len) { + int32_t len_compressed; + len_compressed = unishox_compress(in, in_len, out, out_len); + + if (len_compressed >= 0) { // negative means compression failed because of buffer too small, we leave the rule untouched + // check if we need to store in cache + k_rules[idx] = (const char*) nullptr; // Assign the String to nullptr, clears previous string and disallocate internal buffers of String object + if ((!Settings.flag4.compress_rules_cpu) && out) { // if out == nullptr, don't store cache + // keep copy in cache + k_rules[idx] = in; + } + } + return len_compressed; +} +#endif // USE_RULES_COMPRESSION + +// Returns: +// >= 0 : the actual stored size +// <0 : not enough space +int32_t SetRule(uint32_t idx, const char *content, bool append = false) { + if (nullptr == content) { content = ""; } // if nullptr, use empty string + size_t len_in = strlen(content); + bool needsCompress = false; + size_t offset = 0; + + if (len_in >= MAX_RULE_SIZE) { // if input is more than 512, it will not fit uncompressed + needsCompress = true; + } + if (append) { + if (IsRuleUncompressed(idx) || IsRuleEmpty(idx)) { // if already uncompressed (so below 512) and append mode, check if it still fits uncompressed + offset = strlen(Settings.rules[idx]); + if (len_in + offset >= MAX_RULE_SIZE) { + needsCompress = true; + } + } else { + needsCompress = true; // we append to a non-empty compressed rule, so it won't fit uncompressed + } + } + + if (!needsCompress) { // the rule fits uncompressed, so just copy it + strlcpy(Settings.rules[idx] + offset, content, sizeof(Settings.rules[idx])); + if (0 == Settings.rules[idx][0]) { + Settings.rules[idx][1] = 0; + } + +#ifdef USE_RULES_COMPRESSION + if (0 != len_in + offset) { + // do a dry-run compression to display how much it would be compressed + int32_t len_compressed, len_uncompressed; + + len_uncompressed = strlen(Settings.rules[idx]); + len_compressed = unishox_compress(Settings.rules[idx], len_uncompressed, nullptr /* dry-run */, MAX_RULE_SIZE + 8); + AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Stored uncompressed, would compress from %d to %d (-%d%%)"), len_uncompressed, len_compressed, 100 - changeUIntScale(len_compressed, 0, len_uncompressed, 0, 100)); + } + +#endif // USE_RULES_COMPRESSION + + return len_in + offset; + } else { +#ifdef USE_RULES_COMPRESSION + int32_t len_compressed; + // allocate temp buffer so we don't nuke the rule if it's too big to fit + char *buf_out = (char*) malloc(MAX_RULE_SIZE + 8); // take some margin + if (!buf_out) { return -1; } // fail if couldn't allocate + + // compress + if (append) { + String content_append = GetRule(idx); // get original Rule and decompress it if needed + content_append += content; // concat new content + len_in = content_append.length(); // adjust length + len_compressed = SetRule_compress(idx, content_append.c_str(), len_in, buf_out, MAX_RULE_SIZE + 8); + } else { + len_compressed = SetRule_compress(idx, content, len_in, buf_out, MAX_RULE_SIZE + 8); + } + + if ((len_compressed >= 0) && (len_compressed < MAX_RULE_SIZE - 2)) { + // size is ok, copy to Settings + Settings.rules[idx][0] = 0; // clear first byte to mark as compressed + Settings.rules[idx][1] = (len_in + 7) / 8; // store original length in first bytes (4 bytes chuks) + memcpy(&Settings.rules[idx][2], buf_out, len_compressed); + Settings.rules[idx][len_compressed + 2] = 0; // add NULL termination + AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Compressed from %d to %d (-%d%%)"), len_in, len_compressed, 100 - changeUIntScale(len_compressed, 0, len_in, 0, 100)); + // AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: First bytes: %02X%02X%02X%02X"), Settings.rules[idx][0], Settings.rules[idx][1], Settings.rules[idx][2], Settings.rules[idx][3]); + // AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: GetRuleLenStorage = %d"), GetRuleLenStorage(idx)); + } else { + len_compressed = -1; // failed + // clear rule cache, so it will be reloaded from Settings + k_rules[idx] = (const char *) nullptr; + } + free(buf_out); + return len_compressed; + +#else // USE_RULES_COMPRESSION + return -1; // the rule does not fit and we can't compress +#endif // USE_RULES_COMPRESSION + } + +} + /*******************************************************************************************/ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) @@ -190,19 +417,20 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) char stemp[10]; // Step1: Analyse rule - int pos = rule.indexOf('#'); - if (pos == -1) { return false; } // No # sign in rule - - String rule_task = rule.substring(0, pos); // "INA219" or "SYSTEM" + String rule_expr = rule; // "TELE-INA219#CURRENT>0.100" if (Rules.teleperiod) { - int ppos = rule_task.indexOf("TELE-"); // "TELE-INA219" or "INA219" + int ppos = rule_expr.indexOf("TELE-"); // "TELE-INA219#CURRENT>0.100" or "INA219#CURRENT>0.100" if (ppos == -1) { return false; } // No pre-amble in rule - rule_task = rule.substring(5, pos); // "INA219" or "SYSTEM" + rule_expr = rule.substring(5); // "INA219#CURRENT>0.100" or "SYSTEM#BOOT" } - String rule_expr = rule.substring(pos +1); // "CURRENT>0.100" or "BOOT" or "%var1%" or "MINUTE|5" String rule_name, rule_param; - int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); //Parse the compare expression.Return operator and the left, right part of expression + int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); // Parse the compare expression.Return operator and the left, right part of expression + + // rule_name = "INA219#CURRENT" + // rule_param = "0.100" or "%VAR1%" + +//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: expr %s, name %s, param %s"), rule_expr.c_str(), rule_name.c_str(), rule_param.c_str()); char rule_svalue[80] = { 0 }; float rule_value = 0; @@ -250,11 +478,12 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) if (temp_value > -1) { rule_value = temp_value; } else { - rule_value = CharToFloat((char*)rule_svalue); // 0.1 - This saves 9k code over toFLoat()! + rule_value = CharToFloat((char*)rule_svalue); // 0.1 - This saves 9k code over toFLoat()! } } - // Step2: Search rule_task and rule_name + // Step2: Search rule_name + int pos; int rule_name_idx = 0; if ((pos = rule_name.indexOf("[")) > 0) { // "SUBTYPE1#CURRENT[1]" rule_name_idx = rule_name.substring(pos +1).toInt(); @@ -267,10 +496,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) StaticJsonBuffer<1024> jsonBuf; JsonObject &root = jsonBuf.parseObject(event); if (!root.success()) { return false; } // No valid JSON data - if (!root[rule_task].success()) { return false; } // No rule_task in JSON data - - JsonObject &obj1 = root[rule_task]; - JsonObject *obj = &obj1; + JsonObject *obj = &root; String subtype; uint32_t i = 0; while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT" @@ -289,8 +515,8 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) str_value = (*obj)[rule_name]; // "CURRENT" } -//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"), -// rule_task.c_str(), rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none"); +//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"), +// rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none"); Rules.event_value = str_value; // Prepare %value% @@ -419,7 +645,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event = %s, Rule = %s"), event_saved.c_str(), Settings.rules[rule_set]); - String rules = Settings.rules[rule_set]; + String rules = GetRule(rule_set); Rules.trigger_count[rule_set] = 0; int plen = 0; @@ -476,6 +702,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) RulesVarReplace(commands, stemp, SettingsText(SET_MEM1 +i)); } RulesVarReplace(commands, F("%TIME%"), String(MinutesPastMidnight())); + RulesVarReplace(commands, F("%UTCTIME%"), String(UtcTime())); RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime())); RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL)); RulesVarReplace(commands, F("%TOPIC%"), SettingsText(SET_MQTT_TOPIC)); @@ -530,7 +757,7 @@ bool RulesProcessEvent(char *json_event) //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event %s"), event_saved.c_str()); for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { - if (strlen(Settings.rules[i]) && bitRead(Settings.rule_enabled, i)) { + if (GetRuleLen(i) && bitRead(Settings.rule_enabled, i)) { if (RuleSetProcess(i, event_saved)) { serviced = true; } } } @@ -546,7 +773,7 @@ void RulesInit(void) { rules_flag.data = 0; for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { - if (Settings.rules[i][0] == '\0') { + if (0 == GetRuleLen(i)) { bitWrite(Settings.rule_enabled, i, 0); bitWrite(Settings.rule_once, i, 0); } @@ -579,9 +806,9 @@ void RulesEvery50ms(void) // Boot time SWITCHES Status for (uint32_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 - if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { + if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB))) { #else - if (pin[GPIO_SWT1 +i] < 99) { + if (PinUsed(GPIO_SWT1, i)) { #endif // USE_TM1638 snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (SwitchState(i))); RulesProcessEvent(json_event); @@ -1726,7 +1953,8 @@ void CmndRule(void) { uint8_t index = XdrvMailbox.index; if ((index > 0) && (index <= MAX_RULE_SETS)) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules[index -1]))) { + // if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules[index -1]))) { // TODO postpone size calculation + if (XdrvMailbox.data_len > 0) { // TODO postpone size calculation if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { switch (XdrvMailbox.payload) { case 0: // Off @@ -1752,24 +1980,33 @@ void CmndRule(void) break; } } else { - int offset = 0; + bool append = false; if ('+' == XdrvMailbox.data[0]) { - offset = strlen(Settings.rules[index -1]); - if (XdrvMailbox.data_len < (sizeof(Settings.rules[index -1]) - offset -1)) { // Check free space - XdrvMailbox.data[0] = ' '; // Remove + and make sure at least one space is inserted - } else { - offset = -1; // Not enough space so skip it - } + XdrvMailbox.data[0] = ' '; // Remove + and make sure at least one space is inserted + append = true; } - if (offset != -1) { - strlcpy(Settings.rules[index -1] + offset, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.rules[index -1])); + int32_t res = SetRule(index - 1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, append); + if (res < 0) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("RUL: Not enough space")); } } Rules.triggers[index -1] = 0; // Reset once flag } - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"), + String rule = GetRule(index - 1); + size_t rule_len = rule.length(); + if (rule_len >= MAX_RULE_SIZE) { + // we need to split the rule in chunks + rule = rule.substring(0, MAX_RULE_SIZE); + rule += F("..."); + } + // snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"), + // XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), + // GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]); + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Length\":%d,\"Free\":%d,\"Rules\":\"%s\"}"), XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), - GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]); + GetStateText(bitRead(Settings.rule_stop, index -1)), + rule_len, MAX_RULE_SIZE - GetRuleLenStorage(index - 1), + escapeJSONString(rule.c_str()).c_str()); } } @@ -1796,8 +2033,11 @@ void CmndEvent(void) { if (XdrvMailbox.data_len > 0) { strlcpy(Rules.event_data, XdrvMailbox.data, sizeof(Rules.event_data)); +#ifdef USE_DEVICE_GROUPS + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_EVENT, XdrvMailbox.data); +#endif // USE_DEVICE_GROUPS } - ResponseCmndDone(); + if (XdrvMailbox.command) ResponseCmndDone(); } void CmndVariable(void) diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 30ed26598..44207609d 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -17,6 +17,7 @@ along with this program. If not, see . */ + #ifdef USE_SCRIPT #ifndef USE_RULES /*********************************************************************************************\ @@ -60,6 +61,42 @@ keywords if then else endif, or, and are better readable for beginners (others m #define SCRIPT_MAXPERM (PMEM_SIZE)-4/sizeof(float) #define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS + + +uint32_t EncodeLightId(uint8_t relay_id); +uint32_t DecodeLightId(uint32_t hue_id); + +#if defined(ESP32) && defined(ESP32_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS) + +#include "FS.h" +#include "SPIFFS.h" + + +void SaveFile(const char *name,const uint8_t *buf,uint32_t len) { + File file = SPIFFS.open(name, FILE_WRITE); + if (!file) return; + file.write(buf, len); + file.close(); +} + +#define FORMAT_SPIFFS_IF_FAILED true +uint8_t spiffs_mounted=0; + +void LoadFile(const char *name,uint8_t *buf,uint32_t len) { + if (!spiffs_mounted) { + if(!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)){ + //Serial.println("SPIFFS Mount Failed"); + return; + } + spiffs_mounted=1; + } + File file = SPIFFS.open(name); + if (!file) return; + file.read(buf, len); + file.close(); +} +#endif + // offsets epoch readings by 1.1.2019 00:00:00 to fit into float with second resolution #define EPOCH_OFFSET 1546300800 @@ -68,14 +105,36 @@ enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD}; #ifdef USE_SCRIPT_FATFS #include + +//#define USE_MMC + +#ifdef USE_MMC +#include +#undef FS_USED +#define FS_USED SD_MMC +#else #include +#undef FS_USED +#define FS_USED SD +#endif + +#ifndef ESP32 +#undef FILE_WRITE +#define FILE_WRITE (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT) +#define FILE_APPEND (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT | sdfat::O_APPEND) +#endif + #ifndef FAT_SCRIPT_SIZE #define FAT_SCRIPT_SIZE 4096 #endif +#ifdef ESP32 +#undef FAT_SCRIPT_NAME +#define FAT_SCRIPT_NAME "/script.txt" +#else +#undef FAT_SCRIPT_NAME #define FAT_SCRIPT_NAME "script.txt" -#if USE_LONG_FILE_NAMES==1 -#warning ("FATFS long filenames not supported"); #endif + #if USE_STANDARD_SPI_LIBRARY==0 #warning ("FATFS standard spi should be used"); #endif @@ -232,7 +291,6 @@ void RulesTeleperiod(void) { #define EEP_SCRIPT_SIZE 4095 #endif - static Eeprom24C128_256 eeprom(EEPROM_ADDRESS); // eeprom.writeBytes(address, length, buffer); #define EEP_WRITE(A,B,C) eeprom.writeBytes(A,B,(uint8_t*)C); @@ -554,7 +612,11 @@ char *script; #ifdef USE_SCRIPT_FATFS if (!glob_script_mem.script_sd_found) { - if (SD.begin(USE_SCRIPT_FATFS)) { +#ifdef USE_MMC + if (FS_USED.begin()) { +#else + if (FS_USED.begin(USE_SCRIPT_FATFS)) { +#endif glob_script_mem.script_sd_found=1; } else { glob_script_mem.script_sd_found=0; @@ -1119,6 +1181,28 @@ chknext: } } } +#ifdef ESP32 + if (!strncmp(vname,"core",4)) { + fvar=xPortGetCoreID(); + goto exit; + } +#ifdef USE_SCRIPT_TASK + if (!strncmp(vname,"ct(",3)) { + lp+=3; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + while (*lp==' ') lp++; + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + while (*lp==' ') lp++; + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp++; + fvar=scripter_create_task(fvar,fvar1,fvar2); + len=0; + goto exit; + } +#endif +#endif break; case 'd': if (!strncmp(vname,"day",3)) { @@ -1132,20 +1216,36 @@ chknext: goto exit; } break; -#ifdef USE_SCRIPT_FATFS case 'f': +#ifdef USE_SCRIPT_FATFS if (!strncmp(vname,"fo(",3)) { lp+=3; char str[SCRIPT_MAXSSIZE]; lp=GetStringResult(lp,OPER_EQU,str,0); while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t mode=fvar; + uint8_t mode=0; + if ((*lp=='r') || (*lp=='w') || (*lp=='a')) { + switch (*lp) { + case 'r': + mode=0; + break; + case 'w': + mode=1; + break; + case 'a': + mode=2; + break; + } + lp++; + } else { + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + mode=fvar; + } fvar=-1; for (uint8_t cnt=0;cnt=SFS_MAX) ind=SFS_MAX-1; + if (glob_script_mem.file_flags[ind].is_open) { + uint8_t *buff; + float maxps=WcGetPicstore(-1,0); + if (fvar<1 || fvar>maxps) fvar=1; + uint32_t len=WcGetPicstore(fvar-1, &buff); + if (len) { + //glob_script_mem.files[ind].seek(0,SeekEnd); + fvar=glob_script_mem.files[ind].write(buff,len); + } else { + fvar=0; + } + //AddLog_P2(LOG_LEVEL_INFO, PSTR("picture save: %d"), len); + } else { + fvar=0; + } + lp++; + len=0; + goto exit; + } +#endif #ifdef USE_SCRIPT_FATFS_EXT if (!strncmp(vname,"fe(",3)) { lp+=3; char str[glob_script_mem.max_ssize+1]; lp=GetStringResult(lp,OPER_EQU,str,0); // execute script - File ef=SD.open(str); + File ef=FS_USED.open(str); if (ef) { uint16_t fsiz=ef.size(); if (fsiz<2048) { @@ -1312,7 +1453,7 @@ chknext: lp+=4; char str[glob_script_mem.max_ssize+1]; lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SD.mkdir(str); + fvar=FS_USED.mkdir(str); lp++; len=0; goto exit; @@ -1321,7 +1462,7 @@ chknext: lp+=4; char str[glob_script_mem.max_ssize+1]; lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SD.rmdir(str); + fvar=FS_USED.rmdir(str); lp++; len=0; goto exit; @@ -1330,13 +1471,13 @@ chknext: lp+=3; char str[glob_script_mem.max_ssize+1]; lp=GetStringResult(lp,OPER_EQU,str,0); - if (SD.exists(str)) fvar=1; + if (FS_USED.exists(str)) fvar=1; else fvar=0; lp++; len=0; goto exit; } -#endif +#endif // USE_SCRIPT_FATFS_EXT if (!strncmp(vname,"fl1(",4) || !strncmp(vname,"fl2(",4) ) { uint8_t lknum=*(lp+2)&3; lp+=4; @@ -1354,9 +1495,16 @@ chknext: //card_init(); goto exit; } - break; - #endif //USE_SCRIPT_FATFS + if (!strncmp(vname,"freq",4)) { +#ifdef ESP32 + fvar=getCpuFrequencyMhz(); +#else + fvar=ESP.getCpuFreqMHz(); +#endif + goto exit; + } + break; case 'g': if (!strncmp(vname,"gtmp",4)) { fvar=global_temperature; @@ -1381,7 +1529,7 @@ chknext: goto exit; } if (!strncmp(vname,"heap",4)) { - fvar=ESP.getFreeHeap(); + fvar=ESP_getFreeHeap(); goto exit; } if (!strncmp(vname,"hn(",3)) { @@ -1438,6 +1586,10 @@ chknext: } break; case 'l': + if (!strncmp(vname,"lip",3)) { + if (sp) strlcpy(sp,(const char*)WiFi.localIP().toString().c_str(),glob_script_mem.max_ssize); + goto strexit; + } if (!strncmp(vname,"loglvl",6)) { fvar=glob_script_mem.script_loglevel; tind->index=SCRIPT_LOGLEVEL; @@ -1508,9 +1660,9 @@ chknext: lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); SCRIPT_SKIP_SPACES fvar=fvar1; - if ((*opp=='<' && fvar1' && fvar1>fvar2) || - (*opp=='=' && fvar1==fvar2)) + if ((*opp=='<' && fvar1' && fvar1>fvar2) || + (*opp=='=' && fvar1==fvar2)) { if (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) { float fvar3; @@ -1539,7 +1691,15 @@ chknext: } if (!strncmp(vname,"pn[",3)) { GetNumericResult(vname+3,OPER_EQU,&fvar,0); - fvar=pin[(uint8_t)fvar]; + fvar=Pin(fvar); + // skip ] bracket + len++; + goto exit; + } + if (!strncmp(vname,"pn[",3)) { + GetNumericResult(vname+3,OPER_EQU,&fvar,0); +// fvar=pin_gpio[(uint8_t)fvar]; + fvar=Pin(fvar); // skip ] bracket len++; goto exit; @@ -1547,17 +1707,32 @@ chknext: if (!strncmp(vname,"pd[",3)) { GetNumericResult(vname+3,OPER_EQU,&fvar,0); uint8_t gpiopin=fvar; +/* for (uint8_t i=0;i 0)) { + fvar = gpio_pin[gpiopin]; + // skip ] bracket + len++; + goto exit; + } fvar=999; goto exit; } +#ifdef ESP32 + if (!strncmp(vname,"pheap",5)) { + fvar=ESP.getFreePsram(); + goto exit; + } +#endif if (!strncmp(vname,"prefix1",7)) { if (sp) strlcpy(sp,SettingsText(SET_MQTTPREFIX1),glob_script_mem.max_ssize); goto strexit; @@ -1704,6 +1879,19 @@ chknext: len=0; goto strexit; } +#ifdef ESP32 + if (!strncmp(vname,"sf(",3)) { + lp+=2; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + if (fvar<80) fvar=80; + if (fvar>240) fvar=240; + setCpuFrequencyMhz(fvar); + fvar=getCpuFrequencyMhz(); + lp++; + len=0; + goto exit; + } +#endif #if defined(USE_TIMERS) && defined(USE_SUNRISE) if (!strncmp(vname,"sunrise",7)) { fvar=SunMinutes(0); @@ -1746,7 +1934,7 @@ chknext: goto exit; } #endif -#ifdef USE_SML_SCRIPT_CMD +#if defined(USE_SML_M) && defined (USE_SML_SCRIPT_CMD) if (!strncmp(vname,"sml(",4)) { lp+=4; float fvar1; @@ -1759,13 +1947,18 @@ chknext: float fvar3; lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); fvar=SML_SetBaud(fvar1,fvar3); - } else { + } else if (fvar2==1) { char str[SCRIPT_MAXSSIZE]; lp=GetStringResult(lp,OPER_EQU,str,0); fvar=SML_Write(fvar1,str); + } else { +#ifdef ED300L + fvar=SML_Status(fvar1); +#else + fvar=0; +#endif } lp++; - fvar=0; len=0; goto exit; } @@ -1853,6 +2046,67 @@ chknext: break; case 'w': +#if defined(ESP32) && defined(USE_WEBCAM) + if (!strncmp(vname,"wc(",3)) { + lp+=3; + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + switch ((uint32)fvar1) { + case 0: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=WcSetup(fvar2); + } + break; + case 1: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=WcGetFrame(fvar2); + } + break; + case 2: + { float fvar2,fvar3; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); + fvar=WcSetOptions(fvar2,fvar3); + } + break; + case 3: + fvar=WcGetWidth(); + break; + case 4: + fvar=WcGetHeight(); + break; + case 5: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=WcSetStreamserver(fvar2); + } + break; + case 6: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=WcSetMotionDetect(fvar2); + } + break; +#ifdef USE_FACE_DETECT + case 7: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=WcSetFaceDetect(fvar2); + } + break; +#endif + default: + fvar=0; + } + lp++; + len=0; + goto exit; + } +#endif //ESP32, USE_WEBCAM if (!strncmp(vname,"wday",4)) { fvar=RtcTime.day_of_week; goto exit; @@ -2023,6 +2277,7 @@ char *getop(char *lp, uint8_t *operand) { } +#ifdef ESP8266 #if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) // All version before core 2.4.2 // https://github.com/esp8266/Arduino/issues/2557 @@ -2045,6 +2300,12 @@ uint16_t GetStack(void) { return (4 * (sp - g_pcont->stack)); } #endif +#else +uint16_t GetStack(void) { + register uint8_t *sp asm("a1"); + return (sp - pxTaskGetStackStart(NULL)); +} +#endif char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) { uint8_t operand=0; @@ -2413,6 +2674,40 @@ exit10: return lp; } +#ifdef ESP32 + +TimerHandle_t beep_th; +void StopBeep( TimerHandle_t xTimer ); + +void StopBeep( TimerHandle_t xTimer ) { + ledcWriteTone(7,0); + xTimerStop(xTimer, 0); +} + +void esp32_beep(int32_t freq ,uint32_t len) { + if (freq<0) { + ledcSetup(7,500,10); + ledcAttachPin(-freq,7); + ledcWriteTone(7,0); + if (!beep_th) { + beep_th = xTimerCreate("beep",100,pdFALSE,( void * ) 0,StopBeep); + } + } else { + if (!beep_th) return; + if (!freq) { + ledcWriteTone(7,0); + xTimerStop(beep_th, 10); + return; + } + if (len < 10) return; + if (xTimerIsTimerActive(beep_th)) return; + ledcWriteTone(7,freq); + uint32_t ticks = pdMS_TO_TICKS(len); + xTimerChangePeriod( beep_th, ticks, 10); + } +} +#endif // ESP32 + //#define IFTHEN_DEBUG #define IF_NEST 8 @@ -2444,11 +2739,14 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { return -99; } + JsonObject *jo=0; DynamicJsonBuffer jsonBuffer; // on heap JsonObject &jobj=jsonBuffer.parseObject(js); - JsonObject *jo; - if (js) jo=&jobj; - else jo=0; + if (js) { + jo=&jobj; + } else { + jo=0; + } char *lp=glob_script_mem.scriptptr; @@ -2666,9 +2964,29 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { lp=GetNumericResult(lp,OPER_EQU,&fvar,0); int8_t pinnr=fvar; SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t mode=fvar; - pinMode(pinnr,mode&3); + uint8_t mode=0; + if ((*lp=='I') || (*lp=='O') || (*lp=='P')) { + switch (*lp) { + case 'I': + mode=0; + break; + case 'O': + mode=1; + break; + case 'P': + mode=2; + break; + } + lp++; + } else { + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + mode=fvar; + } + uint8_t pm=0; + if (mode==0) pm=INPUT; + if (mode==1) pm=OUTPUT; + if (mode==2) pm=INPUT_PULLUP; + pinMode(pinnr,pm); goto next_line; } else if (!strncmp(lp,"spin(",5)) { lp+=5; @@ -2707,6 +3025,18 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { goto next_line; } #endif +#endif +#ifdef ESP32 + else if (!strncmp(lp,"beep(",5)) { + lp+=5; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + SCRIPT_SKIP_SPACES + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + esp32_beep(fvar,fvar1); + lp++; + goto next_line; + } #endif else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { @@ -3101,7 +3431,7 @@ const char HTTP_FORM_SCRIPT[] PROGMEM = const char HTTP_FORM_SCRIPT1[] PROGMEM = "
" - "" D_SCRIPT_ENABLE "
" + "
" "