diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2839ba02b..d8bb487bd 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.0 + - [ ] 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/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..9e8312b51 --- /dev/null +++ b/.github/workflows/CI_github_ESP32.yml @@ -0,0 +1,499 @@ +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-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 caa3d240b..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,4 +25,4 @@ platformio_override.ini .vscode/c_cpp_properties.json .vscode/launch.json *.bak -*.code-workspace \ No newline at end of file +*.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/BUILDS.md b/BUILDS.md index 36a0bb48a..7d1e4fb6d 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 | - | @@ -136,6 +136,8 @@ | USE_GPS | - | - | - | - | - | - | - | | USE_HM10 | - | - | - | - | x | - | - | | USE_HRXL | - | - | - | - | x | - | - | +| USE_TASMOTA_SLAVE | - | - | - | - | - | - | - | +| USE_OPENTHERM | - | - | - | - | - | - | - | | | | | | | | | | | USE_NRF24 | - | - | - | - | - | - | - | | USE_MIBLE | - | - | - | - | - | - | - | @@ -149,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 | @@ -167,3 +169,5 @@ | USE_DISPLAY_ILI9488 | - | - | - | - | - | - | - | | USE_DISPLAY_SSD1351 | - | - | - | - | - | - | - | | USE_DISPLAY_RA8876 | - | - | - | - | - | - | - | +| | | | | | | | | +| USE_WEBCAM | - | - | - | - | - | - | - | ESP32 only diff --git a/README.md b/README.md index 23359fe22..148f00818 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ In addition to the [release webpage](https://github.com/arendst/Tasmota/releases [![Dev Version](https://img.shields.io/badge/development%20version-v8.2.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. @@ -144,7 +145,8 @@ 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 - Many more providing Tips, Wips, Pocs, PRs and Donations ## License diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2fcda28f5..d880bc05e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -52,13 +52,18 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 8.2.0.4 +### Version 8.2.0.6 +- 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 223 Hz instead of 880 Hz for less interrupt pressure +- 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.5 +- 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 @@ -70,9 +75,12 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - 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 ``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) @@ -90,3 +98,6 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - 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 experimental basic support for Tasmota on ESP32 based on work by Jörg Schüler-Maroldt diff --git a/lib/FrogmoreScd30/FrogmoreScd30.cpp b/lib/FrogmoreScd30/FrogmoreScd30.cpp index 142f33c39..e610bfb92 100644 --- a/lib/FrogmoreScd30/FrogmoreScd30.cpp +++ b/lib/FrogmoreScd30/FrogmoreScd30.cpp @@ -24,8 +24,6 @@ SOFTWARE. #include #include -#ifdef ESP8266 - #define COMMAND_SCD30_CONTINUOUS_MEASUREMENT 0x0010 #define COMMAND_SCD30_MEASUREMENT_INTERVAL 0x4600 #define COMMAND_SCD30_GET_DATA_READY 0x0202 @@ -60,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; } @@ -108,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 @@ -652,5 +656,3 @@ int FrogmoreScd30::stopMeasuring(void) { return (sendCommand(COMMAND_SCD30_STOP_MEASUREMENT)); } - -#endif // ESP8266 diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.7.5/examples/IRrecvDumpV2/platformio.ini deleted file mode 100644 index d3c660df9..000000000 --- a/lib/IRremoteESP8266-2.7.5/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.5/CPPLINT.cfg b/lib/IRremoteESP8266-2.7.6/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.7.5/CPPLINT.cfg rename to lib/IRremoteESP8266-2.7.6/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.7.5/LICENSE.txt b/lib/IRremoteESP8266-2.7.6/LICENSE.txt similarity index 100% rename from lib/IRremoteESP8266-2.7.5/LICENSE.txt rename to lib/IRremoteESP8266-2.7.6/LICENSE.txt diff --git a/lib/IRremoteESP8266-2.7.5/README.md b/lib/IRremoteESP8266-2.7.6/README.md similarity index 98% rename from lib/IRremoteESP8266-2.7.5/README.md rename to lib/IRremoteESP8266-2.7.6/README.md index 9ce40166f..83859d6b6 100644 --- a/lib/IRremoteESP8266-2.7.5/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.5 Now Available -Version 2.7.5 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.5/README_fr.md b/lib/IRremoteESP8266-2.7.6/README_fr.md similarity index 98% rename from lib/IRremoteESP8266-2.7.5/README_fr.md rename to lib/IRremoteESP8266-2.7.6/README_fr.md index 49fc3188c..6b6c6e7d0 100644 --- a/lib/IRremoteESP8266-2.7.5/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.5 disponible -Version 2.7.5 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.5/ReleaseNotes.md b/lib/IRremoteESP8266-2.7.6/ReleaseNotes.md similarity index 98% rename from lib/IRremoteESP8266-2.7.5/ReleaseNotes.md rename to lib/IRremoteESP8266-2.7.6/ReleaseNotes.md index 51df6df27..69cc753cd 100644 --- a/lib/IRremoteESP8266-2.7.5/ReleaseNotes.md +++ b/lib/IRremoteESP8266-2.7.6/ReleaseNotes.md @@ -1,5 +1,17 @@ # 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]** diff --git a/lib/IRremoteESP8266-2.7.5/SupportedProtocols.md b/lib/IRremoteESP8266-2.7.6/SupportedProtocols.md similarity index 100% rename from lib/IRremoteESP8266-2.7.5/SupportedProtocols.md rename to lib/IRremoteESP8266-2.7.6/SupportedProtocols.md diff --git a/lib/IRremoteESP8266-2.7.5/examples/CommonAcControl/CommonAcControl.ino b/lib/IRremoteESP8266-2.7.6/examples/CommonAcControl/CommonAcControl.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/CommonAcControl/CommonAcControl.ino rename to lib/IRremoteESP8266-2.7.6/examples/CommonAcControl/CommonAcControl.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/CommonAcControl/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/CommonAcControl/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/CommonAcControl/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/CommonAcControl/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/ControlSamsungAC/ControlSamsungAC.ino b/lib/IRremoteESP8266-2.7.6/examples/ControlSamsungAC/ControlSamsungAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/ControlSamsungAC/ControlSamsungAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/ControlSamsungAC/ControlSamsungAC.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/ControlSamsungAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/ControlSamsungAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/ControlSamsungAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/ControlSamsungAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/DumbIRRepeater/DumbIRRepeater.ino b/lib/IRremoteESP8266-2.7.6/examples/DumbIRRepeater/DumbIRRepeater.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/DumbIRRepeater/DumbIRRepeater.ino rename to lib/IRremoteESP8266-2.7.6/examples/DumbIRRepeater/DumbIRRepeater.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/DumbIRRepeater/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/DumbIRRepeater/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/DumbIRRepeater/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/DumbIRRepeater/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRGCSendDemo/IRGCSendDemo.ino b/lib/IRremoteESP8266-2.7.6/examples/IRGCSendDemo/IRGCSendDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRGCSendDemo/IRGCSendDemo.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRGCSendDemo/IRGCSendDemo.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRGCSendDemo/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRGCSendDemo/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRGCSendDemo/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRGCSendDemo/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRGCTCPServer/IRGCTCPServer.ino b/lib/IRremoteESP8266-2.7.6/examples/IRGCTCPServer/IRGCTCPServer.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRGCTCPServer/IRGCTCPServer.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRGCTCPServer/IRGCTCPServer.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRGCTCPServer/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRGCTCPServer/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRGCTCPServer/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRGCTCPServer/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRMQTTServer/IRMQTTServer.h b/lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/IRMQTTServer.h similarity index 99% rename from lib/IRremoteESP8266-2.7.5/examples/IRMQTTServer/IRMQTTServer.h rename to lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/IRMQTTServer.h index b6fe281da..c886b705b 100644 --- a/lib/IRremoteESP8266-2.7.5/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.7" +#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.5/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/IRMQTTServer.ino similarity index 95% rename from lib/IRremoteESP8266-2.7.5/examples/IRMQTTServer/IRMQTTServer.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/IRMQTTServer.ino index af98e3166..c04c1e23b 100644 --- a/lib/IRremoteESP8266-2.7.5/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 @@ -622,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"); @@ -642,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); } @@ -715,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 ": " " " "

" "
"); @@ -825,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 @@ -848,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); @@ -996,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 @@ -1042,20 +1058,20 @@ void handleAirCon(void) { "" + "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" ""), ((9==i)||(10==i)) ? WebColor(COL_TEXT_WARNING) : WebColor(COL_TEXT), i, (0==i) ? " style='width:200px'" : "", i); +#else // ESP32 + WSContentSend_P(PSTR(""), + ((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(""), WebColor(COL_TEXT)); #endif WSContentSend_P(PSTR("
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" + @@ -1260,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 @@ -1271,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 @@ -1289,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 + "
" @@ -1330,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 + @@ -1339,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(); @@ -1401,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. @@ -1423,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. @@ -1456,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"); @@ -2012,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); @@ -2184,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 " + @@ -2768,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 @@ -2792,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.5/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRMQTTServer/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRMQTTServer/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRServer/IRServer.ino b/lib/IRremoteESP8266-2.7.6/examples/IRServer/IRServer.ino similarity index 95% rename from lib/IRremoteESP8266-2.7.5/examples/IRServer/IRServer.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRServer/IRServer.ino index 96fad95d2..92bcd0302 100644 --- a/lib/IRremoteESP8266-2.7.5/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.5/examples/IRServer/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRServer/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRServer/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRServer/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRrecvDemo/IRrecvDemo.ino b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDemo/IRrecvDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRrecvDemo/IRrecvDemo.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRrecvDemo/IRrecvDemo.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDemo/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRrecvDemo/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRrecvDemo/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRrecvDump/IRrecvDump.ino b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDump/IRrecvDump.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRrecvDump/IRrecvDump.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRrecvDump/IRrecvDump.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDump/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRrecvDump/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRrecvDump/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266-2.7.6/examples/IRrecvDumpV2/IRrecvDumpV2.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/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.5/examples/IRsendDemo/IRsendDemo.ino b/lib/IRremoteESP8266-2.7.6/examples/IRsendDemo/IRsendDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRsendDemo/IRsendDemo.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRsendDemo/IRsendDemo.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRsendDemo/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRsendDemo/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRsendDemo/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRsendProntoDemo/IRsendProntoDemo.ino b/lib/IRremoteESP8266-2.7.6/examples/IRsendProntoDemo/IRsendProntoDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRsendProntoDemo/IRsendProntoDemo.ino rename to lib/IRremoteESP8266-2.7.6/examples/IRsendProntoDemo/IRsendProntoDemo.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/IRsendProntoDemo/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/IRsendProntoDemo/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/IRsendProntoDemo/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino b/lib/IRremoteESP8266-2.7.6/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino rename to lib/IRremoteESP8266-2.7.6/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/JVCPanasonicSendDemo/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/JVCPanasonicSendDemo/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/JVCPanasonicSendDemo/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/LGACSend/LGACSend.ino b/lib/IRremoteESP8266-2.7.6/examples/LGACSend/LGACSend.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/LGACSend/LGACSend.ino rename to lib/IRremoteESP8266-2.7.6/examples/LGACSend/LGACSend.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/LGACSend/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/LGACSend/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/LGACSend/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/SmartIRRepeater/SmartIRRepeater.ino b/lib/IRremoteESP8266-2.7.6/examples/SmartIRRepeater/SmartIRRepeater.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/SmartIRRepeater/SmartIRRepeater.ino rename to lib/IRremoteESP8266-2.7.6/examples/SmartIRRepeater/SmartIRRepeater.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/SmartIRRepeater/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/SmartIRRepeater/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/SmartIRRepeater/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/SmartIRRepeater/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnArgoAC/TurnOnArgoAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnArgoAC/TurnOnArgoAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnArgoAC/TurnOnArgoAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnArgoAC/TurnOnArgoAC.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnArgoAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnArgoAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnArgoAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnDaikinAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnDaikinAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnDaikinAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnFujitsuAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnFujitsuAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnFujitsuAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnGreeAC/TurnOnGreeAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnGreeAC/TurnOnGreeAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnGreeAC/TurnOnGreeAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnGreeAC/TurnOnGreeAC.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnGreeAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnGreeAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnGreeAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnGreeAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnKelvinatorAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnKelvinatorAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnKelvinatorAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnMitsubishiAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnMitsubishiHeavyAc/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiHeavyAc/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnMitsubishiHeavyAc/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnMitsubishiHeavyAc/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnPanasonicAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnPanasonicAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnPanasonicAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnPanasonicAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnToshibaAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnToshibaAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnToshibaAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino b/lib/IRremoteESP8266-2.7.6/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino diff --git a/lib/IRremoteESP8266-2.7.5/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.7.6/examples/TurnOnTrotecAC/platformio.ini similarity index 100% rename from lib/IRremoteESP8266-2.7.5/examples/TurnOnTrotecAC/platformio.ini rename to lib/IRremoteESP8266-2.7.6/examples/TurnOnTrotecAC/platformio.ini diff --git a/lib/IRremoteESP8266-2.7.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/keywords.txt b/lib/IRremoteESP8266-2.7.6/keywords.txt similarity index 99% rename from lib/IRremoteESP8266-2.7.5/keywords.txt rename to lib/IRremoteESP8266-2.7.6/keywords.txt index 2d0ea9255..7b9e356af 100644 --- a/lib/IRremoteESP8266-2.7.5/keywords.txt +++ b/lib/IRremoteESP8266-2.7.6/keywords.txt @@ -629,6 +629,7 @@ xorBytes KEYWORD2 # Constants (LITERAL1) ####################################### +*kAllProtocolNamesStr LITERAL1 // LITERAL1 AIRWELL LITERAL1 AIWA_RC_T501 LITERAL1 diff --git a/lib/IRremoteESP8266-2.7.5/library.json b/lib/IRremoteESP8266-2.7.6/library.json similarity index 97% rename from lib/IRremoteESP8266-2.7.5/library.json rename to lib/IRremoteESP8266-2.7.6/library.json index 3220f9bd2..c2780ac6c 100644 --- a/lib/IRremoteESP8266-2.7.5/library.json +++ b/lib/IRremoteESP8266-2.7.6/library.json @@ -1,6 +1,6 @@ { "name": "IRremoteESP8266", - "version": "2.7.5", + "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.5/library.properties b/lib/IRremoteESP8266-2.7.6/library.properties similarity index 97% rename from lib/IRremoteESP8266-2.7.5/library.properties rename to lib/IRremoteESP8266-2.7.6/library.properties index 16d4c2d20..b67edbce0 100644 --- a/lib/IRremoteESP8266-2.7.5/library.properties +++ b/lib/IRremoteESP8266-2.7.6/library.properties @@ -1,5 +1,5 @@ name=IRremoteESP8266 -version=2.7.5 +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.5/pylintrc b/lib/IRremoteESP8266-2.7.6/pylintrc similarity index 100% rename from lib/IRremoteESP8266-2.7.5/pylintrc rename to lib/IRremoteESP8266-2.7.6/pylintrc diff --git a/lib/IRremoteESP8266-2.7.5/src/CPPLINT.cfg b/lib/IRremoteESP8266-2.7.6/src/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/CPPLINT.cfg rename to lib/IRremoteESP8266-2.7.6/src/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.7.5/src/IRac.cpp b/lib/IRremoteESP8266-2.7.6/src/IRac.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/IRac.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRac.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/IRac.h b/lib/IRremoteESP8266-2.7.6/src/IRac.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/IRac.h rename to lib/IRremoteESP8266-2.7.6/src/IRac.h diff --git a/lib/IRremoteESP8266-2.7.5/src/IRrecv.cpp b/lib/IRremoteESP8266-2.7.6/src/IRrecv.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/IRrecv.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRrecv.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/IRrecv.h b/lib/IRremoteESP8266-2.7.6/src/IRrecv.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/IRrecv.h rename to lib/IRremoteESP8266-2.7.6/src/IRrecv.h diff --git a/lib/IRremoteESP8266-2.7.5/src/IRremoteESP8266.h b/lib/IRremoteESP8266-2.7.6/src/IRremoteESP8266.h similarity index 99% rename from lib/IRremoteESP8266-2.7.5/src/IRremoteESP8266.h rename to lib/IRremoteESP8266-2.7.6/src/IRremoteESP8266.h index dd8008bc0..2bf906697 100644 --- a/lib/IRremoteESP8266-2.7.5/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.5" +#define _IRREMOTEESP8266_VERSION_ "2.7.6" // Set the language & locale for the library. See the `locale` dir for options. #ifndef _IR_LOCALE_ diff --git a/lib/IRremoteESP8266-2.7.5/src/IRsend.cpp b/lib/IRremoteESP8266-2.7.6/src/IRsend.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/IRsend.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRsend.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/IRsend.h b/lib/IRremoteESP8266-2.7.6/src/IRsend.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/IRsend.h rename to lib/IRremoteESP8266-2.7.6/src/IRsend.h diff --git a/lib/IRremoteESP8266-2.7.5/src/IRtext.cpp b/lib/IRremoteESP8266-2.7.6/src/IRtext.cpp similarity index 76% rename from lib/IRremoteESP8266-2.7.5/src/IRtext.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRtext.cpp index 72ddd45b7..cb9ccd722 100644 --- a/lib/IRremoteESP8266-2.7.5/src/IRtext.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/IRtext.cpp @@ -163,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.5/src/IRtext.h b/lib/IRremoteESP8266-2.7.6/src/IRtext.h similarity index 99% rename from lib/IRremoteESP8266-2.7.5/src/IRtext.h rename to lib/IRremoteESP8266-2.7.6/src/IRtext.h index 13d2d400c..cbce59747 100644 --- a/lib/IRremoteESP8266-2.7.5/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; diff --git a/lib/IRremoteESP8266-2.7.5/src/IRtimer.cpp b/lib/IRremoteESP8266-2.7.6/src/IRtimer.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/IRtimer.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRtimer.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/IRtimer.h b/lib/IRremoteESP8266-2.7.6/src/IRtimer.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/IRtimer.h rename to lib/IRremoteESP8266-2.7.6/src/IRtimer.h diff --git a/lib/IRremoteESP8266-2.7.5/src/IRutils.cpp b/lib/IRremoteESP8266-2.7.6/src/IRutils.cpp similarity index 71% rename from lib/IRremoteESP8266-2.7.5/src/IRutils.cpp rename to lib/IRremoteESP8266-2.7.6/src/IRutils.cpp index db9095143..39d9973ed 100644 --- a/lib/IRremoteESP8266-2.7.5/src/IRutils.cpp +++ b/lib/IRremoteESP8266-2.7.6/src/IRutils.cpp @@ -91,169 +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, "AIRWELL")) - return decode_type_t::AIRWELL; - 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, "DAIKIN64")) - return decode_type_t::DAIKIN64; - 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_AC3")) - return decode_type_t::HITACHI_AC3; - 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, "SYMPHONY")) - return decode_type_t::SYMPHONY; - 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()); @@ -271,251 +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 AIRWELL: - result = F("AIRWELL"); - 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 DAIKIN64: - result = F("DAIKIN64"); - 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_AC3: - result = F("HITACHI_AC3"); - 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 SYMPHONY: - result = F("SYMPHONY"); - 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; diff --git a/lib/IRremoteESP8266-2.7.5/src/IRutils.h b/lib/IRremoteESP8266-2.7.6/src/IRutils.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/IRutils.h rename to lib/IRremoteESP8266-2.7.6/src/IRutils.h diff --git a/lib/IRremoteESP8266-2.7.5/src/i18n.h b/lib/IRremoteESP8266-2.7.6/src/i18n.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/i18n.h rename to lib/IRremoteESP8266-2.7.6/src/i18n.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Airwell.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Airwell.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Airwell.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Airwell.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Aiwa.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Aiwa.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Aiwa.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Aiwa.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Amcor.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Amcor.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Amcor.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Amcor.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Amcor.h b/lib/IRremoteESP8266-2.7.6/src/ir_Amcor.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Amcor.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Amcor.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Argo.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Argo.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Argo.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Argo.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Argo.h b/lib/IRremoteESP8266-2.7.6/src/ir_Argo.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Argo.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Argo.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Carrier.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Carrier.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Carrier.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Carrier.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Coolix.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Coolix.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Coolix.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Coolix.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Coolix.h b/lib/IRremoteESP8266-2.7.6/src/ir_Coolix.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Coolix.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Coolix.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Daikin.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Daikin.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Daikin.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Daikin.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Daikin.h b/lib/IRremoteESP8266-2.7.6/src/ir_Daikin.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Daikin.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Daikin.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Denon.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Denon.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Denon.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Denon.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Dish.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Dish.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Dish.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Dish.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Electra.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Electra.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Electra.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Electra.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Electra.h b/lib/IRremoteESP8266-2.7.6/src/ir_Electra.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Electra.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Electra.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Epson.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Epson.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Epson.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Epson.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Fujitsu.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Fujitsu.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Fujitsu.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Fujitsu.h b/lib/IRremoteESP8266-2.7.6/src/ir_Fujitsu.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Fujitsu.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Fujitsu.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_GICable.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_GICable.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_GICable.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_GICable.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_GlobalCache.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_GlobalCache.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_GlobalCache.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Goodweather.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Goodweather.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Goodweather.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Goodweather.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Goodweather.h b/lib/IRremoteESP8266-2.7.6/src/ir_Goodweather.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Goodweather.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Goodweather.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Gree.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Gree.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Gree.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Gree.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Gree.h b/lib/IRremoteESP8266-2.7.6/src/ir_Gree.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Gree.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Gree.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Haier.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Haier.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Haier.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Haier.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Haier.h b/lib/IRremoteESP8266-2.7.6/src/ir_Haier.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Haier.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Haier.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Hitachi.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Hitachi.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Hitachi.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Hitachi.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Hitachi.h b/lib/IRremoteESP8266-2.7.6/src/ir_Hitachi.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Hitachi.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Hitachi.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Inax.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Inax.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Inax.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Inax.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_JVC.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_JVC.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_JVC.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_JVC.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Kelvinator.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Kelvinator.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Kelvinator.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Kelvinator.h b/lib/IRremoteESP8266-2.7.6/src/ir_Kelvinator.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Kelvinator.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Kelvinator.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_LG.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_LG.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_LG.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_LG.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_LG.h b/lib/IRremoteESP8266-2.7.6/src/ir_LG.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_LG.h rename to lib/IRremoteESP8266-2.7.6/src/ir_LG.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Lasertag.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Lasertag.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Lasertag.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Lasertag.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Lego.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Lego.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Lego.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Lego.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Lutron.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Lutron.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Lutron.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Lutron.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_MWM.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_MWM.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_MWM.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_MWM.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Magiquest.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Magiquest.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Magiquest.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Magiquest.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Magiquest.h b/lib/IRremoteESP8266-2.7.6/src/ir_Magiquest.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Magiquest.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Magiquest.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Midea.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Midea.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Midea.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Midea.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Midea.h b/lib/IRremoteESP8266-2.7.6/src/ir_Midea.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Midea.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Midea.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Mitsubishi.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Mitsubishi.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Mitsubishi.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Mitsubishi.h b/lib/IRremoteESP8266-2.7.6/src/ir_Mitsubishi.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Mitsubishi.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Mitsubishi.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_MitsubishiHeavy.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_MitsubishiHeavy.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_MitsubishiHeavy.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266-2.7.6/src/ir_MitsubishiHeavy.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_MitsubishiHeavy.h rename to lib/IRremoteESP8266-2.7.6/src/ir_MitsubishiHeavy.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_NEC.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_NEC.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_NEC.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_NEC.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_NEC.h b/lib/IRremoteESP8266-2.7.6/src/ir_NEC.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_NEC.h rename to lib/IRremoteESP8266-2.7.6/src/ir_NEC.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Neoclima.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Neoclima.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Neoclima.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Neoclima.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Neoclima.h b/lib/IRremoteESP8266-2.7.6/src/ir_Neoclima.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Neoclima.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Neoclima.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Nikai.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Nikai.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Nikai.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Nikai.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Panasonic.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Panasonic.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Panasonic.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Panasonic.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Panasonic.h b/lib/IRremoteESP8266-2.7.6/src/ir_Panasonic.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Panasonic.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Panasonic.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Pioneer.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Pioneer.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Pioneer.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Pioneer.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Pronto.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Pronto.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Pronto.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Pronto.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_RC5_RC6.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_RC5_RC6.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_RC5_RC6.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_RCMM.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_RCMM.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_RCMM.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_RCMM.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Samsung.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Samsung.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Samsung.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Samsung.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Samsung.h b/lib/IRremoteESP8266-2.7.6/src/ir_Samsung.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Samsung.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Samsung.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Sanyo.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Sanyo.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Sanyo.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Sanyo.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Sharp.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Sharp.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Sharp.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Sharp.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Sharp.h b/lib/IRremoteESP8266-2.7.6/src/ir_Sharp.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Sharp.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Sharp.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Sherwood.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Sherwood.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Sherwood.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Sherwood.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Sony.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Sony.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Sony.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Sony.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Symphony.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Symphony.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Symphony.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Symphony.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Tcl.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Tcl.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Tcl.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Tcl.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Tcl.h b/lib/IRremoteESP8266-2.7.6/src/ir_Tcl.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Tcl.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Tcl.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Teco.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Teco.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Teco.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Teco.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Teco.h b/lib/IRremoteESP8266-2.7.6/src/ir_Teco.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Teco.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Teco.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Toshiba.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Toshiba.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Toshiba.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Toshiba.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Toshiba.h b/lib/IRremoteESP8266-2.7.6/src/ir_Toshiba.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Toshiba.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Toshiba.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Trotec.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Trotec.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Trotec.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Trotec.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Trotec.h b/lib/IRremoteESP8266-2.7.6/src/ir_Trotec.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Trotec.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Trotec.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Vestel.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Vestel.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Vestel.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Vestel.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Vestel.h b/lib/IRremoteESP8266-2.7.6/src/ir_Vestel.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Vestel.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Vestel.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Whirlpool.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Whirlpool.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Whirlpool.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Whirlpool.h b/lib/IRremoteESP8266-2.7.6/src/ir_Whirlpool.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Whirlpool.h rename to lib/IRremoteESP8266-2.7.6/src/ir_Whirlpool.h diff --git a/lib/IRremoteESP8266-2.7.5/src/ir_Whynter.cpp b/lib/IRremoteESP8266-2.7.6/src/ir_Whynter.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/ir_Whynter.cpp rename to lib/IRremoteESP8266-2.7.6/src/ir_Whynter.cpp diff --git a/lib/IRremoteESP8266-2.7.5/src/locale/README.md b/lib/IRremoteESP8266-2.7.6/src/locale/README.md similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/locale/README.md rename to lib/IRremoteESP8266-2.7.6/src/locale/README.md diff --git a/lib/IRremoteESP8266-2.7.5/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.5/src/locale/de-CH.h rename to lib/IRremoteESP8266-2.7.6/src/locale/de-CH.h diff --git a/lib/IRremoteESP8266-2.7.5/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.5/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.5/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.5/src/locale/en-AU.h rename to lib/IRremoteESP8266-2.7.6/src/locale/en-AU.h diff --git a/lib/IRremoteESP8266-2.7.5/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.5/src/locale/en-IE.h rename to lib/IRremoteESP8266-2.7.6/src/locale/en-IE.h diff --git a/lib/IRremoteESP8266-2.7.5/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.5/src/locale/en-UK.h rename to lib/IRremoteESP8266-2.7.6/src/locale/en-UK.h diff --git a/lib/IRremoteESP8266-2.7.5/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.5/src/locale/en-US.h rename to lib/IRremoteESP8266-2.7.6/src/locale/en-US.h diff --git a/lib/IRremoteESP8266-2.7.5/src/locale/es-ES.h b/lib/IRremoteESP8266-2.7.6/src/locale/es-ES.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/locale/es-ES.h rename to lib/IRremoteESP8266-2.7.6/src/locale/es-ES.h diff --git a/lib/IRremoteESP8266-2.7.5/src/locale/fr-FR.h b/lib/IRremoteESP8266-2.7.6/src/locale/fr-FR.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/locale/fr-FR.h rename to lib/IRremoteESP8266-2.7.6/src/locale/fr-FR.h diff --git a/lib/IRremoteESP8266-2.7.5/src/locale/it-IT.h b/lib/IRremoteESP8266-2.7.6/src/locale/it-IT.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/src/locale/it-IT.h rename to lib/IRremoteESP8266-2.7.6/src/locale/it-IT.h diff --git a/lib/IRremoteESP8266-2.7.5/src/locale/defaults.h b/lib/IRremoteESP8266-2.7.6/src/locale/zh-CN.h similarity index 67% rename from lib/IRremoteESP8266-2.7.5/src/locale/defaults.h rename to lib/IRremoteESP8266-2.7.6/src/locale/zh-CN.h index 509e35cfe..bcb30392c 100644 --- a/lib/IRremoteESP8266-2.7.5/src/locale/defaults.h +++ b/lib/IRremoteESP8266-2.7.6/src/locale/zh-CN.h @@ -1,67 +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 "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! @@ -70,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! @@ -91,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`! @@ -199,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! @@ -331,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! @@ -340,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. @@ -397,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.5/test/IRac_test.cpp b/lib/IRremoteESP8266-2.7.6/test/IRac_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/IRac_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/IRac_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/IRrecv_test.cpp b/lib/IRremoteESP8266-2.7.6/test/IRrecv_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/IRrecv_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/IRrecv_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/IRrecv_test.h b/lib/IRremoteESP8266-2.7.6/test/IRrecv_test.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/IRrecv_test.h rename to lib/IRremoteESP8266-2.7.6/test/IRrecv_test.h diff --git a/lib/IRremoteESP8266-2.7.5/test/IRsend_test.cpp b/lib/IRremoteESP8266-2.7.6/test/IRsend_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/IRsend_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/IRsend_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/IRsend_test.h b/lib/IRremoteESP8266-2.7.6/test/IRsend_test.h similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/IRsend_test.h rename to lib/IRremoteESP8266-2.7.6/test/IRsend_test.h diff --git a/lib/IRremoteESP8266-2.7.5/test/IRutils_test.cpp b/lib/IRremoteESP8266-2.7.6/test/IRutils_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/IRutils_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/IRutils_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/Makefile b/lib/IRremoteESP8266-2.7.6/test/Makefile similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/Makefile rename to lib/IRremoteESP8266-2.7.6/test/Makefile diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Airwell_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Airwell_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Airwell_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Airwell_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Aiwa_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Aiwa_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Amcor_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Amcor_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Argo_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Argo_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Argo_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Argo_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Carrier_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Carrier_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Coolix_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Coolix_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Coolix_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Daikin_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Daikin_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Daikin_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Denon_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Denon_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Dish_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Dish_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Electra_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Electra_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Electra_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Electra_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Epson_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Epson_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Fujitsu_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Fujitsu_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Fujitsu_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_GICable_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_GICable_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_GlobalCache_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_GlobalCache_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Goodweather_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Goodweather_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Goodweather_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Goodweather_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Gree_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Gree_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Gree_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Gree_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Haier_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Haier_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Haier_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Haier_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Hitachi_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Hitachi_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Hitachi_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Inax_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Inax_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_JVC_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_JVC_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Kelvinator_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Kelvinator_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Kelvinator_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_LG_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_LG_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_LG_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_LG_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Lasertag_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Lasertag_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Lego_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Lego_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Lutron_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Lutron_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_MWM_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_MWM_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Magiquest_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Magiquest_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Midea_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Midea_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Midea_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Midea_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_MitsubishiHeavy_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_MitsubishiHeavy_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_MitsubishiHeavy_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Mitsubishi_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Mitsubishi_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Mitsubishi_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_NEC_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_NEC_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Neoclima_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Neoclima_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Neoclima_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Neoclima_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Nikai_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Nikai_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Panasonic_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Panasonic_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Panasonic_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Pioneer_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Pioneer_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Pronto_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Pronto_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/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.5/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.5/test/ir_RCMM_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_RCMM_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Samsung_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Samsung_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Samsung_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Sanyo_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Sanyo_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Sharp_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Sharp_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Sharp_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Sherwood_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Sherwood_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Sony_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Sony_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Symphony_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Symphony_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Symphony_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Symphony_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Tcl_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Tcl_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Teco_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Teco_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Teco_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Teco_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Toshiba_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Toshiba_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Toshiba_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Trotec_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Trotec_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Trotec_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Trotec_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Vestel_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Vestel_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Vestel_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266-2.7.6/test/ir_Whirlpool_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/test/ir_Whirlpool_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Whirlpool_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/test/ir_Whynter_test.cpp rename to lib/IRremoteESP8266-2.7.6/test/ir_Whynter_test.cpp diff --git a/lib/IRremoteESP8266-2.7.5/tools/Makefile b/lib/IRremoteESP8266-2.7.6/tools/Makefile similarity index 100% rename from lib/IRremoteESP8266-2.7.5/tools/Makefile rename to lib/IRremoteESP8266-2.7.6/tools/Makefile diff --git a/lib/IRremoteESP8266-2.7.5/tools/RawToGlobalCache.sh b/lib/IRremoteESP8266-2.7.6/tools/RawToGlobalCache.sh similarity index 100% rename from lib/IRremoteESP8266-2.7.5/tools/RawToGlobalCache.sh rename to lib/IRremoteESP8266-2.7.6/tools/RawToGlobalCache.sh diff --git a/lib/IRremoteESP8266-2.7.5/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.5/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.5/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.5/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.5/tools/gc_decode.cpp b/lib/IRremoteESP8266-2.7.6/tools/gc_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/tools/gc_decode.cpp rename to lib/IRremoteESP8266-2.7.6/tools/gc_decode.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/tools/generate_irtext_h.sh rename to lib/IRremoteESP8266-2.7.6/tools/generate_irtext_h.sh diff --git a/lib/IRremoteESP8266-2.7.5/tools/mkkeywords b/lib/IRremoteESP8266-2.7.6/tools/mkkeywords similarity index 100% rename from lib/IRremoteESP8266-2.7.5/tools/mkkeywords rename to lib/IRremoteESP8266-2.7.6/tools/mkkeywords diff --git a/lib/IRremoteESP8266-2.7.5/tools/mode2_decode.cpp b/lib/IRremoteESP8266-2.7.6/tools/mode2_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.7.5/tools/mode2_decode.cpp rename to lib/IRremoteESP8266-2.7.6/tools/mode2_decode.cpp diff --git a/lib/IRremoteESP8266-2.7.5/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.5/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 212992775..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,17 +159,31 @@ 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); } else { Serial.begin(speed, SERIAL_8N1); } -#ifdef ESP8266 if (m_hardswap) { Serial.swap(); } -#endif // ESP8266 +#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; @@ -170,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; } @@ -183,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]; @@ -193,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]; @@ -205,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; @@ -250,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/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/src/esp8266toEsp32.cpp b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp index 6635f6d4d..e15770022 100644 --- a/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp +++ b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp @@ -22,99 +22,3 @@ #include "esp8266toEsp32.h" // ESP Stuff - -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_getBootVersion(void) -{ - return 1; -} - -bool ESP_rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size) -{ - return false; -} - -bool ESP_rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size) -{ - return false; -} - -void ESP_reset() -{ - ESP.restart(); -} - -uint32_t ESP_getFlashChipId() -{ - return 0; -} - -uint32_t ESP_getChipId() -{ - uint32_t id = 0; - for (uint32_t i = 0; i < 17; i = i +8) { - id |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; - } - return id; -} - -String String_ESP_getChipId() -{ - uint64_t mac = ESP.getEfuseMac(); - return String(uint32_t(mac >> 32)) + String(uint32_t(mac)); -} - -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; -} - -#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 -} diff --git a/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h index 276772b95..90e2460b3 100644 --- a/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h +++ b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h @@ -28,26 +28,27 @@ #include -// -// ESP32 -// -#define ESP_flashReadHeader(offset, data, size) ESP32_flashRead(offset, data, size) -#define ESP_flashRead(offset, data, size) ESP32_flashRead(offset, data, size) -String ESP_getResetReason(void); -uint32_t ESP_ResetInfoReason(void); -uint32_t ESP_getBootVersion(void); -bool ESP_rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size); -bool ESP_rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size); -void ESP_reset(); -uint32_t ESP_getFlashChipId(); -uint32_t ESP_getChipId(); -String String_ESP_getChipId(); -uint32_t ESP_getSketchSize(); -void DisableBrownout(void); +// 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) @@ -57,6 +58,31 @@ 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; @@ -74,7 +100,7 @@ typedef double real64_t; // Serial minimal type to hold the config typedef int SerConfu8; typedef int SerialConfig; -#define analogWrite(a, b) +//#define analogWrite(a, b) // // WS2812 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, "
" D_GPIO "%d
" D_GPIO "%d
" D_ADC "0
")); + 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(); @@ -1522,22 +1549,30 @@ void TemplateSaveSettings(void) 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++) { -#ifdef ESP8266 + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { if (6 == i) { j = 9; } if (8 == i) { j = 12; } -#else // ESP32 - if (6 == i) { j = 12; } -#endif // ESP8266 - ESP32 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++; } +#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 @@ -1562,65 +1597,78 @@ void HandleModuleConfiguration(void) 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; + } + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); + } + 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; +#else // ESP32 + uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0; + midx = ridx >> 5; +#endif // ESP8266 - ESP32 + if (!GetUsedInModule(midx, cmodule.io)) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), ridx); + } + } + 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 @@ -1629,8 +1677,9 @@ void HandleModuleConfiguration(void) 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); + WSContentSend_P(PSTR("%s " D_GPIO "%d"), + (WEMOS==my_module_type)?stemp:"", i, i, i); + WSContentSend_P(PSTR(""), i); #endif // ESP8266 } } @@ -1660,30 +1709,29 @@ 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)); - uint8_t value = (!strlen(tmp)) ? 0 : atoi(tmp); -#ifdef ESP8266 - Settings.my_gp.io[i] = value; -#else // ESP32 - if (i == ADC0_PIN) { - Settings.my_adc0 = value; - } else { - Settings.my_gp.io[i] = value; - } + 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); @@ -2118,7 +2166,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 @@ -2130,7 +2178,11 @@ void HandleInformation(void) 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_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; @@ -2204,7 +2256,9 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1}2 ")); // Empty line WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP_getChipId()); - WSContentSend_P(PSTR("}1" D_FLASH_CHIP_ID "}20x%06X"), ESP_getFlashChipId()); +#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); @@ -3060,11 +3114,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 6ad843980..eaa2562e8 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -1028,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 } diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 1c5751829..345951696 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -135,6 +135,9 @@ const char kLightCommands[] PROGMEM = "|" // No prefix #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 = { @@ -144,6 +147,9 @@ void (* const LightCommand[])(void) PROGMEM = { #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 @@ -282,7 +288,12 @@ 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 @@ -625,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 @@ -720,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 } @@ -974,6 +988,9 @@ public: (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); @@ -1227,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 } } @@ -1330,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 } } } @@ -1477,9 +1494,6 @@ void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value) // AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Light signal %d"), signal); light_controller.changeRGB(signal, 255 - signal, 0, true); // keep bri Settings.light_scheme = 0; -#ifdef USE_DEVICE_GROUPS - LightUpdateScheme(); -#endif // USE_DEVICE_GROUPS if (0 == light_state.getBri()) { light_controller.changeBri(50); } @@ -1688,17 +1702,19 @@ void LightCycleColor(int8_t direction) #ifdef USE_LIGHT_PALETTE if (Light.palette_count) { - 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; + 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(); } - LightSetPaletteEntry(); return; } #endif // USE_LIGHT_PALETTE @@ -1833,9 +1849,6 @@ void LightAnimate(void) Light.wakeup_active = 0; Settings.light_scheme = LS_POWER; -#ifdef USE_DEVICE_GROUPS - LightUpdateScheme(); -#endif // USE_DEVICE_GROUPS } } break; @@ -1858,6 +1871,14 @@ void LightAnimate(void) 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 @@ -1977,7 +1998,7 @@ bool isChannelGammaCorrected(uint32_t channel) { if (!Settings.light_correction) { return false; } // Gamma correction not activated if (channel >= Light.subtype) { return false; } // Out of range #ifdef ESP8266 - if (PHILIPS == my_module_type) { + 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 } @@ -1988,7 +2009,7 @@ bool isChannelGammaCorrected(uint32_t channel) { // is the channel a regular PWM or ColorTemp control bool isChannelCT(uint32_t channel) { #ifdef ESP8266 - if (PHILIPS == my_module_type) { + 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 } @@ -2114,13 +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]); 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); + analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col : cur_col); } } } @@ -2172,7 +2193,7 @@ void calcGammaBulbs(uint16_t cur_col_10[5]) { uint16_t white_bri10_1023 = (white_bri10 > 1023) ? 1023 : white_bri10; // max 1023 #ifdef ESP8266 - if (PHILIPS == my_module_type) { // channel 1 is the color tone, mapped to cold channel (0..255) + 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 @@ -2213,21 +2234,21 @@ void calcGammaBulbs(uint16_t cur_col_10[5]) { } #ifdef USE_DEVICE_GROUPS -void LightSendDeviceGroupStatus(bool force) +void LightSendDeviceGroupStatus(bool status) { - static uint8_t last_channels[LST_MAX]; static uint8_t last_bri; - uint8_t bri = light_state.getBri(); - bool send_bri_update = (force || bri != last_bri); - + bool send_bri_update = (status || bri != last_bri); if (Light.subtype > LST_SINGLE && !Light.devgrp_no_channels_out) { - uint8_t channels[LST_MAX]; - light_state.getChannels(channels); - if (force || memcmp(last_channels, channels, LST_MAX)) { - memcpy(last_channels, channels, LST_MAX); - SendLocalDeviceGroupMessage((send_bri_update ? DGR_MSGTYP_PARTIAL_UPDATE : DGR_MSGTYP_UPDATE), DGR_ITEM_LIGHT_CHANNELS, channels); + 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; @@ -2270,13 +2291,36 @@ void LightHandleDevGroupItem(void) 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: @@ -2330,17 +2374,6 @@ void LightHandleDevGroupItem(void) break; } } - -void LightUpdateScheme(void) -{ - static uint8_t last_scheme; - - if (Settings.light_scheme != last_scheme) { - 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 /*********************************************************************************************\ @@ -2384,9 +2417,6 @@ bool LightColorEntry(char *buffer, uint32_t buffer_length) } } else { value = atoi(buffer); -#ifdef USE_LIGHT_PALETTE - value--; -#endif // USE_LIGHT_PALETTE } #ifdef USE_LIGHT_PALETTE if (Light.palette_count) value = value % Light.palette_count; @@ -2417,6 +2447,7 @@ bool LightColorEntry(char *buffer, uint32_t buffer_length) } #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 @@ -2475,9 +2506,6 @@ void CmndSupportColor(void) } #endif // USE_LIGHT_PALETTE Settings.light_scheme = 0; -#ifdef USE_DEVICE_GROUPS - LightUpdateScheme(); -#endif // USE_DEVICE_GROUPS coldim = true; } else { // Color3, 4, 5 and 6 for (uint32_t i = 0; i < LST_RGB; i++) { @@ -2647,9 +2675,6 @@ void CmndScheme(void) #endif // USE_LIGHT_PALETTE } Settings.light_scheme = XdrvMailbox.payload; -#ifdef USE_DEVICE_GROUPS - LightUpdateScheme(); -#endif // USE_DEVICE_GROUPS if (LS_WAKEUP == Settings.light_scheme) { Light.wakeup_active = 3; } @@ -2673,9 +2698,6 @@ void CmndWakeup(void) } Light.wakeup_active = 3; Settings.light_scheme = LS_WAKEUP; -#ifdef USE_DEVICE_GROUPS - LightUpdateScheme(); -#endif // USE_DEVICE_GROUPS LightPowerOn(); ResponseCmndChar(D_JSON_STARTED); } @@ -2755,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(); @@ -2932,12 +2957,15 @@ void CmndPalette(void) 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) = '"'; } - memcpy(Light.current_color, palette_entry, Light.subtype); - LightGetColor(p); - p += strlen(p); - if (Settings.flag.decimal_text) { // SetOption17 - Switch between decimal or hexadecimal output - *p++ = '"'; + else { + for (uint32_t i = 0; i < Light.subtype; i++) { + p += sprintf_P(p, PSTR("%02X"), *palette_entry++); + } } *p++ = ','; } @@ -2949,6 +2977,20 @@ void CmndPalette(void) } #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 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 68365fceb..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 { diff --git a/tasmota/xdrv_07_domoticz.ino b/tasmota/xdrv_07_domoticz.ino index ef7202fa7..997905eff 100644 --- a/tasmota/xdrv_07_domoticz.ino +++ b/tasmota/xdrv_07_domoticz.ino @@ -110,7 +110,7 @@ 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 > devices_present) || (device > MAX_DOMOTICZ_IDX)) { device = 1; } if (Settings.domoticz_relay_idx[device -1]) { #ifdef USE_SHUTTER if (domoticz_is_shutter) { @@ -576,7 +576,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]); } diff --git a/tasmota/xdrv_08_serial_bridge.ino b/tasmota/xdrv_08_serial_bridge.ino index 990bf9c32..a3ae5ae32 100644 --- a/tasmota/xdrv_08_serial_bridge.ino +++ b/tasmota/xdrv_08_serial_bridge.ino @@ -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_10_rules.ino b/tasmota/xdrv_10_rules.ino index 4146fa382..fa5841564 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -580,9 +580,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); @@ -1797,8 +1797,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 ae4f9a9af..38c8f158f 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -62,6 +62,7 @@ keywords if then else endif, or, and are better readable for beginners (others m #define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS + uint32_t EncodeLightId(uint8_t relay_id); uint32_t DecodeLightId(uint32_t hue_id); @@ -69,6 +70,8 @@ uint32_t DecodeLightId(uint32_t hue_id); #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; @@ -102,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 @@ -266,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); @@ -588,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; @@ -1153,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)) { @@ -1166,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=get_picstore(-1,0); + if (fvar<1 || fvar>maxps) fvar=1; + uint32_t len=get_picstore(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) { @@ -1346,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; @@ -1355,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; @@ -1364,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; @@ -1388,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; @@ -1415,7 +1529,7 @@ chknext: goto exit; } if (!strncmp(vname,"heap",4)) { - fvar=ESP.getFreeHeap(); + fvar=ESP_getFreeHeap(); goto exit; } if (!strncmp(vname,"hn(",3)) { @@ -1472,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; @@ -1573,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; @@ -1581,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; @@ -1780,7 +1921,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; @@ -1892,6 +2033,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=wc_setup(fvar2); + } + break; + case 1: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=wc_get_frame(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=wc_set_options(fvar2,fvar3); + } + break; + case 3: + fvar=wc_get_width(); + break; + case 4: + fvar=wc_get_height(); + break; + case 5: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=wc_set_streamserver(fvar2); + } + break; + case 6: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=wc_set_motion_detect(fvar2); + } + break; +#ifdef USE_FACE_DETECT + case 7: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=wc_set_face_detect(fvar2); + } + break; +#endif + default: + fvar=0; + } + lp++; + len=0; + goto exit; + } +#endif if (!strncmp(vname,"wday",4)) { fvar=RtcTime.day_of_week; goto exit; @@ -2712,9 +2914,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; @@ -2753,6 +2975,21 @@ 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); + if (fvar<0) { + ledcSetup(7,500,10); + ledcAttachPin(-fvar,7); + ledcWriteTone(7,0); + } else { + ledcWriteTone(7,fvar); + } + lp++; + goto next_line; + } #endif else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { @@ -3264,7 +3501,7 @@ const char HTTP_FORM_FILE_UPGb[] PROGMEM = const char HTTP_FORM_SDC_DIRa[] PROGMEM = "
"; const char HTTP_FORM_SDC_DIRb[] PROGMEM = - "
%s    %d
"; + "
%s     %s : %8d
"; const char HTTP_FORM_SDC_DIRd[] PROGMEM = "
%s
"; const char HTTP_FORM_SDC_DIRc[] PROGMEM = @@ -3287,6 +3524,7 @@ const char HTTP_FORM_SDC_HREF[] PROGMEM = uint8_t reject(char *name) { + while (*name=='/') name++; if (*name=='_') return 1; if (*name=='.') return 1; @@ -3301,6 +3539,8 @@ uint8_t reject(char *name) { if (!strcasecmp(name,"FSEVEN~1")) return 1; if (!strcasecmp(name,"SYSTEM~1")) return 1; #endif + + if (!strncasecmp(name,"System Volume",13)) return 1; return 0; } @@ -3310,7 +3550,7 @@ void ListDir(char *path, uint8_t depth) { char format[12]; sprintf(format,"%%-%ds",24-depth); - File dir=SD.open(path); + File dir=FS_USED.open(path); if (dir) { dir.rewindDirectory(); if (strlen(path)>1) { @@ -3324,35 +3564,48 @@ void ListDir(char *path, uint8_t depth) { } WSContentSend_P(HTTP_FORM_SDC_DIRd,npath,path,".."); } + char *ep; while (true) { File entry=dir.openNextFile(); if (!entry) { break; } + // esp32 returns path here, shorten to filename + ep=(char*)entry.name(); + if (*ep=='/') ep++; + char *lcp = strrchr(ep,'/'); + if (lcp) { + ep=lcp+1; + } + //AddLog_P2(LOG_LEVEL_INFO, PSTR("entry: %s"),ep); + time_t tm=entry.getLastWrite(); + char tstr[24]; + strftime(tstr, 22, "%d-%m-%Y - %H:%M:%S ", localtime(&tm)); + char *pp=path; if (!*(pp+1)) pp++; char *cp=name; // osx formatted disks contain a lot of stuff we dont want - if (reject((char*)entry.name())) goto fclose; + if (reject((char*)ep)) goto fclose; for (uint8_t cnt=0;cnt1) { strcat(path,"/"); } - strcat(path,entry.name()); + strcat(path,ep); ListDir(path,depth+4); path[plen]=0; } else { - snprintf_P(npath,sizeof(npath),HTTP_FORM_SDC_HREF,WiFi.localIP().toString().c_str(),pp,entry.name()); - WSContentSend_P(HTTP_FORM_SDC_DIRb,npath,entry.name(),name,entry.size()); + snprintf_P(npath,sizeof(npath),HTTP_FORM_SDC_HREF,WiFi.localIP().toString().c_str(),pp,ep); + WSContentSend_P(HTTP_FORM_SDC_DIRb,npath,ep,name,tstr,entry.size()); } fclose: entry.close(); @@ -3419,8 +3672,8 @@ void script_upload(void) { if (upload.status == UPLOAD_FILE_START) { char npath[48]; sprintf(npath,"%s/%s",path,upload.filename.c_str()); - SD.remove(npath); - upload_file=SD.open(npath,FILE_WRITE); + FS_USED.remove(npath); + upload_file=FS_USED.open(npath,FILE_WRITE); if (!upload_file) Web.upload_error=1; } else if(upload.status == UPLOAD_FILE_WRITE) { if (upload_file) upload_file.write(upload.buf,upload.currentSize); @@ -3439,12 +3692,12 @@ uint8_t DownloadFile(char *file) { File download_file; WiFiClient download_Client; - if (!SD.exists(file)) { + if (!FS_USED.exists(file)) { AddLog_P(LOG_LEVEL_INFO,PSTR("file not found")); return 0; } - download_file=SD.open(file,FILE_READ); + download_file=FS_USED.open(file,FILE_READ); if (!download_file) { AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file")); return 0; @@ -3508,6 +3761,7 @@ void HandleScriptTextareaConfiguration(void) { } } + void HandleScriptConfiguration(void) { if (!HttpCheckPriviledgedAccess()) { return; } @@ -3609,19 +3863,17 @@ void ScriptSaveSettings(void) { strlcpy(glob_script_mem.script_ram,str.c_str(), glob_script_mem.script_size); -#ifdef USE_24C256 -#ifndef USE_SCRIPT_FATFS +#if defined(USE_24C256) && !defined(USE_SCRIPT_FATFS) if (glob_script_mem.flags&1) { EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram); } #endif -#endif -#ifdef USE_SCRIPT_FATFS +#if !defined(USE_24C256) && defined(USE_SCRIPT_FATFS) if (glob_script_mem.flags&1) { - SD.remove(FAT_SCRIPT_NAME); - File file=SD.open(FAT_SCRIPT_NAME,FILE_WRITE); - file.write(glob_script_mem.script_ram,FAT_SCRIPT_SIZE); + FS_USED.remove(FAT_SCRIPT_NAME); + File file=FS_USED.open(FAT_SCRIPT_NAME,FILE_WRITE); + file.write((const uint8_t*)glob_script_mem.script_ram,FAT_SCRIPT_SIZE); file.close(); } #endif @@ -4268,6 +4520,14 @@ void dateTime(uint16_t* date, uint16_t* time) { #ifdef SUPPORT_MQTT_EVENT + +#ifndef MQTT_EVENT_MSIZE +#define MQTT_EVENT_MSIZE 256 +#endif +#ifndef MQTT_EVENT_JSIZE +#define MQTT_EVENT_JSIZE 400 +#endif + /********************************************************************************************/ /* * Script: Process received MQTT message. @@ -4282,8 +4542,8 @@ bool ScriptMqttData(void) { bool serviced = false; //toLog(">>> 1"); - toLog(XdrvMailbox.data); - if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { + //toLog(XdrvMailbox.data); + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > MQTT_EVENT_MSIZE) { return false; } String sTopic = XdrvMailbox.topic; @@ -4303,7 +4563,7 @@ bool ScriptMqttData(void) if (event_item.Key.length() == 0) { //If did not specify Key value = sData; } else { //If specified Key, need to parse Key/Value from JSON data - StaticJsonBuffer<400> jsonBuf; + StaticJsonBuffer jsonBuf; JsonObject& jsonData = jsonBuf.parseObject(sData); String key1 = event_item.Key; String key2; @@ -4547,7 +4807,7 @@ uint32_t cnt; nbuf[cnt]=0; } -void ScriptWebShow(void) { +void ScriptWebShow(char mc) { uint8_t web_script=Run_Scripter(">W",-2,0); if (web_script==99) { char line[128]; @@ -4574,12 +4834,16 @@ void ScriptWebShow(void) { cp++; } char *lin=line; + if (!mc && (*lin!='&')) { + if (*lin=='@') { lin++; optflg=1; } else { optflg=0; } + + // check for input elements if (!strncmp(lin,"sl(",3)) { // insert slider sl(min max var left mid right) @@ -4739,6 +5003,12 @@ void ScriptWebShow(void) { WSContentSend_PD(PSTR("{s}%s{e}"),tmp); } } + } else { + if (*lin==mc) { + Replace_Cmd_Vars(lin+1,tmp,sizeof(tmp)); + WSContentSend_PD(PSTR("%s"),tmp); + } + } } if (*lp==SCRIPT_EOL) { lp++; @@ -4755,12 +5025,7 @@ void ScriptWebShow(void) { #ifdef USE_SENDMAIL -#ifdef ESP8266 -void script_send_email_body(BearSSL::WiFiClientSecure_light *client) { -#else -void script_send_email_body(WiFiClient *client) { -#endif - +void script_send_email_body(void(*func)(char *)) { uint8_t msect=Run_Scripter(">m",-2,0); if (msect==99) { char line[128]; @@ -4786,7 +5051,8 @@ uint8_t msect=Run_Scripter(">m",-2,0); cp++; } Replace_Cmd_Vars(line,tmp,sizeof(tmp)); - client->println(tmp); + //client->println(tmp); + func(tmp); } if (*lp==SCRIPT_EOL) { lp++; @@ -4797,7 +5063,8 @@ uint8_t msect=Run_Scripter(">m",-2,0); } } } else { - client->println("*"); + //client->println("*"); + func((char*)"*"); } } #endif @@ -4844,6 +5111,51 @@ void ScriptJsonAppend(void) { #endif //USE_SCRIPT_JSON_EXPORT +bool RulesProcessEvent(char *json_event) { + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,json_event); +} + +#ifdef ESP32 +#ifdef USE_SCRIPT_TASK +uint16_t task_timer1; +uint16_t task_timer2; + +void script_task1(void *arg) { + while (1) { + delay(task_timer1); + Run_Scripter(">t1",3,0); + } +} + +void script_task2(void *arg) { + while (1) { + delay(task_timer2); + Run_Scripter(">t2",3,0); + } +} +#ifndef STASK_STACK +#define STASK_STACK 4096 +#endif + +#ifndef STASK_PRIO +#define STASK_PRIO 5 +#endif + +uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) { + //return 0; + BaseType_t res=0; + if (core>1) {core = 1;} + if (num == 1) { + res = xTaskCreatePinnedToCore(script_task1, "T 1", STASK_STACK, NULL, STASK_PRIO, NULL, core); + task_timer1=time; + } else { + res = xTaskCreatePinnedToCore(script_task2, "T 2", STASK_STACK, NULL, STASK_PRIO, NULL, core); + task_timer2=time; + } + return res; +} +#endif // USE_SCRIPT_TASK +#endif // ESP32 /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -4854,14 +5166,8 @@ bool Xdrv10(uint8_t function) switch (function) { case FUNC_PRE_INIT: - /* -#ifdef USE_WEBCAM - if (Settings.module==ESP32_CAM_AITHINKER) { - webcam_setup(); - } -#endif -*/ // set defaults to rules memory + //bitWrite(Settings.rule_enabled,0,0); glob_script_mem.script_ram=Settings.rules[0]; glob_script_mem.script_size=MAX_SCRIPT_SIZE; glob_script_mem.flags=0; @@ -4901,15 +5207,29 @@ bool Xdrv10(uint8_t function) #endif #ifdef USE_SCRIPT_FATFS - if (SD.begin(USE_SCRIPT_FATFS)) { + +#ifdef USE_MMC + if (FS_USED.begin()) { +#else + +#ifdef ESP32 + if (PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_MISO) && PinUsed(GPIO_SPI_CLK)) { + SPI.begin(Pin(GPIO_SPI_CLK),Pin(GPIO_SPI_MISO),Pin(GPIO_SPI_MOSI), -1); + } +#endif + if (FS_USED.begin(USE_SCRIPT_FATFS)) { +#endif + + //FS_USED.dateTimeCallback(dateTime); + glob_script_mem.script_sd_found=1; char *script; script=(char*)calloc(FAT_SCRIPT_SIZE+4,1); if (!script) break; glob_script_mem.script_ram=script; glob_script_mem.script_size=FAT_SCRIPT_SIZE; - if (SD.exists(FAT_SCRIPT_NAME)) { - File file=SD.open(FAT_SCRIPT_NAME,FILE_READ); + if (FS_USED.exists(FAT_SCRIPT_NAME)) { + File file=FS_USED.open(FAT_SCRIPT_NAME,FILE_READ); file.read((uint8_t*)script,FAT_SCRIPT_SIZE); file.close(); } @@ -4920,11 +5240,6 @@ bool Xdrv10(uint8_t function) glob_script_mem.flags=1; -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) - // for unkonwn reasons is not defined in 2.52 - SdFile::dateTimeCallback(dateTime); -#endif - } else { glob_script_mem.script_sd_found=0; } @@ -4987,6 +5302,13 @@ bool Xdrv10(uint8_t function) case FUNC_WEB_ADD_BUTTON: WSContentSend_P(HTTP_BTN_MENU_RULES); break; +#ifdef USE_SCRIPT_WEB_DISPLAY + case FUNC_WEB_ADD_MAIN_BUTTON: + if (bitRead(Settings.rule_enabled, 0)) { + ScriptWebShow('&'); + } + break; +#endif // USE_SCRIPT_WEB_DISPLAY case FUNC_WEB_ADD_HANDLER: Webserver->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration); Webserver->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); @@ -5014,7 +5336,7 @@ bool Xdrv10(uint8_t function) #ifdef USE_SCRIPT_WEB_DISPLAY case FUNC_WEB_SENSOR: if (bitRead(Settings.rule_enabled, 0)) { - ScriptWebShow(); + ScriptWebShow(0); } break; #endif //USE_SCRIPT_WEB_DISPLAY diff --git a/tasmota/xdrv_11_knx.ino b/tasmota/xdrv_11_knx.ino index d6f2864dc..47b62547d 100644 --- a/tasmota/xdrv_11_knx.ino +++ b/tasmota/xdrv_11_knx.ino @@ -473,19 +473,19 @@ void KNX_INIT(void) { device_param[i].show = true; } - for (uint32_t i = GPIO_SWT1; i < GPIO_SWT4 + 1; ++i) + for (uint32_t i = GPIO_SWT1; i < GPIO_SWT1 + 4; ++i) { if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1 + 8].show = true; } } - for (uint32_t i = GPIO_KEY1; i < GPIO_KEY4 + 1; ++i) + for (uint32_t i = GPIO_KEY1; i < GPIO_KEY1 + 4; ++i) { if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1 + 8].show = true; } } - for (uint32_t i = GPIO_SWT1_NP; i < GPIO_SWT4_NP + 1; ++i) + for (uint32_t i = GPIO_SWT1_NP; i < GPIO_SWT1_NP + 4; ++i) { if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1_NP + 8].show = true; } } - for (uint32_t i = GPIO_KEY1_NP; i < GPIO_KEY4_NP + 1; ++i) + for (uint32_t i = GPIO_KEY1_NP; i < GPIO_KEY1_NP + 4; ++i) { if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1_NP + 8].show = true; } } diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index f33d6ffde..51de71219 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -28,12 +28,14 @@ const char kHAssJsonSensorTypes[] PROGMEM = D_JSON_MOISTURE "|PB0.3|PB0.5|PB1|PB2.5|PB5|PB10|PM1|PM2.5|PM10|" D_JSON_POWERFACTOR "|" D_JSON_POWERUSAGE "|" D_JSON_REACTIVE_POWERUSAGE "|" D_JSON_TODAY "|" D_JSON_TOTAL "|" D_JSON_VOLTAGE "|" D_JSON_WEIGHT "|" D_JSON_YESTERDAY "|" D_JSON_CO2 "|" D_JSON_ECO2 "|" D_JSON_TVOC "|"; + const char kHAssJsonSensorUnits[] PROGMEM = "||||" "VA|%|A|Cm|Hz|%|LX|" "%|ppd|ppd|ppd|ppd|ppd|ppd|µg/m³|µg/m³|µg/m³|Cos φ|W|" "VAr|kWh|kWh|V|Kg|kWh|" "ppm|ppm|ppb|"; + const char kHAssJsonSensorDevCla[] PROGMEM = "dev_cla\":\"temperature|ic\":\"mdi:weather-rainy|dev_cla\":\"pressure|dev_cla\":\"pressure|" "dev_cla\":\"power|dev_cla\":\"battery|ic\":\"mdi:alpha-a-circle-outline|ic\":\"mdi:leak|ic\":\"mdi:current-ac|dev_cla\":\"humidity|dev_cla\":\"illuminance|" @@ -74,12 +76,12 @@ const char HASS_DISCOVER_BIN_PIR[] PROGMEM = "\"pl_on\":\"%s\"," // ON "\"off_dly\":1"; // Switchmode13 and Switchmode14 doesn't transmit an OFF state. -const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = +const char HASS_DISCOVER_BASE_LIGHT[] PROGMEM = ",\"bri_cmd_t\":\"%s\"," // cmnd/led2/Dimmer "\"bri_stat_t\":\"%s\"," // stat/led2/RESULT "\"bri_scl\":100," // 100% "\"on_cmd_type\":\"%s\"," // power on (first), power on (last), no power on (brightness) - "\"bri_val_tpl\":\"{{value_json." D_CMND_DIMMER "}}\""; + "\"bri_val_tpl\":\"{{value_json.%s}}\""; const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = ",\"rgb_cmd_t\":\"%s2\"," // cmnd/led2/Color2 @@ -132,6 +134,22 @@ const char HASS_TRIGGER_TYPE[] PROGMEM = const char kHAssTriggerType[] PROGMEM = "none|button_short_press|button_long_press|button_double_press"; +const char kHAssTriggerTypeButtons[] PROGMEM = + "|button_short_press|button_double_press|button_triple_press|button_quadruple_press|button_quintuple_press|button_long_press|"; + +const char kHAssTriggerStringButtons[] PROGMEM = + "|SINGLE|DOUBLE|TRIPLE|QUAD|PENTA|HOLD|"; + +const char kHAssError1[] PROGMEM = + "HASS: MQTT discovery failed due to too long topic or friendly name." + "Please shorten topic and friendly name. Failed to format"; + +const char kHAssError2[] PROGMEM = + "HASS: The configuration of the Relays is wrong, there is a Light that is using an index lower than the number of the selected relay.\n " + "The Relays have priority over the Lights, an incorrect order could lead to an erroneous Light control.\n " + "Please update your configuration to avoid inconsistent results.\n " + "Entities for Relays and Lights will not be available in Home Assistant until the configuration will be updated."; + uint8_t hass_init_step = 0; uint8_t hass_mode = 0; int hass_tele_period = 0; @@ -147,9 +165,7 @@ void TryResponseAppend_P(const char *format, ...) int slen = sizeof(mqtt_data) - 1 - mlen; if (dlen >= slen) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: MQTT discovery failed due to too long topic or friendly name. " - "Please shorten topic and friendly name. Failed to format(%u/%u):"), - dlen, slen); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s (%u/%u):"), kHAssError1, dlen, slen); va_start(args, format); vsnprintf_P(log_data, sizeof(log_data), format, args); AddLog(LOG_LEVEL_ERROR); @@ -169,13 +185,33 @@ void HAssAnnounceRelayLight(void) char stemp2[TOPSZ]; char stemp3[TOPSZ]; char unique_id[30]; - bool is_light = false; - bool is_topic_light = false; + + bool LightControl = light_controller.isCTRGBLinked(); // SetOption37 - Color remapping for led channels, also provides an option for allowing independent handling of RGB and white channels + bool PwmMulti = Settings.flag3.pwm_multi_channels; // SetOption68 - Multi-channel PWM instead of a single light + bool is_topic_light = false; // Switch HAss domain between Lights and Relays + bool ind_light = false; // Controls Separated Lights when SetOption37 is >= 128 + bool ct_light = false; // Controls a CT Light when SetOption37 is >= 128 + bool wt_light = false; // Controls a White Light when SetOption37 is >= 128 + bool err_flag = false; // When true it blocks the creation of entities if the order of the Relays is not correct to avoid issue with Lights + + uint8_t dimmer = 1; + uint8_t max_lights = 1; + + // If there is a special Light to be enabled and managed with SetOption68 or SetOption37 >= 128, Discovery calculates the maximum number of entities to be generated in advance + + if (PwmMulti) { max_lights = Light.subtype; } + + if (!LightControl) { + ind_light = 1; + if (!PwmMulti) { max_lights = 2;} + } + + // Lights types: 0 = LST_NONE / 1 = LST_SINGLE / 2 = LST_COLDWARM / 3 = LST_RGB / 4 = LST_RGBW / 5 = LST_RGBCW for (uint32_t i = 1; i <= MAX_RELAYS; i++) { - is_light = ((i == devices_present) && (light_type)); - is_topic_light = Settings.flag.hass_light || is_light; // SetOption30 - Enforce HAss autodiscovery as light + bool RelayX = PinUsed(GPIO_REL1 +i-1); + is_topic_light = Settings.flag.hass_light && RelayX || light_type && !RelayX; // SetOption30 - Enforce HAss autodiscovery as light mqtt_data[0] = '\0'; // Clear retained message @@ -189,76 +225,101 @@ void HAssAnnounceRelayLight(void) snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), (is_topic_light) ? "light" : "switch", unique_id); - if (Settings.flag.hass_discovery && (i <= devices_present)) - { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) - char name[33 + 2]; // friendlyname(33) + " " + index - char value_template[33]; - char prefix[TOPSZ]; - char *command_topic = stemp1; - char *state_topic = stemp2; - char *availability_topic = stemp3; + if ((i < Light.device) && !RelayX) { + err_flag = true; + AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s"), kHAssError2); + } else { + if (Settings.flag.hass_discovery && (RelayX || (Light.device > 0) && (max_lights > 0)) && !err_flag ) + { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) + char name[33 + 2]; // friendlyname(33) + " " + index + char value_template[33]; + char prefix[TOPSZ]; + char *command_topic = stemp1; + char *state_topic = stemp2; + char *availability_topic = stemp3; - if (i > MAX_FRIENDLYNAMES) { - snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i); - } else { - snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i - 1)); + if (i > MAX_FRIENDLYNAMES) { + snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i-1); + } else { + snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i-1)); + } + + GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1 + GetTopic_P(command_topic, CMND, mqtt_topic, value_template); + GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); + + #ifdef USE_LIGHT + if ((i >= Light.device) + #ifdef ESP8266 + || PWM_DIMMER == my_module_type + #endif + ) + { + if (!RelayX) { + char *brightness_command_topic = stemp1; + strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); // SetOption20 - Control power in relation to Dimmer/Color/Ct changes + char channel_num[9]; + if (PwmMulti) { // SetOption68 - Multi-channel PWM instead of a single light + snprintf_P(channel_num, sizeof(channel_num), PSTR("Channel%d"), i); + } else { + if (!LightControl) { // SetOption37 >= 128 - Color remapping for led channels, also provides an option for allowing independent handling of RGB and white channels + snprintf_P(channel_num, sizeof(channel_num), PSTR("" D_CMND_DIMMER "%d"), dimmer); + dimmer ++; + } else { + snprintf_P(channel_num, sizeof(channel_num), PSTR("" D_CMND_DIMMER "")); + } + } + GetTopic_P(brightness_command_topic, CMND, mqtt_topic, channel_num); + TryResponseAppend_P(HASS_DISCOVER_BASE_LIGHT, brightness_command_topic, state_topic, stemp3, channel_num); + } + if ((ind_light && !PwmMulti) || LightControl) { + + if (Light.subtype >= LST_RGB) { + char *rgb_command_topic = stemp1; + + GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic); + + char *effect_command_topic = stemp1; + GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic); + } + if (LST_RGBW == Light.subtype) { wt_light = true; } + if (LST_RGBCW == Light.subtype) { ct_light = true; } + } + + if ((!ind_light && ct_light) || (LST_COLDWARM == Light.subtype && + !PwmMulti && LightControl)) { + char *color_temp_command_topic = stemp1; + + GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic); + ct_light = false; + } + if ((!ind_light && wt_light) || (LST_RGBW == Light.subtype && + !PwmMulti && LightControl)) { + char *white_temp_command_topic = stemp1; + + GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic); + wt_light = false; + } + ind_light = false; + max_lights--; + } + #endif // USE_LIGHT + TryResponseAppend_P(PSTR("}")); } - GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1 - GetTopic_P(command_topic, CMND, mqtt_topic, value_template); - GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); - -#ifdef USE_LIGHT - if (is_light -#ifdef ESP8266 - || PWM_DIMMER == my_module_type -#endif - ) - { - char *brightness_command_topic = stemp1; - - GetTopic_P(brightness_command_topic, CMND, mqtt_topic, D_CMND_DIMMER); - strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); // SetOption20 - Control power in relation to Dimmer/Color/Ct changes - TryResponseAppend_P(HASS_DISCOVER_LIGHT_DIMMER, brightness_command_topic, state_topic, stemp3); - - if (Light.subtype >= LST_RGB) - { - char *rgb_command_topic = stemp1; - - GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic); - - char *effect_command_topic = stemp1; - GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic); - } - if (LST_RGBW == Light.subtype) - { - char *white_temp_command_topic = stemp1; - - GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic); - } - if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) - { - char *color_temp_command_topic = stemp1; - - GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic); - } - } -#endif // USE_LIGHT - TryResponseAppend_P(PSTR("}")); } MqttPublish(stopic, true); } } -void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t toggle, uint8_t hold) +void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t toggle, uint8_t hold, uint8_t single, uint8_t trg_start, uint8_t trg_end) { // key 0 = button // key 1 = switch @@ -266,11 +327,13 @@ void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t char stemp1[TOPSZ]; char stemp2[TOPSZ]; char unique_id[30]; + char trigger2[8]; mqtt_data[0] = '\0'; // Clear retained message - for (uint8_t i = 2; i <= 3; i++) { - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d_%s"), ESP_getChipId(), key ? "SW" : "BTN", device + 1, GetStateText(i)); + for (uint8_t i = trg_start; i <= trg_end; i++) { + GetTextIndexed(trigger2, sizeof(trigger2), i, kHAssTriggerStringButtons); + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d_%s"), ESP_getChipId(), key ? "SW" : "BTN", device + 1, key ? GetStateText(i) : trigger2); snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/device_automation/%s/config"), unique_id); if (Settings.flag.hass_discovery && present) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) @@ -289,13 +352,23 @@ void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t char param[21]; char subtype[9]; uint8_t pload = toggle; - - if ((i == 2 && toggle != 0) || (i == 3 && hold != 0)) { - if (i == 3) { pload = hold; } - GetTextIndexed(param, sizeof(param), pload, kHAssTriggerType); - snprintf_P(subtype, sizeof(subtype), PSTR("%s_%d"), key ? "switch" : "button", device + 1); - Response_P(HASS_TRIGGER_TYPE, state_topic, GetStateText(i), param, subtype, ESP_getChipId()); - } else { mqtt_data[0] = '\0'; } // Need to be cleaned again to avoid duplicate. + if (key) { + if ((i == 2 && toggle != 0) || (i == 3 && hold != 0)) { + if (i == 3) { pload = hold; } + GetTextIndexed(param, sizeof(param), pload, kHAssTriggerType); + snprintf_P(subtype, sizeof(subtype), PSTR("switch_%d"), device + 1); + Response_P(HASS_TRIGGER_TYPE, state_topic, GetStateText(i), param, subtype, ESP_getChipId()); + } else { mqtt_data[0] = '\0'; } // Need to be cleaned again to avoid duplicate + } else { + char trigger1[24]; + GetTextIndexed(trigger1, sizeof(trigger1), i, kHAssTriggerTypeButtons); + snprintf_P(subtype, sizeof(subtype), PSTR("button_%d"), device + 1); + if (i > 1 && single) { + mqtt_data[0] = '\0'; // Need to be cleaned again to avoid duplicate + } else { + Response_P(HASS_TRIGGER_TYPE, state_topic, trigger2, trigger1, subtype, ESP_getChipId()); + } + } } MqttPublish(stopic, true); } @@ -328,7 +401,7 @@ void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint GetTopic_P(state_topic, STAT, mqtt_topic, jsoname); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), SettingsText(SET_FRIENDLYNAME1), device + 1); + snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), ModuleName().c_str(), device + 1); Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); if (!pir) { TryResponseAppend_P(HASS_DISCOVER_BIN_SWITCH, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2), SettingsText(SET_STATE_TXT1)); @@ -352,9 +425,9 @@ void HAssAnnounceSwitches(void) uint8_t hold = 0; uint8_t pir = 0; - if (pin[GPIO_SWT1 + switch_index] < 99) { switch_present = 1; } + if (PinUsed(GPIO_SWT1, switch_index)) { switch_present = 1; } - if (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)) // Enable Discovery for Switches only if Switchtopic is set to a custom name + if (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)) // Enable Discovery for Switches only if SwitchTopic is set to a custom name { // switch matrix for triggers and binary sensor generation when switchtopic is set as custom (default index is 0,0 - TOGGLE, TOGGLE): @@ -408,24 +481,22 @@ void HAssAnnounceSwitches(void) case PUSHON: case PUSHON_INV: toggle = 0; - pir = 1; // Binary sensor with only ON state and automatic OFF after 1 second. + pir = 1; // Binary sensor with only ON state and automatic OFF after 1 second } } else { switch_present = 0;} - HAssAnnouncerTriggers(switch_index, switch_present, 1, toggle, hold); + HAssAnnouncerTriggers(switch_index, switch_present, 1, toggle, hold, 0, 2, 3); HAssAnnouncerBinSensors(switch_index, switch_present, dual, toggle, pir); } } - void HAssAnnounceButtons(void) { for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { uint8_t button_present = 0; - uint8_t toggle = 1; - uint8_t hold = 0; + uint8_t single = 0; #ifdef ESP8266 if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) @@ -434,48 +505,33 @@ void HAssAnnounceButtons(void) } else #endif { - if (pin[GPIO_KEY1 + button_index] < 99) { + if (PinUsed(GPIO_KEY1, button_index)) { button_present = 1; } } - // button matrix for triggers generation when buttontopic is set as custom (default TOGGLE = 1 HOLD = 0): - // N SetOption1 SetOption11 SetOption13 PRESS DOUBLE PRESS HOLD T,H - // 1 0 0 0 TOGGLE (button_short_press) NONE (toggle real relay) NONE (reset device) 1,0 - // 2 1 0 0 TOGGLE (button_short_press) NONE (toggle real relay) HOLD (button_long_press) 1,2 - // 3 0 1 0 NONE (toggle real relay) TOGGLE (button_double_press) NONE (reset device) 3,0 - // 4 1 1 0 NONE (toggle real relay) TOGGLE (button_double_press) HOLD (button_long_press) 3,2 - // 5 0 0 1 TOGGLE (button_short_press) NONE (toggle real relay) NONE (reset device) 1,0 - // 6 1 0 1 TOGGLE (button_short_press) NONE (toggle real relay) NONE (MQTT HOLD) 1,0 - // 7 0 1 1 NONE (toggle real relay) NONE (toggle real relay) NONE (reset device) 0,0 - // 8 1 1 1 NONE (toggle real relay) NONE (toggle real relay) NONE (MQTT HOLD) 0.0 + // Button matrix for triggers generation when SetOption73 is enabled: + // N SetOption1 SetOption11 SetOption13 PRESS MULTI PRESS HOLD + // 1 0 0 0 SINGLE (10 - button_short_press) DOUBLE to PENTA YES (button_long_press) + // 2 1 0 0 SINGLE (10 - button_short_press) DOUBLE to PENTA YES (button_long_press) + // 3 0 1 0 DOUBLE (11 - button_short_press) SINGLE then TRIPLE TO PENTA YES (button_long_press) + // 4 1 1 0 DOUBLE (11 - button_short_press) SINGLE then TRIPLE TO PENTA YES (button_long_press) + // 5 0 0 1 SINGLE (10 - button_short_press) NONE NONE + // 6 1 0 1 SINGLE (10 - button_short_press) NONE NONE + // 7 0 1 1 SINGLE (10 - button_short_press) NONE NONE + // 8 1 1 1 SINGLE (10 - button_short_press) NONE NONE - // Trigger types: "0 = none | 1 = button_short_press | 2 = button_long_press | 3 = button_double_press"; + // Trigger types: 10 = button_short_press | 11 = button_double_press | 12 = button_triple_press | 13 = button_quadruple_press | 14 = button_quintuple_press | 3 = button_long_press - if (Settings.flag.button_restrict) { // [SetOption1] Enable/Disable button multipress - if (!Settings.flag.button_single) { - hold = 2; // Default TOGGLE (button_short_press) + HOLD (button_long_press) trigger if [SetOption13] is OFF - } + if (!Settings.flag3.mqtt_buttons) { // Enable Buttons for discovery [SetOption73] - Decouple button from relay and send just mqtt topic + button_present = 0; + } else { + if (Settings.flag.button_single) { // [SetOption13] Immediate action on button press, just SINGLE trigger + single = 1; + } } - if (Settings.flag.button_swap) { // [SetOption11] Swap button single and double press functionality - if (!Settings.flag.button_single) { - if (!Settings.flag.button_restrict) { - hold = 0; // TOGGLE (button_double_press) and remove HOLD (button_long_press) trigger if [SetOption1] is OFF - } - toggle = 3; // TOGGLE (button_double_press) - } else {toggle = 0; hold = 0;} // [SetOption13] Immediate action on button press, no TOGGLE or HOLD triggers - } - - if (KeyTopicActive(0)) { // Enable Discovery for Buttons only if Buttontopic is set to 1 or a custom name - - if (!strcmp(SettingsText(SET_MQTT_BUTTON_TOPIC), mqtt_topic)) { - toggle = 0; // When ButtonTopic is set to 1, TOGGLE is not allowed but an HOLD trigger can be generated. - } - - } else { button_present = 0; } - - HAssAnnouncerTriggers(button_index, button_present, 0, toggle, hold); + HAssAnnouncerTriggers(button_index, button_present, 0, 0, 0, single, 1, 6); } } @@ -502,7 +558,7 @@ void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *availability_topic = stemp2; GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); - snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_FRIENDLYNAME1), sensorname, MultiSubName); + snprintf_P(name, sizeof(name), PSTR("%s %s %s"), ModuleName().c_str(), sensorname, MultiSubName); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); @@ -573,7 +629,6 @@ void HAssAnnounceSensors(void) snprintf_P(sensordata, sizeof(sensordata), PSTR("%s}"), sensordata); // {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}} // USE THE FOLLOWING LINE TO TEST JSON //snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"HX711\":{\"Weight\":[22,34,1023.4]}}")); - //snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"TX23\":{\"Speed\":{\"Act\":8.6,\"Avg\":8.2,\"Min\":0,\"Max\":15.8},\"Dir\":{\"Card\":\"SSO\",\"Deg\":157.5,\"Avg\":145.5,\"AvgCard\":\"SO\",\"Min\":112.5,\"Max\":292.5,\"Range\":180}}}")); StaticJsonBuffer<500> jsonBuffer; JsonObject &root = jsonBuffer.parseObject(sensordata); @@ -621,7 +676,7 @@ void HAssAnnounceSensors(void) } while (hass_xsns_index != 0); } -void HAssAnnounceStatusSensor(void) +void HAssAnnounceDeviceInfoAndStatusSensor(void) { char stopic[TOPSZ]; char stemp1[TOPSZ]; @@ -642,13 +697,13 @@ void HAssAnnounceStatusSensor(void) char *state_topic = stemp1; char *availability_topic = stemp2; - snprintf_P(name, sizeof(name), PSTR("%s status"), SettingsText(SET_FRIENDLYNAME1)); + snprintf_P(name, sizeof(name), PSTR("%s status"), ModuleName().c_str()); GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP_getChipId(), SettingsText(SET_FRIENDLYNAME1), + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP_getChipId(), ModuleName().c_str(), ModuleName().c_str(), my_version, my_image); TryResponseAppend_P(PSTR("}")); } @@ -657,16 +712,13 @@ void HAssAnnounceStatusSensor(void) void HAssPublishStatus(void) { - Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\"," - "\"" D_JSON_COREVERSION "\":\"" ARDUINO_CORE_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," - "\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"," - "\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d," - "\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_CMND_IPADDRESS "\":\"%s\"," - "\"" D_JSON_RSSI "\":\"%d\",\"LoadAvg\":%lu}"), - my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getSdkVersion(), ModuleName().c_str(), - GetResetReason().c_str(), GetUptime().c_str(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), - Settings.bootcount, Settings.save_flag, WiFi.localIP().toString().c_str(), - WifiGetRssiAsQuality(WiFi.RSSI()), loop_load_avg); + Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" D_CMND_MODULE " or " D_CMND_TEMPLATE"\":\"%s\"," + "\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\"," + "\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_RSSI "\":\"%d\",\"" D_JSON_SIGNAL " (dBm)""\":\"%d\"," + "\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"LoadAvg\":%lu}"), + my_version, my_image, GetBuildDateAndTime().c_str(), ModuleName().c_str(), GetResetReason().c_str(), + GetUptime().c_str(), my_hostname, WiFi.localIP().toString().c_str(), WifiGetRssiAsQuality(WiFi.RSSI()), + WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), loop_load_avg); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE)); } @@ -685,8 +737,6 @@ void HAssDiscovery(void) if (Settings.flag.hass_discovery || (1 == hass_mode)) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) - // Send info about relays and lights - HAssAnnounceRelayLight(); // Send info about buttons HAssAnnounceButtons(); @@ -697,8 +747,11 @@ void HAssDiscovery(void) // Send info about sensors HAssAnnounceSensors(); + // Send info about relays and lights + HAssAnnounceRelayLight(); + // Send info about status sensor - HAssAnnounceStatusSensor(); + HAssAnnounceDeviceInfoAndStatusSensor(); } } @@ -717,7 +770,7 @@ void HAssAnyKey(void) uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; // 0 = Button, 1 = Switch uint32_t device = XdrvMailbox.payload & 0xFF; // Device number or 1 if more Buttons than Devices - uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; // 0 = Off, 1 = On, 2 = Toggle, 3 = Hold + uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; // 0 = Off, 1 = On, 2 = Toggle, 3 = Hold, 10,11,12,13 and 14 for Button Multipress if (!key && KeyTopicActive(0)) { // Button and ButtonTopic is active device = (XdrvMailbox.payload >> 24) & 0xFF; // Button number @@ -726,19 +779,26 @@ void HAssAnyKey(void) char scommand[CMDSZ]; char sw_topic[TOPSZ]; char key_topic[TOPSZ]; + char trg_state[8]; char *tmpbtn = SettingsText(SET_MQTT_BUTTON_TOPIC); char *tmpsw = SettingsText(SET_MQTT_SWITCH_TOPIC); uint8_t evkey = 0; // Flag to select the correct topic for a trigger or a binary_sensor Format(sw_topic, tmpsw, sizeof(sw_topic)); Format(key_topic, tmpbtn, sizeof(key_topic)); - if (state == 2 || state == 3 ) { evkey = 1;} + if (state >= 2) { evkey = 1;} snprintf_P(scommand, sizeof(scommand), PSTR("%s%d%s"), (key) ? "SWITCH" : "BUTTON", device, (evkey) ? "T" : ""); char stopic[TOPSZ]; + if (state == 3) { + snprintf_P(trg_state, sizeof(trg_state), GetStateText(3)); + } else { + GetTextIndexed(trg_state, sizeof(trg_state), state -9, kHAssTriggerStringButtons); + } + GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P(S_JSON_COMMAND_SVALUE, (evkey) ? "TRIG" : PSTR(D_RSLT_STATE), GetStateText(state)); + Response_P(S_JSON_COMMAND_SVALUE, (evkey) ? "TRIG" : PSTR(D_RSLT_STATE), (key) ? GetStateText(state) : trg_state); MqttPublish(stopic); } diff --git a/tasmota/xdrv_14_mp3.ino b/tasmota/xdrv_14_mp3.ino index c28f3e693..ed6beee63 100644 --- a/tasmota/xdrv_14_mp3.ino +++ b/tasmota/xdrv_14_mp3.ino @@ -136,7 +136,7 @@ uint16_t MP3_Checksum(uint8_t *array) \*********************************************************************************************/ void MP3PlayerInit(void) { - MP3Player = new TasmotaSerial(-1, pin[GPIO_MP3_DFR562]); + MP3Player = new TasmotaSerial(-1, Pin(GPIO_MP3_DFR562)); // start serial communication fixed to 9600 baud if (MP3Player->begin(9600)) { MP3Player->flush(); @@ -232,7 +232,7 @@ bool Xdrv14(uint8_t function) { bool result = false; - if (pin[GPIO_MP3_DFR562] < 99) { + if (PinUsed(GPIO_MP3_DFR562)) { switch (function) { case FUNC_PRE_INIT: MP3PlayerInit(); // init and start communication diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino index 466c3faf1..bada16061 100644 --- a/tasmota/xdrv_16_tuyamcu.ino +++ b/tasmota/xdrv_16_tuyamcu.ino @@ -36,6 +36,7 @@ #define TUYA_CMD_SET_DP 0x06 #define TUYA_CMD_STATE 0x07 #define TUYA_CMD_QUERY_STATE 0x08 +#define TUYA_CMD_SET_TIME 0x1C #define TUYA_LOW_POWER_CMD_WIFI_STATE 0x02 #define TUYA_LOW_POWER_CMD_WIFI_RESET 0x03 @@ -539,6 +540,9 @@ void TuyaNormalPowerModePacketProcess(void) if (Tuya.buffer[6] == 0) { AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart")); Tuya.wifi_state = -2; + #ifdef USE_TUYA_TIME + TuyaSetTime(); + #endif } break; @@ -565,7 +569,7 @@ void TuyaNormalPowerModePacketProcess(void) uint8_t key1_gpio = Tuya.buffer[7]; bool key1_set = false; bool led1_set = false; - 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_LED1) led1_set = true; else if (Settings.my_gp.io[i] == GPIO_KEY1) key1_set = true; } @@ -592,9 +596,9 @@ void TuyaNormalPowerModePacketProcess(void) bool TuyaModuleSelected(void) { - if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { // fallback to hardware-serial if not explicitly selected - pin[GPIO_TUYA_TX] = 1; - pin[GPIO_TUYA_RX] = 3; + if (!PinUsed(GPIO_TUYA_RX) || !PinUsed(GPIO_TUYA_TX)) { // fallback to hardware-serial if not explicitly selected + SetPin(1, GPIO_TUYA_TX); + SetPin(3, GPIO_TUYA_RX); Settings.my_gp.io[1] = GPIO_TUYA_TX; Settings.my_gp.io[3] = GPIO_TUYA_RX; restart_flag = 2; @@ -639,7 +643,7 @@ void TuyaInit(void) { Tuya.buffer = (char*)(malloc(TUYA_BUFFER_SIZE)); if (Tuya.buffer != nullptr) { - TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 2); + TuyaSerial = new TasmotaSerial(Pin(GPIO_TUYA_RX), Pin(GPIO_TUYA_TX), 2); if (TuyaSerial->begin(9600)) { if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } // Get MCU Configuration @@ -797,6 +801,24 @@ void TuyaSetWifiLed(void) } } +#ifdef USE_TUYA_TIME +void TuyaSetTime(void) +{ + uint16_t payload_len = 8; + uint8_t payload_buffer[8]; + payload_buffer[0] = 0x01; + payload_buffer[1] = (uint8_t)RtcTime.year; + payload_buffer[2] = RtcTime.month; + payload_buffer[3] = RtcTime.day_of_month; + payload_buffer[4] = RtcTime.hour; + payload_buffer[5] = RtcTime.minute; + payload_buffer[6] = RtcTime.second; + payload_buffer[7] = RtcTime.day_of_week; + + TuyaSendCmd(TUYA_CMD_SET_TIME, payload_buffer, payload_len); +} +#endif //USE_TUYA_TIME + #ifdef USE_ENERGY_SENSOR /*********************************************************************************************\ * Energy Interface diff --git a/tasmota/xdrv_17_rcswitch.ino b/tasmota/xdrv_17_rcswitch.ino index 9cd2d14d8..c5013828c 100644 --- a/tasmota/xdrv_17_rcswitch.ino +++ b/tasmota/xdrv_17_rcswitch.ino @@ -81,12 +81,12 @@ void RfReceiveCheck(void) void RfInit(void) { - if (pin[GPIO_RFSEND] < 99) { - mySwitch.enableTransmit(pin[GPIO_RFSEND]); + if (PinUsed(GPIO_RFSEND)) { + mySwitch.enableTransmit(Pin(GPIO_RFSEND)); } - if (pin[GPIO_RFRECV] < 99) { - pinMode( pin[GPIO_RFRECV], INPUT); - mySwitch.enableReceive(pin[GPIO_RFRECV]); + if (PinUsed(GPIO_RFRECV)) { + pinMode( Pin(GPIO_RFRECV), INPUT); + mySwitch.enableReceive(Pin(GPIO_RFRECV)); } } @@ -170,15 +170,15 @@ bool Xdrv17(uint8_t function) { bool result = false; - if ((pin[GPIO_RFSEND] < 99) || (pin[GPIO_RFRECV] < 99)) { + if (PinUsed(GPIO_RFSEND) || PinUsed(GPIO_RFRECV)) { switch (function) { case FUNC_EVERY_50_MSECOND: - if (pin[GPIO_RFRECV] < 99) { + if (PinUsed(GPIO_RFRECV)) { RfReceiveCheck(); } break; case FUNC_COMMAND: - if (pin[GPIO_RFSEND] < 99) { + if (PinUsed(GPIO_RFSEND)) { result = DecodeCommand(kRfSendCommands, RfSendCommand); } break; diff --git a/tasmota/xdrv_18_armtronix_dimmers.ino b/tasmota/xdrv_18_armtronix_dimmers.ino index 4a1322b25..710697a28 100644 --- a/tasmota/xdrv_18_armtronix_dimmers.ino +++ b/tasmota/xdrv_18_armtronix_dimmers.ino @@ -97,7 +97,7 @@ void ArmtronixInit(void) Armtronix.dim_state[1] = -1; Armtronix.knob_state[0] = -1; Armtronix.knob_state[1] = -1; - ArmtronixSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + ArmtronixSerial = new TasmotaSerial(Pin(GPIO_RXD), Pin(GPIO_TXD), 2); if (ArmtronixSerial->begin(115200)) { if (ArmtronixSerial->hardwareSerial()) { ClaimSerial(); } ArmtronixSerial->println("Status"); diff --git a/tasmota/xdrv_19_ps16dz_dimmer.ino b/tasmota/xdrv_19_ps16dz_dimmer.ino index ab07289f7..d387ae05c 100644 --- a/tasmota/xdrv_19_ps16dz_dimmer.ino +++ b/tasmota/xdrv_19_ps16dz_dimmer.ino @@ -187,7 +187,7 @@ void PS16DZInit(void) { Ps16dz.rx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE)); if (Ps16dz.rx_buffer != nullptr) { - PS16DZSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + PS16DZSerial = new TasmotaSerial(Pin(GPIO_RXD), Pin(GPIO_TXD), 2); if (PS16DZSerial->begin(19200)) { if (PS16DZSerial->hardwareSerial()) { ClaimSerial(); } } diff --git a/tasmota/xdrv_21_wemo.ino b/tasmota/xdrv_21_wemo.ino index eef509e2a..fb123d4ab 100644 --- a/tasmota/xdrv_21_wemo.ino +++ b/tasmota/xdrv_21_wemo.ino @@ -259,10 +259,10 @@ bool Xdrv21(uint8_t function) if (devices_present && (EMUL_WEMO == Settings.flag2.emulation)) { switch (function) { case FUNC_WEB_ADD_HANDLER: - Webserver->on("/upnp/control/basicevent1", HTTP_POST, HandleUpnpEvent); - Webserver->on("/eventservice.xml", HandleUpnpService); - Webserver->on("/metainfoservice.xml", HandleUpnpMetaService); - Webserver->on("/setup.xml", HandleUpnpSetupWemo); + Webserver->on(F("/upnp/control/basicevent1"), HTTP_POST, HandleUpnpEvent); + Webserver->on(F("/eventservice.xml"), HandleUpnpService); + Webserver->on(F("/metainfoservice.xml"), HandleUpnpMetaService); + Webserver->on(F("/setup.xml"), HandleUpnpSetupWemo); break; } } diff --git a/tasmota/xdrv_23_zigbee_4_persistence.ino b/tasmota/xdrv_23_zigbee_4_persistence.ino index d7b0d0f59..78cb79bec 100644 --- a/tasmota/xdrv_23_zigbee_4_persistence.ino +++ b/tasmota/xdrv_23_zigbee_4_persistence.ino @@ -45,7 +45,7 @@ // str - Manuf (null terminated C string, 32 chars max) // str - FriendlyName (null terminated C string, 32 chars max) // reserved for extensions -// -- V2 -- +// -- V2 -- // int8_t - bulbtype // Memory footprint @@ -61,7 +61,7 @@ public: uint32_t name; // simple 4 letters name. Currently 'skey', 'crt ', 'crt1', 'crt2' uint16_t len; // len of object uint16_t reserved; // align on 4 bytes boundary -}; +}; const static uint32_t ZIGB_NAME = 0x3167697A; // 'zig1' little endian const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); // 2040 @@ -86,7 +86,7 @@ const uint16_t Z_ClusterNumber[] PROGMEM = { // convert a 1 byte cluster code to the actual cluster number uint16_t fromClusterCode(uint8_t c) { - if (c >= sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0])) { + if (c >= ARRAY_SIZE(Z_ClusterNumber)) { return 0xFFFF; // invalid } return pgm_read_word(&Z_ClusterNumber[c]); @@ -94,7 +94,7 @@ uint16_t fromClusterCode(uint8_t c) { // convert a cluster number to 1 byte, or 0xFF if not in table uint8_t toClusterCode(uint16_t c) { - for (uint32_t i = 0; i < sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0]); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(Z_ClusterNumber); i++) { if (c == pgm_read_word(&Z_ClusterNumber[i])) { return i; } @@ -202,8 +202,8 @@ void hydrateDevices(const SBuffer &buf) { for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) { uint32_t dev_record_len = buf.get8(k); -// AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device %d Before Memory = %d // DIFF %d // record_len %d"), i, ESP.getFreeHeap(), before - ESP.getFreeHeap(), dev_record_len); -// before = ESP.getFreeHeap(); +// AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device %d Before Memory = %d // DIFF %d // record_len %d"), i, ESP_getFreeHeap(), before - ESP_getFreeHeap(), dev_record_len); +// before = ESP_getFreeHeap(); SBuffer buf_d = buf.subBuffer(k, dev_record_len); @@ -238,7 +238,7 @@ void hydrateDevices(const SBuffer &buf) { // ignore } } -//AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device 0x%04X Memory3.shrink = %d"), shortaddr, ESP.getFreeHeap()); +//AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device 0x%04X Memory3.shrink = %d"), shortaddr, ESP_getFreeHeap()); // parse 3 strings char empty[] = ""; @@ -269,14 +269,14 @@ void hydrateDevices(const SBuffer &buf) { // next iteration k += dev_record_len; -//AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device %d After Memory = %d"), i, ESP.getFreeHeap()); +//AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device %d After Memory = %d"), i, ESP_getFreeHeap()); } } void loadZigbeeDevices(void) { z_flashdata_t flashdata; memcpy_P(&flashdata, z_dev_start, sizeof(z_flashdata_t)); -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Memory %d"), ESP.getFreeHeap()); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Memory %d"), ESP_getFreeHeap()); AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len); // Check the signature @@ -291,7 +291,7 @@ void loadZigbeeDevices(void) { } else { AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No zigbee devices data in Flash")); } -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Memory %d"), ESP.getFreeHeap()); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Memory %d"), ESP_getFreeHeap()); } void saveZigbeeDevices(void) { @@ -341,7 +341,7 @@ void eraseZigbeeDevices(void) { // Fill the Zigbee area with 0xFF memset(spi_buffer + z_block_offset, 0xFF, z_block_len); - + // buffer is now ready, write it back if (ESP.flashEraseSector(z_spi_start_sector)) { ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index 67ff8993f..a0c59f20b 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -52,7 +52,7 @@ uint8_t Z_getDatatypeLen(uint8_t t) { if ( ((t >= 0x08) && (t <= 0x0F)) || // data8 - data64 ((t >= 0x18) && (t <= 0x2F)) ) { // map/uint/int return (t & 0x07) + 1; - } + } switch (t) { case Zbool: case Zenum8: @@ -574,7 +574,7 @@ typedef struct Z_AttributeConverter { enum Cx_cluster_short { Cx0000, Cx0001, Cx0002, Cx0003, Cx0004, Cx0005, Cx0006, Cx0007, - Cx0008, Cx0009, Cx000A, Cx000B, Cx000C, Cx000D, Cx000E, Cx000F, + Cx0008, Cx0009, Cx000A, Cx000B, Cx000C, Cx000D, Cx000E, Cx000F, Cx0010, Cx0011, Cx0012, Cx0013, Cx0014, Cx001A, Cx0020, Cx0100, Cx0101, Cx0102, Cx0300, Cx0400, Cx0401, Cx0402, Cx0403, Cx0404, Cx0405, Cx0406, Cx0B01, Cx0B05, @@ -582,14 +582,14 @@ enum Cx_cluster_short { const uint16_t Cx_cluster[] PROGMEM = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x001A, 0x0020, 0x0100, 0x0101, 0x0102, 0x0300, 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0B01, 0x0B05, }; uint16_t CxToCluster(uint8_t cx) { - if (cx < sizeof(Cx_cluster)/sizeof(Cx_cluster[0])) { + if (cx < ARRAY_SIZE(Cx_cluster)) { return pgm_read_word(&Cx_cluster[cx]); } return 0xFFFF; @@ -909,7 +909,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { { Zuint16, Cx0101, 0x0503, Z(AqaraVibrationsOrAngle), &Z_Copy }, { Zuint32, Cx0101, 0x0505, Z(AqaraVibration505), &Z_Copy }, { Zuint48, Cx0101, 0x0508, Z(AqaraAccelerometer), &Z_AqaraVibration }, - + // Window Covering cluster { Zenum8, Cx0102, 0x0000, Z(WindowCoveringType), &Z_Copy }, { Zuint16, Cx0102, 0x0001, Z(PhysicalClosedLimitLift),&Z_Copy }, @@ -975,7 +975,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { // Illuminance Level Sensing cluster { Zenum8, Cx0401, 0x0000, Z(IlluminanceLevelStatus), &Z_Copy }, // Illuminance (in Lux) { Zenum8, Cx0401, 0x0001, Z(IlluminanceLightSensorType), &Z_Copy }, // LightSensorType - { Zuint16, Cx0401, 0x0010, Z(IlluminanceTargetLevel), &Z_Copy }, // + { Zuint16, Cx0401, 0x0010, Z(IlluminanceTargetLevel), &Z_Copy }, // { Zunk, Cx0401, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values // Temperature Measurement cluster diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index a73c47c80..51a5af99a 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -420,7 +420,7 @@ ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) // Closing the Permit Join ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) - + ZI_LABEL(ZIGBEE_LABEL_READY) ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted) ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted) @@ -491,7 +491,7 @@ void ZigbeeGotoLabel(uint8_t label) { uint8_t cur_d8 = 0; uint8_t cur_instr_len = 1; // size of current instruction in words - for (uint32_t i = 0; i < sizeof(zb_prog)/sizeof(zb_prog[0]); i += cur_instr_len) { + for (uint32_t i = 0; i < ARRAY_SIZE(zb_prog); i += cur_instr_len) { const Zigbee_Instruction *cur_instr_line = &zb_prog[i]; cur_instr = pgm_read_byte(&cur_instr_line->i.i); cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); @@ -550,7 +550,7 @@ void ZigbeeStateMachine_Run(void) { zigbee.recv_until = false; zigbee.state_no_timeout = false; // reset the no_timeout for next instruction - if (zigbee.pc > (sizeof(zb_prog)/sizeof(zb_prog[0]))) { + if (zigbee.pc > ARRAY_SIZE(zb_prog)) { AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Invalid pc: %d, aborting"), zigbee.pc); zigbee.pc = -1; } @@ -626,10 +626,8 @@ void ZigbeeStateMachine_Run(void) { case ZGB_INSTR_MQTT_STATE: { const char *f_msg = (const char*) cur_ptr1; - char buf[strlen_P(f_msg) + 1]; - strcpy_P(buf, f_msg); Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":%d,\"Message\":\"%s\"}}"), - cur_d8, buf); + cur_d8, f_msg); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); XdrvRulesProcess(); } diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 86c63eb5b..983c76cf3 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -160,28 +160,28 @@ void ZigbeeInit(void) // update commands with the current settings Z_UpdateConfig(Settings.zb_channel, Settings.zb_pan_id, Settings.zb_ext_panid, Settings.zb_precfgkey_l, Settings.zb_precfgkey_h); -// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem1 = %d"), ESP.getFreeHeap()); +// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem1 = %d"), ESP_getFreeHeap()); zigbee.active = false; - if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]); + if (PinUsed(GPIO_ZIGBEE_RX) && PinUsed(GPIO_ZIGBEE_TX)) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), Pin(GPIO_ZIGBEE_RX), Pin(GPIO_ZIGBEE_TX)); // if seriallog_level is 0, we allow GPIO 13/15 to switch to Hardware Serial - ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], seriallog_level ? 1 : 2, 0, 256); // set a receive buffer of 256 bytes + ZigbeeSerial = new TasmotaSerial(Pin(GPIO_ZIGBEE_RX), Pin(GPIO_ZIGBEE_TX), seriallog_level ? 1 : 2, 0, 256); // set a receive buffer of 256 bytes ZigbeeSerial->begin(115200); if (ZigbeeSerial->hardwareSerial()) { ClaimSerial(); uint32_t aligned_buffer = ((uint32_t)serial_in_buffer + 3) & ~3; zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer) - 3, (char*) aligned_buffer); } else { -// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem2 = %d"), ESP.getFreeHeap()); +// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem2 = %d"), ESP_getFreeHeap()); zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); -// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem3 = %d"), ESP.getFreeHeap()); +// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem3 = %d"), ESP_getFreeHeap()); } zigbee.active = true; zigbee.init_phase = true; // start the state machine zigbee.state_machine = true; // start the state machine ZigbeeSerial->flush(); } -// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem9 = %d"), ESP.getFreeHeap()); +// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem9 = %d"), ESP_getFreeHeap()); } /*********************************************************************************************\ @@ -310,7 +310,7 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) { // Returns: None // void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) { - + SBuffer buf(32+len); buf.add8(Z_SREQ | Z_AF); // 24 buf.add8(AF_DATA_REQUEST_EXT); // 02 @@ -345,7 +345,7 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI } /********************************************************************************************/ -// +// // High-level function // Send a command specified as an HEX string for the workload. // The target endpoint is computed if zero, i.e. sent to the first known endpoint of the device. @@ -1016,7 +1016,7 @@ void CmndZbStatus(void) { if (XdrvMailbox.payload > 0) { if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } } - + String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr); Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str()); } diff --git a/tasmota/xdrv_24_buzzer.ino b/tasmota/xdrv_24_buzzer.ino index 5e582d7db..f158f3cd9 100644 --- a/tasmota/xdrv_24_buzzer.ino +++ b/tasmota/xdrv_24_buzzer.ino @@ -41,7 +41,7 @@ struct BUZZER { void BuzzerOff(void) { - DigitalWrite(GPIO_BUZZER, Buzzer.inverted); // Buzzer Off + DigitalWrite(GPIO_BUZZER, 0, Buzzer.inverted); // Buzzer Off } //void BuzzerBeep(uint32_t count = 1, uint32_t on = 1, uint32_t off = 1, uint32_t tune = 0, uint32_t mode = 0); @@ -81,7 +81,7 @@ void BuzzerSetStateToLed(uint32_t state) { if (Buzzer.enable && (2 == Buzzer.mode)) { Buzzer.state = (state != 0); - DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); + DigitalWrite(GPIO_BUZZER, 0, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); } } @@ -111,8 +111,8 @@ bool BuzzerPinState(void) void BuzzerInit(void) { - if (pin[GPIO_BUZZER] < 99) { - pinMode(pin[GPIO_BUZZER], OUTPUT); + if (PinUsed(GPIO_BUZZER)) { + pinMode(Pin(GPIO_BUZZER), OUTPUT); BuzzerOff(); } else { Buzzer.active = false; @@ -140,7 +140,7 @@ void BuzzerEvery100mSec(void) Buzzer.duration = Buzzer.set[Buzzer.state]; } } - DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); + DigitalWrite(GPIO_BUZZER, 0, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); } else { Buzzer.enable = false; } diff --git a/tasmota/xdrv_25_A4988_Stepper.ino b/tasmota/xdrv_25_A4988_Stepper.ino index d6456f2a1..48f559314 100644 --- a/tasmota/xdrv_25_A4988_Stepper.ino +++ b/tasmota/xdrv_25_A4988_Stepper.ino @@ -27,12 +27,12 @@ #include -short A4988_dir_pin = pin[GPIO_MAX]; -short A4988_stp_pin = pin[GPIO_MAX]; -short A4988_ms1_pin = pin[GPIO_MAX]; -short A4988_ms2_pin = pin[GPIO_MAX]; -short A4988_ms3_pin = pin[GPIO_MAX]; -short A4988_ena_pin = pin[GPIO_MAX]; +short A4988_dir_pin = 0; +short A4988_stp_pin = 0; +short A4988_ms1_pin = 0; +short A4988_ms2_pin = 0; +short A4988_ms3_pin = 0; +short A4988_ena_pin = 0; int A4988_spr = 0; float A4988_rpm = 0; short A4988_mis = 0; @@ -41,12 +41,12 @@ A4988_Stepper* myA4988 = nullptr; void A4988Init(void) { - A4988_dir_pin = pin[GPIO_A4988_DIR]; - A4988_stp_pin = pin[GPIO_A4988_STP]; - A4988_ena_pin = pin[GPIO_A4988_ENA]; - A4988_ms1_pin = pin[GPIO_A4988_MS1]; - A4988_ms2_pin = pin[GPIO_A4988_MS2]; - A4988_ms3_pin = pin[GPIO_A4988_MS3]; + A4988_dir_pin = Pin(GPIO_A4988_DIR); + A4988_stp_pin = Pin(GPIO_A4988_STP); + A4988_ena_pin = Pin(GPIO_A4988_ENA); + A4988_ms1_pin = Pin(GPIO_A4988_MS1); + A4988_ms2_pin = Pin(GPIO_A4988_MS2); + A4988_ms3_pin = Pin(GPIO_A4988_MS3); A4988_spr = 200; A4988_rpm = 30; A4988_mis = 1; @@ -93,7 +93,7 @@ void CmndDoTurn(void) { } void CmndSetMIS(void) { - if ((pin[GPIO_A4988_MS1] < 99) && (pin[GPIO_A4988_MS2] < 99) && (pin[GPIO_A4988_MS3] < 99) && (XdrvMailbox.data_len > 0)) { + if (PinUsed(GPIO_A4988_MS1) && PinUsed(GPIO_A4988_MS2) && PinUsed(GPIO_A4988_MS3) && (XdrvMailbox.data_len > 0)) { short newMIS = strtoul(XdrvMailbox.data,nullptr,10); myA4988->setMIS(newMIS); ResponseCmndDone(); @@ -122,7 +122,7 @@ void CmndSetRPM(void) { bool Xdrv25(uint8_t function) { bool result = false; - if ((pin[GPIO_A4988_DIR] < 99) && (pin[GPIO_A4988_STP] < 99)) { + if (PinUsed(GPIO_A4988_DIR) && PinUsed(GPIO_A4988_STP)) { switch (function) { case FUNC_INIT: A4988Init(); diff --git a/tasmota/xdrv_26_ariluxrf.ino b/tasmota/xdrv_26_ariluxrf.ino index 2a6c73eaf..5027dfdcb 100644 --- a/tasmota/xdrv_26_ariluxrf.ino +++ b/tasmota/xdrv_26_ariluxrf.ino @@ -147,7 +147,7 @@ void AriluxRfHandler(void) void AriluxRfInit(void) { - if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { + if (PinUsed(GPIO_ARIRFRCV) && PinUsed(GPIO_ARIRFSEL)) { if (Settings.last_module != Settings.module) { Settings.rf_code[1][6] = 0; Settings.rf_code[1][7] = 0; @@ -155,16 +155,16 @@ void AriluxRfInit(void) } Arilux.rf_received_value = 0; - digitalWrite(pin[GPIO_ARIRFSEL], 0); // Turn on RF - attachInterrupt(pin[GPIO_ARIRFRCV], AriluxRfInterrupt, CHANGE); + digitalWrite(Pin(GPIO_ARIRFSEL), 0); // Turn on RF + attachInterrupt(Pin(GPIO_ARIRFRCV), AriluxRfInterrupt, CHANGE); } } void AriluxRfDisable(void) { - if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { - detachInterrupt(pin[GPIO_ARIRFRCV]); - digitalWrite(pin[GPIO_ARIRFSEL], 1); // Turn off RF + if (PinUsed(GPIO_ARIRFRCV) && PinUsed(GPIO_ARIRFSEL)) { + detachInterrupt(Pin(GPIO_ARIRFRCV)); + digitalWrite(Pin(GPIO_ARIRFSEL), 1); // Turn off RF } } @@ -178,7 +178,7 @@ bool Xdrv26(uint8_t function) switch (function) { case FUNC_EVERY_50_MSECOND: - if (pin[GPIO_ARIRFRCV] < 99) { AriluxRfHandler(); } + if (PinUsed(GPIO_ARIRFRCV)) { AriluxRfHandler(); } break; case FUNC_EVERY_SECOND: if (10 == uptime) { AriluxRfInit(); } // Needs rest before enabling RF interrupts diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index f94464cad..22cee4fe0 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -39,14 +39,14 @@ enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_TOGGLE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|" D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" - D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" + D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|" D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPPOSITION; void (* const ShutterCommand[])(void) PROGMEM = { &CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterStop, &CmndShutterPosition, &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, - &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, + &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons, &CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopPosition}; @@ -97,7 +97,7 @@ void ShutterRtc50mS(void) Shutter.pwm_frequency[i] += Shutter.accelerator[i]; Shutter.pwm_frequency[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i],Shutter.pwm_frequency[i])); analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(pin[GPIO_PWM1+i], 50); + analogWrite(Pin(GPIO_PWM1, i), 50); } } } @@ -201,12 +201,12 @@ void ShutterInit(void) } } else { Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; - if ((pin[GPIO_PWM1+i] < 99) && (pin[GPIO_CNTR1+i] < 99)) { + if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) { Shutter.mode = SHT_OFF_ON__OPEN_CLOSE_STEPPER; Shutter.pwm_frequency[i] = 0; Shutter.accelerator[i] = 0; analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(pin[GPIO_PWM1+i], 50); + analogWrite(Pin(GPIO_PWM1, i), 50); } } @@ -255,18 +255,24 @@ void ShutterInit(void) } } -void ShutterReportPosition(bool always) +void ShutterReportPosition(bool always, uint32_t index) { Response_P(PSTR("{")); rules_flag.shutter_moving = 0; - for (uint32_t i = 0; i < shutters_present; i++) { + uint32_t i = 0; + uint32_t n = shutters_present; + if( index != MAX_SHUTTERS) { + i = index; + n = index+1; + } + for (i; i < n; i++) { //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d"), i+1,Shutter.real_position[i]); uint32_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i); if (Shutter.direction[i] != 0) { rules_flag.shutter_moving = 1; ShutterLogPos(i); } - if (i) { ResponseAppend_P(PSTR(",")); } + if (i && index == MAX_SHUTTERS) { ResponseAppend_P(PSTR(",")); } uint32_t target = ShutterRealToPercentPosition(Shutter.target_position[i], i); ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter.direction[i],(Settings.shutter_options[i] & 1) ? 100-target : target ); } @@ -296,16 +302,16 @@ void ShutterUpdatePosition(void) for (uint32_t i = 0; i < shutters_present; i++) { if (Shutter.direction[i] != 0) { int32_t stop_position_delta = 20; - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - // Calculate position with counter. Much more accurate and no need for motordelay workaround - // adding some steps to stop early - Shutter.real_position[i] = ShutterCounterBasedPosition(i); - if (!Shutter.start_reported) { - ShutterReportPosition(true); - XdrvRulesProcess(); - Shutter.start_reported = 1; - } + // Calculate position with counter. Much more accurate and no need for motordelay workaround + // adding some steps to stop early + Shutter.real_position[i] = ShutterCounterBasedPosition(i); + if (!Shutter.start_reported) { + ShutterReportPosition(true, i); + XdrvRulesProcess(); + Shutter.start_reported = 1; + } + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i]; int32_t max_freq_change_per_sec = Shutter.max_pwm_frequency*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); int32_t min_runtime_ms = Shutter.pwm_frequency[i]*1000 / max_freq_change_per_sec; @@ -352,13 +358,13 @@ void ShutterUpdatePosition(void) Shutter.accelerator[i] = 0; Shutter.pwm_frequency[i] = Shutter.pwm_frequency[i] > 250 ? 250 : Shutter.pwm_frequency[i]; analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(pin[GPIO_PWM1+i], 50); + analogWrite(Pin(GPIO_PWM1, i), 50); Shutter.pwm_frequency[i] = 0; analogWriteFreq(Shutter.pwm_frequency[i]); while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { delay(1); } - analogWrite(pin[GPIO_PWM1+i], 0); + analogWrite(Pin(GPIO_PWM1, i), 0); Shutter.real_position[i] = ShutterCounterBasedPosition(i); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]); @@ -395,7 +401,7 @@ void ShutterUpdatePosition(void) MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN Shutter.direction[i] = 0; - ShutterReportPosition(true); + ShutterReportPosition(true, i); rules_flag.shutter_moved = 1; XdrvRulesProcess(); } @@ -421,7 +427,7 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { Shutter.pwm_frequency[i] = 0; analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(pin[GPIO_PWM1+i], 0); + analogWrite(Pin(GPIO_PWM1, i), 0); RtcSettings.pulse_counter[i] = 0; Shutter.accelerator[i] = Shutter.max_pwm_frequency / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Ramp up: %d"), Shutter.accelerator[i]); @@ -450,10 +456,10 @@ void ShutterWaitForMotorStop(uint32_t i) Shutter.pwm_frequency[i] = tmax(Shutter.pwm_frequency[i]-((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) , 0); //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld"), Shutter.pwm_frequency[i]); analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(pin[GPIO_PWM1+i], 50); + analogWrite(Pin(GPIO_PWM1, i), 50); delay(50); } - analogWrite(pin[GPIO_PWM1+i], 0); + analogWrite(Pin(GPIO_PWM1, i), 0); Shutter.real_position[i] = ShutterCounterBasedPosition(i); } else { ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); @@ -645,7 +651,7 @@ void ShutterButtonHandler(void) // 5x..7x && no SetOption1 (0) checked above // simultaneous or stand alone button press 5x, 6x, 7x detected char scmnd[20]; - GetTextIndexed(scmnd, sizeof(scmnd), press_index -3, kCommands); + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); ExecuteCommand(scmnd, SRC_BUTTON); return; } else if ((buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) || ((shutter_index_num_buttons==1) && (buttonState == SHT_PRESSED_EXT_HOLD))){ @@ -910,13 +916,13 @@ void CmndShutterPosition(void) } } else { target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index); - ShutterReportPosition(true); + ShutterReportPosition(true, index); } XdrvMailbox.index = index +1; // Fix random index for ShutterClose if (XdrvMailbox.command) ResponseCmndIdxNumber((Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent); } else { - ShutterReportPosition(true); + ShutterReportPosition(true, MAX_SHUTTERS); if (XdrvMailbox.command) ResponseCmndIdxChar("Locked"); } @@ -1164,6 +1170,16 @@ void CmndShutterSetClose(void) } } +void CmndShutterSetOpen(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + Shutter.real_position[XdrvMailbox.index -1] = Shutter.open_max[XdrvMailbox.index -1]; + ShutterStartInit(XdrvMailbox.index -1, 0, Shutter.open_max[XdrvMailbox.index -1]); + Settings.shutter_position[XdrvMailbox.index -1] = 100; + ResponseCmndIdxChar(D_CONFIGURATION_RESET); + } +} + void CmndShutterInvert(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { @@ -1261,7 +1277,7 @@ bool Xdrv27(uint8_t function) break; case FUNC_EVERY_SECOND: //case FUNC_EVERY_250_MSECOND: - ShutterReportPosition(false); + ShutterReportPosition(false, MAX_SHUTTERS); break; case FUNC_COMMAND: diff --git a/tasmota/xdrv_29_deepsleep.ino b/tasmota/xdrv_29_deepsleep.ino index 69366cdd8..4ac0531db 100644 --- a/tasmota/xdrv_29_deepsleep.ino +++ b/tasmota/xdrv_29_deepsleep.ino @@ -54,9 +54,9 @@ bool DeepSleepEnabled(void) return false; // Disabled } - if (pin[GPIO_DEEPSLEEP] < 99) { - pinMode(pin[GPIO_DEEPSLEEP], INPUT_PULLUP); - return (digitalRead(pin[GPIO_DEEPSLEEP])); // Disable DeepSleep if user holds pin GPIO_DEEPSLEEP low + if (PinUsed(GPIO_DEEPSLEEP)) { + pinMode(Pin(GPIO_DEEPSLEEP), INPUT_PULLUP); + return (digitalRead(Pin(GPIO_DEEPSLEEP))); // Disable DeepSleep if user holds pin GPIO_DEEPSLEEP low } return true; // Enabled diff --git a/tasmota/xdrv_30_exs_dimmer.ino b/tasmota/xdrv_30_exs_dimmer.ino index 765fba1c6..485112977 100644 --- a/tasmota/xdrv_30_exs_dimmer.ino +++ b/tasmota/xdrv_30_exs_dimmer.ino @@ -395,11 +395,11 @@ void EsxMcuStart(void) int retries = 3; #ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), pin[GPIO_EXS_ENABLE]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), Pin(GPIO_EXS_ENABLE)); #endif - pinMode(pin[GPIO_EXS_ENABLE], OUTPUT); - digitalWrite(pin[GPIO_EXS_ENABLE], LOW); + pinMode(Pin(GPIO_EXS_ENABLE), OUTPUT); + digitalWrite(Pin(GPIO_EXS_ENABLE), LOW); delay(1); // wait 1ms fot the MCU to come online @@ -413,13 +413,13 @@ void EsxMcuStart(void) void ExsInit(void) { #ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), pin[GPIO_TXD], pin[GPIO_RXD]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), Pin(GPIO_TXD), Pin(GPIO_RXD)); #endif Exs.buffer = (uint8_t *)malloc(EXS_BUFFER_SIZE); if (Exs.buffer != nullptr) { - ExsSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + ExsSerial = new TasmotaSerial(Pin(GPIO_RXD), Pin(GPIO_TXD), 2); if (ExsSerial->begin(9600)) { if (ExsSerial->hardwareSerial()) diff --git a/tasmota/xdrv_31_tasmota_slave.ino b/tasmota/xdrv_31_tasmota_slave.ino index a30e05bd3..38306a791 100644 --- a/tasmota/xdrv_31_tasmota_slave.ino +++ b/tasmota/xdrv_31_tasmota_slave.ino @@ -219,11 +219,11 @@ uint8_t TasmotaSlave_UpdateInit(void) void TasmotaSlave_Reset(void) { if (TSlave.SerialEnabled) { - digitalWrite(pin[GPIO_TASMOTASLAVE_RST], !TSlave.inverted); + digitalWrite(Pin(GPIO_TASMOTASLAVE_RST), !TSlave.inverted); delay(1); - digitalWrite(pin[GPIO_TASMOTASLAVE_RST], TSlave.inverted); + digitalWrite(Pin(GPIO_TASMOTASLAVE_RST), TSlave.inverted); delay(1); - digitalWrite(pin[GPIO_TASMOTASLAVE_RST], !TSlave.inverted); + digitalWrite(Pin(GPIO_TASMOTASLAVE_RST), !TSlave.inverted); delay(5); } } @@ -437,31 +437,33 @@ void TasmotaSlave_Init(void) return; } if (!TSlave.SerialEnabled) { - if ((pin[GPIO_TASMOTASLAVE_RXD] < 99) && (pin[GPIO_TASMOTASLAVE_TXD] < 99) && - ((pin[GPIO_TASMOTASLAVE_RST] < 99) || (pin[GPIO_TASMOTASLAVE_RST_INV] < 99))) { - TasmotaSlave_Serial = new TasmotaSerial(pin[GPIO_TASMOTASLAVE_RXD], pin[GPIO_TASMOTASLAVE_TXD], 1, 0, 200); + if (PinUsed(GPIO_TASMOTASLAVE_RXD) && PinUsed(GPIO_TASMOTASLAVE_TXD) && + (PinUsed(GPIO_TASMOTASLAVE_RST) || PinUsed(GPIO_TASMOTASLAVE_RST_INV))) { + TasmotaSlave_Serial = new TasmotaSerial(Pin(GPIO_TASMOTASLAVE_RXD), Pin(GPIO_TASMOTASLAVE_TXD), 1, 0, 200); if (TasmotaSlave_Serial->begin(USE_TASMOTA_SLAVE_SERIAL_SPEED)) { if (TasmotaSlave_Serial->hardwareSerial()) { ClaimSerial(); } - TasmotaSlave_Serial->setTimeout(50); - if (pin[GPIO_TASMOTASLAVE_RST_INV] < 99) { - pin[GPIO_TASMOTASLAVE_RST] = pin[GPIO_TASMOTASLAVE_RST_INV]; - pin[GPIO_TASMOTASLAVE_RST_INV] = 99; + TasmotaSlave_Serial->setTimeout(100); // Theo 20200502 - increase from 50 + if (PinUsed(GPIO_TASMOTASLAVE_RST_INV)) { + SetPin(Pin(GPIO_TASMOTASLAVE_RST_INV), GPIO_TASMOTASLAVE_RST); TSlave.inverted = HIGH; } - pinMode(pin[GPIO_TASMOTASLAVE_RST], OUTPUT); + pinMode(Pin(GPIO_TASMOTASLAVE_RST), OUTPUT); TSlave.SerialEnabled = true; TasmotaSlave_Reset(); AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Enabled")); } } } - if (TSlave.SerialEnabled) { // All go for hardware now we need to detect features if there are any + if (TSlave.SerialEnabled) { // All go for hardware now we need to detect features if there are any TasmotaSlave_sendCmnd(CMND_FEATURES, 0); - char buffer[32]; + char buffer[32] = { 0 }; TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)); uint8_t len = TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)); + + if (len) { AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)buffer, len); } // Theo 20200502 - DMP: 99 17 34 01 02 00 00 00 + memcpy(&TSlaveSettings, &buffer, sizeof(TSlaveSettings)); if (20191129 == TSlaveSettings.features_version) { TSlave.type = true; @@ -495,6 +497,9 @@ void TasmotaSlave_sendCmnd(uint8_t cmnd, uint8_t param) buffer[0] = CMND_START; memcpy(&buffer[1], &TSlaveCommand, sizeof(TSlaveCommand)); buffer[sizeof(TSlaveCommand)+1] = CMND_END; + + TasmotaSlave_Serial->flush(); // Theo 20200502 + for (uint8_t ca = 0; ca < sizeof(buffer); ca++) { TasmotaSlave_Serial->write(buffer[ca]); } diff --git a/tasmota/xdrv_33_nrf24l01.ino b/tasmota/xdrv_33_nrf24l01.ino index b6eecce32..442bcb4db 100644 --- a/tasmota/xdrv_33_nrf24l01.ino +++ b/tasmota/xdrv_33_nrf24l01.ino @@ -60,7 +60,7 @@ RF24 NRF24radio; bool NRF24initRadio() { - NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); + NRF24radio.begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_DC)); NRF24radio.powerUp(); if(NRF24radio.isChipConnected()){ @@ -73,7 +73,7 @@ bool NRF24initRadio() bool NRF24Detect(void) { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_DC]<99)){ + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_DC)) { SPI.pins(SCK,MOSI,MISO,-1); if(NRF24initRadio()){ NRF24.chipType = 32; // SPACE diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index 1f04db8c6..fd7a9d86b 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -40,7 +40,7 @@ void (* const PWMDimmerCommand[])(void) PROGMEM = { #ifdef USE_PWM_DIMMER_REMOTE struct remote_pwm_dimmer { - power_t power; + bool power_on; uint8_t bri_power_on; uint8_t bri_preset_low; uint8_t bri_preset_high; @@ -94,7 +94,7 @@ void PWMModulePreInit(void) PWMDimmerSetPoweredOffLed(); // The relay initializes to on. If the power is supposed to be off, turn the relay off. - if (!power && pin[GPIO_REL1] < 99) digitalWrite(pin[GPIO_REL1], bitRead(rel_inverted, 0) ? 1 : 0); + if (!power && PinUsed(GPIO_REL1)) digitalWrite(Pin(GPIO_REL1), bitRead(rel_inverted, 0) ? 1 : 0); #ifdef USE_PWM_DIMMER_REMOTE // If remote device mode is enabled, set the device group count to the number of buttons @@ -104,7 +104,7 @@ void PWMModulePreInit(void) device_group_count = 0; for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { - if (pin[GPIO_KEY1 + button_index] < 99) device_group_count++; + if (PinUsed(GPIO_KEY1, button_index)) device_group_count++; } remote_pwm_dimmer_count = device_group_count - 1; @@ -156,16 +156,16 @@ void PWMDimmerSetBrightnessLeds(int32_t operation) void PWMDimmerSetPoweredOffLed(void) { // Set the powered-off LED state. - if (pin[GPIO_LEDLNK] < 99) { + if (PinUsed(GPIO_LEDLNK)) { bool power_off_led_on = !power && Settings.flag4.powered_off_led; if (ledlnk_inverted) power_off_led_on ^= 1; - digitalWrite(pin[GPIO_LEDLNK], power_off_led_on); + digitalWrite(Pin(GPIO_LEDLNK), power_off_led_on); } } void PWMDimmerSetPower(void) { - DigitalWrite(GPIO_REL1, bitRead(rel_inverted, 0) ? !power : power); + DigitalWrite(GPIO_REL1, 0, bitRead(rel_inverted, 0) ? !power : power); PWMDimmerSetBrightnessLeds(0); PWMDimmerSetPoweredOffLed(); } @@ -178,7 +178,7 @@ void PWMDimmerHandleDevGroupItem(void) uint8_t device_group_index = *(uint8_t *)XdrvMailbox.topic; if (device_group_index > remote_pwm_dimmer_count) return; bool device_is_local = device_groups[device_group_index].local; - struct remote_pwm_dimmer * remote_pwm_dimmer = &remote_pwm_dimmers[device_group_index]; + struct remote_pwm_dimmer * remote_pwm_dimmer = &remote_pwm_dimmers[device_group_index - 1]; #else // USE_PWM_DIMMER_REMOTE if (*(uint8_t *)XdrvMailbox.topic) return; #endif // !USE_PWM_DIMMER_REMOTE @@ -190,7 +190,7 @@ void PWMDimmerHandleDevGroupItem(void) break; case DGR_ITEM_POWER: if (!device_is_local) { - remote_pwm_dimmer->power = value; + remote_pwm_dimmer->power_on = value & 1; remote_pwm_dimmer->power_button_increases_bri = (remote_pwm_dimmer->bri < 128); } break; @@ -250,13 +250,13 @@ void PWMDimmerHandleButton(void) * Released Hold down On No Dimmer * Released Press & release up Off No Power on at bri preset low * Released Press & release down Off No Power on at bri preset high - * + * * Holding any button for over 10 seconds executes the WiFiConfig 2 command. - * + * * In remote mode, whichever button is pressed first becomes the power button and any buttons * pressed while it is held affect the device associated with it. The up and down buttons change * depeneding on which button is the current power button: - * + * * Power Down Up * ----- ---- -- * 1 2 3 @@ -276,17 +276,17 @@ void PWMDimmerHandleButton(void) } bool state_updated = false; - int8_t bri_offset = 0; + int32_t bri_offset = 0; uint8_t power_on_bri = 0; uint8_t dgr_item = 0; uint8_t dgr_value; uint8_t dgr_more_to_come = false; uint32_t button_index = XdrvMailbox.index; uint32_t now = millis(); - + // Initialize some variables. #ifdef USE_PWM_DIMMER_REMOTE - bool power_is_on = (!active_device_is_local ? active_remote_pwm_dimmer->power : power); + bool power_is_on = (!active_device_is_local ? active_remote_pwm_dimmer->power_on : power); bool is_power_button = (button_index == power_button_index); #else // USE_PWM_DIMMER_REMOTE bool power_is_on = power; @@ -344,8 +344,9 @@ void PWMDimmerHandleButton(void) // If this is about the power button, ... if (is_power_button) { - // If no other buttons are pressed, ... - if (buttons_pressed == 1) { + // If no other buttons are pressed and the up or down button was not tapped while holding + // the power button before this, ... + if (buttons_pressed == 1 && !tap_count) { // If the power is on, adjust the brightness. Set the direction based on the current // direction for the device and then invert the direction when the power button is @@ -481,7 +482,7 @@ void PWMDimmerHandleButton(void) // If the up or down button was tapped while the power button was held, ... else if (tap_count) { - + // If the button was tapped but not held, handle the operation based on which button was // tapped. if (!button_was_held) { @@ -545,11 +546,11 @@ void PWMDimmerHandleButton(void) // If the power is on, ... if (power_is_on) { - + // If the button was not held, adjust the brightness. Set the direction based on which // button is pressed. The new brightness will be calculated below. if (button_hold_time[button_index] >= now) { - bri_offset = (is_down_button ? -10 : 10); + bri_offset = (is_down_button ? -1 : 1); dgr_item = 255; } @@ -590,9 +591,8 @@ void PWMDimmerHandleButton(void) else #endif // USE_PWM_DIMMER_REMOTE bri = light_state.getBri(); - int32_t new_bri; - bri_offset *= (Settings.light_correction ? 4 : bri / 16 + 1); - new_bri = bri + bri_offset; + int32_t new_bri = bri + bri_offset * ((dgr_item ? 16 : Settings.light_correction ? 4 : bri / 16 + 1)); + if (bri_offset > 0) { if (new_bri > 255) new_bri = 255; } @@ -609,9 +609,15 @@ void PWMDimmerHandleButton(void) else { #endif // USE_PWM_DIMMER_REMOTE skip_light_fade = true; +#ifdef USE_DEVICE_GROUPS + ignore_dgr_sends = true; +#endif // USE_DEVICE_GROUPS light_state.setBri(new_bri); LightAnimate(); skip_light_fade = false; +#ifdef USE_DEVICE_GROUPS + ignore_dgr_sends = false; +#endif // USE_DEVICE_GROUPS Settings.bri_power_on = new_bri; #ifdef USE_PWM_DIMMER_REMOTE } @@ -628,8 +634,8 @@ void PWMDimmerHandleButton(void) #ifdef USE_DEVICE_GROUPS #ifdef USE_PWM_DIMMER_REMOTE if (!active_device_is_local) { - active_remote_pwm_dimmer->power ^= 1; - new_power = active_remote_pwm_dimmer->power; + active_remote_pwm_dimmer->power_on ^= 1; + new_power = active_remote_pwm_dimmer->power_on; } else { #endif // USE_PWM_DIMMER_REMOTE diff --git a/tasmota/xdrv_36_keeloq.ino b/tasmota/xdrv_36_keeloq.ino index d975a5a79..c271cf3f7 100644 --- a/tasmota/xdrv_36_keeloq.ino +++ b/tasmota/xdrv_36_keeloq.ino @@ -242,8 +242,8 @@ void CreateKeeloqPacket() void KeeloqInit() { - jaroliftDevice.port_tx = pin[GPIO_CC1101_GDO2]; // Output port for transmission - jaroliftDevice.port_rx = pin[GPIO_CC1101_GDO0]; // Input port for reception + jaroliftDevice.port_tx = Pin(GPIO_CC1101_GDO2); // Output port for transmission + jaroliftDevice.port_rx = Pin(GPIO_CC1101_GDO0); // Input port for reception DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("cc1101.init()")); delay(100); @@ -266,7 +266,7 @@ void KeeloqInit() \*********************************************************************************************/ bool Xdrv36(uint8_t function) { - if ((99 == pin[GPIO_CC1101_GDO0]) || (99 == pin[GPIO_CC1101_GDO2])) { return false; } + if (!PinUsed(GPIO_CC1101_GDO0) || !PinUsed(GPIO_CC1101_GDO2)) { return false; } bool result = false; @@ -284,4 +284,4 @@ bool Xdrv36(uint8_t function) return result; } -#endif // USE_KEELOQ +#endif // USE_KEELOQ diff --git a/tasmota/xdrv_37_sonoff_d1.ino b/tasmota/xdrv_37_sonoff_d1.ino index 9ebc4b4f3..cd1bdd4b2 100644 --- a/tasmota/xdrv_37_sonoff_d1.ino +++ b/tasmota/xdrv_37_sonoff_d1.ino @@ -17,6 +17,7 @@ along with this program. If not, see . */ +#ifdef USE_LIGHT #ifdef USE_SONOFF_D1 /*********************************************************************************************\ * Sonoff D1 dimmer 433 @@ -196,3 +197,4 @@ bool Xdrv37(uint8_t function) } #endif // USE_SONOFF_D1 +#endif // USE_LIGHT diff --git a/tasmota/xdrv_81_webcam.ino b/tasmota/xdrv_81_webcam.ino new file mode 100644 index 000000000..a317b1aaa --- /dev/null +++ b/tasmota/xdrv_81_webcam.ino @@ -0,0 +1,901 @@ +/* + xdrv_81_webcam.ino - ESP32 webcam support for Tasmota + + Copyright (C) 2020 Gerhard Mutz 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 . +*/ + +#ifdef ESP32 +#ifdef USE_WEBCAM +/*********************************************************************************************\ + * ESP32 webcam based on example in Arduino-ESP32 library + * + * Template as used on ESP32-CAM WiFi + bluetooth Camera Module Development Board ESP32 With Camera Module OV2640 Geekcreit for Arduino + * {"NAME":"AITHINKER CAM No SPI","GPIO":[4992,65504,65504,65504,65504,5088,65504,65504,65504,65504,65504,65504,65504,65504,5089,5090,0,5091,5184,5152,0,5120,5024,5056,0,0,0,0,4928,65504,5094,5095,5092,0,0,5093],"FLAG":0,"BASE":1} + * + * Command: Webcam + * 0 = Stop streaming + * 1 = FRAMESIZE_QQVGA2 (128x160) + * 2 = FRAMESIZE_QCIF (176x144) + * 3 = FRAMESIZE_HQVGA (240x176) + * 4 = FRAMESIZE_QVGA (320x240) + * 5 = FRAMESIZE_CIF (400x296) + * 6 = FRAMESIZE_VGA (640x480) + * 7 = FRAMESIZE_SVGA (800x600) + * 8 = FRAMESIZE_XGA (1024x768) + * 9 = FRAMESIZE_SXGA (1280x1024) + * 10 = FRAMESIZE_UXGA (1600x1200) + * + * Only boards with PSRAM should be used. To enable PSRAM board should be se set to esp32cam in common32 of platform_override.ini + * board = esp32cam + * To speed up cam processing cpu frequency should be better set to 240Mhz in common32 of platform_override.ini + * board_build.f_cpu = 240000000L +\*********************************************************************************************/ + +#define XDRV_81 81 + +#define CAMERA_MODEL_AI_THINKER + +#define USE_TEMPLATE + +#define WC_LOGLEVEL LOG_LEVEL_INFO + +#include "fb_gfx.h" +#include "fd_forward.h" +#include "fr_forward.h" + +#define PWDN_GPIO_NUM 32 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 0 +#define SIOD_GPIO_NUM 26 +#define SIOC_GPIO_NUM 27 + +#define Y9_GPIO_NUM 35 +#define Y8_GPIO_NUM 34 +#define Y7_GPIO_NUM 39 +#define Y6_GPIO_NUM 36 +#define Y5_GPIO_NUM 21 +#define Y4_GPIO_NUM 19 +#define Y3_GPIO_NUM 18 +#define Y2_GPIO_NUM 5 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 23 +#define PCLK_GPIO_NUM 22 + + +#include "esp_camera.h" +#include "sensor.h" + +uint8_t wc_up; +uint16_t wc_width; +uint16_t wc_height; +uint8_t wc_stream_active; +#ifdef USE_FACE_DETECT +uint8_t faces; +uint16_t face_detect_time; +#endif + +bool WcPinUsed(void) { + bool pin_used = true; + for (uint32_t i = 0; i < MAX_WEBCAM_DATA; i++) { + if (!PinUsed(GPIO_WEBCAM_DATA, i)) { + pin_used = false; + } +// if (i < MAX_WEBCAM_HSD) { +// if (!PinUsed(GPIO_WEBCAM_HSD, i)) { +// pin_used = false; +// } +// } + } + if (!PinUsed(GPIO_WEBCAM_XCLK) || !PinUsed(GPIO_WEBCAM_PCLK) || + !PinUsed(GPIO_WEBCAM_VSYNC) || !PinUsed(GPIO_WEBCAM_HREF) || + !PinUsed(GPIO_WEBCAM_SIOD) || !PinUsed(GPIO_WEBCAM_SIOC)) { + pin_used = false; + } + return pin_used; +} + +uint32_t wc_setup(int32_t fsiz) { + if (fsiz > 10) { fsiz = 10; } + + wc_stream_active = 0; + + if (fsiz < 0) { + esp_camera_deinit(); + wc_up = 0; + return 0; + } + + if (wc_up) { + esp_camera_deinit(); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: deinit")); + //return wc_up; + } + +//esp_log_level_set("*", ESP_LOG_VERBOSE); + + camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.xclk_freq_hz = 20000000; + config.pixel_format = PIXFORMAT_JPEG; +// config.pixel_format = PIXFORMAT_GRAYSCALE; +// config.pixel_format = PIXFORMAT_RGB565; + +#ifndef USE_TEMPLATE + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; +#else + if (WcPinUsed()) { + config.pin_d0 = Pin(GPIO_WEBCAM_DATA); // Y2_GPIO_NUM; + config.pin_d1 = Pin(GPIO_WEBCAM_DATA, 1); // Y3_GPIO_NUM; + config.pin_d2 = Pin(GPIO_WEBCAM_DATA, 2); // Y4_GPIO_NUM; + config.pin_d3 = Pin(GPIO_WEBCAM_DATA, 3); // Y5_GPIO_NUM; + config.pin_d4 = Pin(GPIO_WEBCAM_DATA, 4); // Y6_GPIO_NUM; + config.pin_d5 = Pin(GPIO_WEBCAM_DATA, 5); // Y7_GPIO_NUM; + config.pin_d6 = Pin(GPIO_WEBCAM_DATA, 6); // Y8_GPIO_NUM; + config.pin_d7 = Pin(GPIO_WEBCAM_DATA, 7); // Y9_GPIO_NUM; + config.pin_xclk = Pin(GPIO_WEBCAM_XCLK); // XCLK_GPIO_NUM; + config.pin_pclk = Pin(GPIO_WEBCAM_PCLK); // PCLK_GPIO_NUM; + config.pin_vsync = Pin(GPIO_WEBCAM_VSYNC); // VSYNC_GPIO_NUM; + config.pin_href = Pin(GPIO_WEBCAM_HREF); // HREF_GPIO_NUM; + config.pin_sscb_sda = Pin(GPIO_WEBCAM_SIOD); // SIOD_GPIO_NUM; + config.pin_sscb_scl = Pin(GPIO_WEBCAM_SIOC); // SIOC_GPIO_NUM; + config.pin_pwdn = (PinUsed(GPIO_WEBCAM_PWDN)) ? Pin(GPIO_WEBCAM_PWDN) : -1; // PWDN_GPIO_NUM; + config.pin_reset = (PinUsed(GPIO_WEBCAM_RESET)) ? Pin(GPIO_WEBCAM_RESET) : -1; // RESET_GPIO_NUM; + + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: User template")); + } else { + // defaults to AI THINKER + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Default template")); + } +#endif + + //ESP.getPsramSize() + + //esp_log_level_set("*", ESP_LOG_INFO); + + + // if PSRAM IC present, init with UXGA resolution and higher JPEG quality + // for larger pre-allocated frame buffer. + + bool psram = psramFound(); + if (psram) { + config.frame_size = FRAMESIZE_UXGA; + config.jpeg_quality = 10; + config.fb_count = 2; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: PSRAM found")); + } else { + config.frame_size = FRAMESIZE_VGA; + config.jpeg_quality = 12; + config.fb_count = 1; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: PSRAM not found")); + } + + // stupid workaround camera diver eats up static ram should prefer PSRAM + // so we steal static ram to force driver to alloc PSRAM + //ESP.getMaxAllocHeap() + +// void *x=malloc(70000); + void *x = 0; + esp_err_t err = esp_camera_init(&config); + if (x) { free(x); } + + if (err != ESP_OK) { + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Init failed with error 0x%x"), err); + return 0; + } + + sensor_t * wc_s = esp_camera_sensor_get(); + // initial sensors are flipped vertically and colors are a bit saturated + if (OV3660_PID == wc_s->id.PID) { + wc_s->set_vflip(wc_s, 1); // flip it back + wc_s->set_brightness(wc_s, 1); // up the brightness just a bit + wc_s->set_saturation(wc_s, -2); // lower the saturation + } + // drop down frame size for higher initial frame rate + wc_s->set_framesize(wc_s, (framesize_t)fsiz); + + camera_fb_t *wc_fb = esp_camera_fb_get(); + wc_width = wc_fb->width; + wc_height = wc_fb->height; + esp_camera_fb_return(wc_fb); + + +#ifdef USE_FACE_DETECT + fd_init(); +#endif + + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Initialized")); + + wc_up = 1; + if (psram) { wc_up=2; } + + return wc_up; +} + +int32_t wc_set_options(uint32_t sel, int32_t value) { + int32_t res = 0; + sensor_t *s = esp_camera_sensor_get(); + if (!s) { return -99; } + + switch (sel) { + case 0: + if (value >= 0) { s->set_framesize(s, (framesize_t)value); } + res = s->status.framesize; + break; + case 1: + if (value >= 0) { s->set_special_effect(s, value); } + res = s->status.special_effect; + break; + case 2: + if (value >= 0) { s->set_vflip(s, value); } + res = s->status.vflip; + break; + case 3: + if (value >= 0) { s->set_hmirror(s, value); } + res = s->status.hmirror; + break; + case 4: + if (value >= -4) { s->set_contrast(s, value); } + res = s->status.contrast; + break; + case 5: + if (value >= -4) { s->set_brightness(s, value); } + res = s->status.brightness; + break; + case 6: + if (value >= -4) { s->set_saturation(s,value); } + res = s->status.saturation; + break; + } + + return res; +} + +uint32_t wc_get_width(void) { + camera_fb_t *wc_fb = esp_camera_fb_get(); + if (!wc_fb) { return 0; } + wc_width = wc_fb->width; + esp_camera_fb_return(wc_fb); + return wc_width; +} + +uint32_t wc_get_height(void) { + camera_fb_t *wc_fb = esp_camera_fb_get(); + if (!wc_fb) { return 0; } + wc_height = wc_fb->height; + esp_camera_fb_return(wc_fb); + return wc_height; +} + +#ifndef MAX_PICSTORE +#define MAX_PICSTORE 4 +#endif +struct PICSTORE { + uint8_t *buff; + uint32_t len; +}; + +struct PICSTORE picstore[MAX_PICSTORE]; + +#ifdef COPYFRAME +struct PICSTORE tmp_picstore; +#endif + +uint32_t get_picstore(int32_t num, uint8_t **buff) { + if (num<0) { return MAX_PICSTORE; } + *buff = picstore[num].buff; + return picstore[num].len; +} + +uint32_t wc_get_jpeg(uint8_t **buff) { + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + camera_fb_t *wc_fb; + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { return 0; } + if (wc_fb->format != PIXFORMAT_JPEG) { + bool jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted) { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + esp_camera_fb_return(wc_fb); + *buff = _jpg_buf; + return _jpg_buf_len; +} + +uint32_t wc_get_frame(int32_t bnum) { + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + camera_fb_t *wc_fb = 0; + bool jpeg_converted = false; + + if (bnum < 0) { + if (bnum < -MAX_PICSTORE) { bnum=-1; } + bnum = -bnum; + bnum--; + if (picstore[bnum].buff) { free(picstore[bnum].buff); } + picstore[bnum].len = 0; + return 0; + } + +#ifdef COPYFRAME + if (bnum & 0x10) { + bnum &= 0xf; + _jpg_buf = tmp_picstore.buff; + _jpg_buf_len = tmp_picstore.len; + if (!_jpg_buf_len) { return 0; } + goto pcopy; + } +#endif + + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Can't get frame")); + return 0; + } + if (!bnum) { + wc_width = wc_fb->width; + wc_height = wc_fb->height; + esp_camera_fb_return(wc_fb); + return 0; + } + + if (wc_fb->format != PIXFORMAT_JPEG) { + jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted){ + //Serial.println("JPEG compression failed"); + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + +pcopy: + if ((bnum < 1) || (bnum > MAX_PICSTORE)) { bnum = 1; } + bnum--; + if (picstore[bnum].buff) { free(picstore[bnum].buff); } + picstore[bnum].buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (picstore[bnum].buff) { + memcpy(picstore[bnum].buff, _jpg_buf, _jpg_buf_len); + picstore[bnum].len = _jpg_buf_len; + } else { + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Can't allocate picstore")); + picstore[bnum].len = 0; + } + if (wc_fb) { esp_camera_fb_return(wc_fb); } + if (jpeg_converted) { free(_jpg_buf); } + if (!picstore[bnum].buff) { return 0; } + + return _jpg_buf_len; +} + +bool HttpCheckPriviledgedAccess(bool); +extern ESP8266WebServer *Webserver; + +void HandleImage(void) { + if (!HttpCheckPriviledgedAccess(true)) { return; } + + uint32_t bnum = Webserver->arg(F("p")).toInt(); + if ((bnum < 0) || (bnum > MAX_PICSTORE)) { bnum= 1; } + WiFiClient client = Webserver->client(); + String response = "HTTP/1.1 200 OK\r\n"; + response += "Content-disposition: inline; filename=cap.jpg\r\n"; + response += "Content-type: image/jpeg\r\n\r\n"; + Webserver->sendContent(response); + + if (!bnum) { + uint8_t *buff; + uint32_t len; + len = wc_get_jpeg(&buff); + if (len) { + client.write(buff,len); + free(buff); + } + } else { + bnum--; + if (!picstore[bnum].len) { + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: No image #: %d"), bnum); + return; + } + client.write((char *)picstore[bnum].buff, picstore[bnum].len); + } + + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Sending image #: %d"), bnum+1); +} + +ESP8266WebServer *CamServer; +#define BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e" + +WiFiClient client; + +void handleMjpeg(void) { + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Handle camserver")); + //if (!wc_stream_active) { + wc_stream_active = 1; + client = CamServer->client(); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Create client")); + //} +} + +#ifdef USE_FACE_DETECT + +static mtmn_config_t mtmn_config = {0}; + +void fd_init(void) { + mtmn_config.type = FAST; + mtmn_config.min_face = 80; + mtmn_config.pyramid = 0.707; + mtmn_config.pyramid_times = 4; + mtmn_config.p_threshold.score = 0.6; + mtmn_config.p_threshold.nms = 0.7; + mtmn_config.p_threshold.candidate_number = 20; + mtmn_config.r_threshold.score = 0.7; + mtmn_config.r_threshold.nms = 0.7; + mtmn_config.r_threshold.candidate_number = 10; + mtmn_config.o_threshold.score = 0.7; + mtmn_config.o_threshold.nms = 0.7; + mtmn_config.o_threshold.candidate_number = 1; +} + +#define FACE_COLOR_WHITE 0x00FFFFFF +#define FACE_COLOR_BLACK 0x00000000 +#define FACE_COLOR_RED 0x000000FF +#define FACE_COLOR_GREEN 0x0000FF00 +#define FACE_COLOR_BLUE 0x00FF0000 +#define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN) +#define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN) +#define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED) +void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, int face_id); + +/* +void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, int face_id) { + int x, y, w, h, i; + uint32_t color = FACE_COLOR_YELLOW; + if(face_id < 0){ + color = FACE_COLOR_RED; + } else if(face_id > 0){ + color = FACE_COLOR_GREEN; + } + fb_data_t fb; + fb.width = image_matrix->w; + fb.height = image_matrix->h; + fb.data = image_matrix->item; + fb.bytes_per_pixel = 3; + fb.format = FB_BGR888; + for (i = 0; i < boxes->len; i++){ + // rectangle box + x = (int)boxes->box[i].box_p[0]; + y = (int)boxes->box[i].box_p[1]; + w = (int)boxes->box[i].box_p[2] - x + 1; + h = (int)boxes->box[i].box_p[3] - y + 1; + fb_gfx_drawFastHLine(&fb, x, y, w, color); + fb_gfx_drawFastHLine(&fb, x, y+h-1, w, color); + fb_gfx_drawFastVLine(&fb, x, y, h, color); + fb_gfx_drawFastVLine(&fb, x+w-1, y, h, color); +#if 0 + // landmark + int x0, y0, j; + for (j = 0; j < 10; j+=2) { + x0 = (int)boxes->landmark[i].landmark_p[j]; + y0 = (int)boxes->landmark[i].landmark_p[j+1]; + fb_gfx_fillRect(&fb, x0, y0, 3, 3, color); + } +#endif + } +} +*/ + +#define DL_SPIRAM_SUPPORT + +uint32_t wc_set_face_detect(int32_t value) { + if (value >= 0) { face_detect_time = value; } + return faces; +} + +uint32_t face_ltime; + +uint32_t detect_face(void); + +uint32_t detect_face(void) { +dl_matrix3du_t *image_matrix; +size_t out_len, out_width, out_height; +uint8_t * out_buf; +bool s; +bool detected = false; +int face_id = 0; +camera_fb_t *fb; + + if ((millis() - face_ltime) > face_detect_time) { + face_ltime = millis(); + fb = esp_camera_fb_get(); + if (!fb) { return ESP_FAIL; } + + image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3); + if (!image_matrix) { + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: dl_matrix3du_alloc failed")); + esp_camera_fb_return(fb); + return ESP_FAIL; + } + + out_buf = image_matrix->item; + //out_len = fb->width * fb->height * 3; + //out_width = fb->width; + //out_height = fb->height; + + s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf); + esp_camera_fb_return(fb); + if (!s){ + dl_matrix3du_free(image_matrix); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: to rgb888 failed")); + return ESP_FAIL; + } + + box_array_t *net_boxes = face_detect(image_matrix, &mtmn_config); + if (net_boxes){ + detected = true; + faces = net_boxes->len; + //if(recognition_enabled){ + // face_id = run_face_recognition(image_matrix, net_boxes); + //} + //draw_face_boxes(image_matrix, net_boxes, face_id); + free(net_boxes->score); + free(net_boxes->box); + free(net_boxes->landmark); + free(net_boxes); + } else { + faces = 0; + } + dl_matrix3du_free(image_matrix); + //Serial.printf("face detected: %d",faces); + + } +} +#endif + +void handleMjpeg_task(void) { + camera_fb_t *wc_fb; + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + + //WiFiClient client = CamServer->client(); + uint32_t tlen; + bool jpeg_converted = false; + + if (!client.connected()) { + wc_stream_active = 0; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Client fail")); + goto exit; + } + + if (1 == wc_stream_active) { + client.flush(); + client.setTimeout(3); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Start stream")); + client.print("HTTP/1.1 200 OK\r\n" + "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" + "\r\n"); + wc_stream_active = 2; + } else { + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { + wc_stream_active = 0; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Frame fail")); + goto exit; + } + + + if (wc_fb->format != PIXFORMAT_JPEG) { + jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted){ + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: JPEG compression failed")); + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + + client.printf("Content-Type: image/jpeg\r\n" + "Content-Length: %d\r\n" + "\r\n", static_cast(_jpg_buf_len)); + tlen = client.write(_jpg_buf, _jpg_buf_len); + /* + if (tlen!=_jpg_buf_len) { + esp_camera_fb_return(wc_fb); + wc_stream_active=0; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Send fail")); + }*/ + client.print("\r\n--" BOUNDARY "\r\n"); + +#ifdef COPYFRAME + if (tmp_picstore.buff) { free(tmp_picstore.buff); } + tmp_picstore.buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (tmp_picstore.buff) { + memcpy(tmp_picstore.buff, _jpg_buf, _jpg_buf_len); + tmp_picstore.len = _jpg_buf_len; + } else { + tmp_picstore.len = 0; + } +#endif + + if (jpeg_converted) { free(_jpg_buf); } + esp_camera_fb_return(wc_fb); + //AddLog_P2(WC_LOGLEVEL, PSTR("CAM: send frame")); + +exit: + if (!wc_stream_active) { + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream exit")); + client.flush(); + client.stop(); + } + } +} + +void CamHandleRoot(void) { + //CamServer->redirect("http://" + String(ip) + ":81/cam.mjpeg"); + CamServer->sendHeader("Location", WiFi.localIP().toString() + ":81/cam.mjpeg"); + CamServer->send(302, "", ""); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: root called")); +} + +uint16_t motion_detect; +uint32_t motion_ltime; +uint32_t motion_trigger; +uint32_t motion_brightness; +uint8_t *last_motion_buffer; + +uint32_t wc_set_motion_detect(int32_t value) { + if (value >= 0) { motion_detect = value; } + if (-1 == value) { + return motion_trigger; + } else { + return motion_brightness; + } +} + +// optional motion detector +void detect_motion(void) { + camera_fb_t *wc_fb; + uint8_t *out_buf = 0; + + if ((millis()-motion_ltime) > motion_detect) { + motion_ltime = millis(); + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { return; } + + if (!last_motion_buffer) { + last_motion_buffer=(uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } + if (last_motion_buffer) { + if (PIXFORMAT_JPEG == wc_fb->format) { + out_buf = (uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height*3)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (out_buf) { + fmt2rgb888(wc_fb->buf, wc_fb->len, wc_fb->format, out_buf); + uint32_t x, y; + uint8_t *pxi = out_buf; + uint8_t *pxr = last_motion_buffer; + // convert to bw + uint64_t accu = 0; + uint64_t bright = 0; + for (y = 0; y < wc_fb->height; y++) { + for (x = 0; x < wc_fb->width; x++) { + int32_t gray = (pxi[0] + pxi[1] + pxi[2]) / 3; + int32_t lgray = pxr[0]; + pxr[0] = gray; + pxi += 3; + pxr++; + accu += abs(gray - lgray); + bright += gray; + } + } + motion_trigger = accu / ((wc_fb->height * wc_fb->width) / 100); + motion_brightness = bright / ((wc_fb->height * wc_fb->width) / 100); + free(out_buf); + } + } + } + esp_camera_fb_return(wc_fb); + } +} + +void wc_show_stream(void) { + if (CamServer) { + WSContentSend_P(PSTR("

Webcam stream

"), + WiFi.localIP().toString().c_str()); + } +} + +uint32_t wc_set_streamserver(uint32_t flag) { + if (global_state.wifi_down) { return 0; } + + wc_stream_active = 0; + + if (flag) { + if (!CamServer) { + CamServer = new ESP8266WebServer(81); + CamServer->on("/", CamHandleRoot); + CamServer->on("/cam.mjpeg", handleMjpeg); + CamServer->on("/cam.jpg", handleMjpeg); + CamServer->on("/stream", handleMjpeg); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream init")); + CamServer->begin(); + } + } else { + if (CamServer) { + CamServer->stop(); + delete CamServer; + CamServer = NULL; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream exit")); + } + } + return 0; +} + +void WcStreamControl(uint32_t resolution) { + wc_set_streamserver(resolution); + /*if (0 == resolution) { + resolution=-1; + }*/ + wc_setup(resolution); +} + +void wc_loop(void) { + if (CamServer) { CamServer->handleClient(); } + if (wc_stream_active) { handleMjpeg_task(); } + if (motion_detect) { detect_motion(); } +#ifdef USE_FACE_DETECT + if (face_detect_time) { detect_face(); } +#endif +} + +void wc_pic_setup(void) { + Webserver->on("/wc.jpg", HandleImage); + Webserver->on("/wc.mjpeg", HandleImage); +} + +/* +typedef enum { + // FRAMESIZE_96x96, // 96x96 + FRAMESIZE_QQVGA, // 160x120 0 + FRAMESIZE_QQVGA2, // 128x160 1 + FRAMESIZE_QCIF, // 176x144 2 + FRAMESIZE_HQVGA, // 240x176 3 + + // FRAMESIZE_240x240, // 240x240 3 + + FRAMESIZE_QVGA, // 320x240 4 + FRAMESIZE_CIF, // 400x296 5 + FRAMESIZE_VGA, // 640x480 6 + FRAMESIZE_SVGA, // 800x600 7 + FRAMESIZE_XGA, // 1024x768 8 + FRAMESIZE_SXGA, // 1280x1024 9 + FRAMESIZE_UXGA, // 1600x1200 10 + + + FRAMESIZE_QXGA, // 2048*1536 + FRAMESIZE_INVALID +} framesize_t; + +flash led = gpio4 +red led = gpio 33 +*/ + +void WcInit(void) { + if (Settings.esp32_webcam_resolution > 10) { + Settings.esp32_webcam_resolution = 0; + } +} + + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +#define D_CMND_WEBCAM "Webcam" + +const char kWCCommands[] PROGMEM = "|" // no prefix + D_CMND_WEBCAM + ; + +void (* const WCCommand[])(void) PROGMEM = { + &CmndWebcam, + }; + +void CmndWebcam(void) { + uint32_t flag = 0; + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { + Settings.esp32_webcam_resolution=XdrvMailbox.payload; + WcStreamControl(Settings.esp32_webcam_resolution); + } + if (CamServer) { flag = 1; } + Response_P(PSTR("{\"" D_CMND_WEBCAM "\":{\"Streaming\":\"%s\"}"),GetStateText(flag)); +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv81(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_LOOP: + wc_loop(); + break; + case FUNC_WEB_ADD_HANDLER: + wc_pic_setup(); + break; + case FUNC_WEB_ADD_MAIN_BUTTON: + if (Settings.esp32_webcam_resolution) { +//#ifndef USE_SCRIPT + WcStreamControl(Settings.esp32_webcam_resolution); + delay(50); // Give the webcam webserver some time to prepare the stream + wc_show_stream(); +//#endif + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kWCCommands, WCCommand); + break; + case FUNC_PRE_INIT: + WcInit(); + break; + + } + return result; +} + +#endif // USE_WEBCAM +#endif // ESP32 diff --git a/tasmota/xdrv_99_debug.ino b/tasmota/xdrv_99_debug.ino index feb04cabd..eeb4e7ca1 100644 --- a/tasmota/xdrv_99_debug.ino +++ b/tasmota/xdrv_99_debug.ino @@ -312,7 +312,7 @@ void DebugCfgDump(char* parms) char *p; uint8_t *buffer = (uint8_t *) &Settings; - maxrow = ((sizeof(SYSCFG)+CFG_COLS)/CFG_COLS); + maxrow = ((sizeof(Settings)+CFG_COLS)/CFG_COLS); uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; uint16_t mrow = strtol(p, &p, 10); @@ -356,7 +356,7 @@ void DebugCfgPeek(char* parms) char *p; uint16_t address = strtol(parms, &p, 16); - if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; + if (address > sizeof(Settings)) address = sizeof(Settings) -4; address = (address >> 2) << 2; uint8_t *buffer = (uint8_t *) &Settings; @@ -381,7 +381,7 @@ void DebugCfgPoke(char* parms) char *p; uint16_t address = strtol(parms, &p, 16); - if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; + if (address > sizeof(Settings)) address = sizeof(Settings) -4; address = (address >> 2) << 2; uint32_t data = strtol(p, &p, 16); diff --git a/tasmota/xdsp_02_ssd1306.ino b/tasmota/xdsp_02_ssd1306.ino index 6060686b9..24700b0af 100644 --- a/tasmota/xdsp_02_ssd1306.ino +++ b/tasmota/xdsp_02_ssd1306.ino @@ -71,8 +71,8 @@ void SSD1306InitDriver(void) } uint8_t reset_pin = -1; - if (pin[GPIO_OLED_RESET] < 99) { - reset_pin = pin[GPIO_OLED_RESET]; + if (PinUsed(GPIO_OLED_RESET)) { + reset_pin = Pin(GPIO_OLED_RESET); } // allocate screen buffer diff --git a/tasmota/xdsp_04_ili9341.ino b/tasmota/xdsp_04_ili9341.ino index 5ad269e82..f755314c8 100644 --- a/tasmota/xdsp_04_ili9341.ino +++ b/tasmota/xdsp_04_ili9341.ino @@ -90,7 +90,7 @@ void Ili9341InitDriver(void) if (Settings.display_height != ILI9341_TFTHEIGHT) { Settings.display_height = ILI9341_TFTHEIGHT; } - tft = new Adafruit_ILI9341(pin[GPIO_SPI_CS], pin[GPIO_SPI_DC]); + tft = new Adafruit_ILI9341(Pin(GPIO_SPI_CS), Pin(GPIO_SPI_DC)); tft->begin(); #ifdef USE_DISPLAY_MODES1TO5 @@ -128,9 +128,9 @@ void Ili9341DisplayOnOff(uint8_t on) { // tft->showDisplay(on); // tft->invertDisplay(on); - if (pin[GPIO_BACKLIGHT] < 99) { - pinMode(pin[GPIO_BACKLIGHT], OUTPUT); - digitalWrite(pin[GPIO_BACKLIGHT], on); + if (PinUsed(GPIO_BACKLIGHT)) { + pinMode(Pin(GPIO_BACKLIGHT), OUTPUT); + digitalWrite(Pin(GPIO_BACKLIGHT), on); } } diff --git a/tasmota/xdsp_05_epaper_29.ino b/tasmota/xdsp_05_epaper_29.ino index 946fc6343..dc7397221 100644 --- a/tasmota/xdsp_05_epaper_29.ino +++ b/tasmota/xdsp_05_epaper_29.ino @@ -67,13 +67,13 @@ void EpdInitDriver29() epd = new Epd(EPD_WIDTH,EPD_HEIGHT); // whiten display with full update, takes 3 seconds - if ((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CLK] < 99) && (pin[GPIO_SPI_MOSI] < 99)) { - epd->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SPI_CS], pin[GPIO_SPI_CLK], pin[GPIO_SPI_MOSI]); + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_CLK) && PinUsed(GPIO_SPI_MOSI)) { + epd->Begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),Pin(GPIO_SPI_CS), Pin(GPIO_SPI_CLK), Pin(GPIO_SPI_MOSI)); } - else if ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_MOSI] < 99)) { - epd->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SSPI_CS], pin[GPIO_SSPI_SCLK], pin[GPIO_SSPI_MOSI]); + else if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_SCLK) && PinUsed(GPIO_SSPI_MOSI)) { + epd->Begin(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),Pin(GPIO_SSPI_CS), Pin(GPIO_SSPI_SCLK), Pin(GPIO_SSPI_MOSI)); } else { free(buffer); return; diff --git a/tasmota/xdsp_06_epaper_42.ino b/tasmota/xdsp_06_epaper_42.ino index 7a19ebb7f..fee42db6c 100644 --- a/tasmota/xdsp_06_epaper_42.ino +++ b/tasmota/xdsp_06_epaper_42.ino @@ -65,15 +65,15 @@ void EpdInitDriver42() epd42 = new Epd42(EPD_WIDTH42,EPD_HEIGHT42); #ifdef USE_SPI - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)) { - epd42->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)) { + epd42->Begin(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); } else { free(buffer); return; } #else - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { - epd42->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) { + epd42->Begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); } else { free(buffer); return; diff --git a/tasmota/xdsp_08_ILI9488.ino b/tasmota/xdsp_08_ILI9488.ino index 84a493fc6..1dc877e18 100644 --- a/tasmota/xdsp_08_ILI9488.ino +++ b/tasmota/xdsp_08_ILI9488.ino @@ -80,16 +80,16 @@ void ILI9488_InitDriver() bg_color = ILI9488_BLACK; uint8_t bppin=BACKPLANE_PIN; - if (pin[GPIO_BACKLIGHT]<99) { - bppin=pin[GPIO_BACKLIGHT]; + if (PinUsed(GPIO_BACKLIGHT)) { + bppin=Pin(GPIO_BACKLIGHT); } // init renderer - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ - ili9488 = new ILI9488(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK],bppin); + if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)) { + ili9488 = new ILI9488(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK),bppin); } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { - ili9488 = new ILI9488(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK],bppin); + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) { + ili9488 = new ILI9488(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK),bppin); } else { return; } diff --git a/tasmota/xdsp_09_SSD1351.ino b/tasmota/xdsp_09_SSD1351.ino index be7138f65..5f3148be6 100644 --- a/tasmota/xdsp_09_SSD1351.ino +++ b/tasmota/xdsp_09_SSD1351.ino @@ -60,11 +60,11 @@ void SSD1351_InitDriver() { bg_color = SSD1351_BLACK; // init renderer - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ - ssd1351 = new SSD1351(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)){ + ssd1351 = new SSD1351(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)){ - ssd1351 = new SSD1351(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) { + ssd1351 = new SSD1351(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); } else { return; } diff --git a/tasmota/xdsp_10_RA8876.ino b/tasmota/xdsp_10_RA8876.ino index 88f3ebad2..9c86ff2d7 100644 --- a/tasmota/xdsp_10_RA8876.ino +++ b/tasmota/xdsp_10_RA8876.ino @@ -72,11 +72,11 @@ void RA8876_InitDriver() bg_color = RA8876_BLACK; // init renderer, must use hardware spi - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]==13) && (pin[GPIO_SSPI_MISO]==12) && (pin[GPIO_SSPI_SCLK]==14)) { - ra8876 = new RA8876(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_MISO],pin[GPIO_SSPI_SCLK],pin[GPIO_BACKLIGHT]); + if (PinUsed(GPIO_SSPI_CS) && (Pin(GPIO_SSPI_MOSI)==13) && (Pin(GPIO_SSPI_MISO)==12) && (Pin(GPIO_SSPI_SCLK)==14)) { + ra8876 = new RA8876(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_MISO),Pin(GPIO_SSPI_SCLK),Pin(GPIO_BACKLIGHT)); } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]==13) && (pin[GPIO_SPI_MISO]==12) && (pin[GPIO_SPI_CLK]==14)) { - ra8876 = new RA8876(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_MISO],pin[GPIO_SPI_CLK],pin[GPIO_BACKLIGHT]); + if (PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_MOSI)==13) && (Pin(GPIO_SPI_MISO)==12) && (Pin(GPIO_SPI_CLK)==14)) { + ra8876 = new RA8876(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_MISO),Pin(GPIO_SPI_CLK),Pin(GPIO_BACKLIGHT)); } else { return; } diff --git a/tasmota/xlgt_01_ws2812.ino b/tasmota/xlgt_01_ws2812.ino index f6d332198..368567181 100644 --- a/tasmota/xlgt_01_ws2812.ino +++ b/tasmota/xlgt_01_ws2812.ino @@ -449,10 +449,10 @@ void Ws2812ShowScheme(void) void Ws2812ModuleSelected(void) { - if (pin[GPIO_WS2812] < 99) { // RGB led + if (PinUsed(GPIO_WS2812)) { // RGB led // For DMA, the Pin is ignored as it uses GPIO3 due to DMA hardware use. - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); + strip = new NeoPixelBus(WS2812_MAX_LEDS, Pin(GPIO_WS2812)); strip->Begin(); Ws2812Clear(); diff --git a/tasmota/xlgt_02_my92x1.ino b/tasmota/xlgt_02_my92x1.ino index 69bccaf36..6d12abf84 100644 --- a/tasmota/xlgt_02_my92x1.ino +++ b/tasmota/xlgt_02_my92x1.ino @@ -115,9 +115,9 @@ bool My92x1SetChannels(void) void My92x1ModuleSelected(void) { - if ((pin[GPIO_DCKI] < 99) && (pin[GPIO_DI] < 99)) { - My92x1.pdi_pin = pin[GPIO_DI]; - My92x1.pdcki_pin = pin[GPIO_DCKI]; + if (PinUsed(GPIO_DCKI) && PinUsed(GPIO_DI)) { + My92x1.pdi_pin = Pin(GPIO_DI); + My92x1.pdcki_pin = Pin(GPIO_DCKI); pinMode(My92x1.pdi_pin, OUTPUT); pinMode(My92x1.pdcki_pin, OUTPUT); diff --git a/tasmota/xlgt_03_sm16716.ino b/tasmota/xlgt_03_sm16716.ino index 8332c80d1..314122a8b 100644 --- a/tasmota/xlgt_03_sm16716.ino +++ b/tasmota/xlgt_03_sm16716.ino @@ -100,9 +100,9 @@ void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) /* bool SM16716_ModuleSelected(void) { - Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; - Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; - Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; + Sm16716.pin_clk = Pin(GPIO_SM16716_CLK); + Sm16716.pin_dat = Pin(GPIO_SM16716_DAT); + Sm16716.pin_sel = Pin(GPIO_SM16716_SEL); DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), Sm16716.pin_clk, Sm16716.pin_dat); return (Sm16716.pin_clk < 99) && (Sm16716.pin_dat < 99); } @@ -122,9 +122,9 @@ bool Sm16716SetChannels(void) /* // handle any PWM pins, skipping the first 3 values for sm16716 for (uint32_t i = 3; i < Light.subtype; i++) { - if (pin[GPIO_PWM1 +i-3] < 99) { + if (PinUsed(GPIO_PWM1, i-3)) { //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol); - analogWrite(pin[GPIO_PWM1 +i-3], bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); + analogWrite(Pin(GPIO_PWM1, i-3), bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); } } */ @@ -138,17 +138,17 @@ bool Sm16716SetChannels(void) void Sm16716ModuleSelected(void) { - if ((pin[GPIO_SM16716_CLK] < 99) && (pin[GPIO_SM16716_DAT] < 99)) { - Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; - Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; - Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; + if (PinUsed(GPIO_SM16716_CLK) && PinUsed(GPIO_SM16716_DAT)) { + Sm16716.pin_clk = Pin(GPIO_SM16716_CLK); + Sm16716.pin_dat = Pin(GPIO_SM16716_DAT); + Sm16716.pin_sel = Pin(GPIO_SM16716_SEL); /* // init PWM for (uint32_t i = 0; i < Light.subtype; 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); } } */ diff --git a/tasmota/xlgt_04_sm2135.ino b/tasmota/xlgt_04_sm2135.ino index a77108dbb..e262395a8 100644 --- a/tasmota/xlgt_04_sm2135.ino +++ b/tasmota/xlgt_04_sm2135.ino @@ -134,9 +134,9 @@ bool Sm2135SetChannels(void) void Sm2135ModuleSelected(void) { - if ((pin[GPIO_SM2135_CLK] < 99) && (pin[GPIO_SM2135_DAT] < 99)) { - Sm2135.clk = pin[GPIO_SM2135_CLK]; - Sm2135.data = pin[GPIO_SM2135_DAT]; + if (PinUsed(GPIO_SM2135_CLK) && PinUsed(GPIO_SM2135_DAT)) { + Sm2135.clk = Pin(GPIO_SM2135_CLK); + Sm2135.data = Pin(GPIO_SM2135_DAT); pinMode(Sm2135.data, OUTPUT); digitalWrite(Sm2135.data, HIGH); diff --git a/tasmota/xlgt_05_sonoff_l1.ino b/tasmota/xlgt_05_sonoff_l1.ino index 3caf1b06d..295330142 100644 --- a/tasmota/xlgt_05_sonoff_l1.ino +++ b/tasmota/xlgt_05_sonoff_l1.ino @@ -221,7 +221,7 @@ bool SnfL1SetChannels(void) void SnfL1ModuleSelected(void) { if (SONOFF_L1 == my_module_type) { - if ((pin[GPIO_RXD] < 99) && (pin[GPIO_TXD] < 99)) { + if (PinUsed(GPIO_RXD) && PinUsed(GPIO_TXD)) { SetSerial(19200, TS_SERIAL_8N1); light_type = LT_RGB; diff --git a/tasmota/xlgt_06_electriq_moodl.ino b/tasmota/xlgt_06_electriq_moodl.ino index 7e972f968..57c493e64 100644 --- a/tasmota/xlgt_06_electriq_moodl.ino +++ b/tasmota/xlgt_06_electriq_moodl.ino @@ -70,7 +70,7 @@ bool ElectriqMoodLSetChannels(void) void ElectriqMoodLModuleSelected(void) { - if (pin[GPIO_ELECTRIQ_MOODL_TX] < 99) { + if (PinUsed(GPIO_ELECTRIQ_MOODL_TX)) { SetSerial(9600, TS_SERIAL_8N1); light_type = LT_RGBW; light_flg = XLGT_06; diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino index 4cbe111cd..d0c17353a 100644 --- a/tasmota/xnrg_01_hlw8012.ino +++ b/tasmota/xnrg_01_hlw8012.ino @@ -138,12 +138,12 @@ void HlwEvery200ms(void) } } - if (pin[GPIO_NRG_CF1] < 99) { + if (PinUsed(GPIO_NRG_CF1)) { Hlw.cf1_timer++; if (Hlw.cf1_timer >= 8) { Hlw.cf1_timer = 0; Hlw.select_ui_flag = (Hlw.select_ui_flag) ? false : true; - DigitalWrite(GPIO_NRG_SEL, Hlw.select_ui_flag); + DigitalWrite(GPIO_NRG_SEL, 0, Hlw.select_ui_flag); if (Hlw.cf1_pulse_counter) { cf1_pulse_length = Hlw.cf1_summed_pulse_length / Hlw.cf1_pulse_counter; @@ -233,38 +233,36 @@ void HlwSnsInit(void) Hlw.current_ratio = HLW_IREF; } - if (pin[GPIO_NRG_SEL] < 99) { - pinMode(pin[GPIO_NRG_SEL], OUTPUT); - digitalWrite(pin[GPIO_NRG_SEL], Hlw.select_ui_flag); + if (PinUsed(GPIO_NRG_SEL)) { + pinMode(Pin(GPIO_NRG_SEL), OUTPUT); + digitalWrite(Pin(GPIO_NRG_SEL), Hlw.select_ui_flag); } - if (pin[GPIO_NRG_CF1] < 99) { - pinMode(pin[GPIO_NRG_CF1], INPUT_PULLUP); - attachInterrupt(pin[GPIO_NRG_CF1], HlwCf1Interrupt, FALLING); + if (PinUsed(GPIO_NRG_CF1)) { + pinMode(Pin(GPIO_NRG_CF1), INPUT_PULLUP); + attachInterrupt(Pin(GPIO_NRG_CF1), HlwCf1Interrupt, FALLING); } - pinMode(pin[GPIO_HLW_CF], INPUT_PULLUP); - attachInterrupt(pin[GPIO_HLW_CF], HlwCfInterrupt, FALLING); + pinMode(Pin(GPIO_HLW_CF), INPUT_PULLUP); + attachInterrupt(Pin(GPIO_HLW_CF), HlwCfInterrupt, FALLING); } void HlwDrvInit(void) { Hlw.model_type = 0; // HLW8012 - if (pin[GPIO_HJL_CF] < 99) { - pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; - pin[GPIO_HJL_CF] = 99; + if (PinUsed(GPIO_HJL_CF)) { + SetPin(Pin(GPIO_HJL_CF), GPIO_HLW_CF); Hlw.model_type = 1; // HJL-01/BL0937 } - if (pin[GPIO_HLW_CF] < 99) { // HLW8012 or HJL-01 based device Power monitor + if (PinUsed(GPIO_HLW_CF)) { // HLW8012 or HJL-01 based device Power monitor Hlw.ui_flag = true; // Voltage on high - if (pin[GPIO_NRG_SEL_INV] < 99) { - pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; - pin[GPIO_NRG_SEL_INV] = 99; + if (PinUsed(GPIO_NRG_SEL_INV)) { + SetPin(Pin(GPIO_NRG_SEL_INV), GPIO_NRG_SEL); Hlw.ui_flag = false; // Voltage on low } - if (pin[GPIO_NRG_CF1] < 99) { // Voltage and/or Current monitor - if (99 == pin[GPIO_NRG_SEL]) { // Voltage and/or Current selector + if (PinUsed(GPIO_NRG_CF1)) { // Voltage and/or Current monitor + if (!PinUsed(GPIO_NRG_SEL)) { // Voltage and/or Current selector Energy.current_available = false; // Assume Voltage } } else { diff --git a/tasmota/xnrg_02_cse7766.ino b/tasmota/xnrg_02_cse7766.ino index d7bb93665..dae622845 100644 --- a/tasmota/xnrg_02_cse7766.ino +++ b/tasmota/xnrg_02_cse7766.ino @@ -23,6 +23,8 @@ * CSE7759 and CSE7766 - Energy (Sonoff S31 and Sonoff Pow R2) * HLW8032 - Energy (Blitzwolf SHP5) * + * Needs GPIO_CSE7766_RX only + * * Based on datasheet from http://www.chipsea.com/UploadFiles/2017/08/11144342F01B5662.pdf \*********************************************************************************************/ @@ -223,8 +225,8 @@ void CseEverySecond(void) void CseSnsInit(void) { // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions -// CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], pin[GPIO_CSE7766_TX], 1); - CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], -1, 1); +// CseSerial = new TasmotaSerial(Pin(GPIO_CSE7766_RX), Pin(GPIO_CSE7766_TX), 1); + CseSerial = new TasmotaSerial(Pin(GPIO_CSE7766_RX), -1, 1); if (CseSerial->begin(4800, 2)) { // Fake Software Serial 8E1 by using two stop bits if (CseSerial->hardwareSerial()) { SetSerial(4800, TS_SERIAL_8E1); @@ -243,8 +245,8 @@ void CseDrvInit(void) { Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE)); if (Cse.rx_buffer != nullptr) { -// if ((pin[GPIO_CSE7766_RX] < 99) && (pin[GPIO_CSE7766_TX] < 99)) { - if (pin[GPIO_CSE7766_RX] < 99) { +// if (PinUsed(GPIO_CSE7766_RX) && PinUsed(GPIO_CSE7766_TX)) { + if (PinUsed(GPIO_CSE7766_RX)) { energy_flg = XNRG_02; } } diff --git a/tasmota/xnrg_03_pzem004t.ino b/tasmota/xnrg_03_pzem004t.ino index 466298b0f..f06b34b0e 100644 --- a/tasmota/xnrg_03_pzem004t.ino +++ b/tasmota/xnrg_03_pzem004t.ino @@ -241,7 +241,7 @@ void PzemEvery250ms(void) void PzemSnsInit(void) { // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions - PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1); + PzemSerial = new TasmotaSerial(Pin(GPIO_PZEM004_RX), Pin(GPIO_PZEM0XX_TX), 1); if (PzemSerial->begin(9600)) { if (PzemSerial->hardwareSerial()) { ClaimSerial(); @@ -256,7 +256,7 @@ void PzemSnsInit(void) void PzemDrvInit(void) { - if ((pin[GPIO_PZEM004_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { // Any device with a Pzem004T + if (PinUsed(GPIO_PZEM004_RX) && PinUsed(GPIO_PZEM0XX_TX)) { // Any device with a Pzem004T energy_flg = XNRG_03; } } diff --git a/tasmota/xnrg_04_mcp39f501.ino b/tasmota/xnrg_04_mcp39f501.ino index 92eec2013..b3f8dbe6c 100644 --- a/tasmota/xnrg_04_mcp39f501.ino +++ b/tasmota/xnrg_04_mcp39f501.ino @@ -562,7 +562,7 @@ void McpEverySecond(void) void McpSnsInit(void) { // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions - McpSerial = new TasmotaSerial(pin[GPIO_MCP39F5_RX], pin[GPIO_MCP39F5_TX], 1); + McpSerial = new TasmotaSerial(Pin(GPIO_MCP39F5_RX), Pin(GPIO_MCP39F5_TX), 1); if (McpSerial->begin(MCP_BAUDRATE)) { if (McpSerial->hardwareSerial()) { ClaimSerial(); @@ -570,7 +570,7 @@ void McpSnsInit(void) } else { mcp_buffer = (char*)(malloc(MCP_BUFFER_SIZE)); } - DigitalWrite(GPIO_MCP39F5_RST, 1); // MCP enable + DigitalWrite(GPIO_MCP39F5_RST, 0, 1); // MCP enable } else { energy_flg = ENERGY_NONE; } @@ -578,10 +578,10 @@ void McpSnsInit(void) void McpDrvInit(void) { - if ((pin[GPIO_MCP39F5_RX] < 99) && (pin[GPIO_MCP39F5_TX] < 99)) { - if (pin[GPIO_MCP39F5_RST] < 99) { - pinMode(pin[GPIO_MCP39F5_RST], OUTPUT); - digitalWrite(pin[GPIO_MCP39F5_RST], 0); // MCP disable - Reset Delta Sigma ADC's + if (PinUsed(GPIO_MCP39F5_RX) && PinUsed(GPIO_MCP39F5_TX)) { + if (PinUsed(GPIO_MCP39F5_RST)) { + pinMode(Pin(GPIO_MCP39F5_RST), OUTPUT); + digitalWrite(Pin(GPIO_MCP39F5_RST), 0); // MCP disable - Reset Delta Sigma ADC's } mcp_calibrate = 0; mcp_timeout = 2; // Initial wait diff --git a/tasmota/xnrg_05_pzem_ac.ino b/tasmota/xnrg_05_pzem_ac.ino index 86c4355aa..bd656a874 100644 --- a/tasmota/xnrg_05_pzem_ac.ino +++ b/tasmota/xnrg_05_pzem_ac.ino @@ -117,7 +117,7 @@ void PzemAcEverySecond(void) void PzemAcSnsInit(void) { - PzemAcModbus = new TasmotaModbus(pin[GPIO_PZEM016_RX], pin[GPIO_PZEM0XX_TX]); + PzemAcModbus = new TasmotaModbus(Pin(GPIO_PZEM016_RX), Pin(GPIO_PZEM0XX_TX)); uint8_t result = PzemAcModbus->Begin(9600); if (result) { if (2 == result) { ClaimSerial(); } @@ -130,7 +130,7 @@ void PzemAcSnsInit(void) void PzemAcDrvInit(void) { - if ((pin[GPIO_PZEM016_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + if (PinUsed(GPIO_PZEM016_RX) && PinUsed(GPIO_PZEM0XX_TX)) { energy_flg = XNRG_05; } } diff --git a/tasmota/xnrg_06_pzem_dc.ino b/tasmota/xnrg_06_pzem_dc.ino index 961e4f854..444861fb0 100644 --- a/tasmota/xnrg_06_pzem_dc.ino +++ b/tasmota/xnrg_06_pzem_dc.ino @@ -113,7 +113,7 @@ void PzemDcEverySecond(void) void PzemDcSnsInit(void) { - PzemDcModbus = new TasmotaModbus(pin[GPIO_PZEM017_RX], pin[GPIO_PZEM0XX_TX]); + PzemDcModbus = new TasmotaModbus(Pin(GPIO_PZEM017_RX), Pin(GPIO_PZEM0XX_TX)); uint8_t result = PzemDcModbus->Begin(9600, 2); // Uses two stop bits!! if (result) { if (2 == result) { ClaimSerial(); } @@ -127,7 +127,7 @@ void PzemDcSnsInit(void) void PzemDcDrvInit(void) { - if ((pin[GPIO_PZEM017_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + if (PinUsed(GPIO_PZEM017_RX) && PinUsed(GPIO_PZEM0XX_TX)) { energy_flg = XNRG_06; } } diff --git a/tasmota/xnrg_07_ade7953.ino b/tasmota/xnrg_07_ade7953.ino index 46f2ebd81..44bec2e59 100644 --- a/tasmota/xnrg_07_ade7953.ino +++ b/tasmota/xnrg_07_ade7953.ino @@ -199,7 +199,7 @@ void Ade7953EnergyEverySecond(void) void Ade7953DrvInit(void) { - if (pin[GPIO_ADE7953_IRQ] < 99) { // Irq on GPIO16 is not supported... + if (PinUsed(GPIO_ADE7953_IRQ)) { // Irq on GPIO16 is not supported... delay(100); // Need 100mS to init ADE7953 if (I2cSetDevice(ADE7953_ADDR)) { if (HLW_PREF_PULSE == Settings.energy_power_calibration) { diff --git a/tasmota/xnrg_08_sdm120.ino b/tasmota/xnrg_08_sdm120.ino index e41323a73..93af326f8 100644 --- a/tasmota/xnrg_08_sdm120.ino +++ b/tasmota/xnrg_08_sdm120.ino @@ -176,7 +176,7 @@ void SDM120Every250ms(void) void Sdm120SnsInit(void) { - Sdm120Modbus = new TasmotaModbus(pin[GPIO_SDM120_RX], pin[GPIO_SDM120_TX]); + Sdm120Modbus = new TasmotaModbus(Pin(GPIO_SDM120_RX), Pin(GPIO_SDM120_TX)); uint8_t result = Sdm120Modbus->Begin(SDM120_SPEED); if (result) { if (2 == result) { ClaimSerial(); } @@ -187,7 +187,7 @@ void Sdm120SnsInit(void) void Sdm120DrvInit(void) { - if ((pin[GPIO_SDM120_RX] < 99) && (pin[GPIO_SDM120_TX] < 99)) { + if (PinUsed(GPIO_SDM120_RX) && PinUsed(GPIO_SDM120_TX)) { energy_flg = XNRG_08; } } diff --git a/tasmota/xnrg_09_dds2382.ino b/tasmota/xnrg_09_dds2382.ino index aea1f03ea..a516ea4a8 100644 --- a/tasmota/xnrg_09_dds2382.ino +++ b/tasmota/xnrg_09_dds2382.ino @@ -91,7 +91,7 @@ void Dds2382EverySecond(void) void Dds2382SnsInit(void) { - Dds2382Modbus = new TasmotaModbus(pin[GPIO_DDS2382_RX], pin[GPIO_DDS2382_TX]); + Dds2382Modbus = new TasmotaModbus(Pin(GPIO_DDS2382_RX), Pin(GPIO_DDS2382_TX)); uint8_t result = Dds2382Modbus->Begin(DDS2382_SPEED); if (result) { if (2 == result) { ClaimSerial(); } @@ -102,7 +102,7 @@ void Dds2382SnsInit(void) void Dds2382DrvInit(void) { - if ((pin[GPIO_DDS2382_RX] < 99) && (pin[GPIO_DDS2382_TX] < 99)) { + if (PinUsed(GPIO_DDS2382_RX) && PinUsed(GPIO_DDS2382_TX)) { energy_flg = XNRG_09; } } diff --git a/tasmota/xnrg_10_sdm630.ino b/tasmota/xnrg_10_sdm630.ino index 81b8bdcbf..4bfeecc96 100644 --- a/tasmota/xnrg_10_sdm630.ino +++ b/tasmota/xnrg_10_sdm630.ino @@ -174,7 +174,7 @@ void SDM630Every250ms(void) void Sdm630SnsInit(void) { - Sdm630Modbus = new TasmotaModbus(pin[GPIO_SDM630_RX], pin[GPIO_SDM630_TX]); + Sdm630Modbus = new TasmotaModbus(Pin(GPIO_SDM630_RX), Pin(GPIO_SDM630_TX)); uint8_t result = Sdm630Modbus->Begin(SDM630_SPEED); if (result) { if (2 == result) { ClaimSerial(); } @@ -186,7 +186,7 @@ void Sdm630SnsInit(void) void Sdm630DrvInit(void) { - if ((pin[GPIO_SDM630_RX] < 99) && (pin[GPIO_SDM630_TX] < 99)) { + if (PinUsed(GPIO_SDM630_RX) && PinUsed(GPIO_SDM630_TX)) { energy_flg = XNRG_10; } } diff --git a/tasmota/xnrg_11_ddsu666.ino b/tasmota/xnrg_11_ddsu666.ino index 6299f4f4a..87b585d84 100644 --- a/tasmota/xnrg_11_ddsu666.ino +++ b/tasmota/xnrg_11_ddsu666.ino @@ -133,7 +133,7 @@ void DDSU666Every250ms(void) void Ddsu666SnsInit(void) { - Ddsu666Modbus = new TasmotaModbus(pin[GPIO_DDSU666_RX], pin[GPIO_DDSU666_TX]); + Ddsu666Modbus = new TasmotaModbus(Pin(GPIO_DDSU666_RX), Pin(GPIO_DDSU666_TX)); uint8_t result = Ddsu666Modbus->Begin(DDSU666_SPEED); if (result) { if (2 == result) { ClaimSerial(); } @@ -144,7 +144,7 @@ void Ddsu666SnsInit(void) void Ddsu666DrvInit(void) { - if ((pin[GPIO_DDSU666_RX] < 99) && (pin[GPIO_DDSU666_TX] < 99)) { + if (PinUsed(GPIO_DDSU666_RX) && PinUsed(GPIO_DDSU666_TX)) { energy_flg = XNRG_11; } } diff --git a/tasmota/xnrg_12_solaxX1.ino b/tasmota/xnrg_12_solaxX1.ino index e9da3147d..418a92acb 100644 --- a/tasmota/xnrg_12_solaxX1.ino +++ b/tasmota/xnrg_12_solaxX1.ino @@ -406,10 +406,10 @@ void solaxX1250MSecond(void) // Every Second void solaxX1SnsInit(void) { AddLog_P(LOG_LEVEL_DEBUG, PSTR("SX1: Solax X1 Inverter Init")); - DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX]); + DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX)); protocolStatus.status = 0b00100000; // hasAddress - solaxX1Serial = new TasmotaSerial(pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX], 1); + solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1); if (solaxX1Serial->begin(SOLAXX1_SPEED)) { if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } } else { @@ -419,7 +419,7 @@ void solaxX1SnsInit(void) void solaxX1DrvInit(void) { - if ((pin[GPIO_SOLAXX1_RX] < 99) && (pin[GPIO_SOLAXX1_TX] < 99)) { + if (PinUsed(GPIO_SOLAXX1_RX) && PinUsed(GPIO_SOLAXX1_TX)) { energy_flg = XNRG_12; } } diff --git a/tasmota/xnrg_13_fif_le01mr.ino b/tasmota/xnrg_13_fif_le01mr.ino index 18f45065c..71e77793a 100644 --- a/tasmota/xnrg_13_fif_le01mr.ino +++ b/tasmota/xnrg_13_fif_le01mr.ino @@ -213,7 +213,7 @@ void FifLEEvery250ms(void) void FifLESnsInit(void) { - FifLEModbus = new TasmotaModbus(pin[GPIO_LE01MR_RX], pin[GPIO_LE01MR_TX]); + FifLEModbus = new TasmotaModbus(Pin(GPIO_LE01MR_RX), Pin(GPIO_LE01MR_TX)); uint8_t result = FifLEModbus->Begin(LE01MR_SPEED); if (result) { if (2 == result) { ClaimSerial(); } @@ -224,7 +224,7 @@ void FifLESnsInit(void) void FifLEDrvInit(void) { - if ((pin[GPIO_LE01MR_RX] < 99) && (pin[GPIO_LE01MR_TX] < 99)) { + if (PinUsed(GPIO_LE01MR_RX) && PinUsed(GPIO_LE01MR_TX)) { energy_flg = XNRG_13; } } diff --git a/tasmota/xsns_01_counter.ino b/tasmota/xsns_01_counter.ino index 663d9413a..fec5791f7 100644 --- a/tasmota/xsns_01_counter.ino +++ b/tasmota/xsns_01_counter.ino @@ -59,7 +59,7 @@ void CounterUpdate(uint8_t index) if (Counter.pin_state) { // handle low and high debounce times when configured - if (digitalRead(pin[GPIO_CNTR1 +index]) == bitRead(Counter.pin_state, index)) { + if (digitalRead(Pin(GPIO_CNTR1, index)) == bitRead(Counter.pin_state, index)) { // new pin state to be ignored because debounce time was not met during last IRQ return; } @@ -127,15 +127,15 @@ void CounterInit(void) function counter_callbacks[] = { CounterUpdate1, CounterUpdate2, CounterUpdate3, CounterUpdate4 }; for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { + if (PinUsed(GPIO_CNTR1, i)) { Counter.any_counter = true; - pinMode(pin[GPIO_CNTR1 +i], bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); + pinMode(Pin(GPIO_CNTR1, i), bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); if ((0 == Settings.pulse_counter_debounce_low) && (0 == Settings.pulse_counter_debounce_high)) { Counter.pin_state = 0; - attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); + attachInterrupt(Pin(GPIO_CNTR1, i), counter_callbacks[i], FALLING); } else { Counter.pin_state = 0x8f; - attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], CHANGE); + attachInterrupt(Pin(GPIO_CNTR1, i), counter_callbacks[i], CHANGE); } } } @@ -144,7 +144,7 @@ void CounterInit(void) void CounterEverySecond(void) { for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { + if (PinUsed(GPIO_CNTR1, i)) { if (bitRead(Settings.pulse_counter_type, i)) { uint32_t time = micros() - Counter.timer[i]; if (time > 4200000000) { // 70 minutes @@ -158,7 +158,7 @@ void CounterEverySecond(void) void CounterSaveState(void) { for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { + if (PinUsed(GPIO_CNTR1, i)) { Settings.pulse_counter[i] = RtcSettings.pulse_counter[i]; } } @@ -169,7 +169,7 @@ void CounterShow(bool json) bool header = false; uint8_t dsxflg = 0; for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { + if (PinUsed(GPIO_CNTR1, i)) { char counter[33]; if (bitRead(Settings.pulse_counter_type, i)) { dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter); @@ -213,7 +213,7 @@ void CounterShow(bool json) void CmndCounter(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { - if ((XdrvMailbox.data_len > 0) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { + if ((XdrvMailbox.data_len > 0) && PinUsed(GPIO_CNTR1, XdrvMailbox.index -1)) { if ((XdrvMailbox.data[0] == '-') || (XdrvMailbox.data[0] == '+')) { RtcSettings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; Settings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; @@ -229,7 +229,7 @@ void CmndCounter(void) void CmndCounterType(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && PinUsed(GPIO_CNTR1, XdrvMailbox.index -1)) { bitWrite(Settings.pulse_counter_type, XdrvMailbox.index -1, XdrvMailbox.payload &1); RtcSettings.pulse_counter[XdrvMailbox.index -1] = 0; Settings.pulse_counter[XdrvMailbox.index -1] = 0; diff --git a/tasmota/xsns_02_analog.ino b/tasmota/xsns_02_analog.ino index 038da9fa5..235a91bdd 100644 --- a/tasmota/xsns_02_analog.ino +++ b/tasmota/xsns_02_analog.ino @@ -166,18 +166,30 @@ void AdcGetCurrentPower(uint8_t factor) uint16_t analog = 0; uint16_t analog_min = 1023; uint16_t analog_max = 0; - for (uint32_t i = 0; i < samples; i++) { - analog = analogRead(A0); - if (analog < analog_min) { - analog_min = analog; + + if (0 == Settings.adc_param1) { + for (uint32_t i = 0; i < samples; i++) { + analog = analogRead(A0); + if (analog < analog_min) { + analog_min = analog; + } + if (analog > analog_max) { + analog_max = analog; + } + delay(1); } - if (analog > analog_max) { - analog_max = analog; + Adc.current = (float)(analog_max-analog_min) * ((float)(Settings.adc_param2) / 100000); + } + else { + analog = AdcRead(5); + if (analog > Settings.adc_param1) { + Adc.current = ((float)(analog) - (float)Settings.adc_param1) * ((float)(Settings.adc_param2) / 100000); + } + else { + Adc.current = 0; } - delay(1); } - Adc.current = (float)(analog_max-analog_min) * ((float)(Settings.adc_param2) / 100000); float power = Adc.current * (float)(Settings.adc_param3) / 10; uint32_t current_millis = millis(); Adc.energy = Adc.energy + ((power * (current_millis - Adc.previous_millis)) / 3600000000); @@ -305,11 +317,18 @@ void AdcShow(bool json) \*********************************************************************************************/ const char kAdcCommands[] PROGMEM = "|" // No prefix - D_CMND_ADC "|" D_CMND_ADCS "|" D_CMND_ADCPARAM; +#ifdef ESP8266 + D_CMND_ADC "|" D_CMND_ADCS "|" +#endif // ESP8266 + D_CMND_ADCPARAM; void (* const AdcCommand[])(void) PROGMEM = { - &CmndAdc, &CmndAdcs, &CmndAdcParam }; +#ifdef ESP8266 + &CmndAdc, &CmndAdcs, +#endif // ESP8266 + &CmndAdcParam }; +#ifdef ESP8266 void CmndAdc(void) { if (ValidAdc() && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < ADC0_END)) { @@ -334,6 +353,7 @@ void CmndAdcs(void) } ResponseJsonEndEnd(); } +#endif // ESP8266 void CmndAdcParam(void) { @@ -357,7 +377,7 @@ void CmndAdcParam(void) Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); } if (ADC0_CT_POWER == XdrvMailbox.payload) { - if ((Settings.adc_param1 & CT_FLAG_ENERGY_RESET) > 0) { + if (((1 == Settings.adc_param1) & CT_FLAG_ENERGY_RESET) > 0) { Adc.energy = 0; Settings.adc_param1 ^= CT_FLAG_ENERGY_RESET; // Cancel energy reset flag } @@ -435,4 +455,4 @@ bool Xsns02(uint8_t function) return result; } -#endif // USE_ADC_VCC +#endif // USE_ADC_VCC \ No newline at end of file diff --git a/tasmota/xsns_05_ds18x20.ino b/tasmota/xsns_05_ds18x20.ino index 046fb11c4..a762014c7 100644 --- a/tasmota/xsns_05_ds18x20.ino +++ b/tasmota/xsns_05_ds18x20.ino @@ -304,10 +304,10 @@ void Ds18x20Init(void) { uint64_t ids[DS18X20_MAX_SENSORS]; - ds18x20_pin = pin[GPIO_DSB]; + ds18x20_pin = Pin(GPIO_DSB); - if (pin[GPIO_DSB_OUT] < 99) { - ds18x20_pin_out = pin[GPIO_DSB_OUT]; + if (PinUsed(GPIO_DSB_OUT)) { + ds18x20_pin_out = Pin(GPIO_DSB_OUT); ds18x20_dual_mode = true; // Dual pins mode as used by Shelly pinMode(ds18x20_pin_out, OUTPUT); pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); // SetOption74 - Enable internal pullup for single DS18x20 sensor @@ -518,7 +518,7 @@ bool Xsns05(uint8_t function) { bool result = false; - if (pin[GPIO_DSB] < 99) { + if (PinUsed(GPIO_DSB)) { switch (function) { case FUNC_INIT: Ds18x20Init(); diff --git a/tasmota/xsns_06_dht.ino b/tasmota/xsns_06_dht.ino index 2edc4abfa..46fb69fa5 100644 --- a/tasmota/xsns_06_dht.ino +++ b/tasmota/xsns_06_dht.ino @@ -204,8 +204,8 @@ bool DhtPinState() void DhtInit(void) { if (dht_sensors) { - if (pin[GPIO_DHT11_OUT] < 99) { - dht_pin_out = pin[GPIO_DHT11_OUT]; + if (PinUsed(GPIO_DHT11_OUT)) { + dht_pin_out = Pin(GPIO_DHT11_OUT); dht_dual_mode = true; // Dual pins mode as used by Shelly dht_sensors = 1; // We only support one sensor in pseudo mode pinMode(dht_pin_out, OUTPUT); diff --git a/tasmota/xsns_07_sht1x.ino b/tasmota/xsns_07_sht1x.ino index 735802ca2..e58c5553f 100644 --- a/tasmota/xsns_07_sht1x.ino +++ b/tasmota/xsns_07_sht1x.ino @@ -159,8 +159,8 @@ bool ShtRead(void) void ShtDetect(void) { - sht_sda_pin = pin[GPIO_I2C_SDA]; - sht_scl_pin = pin[GPIO_I2C_SCL]; + sht_sda_pin = Pin(GPIO_I2C_SDA); + sht_scl_pin = Pin(GPIO_I2C_SCL); if (ShtRead()) { sht_type = 1; AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SHT1X_FOUND)); diff --git a/tasmota/xsns_08_htu21.ino b/tasmota/xsns_08_htu21.ino index e0cab5d1c..1dc4d4fb1 100644 --- a/tasmota/xsns_08_htu21.ino +++ b/tasmota/xsns_08_htu21.ino @@ -59,14 +59,18 @@ const char kHtuTypes[] PROGMEM = "HTU21|SI7013|SI7020|SI7021|T/RH?"; -uint8_t htu_address; -uint8_t htu_type = 0; -uint8_t htu_delay_temp; -uint8_t htu_delay_humidity = 50; -uint8_t htu_valid = 0; -float htu_temperature = 0; -float htu_humidity = 0; -char htu_types[7]; +struct { + float temperature = 0; + float humidity = 0; + uint8_t address; + uint8_t type = 0; + uint8_t delay_temp; + uint8_t delay_humidity = 50; + uint8_t valid = 0; + char types[7]; +} Htu; + +/*********************************************************************************************/ uint8_t HtuCheckCrc8(uint16_t data) { @@ -82,6 +86,8 @@ uint8_t HtuCheckCrc8(uint16_t data) uint8_t HtuReadDeviceId(void) { + HtuReset(); // Fixes ESP32 sensor loss at restart + uint16_t deviceID = 0; uint8_t checksum = 0; @@ -146,12 +152,12 @@ bool HtuRead(void) uint8_t checksum = 0; uint16_t sensorval = 0; - if (htu_valid) { htu_valid--; } + if (Htu.valid) { Htu.valid--; } Wire.beginTransmission(HTU21_ADDR); Wire.write(HTU21_READTEMP); if (Wire.endTransmission() != 0) { return false; } // In case of error - delay(htu_delay_temp); // Sensor time at max resolution + delay(Htu.delay_temp); // Sensor time at max resolution Wire.requestFrom(HTU21_ADDR, 3); if (3 == Wire.available()) { @@ -161,12 +167,12 @@ bool HtuRead(void) } if (HtuCheckCrc8(sensorval) != checksum) { return false; } // Checksum mismatch - htu_temperature = ConvertTemp(0.002681 * (float)sensorval - 46.85); + Htu.temperature = ConvertTemp(0.002681 * (float)sensorval - 46.85); Wire.beginTransmission(HTU21_ADDR); Wire.write(HTU21_READHUM); if (Wire.endTransmission() != 0) { return false; } // In case of error - delay(htu_delay_humidity); // Sensor time at max resolution + delay(Htu.delay_humidity); // Sensor time at max resolution Wire.requestFrom(HTU21_ADDR, 3); if (3 <= Wire.available()) { @@ -177,19 +183,19 @@ bool HtuRead(void) if (HtuCheckCrc8(sensorval) != checksum) { return false; } // Checksum mismatch sensorval ^= 0x02; // clear status bits - htu_humidity = 0.001907 * (float)sensorval - 6; - if (htu_humidity > 100) { htu_humidity = 100.0; } - if (htu_humidity < 0) { htu_humidity = 0.01; } + Htu.humidity = 0.001907 * (float)sensorval - 6; + if (Htu.humidity > 100) { Htu.humidity = 100.0; } + if (Htu.humidity < 0) { Htu.humidity = 0.01; } - if ((0.00 == htu_humidity) && (0.00 == htu_temperature)) { - htu_humidity = 0.0; + if ((0.00 == Htu.humidity) && (0.00 == Htu.temperature)) { + Htu.humidity = 0.0; } - if ((htu_temperature > 0.00) && (htu_temperature < 80.00)) { - htu_humidity = (-0.15) * (25 - htu_temperature) + htu_humidity; + if ((Htu.temperature > 0.00) && (Htu.temperature < 80.00)) { + Htu.humidity = (-0.15) * (25 - Htu.temperature) + Htu.humidity; } - htu_humidity = ConvertHumidity(htu_humidity); + Htu.humidity = ConvertHumidity(Htu.humidity); - htu_valid = SENSOR_MAX_MISS; + Htu.valid = SENSOR_MAX_MISS; return true; } @@ -197,17 +203,17 @@ bool HtuRead(void) void HtuDetect(void) { - htu_address = HTU21_ADDR; - if (I2cActive(htu_address)) { return; } + Htu.address = HTU21_ADDR; + if (I2cActive(Htu.address)) { return; } - htu_type = HtuReadDeviceId(); - if (htu_type) { + Htu.type = HtuReadDeviceId(); + if (Htu.type) { uint8_t index = 0; HtuInit(); - switch (htu_type) { + switch (Htu.type) { case HTU21_CHIPID: - htu_delay_temp = 50; - htu_delay_humidity = 16; + Htu.delay_temp = 50; + Htu.delay_humidity = 16; break; case SI7021_CHIPID: index++; // 3 @@ -215,16 +221,16 @@ void HtuDetect(void) index++; // 2 case SI7013_CHIPID: index++; // 1 - htu_delay_temp = 12; - htu_delay_humidity = 23; + Htu.delay_temp = 12; + Htu.delay_humidity = 23; break; default: index = 4; - htu_delay_temp = 50; - htu_delay_humidity = 23; + Htu.delay_temp = 50; + Htu.delay_humidity = 23; } - GetTextIndexed(htu_types, sizeof(htu_types), index, kHtuTypes); - I2cSetActiveFound(htu_address, htu_types); + GetTextIndexed(Htu.types, sizeof(Htu.types), index, kHtuTypes); + I2cSetActiveFound(Htu.address, Htu.types); } } @@ -233,15 +239,15 @@ void HtuEverySecond(void) if (uptime &1) { // Every 2 seconds // HTU21: 68mS, SI70xx: 37mS if (!HtuRead()) { - AddLogMissed(htu_types, htu_valid); + AddLogMissed(Htu.types, Htu.valid); } } } void HtuShow(bool json) { - if (htu_valid) { - TempHumDewShow(json, (0 == tele_period), htu_types, htu_temperature, htu_humidity); + if (Htu.valid) { + TempHumDewShow(json, (0 == tele_period), Htu.types, Htu.temperature, Htu.humidity); } } @@ -258,7 +264,7 @@ bool Xsns08(uint8_t function) if (FUNC_INIT == function) { HtuDetect(); } - else if (htu_type) { + else if (Htu.type) { switch (function) { case FUNC_EVERY_SECOND: HtuEverySecond(); diff --git a/tasmota/xsns_15_mhz19.ino b/tasmota/xsns_15_mhz19.ino index 633ca3f59..bc0ee8904 100644 --- a/tasmota/xsns_15_mhz19.ino +++ b/tasmota/xsns_15_mhz19.ino @@ -325,8 +325,8 @@ bool MhzCommandSensor(void) void MhzInit(void) { mhz_type = 0; - if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { - MhzSerial = new TasmotaSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD], 1); + if (PinUsed(GPIO_MHZ_RXD) && PinUsed(GPIO_MHZ_TXD)) { + MhzSerial = new TasmotaSerial(Pin(GPIO_MHZ_RXD), Pin(GPIO_MHZ_TXD), 1); if (MhzSerial->begin(9600)) { if (MhzSerial->hardwareSerial()) { ClaimSerial(); } mhz_type = 1; diff --git a/tasmota/xsns_17_senseair.ino b/tasmota/xsns_17_senseair.ino index 60614b4c7..90ba979bb 100644 --- a/tasmota/xsns_17_senseair.ino +++ b/tasmota/xsns_17_senseair.ino @@ -132,8 +132,8 @@ void Senseair250ms(void) // Every 250 mSec void SenseairInit(void) { senseair_type = 0; - if ((pin[GPIO_SAIR_RX] < 99) && (pin[GPIO_SAIR_TX] < 99)) { - SenseairModbus = new TasmotaModbus(pin[GPIO_SAIR_RX], pin[GPIO_SAIR_TX]); + if (PinUsed(GPIO_SAIR_RX) && PinUsed(GPIO_SAIR_TX)) { + SenseairModbus = new TasmotaModbus(Pin(GPIO_SAIR_RX), Pin(GPIO_SAIR_TX)); uint8_t result = SenseairModbus->Begin(SENSEAIR_MODBUS_SPEED); if (result) { if (2 == result) { ClaimSerial(); } diff --git a/tasmota/xsns_18_pms5003.ino b/tasmota/xsns_18_pms5003.ino index 525012f5a..d54527ddc 100644 --- a/tasmota/xsns_18_pms5003.ino +++ b/tasmota/xsns_18_pms5003.ino @@ -171,7 +171,7 @@ bool PmsReadData(void) bool PmsCommandSensor(void) { - if ((pin[GPIO_PMS5003_TX] < 99) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + if (PinUsed(GPIO_PMS5003_TX) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { if (XdrvMailbox.payload < MIN_INTERVAL_PERIOD) { // Set Active Mode if interval is less than 60 seconds Settings.pms_wake_interval = 0; @@ -239,12 +239,12 @@ void PmsSecond(void) // Every second void PmsInit(void) { Pms.type = 0; - if (pin[GPIO_PMS5003_RX] < 99) { - PmsSerial = new TasmotaSerial(pin[GPIO_PMS5003_RX], (pin[GPIO_PMS5003_TX] < 99) ? pin[GPIO_PMS5003_TX] : -1, 1); + if (PinUsed(GPIO_PMS5003_RX)) { + PmsSerial = new TasmotaSerial(Pin(GPIO_PMS5003_RX), (PinUsed(GPIO_PMS5003_TX)) ? Pin(GPIO_PMS5003_TX) : -1, 1); if (PmsSerial->begin(9600)) { if (PmsSerial->hardwareSerial()) { ClaimSerial(); } - if (99 == pin[GPIO_PMS5003_TX]) { // setting interval not supported if TX pin not connected + if (!PinUsed(GPIO_PMS5003_TX)) { // setting interval not supported if TX pin not connected Settings.pms_wake_interval = 0; Pms.ready = 1; } diff --git a/tasmota/xsns_20_novasds.ino b/tasmota/xsns_20_novasds.ino index 0b1580dde..ffaee14bc 100644 --- a/tasmota/xsns_20_novasds.ino +++ b/tasmota/xsns_20_novasds.ino @@ -202,8 +202,8 @@ bool NovaSdsCommandSensor(void) void NovaSdsInit(void) { novasds_type = 0; - if (pin[GPIO_SDS0X1_RX] < 99 && pin[GPIO_SDS0X1_TX] < 99) { - NovaSdsSerial = new TasmotaSerial(pin[GPIO_SDS0X1_RX], pin[GPIO_SDS0X1_TX], 1); + if (PinUsed(GPIO_SDS0X1_RX) && PinUsed(GPIO_SDS0X1_TX)) { + NovaSdsSerial = new TasmotaSerial(Pin(GPIO_SDS0X1_RX), Pin(GPIO_SDS0X1_TX), 1); if (NovaSdsSerial->begin(9600)) { if (NovaSdsSerial->hardwareSerial()) { ClaimSerial(); diff --git a/tasmota/xsns_22_sr04.ino b/tasmota/xsns_22_sr04.ino index a23a38117..187a67c3f 100644 --- a/tasmota/xsns_22_sr04.ino +++ b/tasmota/xsns_22_sr04.ino @@ -40,10 +40,10 @@ TasmotaSerial* sonar_serial = nullptr; uint8_t Sr04TModeDetect(void) { sr04_type = 0; - if (99 == pin[GPIO_SR04_ECHO]) { return sr04_type; } + if (!PinUsed(GPIO_SR04_ECHO)) { return sr04_type; } - int sr04_echo_pin = pin[GPIO_SR04_ECHO]; - int sr04_trig_pin = (pin[GPIO_SR04_TRIG] < 99) ? pin[GPIO_SR04_TRIG] : -1; + int sr04_echo_pin = Pin(GPIO_SR04_ECHO); + int sr04_trig_pin = (PinUsed(GPIO_SR04_TRIG)) ? Pin(GPIO_SR04_TRIG) : Pin(GPIO_SR04_ECHO); // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only sonar_serial = new TasmotaSerial(sr04_echo_pin, sr04_trig_pin, 1); if (sonar_serial->begin(9600,1)) { @@ -62,7 +62,7 @@ uint8_t Sr04TModeDetect(void) delete sonar_serial; sonar_serial = nullptr; if (-1 == sr04_trig_pin) { - sr04_trig_pin = pin[GPIO_SR04_ECHO]; // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only + sr04_trig_pin = Pin(GPIO_SR04_ECHO); // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only } sonar = new NewPing(sr04_trig_pin, sr04_echo_pin, 300); } else { @@ -193,7 +193,7 @@ bool Xsns22(uint8_t function) if (sr04_type) { switch (function) { case FUNC_INIT: - result = (pin[GPIO_SR04_ECHO]<99); + result = (PinUsed(GPIO_SR04_ECHO)); break; case FUNC_EVERY_SECOND: Sr04TReading(); diff --git a/tasmota/xsns_27_apds9960.ino b/tasmota/xsns_27_apds9960.ino index 1751eb947..e0fe1acaf 100644 --- a/tasmota/xsns_27_apds9960.ino +++ b/tasmota/xsns_27_apds9960.ino @@ -33,12 +33,9 @@ * Source: Shawn Hymel (SparkFun Electronics) * Adaption for TASMOTA: Christian Baars * - * I2C Address: 0x39 + * I2C Address: 0x39 - standard address \*********************************************************************************************/ -#define XSNS_27 27 -#define XI2C_21 21 // See I2CDEVICES.md - // #if defined(USE_SHT) || defined(USE_VEML6070) || defined(USE_TSL2561) // #warning **** Turned off conflicting drivers SHT and VEML6070 **** // #ifdef USE_SHT @@ -52,6 +49,10 @@ // #endif // #endif +#define XSNS_27 27 +#define XI2C_21 21 // See I2CDEVICES.md + + #define APDS9960_I2C_ADDR 0x39 #define APDS9960_CHIPID_1 0xAB @@ -61,38 +62,47 @@ #define APDS9930_CHIPID_1 0x12 // we will check, if someone got an incorrect sensor #define APDS9930_CHIPID_2 0x39 // there are case reports about "accidentially bought" 9930's +#define APDS9960_MODE_GESTURE 0 +#define APDS9960_MODE_COLOR 1 + /* Gesture parameters */ -#define GESTURE_THRESHOLD_OUT 10 -#define GESTURE_SENSITIVITY_1 50 -#define GESTURE_SENSITIVITY_2 20 +#define GESTURE_THRESHOLD_OUT 10 +#define GESTURE_SENSITIVITY_1 50 +#define GESTURE_SENSITIVITY_2 20 -uint8_t APDS9960addr; -uint8_t APDS9960type = 0; -char APDS9960stype[] = "APDS9960"; -char currentGesture[6]; -uint8_t gesture_mode = 1; +#define APDS9960_LONG_RECOVERY 50 // long pause after sensor overload in loops +#define APDS9960_MAX_GESTURE_CYCLES 50 // how many FIFO-reads are allowed to prevent crash +/******************************************************************************\ + * Constants +\******************************************************************************/ -volatile uint8_t recovery_loop_counter = 0; //count number of stateloops to switch the sensor off, if needed -#define APDS9960_LONG_RECOVERY 50 //long pause after sensor overload in loops -#define APDS9960_MAX_GESTURE_CYCLES 50 //how many FIFO-reads are allowed to prevent crash -bool APDS9960_overload = false; +const char APDS9960_TAG[] PROGMEM = "APDS9960"; // Only one actualy #ifdef USE_WEBSERVER -const char HTTP_APDS_9960_SNS[] PROGMEM = - "{s}" "Red" "{m}%s{e}" - "{s}" "Green" "{m}%s{e}" - "{s}" "Blue" "{m}%s{e}" - "{s}" "Ambient" "{m}%s " D_UNIT_LUX "{e}" - "{s}" "CCT" "{m}%s " "K" "{e}" // calculated color temperature in Kelvin - "{s}" "Proximity" "{m}%s{e}"; // {s} = , {m} = , {e} = + +#ifdef USE_APDS9960_GESTURE +const char HTTP_SNS_GESTURE[] PROGMEM = "{s}%s " D_GESTURE "{m}%s{e}"; +#endif // USE_APDS9960_GESTURE + +#ifdef USE_APDS9960_COLOR +const char HTTP_SNS_COLOR_RED[] PROGMEM = "{s}%s " D_COLOR_RED "{m}%u{e}"; +const char HTTP_SNS_COLOR_GREEN[] PROGMEM = "{s}%s " D_COLOR_GREEN "{m}%u{e}"; +const char HTTP_SNS_COLOR_BLUE[] PROGMEM = "{s}%s " D_COLOR_BLUE "{m}%u{e}"; +const char HTTP_SNS_CCT[] PROGMEM = "{s}%s " D_CCT "{m}%u " D_UNIT_KELVIN "{e}"; +#endif // USE_APDS9960_COLOR + +#ifdef USE_APDS9960_PROXIMITY +const char HTTP_SNS_PROXIMITY[] PROGMEM = "{s}%s " D_PROXIMITY "{m}%u{e}"; +#endif // USE_APDS9960_PROXIMITY + #endif // USE_WEBSERVER -/*********************************************************************************************\ +/******************************************************************************\ * APDS9960 * * Programmer : APDS9960 Datasheet and Sparkfun -\*********************************************************************************************/ +\******************************************************************************/ /* Misc parameters */ #define FIFO_PAUSE_TIME 30 // Wait period (ms) between FIFO reads @@ -153,9 +163,10 @@ const char HTTP_APDS_9960_SNS[] PROGMEM = #define APDS9960_AEN 0b00000010 #define APDS9960_PEN 0b00000100 #define APDS9960_WEN 0b00001000 -#define APSD9960_AIEN 0b00010000 +#define APDS9960_AIEN 0b00010000 #define APDS9960_PIEN 0b00100000 #define APDS9960_GEN 0b01000000 + #define APDS9960_GVALID 0b00000001 /* On/Off definitions */ @@ -212,149 +223,137 @@ const char HTTP_APDS_9960_SNS[] PROGMEM = #define GWTIME_30_8MS 6 #define GWTIME_39_2MS 7 + + /* Default values */ -#define DEFAULT_ATIME 0xdb // 103ms = 0xdb -#define DEFAULT_WTIME 246 // 27ms -#define DEFAULT_PROX_PPULSE 0x87 // 16us, 8 pulses -#define DEFAULT_GESTURE_PPULSE 0x89 // 16us, 10 pulses ---89 -#define DEFAULT_POFFSET_UR 0 // 0 offset -#define DEFAULT_POFFSET_DL 0 // 0 offset -#define DEFAULT_CONFIG1 0x60 // No 12x wait (WTIME) factor +#define DEFAULT_ATIME 0xdb // 103ms = 0xdb = 219 +#define DEFAULT_WTIME 246 // 27ms +#define DEFAULT_PROX_PPULSE 0x87 // 16us, 8 pulses +#define DEFAULT_GESTURE_PPULSE 0x89 // 16us, 10 pulses ---89 +#define DEFAULT_POFFSET_UR 0 // 0 offset +#define DEFAULT_POFFSET_DL 0 // 0 offset +#define DEFAULT_CONFIG1 0x60 // No 12x wait (WTIME) factor #define DEFAULT_LDRIVE LED_DRIVE_100MA #define DEFAULT_PGAIN PGAIN_4X -#define DEFAULT_AGAIN AGAIN_4X // we have to divide by the same facot at the end -#define DEFAULT_PILT 0 // Low proximity threshold -#define DEFAULT_PIHT 50 // High proximity threshold -#define DEFAULT_AILT 0xFFFF // Force interrupt for calibration +#define DEFAULT_AGAIN AGAIN_4X // we have to divide by the same facot at the end +#define DEFAULT_PILT 0 // Low proximity threshold +#define DEFAULT_PIHT 50 // High proximity threshold +#define DEFAULT_AILT 0xFFFF // Force interrupt for calibration #define DEFAULT_AIHT 0 -#define DEFAULT_PERS 0x11 // 2 consecutive prox or ALS for int. -#define DEFAULT_CONFIG2 0x01 // No saturation interrupts or LED boost -#define DEFAULT_CONFIG3 0 // Enable all photodiodes, no SAI -#define DEFAULT_GPENTH 40 // Threshold for entering gesture mode -#define DEFAULT_GEXTH 30 // Threshold for exiting gesture mode -#define DEFAULT_GCONF1 0x40 // 4 gesture events for int., 1 for exit +#define DEFAULT_PERS 0x11 // 2 consecutive prox or ALS for int. +#define DEFAULT_CONFIG2 0x01 // No saturation interrupts or LED boost +#define DEFAULT_CONFIG3 0 // Enable all photodiodes, no SAI +#define DEFAULT_GPENTH 40 // Threshold for entering gesture mode +#define DEFAULT_GEXTH 30 // Threshold for exiting gesture mode +#define DEFAULT_GCONF1 0x40 // 4 gesture events for int., 1 for exit #define DEFAULT_GGAIN GGAIN_4X -#define DEFAULT_GLDRIVE LED_DRIVE_100MA // default 100ma -#define DEFAULT_GWTIME GWTIME_2_8MS // default 2_8MS -#define DEFAULT_GOFFSET 0 // No offset scaling for gesture mode -#define DEFAULT_GPULSE 0xC9 // 32us, 10 pulses -#define DEFAULT_GCONF3 0 // All photodiodes active during gesture -#define DEFAULT_GIEN 0 // Disable gesture interrupts +#define DEFAULT_GLDRIVE LED_DRIVE_100MA // default 100ma +#define DEFAULT_GWTIME GWTIME_2_8MS // default 2_8MS +#define DEFAULT_GOFFSET 0 // No offset scaling for gesture mode +#define DEFAULT_GPULSE 0xC9 // 32us, 10 pulses +#define DEFAULT_GCONF3 0 // All photodiodes active during gesture +#define DEFAULT_GIEN 0 // Disable gesture interrupts #define APDS9960_ERROR 0xFF +#ifdef USE_APDS9960_GESTURE + /* Direction definitions */ +const char GESTURE_UP[] PROGMEM = "Up"; +const char GESTURE_DOWN[] PROGMEM = "Down"; +const char GESTURE_LEFT[] PROGMEM = "Left"; +const char GESTURE_RIGHT[] PROGMEM = "Right"; +const char GESTURE_LONG[] PROGMEM = "Long"; +const char GESTURE_NONE[] PROGMEM = "None"; + enum { DIR_NONE, DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN, + DIR_NEAR, + DIR_FAR, DIR_ALL }; /* State definitions*/ +/* enum { APDS9960_NA_STATE, + APDS9960_NEAR_STATE, + APDS9960_FAR_STATE, APDS9960_ALL_STATE }; +*/ /* Container for gesture data */ typedef struct gesture_data_type { - uint8_t u_data[32]; - uint8_t d_data[32]; - uint8_t l_data[32]; - uint8_t r_data[32]; - uint8_t index; - uint8_t total_gestures; - uint8_t in_threshold; - uint8_t out_threshold; -} gesture_data_type; + uint8_t u_data[32]; + uint8_t d_data[32]; + uint8_t l_data[32]; + uint8_t r_data[32]; + uint8_t index; + uint8_t total_gestures; + uint8_t in_threshold; + uint8_t out_threshold; +} gesture_data_t; -/*Members*/ - gesture_data_type gesture_data_; - int16_t gesture_ud_delta_ = 0; - int16_t gesture_lr_delta_ = 0; - int16_t gesture_ud_count_ = 0; - int16_t gesture_lr_count_ = 0; - int16_t gesture_state_ = 0; - int16_t gesture_motion_ = DIR_NONE; +typedef struct gesture_type { + int16_t ud_delta_ = 0; + int16_t lr_delta_ = 0; + int16_t ud_count_ = 0; + int16_t lr_count_ = 0; + int16_t state_ = 0; + int16_t motion_ = DIR_NONE; +} gesture_t; - typedef struct color_data_type { - uint16_t a; // measured ambient - uint16_t r; - uint16_t g; - uint16_t b; - uint8_t p; // proximity - uint16_t cct; // calculated color temperature - uint16_t lux; // calculated illuminance - atm only from rgb - } color_data_type; +#endif // USE_APDS9960_GESTURE - color_data_type color_data; - uint8_t APDS9960_aTime = DEFAULT_ATIME; +#if defined(USE_APDS9960_COLOR) || defined(USE_APDS9960_PROXIMITY) +typedef struct color_data_type { + uint16_t a; // measured ambient + uint16_t r; // Red + uint16_t g; // Green + uint16_t b; // Blue + uint8_t p; // proximity + uint16_t cct; // calculated color temperature + uint16_t lux; // calculated illuminance - atm only from rgb +} color_data_t; +#endif // USE_APDS9960_COLOR || USE_APDS9960_PROXIMITY - /******************************************************************************* - * Helper functions - ******************************************************************************/ +/******************************************************************************\ + * Globals +\******************************************************************************/ - /** - * @brief Writes a single byte to the I2C device (no register) - * - * @param[in] val the 1-byte value to write to the I2C device - * @return True if successful write operation. False otherwise. - */ - bool wireWriteByte(uint8_t val) - { - Wire.beginTransmission(APDS9960_I2C_ADDR); - Wire.write(val); - if( Wire.endTransmission() != 0 ) { - return false; - } +#ifdef USE_APDS9960_GESTURE +gesture_data_t gesture_data; +gesture_t gesture; +char currentGesture[6]; +#endif // USE_APDS9960_GESTURE - return true; - } - /** - * @brief Reads a block (array) of bytes from the I2C device and register - * - * @param[in] reg the register to read from - * @param[out] val pointer to the beginning of the data - * @param[in] len number of bytes to read - * @return Number of bytes read. -1 on read error. - */ +#if defined(USE_APDS9960_COLOR) || defined(USE_APDS9960_PROXIMITY) +color_data_t color_data; +#endif // USE_APDS9960_COLOR || USE_APDS9960_PROXIMITY -int8_t wireReadDataBlock( uint8_t reg, - uint8_t *val, - uint16_t len) -{ - unsigned char i = 0; +volatile uint8_t recovery_loop_counter = 0; // count number of stateloops to switch the sensor off, if needed +bool APDS9960_overload = false; +uint8_t APDS9960_aTime = DEFAULT_ATIME; +uint8_t APDS9960_type = 0; +uint8_t gesture_mode = 1; // 1 : Gesture | 2 : Color - /* Indicate which register we want to read from */ - if (!wireWriteByte(reg)) { - return -1; - } - - /* Read block data */ - Wire.requestFrom(APDS9960_I2C_ADDR, len); - while (Wire.available()) { - if (i >= len) { - return -1; - } - val[i] = Wire.read(); - i++; - } - - return i; -} +/******************************************************************************\ + * Helper functions +\******************************************************************************/ +#ifdef USE_APDS9960_COLOR /** -* Taken from the Adafruit-library -* @brief Converts the raw R/G/B values to color temperature in degrees -* Kelvin -*/ - -void calculateColorTemperature(void) -{ + * Taken from the Adafruit-library + * @brief Converts the raw R/G/B values to color temperature in degrees + * Kelvin + */ +void calculateColorTemperature(void) { float X, Y, Z; /* RGB to XYZ correlation */ float xc, yc; /* Chromaticity co-ordinates */ float n; /* McCamy's formula */ @@ -365,8 +364,8 @@ void calculateColorTemperature(void) /* and 60W incandescent values for a wide range. */ /* Note: Y = Illuminance or lux */ X = (-0.14282F * color_data.r) + (1.54924F * color_data.g) + (-0.95641F * color_data.b); - Y = (-0.32466F * color_data.r) + (1.57837F * color_data.g) + (-0.73191F * color_data.b); // this is Lux ... under certain circumstances - Z = (-0.68202F * color_data.r) + (0.77073F * color_data.g) + ( 0.56332F * color_data.b); + Y = (-0.32466F * color_data.r) + (1.57837F * color_data.g) + (-0.73191F * color_data.b); // this is Lux ... under certain circumstances + Z = (-0.68202F * color_data.r) + (0.77073F * color_data.g) + (+0.56332F * color_data.b); /* 2. Calculate the chromaticity co-ordinates */ xc = (X) / (X + Y + Z); @@ -380,965 +379,909 @@ void calculateColorTemperature(void) return; } +#endif // USE_APDS9960_COLOR - /******************************************************************************* - * Getters and setters for register values - ******************************************************************************/ +/******************************************************************************\ + * Getters and setters for register values +\******************************************************************************/ - /** - * @brief Returns the lower threshold for proximity detection - * - * @return lower threshold - */ - uint8_t getProxIntLowThresh(void) - { - uint8_t val; +/** + * @brief Returns the lower threshold for proximity detection + * + * @return lower threshold + */ +uint8_t getProxIntLowThresh(void) { + uint8_t val; - /* Read value from PILT register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT) ; - return val; - } + /* Read value from PILT register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT); - /** - * @brief Sets the lower threshold for proximity detection - * - * @param[in] threshold the lower proximity threshold - */ - void setProxIntLowThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold); - } - - /** - * @brief Returns the high threshold for proximity detection - * - * @return high threshold - */ - uint8_t getProxIntHighThresh(void) - { - uint8_t val; - - /* Read value from PIHT register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ; - return val; - } - - /** - * @brief Sets the high threshold for proximity detection - * - * @param[in] threshold the high proximity threshold - */ - - void setProxIntHighThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold); - } - - - /** - * @brief Returns LED drive strength for proximity and ALS - * - * Value LED Current - * 0 100 mA - * 1 50 mA - * 2 25 mA - * 3 12.5 mA - * - * @return the value of the LED drive strength. 0xFF on failure. - */ - uint8_t getLEDDrive(void) - { - uint8_t val; - - /* Read value from CONTROL register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ; - /* Shift and mask out LED drive bits */ - val = (val >> 6) & 0b00000011; - - return val; - } - - /** - * @brief Sets the LED drive strength for proximity and ALS - * - * Value LED Current - * 0 100 mA - * 1 50 mA - * 2 25 mA - * 3 12.5 mA - * - * @param[in] drive the value (0-3) for the LED drive strength - */ - void setLEDDrive(uint8_t drive) - { - uint8_t val; - - /* Read value from CONTROL register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); - - /* Set bits in register to given value */ - drive &= 0b00000011; - drive = drive << 6; - val &= 0b00111111; - val |= drive; - - /* Write register value back into CONTROL register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); - } - - - /** - * @brief Returns receiver gain for proximity detection - * - * Value Gain - * 0 1x - * 1 2x - * 2 4x - * 3 8x - * - * @return the value of the proximity gain. 0xFF on failure. - */ - uint8_t getProximityGain(void) - { - uint8_t val; - - /* Read value from CONTROL register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ; - /* Shift and mask out PDRIVE bits */ - val = (val >> 2) & 0b00000011; - - return val; - } - - /** - * @brief Sets the receiver gain for proximity detection - * - * Value Gain - * 0 1x - * 1 2x - * 2 4x - * 3 8x - * - * @param[in] drive the value (0-3) for the gain - */ - void setProximityGain(uint8_t drive) - { - uint8_t val; - - /* Read value from CONTROL register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); - - /* Set bits in register to given value */ - drive &= 0b00000011; - drive = drive << 2; - val &= 0b11110011; - val |= drive; - - /* Write register value back into CONTROL register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); - } - - - /** - * @brief Returns receiver gain for the ambient light sensor (ALS) - * - * Value Gain - * 0 1x - * 1 4x - * 2 16x - * 3 64x - * - * @return the value of the ALS gain. 0xFF on failure. - */ - - /** - * @brief Sets the receiver gain for the ambient light sensor (ALS) - * - * Value Gain - * 0 1x - * 1 4x - * 2 16x - * 3 64x - * - * @param[in] drive the value (0-3) for the gain - */ - void setAmbientLightGain(uint8_t drive) - { - uint8_t val; - - /* Read value from CONTROL register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); - - /* Set bits in register to given value */ - drive &= 0b00000011; - val &= 0b11111100; - val |= drive; - - /* Write register value back into CONTROL register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); - } - - /** - * @brief Get the current LED boost value - * - * Value Boost Current - * 0 100% - * 1 150% - * 2 200% - * 3 300% - * - * @return The LED boost value. 0xFF on failure. - */ - uint8_t getLEDBoost(void) - { - uint8_t val; - - /* Read value from CONFIG2 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ; - - /* Shift and mask out LED_BOOST bits */ - val = (val >> 4) & 0b00000011; - - return val; - } - - /** - * @brief Sets the LED current boost value - * - * Value Boost Current - * 0 100% - * 1 150% - * 2 200% - * 3 300% - * - * @param[in] drive the value (0-3) for current boost (100-300%) - */ - void setLEDBoost(uint8_t boost) - { - uint8_t val; - - /* Read value from CONFIG2 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ; - /* Set bits in register to given value */ - boost &= 0b00000011; - boost = boost << 4; - val &= 0b11001111; - val |= boost; - - /* Write register value back into CONFIG2 register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, val) ; - } - - /** - * @brief Gets proximity gain compensation enable - * - * @return 1 if compensation is enabled. 0 if not. 0xFF on error. - */ - uint8_t getProxGainCompEnable(void) - { - uint8_t val; - - /* Read value from CONFIG3 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - /* Shift and mask out PCMP bits */ - val = (val >> 5) & 0b00000001; - - return val; - } - - /** - * @brief Sets the proximity gain compensation enable - * - * @param[in] enable 1 to enable compensation. 0 to disable compensation. - */ - void setProxGainCompEnable(uint8_t enable) - { - uint8_t val; - - /* Read value from CONFIG3 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - /* Set bits in register to given value */ - enable &= 0b00000001; - enable = enable << 5; - val &= 0b11011111; - val |= enable; - - /* Write register value back into CONFIG3 register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ; - } - - /** - * @brief Gets the current mask for enabled/disabled proximity photodiodes - * - * 1 = disabled, 0 = enabled - * Bit Photodiode - * 3 UP - * 2 DOWN - * 1 LEFT - * 0 RIGHT - * - * @return Current proximity mask for photodiodes. 0xFF on error. - */ - uint8_t getProxPhotoMask(void) - { - uint8_t val; - - /* Read value from CONFIG3 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - /* Mask out photodiode enable mask bits */ - val &= 0b00001111; - - return val; - } - - /** - * @brief Sets the mask for enabling/disabling proximity photodiodes - * - * 1 = disabled, 0 = enabled - * Bit Photodiode - * 3 UP - * 2 DOWN - * 1 LEFT - * 0 RIGHT - * - * @param[in] mask 4-bit mask value - */ - void setProxPhotoMask(uint8_t mask) - { - uint8_t val; - - /* Read value from CONFIG3 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - /* Set bits in register to given value */ - mask &= 0b00001111; - val &= 0b11110000; - val |= mask; - - /* Write register value back into CONFIG3 register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ; - } - - /** - * @brief Gets the entry proximity threshold for gesture sensing - * - * @return Current entry proximity threshold. - */ - uint8_t getGestureEnterThresh(void) - { - uint8_t val; - - /* Read value from GPENTH register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GPENTH) ; - - return val; - } - - /** - * @brief Sets the entry proximity threshold for gesture sensing - * - * @param[in] threshold proximity value needed to start gesture mode - */ - void setGestureEnterThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPENTH, threshold) ; - - } - - /** - * @brief Gets the exit proximity threshold for gesture sensing - * - * @return Current exit proximity threshold. - */ - uint8_t getGestureExitThresh(void) - { - uint8_t val; - - /* Read value from GEXTH register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GEXTH) ; - - return val; - } - - /** - * @brief Sets the exit proximity threshold for gesture sensing - * - * @param[in] threshold proximity value needed to end gesture mode - */ - void setGestureExitThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GEXTH, threshold) ; - } - - /** - * @brief Gets the gain of the photodiode during gesture mode - * - * Value Gain - * 0 1x - * 1 2x - * 2 4x - * 3 8x - * - * @return the current photodiode gain. 0xFF on error. - */ - uint8_t getGestureGain(void) - { - uint8_t val; - - /* Read value from GCONF2 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - /* Shift and mask out GGAIN bits */ - val = (val >> 5) & 0b00000011; - - return val; - } - - /** - * @brief Sets the gain of the photodiode during gesture mode - * - * Value Gain - * 0 1x - * 1 2x - * 2 4x - * 3 8x - * - * @param[in] gain the value for the photodiode gain - */ - void setGestureGain(uint8_t gain) - { - uint8_t val; - - /* Read value from GCONF2 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - /* Set bits in register to given value */ - gain &= 0b00000011; - gain = gain << 5; - val &= 0b10011111; - val |= gain; - - /* Write register value back into GCONF2 register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; - } - - /** - * @brief Gets the drive current of the LED during gesture mode - * - * Value LED Current - * 0 100 mA - * 1 50 mA - * 2 25 mA - * 3 12.5 mA - * - * @return the LED drive current value. 0xFF on error. - */ - uint8_t getGestureLEDDrive(void) - { - uint8_t val; - - /* Read value from GCONF2 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - /* Shift and mask out GLDRIVE bits */ - val = (val >> 3) & 0b00000011; - - return val; - } - - /** - * @brief Sets the LED drive current during gesture mode - * - * Value LED Current - * 0 100 mA - * 1 50 mA - * 2 25 mA - * 3 12.5 mA - * - * @param[in] drive the value for the LED drive current - */ - void setGestureLEDDrive(uint8_t drive) - { - uint8_t val; - - /* Read value from GCONF2 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - /* Set bits in register to given value */ - drive &= 0b00000011; - drive = drive << 3; - val &= 0b11100111; - val |= drive; - - /* Write register value back into GCONF2 register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; - } - - /** - * @brief Gets the time in low power mode between gesture detections - * - * Value Wait time - * 0 0 ms - * 1 2.8 ms - * 2 5.6 ms - * 3 8.4 ms - * 4 14.0 ms - * 5 22.4 ms - * 6 30.8 ms - * 7 39.2 ms - * - * @return the current wait time between gestures. 0xFF on error. - */ - uint8_t getGestureWaitTime(void) - { - uint8_t val; - - /* Read value from GCONF2 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - /* Mask out GWTIME bits */ - val &= 0b00000111; - - return val; - } - - /** - * @brief Sets the time in low power mode between gesture detections - * - * Value Wait time - * 0 0 ms - * 1 2.8 ms - * 2 5.6 ms - * 3 8.4 ms - * 4 14.0 ms - * 5 22.4 ms - * 6 30.8 ms - * 7 39.2 ms - * - * @param[in] the value for the wait time - */ - void setGestureWaitTime(uint8_t time) - { - uint8_t val; - - /* Read value from GCONF2 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - /* Set bits in register to given value */ - time &= 0b00000111; - val &= 0b11111000; - val |= time; - - /* Write register value back into GCONF2 register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; - } - - /** - * @brief Gets the low threshold for ambient light interrupts - * - * @param[out] threshold current low threshold stored on the APDS-9960 - */ - void getLightIntLowThreshold(uint16_t &threshold) - { - uint8_t val_byte; - threshold = 0; - - /* Read value from ambient light low threshold, low byte register */ - val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AILTL) ; - threshold = val_byte; - - /* Read value from ambient light low threshold, high byte register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_byte) ; - threshold = threshold + ((uint16_t)val_byte << 8); - } - - /** - * @brief Sets the low threshold for ambient light interrupts - * - * @param[in] threshold low threshold value for interrupt to trigger - */ - - void setLightIntLowThreshold(uint16_t threshold) - { - uint8_t val_low; - uint8_t val_high; - - /* Break 16-bit threshold into 2 8-bit values */ - val_low = threshold & 0x00FF; - val_high = (threshold & 0xFF00) >> 8; - - /* Write low byte */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTL, val_low) ; - - /* Write high byte */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_high) ; - - } - - - /** - * @brief Gets the high threshold for ambient light interrupts - * - * @param[out] threshold current low threshold stored on the APDS-9960 - */ - void getLightIntHighThreshold(uint16_t &threshold) - { - uint8_t val_byte; - threshold = 0; - - /* Read value from ambient light high threshold, low byte register */ - val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AIHTL); - threshold = val_byte; - - /* Read value from ambient light high threshold, high byte register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_byte) ; - threshold = threshold + ((uint16_t)val_byte << 8); - } - - /** - * @brief Sets the high threshold for ambient light interrupts - * - * @param[in] threshold high threshold value for interrupt to trigger - */ - void setLightIntHighThreshold(uint16_t threshold) - { - uint8_t val_low; - uint8_t val_high; - - /* Break 16-bit threshold into 2 8-bit values */ - val_low = threshold & 0x00FF; - val_high = (threshold & 0xFF00) >> 8; - - /* Write low byte */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTL, val_low); - - /* Write high byte */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_high) ; - } - - - /** - * @brief Gets the low threshold for proximity interrupts - * - * @param[out] threshold current low threshold stored on the APDS-9960 - */ - void getProximityIntLowThreshold(uint8_t &threshold) - { - threshold = 0; - - /* Read value from proximity low threshold register */ - threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT); - - } - - /** - * @brief Sets the low threshold for proximity interrupts - * - * @param[in] threshold low threshold value for interrupt to trigger - */ - void setProximityIntLowThreshold(uint8_t threshold) - { - - /* Write threshold value to register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold) ; - } - - - - /** - * @brief Gets the high threshold for proximity interrupts - * - * @param[out] threshold current low threshold stored on the APDS-9960 - */ - void getProximityIntHighThreshold(uint8_t &threshold) - { - threshold = 0; - - /* Read value from proximity low threshold register */ - threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ; - - } - - /** - * @brief Sets the high threshold for proximity interrupts - * - * @param[in] threshold high threshold value for interrupt to trigger - */ - void setProximityIntHighThreshold(uint8_t threshold) - { - - /* Write threshold value to register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold) ; - } - - /** - * @brief Gets if ambient light interrupts are enabled or not - * - * @return 1 if interrupts are enabled, 0 if not. 0xFF on error. - */ - uint8_t getAmbientLightIntEnable(void) - { - uint8_t val; - - /* Read value from ENABLE register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - /* Shift and mask out AIEN bit */ - val = (val >> 4) & 0b00000001; - - return val; - } - - /** - * @brief Turns ambient light interrupts on or off - * - * @param[in] enable 1 to enable interrupts, 0 to turn them off - */ - void setAmbientLightIntEnable(uint8_t enable) - { - uint8_t val; - - /* Read value from ENABLE register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); - - /* Set bits in register to given value */ - enable &= 0b00000001; - enable = enable << 4; - val &= 0b11101111; - val |= enable; - - /* Write register value back into ENABLE register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ; - } - - /** - * @brief Gets if proximity interrupts are enabled or not - * - * @return 1 if interrupts are enabled, 0 if not. 0xFF on error. - */ - uint8_t getProximityIntEnable(void) - { - uint8_t val; - - /* Read value from ENABLE register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - /* Shift and mask out PIEN bit */ - val = (val >> 5) & 0b00000001; - - return val; - } - - /** - * @brief Turns proximity interrupts on or off - * - * @param[in] enable 1 to enable interrupts, 0 to turn them off - */ - void setProximityIntEnable(uint8_t enable) - { - uint8_t val; - - /* Read value from ENABLE register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - /* Set bits in register to given value */ - enable &= 0b00000001; - enable = enable << 5; - val &= 0b11011111; - val |= enable; - - /* Write register value back into ENABLE register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ; - } - - /** - * @brief Gets if gesture interrupts are enabled or not - * - * @return 1 if interrupts are enabled, 0 if not. 0xFF on error. - */ - uint8_t getGestureIntEnable(void) - { - uint8_t val; - - /* Read value from GCONF4 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - /* Shift and mask out GIEN bit */ - val = (val >> 1) & 0b00000001; - - return val; - } - - /** - * @brief Turns gesture-related interrupts on or off - * - * @param[in] enable 1 to enable interrupts, 0 to turn them off - */ - void setGestureIntEnable(uint8_t enable) - { - uint8_t val; - - /* Read value from GCONF4 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - /* Set bits in register to given value */ - enable &= 0b00000001; - enable = enable << 1; - val &= 0b11111101; - val |= enable; - - /* Write register value back into GCONF4 register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ; - } - - /** - * @brief Clears the ambient light interrupt - * - */ - void clearAmbientLightInt(void) - { - uint8_t throwaway; - throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AICLEAR); - } - - /** - * @brief Clears the proximity interrupt - * - */ - void clearProximityInt(void) - { - uint8_t throwaway; - throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PICLEAR) ; - - } - - /** - * @brief Tells if the gesture state machine is currently running - * - * @return 1 if gesture state machine is running, 0 if not. 0xFF on error. - */ - uint8_t getGestureMode(void) - { - uint8_t val; - - /* Read value from GCONF4 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - /* Mask out GMODE bit */ - val &= 0b00000001; - - return val; - } - - /** - * @brief Tells the state machine to either enter or exit gesture state machine - * - * @param[in] mode 1 to enter gesture state machine, 0 to exit. - */ - void setGestureMode(uint8_t mode) - { - uint8_t val; - - /* Read value from GCONF4 register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - /* Set bits in register to given value */ - mode &= 0b00000001; - val &= 0b11111110; - val |= mode; - - /* Write register value back into GCONF4 register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ; - } - - -bool APDS9960_init(void) -{ - /* Set default values for ambient light and proximity registers */ - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, DEFAULT_ATIME) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, DEFAULT_WTIME) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_PROX_PPULSE) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_UR, DEFAULT_POFFSET_UR) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_DL, DEFAULT_POFFSET_DL) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG1, DEFAULT_CONFIG1) ; - - setLEDDrive(DEFAULT_LDRIVE); - - setProximityGain(DEFAULT_PGAIN); - - setAmbientLightGain(DEFAULT_AGAIN); - - setProxIntLowThresh(DEFAULT_PILT) ; - - setProxIntHighThresh(DEFAULT_PIHT); - - setLightIntLowThreshold(DEFAULT_AILT) ; - - setLightIntHighThreshold(DEFAULT_AIHT) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PERS, DEFAULT_PERS) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, DEFAULT_CONFIG2) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, DEFAULT_CONFIG3) ; - - /* Set default values for gesture sense registers */ - setGestureEnterThresh(DEFAULT_GPENTH); - - setGestureExitThresh(DEFAULT_GEXTH) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF1, DEFAULT_GCONF1) ; - - setGestureGain(DEFAULT_GGAIN) ; - - setGestureLEDDrive(DEFAULT_GLDRIVE) ; - - setGestureWaitTime(DEFAULT_GWTIME) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_U, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_D, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_L, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_R, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPULSE, DEFAULT_GPULSE) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF3, DEFAULT_GCONF3) ; - - setGestureIntEnable(DEFAULT_GIEN); - - disablePower(); // go to sleep - - return true; + return val; } -/******************************************************************************* + +/** + * @brief Sets the lower threshold for proximity detection + * + * @param[in] threshold the lower proximity threshold + */ +inline void setProxIntLowThresh(uint8_t threshold) { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold); +} + +/** + * @brief Returns the high threshold for proximity detection + * + * @return high threshold + */ +uint8_t getProxIntHighThresh(void) { + uint8_t val; + + /* Read value from PIHT register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT); + + return val; +} + +/** + * @brief Sets the high threshold for proximity detection + * + * @param[in] threshold the high proximity threshold + */ +inline void setProxIntHighThresh(uint8_t threshold) { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold); +} + +/** + * @brief Returns LED drive strength for proximity and ALS + * + * Value LED Current + * 0 100 mA + * 1 50 mA + * 2 25 mA + * 3 12.5 mA + * + * @return the value of the LED drive strength. 0xFF on failure. + */ +uint8_t getLEDDrive(void) { + uint8_t val; + + /* Read value from CONTROL register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + /* Shift and mask out LED drive bits */ + val = (val >> 6) & 0b00000011; + + return val; +} + +/** + * @brief Sets the LED drive strength for proximity and ALS + * + * Value LED Current + * 0 100 mA + * 1 50 mA + * 2 25 mA + * 3 12.5 mA + * + * @param[in] drive the value (0-3) for the LED drive strength + */ +void setLEDDrive(uint8_t drive) { + uint8_t val; + + /* Read value from CONTROL register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + /* Set bits in register to given value */ + drive &= 0b00000011; + drive = drive << 6; + val &= 0b00111111; + val |= drive; + + /* Write register value back into CONTROL register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); +} + +/** + * @brief Returns receiver gain for proximity detection + * + * Value Gain + * 0 1x + * 1 2x + * 2 4x + * 3 8x + * + * @return the value of the proximity gain. 0xFF on failure. + */ +uint8_t getProximityGain(void) { + uint8_t val; + + /* Read value from CONTROL register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + /* Shift and mask out PDRIVE bits */ + val = (val >> 2) & 0b00000011; + + return val; +} + +/** + * @brief Sets the receiver gain for proximity detection + * + * Value Gain + * 0 1x + * 1 2x + * 2 4x + * 3 8x + * + * @param[in] drive the value (0-3) for the gain + */ +void setProximityGain(uint8_t drive) { + uint8_t val; + + /* Read value from CONTROL register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + /* Set bits in register to given value */ + drive &= 0b00000011; + drive = drive << 2; + val &= 0b11110011; + val |= drive; + + /* Write register value back into CONTROL register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); +} + +/** + * @brief Returns receiver gain for the ambient light sensor (ALS) + * + * Value Gain + * 0 1x + * 1 4x + * 2 16x + * 3 64x + * + * @return the value of the ALS gain. 0xFF on failure. + */ +uint8_t getAmbientLightGain() { + uint8_t val; + + /* Read value from CONTROL register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + /* Shift and mask out ADRIVE bits */ + val &= 0b00000011; + + return val; +} + +/** + * @brief Sets the receiver gain for the ambient light sensor (ALS) + * + * Value Gain + * 0 1x + * 1 4x + * 2 16x + * 3 64x + * + * @param[in] drive the value (0-3) for the gain + */ +void setAmbientLightGain(uint8_t drive) { + uint8_t val; + + /* Read value from CONTROL register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + /* Set bits in register to given value */ + drive &= 0b00000011; + val &= 0b11111100; + val |= drive; + + /* Write register value back into CONTROL register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); +} + +/** + * @brief Get the current LED boost value + * + * Value Boost Current + * 0 100% + * 1 150% + * 2 200% + * 3 300% + * + * @return The LED boost value. 0xFF on failure. + */ +uint8_t getLEDBoost(void) { + uint8_t val; + + /* Read value from CONFIG2 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2); + + /* Shift and mask out LED_BOOST bits */ + val = (val >> 4) & 0b00000011; + + return val; +} + +/** + * @brief Sets the LED current boost value + * + * Value Boost Current + * 0 100% + * 1 150% + * 2 200% + * 3 300% + * + * @param[in] drive the value (0-3) for current boost (100-300%) + */ +void setLEDBoost(uint8_t boost) { + uint8_t val; + + /* Read value from CONFIG2 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2); + + /* Set bits in register to given value */ + boost &= 0b00000011; + boost = boost << 4; + val &= 0b11001111; + val |= boost; + + /* Write register value back into CONFIG2 register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, val); +} + +/** + * @brief Gets proximity gain compensation enable + * + * @return 1 if compensation is enabled. 0 if not. 0xFF on error. + */ +uint8_t getProxGainCompEnable(void) { + uint8_t val; + + /* Read value from CONFIG3 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3); + + /* Shift and mask out PCMP bits */ + val = (val >> 5) & 0b00000001; + + return val; +} + +/** + * @brief Sets the proximity gain compensation enable + * + * @param[in] enable 1 to enable compensation. 0 to disable compensation. + */ +void setProxGainCompEnable(uint8_t enable) { + uint8_t val; + + /* Read value from CONFIG3 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3); + + /* Set bits in register to given value */ + enable &= 0b00000001; + enable = enable << 5; + val &= 0b11011111; + val |= enable; + + /* Write register value back into CONFIG3 register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val); +} + +/** + * @brief Gets the current mask for enabled/disabled proximity photodiodes + * + * 1 = disabled, 0 = enabled + * Bit Photodiode + * 3 UP + * 2 DOWN + * 1 LEFT + * 0 RIGHT + * + * @return Current proximity mask for photodiodes. 0xFF on error. + */ +uint8_t getProxPhotoMask(void) { + uint8_t val; + + /* Read value from CONFIG3 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3); + + /* Mask out photodiode enable mask bits */ + val &= 0b00001111; + + return val; +} + +/** + * @brief Sets the mask for enabling/disabling proximity photodiodes + * + * 1 = disabled, 0 = enabled + * Bit Photodiode + * 3 UP + * 2 DOWN + * 1 LEFT + * 0 RIGHT + * + * @param[in] mask 4-bit mask value + */ +void setProxPhotoMask(uint8_t mask) { + uint8_t val; + + /* Read value from CONFIG3 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3); + + /* Set bits in register to given value */ + mask &= 0b00001111; + val &= 0b11110000; + val |= mask; + + /* Write register value back into CONFIG3 register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val); +} + +#ifdef USE_APDS9960_GESTURE + +/** + * @brief Gets the entry proximity threshold for gesture sensing + * + * @return Current entry proximity threshold. + */ +uint8_t getGestureEnterThresh(void) { + uint8_t val; + + /* Read value from GPENTH register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GPENTH); + + return val; +} + +/** + * @brief Sets the entry proximity threshold for gesture sensing + * + * @param[in] threshold proximity value needed to start gesture mode + */ +inline void setGestureEnterThresh(uint8_t threshold) { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPENTH, threshold); +} + +/** + * @brief Gets the exit proximity threshold for gesture sensing + * + * @return Current exit proximity threshold. + */ +uint8_t getGestureExitThresh(void) { + uint8_t val; + + /* Read value from GEXTH register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GEXTH); + + return val; +} + +/** + * @brief Sets the exit proximity threshold for gesture sensing + * + * @param[in] threshold proximity value needed to end gesture mode + */ +inline void setGestureExitThresh(uint8_t threshold) { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GEXTH, threshold); +} + +/** + * @brief Gets the gain of the photodiode during gesture mode + * + * Value Gain + * 0 1x + * 1 2x + * 2 4x + * 3 8x + * + * @return the current photodiode gain. 0xFF on error. + */ +uint8_t getGestureGain(void) { + uint8_t val; + + /* Read value from GCONF2 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + /* Shift and mask out GGAIN bits */ + val = (val >> 5) & 0b00000011; + + return val; +} + +/** + * @brief Sets the gain of the photodiode during gesture mode + * + * Value Gain + * 0 1x + * 1 2x + * 2 4x + * 3 8x + * + * @param[in] gain the value for the photodiode gain + */ +void setGestureGain(uint8_t gain) { + uint8_t val; + + /* Read value from GCONF2 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + /* Set bits in register to given value */ + gain &= 0b00000011; + gain = gain << 5; + val &= 0b10011111; + val |= gain; + + /* Write register value back into GCONF2 register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val); +} + +/** + * @brief Gets the drive current of the LED during gesture mode + * + * Value LED Current + * 0 100 mA + * 1 50 mA + * 2 25 mA + * 3 12.5 mA + * + * @return the LED drive current value. 0xFF on error. + */ +uint8_t getGestureLEDDrive(void) { + uint8_t val; + + /* Read value from GCONF2 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + /* Shift and mask out GLDRIVE bits */ + val = (val >> 3) & 0b00000011; + + return val; +} + +/** + * @brief Sets the LED drive current during gesture mode + * + * Value LED Current + * 0 100 mA + * 1 50 mA + * 2 25 mA + * 3 12.5 mA + * + * @param[in] drive the value for the LED drive current + */ +void setGestureLEDDrive(uint8_t drive) { + uint8_t val; + + /* Read value from GCONF2 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + /* Set bits in register to given value */ + drive &= 0b00000011; + drive = drive << 3; + val &= 0b11100111; + val |= drive; + + /* Write register value back into GCONF2 register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val); +} + +/** + * @brief Gets the time in low power mode between gesture detections + * + * Value Wait time + * 0 0 ms + * 1 2.8 ms + * 2 5.6 ms + * 3 8.4 ms + * 4 14.0 ms + * 5 22.4 ms + * 6 30.8 ms + * 7 39.2 ms + * + * @return the current wait time between gestures. 0xFF on error. + */ +uint8_t getGestureWaitTime(void) { + uint8_t val; + + /* Read value from GCONF2 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + /* Mask out GWTIME bits */ + val &= 0b00000111; + + return val; +} + +/** + * @brief Sets the time in low power mode between gesture detections + * + * Value Wait time + * 0 0 ms + * 1 2.8 ms + * 2 5.6 ms + * 3 8.4 ms + * 4 14.0 ms + * 5 22.4 ms + * 6 30.8 ms + * 7 39.2 ms + * + * @param[in] the value for the wait time + */ +void setGestureWaitTime(uint8_t time) { + uint8_t val; + + /* Read value from GCONF2 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + /* Set bits in register to given value */ + time &= 0b00000111; + val &= 0b11111000; + val |= time; + + /* Write register value back into GCONF2 register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val); +} + +#endif // USE_APDS9960_GESTURE + +/** + * @brief Gets the low threshold for ambient light interrupts + * + * @param[out] threshold current low threshold stored on the APDS-9960 + */ +void getLightIntLowThreshold(uint16_t &threshold) { + uint8_t val_byte; + threshold = 0; + + /* Read value from ambient light low threshold, low byte register */ + val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AILTL); + threshold = val_byte; + + /* Read value from ambient light low threshold, high byte register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_byte); + threshold = threshold + ((uint16_t)val_byte << 8); +} + +/** + * @brief Sets the low threshold for ambient light interrupts + * + * @param[in] threshold low threshold value for interrupt to trigger + */ +void setLightIntLowThreshold(uint16_t threshold) { + uint8_t val_low; + uint8_t val_high; + + /* Break 16-bit threshold into 2 8-bit values */ + val_low = threshold & 0x00FF; + val_high = (threshold & 0xFF00) >> 8; + + /* Write low byte */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTL, val_low); + + /* Write high byte */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_high); +} + +/** + * @brief Gets the high threshold for ambient light interrupts + * + * @param[out] threshold current low threshold stored on the APDS-9960 + */ +void getLightIntHighThreshold(uint16_t &threshold) { + uint8_t val_byte; + threshold = 0; + + /* Read value from ambient light high threshold, low byte register */ + val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AIHTL); + threshold = val_byte; + + /* Read value from ambient light high threshold, high byte register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_byte); + threshold = threshold + ((uint16_t)val_byte << 8); +} + +/** + * @brief Sets the high threshold for ambient light interrupts + * + * @param[in] threshold high threshold value for interrupt to trigger + */ +void setLightIntHighThreshold(uint16_t threshold) { + uint8_t val_low; + uint8_t val_high; + + /* Break 16-bit threshold into 2 8-bit values */ + val_low = threshold & 0x00FF; + val_high = (threshold & 0xFF00) >> 8; + + /* Write low byte */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTL, val_low); + + /* Write high byte */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_high); +} + +/** + * @brief Gets the low threshold for proximity interrupts + * + * @param[out] threshold current low threshold stored on the APDS-9960 + */ +void getProximityIntLowThreshold(uint8_t &threshold) { + threshold = 0; + + /* Read value from proximity low threshold register */ + threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT); +} + +/** + * @brief Sets the low threshold for proximity interrupts + * + * @param[in] threshold low threshold value for interrupt to trigger + */ +void setProximityIntLowThreshold(uint8_t threshold) { + /* Write threshold value to register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold); +} + +/** + * @brief Gets the high threshold for proximity interrupts + * + * @param[out] threshold current low threshold stored on the APDS-9960 + */ +void getProximityIntHighThreshold(uint8_t &threshold) { + threshold = 0; + + /* Read value from proximity low threshold register */ + threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT); +} + +/** + * @brief Sets the high threshold for proximity interrupts + * + * @param[in] threshold high threshold value for interrupt to trigger + */ +void setProximityIntHighThreshold(uint8_t threshold) { + /* Write threshold value to register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold); +} + +/** + * @brief Gets if ambient light interrupts are enabled or not + * + * @return 1 if interrupts are enabled, 0 if not. 0xFF on error. + */ +uint8_t getAmbientLightIntEnable(void) { + uint8_t val; + + /* Read value from ENABLE register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + /* Shift and mask out AIEN bit */ + val = (val >> 4) & 0b00000001; + + return val; +} + +/** + * @brief Turns ambient light interrupts on or off + * + * @param[in] enable 1 to enable interrupts, 0 to turn them off + */ +void setAmbientLightIntEnable(uint8_t enable) { + uint8_t val; + + /* Read value from ENABLE register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + /* Set bits in register to given value */ + enable &= 0b00000001; + enable = enable << 4; + val &= 0b11101111; + val |= enable; + + /* Write register value back into ENABLE register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val); +} + +/** + * @brief Gets if proximity interrupts are enabled or not + * + * @return 1 if interrupts are enabled, 0 if not. 0xFF on error. + */ +uint8_t getProximityIntEnable(void) { + uint8_t val; + + /* Read value from ENABLE register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + /* Shift and mask out PIEN bit */ + val = (val >> 5) & 0b00000001; + + return val; +} + +/** + * @brief Turns proximity interrupts on or off + * + * @param[in] enable 1 to enable interrupts, 0 to turn them off + */ +void setProximityIntEnable(uint8_t enable) { + uint8_t val; + + /* Read value from ENABLE register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + /* Set bits in register to given value */ + enable &= 0b00000001; + enable = enable << 5; + val &= 0b11011111; + val |= enable; + + /* Write register value back into ENABLE register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val); +} + +/** + * @brief Gets if gesture interrupts are enabled or not + * + * @return 1 if interrupts are enabled, 0 if not. 0xFF on error. + */ +uint8_t getGestureIntEnable(void) { + uint8_t val; + + /* Read value from GCONF4 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4); + + /* Shift and mask out GIEN bit */ + val = (val >> 1) & 0b00000001; + + return val; +} + +/** + * @brief Turns gesture-related interrupts on or off + * + * @param[in] enable 1 to enable interrupts, 0 to turn them off + */ +void setGestureIntEnable(uint8_t enable) { + uint8_t val; + + /* Read value from GCONF4 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4); + + /* Set bits in register to given value */ + enable &= 0b00000001; + enable = enable << 1; + val &= 0b11111101; + val |= enable; + + /* Write register value back into GCONF4 register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val); +} + +/** + * @brief Clears the ambient light interrupt + * + */ +void clearAmbientLightInt(void) { + uint8_t throwaway; + throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AICLEAR); +} + +/** + * @brief Clears the proximity interrupt + * + */ +void clearProximityInt(void) { + uint8_t throwaway; + throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PICLEAR); +} + +/** + * @brief Tells if the gesture state machine is currently running + * + * @return 1 if gesture state machine is running, 0 if not. 0xFF on error. + */ +uint8_t getGestureMode(void) { + uint8_t val; + + /* Read value from GCONF4 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4); + + /* Mask out GMODE bit */ + val &= 0b00000001; + + return val; +} + +/** + * @brief Tells the state machine to either enter or exit gesture state machine + * + * @param[in] mode 1 to enter gesture state machine, 0 to exit. + */ +void setGestureMode(uint8_t mode) { + uint8_t val; + + /* Read value from GCONF4 register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4); + + /* Set bits in register to given value */ + mode &= 0b00000001; + val &= 0b11111110; + val |= mode; + + /* Write register value back into GCONF4 register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val); +} + + +bool APDS9960_init(void) { + setMode(ALL, OFF); + + /* Set default values for ambient light and proximity registers */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, DEFAULT_ATIME); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, DEFAULT_WTIME); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_PROX_PPULSE); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_UR, DEFAULT_POFFSET_UR); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_DL, DEFAULT_POFFSET_DL); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG1, DEFAULT_CONFIG1); + + setLEDDrive(DEFAULT_LDRIVE); + setProximityGain(DEFAULT_PGAIN); + setAmbientLightGain(DEFAULT_AGAIN); + setProxIntLowThresh(DEFAULT_PILT); + setProxIntHighThresh(DEFAULT_PIHT); + setLightIntLowThreshold(DEFAULT_AILT); + setLightIntHighThreshold(DEFAULT_AIHT); + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PERS, DEFAULT_PERS); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, DEFAULT_CONFIG2); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, DEFAULT_CONFIG3); + + /* Set default values for gesture sense registers */ +#ifdef USE_APDS9960_GESTURE + setGestureEnterThresh(DEFAULT_GPENTH); + setGestureExitThresh(DEFAULT_GEXTH); + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF1, DEFAULT_GCONF1); + + setGestureGain(DEFAULT_GGAIN); + setGestureLEDDrive(DEFAULT_GLDRIVE); + setGestureWaitTime(DEFAULT_GWTIME); + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_U, DEFAULT_GOFFSET); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_D, DEFAULT_GOFFSET); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_L, DEFAULT_GOFFSET); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_R, DEFAULT_GOFFSET); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPULSE, DEFAULT_GPULSE); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF3, DEFAULT_GCONF3); + + setGestureIntEnable(DEFAULT_GIEN); +#endif // USE_APDS9960_GESTURE + + disablePower(); // go to sleep + + return true; +} + + +/******************************************************************************\ * Public methods for controlling the APDS-9960 - ******************************************************************************/ +\******************************************************************************/ /** * @brief Reads and returns the contents of the ENABLE register * * @return Contents of the ENABLE register. 0xFF if error. */ -uint8_t getMode(void) -{ - uint8_t enable_value; +inline uint8_t getMode(void) { + uint8_t enable_value; - /* Read current ENABLE register */ - enable_value = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; + /* Read current ENABLE register */ + enable_value = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); - return enable_value; + return enable_value; } /** @@ -1347,32 +1290,30 @@ uint8_t getMode(void) * @param[in] mode which feature to enable * @param[in] enable ON (1) or OFF (0) */ -void setMode(uint8_t mode, uint8_t enable) -{ - uint8_t reg_val; +void setMode(uint8_t mode, uint8_t enable) { + uint8_t reg_val; - /* Read current ENABLE register */ - reg_val = getMode(); + /* Read current ENABLE register */ + reg_val = getMode(); - - /* Change bit(s) in ENABLE register */ - enable = enable & 0x01; - if( mode >= 0 && mode <= 6 ) { - if (enable) { - reg_val |= (1 << mode); - } else { - reg_val &= ~(1 << mode); - } - } else if( mode == ALL ) { - if (enable) { - reg_val = 0x7F; - } else { - reg_val = 0x00; - } + /* Change bit(s) in ENABLE register */ + enable = enable & 0x01; + if (mode <= 6) { + if (enable) { + reg_val |= (1 << mode); + } else { + reg_val &= ~(1 << mode); } + } else if (mode == ALL) { + if (enable) { + reg_val = 0x7F; + } else { + reg_val = 0x00; + } + } - /* Write value back to ENABLE register */ - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, reg_val) ; + /* Write value back to ENABLE register */ + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, reg_val); } /** @@ -1380,23 +1321,21 @@ void setMode(uint8_t mode, uint8_t enable) * * no interrupts */ -void enableLightSensor(void) -{ - /* Set default gain, interrupts, enable power, and enable sensor */ - setAmbientLightGain(DEFAULT_AGAIN); - setAmbientLightIntEnable(0); - enablePower() ; - setMode(AMBIENT_LIGHT, 1) ; +void enableLightSensor(void) { + /* Set default gain, interrupts, enable power, and enable sensor */ + setAmbientLightGain(DEFAULT_AGAIN); + setAmbientLightIntEnable(OFF); + enablePower(); + setMode(AMBIENT_LIGHT, ON); } /** * @brief Ends the light sensor on the APDS-9960 * */ -void disableLightSensor(void) -{ - setAmbientLightIntEnable(0) ; - setMode(AMBIENT_LIGHT, 0) ; +void disableLightSensor(void) { + setAmbientLightIntEnable(OFF); + setMode(AMBIENT_LIGHT, OFF); } /** @@ -1404,62 +1343,59 @@ void disableLightSensor(void) * * no interrupts */ -void enableProximitySensor(void) -{ - /* Set default gain, LED, interrupts, enable power, and enable sensor */ - setProximityGain(DEFAULT_PGAIN); - setLEDDrive(DEFAULT_LDRIVE) ; - setProximityIntEnable(0) ; - enablePower(); - setMode(PROXIMITY, 1) ; +void enableProximitySensor(void) { + /* Set default gain, LED, interrupts, enable power, and enable sensor */ + setProximityGain(DEFAULT_PGAIN); + setLEDDrive(DEFAULT_LDRIVE); + setProximityIntEnable(OFF); + enablePower(); + setMode(PROXIMITY, ON); } /** * @brief Ends the proximity sensor on the APDS-9960 * */ -void disableProximitySensor(void) -{ - setProximityIntEnable(0) ; - setMode(PROXIMITY, 0) ; +void disableProximitySensor(void) { + setProximityIntEnable(OFF); + setMode(PROXIMITY, OFF); } +#ifdef USE_APDS9960_GESTURE /** * @brief Starts the gesture recognition engine on the APDS-9960 * * no interrupts */ -void enableGestureSensor(void) -{ - /* Enable gesture mode - Set ENABLE to 0 (power off) - Set WTIME to 0xFF - Set AUX to LED_BOOST_300 - Enable PON, WEN, PEN, GEN in ENABLE - */ +void enableGestureSensor(void) { + /* Enable gesture mode + Set ENABLE to 0 (power off) + Set WTIME to 0xFF + Set AUX to LED_BOOST_300 + Enable PON, WEN, PEN, GEN in ENABLE + */ - resetGestureParameters(); - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, 0xFF) ; - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE) ; - setLEDBoost(LED_BOOST_100); // tip from jonn26 - 100 for 300 ---- 200 from Adafruit - setGestureIntEnable(0) ; - setGestureMode(1); - enablePower() ; - setMode(WAIT, 1) ; - setMode(PROXIMITY, 1) ; - setMode(GESTURE, 1); + resetGestureParameters(); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, 0xFF); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE); + setLEDBoost(LED_BOOST_100); // tip from jonn26 - 100 for 300 ---- 200 from Adafruit + setGestureIntEnable(OFF); + setGestureMode(ON); + enablePower(); + setMode(WAIT, ON); + setMode(PROXIMITY, ON); + setMode(GESTURE, ON); } /** * @brief Ends the gesture recognition engine on the APDS-9960 * */ -void disableGestureSensor(void) -{ - resetGestureParameters(); - setGestureIntEnable(0) ; - setGestureMode(0) ; - setMode(GESTURE, 0) ; +void disableGestureSensor(void) { + resetGestureParameters(); + setGestureIntEnable(OFF); + setGestureMode(OFF); + setMode(GESTURE, OFF); } /** @@ -1467,22 +1403,17 @@ void disableGestureSensor(void) * * @return True if gesture available. False otherwise. */ -bool isGestureAvailable(void) -{ - uint8_t val; +bool isGestureAvailable(void) { + uint8_t val; - /* Read value from GSTATUS register */ - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS) ; + /* Read value from GSTATUS register */ + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS); - /* Shift and mask out GVALID bit */ - val &= APDS9960_GVALID; + /* Shift and mask out GVALID bit */ + val &= APDS9960_GVALID; - /* Return true/false based on GVALID bit */ - if( val == 1) { - return true; - } else { - return false; - } + /* Return true/false based on GVALID bit */ + return (val == 1); } /** @@ -1490,143 +1421,183 @@ bool isGestureAvailable(void) * * @return Number corresponding to gesture. -1 on error. */ -int16_t readGesture(void) -{ - uint8_t fifo_level = 0; - uint8_t bytes_read = 0; - uint8_t fifo_data[128]; - uint8_t gstatus; - uint16_t motion; - uint16_t i; - uint8_t gesture_loop_counter = 0; // don't loop forever later +int16_t readGesture(void) { + uint8_t fifo_level = 0; + uint8_t fifo_data[128]; + uint8_t gstatus; + int16_t motion; + uint16_t i; + uint8_t gesture_loop_counter = 0; // don't loop forever later + int8_t bytes_read = 0; - /* Make sure that power and gesture is on and data is valid */ - if( !isGestureAvailable() || !(getMode() & 0b01000001) ) { - return DIR_NONE; + /* Make sure that power and gesture is on and data is valid */ + if (!isGestureAvailable() || !(getMode() & 0b01000001)) { + return DIR_NONE; + } + + /* Keep looping as long as gesture data is valid */ + while (1) { + if (gesture_loop_counter == APDS9960_MAX_GESTURE_CYCLES) { // We will escape after a few loops + disableGestureSensor(); // stop the sensor to prevent problems with power consumption/blocking and return to the main loop + APDS9960_overload = true; // we report this as "long"-gesture + AddLog_P(LOG_LEVEL_DEBUG, PSTR("Sensor overload")); } + gesture_loop_counter += 1; - /* Keep looping as long as gesture data is valid */ - while(1) { - if (gesture_loop_counter == APDS9960_MAX_GESTURE_CYCLES){ // We will escape after a few loops - disableGestureSensor(); // stop the sensor to prevent problems with power consumption/blocking and return to the main loop - APDS9960_overload = true; // we report this as "long"-gesture - AddLog_P(LOG_LEVEL_DEBUG, PSTR("Sensor overload")); + /* Wait some time to collect next batch of FIFO data */ + delay(FIFO_PAUSE_TIME); + + /* Get the contents of the STATUS register. Is data still valid? */ + gstatus = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS); + + /* If we have valid data, read in FIFO */ + if ((gstatus & APDS9960_GVALID) == APDS9960_GVALID) { + /* Read the current FIFO level */ + fifo_level = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GFLVL); + +#ifdef USE_DEBUG_DRIVER + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DRV: FIFO Level : %d"), fifo_level); +#endif // USE_DEBUG_DRIVER + + /* If there's stuff in the FIFO, read it into our data block */ + if (fifo_level > 0) { + bytes_read = (fifo_level * 4); + + if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_GFIFO_U, (uint8_t*)fifo_data, bytes_read)) { + return APDS9960_ERROR; } - gesture_loop_counter += 1; - /* Wait some time to collect next batch of FIFO data */ - delay(FIFO_PAUSE_TIME); - /* Get the contents of the STATUS register. Is data still valid? */ - gstatus = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS); +#ifdef USE_DEBUG_DRIVER + char output[(bytes_read * 2) + 1]; + char *ptr = &output[0]; - /* If we have valid data, read in FIFO */ - if( (gstatus & APDS9960_GVALID) == APDS9960_GVALID ) { + for ( i = 0; i < bytes_read; i++ ) { + ptr += sprintf(ptr, "%02X", fifo_data[i]); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DRV: FIFO Dump : %s"), output); +#endif // USE_DEBUG_DRIVER - /* Read the current FIFO level */ - fifo_level = I2cRead8(APDS9960_I2C_ADDR,APDS9960_GFLVL) ; + /* If at least 1 set of data, sort the data into U/D/L/R */ + if (bytes_read >= 4) { + for (i = 0; i < bytes_read; i += 4) { + gesture_data.u_data[gesture_data.index] = fifo_data[i + 0]; + gesture_data.d_data[gesture_data.index] = fifo_data[i + 1]; + gesture_data.l_data[gesture_data.index] = fifo_data[i + 2]; + gesture_data.r_data[gesture_data.index] = fifo_data[i + 3]; + gesture_data.index++; + gesture_data.total_gestures++; + } - /* If there's stuff in the FIFO, read it into our data block */ - if( fifo_level > 0) { - bytes_read = wireReadDataBlock( APDS9960_GFIFO_U, - (uint8_t*)fifo_data, - (fifo_level * 4) ); - if( bytes_read == -1 ) { - return APDS9960_ERROR; - } - - /* If at least 1 set of data, sort the data into U/D/L/R */ - if( bytes_read >= 4 ) { - for( i = 0; i < bytes_read; i += 4 ) { - gesture_data_.u_data[gesture_data_.index] = \ - fifo_data[i + 0]; - gesture_data_.d_data[gesture_data_.index] = \ - fifo_data[i + 1]; - gesture_data_.l_data[gesture_data_.index] = \ - fifo_data[i + 2]; - gesture_data_.r_data[gesture_data_.index] = \ - fifo_data[i + 3]; - gesture_data_.index++; - gesture_data_.total_gestures++; - } - /* Filter and process gesture data. Decode near/far state */ - if( processGestureData() ) { - if( decodeGesture() ) { - //***TODO: U-Turn Gestures - } - } - /* Reset data */ - gesture_data_.index = 0; - gesture_data_.total_gestures = 0; - } - } - } else { - - /* Determine best guessed gesture and clean up */ - delay(FIFO_PAUSE_TIME); - decodeGesture(); - motion = gesture_motion_; - resetGestureParameters(); - return motion; + /* Filter and process gesture data. Decode near/far state */ + if (processGestureData()) { + if (decodeGesture()) { + // TODO(xx): U-Turn Gestures + } + } + /* Reset data */ + gesture_data.index = 0; + gesture_data.total_gestures = 0; } + } + } else { + /* Determine best guessed gesture and clean up */ + delay(FIFO_PAUSE_TIME); + decodeGesture(); + motion = gesture.motion_; + resetGestureParameters(); + return motion; } + } } +#endif // USE_APDS9960_GESTURE + /** * Turn the APDS-9960 on * */ -void enablePower(void) -{ - setMode(POWER, 1) ; +inline void enablePower(void) { + setMode(POWER, ON); } /** * Turn the APDS-9960 off * */ -void disablePower(void) -{ - setMode(POWER, 0) ; +inline void disablePower(void) { + setMode(POWER, OFF); } -/******************************************************************************* + +/******************************************************************************\ * Ambient light and color sensor controls - ******************************************************************************/ +\******************************************************************************/ - /** - * @brief Reads the ARGB-Data and fills color_data - * - */ - -void readAllColorAndProximityData(void) -{ - if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_CDATAL, (uint8_t *) &color_data, (uint16_t)9)) - { +#if defined(USE_APDS9960_COLOR) || defined(USE_APDS9960_PROXIMITY) +/** + * @brief Reads the ARGB-Data and fills color_data + */ +inline void readAllColorAndProximityData(void) { + if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_CDATAL, (uint8_t *) &color_data, (uint16_t)9)) { // not absolutely shure, if this is a correct way to do this, but it is very short // we fill the struct byte by byte } } -/******************************************************************************* +void APDS9960_adjustATime(void) { // not really used atm + // readAllColorAndProximityData(); + I2cValidRead16LE(&color_data.a, APDS9960_I2C_ADDR, APDS9960_CDATAL); + // disablePower(); + + if (color_data.a < (uint16_t)20) { + APDS9960_aTime = 0x40; + } else if (color_data.a < (uint16_t)40) { + APDS9960_aTime = 0x80; + } else if (color_data.a < (uint16_t)50) { + APDS9960_aTime = DEFAULT_ATIME; + } else if (color_data.a < (uint16_t)70) { + APDS9960_aTime = 0xc0; + } + + if (color_data.a < 200) { + APDS9960_aTime = 0xe9; + } + /* if (color_data.a < 10000){ + APDS9960_aTime = 0xF0; + }*/ + else { + APDS9960_aTime = 0xff; + } + + // disableLightSensor(); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, APDS9960_aTime); + enablePower(); + enableLightSensor(); + delay(20); +} +#endif // USE_APDS9960_COLOR || USE_APDS9960_PROXIMITY + +/******************************************************************************\ * High-level gesture controls - ******************************************************************************/ +\******************************************************************************/ + +#ifdef USE_APDS9960_GESTURE /** * @brief Resets all the parameters in the gesture data member */ -void resetGestureParameters(void) -{ - gesture_data_.index = 0; - gesture_data_.total_gestures = 0; +void resetGestureParameters(void) { + gesture_data.index = 0; + gesture_data.total_gestures = 0; - gesture_ud_delta_ = 0; - gesture_lr_delta_ = 0; + gesture.ud_delta_ = 0; + gesture.lr_delta_ = 0; - gesture_ud_count_ = 0; - gesture_lr_count_ = 0; + gesture.ud_count_ = 0; + gesture.lr_count_ = 0; - gesture_state_ = 0; - gesture_motion_ = DIR_NONE; + gesture.state_ = 0; + gesture.motion_ = DIR_NONE; } /** @@ -1634,103 +1605,97 @@ void resetGestureParameters(void) * * @return True if near or far state seen. False otherwise. */ -bool processGestureData(void) -{ - uint8_t u_first = 0; - uint8_t d_first = 0; - uint8_t l_first = 0; - uint8_t r_first = 0; - uint8_t u_last = 0; - uint8_t d_last = 0; - uint8_t l_last = 0; - uint8_t r_last = 0; - uint16_t ud_ratio_first; - uint16_t lr_ratio_first; - uint16_t ud_ratio_last; - uint16_t lr_ratio_last; - uint16_t ud_delta; - uint16_t lr_delta; - uint16_t i; +bool processGestureData(void) { + uint8_t u_first = 0; + uint8_t d_first = 0; + uint8_t l_first = 0; + uint8_t r_first = 0; + uint8_t u_last = 0; + uint8_t d_last = 0; + uint8_t l_last = 0; + uint8_t r_last = 0; + uint16_t ud_ratio_first; + uint16_t lr_ratio_first; + uint16_t ud_ratio_last; + uint16_t lr_ratio_last; + uint16_t ud_delta; + uint16_t lr_delta; + uint16_t i; - /* If we have less than 4 total gestures, that's not enough */ - if( gesture_data_.total_gestures <= 4 ) { - return false; - } - - /* Check to make sure our data isn't out of bounds */ - if( (gesture_data_.total_gestures <= 32) && \ - (gesture_data_.total_gestures > 0) ) { - - /* Find the first value in U/D/L/R above the threshold */ - for( i = 0; i < gesture_data_.total_gestures; i++ ) { - if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) { - - u_first = gesture_data_.u_data[i]; - d_first = gesture_data_.d_data[i]; - l_first = gesture_data_.l_data[i]; - r_first = gesture_data_.r_data[i]; - break; - } - } - - /* If one of the _first values is 0, then there is no good data */ - if( (u_first == 0) || (d_first == 0) || \ - (l_first == 0) || (r_first == 0) ) { - - return false; - } - /* Find the last value in U/D/L/R above the threshold */ - for( i = gesture_data_.total_gestures - 1; i >= 0; i-- ) { - - if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) { - - u_last = gesture_data_.u_data[i]; - d_last = gesture_data_.d_data[i]; - l_last = gesture_data_.l_data[i]; - r_last = gesture_data_.r_data[i]; - break; - } - } - } - - /* Calculate the first vs. last ratio of up/down and left/right */ - ud_ratio_first = ((u_first - d_first) * 100) / (u_first + d_first); - lr_ratio_first = ((l_first - r_first) * 100) / (l_first + r_first); - ud_ratio_last = ((u_last - d_last) * 100) / (u_last + d_last); - lr_ratio_last = ((l_last - r_last) * 100) / (l_last + r_last); - - /* Determine the difference between the first and last ratios */ - ud_delta = ud_ratio_last - ud_ratio_first; - lr_delta = lr_ratio_last - lr_ratio_first; - - /* Accumulate the UD and LR delta values */ - gesture_ud_delta_ += ud_delta; - gesture_lr_delta_ += lr_delta; - - /* Determine U/D gesture */ - if( gesture_ud_delta_ >= GESTURE_SENSITIVITY_1 ) { - gesture_ud_count_ = 1; - } else if( gesture_ud_delta_ <= -GESTURE_SENSITIVITY_1 ) { - gesture_ud_count_ = -1; - } else { - gesture_ud_count_ = 0; - } - - /* Determine L/R gesture */ - if( gesture_lr_delta_ >= GESTURE_SENSITIVITY_1 ) { - gesture_lr_count_ = 1; - } else if( gesture_lr_delta_ <= -GESTURE_SENSITIVITY_1 ) { - gesture_lr_count_ = -1; - } else { - gesture_lr_count_ = 0; - } + /* If we have less than 4 total gestures, that's not enough */ + if (gesture_data.total_gestures <= 4) { return false; + } + + /* Check to make sure our data isn't out of bounds */ + if ((gesture_data.total_gestures <= 32) && \ + (gesture_data.total_gestures > 0)) { + /* Find the first value in U/D/L/R above the threshold */ + for (i = 0; i < gesture_data.total_gestures; i++) { + if ((gesture_data.u_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.d_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.l_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.r_data[i] > GESTURE_THRESHOLD_OUT) ) { + u_first = gesture_data.u_data[i]; + d_first = gesture_data.d_data[i]; + l_first = gesture_data.l_data[i]; + r_first = gesture_data.r_data[i]; + break; + } + } + + /* If one of the _first values is 0, then there is no good data */ + if ((u_first == 0) || (d_first == 0) || (l_first == 0) || (r_first == 0)) { + return false; + } + + /* Find the last value in U/D/L/R above the threshold */ + for (i = gesture_data.total_gestures - 1; i >= 0; i--) { + if ((gesture_data.u_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.d_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.l_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.r_data[i] > GESTURE_THRESHOLD_OUT)) { + u_last = gesture_data.u_data[i]; + d_last = gesture_data.d_data[i]; + l_last = gesture_data.l_data[i]; + r_last = gesture_data.r_data[i]; + break; + } + } + } + + /* Calculate the first vs. last ratio of up/down and left/right */ + ud_ratio_first = ((u_first - d_first) * 100) / (u_first + d_first); + lr_ratio_first = ((l_first - r_first) * 100) / (l_first + r_first); + ud_ratio_last = ((u_last - d_last) * 100) / (u_last + d_last); + lr_ratio_last = ((l_last - r_last) * 100) / (l_last + r_last); + + /* Determine the difference between the first and last ratios */ + ud_delta = ud_ratio_last - ud_ratio_first; + lr_delta = lr_ratio_last - lr_ratio_first; + + /* Accumulate the UD and LR delta values */ + gesture.ud_delta_ += ud_delta; + gesture.lr_delta_ += lr_delta; + + /* Determine U/D gesture */ + if (gesture.ud_delta_ >= GESTURE_SENSITIVITY_1) { + gesture.ud_count_ = 1; + } else if (gesture.ud_delta_ <= -GESTURE_SENSITIVITY_1) { + gesture.ud_count_ = -1; + } else { + gesture.ud_count_ = 0; + } + + /* Determine L/R gesture */ + if (gesture.lr_delta_ >= GESTURE_SENSITIVITY_1) { + gesture.lr_count_ = 1; + } else if (gesture.lr_delta_ <= -GESTURE_SENSITIVITY_1) { + gesture.lr_count_ = -1; + } else { + gesture.lr_count_ = 0; + } + return false; } /** @@ -1738,213 +1703,232 @@ bool processGestureData(void) * * @return True if near/far event. False otherwise. */ -bool decodeGesture(void) -{ - - /* Determine swipe direction */ - if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 0) ) { - gesture_motion_ = DIR_UP; - } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 0) ) { - gesture_motion_ = DIR_DOWN; - } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == 1) ) { - gesture_motion_ = DIR_RIGHT; - } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == -1) ) { - gesture_motion_ = DIR_LEFT; - } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_UP; - } else { - gesture_motion_ = DIR_RIGHT; - } - } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == -1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_DOWN; - } else { - gesture_motion_ = DIR_LEFT; - } - } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == -1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_UP; - } else { - gesture_motion_ = DIR_LEFT; - } - } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_DOWN; - } else { - gesture_motion_ = DIR_RIGHT; - } +bool decodeGesture(void) { + /* Determine swipe direction */ + if ((gesture.ud_count_ == -1) && (gesture.lr_count_ == 0)) { + gesture.motion_ = DIR_UP; + } else if ((gesture.ud_count_ == 1) && (gesture.lr_count_ == 0)) { + gesture.motion_ = DIR_DOWN; + } else if ((gesture.ud_count_ == 0) && (gesture.lr_count_ == 1)) { + gesture.motion_ = DIR_RIGHT; + } else if ((gesture.ud_count_ == 0) && (gesture.lr_count_ == -1)) { + gesture.motion_ = DIR_LEFT; + } else if ((gesture.ud_count_ == -1) && (gesture.lr_count_ == 1)) { + if (abs(gesture.ud_delta_) > abs(gesture.lr_delta_)) { + gesture.motion_ = DIR_UP; } else { - return false; + gesture.motion_ = DIR_RIGHT; } + } else if ((gesture.ud_count_ == 1) && (gesture.lr_count_ == -1)) { + if (abs(gesture.ud_delta_) > abs(gesture.lr_delta_)) { + gesture.motion_ = DIR_DOWN; + } else { + gesture.motion_ = DIR_LEFT; + } + } else if ((gesture.ud_count_ == -1) && (gesture.lr_count_ == -1)) { + if (abs(gesture.ud_delta_) > abs(gesture.lr_delta_)) { + gesture.motion_ = DIR_UP; + } else { + gesture.motion_ = DIR_LEFT; + } + } else if ((gesture.ud_count_ == 1) && (gesture.lr_count_ == 1)) { + if (abs(gesture.ud_delta_) > abs(gesture.lr_delta_)) { + gesture.motion_ = DIR_DOWN; + } else { + gesture.motion_ = DIR_RIGHT; + } + } else { + return false; + } - return true; + return true; } void handleGesture(void) { - if (isGestureAvailable() ) { + if (isGestureAvailable()) { switch (readGesture()) { case DIR_UP: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("UP")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Up")); + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_UP); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_UP); break; case DIR_DOWN: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("DOWN")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Down")); + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_DOWN); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_DOWN); break; case DIR_LEFT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("LEFT")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Left")); + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_LEFT); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_LEFT); break; case DIR_RIGHT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("RIGHT")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Right")); + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_RIGHT); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_RIGHT); break; default: - if(APDS9960_overload) - { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("LONG")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Long")); - } - else{ - AddLog_P(LOG_LEVEL_DEBUG, PSTR("NONE")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("None")); - } + if (APDS9960_overload) { + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_LONG); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_LONG); + } else { + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_NONE); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_NONE); + } + break; } MqttPublishSensor(); } } -void APDS9960_adjustATime(void) // not really used atm -{ - //readAllColorAndProximityData(); - I2cValidRead16LE(&color_data.a, APDS9960_I2C_ADDR, APDS9960_CDATAL); - //disablePower(); - - if (color_data.a < (uint16_t)20){ - APDS9960_aTime = 0x40; - } - else if (color_data.a < (uint16_t)40){ - APDS9960_aTime = 0x80; - } - else if (color_data.a < (uint16_t)50){ - APDS9960_aTime = DEFAULT_ATIME; - } - else if (color_data.a < (uint16_t)70){ - APDS9960_aTime = 0xc0; - } - if (color_data.a < 200){ - APDS9960_aTime = 0xe9; - } -/* if (color_data.a < 10000){ - APDS9960_aTime = 0xF0; - }*/ - else{ - APDS9960_aTime = 0xff; - } - - //disableLightSensor(); - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, APDS9960_aTime); - enablePower(); - enableLightSensor(); - delay(20); -} - - -void APDS9960_loop(void) -{ - if (recovery_loop_counter > 0){ +void APDS9960_loop(void) { + if (recovery_loop_counter > 0) { recovery_loop_counter -= 1; } - if (recovery_loop_counter == 1 && APDS9960_overload){ //restart sensor just before the end of recovery from long press + + if (recovery_loop_counter == 1 && APDS9960_overload) { // restart sensor just before the end of recovery from long press enableGestureSensor(); APDS9960_overload = false; Response_P(PSTR("{\"Gesture\":\"On\"}")); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); // only after the long break we report, that we are online again + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); // only after the long break we report, that we are online again gesture_mode = 1; } if (gesture_mode) { - if (recovery_loop_counter == 0){ + if (recovery_loop_counter == 0) { handleGesture(); - if (APDS9960_overload) - { + if (APDS9960_overload) { disableGestureSensor(); recovery_loop_counter = APDS9960_LONG_RECOVERY; // long pause after overload/long press - number of stateloops Response_P(PSTR("{\"Gesture\":\"Off\"}")); MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); gesture_mode = 0; - } } + } } } -void APDS9960_detect(void) -{ - if (APDS9960type || I2cActive(APDS9960_I2C_ADDR)) { return; } +#endif // USE_APDS9960_GESTURE - APDS9960type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID); - if (APDS9960type == APDS9960_CHIPID_1 || APDS9960type == APDS9960_CHIPID_2 || APDS9960type == APDS9960_CHIPID_3) { +void APDS9960_detect(void) { + if (APDS9960_type || I2cActive(APDS9960_I2C_ADDR)) { return; } + + APDS9960_type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID); + +#ifdef USE_DEBUG_DRIVER + // Debug new chip + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DRV: %s Chip %X"), APDS9960_TAG, APDS9960_type); +#endif // USE_DEBUG_DRIVER + + if (APDS9960_type == APDS9960_CHIPID_1 || APDS9960_type == APDS9960_CHIPID_2 || APDS9960_type == APDS9960_CHIPID_3) { if (APDS9960_init()) { - I2cSetActiveFound(APDS9960_I2C_ADDR, APDS9960stype); + I2cSetActiveFound(APDS9960_I2C_ADDR, APDS9960_TAG); enableProximitySensor(); + +#if defined(USE_APDS9960_GESTURE) && USE_APDS9960_STARTMODE == APDS9960_MODE_GESTURE + gesture_mode = 1; enableGestureSensor(); +#endif // USE_APDS9960_GESTURE + +#if ( defined(USE_APDS9960_COLOR) || defined(USE_APDS9960_PROXIMITY) ) && USE_APDS9960_STARTMODE == APDS9960_MODE_COLOR + gesture_mode = 0; + enableLightSensor(); + APDS9960_overload = false; +#endif // USE_APDS9960_GESTURE } else { - APDS9960type = 0; + APDS9960_type = 0; } } else { - APDS9960type = 0; + APDS9960_type = 0; } + +#ifdef USE_APDS9960_GESTURE currentGesture[0] = '\0'; +#endif // USE_APDS9960_GESTURE } /*********************************************************************************************\ * Presentation \*********************************************************************************************/ -void APDS9960_show(bool json) -{ - if (!APDS9960type) { return; } +void APDS9960_show(bool json) { + if (!APDS9960_type) { return; } if (!gesture_mode && !APDS9960_overload) { - char red_chr[10]; - char green_chr[10]; - char blue_chr[10]; - char ambient_chr[10]; - char cct_chr[10]; - char prox_chr[10]; + +#if defined(USE_APDS9960_COLOR) || defined(USE_APDS9960_PROXIMITY) + uint16_t ambient; readAllColorAndProximityData(); + ambient = color_data.a/4; - sprintf (ambient_chr, "%u", color_data.a/4); - sprintf (red_chr, "%u", color_data.r); - sprintf (green_chr, "%u", color_data.g); - sprintf (blue_chr, "%u", color_data.b ); - sprintf (prox_chr, "%u", color_data.p ); - - /* disableLightSensor(); + /* disableLightSensor(); I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, DEFAULT_ATIME); // reset to default enableLightSensor();*/ +#ifdef USE_APDS9960_COLOR calculateColorTemperature(); // and calculate Lux - sprintf (cct_chr, "%u", color_data.cct); - +#endif // USE_APDS9960_COLOR if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%s,\"Green\":%s,\"Blue\":%s,\"Ambient\":%s,\"CCT\":%s,\"Proximity\":%s}"), - APDS9960stype, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr); +#if defined(USE_APDS9960_COLOR) && defined(USE_APDS9960_PROXIMITY) + ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%u,\"Green\":%u,\"Blue\":%u,\"" D_JSON_ILLUMINANCE "\":%u,\"CCT\":%u,\"Proximity\":%u}"), + APDS9960_TAG, + color_data.r, + color_data.g, + color_data.b, + ambient, + color_data.cct, + color_data.p); +#else + +#ifdef USE_APDS9960_COLOR + ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%u,\"Green\":%u,\"Blue\":%u,\"" D_JSON_ILLUMINANCE "\":%u,\"CCT\":%u}"), + APDS9960_TAG, + color_data.r, + color_data.g, + color_data.b, + ambient, + color_data.cct); +#endif // USE_APDS9960_COLOR + +#ifdef USE_APDS9960_PROXIMITY + ResponseAppend_P(PSTR(",\"%s\":{\"Proximity\":%u}"), + APDS9960_TAG, + color_data.p); +#endif // USE_APDS9960_PROXIMITY + +#endif // USE_APDS9960_COLOR && USE_APDS9960_PROXIMITY #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_APDS_9960_SNS, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr ); + +#ifdef USE_APDS9960_COLOR + WSContentSend_PD(HTTP_SNS_COLOR_RED, APDS9960_TAG, color_data.r); + WSContentSend_PD(HTTP_SNS_COLOR_GREEN, APDS9960_TAG, color_data.g); + WSContentSend_PD(HTTP_SNS_COLOR_BLUE, APDS9960_TAG, color_data.b); + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, APDS9960_TAG, ambient); + WSContentSend_PD(HTTP_SNS_CCT, APDS9960_TAG, color_data.cct); +#endif // USE_APDS9960_COLOR + +#ifdef USE_APDS9960_PROXIMITY + WSContentSend_PD(HTTP_SNS_PROXIMITY, APDS9960_TAG, color_data.p); +#endif // USE_APDS9960_PROXIMITY + #endif // USE_WEBSERVER } - } - else { - if (json && (currentGesture[0] != '\0' )) { - ResponseAppend_P(PSTR(",\"%s\":{\"%s\":1}"), APDS9960stype, currentGesture); - currentGesture[0] = '\0'; +#endif // USE_APDS9960_COLOR || USE_APDS9960_PROXIMITY + +#ifdef USE_APDS9960_GESTURE + } else { + if (currentGesture[0] != '\0') { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":1}"), APDS9960_TAG, currentGesture); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_GESTURE, APDS9960_TAG, currentGesture); +#endif // USE_WEBSERVER + currentGesture[0] = '\0'; + } } +#endif // USE_APDS9960_GESTURE + } } @@ -1959,19 +1943,21 @@ void APDS9960_show(bool json) * Sensor27 | 2 / On | Enable gesture mode with half gain \*********************************************************************************************/ -bool APDS9960CommandSensor(void) -{ +bool APDS9960CommandSensor(void) { bool serviced = true; switch (XdrvMailbox.payload) { - case 0: // Off + case 0: // Off +#ifdef USE_APDS9960_GESTURE disableGestureSensor(); +#endif // USE_APDS9960_GESTURE gesture_mode = 0; enableLightSensor(); - APDS9960_overload = false; // prevent unwanted re-enabling + APDS9960_overload = false; // prevent unwanted re-enabling break; - case 1: // On with default gain of 4x - if (APDS9960type) { +#ifdef USE_APDS9960_GESTURE + case 1: // On with default gain of 4x + if (APDS9960_type) { setGestureGain(DEFAULT_GGAIN); setProximityGain(DEFAULT_PGAIN); disableLightSensor(); @@ -1980,7 +1966,7 @@ bool APDS9960CommandSensor(void) } break; case 2: // gain of 2x , needed for some models - if (APDS9960type) { + if (APDS9960_type) { setGestureGain(GGAIN_2X); setProximityGain(PGAIN_2X); disableLightSensor(); @@ -1988,15 +1974,16 @@ bool APDS9960CommandSensor(void) gesture_mode = 1; } break; +#endif // USE_APDS9960_GESTURE default: int temp_aTime = (uint8_t)XdrvMailbox.payload; - if (temp_aTime > 2 && temp_aTime < 256){ + if (temp_aTime > 2 && temp_aTime < 256) { disablePower(); I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, temp_aTime); enablePower(); enableLightSensor(); } - break; + break; } Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_27, GetStateText(gesture_mode)); @@ -2007,28 +1994,28 @@ bool APDS9960CommandSensor(void) * Interface \*********************************************************************************************/ -bool Xsns27(uint8_t function) -{ +bool Xsns27(uint8_t function) { if (!I2cEnabled(XI2C_21)) { return false; } bool result = false; if (FUNC_INIT == function) { APDS9960_detect(); - } - else if (APDS9960type) { + } else if (APDS9960_type) { switch (function) { +#ifdef USE_APDS9960_GESTURE case FUNC_EVERY_50_MSECOND: - APDS9960_loop(); - break; + APDS9960_loop(); + break; +#endif // USE_APDS9960_GESTURE case FUNC_COMMAND_SENSOR: - if (XSNS_27 == XdrvMailbox.index) { + if (XSNS_27 == XdrvMailbox.index) { result = APDS9960CommandSensor(); - } - break; + } + break; case FUNC_JSON_APPEND: - APDS9960_show(1); - break; + APDS9960_show(1); + break; #ifdef USE_WEBSERVER case FUNC_WEB_SENSOR: APDS9960_show(0); @@ -2038,5 +2025,6 @@ bool Xsns27(uint8_t function) } return result; } + #endif // USE_APDS9960 #endif // USE_I2C diff --git a/tasmota/xsns_28_tm1638.ino b/tasmota/xsns_28_tm1638.ino index da4de6999..9df220848 100644 --- a/tasmota/xsns_28_tm1638.ino +++ b/tasmota/xsns_28_tm1638.ino @@ -144,10 +144,10 @@ uint8_t Tm1638GetButtons(void) void TmInit(void) { tm1638_type = 0; - if ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99)) { - tm1638_clock_pin = pin[GPIO_TM16CLK]; - tm1638_data_pin = pin[GPIO_TM16DIO]; - tm1638_strobe_pin = pin[GPIO_TM16STB]; + if (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB)) { + tm1638_clock_pin = Pin(GPIO_TM16CLK); + tm1638_data_pin = Pin(GPIO_TM16DIO); + tm1638_strobe_pin = Pin(GPIO_TM16STB); pinMode(tm1638_data_pin, OUTPUT); pinMode(tm1638_clock_pin, OUTPUT); diff --git a/tasmota/xsns_34_hx711.ino b/tasmota/xsns_34_hx711.ino index 86e920945..8145a5a55 100644 --- a/tasmota/xsns_34_hx711.ino +++ b/tasmota/xsns_34_hx711.ino @@ -282,9 +282,9 @@ long HxWeight(void) void HxInit(void) { Hx.type = 0; - if ((pin[GPIO_HX711_DAT] < 99) && (pin[GPIO_HX711_SCK] < 99)) { - Hx.pin_sck = pin[GPIO_HX711_SCK]; - Hx.pin_dout = pin[GPIO_HX711_DAT]; + if (PinUsed(GPIO_HX711_DAT) && PinUsed(GPIO_HX711_SCK)) { + Hx.pin_sck = Pin(GPIO_HX711_SCK); + Hx.pin_dout = Pin(GPIO_HX711_DAT); pinMode(Hx.pin_sck, OUTPUT); pinMode(Hx.pin_dout, INPUT); diff --git a/tasmota/xsns_35_tx20.ino b/tasmota/xsns_35_tx20.ino index 06d715ac0..cf41a41fb 100644 --- a/tasmota/xsns_35_tx20.ino +++ b/tasmota/xsns_35_tx20.ino @@ -206,7 +206,7 @@ void TX2xStartRead(void) delayMicroseconds(TX2X_BIT_TIME / 2); for (int32_t bitcount = 41; bitcount > 0; bitcount--) { - uint32_t dpin = (digitalRead(pin[GPIO_TX2X_TXD_BLACK])); + uint32_t dpin = (digitalRead(Pin(GPIO_TX2X_TXD_BLACK))); #ifdef USE_TX23_WIND_SENSOR dpin ^= 1; #endif // USE_TX23_WIND_SENSOR @@ -263,7 +263,7 @@ void TX2xStartRead(void) // Must clear this bit in the interrupt register, // it gets set even when interrupts are disabled - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << pin[GPIO_TX2X_TXD_BLACK]); + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << Pin(GPIO_TX2X_TXD_BLACK)); } bool Tx2xAvailable(void) @@ -338,13 +338,13 @@ void Tx2xRead(void) // TX23 start transmission by pulling down TxD line for at minimum 500ms // so we pull TxD signal to low every 3 seconds tx23_stage = 0; - pinMode(pin[GPIO_TX2X_TXD_BLACK], OUTPUT); - digitalWrite(pin[GPIO_TX2X_TXD_BLACK], LOW); + pinMode(Pin(GPIO_TX2X_TXD_BLACK), OUTPUT); + digitalWrite(Pin(GPIO_TX2X_TXD_BLACK), LOW); } else if ((uptime % TX23_READ_INTERVAL)==1) { // after pulling down TxD: pull-up TxD every x+1 seconds // to trigger TX23 start transmission tx23_stage = 1; // first rising signal is invalid - pinMode(pin[GPIO_TX2X_TXD_BLACK], INPUT_PULLUP); + pinMode(Pin(GPIO_TX2X_TXD_BLACK), INPUT_PULLUP); } #endif // USE_TX23_WIND_SENSOR if (Tx2xAvailable()) { @@ -465,12 +465,12 @@ void Tx2xInit(void) #endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS #ifdef USE_TX23_WIND_SENSOR tx23_stage = 0; - pinMode(pin[GPIO_TX2X_TXD_BLACK], OUTPUT); - digitalWrite(pin[GPIO_TX2X_TXD_BLACK], LOW); + pinMode(Pin(GPIO_TX2X_TXD_BLACK), OUTPUT); + digitalWrite(Pin(GPIO_TX2X_TXD_BLACK), LOW); #else // USE_TX23_WIND_SENSOR - pinMode(pin[GPIO_TX2X_TXD_BLACK], INPUT); + pinMode(Pin(GPIO_TX2X_TXD_BLACK), INPUT); #endif // USE_TX23_WIND_SENSOR - attachInterrupt(pin[GPIO_TX2X_TXD_BLACK], TX2xStartRead, RISING); + attachInterrupt(Pin(GPIO_TX2X_TXD_BLACK), TX2xStartRead, RISING); } int32_t Tx2xNormalize(int32_t value) @@ -582,7 +582,7 @@ bool Xsns35(uint8_t function) { bool result = false; - if (pin[GPIO_TX2X_TXD_BLACK] < 99) { + if (PinUsed(GPIO_TX2X_TXD_BLACK)) { switch (function) { case FUNC_INIT: Tx2xInit(); diff --git a/tasmota/xsns_36_mgc3130.ino b/tasmota/xsns_36_mgc3130.ino index 4dd76155b..036ecf009 100644 --- a/tasmota/xsns_36_mgc3130.ino +++ b/tasmota/xsns_36_mgc3130.ino @@ -39,14 +39,11 @@ #define MGC3130_I2C_ADDR 0x42 -#define MGC3130_xfer pin[GPIO_MGC3130_XFER] -#define MGC3130_reset pin[GPIO_MGC3130_RESET] - - +uint8_t MGC3130_xfer = 0; +uint8_t MGC3130_reset = 0; bool MGC3130_type = false; char MGC3130stype[] = "MGC3130"; - #define MGC3130_SYSTEM_STATUS 0x15 #define MGC3130_REQUEST_MSG 0x06 #define MGC3130_FW_VERSION 0x83 @@ -478,6 +475,9 @@ void MGC3130_detect(void) { if (MGC3130_type || I2cActive(MGC3130_I2C_ADDR)) { return; } + MGC3130_xfer = Pin(GPIO_MGC3130_XFER); + MGC3130_reset = Pin(GPIO_MGC3130_RESET); + pinMode(MGC3130_xfer, INPUT_PULLUP); pinMode(MGC3130_reset, OUTPUT); digitalWrite(MGC3130_reset, LOW); @@ -588,7 +588,7 @@ bool Xsns36(uint8_t function) bool result = false; - if ((FUNC_INIT == function) && (pin[GPIO_MGC3130_XFER] < 99) && (pin[GPIO_MGC3130_RESET] < 99)) { + if ((FUNC_INIT == function) && PinUsed(GPIO_MGC3130_XFER) && PinUsed(GPIO_MGC3130_RESET)) { MGC3130_detect(); } else if (MGC3130_type) { diff --git a/tasmota/xsns_37_rfsensor.ino b/tasmota/xsns_37_rfsensor.ino index 87ce5c9d7..53945f2bb 100644 --- a/tasmota/xsns_37_rfsensor.ino +++ b/tasmota/xsns_37_rfsensor.ino @@ -607,9 +607,9 @@ void RfSnsInit(void) RfSnsInitAlectoV2(); #endif if (rfsns_any_sensor) { - rfsns_rf_bit = digitalPinToBitMask(pin[GPIO_RF_SENSOR]); - rfsns_rf_port = digitalPinToPort(pin[GPIO_RF_SENSOR]); - pinMode(pin[GPIO_RF_SENSOR], INPUT); + rfsns_rf_bit = digitalPinToBitMask(Pin(GPIO_RF_SENSOR)); + rfsns_rf_port = digitalPinToPort(Pin(GPIO_RF_SENSOR)); + pinMode(Pin(GPIO_RF_SENSOR), INPUT); } else { free(rfsns_raw_signal); rfsns_raw_signal = nullptr; @@ -654,14 +654,14 @@ bool Xsns37(uint8_t function) { bool result = false; - if ((pin[GPIO_RF_SENSOR] < 99) && (FUNC_INIT == function)) { + if (PinUsed(GPIO_RF_SENSOR) && (FUNC_INIT == function)) { RfSnsInit(); } else if (rfsns_raw_signal) { switch (function) { case FUNC_LOOP: if ((*portInputRegister(rfsns_rf_port) &rfsns_rf_bit) == rfsns_rf_bit) { - if (RfSnsFetchSignal(pin[GPIO_RF_SENSOR], HIGH)) { + if (RfSnsFetchSignal(Pin(GPIO_RF_SENSOR), HIGH)) { RfSnsAnalyzeRawSignal(); } } diff --git a/tasmota/xsns_38_az7798.ino b/tasmota/xsns_38_az7798.ino index b6b1dffbe..352f59389 100644 --- a/tasmota/xsns_38_az7798.ino +++ b/tasmota/xsns_38_az7798.ino @@ -267,8 +267,8 @@ void AzEverySecond(void) void AzInit(void) { az_type = 0; - if ((pin[GPIO_AZ_RXD] < 99) && (pin[GPIO_AZ_TXD] < 99)) { - AzSerial = new TasmotaSerial(pin[GPIO_AZ_RXD], pin[GPIO_AZ_TXD], 1); + if (PinUsed(GPIO_AZ_RXD) && PinUsed(GPIO_AZ_TXD)) { + AzSerial = new TasmotaSerial(Pin(GPIO_AZ_RXD), Pin(GPIO_AZ_TXD), 1); if (AzSerial->begin(9600)) { if (AzSerial->hardwareSerial()) { ClaimSerial(); } az_type = 1; diff --git a/tasmota/xsns_39_max31855.ino b/tasmota/xsns_39_max31855.ino index b4a430b54..c8cdfc7aa 100644 --- a/tasmota/xsns_39_max31855.ino +++ b/tasmota/xsns_39_max31855.ino @@ -34,13 +34,13 @@ void MAX31855_Init(void){ return; // Set GPIO modes for SW-SPI - pinMode(pin[GPIO_MAX31855CS], OUTPUT); - pinMode(pin[GPIO_MAX31855CLK], OUTPUT); - pinMode(pin[GPIO_MAX31855DO], INPUT); + pinMode(Pin(GPIO_MAX31855CS), OUTPUT); + pinMode(Pin(GPIO_MAX31855CLK), OUTPUT); + pinMode(Pin(GPIO_MAX31855DO), INPUT); // Chip not selected / Clock low - digitalWrite(pin[GPIO_MAX31855CS], HIGH); - digitalWrite(pin[GPIO_MAX31855CLK], LOW); + digitalWrite(Pin(GPIO_MAX31855CS), HIGH); + digitalWrite(Pin(GPIO_MAX31855CLK), LOW); initialized = true; } @@ -99,22 +99,22 @@ float MAX31855_GetReferenceTemperature(int32_t RawData){ int32_t MAX31855_ShiftIn(uint8_t Length){ int32_t dataIn = 0; - digitalWrite(pin[GPIO_MAX31855CS], LOW); // CS = LOW -> Start SPI communication + digitalWrite(Pin(GPIO_MAX31855CS), LOW); // CS = LOW -> Start SPI communication delayMicroseconds(1); // CS fall to output enable = max. 100ns for (uint32_t i = 0; i < Length; i++) { - digitalWrite(pin[GPIO_MAX31855CLK], LOW); + digitalWrite(Pin(GPIO_MAX31855CLK), LOW); delayMicroseconds(1); // CLK pulse width low = min. 100ns / CLK fall to output valid = max. 40ns dataIn <<= 1; - if(digitalRead(pin[GPIO_MAX31855DO])) + if(digitalRead(Pin(GPIO_MAX31855DO))) dataIn |= 1; - digitalWrite(pin[GPIO_MAX31855CLK], HIGH); + digitalWrite(Pin(GPIO_MAX31855CLK), HIGH); delayMicroseconds(1); // CLK pulse width high = min. 100ns } - digitalWrite(pin[GPIO_MAX31855CS], HIGH); // CS = HIGH -> End SPI communication - digitalWrite(pin[GPIO_MAX31855CLK], LOW); + digitalWrite(Pin(GPIO_MAX31855CS), HIGH); // CS = HIGH -> End SPI communication + digitalWrite(Pin(GPIO_MAX31855CLK), LOW); return dataIn; } @@ -151,7 +151,7 @@ void MAX31855_Show(bool Json){ bool Xsns39(uint8_t function) { bool result = false; - if((pin[GPIO_MAX31855CS] < 99) && (pin[GPIO_MAX31855CLK] < 99) && (pin[GPIO_MAX31855DO] < 99)){ + if(PinUsed(GPIO_MAX31855CS) && PinUsed(GPIO_MAX31855CLK) && PinUsed(GPIO_MAX31855DO)){ switch (function) { case FUNC_INIT: diff --git a/tasmota/xsns_40_pn532.ino b/tasmota/xsns_40_pn532.ino index b241eb320..8218d0b35 100644 --- a/tasmota/xsns_40_pn532.ino +++ b/tasmota/xsns_40_pn532.ino @@ -66,8 +66,8 @@ uint8_t pn532_newdata_len = 0; void PN532_Init(void) { - if ((pin[GPIO_PN532_RXD] < 99) && (pin[GPIO_PN532_TXD] < 99)) { - PN532_Serial = new TasmotaSerial(pin[GPIO_PN532_RXD], pin[GPIO_PN532_TXD], 1); + if (PinUsed(GPIO_PN532_RXD) && PinUsed(GPIO_PN532_TXD)) { + PN532_Serial = new TasmotaSerial(Pin(GPIO_PN532_RXD), Pin(GPIO_PN532_TXD), 1); if (PN532_Serial->begin(115200)) { if (PN532_Serial->hardwareSerial()) { ClaimSerial(); } PN532_wakeup(); diff --git a/tasmota/xsns_43_hre.ino b/tasmota/xsns_43_hre.ino index fa03d0301..19fffe957 100644 --- a/tasmota/xsns_43_hre.ino +++ b/tasmota/xsns_43_hre.ino @@ -71,10 +71,10 @@ bool hre_good = false; // The settling times here were determined using a single unit hooked to a scope int hreReadBit() { - digitalWrite(pin[GPIO_HRE_CLOCK], HIGH); + digitalWrite(Pin(GPIO_HRE_CLOCK), HIGH); delay(1); - int bit = digitalRead(pin[GPIO_HRE_DATA]); - digitalWrite(pin[GPIO_HRE_CLOCK], LOW); + int bit = digitalRead(Pin(GPIO_HRE_DATA)); + digitalWrite(Pin(GPIO_HRE_CLOCK), LOW); delay(1); return bit; } @@ -110,12 +110,12 @@ void hreInit(void) hre_read_errors = 0; hre_good = false; - pinMode(pin[GPIO_HRE_CLOCK], OUTPUT); - pinMode(pin[GPIO_HRE_DATA], INPUT); + pinMode(Pin(GPIO_HRE_CLOCK), OUTPUT); + pinMode(Pin(GPIO_HRE_DATA), INPUT); // Note that the level shifter inverts this line and we want to leave it // high when not being read. - digitalWrite(pin[GPIO_HRE_CLOCK], LOW); + digitalWrite(Pin(GPIO_HRE_CLOCK), LOW); hre_state = hre_sync; } @@ -260,8 +260,7 @@ void hreShow(boolean json) bool Xsns43(byte function) { // If we don't have pins assigned give up quickly. - if (pin[GPIO_HRE_CLOCK] >= 99 || pin[GPIO_HRE_DATA] >= 99) - return false; + if (!PinUsed(GPIO_HRE_CLOCK) || !PinUsed(GPIO_HRE_DATA)) { return false; } switch (function) { diff --git a/tasmota/xsns_47_max31865.ino b/tasmota/xsns_47_max31865.ino index 3b8a11706..e29054392 100644 --- a/tasmota/xsns_47_max31865.ino +++ b/tasmota/xsns_47_max31865.ino @@ -51,10 +51,10 @@ void MAX31865_Init(void){ return; max31865.setPins( - pin[GPIO_SSPI_CS], - pin[GPIO_SSPI_MOSI], - pin[GPIO_SSPI_MISO], - pin[GPIO_SSPI_SCLK] + Pin(GPIO_SSPI_CS), + Pin(GPIO_SSPI_MOSI), + Pin(GPIO_SSPI_MISO), + Pin(GPIO_SSPI_SCLK) ); if(max31865.begin(PTD_WIRES)) @@ -110,8 +110,8 @@ void MAX31865_Show(bool Json){ bool Xsns47(uint8_t function) { bool result = false; - if((pin[GPIO_SSPI_MISO] < 99) && (pin[GPIO_SSPI_MOSI] < 99) && - (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_CS] < 99)) { + if (PinUsed(GPIO_SSPI_MISO) && PinUsed(GPIO_SSPI_MOSI) && + PinUsed(GPIO_SSPI_SCLK) && PinUsed(GPIO_SSPI_CS)) { switch (function) { case FUNC_INIT: diff --git a/tasmota/xsns_51_rdm6300.ino b/tasmota/xsns_51_rdm6300.ino index 7fd8bc5b5..de2714cb2 100644 --- a/tasmota/xsns_51_rdm6300.ino +++ b/tasmota/xsns_51_rdm6300.ino @@ -36,8 +36,8 @@ uint8_t rdm_blcnt; TasmotaSerial *RDM6300_Serial = nullptr; void RDM6300_Init() { - if (pin[GPIO_RDM6300_RX] < 99) { - RDM6300_Serial = new TasmotaSerial(pin[GPIO_RDM6300_RX],-1,1); + if (PinUsed(GPIO_RDM6300_RX)) { + RDM6300_Serial = new TasmotaSerial(Pin(GPIO_RDM6300_RX),-1,1); if (RDM6300_Serial->begin(RDM6300_BAUDRATE)) { if (RDM6300_Serial->hardwareSerial()) { ClaimSerial(); diff --git a/tasmota/xsns_52_ibeacon.ino b/tasmota/xsns_52_ibeacon.ino index 97130df2a..88b040e76 100644 --- a/tasmota/xsns_52_ibeacon.ino +++ b/tasmota/xsns_52_ibeacon.ino @@ -95,8 +95,8 @@ void IBEACON_Init() { hm17_found=0; // actually doesnt work reliably with software serial - if ((pin[GPIO_IBEACON_RX] < 99) && (pin[GPIO_IBEACON_TX] < 99)) { - IBEACON_Serial = new TasmotaSerial(pin[GPIO_IBEACON_RX], pin[GPIO_IBEACON_TX],1); + if (PinUsed(GPIO_IBEACON_RX) && PinUsed(GPIO_IBEACON_TX)) { + IBEACON_Serial = new TasmotaSerial(Pin(GPIO_IBEACON_RX), Pin(GPIO_IBEACON_TX),1); if (IBEACON_Serial->begin(HM17_BAUDRATE)) { if (IBEACON_Serial->hardwareSerial()) { ClaimSerial(); diff --git a/tasmota/xsns_53_sml.ino b/tasmota/xsns_53_sml.ino index c5f2d3cc2..cb08856b1 100755 --- a/tasmota/xsns_53_sml.ino +++ b/tasmota/xsns_53_sml.ino @@ -1831,11 +1831,17 @@ uint8_t *script_meter; #endif bool Gpio_used(uint8_t gpiopin) { +/* for (uint16_t i=0;i 0)) { + return true; + } return false; } @@ -2206,7 +2212,10 @@ void SML_Check_Send(void) { char *cp; for (uint32_t cnt=sml_desc_cnt; cnt=0 && script_meter_desc[cnt].txmem) { - if ((sml_100ms_cnt%script_meter_desc[cnt].tsecs)==0) { + //AddLog_P2(LOG_LEVEL_INFO, PSTR("100 ms>> %d - %s - %d"),sml_desc_cnt,script_meter_desc[cnt].txmem,script_meter_desc[cnt].tsecs); + if ((sml_100ms_cnt>=script_meter_desc[cnt].tsecs)) { + sml_100ms_cnt=0; + //AddLog_P2(LOG_LEVEL_INFO, PSTR("100 ms>> 2"),cp); if (script_meter_desc[cnt].max_index>1) { script_meter_desc[cnt].index++; if (script_meter_desc[cnt].index>=script_meter_desc[cnt].max_index) { diff --git a/tasmota/xsns_56_hpma.ino b/tasmota/xsns_56_hpma.ino index c66b3c7fd..a0e8aef5a 100644 --- a/tasmota/xsns_56_hpma.ino +++ b/tasmota/xsns_56_hpma.ino @@ -64,8 +64,8 @@ void HpmaSecond(void) // Every second void HpmaInit(void) { hpma_type = 0; - if (pin[GPIO_HPMA_RX] < 99 && pin[GPIO_HPMA_TX] < 99) { - HpmaSerial = new TasmotaSerial(pin[GPIO_HPMA_RX], pin[GPIO_HPMA_TX], 1); + if (PinUsed(GPIO_HPMA_RX) && PinUsed(GPIO_HPMA_TX)) { + HpmaSerial = new TasmotaSerial(Pin(GPIO_HPMA_RX), Pin(GPIO_HPMA_TX), 1); hpma115S0 = new HPMA115S0(*HpmaSerial); if (HpmaSerial->begin(9600)) { diff --git a/tasmota/xsns_60_GPS.ino b/tasmota/xsns_60_GPS.ino index 87e15d6a2..e7094887b 100644 --- a/tasmota/xsns_60_GPS.ino +++ b/tasmota/xsns_60_GPS.ino @@ -119,10 +119,6 @@ rule3 on tele-FLOG#sec do DisplayText [f0c1l4]SAV:%value% endon on tele-FLOG#r #include "NTPServer.h" #include "NTPPacket.h" -#ifdef ESP32 -#include -#endif - /*********************************************************************************************\ * constants \*********************************************************************************************/ @@ -299,11 +295,7 @@ enum UBXMsgType { #ifdef USE_FLOG FLOG *Flog = nullptr; #endif //USE_FLOG -#ifdef ESP8266 TasmotaSerial *UBXSerial; -#else -HardwareSerial *UBXSerial; -#endif NtpServer timeServer(PortUdp); @@ -362,22 +354,14 @@ void UBXTriggerTele(void) void UBXDetect(void) { UBX.mode.init = 0; - if ((pin[GPIO_GPS_RX] < 99) && (pin[GPIO_GPS_TX] < 99)) { -#ifdef ESP8266 - UBXSerial = new TasmotaSerial(pin[GPIO_GPS_RX], pin[GPIO_GPS_TX], 1, 0, UBX_SERIAL_BUFFER_SIZE); // 64 byte buffer is NOT enough + if (PinUsed(GPIO_GPS_RX) && PinUsed(GPIO_GPS_TX)) { + UBXSerial = new TasmotaSerial(Pin(GPIO_GPS_RX), Pin(GPIO_GPS_TX), 1, 0, UBX_SERIAL_BUFFER_SIZE); // 64 byte buffer is NOT enough if (UBXSerial->begin(9600)) { -#else - UBXSerial = new HardwareSerial(2); - UBXSerial->begin(9600,SERIAL_8N1,pin[GPIO_GPS_RX], pin[GPIO_GPS_TX]); - { -#endif DEBUG_SENSOR_LOG(PSTR("UBX: started serial")); -#ifdef ESP8266 if (UBXSerial->hardwareSerial()) { ClaimSerial(); DEBUG_SENSOR_LOG(PSTR("UBX: claim HW")); } -#endif } } else { diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino index d21f9e7e1..5dd82f3a5 100644 --- a/tasmota/xsns_61_MI_NRF24.ino +++ b/tasmota/xsns_61_MI_NRF24.ino @@ -27,10 +27,10 @@ 0.9.4.0 20200304 integrate - sensor types can be ignored (default for LYWSD03), add CGD1 (Alarm clock), correct PDU-types for LYWSD02 --- - 0.9.3.0 20200222 integrate - use now the correct id-word instead of MAC-OUI, + 0.9.3.0 20200222 integrate - use now the correct id-word instead of MAC-OUI, add CGG1 --- - 0.9.2.0 20200212 integrate - "backports" from MI-HM10, change reading pattern, + 0.9.2.0 20200212 integrate - "backports" from MI-HM10, change reading pattern, add missing PDU-types, renaming driver --- 0.9.1.0 20200117 integrate - Added support for the LYWSD02 @@ -176,7 +176,7 @@ struct { uint8_t packetMode; // 0 - normal BLE-advertisements, 1 - 6 "special" sensor packets uint8_t perPage = 4; uint8_t firstUsedPacketMode = 1; - + FIFO_t buffer; struct { @@ -232,16 +232,16 @@ static union{ /********************************************************************************************/ /** - * @brief - * + * @brief + * * @param _mode Packet mode 0-6 * @return true If no error occured - * @return false If NRF24L01 is not connected + * @return false If NRF24L01 is not connected */ bool MINRFinitBLE(uint8_t _mode) { if (MINRF.timer%1000 == 0){ // only re-init every 20 seconds - NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); + NRF24radio.begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_DC)); NRF24radio.setAutoAck(false); NRF24radio.setDataRate(RF24_1MBPS); NRF24radio.disableCRC(); @@ -264,7 +264,7 @@ bool MINRFinitBLE(uint8_t _mode) /** * @brief cycle through the channels 37-39, skip ignored channel - * + * */ void MINRFhopChannel() { @@ -405,19 +405,19 @@ bool MINRFhandleBeacon(scan_entry_t * entry, uint32_t offset); /** * @brief handle a generic BLE-packet in the scan process - * + * */ void MINRFhandleScan(void){ if(MINRFscanResult.size()>20 || MINRF.stopScan) { MINRF.activeScan=false; MINRFcomputefirstUsedPacketMode(); uint32_t i = 0; // pass counter as reference to lambda - MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), + MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), MINRFscanResult.end(), [&i](scan_entry_t e) { if(e.showedUp>2) AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: Beacon %02u: %02X%02X%02X%02X%02X%02X Cid: %04X Svc: %04X UUID: %04X"),i,e.mac[0],e.mac[1],e.mac[2],e.mac[3],e.mac[4],e.mac[5],e.cid,e.svc,e.uuid); i++; - return ((e.showedUp < 3)); + return ((e.showedUp < 3)); }), MINRFscanResult.end()); MINRF.stopScan=false; @@ -447,7 +447,7 @@ void MINRFhandleScan(void){ /** * @brief start beacon mode, can co-exist with Mijia-sniffing - * + * * @param entry number of entry in scan list */ void MINRFstartBeacon(uint16_t entry){ @@ -459,7 +459,7 @@ void MINRFstartBeacon(uint16_t entry){ /** * @brief semi-generic BLE-ADV-parser - * + * * @param entry Entry of scan list * @param offset Depends on the reading mode: 0->regular BLE-ADV, 6->"cutted" BLE-ADV with MAC as PDU * @return true - when name, cid, uuid or svc is found with any value @@ -536,7 +536,7 @@ bool MINRFhandleBeacon(scan_entry_t * entry, uint32_t offset){ /** * @brief increase beacon timer every second and process the result - * + * */ void MINRFbeaconCounter(void){ if(MINRF.beacon.active) { @@ -550,7 +550,7 @@ void MINRFbeaconCounter(void){ /** * @brief compute "PDU" from MAC for each possible channel and store it globally - * + * */ void MINRFcomputeBeaconPDU(void){ for (uint32_t i = 0; i<3; i++){ @@ -570,7 +570,7 @@ void MINRFcomputeBeaconPDU(void){ /** * @brief reverse 6-byte-array, hard-coded size of 6 - * + * * @param _mac pass an uint_t[6] */ void MINRFreverseMAC(uint8_t _mac[]){ @@ -582,8 +582,8 @@ void MINRFreverseMAC(uint8_t _mac[]){ } /** - * @brief - * + * @brief + * * @param _string input string in format: AABBCCDDEEFF (upper case!) * @param _mac target byte array with fixed size of 6 */ @@ -594,7 +594,7 @@ void MINRFMACStringToBytes(char* _string, uint8_t _mac[]) { //uppercase uint8_t value = 0; if(c >= '0' && c <= '9') value = (c - '0'); - else if (c >= 'A' && c <= 'F') + else if (c >= 'A' && c <= 'F') value = (10 + (c - 'A')); _mac[(index/2)] += value << (((index + 1) % 2) * 4); index++; @@ -604,7 +604,7 @@ void MINRFMACStringToBytes(char* _string, uint8_t _mac[]) { //uppercase /** * @brief helper function, to avoid to start with an ignored sensor type - * + * */ void MINRFcomputefirstUsedPacketMode(void){ for (uint32_t i = 0; ilux=MINRF.buffer.miBeacon.lux & 0x00ffffff; DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), MINRF.buffer.miBeacon.lux & 0x00ffffff); - break; + break; case 0x08: _tempFloat =(float)MINRF.buffer.miBeacon.moist; if(_tempFloat<100){ @@ -881,7 +881,7 @@ void MINRFhandleCGD1Packet(void){ // no MiBeacon \*********************************************************************************************/ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds - + if(MINRF.timer>6000){ // happens every 6000/20 = 300 seconds DEBUG_SENSOR_LOG(PSTR("MINRF: check for FAKE sensors")); MINRFpurgeFakeSensors(); @@ -890,7 +890,7 @@ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds MINRF.timer++; if (!MINRFreceivePacket()){ - // DEBUG_SENSOR_LOG(PSTR("MINRF: nothing received")); + // DEBUG_SENSOR_LOG(PSTR("MINRF: nothing received")); } else { @@ -1057,7 +1057,7 @@ void MINRFShow(bool json) } ResponseAppend_P(PSTR(",\"%s-%02x%02x%02x\":{"),kMINRFSlaveType[MIBLEsensors[i].type-1],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); if (MIBLEsensors[i].type==FLORA && !isnan(MIBLEsensors[i].temp)){ - char stemp[FLOATSZ]; + char stemp[FLOATSZ]; dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, stemp); ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), stemp); @@ -1074,7 +1074,7 @@ void MINRFShow(bool json) } ResponseJsonEnd(); } - if (MIBLEsensors[i].type>FLORA){ + if (MIBLEsensors[i].type>FLORA){ if(!isnan(MIBLEsensors[i].temp) && !isnan(MIBLEsensors[i].hum)){ ResponseAppendTHD(MIBLEsensors[i].temp,MIBLEsensors[i].hum); } @@ -1112,7 +1112,7 @@ void MINRFShow(bool json) continue; } WSContentSend_PD(HTTP_MINRF_HL); - WSContentSend_PD(HTTP_MINRF_MAC, kMINRFSlaveType[MIBLEsensors[i].type-1], D_MAC_ADDRESS, MIBLEsensors[i].serial[0], MIBLEsensors[i].serial[1],MIBLEsensors[i].serial[2],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); + WSContentSend_PD(HTTP_MINRF_MAC, kMINRFSlaveType[MIBLEsensors[i].type-1], D_MAC_ADDRESS, MIBLEsensors[i].serial[0], MIBLEsensors[i].serial[1],MIBLEsensors[i].serial[2],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); if (MIBLEsensors[i].type==FLORA){ if(!isnan(MIBLEsensors[i].temp)){ char temperature[FLOATSZ]; @@ -1139,7 +1139,7 @@ void MINRFShow(bool json) if(MINRF.beacon.active){ WSContentSend_PD(HTTP_MINRF_HL); WSContentSend_PD(HTTP_MINRF_HL); - WSContentSend_PD(HTTP_MINRF_MAC, F("Beacon"), D_MAC_ADDRESS, MINRF.beacon.mac[0], MINRF.beacon.mac[1],MINRF.beacon.mac[2],MINRF.beacon.mac[3],MINRF.beacon.mac[4],MINRF.beacon.mac[5]); + WSContentSend_PD(HTTP_MINRF_MAC, F("Beacon"), D_MAC_ADDRESS, MINRF.beacon.mac[0], MINRF.beacon.mac[1],MINRF.beacon.mac[2],MINRF.beacon.mac[3],MINRF.beacon.mac[4],MINRF.beacon.mac[5]); WSContentSend_PD(PSTR("{s}Beacon Time{m}%u seconds{e}"),MINRF.beacon.time); } if(counter>3) { diff --git a/tasmota/xsns_62_MI_ESP32.ino b/tasmota/xsns_62_MI_ESP32.ino new file mode 100644 index 000000000..c9a5ba73a --- /dev/null +++ b/tasmota/xsns_62_MI_ESP32.ino @@ -0,0 +1,1199 @@ +/* + xsns_62_MI_ESP32.ino - MI-BLE-sensors via ESP32 support for Tasmota + + Copyright (C) 2020 Christian Baars 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 . + + + -------------------------------------------------------------------------------------------- + Version yyyymmdd Action Description + -------------------------------------------------------------------------------------------- + 0.9.0.0 20200413 started - initial development by Christian Baars + forked - from arendst/tasmota - https://github.com/arendst/Tasmota + +*/ +#ifdef ESP32 // ESP32 only. Use define USE_HM10 for ESP8266 support + +#ifdef USE_MI_ESP32 + +#define XSNS_62 62 + +#include +#include + +void MI32scanEndedCB(NimBLEScanResults results); +void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); + + +struct { + uint16_t perPage = 4; + uint32_t period; // set manually in addition to TELE-period, is set to TELE-period after start + struct { + uint32_t init:1; + uint32_t connected:1; + uint32_t autoScan:1; + uint32_t canScan:1; + uint32_t runningScan:1; + uint32_t canConnect:1; + uint32_t willConnect:1; + uint32_t readingDone:1; + uint32_t shallSetTime:1; + uint32_t willSetTime:1; + uint32_t shallReadBatt:1; + uint32_t willReadBatt:1; + } mode; + struct { + uint8_t sensor; // points to to the number 0...255 + } state; +} MI32; + +#pragma pack(1) // byte-aligned structures to read the sensor data + + struct { + uint16_t temp; + uint8_t hum; + uint16_t volt; // LYWSD03 only + } LYWSD0x_HT; + struct { + uint8_t spare; + uint16_t temp; + uint16_t hum; + } CGD1_HT; + struct { + uint16_t temp; + uint8_t spare; + uint32_t lux; + uint8_t moist; + uint16_t fert; + } Flora_TLMF; // temperature, lux, moisture, fertility + + +struct mi_beacon_t{ + uint16_t frame; + uint16_t productID; + uint8_t counter; + uint8_t Mac[6]; + uint8_t spare; + uint8_t type; + uint8_t ten; + uint8_t size; + union { + struct{ //0d + uint16_t temp; + uint16_t hum; + }HT; + uint8_t bat; //0a + uint16_t temp; //04 + uint16_t hum; //06 + uint32_t lux; //07 + uint8_t moist; //08 + uint16_t fert; //09 + }; +}; + +struct cg_packet_t { + uint16_t frameID; + uint8_t serial[6]; + uint16_t mode; + union { + struct { + int16_t temp; // -9 - 59 °C + uint16_t hum; + }; + uint8_t bat; + }; +}; + +#pragma pack(0) + +struct mi_sensor_t{ + uint8_t type; //Flora = 1; MI-HT_V1=2; LYWSD02=3; LYWSD03=4; CGG1=5; CGD1=6 + uint8_t serial[6]; + uint8_t showedUp; + float temp; //Flora, MJ_HT_V1, LYWSD0x, CGx + union { + struct { + float moisture; + float fertility; + uint32_t lux; + }; // Flora + struct { + float hum; + }; // MJ_HT_V1, LYWSD0x + }; + union + { + uint8_t bat; // many values seem to be hard-coded garbage (LYWSD0x, GCD1) + uint16_t volt; // LYWSD03MMC + }; +}; + +std::vector MIBLEsensors; +BLEScan* MI32Scan; +BLEScanResults MI32foundDevices; + +/*********************************************************************************************\ + * constants +\*********************************************************************************************/ + +#define D_CMND_MI32 "MI32" + +const char S_JSON_MI32_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MI32 "%s\":%d}"; +const char S_JSON_MI32_COMMAND[] PROGMEM = "{\"" D_CMND_MI32 "%s%s\"}"; +const char kMI32_Commands[] PROGMEM = "Period|Time|Page|Battery"; + +#define FLORA 1 +#define MJ_HT_V1 2 +#define LYWSD02 3 +#define LYWSD03MMC 4 +#define CGG1 5 +#define CGD1 6 + +const uint16_t kMI32SlaveID[6]={ 0x0098, // Flora + 0x01aa, // MJ_HT_V1 + 0x045b, // LYWSD02 + 0x055b, // LYWSD03 + 0x0347, // CGG1 + 0x0576 // CGD1 + }; + +const char kMI32SlaveType1[] PROGMEM = "Flora"; +const char kMI32SlaveType2[] PROGMEM = "MJ_HT_V1"; +const char kMI32SlaveType3[] PROGMEM = "LYWSD02"; +const char kMI32SlaveType4[] PROGMEM = "LYWSD03"; +const char kMI32SlaveType5[] PROGMEM = "CGG1"; +const char kMI32SlaveType6[] PROGMEM = "CGD1"; +const char * kMI32SlaveType[] PROGMEM = {kMI32SlaveType1,kMI32SlaveType2,kMI32SlaveType3,kMI32SlaveType4,kMI32SlaveType5,kMI32SlaveType6}; + +/*********************************************************************************************\ + * enumerations +\*********************************************************************************************/ + +enum MI32_Commands { // commands useable in console or rules + CMND_MI32_PERIOD, // set period like TELE-period in seconds between read-cycles + CMND_MI32_TIME, // set LYWSD02-Time from ESP8266-time + CMND_MI32_PAGE, // sensor entries per web page, which will be shown alternated + CMND_MI32_BATTERY // read all battery levels + }; + +enum MI32_TASK { + MI32_TASK_SCAN = 0, + MI32_TASK_CONN = 1, + MI32_TASK_TIME = 2, + MI32_TASK_BATT = 3, +}; + +/*********************************************************************************************\ + * Classes +\*********************************************************************************************/ + +class MI32SensorCallback : public NimBLEClientCallbacks { + void onConnect(NimBLEClient* pclient) { + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("connected %s"), kMI32SlaveType[(MIBLEsensors[MI32.state.sensor].type)-1]); + MI32.mode.willConnect = 0; + MI32.mode.connected = 1; + } + void onDisconnect(NimBLEClient* pclient) { + MI32.mode.connected = 0; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("disconnected %s"), kMI32SlaveType[(MIBLEsensors[MI32.state.sensor].type)-1]); + } + bool onConnParamsUpdateRequest(NimBLEClient* MI32Client, 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; + } +}; + +class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks { + void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Advertised Device: %s Buffer: %u"),advertisedDevice.getAddress().toString().c_str(),advertisedDevice.getServiceData().length()); + if (advertisedDevice->getServiceData().length() == 0) return; + uint16_t uuid = advertisedDevice->getServiceDataUUID().getNative()->u16.value; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%x"),uuid); + uint8_t addr[6]; + memcpy(addr,advertisedDevice->getAddress().getNative(),6); + MI32_ReverseMAC(addr); + if(uuid==0xfe95) { + MI32ParseResponse((char*)advertisedDevice->getServiceData().c_str(),advertisedDevice->getServiceData().length(), addr); + } + else if(uuid==0xfdcd) { + MI32parseCGD1Packet((char*)advertisedDevice->getServiceData().c_str(),advertisedDevice->getServiceData().length(), addr); + } + }; +}; + + +static MI32AdvCallbacks MI32ScanCallbacks; +static MI32SensorCallback MI32SensorCB; +static NimBLEClient* MI32Client; + +/*********************************************************************************************\ + * BLE callback functions +\*********************************************************************************************/ + +void MI32scanEndedCB(NimBLEScanResults results){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Scan ended")); + MI32.mode.runningScan = 0; +} + +void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Notified length: %u"),length); + switch(MIBLEsensors[MI32.state.sensor].type){ + case LYWSD03MMC: case LYWSD02: + MI32readHT_LY((char*)pData); + MI32.mode.readingDone = 1; + break; + default: + MI32.mode.readingDone = 1; + break; + } +} +/*********************************************************************************************\ + * Helper functions +\*********************************************************************************************/ + +void MI32_ReverseMAC(uint8_t _mac[]){ + uint8_t _reversedMAC[6]; + for (uint8_t i=0; i<6; i++){ + _reversedMAC[5-i] = _mac[i]; + } + memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); +} + +/*********************************************************************************************\ + * common functions +\*********************************************************************************************/ + + +/** + * @brief Return the slot number of a known sensor or return create new sensor slot + * + * @param _serial BLE address of the sensor + * @param _type Type number of the sensor + * @return uint32_t Known or new slot in the sensors-vector + */ +uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ + + DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_MI32, _type); + bool _success = false; + for (uint32_t i=0;i<6;i++){ // i < sizeof(kMI32SlaveID) gives compiler warning + if(_type == kMI32SlaveID[i]){ + DEBUG_SENSOR_LOG(PSTR("MI32: ID is type %u"), i); + _type = i+1; + _success = true; + } + else { + DEBUG_SENSOR_LOG(PSTR("%s: ID-type is not: %x"),D_CMND_MI32,kMI32SlaveID[i]); + } + } + if(!_success) return 0xff; + + DEBUG_SENSOR_LOG(PSTR("%s: vector size %u"),D_CMND_MI32, MIBLEsensors.size()); + for(uint32_t i=0; iconnect(NimBLEAddress(address), 0,false)) { + MI32.mode.willConnect = 0; + vTaskDelete( NULL ); + } + } + else { + MI32Client = NimBLEDevice::getDisconnectedClient(); + } + } + if(!MI32Client) { + if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) { + MI32.mode.willConnect = 0; + vTaskDelete( NULL ); + } + MI32Client = NimBLEDevice::createClient(); + MI32Client->setClientCallbacks(&MI32SensorCB , false); + MI32Client->setConnectionParams(12,12,0,51); + MI32Client->setConnectTimeout(10); + if (!MI32Client->connect(NimBLEAddress(address),0,false)) { + MI32.mode.willConnect = 0; + NimBLEDevice::deleteClient(MI32Client); + vTaskDelete( NULL ); + } + } +} + + +void MI32StartScanTask(){ + if (MI32.mode.connected) return; + MI32.mode.runningScan = 1; + xTaskCreatePinnedToCore( + MI32ScanTask, /* Function to implement the task */ + "MI32ScanTask", /* Name of the task */ + 4096, /* Stack size in words */ + NULL, /* Task input parameter */ + 0, /* Priority of the task */ + NULL, /* Task handle. */ + 0); /* Core where the task should run */ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Start scanning"),D_CMND_MI32); +} + +void MI32ScanTask(void *pvParameters){ + NimBLEScan* pScan = NimBLEDevice::getScan(); + pScan->setAdvertisedDeviceCallbacks(&MI32ScanCallbacks); + pScan->setActiveScan(false); + pScan->start(5, MI32scanEndedCB); // hard coded duration + uint32_t timer = 0; + while (MI32.mode.runningScan){ + if (timer>15){ + vTaskDelete( NULL ); + } + timer++; + vTaskDelay(1000); + } + vTaskDelete( NULL ); +} + +void MI32StartSensorTask(){ + MI32.mode.willConnect = 1; + xTaskCreatePinnedToCore( + MI32SensorTask, /* Function to implement the task */ + "MI32SensorTask", /* Name of the task */ + 8192, /* Stack size in words */ + NULL, /* Task input parameter */ + 15, /* Priority of the task */ + NULL, /* Task handle. */ + 0); /* Core where the task should run */ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Start sensor connections"),D_CMND_MI32); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor); +} + +void MI32SensorTask(void *pvParameters){ + if (MIBLEsensors[MI32.state.sensor].type != LYWSD03MMC) { + MI32.mode.willConnect = 0; + vTaskDelete( NULL ); + } + MI32ConnectActiveSensor(); + MI32.mode.readingDone = 1; + switch(MIBLEsensors[MI32.state.sensor].type){ + case LYWSD03MMC: + MI32.mode.readingDone = 0; + MI32connectLYWSD03(); + break; + default: + break; + } + uint32_t timer = 0; + while (!MI32.mode.readingDone){ + if (timer>150){ + break; + } + timer++; + vTaskDelay(100); + } + MI32Client->disconnect(); + NimBLEDevice::deleteClient(MI32Client); + vTaskDelay(500); + MI32.mode.connected = 0; + vTaskDelete( NULL ); +} + +void MI32connectLYWSD03(){ + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + static BLEUUID serviceUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6"); + static BLEUUID charUUID("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6"); + pSvc = MI32Client->getService(serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(charUUID); + } + if(pChr->canNotify()) { + if(!pChr->registerForNotify(MI32notifyCB)) { + MI32.mode.willConnect = 0; + MI32Client->disconnect(); + return; + } + } +} + +void MI32StartTimeTask(){ + MI32.mode.willConnect = 1; + xTaskCreatePinnedToCore( + MI32TimeTask, /* Function to implement the task */ + "MI32TimeTask", /* Name of the task */ + 8912, /* Stack size in words */ + NULL, /* Task input parameter */ + 15, /* Priority of the task */ + NULL, /* Task handle. */ + 0); /* Core where the task should run */ + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Start time set"),D_CMND_MI32); + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor); +} + +void MI32TimeTask(void *pvParameters){ + if (MIBLEsensors[MI32.state.sensor].type != LYWSD02) { + MI32.mode.shallSetTime = 0; + vTaskDelete( NULL ); + } + MI32ConnectActiveSensor(); + + uint32_t timer = 0; + while (MI32.mode.connected == 0){ + if (timer>1000){ + break; + } + timer++; + vTaskDelay(10); + } + + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + static BLEUUID serviceUUID("EBE0CCB0-7A0A-4B0C-8A1A-6FF2997DA3A6"); + static BLEUUID charUUID("EBE0CCB7-7A0A-4B0C-8A1A-6FF2997DA3A6"); + pSvc = MI32Client->getService(serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(charUUID); + } + if(pChr->canWrite()) { + union { + uint8_t buf[5]; + uint32_t time; + } _utc; + _utc.time = Rtc.utc_time; + _utc.buf[4] = Rtc.time_timezone / 60; + + if(!pChr->writeValue(_utc.buf,sizeof(_utc.buf),true)) { // true is important ! + MI32.mode.willConnect = 0; + MI32Client->disconnect(); + } + else { + MI32.mode.shallSetTime = 0; + MI32.mode.willSetTime = 0; + } + } + MI32Client->disconnect(); + NimBLEDevice::deleteClient(MI32Client); + vTaskDelay(500); + MI32.mode.connected = 0; + vTaskDelete( NULL ); +} + +void MI32StartBatteryTask(){ + if (MI32.mode.connected) return; + MI32.mode.willReadBatt = 1; + xTaskCreatePinnedToCore( + MI32BatteryTask, /* Function to implement the task */ + "MI32BatteryTask", /* Name of the task */ + 8192, /* Stack size in words */ + NULL, /* Task input parameter */ + 15, /* Priority of the task */ + NULL, /* Task handle. */ + 0); /* Core where the task should run */ +} + +void MI32BatteryTask(void *pvParameters){ + // all reported battery values are probably crap, but we allow the reading on demand + switch (MIBLEsensors[MI32.state.sensor].type){ + case LYWSD03MMC: case MJ_HT_V1: case CGG1: + MI32.mode.willConnect = 0; + MI32.mode.willReadBatt = 0; + vTaskDelete( NULL ); + break; + default: + break; + } + + MI32.mode.connected = 0; + MI32ConnectActiveSensor(); + uint32_t timer = 0; + while (MI32.mode.connected == 0){ + if (timer>1000){ + break; + } + timer++; + vTaskDelay(10); + } + + switch(MIBLEsensors[MI32.state.sensor].type){ + case FLORA: + MI32batteryFLORA(); + break; + case LYWSD02: + MI32batteryLYWSD02(); + break; + case CGD1: + MI32batteryCGD1(); + break; + } + MI32Client->disconnect(); + MI32.mode.willReadBatt = 0; + NimBLEDevice::deleteClient(MI32Client); + vTaskDelay(500); + MI32.mode.connected = 0; + vTaskDelete( NULL ); +} + +void MI32batteryFLORA(){ + uint32_t timer = 0; + while (!MI32.mode.connected){ + if (timer>1000){ + break; + } + timer++; + vTaskDelay(10); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s connected for battery"),kMI32SlaveType[MIBLEsensors[MI32.state.sensor].type-1] ); + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + static BLEUUID FLserviceUUID("00001204-0000-1000-8000-00805f9b34fb"); + static BLEUUID FLcharUUID("00001a02-0000-1000-8000-00805f9b34fb"); + + pSvc = MI32Client->getService(FLserviceUUID); + if(pSvc) { /** make sure it's not null */ + pChr = pSvc->getCharacteristic(FLcharUUID); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: got Flora char %s"),D_CMND_MI32, pChr->getUUID().toString().c_str()); + } + else { + MI32.mode.readingDone = 1; + return; + } + if(pChr->canRead()) { + const char *buf = pChr->readValue().c_str(); + MI32readBat((char*)buf); + } +} + +void MI32batteryLYWSD02(){ + uint32_t timer = 0; + while (!MI32.mode.connected){ + if (timer>1000){ + break; + } + timer++; + vTaskDelay(10); + } + + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + static BLEUUID LY2serviceUUID("EBE0CCB0-7A0A-4B0C-8A1A-6FF2997DA3A6"); + static BLEUUID LY2charUUID("EBE0CCC4-7A0A-4B0C-8A1A-6FF2997DA3A6"); + + pSvc = MI32Client->getService(LY2serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(LY2charUUID); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: got LYWSD02 char %s"),D_CMND_MI32, pChr->getUUID().toString().c_str()); + } + else { + return; + } + if(pChr->canRead()) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LYWSD02 char")); + const char *buf = pChr->readValue().c_str(); + MI32readBat((char*)buf); + } +} + +void MI32batteryCGD1(){ + uint32_t timer = 0; + while (!MI32.mode.connected){ + if (timer>1000){ + break; + } + timer++; + vTaskDelay(10); + } + + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + static BLEUUID CGD1serviceUUID("180F"); + static BLEUUID CGD1charUUID("2A19"); + + pSvc = MI32Client->getService(CGD1serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(CGD1charUUID); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: got CGD1 char %s"),D_CMND_MI32, pChr->getUUID().toString().c_str()); + } + else { + return; + } + if(pChr->canRead()) { + const char *buf = pChr->readValue().c_str(); + MI32readBat((char*)buf); + } +} + + +/*********************************************************************************************\ + * parse the response from advertisements +\*********************************************************************************************/ + +void MI32parseMiBeacon(char * _buf, uint32_t _slot){ + float _tempFloat; + mi_beacon_t _beacon; + if (MIBLEsensors[_slot].type==MJ_HT_V1 || MIBLEsensors[_slot].type==CGG1){ + memcpy((uint8_t*)&_beacon+1,(uint8_t*)_buf, sizeof(_beacon)); // shift by one byte for the MJ_HT_V1 + memcpy((uint8_t*)&_beacon.Mac,(uint8_t*)&_beacon.Mac+1,6); // but shift back the MAC + } + else{ + memcpy((void*)&_beacon,(void*)_buf, sizeof(_beacon)); + } + MI32_ReverseMAC(_beacon.Mac); + + DEBUG_SENSOR_LOG(PSTR("MiBeacon type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]); + DEBUG_SENSOR_LOG(PSTR(" type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]); + + if(MIBLEsensors[_slot].type==4 || MIBLEsensors[_slot].type==6){ + DEBUG_SENSOR_LOG(PSTR("LYWSD03 and CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type); + return; + } + DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kMI32SlaveType[MIBLEsensors[_slot].type-1],_slot); + switch(_beacon.type){ + case 0x04: + _tempFloat=(float)(_beacon.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 4: U16: %u Temp"), _beacon.temp ); + break; + case 0x06: + _tempFloat=(float)(_beacon.hum)/10.0f; + if(_tempFloat<101){ + MIBLEsensors[_slot].hum=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 6: U16: %u Hum"), _beacon.hum); + break; + case 0x07: + MIBLEsensors[_slot].lux=_beacon.lux & 0x00ffffff; + DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); + break; + case 0x08: + _tempFloat =(float)_beacon.moist; + if(_tempFloat<100){ + MIBLEsensors[_slot].moisture=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 8: U8: %u Moisture"), _beacon.moist); + break; + case 0x09: + _tempFloat=(float)(_beacon.fert); + if(_tempFloat<65535){ // ??? + MIBLEsensors[_slot].fertility=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %u Fertility"), _beacon.fert); + break; + case 0x0a: + if(_beacon.bat<101){ + MIBLEsensors[_slot].bat = _beacon.bat; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode a: U8: %u %%"), _beacon.bat); + break; + case 0x0d: + _tempFloat=(float)(_beacon.HT.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated")); + } + _tempFloat=(float)(_beacon.HT.hum)/10.0f; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); + break; + } +} + +void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6]){ // no MiBeacon + uint8_t _addr[6]; + memcpy(_addr,addr,6); + uint32_t _slot = MIBLEgetSensorSlot(_addr, 0x0576); // This must be hard-coded, no object-id in Cleargrass-packet + DEBUG_SENSOR_LOG(PSTR("MI32: Sensor slot: %u"), _slot); + if(_slot==0xff) return; + cg_packet_t _packet; + memcpy((char*)&_packet,_buf,sizeof(_packet)); + switch (_packet.mode){ + case 0x0401: + float _tempFloat; + _tempFloat=(float)(_packet.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors.at(_slot).temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("CGD1: temp updated")); + } + _tempFloat=(float)(_packet.hum)/10.0f; + if(_tempFloat<100){ + MIBLEsensors.at(_slot).hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("CGD1: U16: %x Temp U16: %x Hum"), _packet.temp, _packet.hum); + break; + case 0x0102: + if(_packet.bat<101){ + MIBLEsensors.at(_slot).bat = _packet.bat; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + break; + default: + DEBUG_SENSOR_LOG(PSTR("MI32: unexpected CGD1-packet")); + } +} + +void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6]) { + if(bufsize<10) { + return; + } + char * _pos = buf; + uint16_t _type= _pos[3]*256 + _pos[2]; + // AddLog_P2(LOG_LEVEL_INFO, PSTR("%02x %02x %02x %02x"),(uint8_t)buf[0], (uint8_t)buf[1],(uint8_t)buf[2],(uint8_t)buf[3]); + uint8_t _addr[6]; + memcpy(_addr,addr,6); + uint16_t _slot = MIBLEgetSensorSlot(_addr, _type); + if(_slot!=0xff) MI32parseMiBeacon(_pos,_slot); +} + +/***********************************************************************\ + * Read data from connections +\***********************************************************************/ + +void MI32readHT_LY(char *_buf){ + DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0] != 0 && _buf[1] != 0){ + memcpy(&LYWSD0x_HT,(void *)_buf,sizeof(LYWSD0x_HT)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u, V: %u"),D_CMND_MI32,LYWSD0x_HT.temp,LYWSD0x_HT.hum, LYWSD0x_HT.volt); + uint32_t _slot = MI32.state.sensor; + + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + static float _tempFloat; + _tempFloat=(float)(LYWSD0x_HT.temp)/100.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + MIBLEsensors[_slot].showedUp=255; // this sensor is real + } + _tempFloat=(float)LYWSD0x_HT.hum; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("LYWSD0x: hum updated")); + } + if (MIBLEsensors[_slot].type == LYWSD03MMC){ + MIBLEsensors[_slot].volt = LYWSD0x_HT.volt; + } + } +} + +bool MI32readBat(char *_buf){ + DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0] != 0){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Battery: %u"),D_CMND_MI32,_buf[0]); + uint32_t _slot = MI32.state.sensor; + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + if(_buf[0]<101){ + MIBLEsensors[_slot].bat=_buf[0]; + return true; + } + } + return false; +} + +/** + * @brief Main loop of the driver, "high level"-loop + * + */ + +void MI32EverySecond(bool restart){ + static uint32_t _counter = MI32.period - 15; + static uint32_t _nextSensorSlot = 0; + + if(restart){ + _counter = 0; + MI32.mode.canScan = 0; + MI32.mode.canConnect = 1; + return; + } + + if (MI32.mode.shallSetTime) { + MI32.mode.canScan = 0; + MI32.mode.canConnect = 0; + if (MI32.mode.willSetTime == 0){ + MI32.mode.willSetTime = 1; + MI32StartTask(MI32_TASK_TIME); + } + } + + if (MI32.mode.willReadBatt) return; + + if (_counter>MI32.period) { + _counter = 0; + MI32.mode.canScan = 0; + MI32.mode.canConnect = 1; + } + + if(MI32.mode.connected == 1 || MI32.mode.willConnect == 1) return; + + if(MIBLEsensors.size()==0) { + if (MI32.mode.runningScan == 0 && MI32.mode.canScan == 1) MI32StartTask(MI32_TASK_SCAN); + return; + } + + if(_counter==0) { + MI32.state.sensor = _nextSensorSlot; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: active sensor now: %u"),D_CMND_MI32, MI32.state.sensor); + MI32.mode.canScan = 0; + if (MI32.mode.runningScan == 1 || MI32.mode.connected == 1) return; + _nextSensorSlot++; + MI32.mode.canConnect = 1; + if(MI32.mode.connected == 0) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("will connect to %s"),kMI32SlaveType[MIBLEsensors[MI32.state.sensor].type-1] ); + + if (MI32.mode.shallReadBatt) { + MI32StartTask(MI32_TASK_BATT); + } + else{ + MI32StartTask(MI32_TASK_CONN); + } + + } + if (MI32.state.sensor==MIBLEsensors.size()-1) { + _nextSensorSlot= 0; + _counter++; + if (MI32.mode.shallReadBatt){ + MI32.mode.shallReadBatt = 0; + } + MI32.mode.canConnect = 0; + MI32.mode.canScan = 1; + } + } + else _counter++; + if (MI32.state.sensor>MIBLEsensors.size()-1) { + _nextSensorSlot = 0; + MI32.mode.canScan = 1; + } + MI32StartTask(MI32_TASK_SCAN); +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +bool MI32Cmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_MI32); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_MI32), disp_len)) { // prefix + uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kMI32_Commands); + switch (command_code) { + case CMND_MI32_PERIOD: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload==1) { + MI32EverySecond(true); + XdrvMailbox.payload = MI32.period; + } + else { + MI32.period = XdrvMailbox.payload; + } + } + else { + XdrvMailbox.payload = MI32.period; + } + Response_P(S_JSON_MI32_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_MI32_TIME: + if (XdrvMailbox.data_len > 0) { + if(MIBLEsensors.size()>XdrvMailbox.payload){ + if(MIBLEsensors[XdrvMailbox.payload].type == LYWSD02){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: will set Time"),D_CMND_MI32); + MI32.state.sensor = XdrvMailbox.payload; + MI32.mode.canScan = 0; + MI32.mode.canConnect = 0; + MI32.mode.shallSetTime = 1; + MI32.mode.willSetTime = 0; + } + } + } + Response_P(S_JSON_MI32_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_MI32_PAGE: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload == 0) XdrvMailbox.payload = MI32.perPage; // ignore 0 + MI32.perPage = XdrvMailbox.payload; + } + else XdrvMailbox.payload = MI32.perPage; + Response_P(S_JSON_MI32_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_MI32_BATTERY: + MI32EverySecond(true); + MI32.mode.shallReadBatt = 1; + MI32.mode.canConnect = 1; + XdrvMailbox.payload = MI32.period; + Response_P(S_JSON_MI32_COMMAND, command, ""); + break; + default: + // else for Unknown command + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +const char HTTP_MI32[] PROGMEM = "{s}MI ESP32 {m}%u%s / %u{e}"; +const char HTTP_MI32_SERIAL[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; +const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; +const char HTTP_VOLTAGE[] PROGMEM = "{s}%s " D_VOLTAGE "{m}%s V{e}"; +const char HTTP_MI32_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%uus/cm{e}"; +const char HTTP_MI32_HL[] PROGMEM = "{s}
{m}
{e}"; + +void MI32Show(bool json) +{ + if (json) { + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { +/* + char slave[33]; + snprintf_P(slave, sizeof(slave), PSTR("%s-%02x%02x%02x"), + kMI32SlaveType[MIBLEsensors[i].type-1],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); + ResponseAppend_P(PSTR(",\"%s\":{"), slave); +*/ + ResponseAppend_P(PSTR(",\"%s-%02x%02x%02x\":{"), + kMI32SlaveType[MIBLEsensors[i].type-1], + MIBLEsensors[i].serial[3], MIBLEsensors[i].serial[4], MIBLEsensors[i].serial[5]); + + if (MIBLEsensors[i].type == FLORA) { + if (!isnan(MIBLEsensors[i].temp)) { // this is the error code -> no temperature + char temperature[FLOATSZ]; // all sensors have temperature + dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); + ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); + } else { + ResponseAppend_P(PSTR("}")); + continue; + } + if (MIBLEsensors[i].lux!=0x0ffffff) { // this is the error code -> no lux + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); + } + if (!isnan(MIBLEsensors[i].moisture)) { + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%d"), MIBLEsensors[i].moisture); + } + if (!isnan(MIBLEsensors[i].fertility)) { + ResponseAppend_P(PSTR(",\"Fertility\":%d"), MIBLEsensors[i].fertility); + } + } + if (MIBLEsensors[i].type > FLORA){ + if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)) { + ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum); + } + } + if (MIBLEsensors[i].bat != 0x00) { // this is the error code -> no battery + if (MIBLEsensors[i].type != LYWSD03MMC) { + ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); + } else { + char voltage[FLOATSZ]; + dtostrfd((MIBLEsensors[i].volt)/1000.0f, Settings.flag2.voltage_resolution, voltage); + ResponseAppend_P(PSTR(",\"" D_VOLTAGE "\":%s"), voltage); + } + } + ResponseAppend_P(PSTR("}")); + } +#ifdef USE_WEBSERVER + } else { + static uint16_t _page = 0; + static uint16_t _counter = 0; + int32_t i = _page * MI32.perPage; + uint32_t j = i + MI32.perPage; + if (j+1>MIBLEsensors.size()){ + j = MIBLEsensors.size(); + } + char stemp[5] ={0}; + if (MIBLEsensors.size()-(_page*MI32.perPage)>1 && MI32.perPage!=1) { + sprintf_P(stemp,"-%u",j); + } + if (MIBLEsensors.size()==0) i=-1; // only for the GUI + + WSContentSend_PD(HTTP_MI32, i+1,stemp,MIBLEsensors.size()); + for (i; i no valid value + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMI32SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].lux); + } + if (!isnan(MIBLEsensors[i].moisture)) { + WSContentSend_PD(HTTP_SNS_MOISTURE, kMI32SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].moisture); + } + if (!isnan(MIBLEsensors[i].fertility)) { + WSContentSend_PD(HTTP_MI32_FLORA_DATA, kMI32SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].fertility); + } + } + if (MIBLEsensors[i].type>FLORA) { // everything "above" Flora + if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)) { + WSContentSend_THD(kMI32SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); + } + } + if(MIBLEsensors[i].bat!=0x00){ + if (MIBLEsensors[i].type != LYWSD03MMC) { + WSContentSend_PD(HTTP_BATTERY, kMI32SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); + } else { + char voltage[FLOATSZ]; + dtostrfd((MIBLEsensors[i].volt)/1000.0f, Settings.flag2.voltage_resolution, voltage); + WSContentSend_PD(HTTP_VOLTAGE, kMI32SlaveType[MIBLEsensors[i].type-1], voltage); + } + } + } + _counter++; + if(_counter>3) { + _page++; + _counter=0; + } + if (MIBLEsensors.size()%MI32.perPage==0 && _page==MIBLEsensors.size()/MI32.perPage) { _page = 0; } + if (_page>MIBLEsensors.size()/MI32.perPage) { _page = 0; } +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns62(uint8_t function) +{ + bool result = false; + if (FUNC_INIT == function){ + MI32Init(); + } + + if (MI32.mode.init) { + switch (function) { + case FUNC_EVERY_SECOND: + MI32EverySecond(false); + break; + case FUNC_COMMAND: + result = MI32Cmd(); + break; + case FUNC_JSON_APPEND: + MI32Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MI32Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} +#endif // USE_MI_ESP32 +#endif // ESP32 diff --git a/tasmota/xsns_62_MI_HM10.ino b/tasmota/xsns_62_MI_HM10.ino index b19ba6ff0..6655a0c87 100644 --- a/tasmota/xsns_62_MI_HM10.ino +++ b/tasmota/xsns_62_MI_HM10.ino @@ -34,6 +34,8 @@ forked - from arendst/tasmota - https://github.com/arendst/Tasmota */ +#ifdef ESP8266 // ESP8266 only. Use define USE_MI_ESP32 for ESP32 support + #ifdef USE_HM10 #define XSNS_62 62 @@ -250,7 +252,7 @@ void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay){ void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot){ HM10.last_command = HM10_TASK_LIST[slot][0]; // save command - HM10_TASK_LIST[slot][0] = task; + HM10_TASK_LIST[slot][0] = task; } void HM10_ReverseMAC(uint8_t _mac[]){ @@ -355,7 +357,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ } } if(!_success) return 0xff; - + DEBUG_SENSOR_LOG(PSTR("%s: vector size %u"),D_CMND_HM10, MIBLEsensors.size()); for(uint32_t i=0; ibegin(HM10.serialSpeed)) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start serial communication fixed to 115200 baud"),D_CMND_HM10); if (HM10Serial->hardwareSerial()) { @@ -470,7 +472,7 @@ void HM10parseMiBeacon(char * _buf, uint32_t _slot){ case 0x07: MIBLEsensors[_slot].lux=_beacon.lux & 0x00ffffff; DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); - break; + break; case 0x08: _tempFloat =(float)_beacon.moist; if(_tempFloat<100){ @@ -610,7 +612,7 @@ void HM10readHT_MJ_HT_V1(char *_buf){ // T=22.7 H=42.2 (response as ASCII) // 0123456789012 uint32_t _temp = (atoi(_buf+2) * 10) + atoi(_buf+5); - uint32_t _hum = (atoi(_buf+9) * 10) + atoi(_buf+12); + uint32_t _hum = (atoi(_buf+9) * 10) + atoi(_buf+12); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, H * 10: %u"),D_CMND_HM10,_temp,_hum); uint32_t _slot = HM10.state.sensor; @@ -682,7 +684,7 @@ bool HM10SerialHandleFeedback(){ // every 50 milliseconds bool success = false; uint32_t i = 0; static char ret[HM10_MAX_RX_BUF] = {0}; - + while(HM10Serial->available()) { // delay(0); if(iFLORA){ + if (MIBLEsensors[i].type>FLORA){ if(!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)){ ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum); } } if(MIBLEsensors[i].bat!=0x00){ // this is the error code -> no battery ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); - } + } ResponseAppend_P(PSTR("}")); } #ifdef USE_WEBSERVER @@ -1210,7 +1212,7 @@ void HM10Show(bool json) WSContentSend_PD(HTTP_HM10, HM10.firmware, i+1,stemp,MIBLEsensors.size()); for (i; ibegin(9600)) { if (HRXLSerial->hardwareSerial()) @@ -100,8 +100,7 @@ void HRXLShow(bool json) bool Xsns64(uint8_t function) { - if (pin[GPIO_HRXL_RX] >= 99) - return false; + if (!PinUsed(GPIO_HRXL_RX)) { return false; } switch (function) { diff --git a/tasmota/xsns_65_hdc1080.ino b/tasmota/xsns_65_hdc1080.ino index 6e834ef90..5b3b159ba 100644 --- a/tasmota/xsns_65_hdc1080.ino +++ b/tasmota/xsns_65_hdc1080.ino @@ -62,7 +62,7 @@ #define HDC1080_CONV_TIME 15 // Assume 6.50 + 6.35 ms + x of conversion delay for this device #define HDC1080_TEMP_MULT 0.0025177 -#define HDC1080_RH_MULT 0.0025177 +#define HDC1080_RH_MULT 0.0015258 #define HDC1080_TEMP_OFFSET 40.0 const char* hdc_type_name = "HDC1080"; diff --git a/tasmota/xsns_67_as3935.ino b/tasmota/xsns_67_as3935.ino index aade21dec..59618c32a 100644 --- a/tasmota/xsns_67_as3935.ino +++ b/tasmota/xsns_67_as3935.ino @@ -51,31 +51,7 @@ #define INDOORS 0x24 #define OUTDOORS 0x1C -// Translation -// http -#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:" -// http Message -#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..." -// CMD Status -#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:" + // Global const char HTTP_SNS_UNIT_KILOMETER[] PROGMEM = D_UNIT_KILOMETER; @@ -425,9 +401,9 @@ void AS3935SetWdth(uint8_t wdth) { } bool AS3935AutoTune(){ - detachInterrupt(pin[GPIO_AS3935]); - bool result = AS3935AutoTuneCaps(pin[GPIO_AS3935]); - attachInterrupt(digitalPinToInterrupt(pin[GPIO_AS3935]), AS3935Isr, RISING); + detachInterrupt(Pin(GPIO_AS3935)); + bool result = AS3935AutoTuneCaps(Pin(GPIO_AS3935)); + attachInterrupt(digitalPinToInterrupt(Pin(GPIO_AS3935)), AS3935Isr, RISING); return result; } @@ -521,8 +497,8 @@ void AS3935Detect(void) { if (AS3935init()) { I2cSetActiveFound(AS3935_ADDR, D_NAME_AS3935); - pinMode(pin[GPIO_AS3935], INPUT); - attachInterrupt(digitalPinToInterrupt(pin[GPIO_AS3935]), AS3935Isr, RISING); + pinMode(Pin(GPIO_AS3935), INPUT); + attachInterrupt(digitalPinToInterrupt(Pin(GPIO_AS3935)), AS3935Isr, RISING); AS3935Setup(); as3935_active = 1; } @@ -572,14 +548,14 @@ void AS3935EverySecond() { as3935_sensor.mqtt_irq = 0; // start http times as3935_sensor.http_count_start = 1; + as3935_sensor.http_count = 0; as3935_sensor.icount++; // Int counter as3935_sensor.detected = false; } if (as3935_sensor.http_count_start) as3935_sensor.http_count++; // clear Http - if (as3935_sensor.http_count == as3935_sensor.http_timer) { - as3935_sensor.http_count = 0; + if (as3935_sensor.http_count > as3935_sensor.http_timer) { as3935_sensor.http_count_start = 0; as3935_sensor.http_intensity = 0; as3935_sensor.http_distance = 0; diff --git a/tasmota/xsns_68_windmeter.ino b/tasmota/xsns_68_windmeter.ino new file mode 100644 index 000000000..4738cfd9b --- /dev/null +++ b/tasmota/xsns_68_windmeter.ino @@ -0,0 +1,375 @@ +/* + xsns_68_windmeter.ino - Analog wind sensor support for Tasmota + + Copyright (C) 2020 Matteo Albinola + (inspired by great works of Thomas Eckerstorfer, Norbert Richter, Maarten Damen 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 . +*/ + +#ifdef USE_WINDMETER +/*********************************************************************************************\ + * WindMeter sensor (speed) +\*********************************************************************************************/ + +#define XSNS_68 68 + +#define D_WINDMETER_NAME "WindMeter" + +#define WINDMETER_DEF_RADIUS 61 // Radius in millimeters (calculated by measuring the distance from the centre to the edge of one of the cups) +#define WINDMETER_DEF_PULSES_X_ROT 1 // Number of pulses for a complete rotation +#define WINDMETER_DEF_PULSE_DEBOUNCE 10 // Pulse counter debounce time (milliseconds) +#define WINDMETER_DEF_COMP_FACTOR 1.18 // Compensation factor +#define WINDMETER_DEF_TELE_PCHANGE 255 // Minimum percentage change between current and last reported speed in order to trigger a new tele message (0...100, 255 means off) +#define WINDMETER_WEIGHT_AVG_SAMPLE 150 // No of samples to take + +#ifdef USE_WEBSERVER +#define D_WINDMETER_WIND_AVG "∅" +#define D_WINDMETER_WIND_ANGLE "∠" +#define D_WINDMETER_WIND_DEGREE "°" +const char HTTP_SNS_WINDMETER[] PROGMEM = + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED "{m}%s %s{e}" +#ifndef USE_WINDMETER_NOSTATISTICS + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED " " D_WINDMETER_WIND_AVG "{m}%s %s{e}" + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED_MIN "{m}%s %s{e}" + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED_MAX "{m}%s %s{e}" +#endif // USE_WINDMETER_NOSTATISTICS +// "{s}WindMeter " D_TX20_WIND_DIRECTION "{m}%s %s" D_WINDMETER_WIND_DEGREE "{e}" +//#ifndef USE_WINDMETER_NOSTATISTICS +// "{s}WindMeter " D_TX20_WIND_DIRECTION " " D_WINDMETER_WIND_AVG "{m}%s %s" D_WINDMETER_WIND_DEGREE "{e}" +// "{s}WindMeter " D_TX20_WIND_DIRECTION " " D_WINDMETER_WIND_ANGLE "{m}%s" D_WINDMETER_WIND_DEGREE " (%s,%s)" D_WINDMETER_WIND_DEGREE; +//#endif // USE_WINDMETER_NOSTATISTICS + ; +#endif // USE_WEBSERVER + +// float saves 48 byte +float const windmeter_pi = 3.1415926535897932384626433; // Pi +float const windmeter_2pi = windmeter_pi * 2; + +struct WINDMETER { + uint32_t counter_time; + unsigned long counter = 0; + //uint32_t speed_time; + float speed = 0; + float last_tele_speed = 0; +#ifndef USE_WINDMETER_NOSTATISTICS + float speed_min = 0; + float speed_max = 0; + float speed_avg = 0; + uint32_t samples_count = 0; + uint32_t avg_samples_no; +#endif // USE_WINDMETER_NOSTATISTICS +} WindMeter; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception +void WindMeterUpdateSpeed(void) ICACHE_RAM_ATTR; +#endif // ARDUINO_ESP8266_RELEASE_2_3_0 + +void WindMeterUpdateSpeed(void) +{ + uint32_t time = micros(); + uint32_t time_diff = time - WindMeter.counter_time; + if (time_diff > Settings.windmeter_pulse_debounce * 1000) { + WindMeter.counter_time = time; + WindMeter.counter++; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("WMET: Counter %d"), WindMeter.counter); + } +} + +/********************************************************************************************/ + +void WindMeterInit(void) +{ + if (!Settings.flag2.speed_conversion) { + Settings.flag2.speed_conversion = 2; // 0 = none, 1 = m/s, 2 = km/h, 3 = kn, 4 = mph, 5 = ft/s, 6 = yd/s + } + if (!Settings.windmeter_radius) { + Settings.windmeter_radius = WINDMETER_DEF_RADIUS; + } + if (!Settings.windmeter_pulses_x_rot) { + Settings.windmeter_pulses_x_rot = WINDMETER_DEF_PULSES_X_ROT; + } + if (!Settings.windmeter_pulse_debounce) { + Settings.windmeter_pulse_debounce = WINDMETER_DEF_PULSE_DEBOUNCE; + } + if (!Settings.windmeter_speed_factor) { + Settings.windmeter_speed_factor = (int16_t)(WINDMETER_DEF_COMP_FACTOR * 1000); + } + if (!Settings.windmeter_tele_pchange) { + Settings.windmeter_tele_pchange = WINDMETER_DEF_TELE_PCHANGE; + } + +#ifndef USE_WINDMETER_NOSTATISTICS + WindMeterResetStatData(); + WindMeterCheckSampleCount(); +#endif // USE_WINDMETER_NOSTATISTICS + + pinMode(Pin(GPIO_WINDMETER_SPEED), INPUT_PULLUP); + attachInterrupt(Pin(GPIO_WINDMETER_SPEED), WindMeterUpdateSpeed, FALLING); +} + +void WindMeterEverySecond(void) +{ + //uint32_t time = micros(); + //uint32_t delta_time = time - WindMeter.speed_time; + //AddLog_P2(LOG_LEVEL_INFO, PSTR("delta_time: %d"), delta_time); + + // speed = ( (pulses / pulses_per_rotation) * (2 * pi * radius) ) / delta_time + WindMeter.speed = ((WindMeter.counter / Settings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)Settings.windmeter_radius / 1000))) * ((float)Settings.windmeter_speed_factor / 1000); + //WindMeter.speed = (((WindMeter.counter / Settings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)Settings.windmeter_radius / 1000))) / ((float)delta_time / 1000000)) * ((float)Settings.windmeter_speed_factor / 1000); + WindMeter.counter = 0; + //WindMeter.speed_time = time; + + //char speed_string[FLOATSZ]; + //dtostrfd(WindMeter.speed, 2, speed_string); + //char uspeed_string[FLOATSZ]; + //dtostrfd(ConvertSpeed(WindMeter.speed), 2, uspeed_string); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("WMET: Speed %s [m/s] - %s [unit]"), speed_string, uspeed_string); + +#ifndef USE_WINDMETER_NOSTATISTICS + if (WindMeter.speed < WindMeter.speed_min) { + WindMeter.speed_min = WindMeter.speed; + } + if (WindMeter.speed > WindMeter.speed_max) { + WindMeter.speed_max = WindMeter.speed; + } + + // exponentially weighted average is not quite as smooth as the arithmetic average + // but close enough to the moving average and does not require the regular reset + // of the divider with the associated jump in avg values after period is over + if (WindMeter.samples_count <= WindMeter.avg_samples_no) { + WindMeter.samples_count++; + } + WindMeter.speed_avg -= WindMeter.speed_avg / WindMeter.samples_count; + WindMeter.speed_avg += float(WindMeter.speed) / WindMeter.samples_count; + + WindMeterCheckSampleCount(); + if (0==Settings.tele_period) { + WindMeterResetStatData(); + } +#endif // USE_WINDMETER_NOSTATISTICS + + if (WindMeterShouldTriggerTele()) { + WindMeterTriggerTele(); + } +} + +bool WindMeterShouldTriggerTele() +{ + if (Settings.windmeter_tele_pchange > 100) { + return false; + } else if (WindMeter.last_tele_speed == 0) { + return WindMeter.speed > 0; + } else { + float perc_change = (WindMeter.speed / WindMeter.last_tele_speed) -1; + return (perc_change * ((perc_change < 0) ? -100 : 100)) >= Settings.windmeter_tele_pchange; + } +} + +void WindMeterResetStatData(void) +{ + WindMeter.speed_min = WindMeter.speed; + WindMeter.speed_max = WindMeter.speed; + //WindMeter.direction_min = WindMeter.direction; + //WindMeter.direction_max = WindMeter.direction; +} + +void WindMeterCheckSampleCount(void) +{ + uint32_t prev_avg_samples_no = WindMeter.avg_samples_no; + if (Settings.tele_period) { + // number for avg samples = teleperiod value if set + WindMeter.avg_samples_no = Settings.tele_period; + } else { + // otherwise use default number of samples for this driver + WindMeter.avg_samples_no = WINDMETER_WEIGHT_AVG_SAMPLE; + } + if (prev_avg_samples_no != WindMeter.avg_samples_no) { + WindMeter.speed_avg = WindMeter.speed; + WindMeter.samples_count = 0; + } +} + +void WindMeterShow(bool json) +{ + char speed_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed), 2, speed_string); +#ifndef USE_WINDMETER_NOSTATISTICS + char speed_min_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed_min), 2, speed_min_string); + char speed_max_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed_max), 2, speed_max_string); + char speed_avg_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed_avg), 2, speed_avg_string); + //char direction_avg_string[FLOATSZ]; + //dtostrfd(WindMeter.direction_avg, 1, direction_avg_string); + //char direction_avg_cardinal_string[4]; + //GetTextIndexed(direction_avg_cardinal_string, sizeof(direction_avg_cardinal_string), int((WindMeter.direction_avg/22.5f)+0.5f) % 16, kWindMeterDirections); + //char direction_range_string[FLOATSZ]; + //dtostrfd(Tx2xNormalize(WindMeter.direction_max-WindMeter.direction_min)*22.5, 1, direction_range_string); + //char direction_min_string[FLOATSZ]; + //dtostrfd(Tx2xNormalize(WindMeter.direction_min)*22.5, 1, direction_min_string); + //char direction_max_string[FLOATSZ]; + //dtostrfd(Tx2xNormalize(WindMeter.direction_max)*22.5, 1, direction_max_string); +#endif // USE_WINDMETER_NOSTATISTICS + + if (json) { + WindMeter.last_tele_speed = WindMeter.speed; +#ifndef USE_WINDMETER_NOSTATISTICS + ResponseAppend_P(PSTR(",\"" D_WINDMETER_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s,\"Avg\":%s,\"AvgCard\":\"%s\",\"Min\":%s,\"Max\":%s,\"Range\":%s}}"), + speed_string, + speed_avg_string, + speed_min_string, + speed_max_string, + "n/a", //direction_cardinal_string, + "n/a", //direction_string, + "n/a", //direction_avg_string, + "n/a", //direction_avg_cardinal_string, + "n/a", //direction_min_string, + "n/a", //direction_max_string, + "n/a" //direction_range_string + ); +#else // USE_WINDMETER_NOSTATISTICS + ResponseAppend_P(PSTR(",\"" D_WINDMETER_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s}}"), + speed_string, + "n/a", //wind_direction_cardinal_string, + "n/a" //wind_direction_string + ); +#endif // USE_WINDMETER_NOSTATISTICS +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_WINDMETER, + speed_string, + SpeedUnit().c_str(), +#ifndef USE_WINDMETER_NOSTATISTICS + speed_avg_string, + SpeedUnit().c_str(), + speed_min_string, + SpeedUnit().c_str(), + speed_max_string, + SpeedUnit().c_str(), +#endif // USE_WINDMETER_NOSTATISTICS + "n/a", //wind_direction_cardinal_string, + "n/a" //wind_direction_string +#ifndef USE_WINDMETER_NOSTATISTICS + ,"n/a", //,wind_direction_avg_cardinal_string, + "n/a", //wind_direction_avg_string, + "n/a", //wind_direction_range_string, + "n/a", //wind_direction_min_string, + "n/a" //wind_direction_max_string +#endif // USE_WINDMETER_NOSTATISTICS + ); +#endif // USE_WEBSERVER + } +} + +void WindMeterTriggerTele(void) +{ + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +#ifdef USE_RULES + RulesTeleperiod(); // Allow rule based HA messages +#endif // USE_RULES + } +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +bool Xsns68Cmnd(void) +{ + bool serviced = true; + bool show_parms = true; + char sub_string[XdrvMailbox.data_len +1]; + switch (XdrvMailbox.payload) { + case 1: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_radius = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + case 2: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_pulses_x_rot = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + case 3: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_pulse_debounce = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + case 4: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_speed_factor = (int16_t)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 1000); + } + break; + case 5: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_tele_pchange = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + } + + if (show_parms) { + char speed_factor_string[FLOATSZ]; + dtostrfd((float)Settings.windmeter_speed_factor / 1000, 3, speed_factor_string); + char tele_pchange_string[4] = "off"; + if (Settings.windmeter_tele_pchange <= 100) { + itoa(Settings.windmeter_tele_pchange, tele_pchange_string, 10); + } + Response_P(PSTR("{\"" D_WINDMETER_NAME "\":{\"Radius\":%d,\"PulsesPerRot\":%d,\"PulseDebounce\":%d,\"SpeedFactor\":%s,\"TeleTriggerMin%Change\":%s}}"), + Settings.windmeter_radius, Settings.windmeter_pulses_x_rot, Settings.windmeter_pulse_debounce, speed_factor_string, tele_pchange_string); + } + return serviced; +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns68(uint8_t function) +{ + bool result = false; + if (PinUsed(GPIO_WINDMETER_SPEED)) { + switch (function) { + case FUNC_INIT: + WindMeterInit(); + break; + case FUNC_EVERY_SECOND: + WindMeterEverySecond(); + break; +#ifndef USE_WINDMETER_NOSTATISTICS + case FUNC_AFTER_TELEPERIOD: + WindMeterResetStatData(); + break; +#endif // USE_WINDMETER_NOSTATISTICS + case FUNC_JSON_APPEND: + WindMeterShow(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + WindMeterShow(false); + break; +#endif // USE_WEBSERVER + case FUNC_COMMAND_SENSOR: + if (XSNS_68 == XdrvMailbox.index) { + result = Xsns68Cmnd(); + } + } + } + return result; +} + +#endif // USE_WINDMETER diff --git a/tasmota/xsns_69_opentherm.ino b/tasmota/xsns_69_opentherm.ino new file mode 100644 index 000000000..39b7748fe --- /dev/null +++ b/tasmota/xsns_69_opentherm.ino @@ -0,0 +1,598 @@ +/* + xsns_69_opentherm.ino - OpenTherm protocol support for Tasmota + + Copyright (C) 2020 Yuriy Sannikov + + 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 . +*/ + +#ifdef USE_OPENTHERM + +#define XSNS_69 69 + +#include + +// Hot water and boiler parameter ranges +#define OT_HOT_WATER_MIN 23 +#define OT_HOT_WATER_MAX 55 +#define OT_BOILER_MIN 40 +#define OT_BOILER_MAX 85 + +#define OT_HOT_WATER_DEFAULT 36; +#define OT_BOILER_DEFAULT 85; + +// Seconds before OT will make an attempt to connect to the boiler after connection error +#define SNS_OT_DISCONNECT_COOLDOWN_SECONDS 10 + +// Count of the OpenThermSettingsFlags +#define OT_FLAGS_COUNT 6 +enum OpenThermSettingsFlags +{ + // If set, central heating on/off state follows diagnostic indication bit(6), however + // EnableCentralHeating flag has a priority over it + EnableCentralHeatingOnDiagnostics = 0x01, + // If set, DHW is on after restart. + EnableHotWater = 0x02, + // If set, keep CH always on after restart. If off, follows the EnableCentralHeatingOnDiagnostics rule + EnableCentralHeating = 0x04, + EnableCooling = 0x08, + EnableTemperatureCompensation = 0x10, + EnableCentralHeating2 = 0x20, +}; + +enum OpenThermConnectionStatus +{ + OTC_NONE, // OT not initialized + OTC_DISCONNECTED, // OT communication timed out + OTC_CONNECTING, // Connecting after start or from DISCONNECTED state + OTC_HANDSHAKE, // Wait for the handshake response + OTC_READY, // Last Known Good response state is SUCCESS and no requests are in flight + OTC_INFLIGHT // Request sent, waiting from the response +}; + +OpenThermConnectionStatus sns_ot_connection_status = OpenThermConnectionStatus::OTC_NONE; +uint8_t sns_ot_disconnect_cooldown = 0; + +OpenTherm *sns_ot_master = NULL; + +// Has valid values if connection status is READY or INFLIGHT +typedef struct OT_BOILER_STATUS_T +{ + // Boiler fault code + uint8_t m_fault_code; + // Boiler OEM fault code + uint8_t m_oem_fault_code; + // Boilder OEM Diagnostics code + uint16_t m_oem_diag_code; + // OpenTherm ID(3) response. + uint8_t m_slave_flags; + // OpenTherm ID(1) codes. Should be used to display state + unsigned long m_slave_raw_status; + // Desired boiler states + bool m_enableCentralHeating; + bool m_enableHotWater; + bool m_enableCooling; + bool m_enableOutsideTemperatureCompensation; + bool m_enableCentralHeating2; + + // Some boilers has an input for the heat request. When short, heat is requested + // OT ID(0) bit 6 may indicate state of the Heat Request input + // By enabling this bit we will set m_enableCentralHeating to true when OT ID(0) bit 6 is set. + // This enables to use external mechanical thermostat to enable heating. + // Some of the use cases might be setting an emergency temperature to prevent freezing + // in case of the software thermostat failure. + bool m_useDiagnosticIndicationAsHeatRequest; + + // Hot Water temperature + float m_hotWaterSetpoint_read; + // Flame Modulation + float m_flame_modulation_read; + // Boiler Temperature + float m_boiler_temperature_read; + + // Boiler desired values + float m_boilerSetpoint; + float m_hotWaterSetpoint; + +} OT_BOILER_STATUS; + +OT_BOILER_STATUS sns_ot_boiler_status; + +const char *sns_opentherm_connection_stat_to_str(int status) +{ + switch (status) + { + case OpenThermConnectionStatus::OTC_NONE: + return "NONE"; + case OpenThermConnectionStatus::OTC_DISCONNECTED: + return "FAULT"; + case OpenThermConnectionStatus::OTC_CONNECTING: + return "CONNECTING"; + case OpenThermConnectionStatus::OTC_HANDSHAKE: + return "HANDSHAKE"; + case OpenThermConnectionStatus::OTC_READY: + return "READY"; + case OpenThermConnectionStatus::OTC_INFLIGHT: + return "BUSY"; + default: + return "UNKNOWN"; + } +} + +void sns_opentherm_init_boiler_status() +{ + memset(&sns_ot_boiler_status, 0, sizeof(OT_BOILER_STATUS)); + + // Settings + sns_ot_boiler_status.m_useDiagnosticIndicationAsHeatRequest = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics; + sns_ot_boiler_status.m_enableHotWater = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableHotWater; + sns_ot_boiler_status.m_enableCentralHeating = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeating; + sns_ot_boiler_status.m_enableCooling = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCooling; + sns_ot_boiler_status.m_enableOutsideTemperatureCompensation = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableTemperatureCompensation; + sns_ot_boiler_status.m_enableCentralHeating2 = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeating2; + + sns_ot_boiler_status.m_boilerSetpoint = (float)Settings.ot_boiler_setpoint; + sns_ot_boiler_status.m_hotWaterSetpoint = (float)Settings.ot_hot_water_setpoint; + + sns_ot_boiler_status.m_fault_code = 0; + sns_ot_boiler_status.m_oem_fault_code = 0; + sns_ot_boiler_status.m_oem_diag_code = 0; + sns_ot_boiler_status.m_hotWaterSetpoint_read = 0; + sns_ot_boiler_status.m_flame_modulation_read = 0; + sns_ot_boiler_status.m_boiler_temperature_read = 0; +} + +void ICACHE_RAM_ATTR sns_opentherm_handleInterrupt() +{ + sns_ot_master->handleInterrupt(); +} + +void sns_opentherm_processResponseCallback(unsigned long response, int st) +{ + OpenThermResponseStatus status = (OpenThermResponseStatus)st; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, + PSTR("[OTH]: Processing response. Status=%s, Response=0x%lX"), + sns_ot_master->statusToString(status), response); + + if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_HANDSHAKE) + { + return sns_ot_process_handshake(response, st); + } + + switch (status) + { + case OpenThermResponseStatus::SUCCESS: + if (sns_ot_master->isValidResponse(response)) + { + sns_opentherm_process_success_response(&sns_ot_boiler_status, response); + } + sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY; + break; + + case OpenThermResponseStatus::INVALID: + sns_opentherm_check_retry_request(); + sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY; + break; + + // Timeout may indicate not valid/supported command or connection error + // In this case we do reconnect. + // If this command will timeout multiple times, it will be excluded from the rotation later on + // after couple of failed attempts. See sns_opentherm_check_retry_request logic + case OpenThermResponseStatus::TIMEOUT: + sns_opentherm_check_retry_request(); + sns_ot_connection_status = OpenThermConnectionStatus::OTC_DISCONNECTED; + break; + } +} + +bool sns_opentherm_Init() +{ + if (PinUsed(GPIO_BOILER_OT_RX) && PinUsed(GPIO_BOILER_OT_TX)) + { + sns_ot_master = new OpenTherm(Pin(GPIO_BOILER_OT_RX), Pin(GPIO_BOILER_OT_TX)); + sns_ot_master->begin(sns_opentherm_handleInterrupt, sns_opentherm_processResponseCallback); + sns_ot_connection_status = OpenThermConnectionStatus::OTC_CONNECTING; + + sns_opentherm_init_boiler_status(); + return true; + } + return false; + // !warning, sns_opentherm settings are not ready at this point +} + +void sns_opentherm_stat(bool json) +{ + if (!sns_ot_master) + { + return; + } + const char *statusStr = sns_opentherm_connection_stat_to_str(sns_ot_connection_status); + + if (json) + { + ResponseAppend_P(PSTR(",\"OPENTHERM\":{")); + ResponseAppend_P(PSTR("\"conn\":\"%s\","), statusStr); + ResponseAppend_P(PSTR("\"settings\":%d,"), Settings.ot_flags); + sns_opentherm_dump_telemetry(); + ResponseJsonEnd(); +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_P(PSTR("{s}OpenTherm status{m}%s (0x%X){e}"), statusStr, (int)sns_ot_boiler_status.m_slave_flags); + if (sns_ot_connection_status < OpenThermConnectionStatus::OTC_READY) + { + return; + } + WSContentSend_P(PSTR("{s}Std/OEM Fault Codes{m}%d / %d{e}"), + (int)sns_ot_boiler_status.m_fault_code, + (int)sns_ot_boiler_status.m_oem_fault_code); + + WSContentSend_P(PSTR("{s}OEM Diagnostic Code{m}%d{e}"), + (int)sns_ot_boiler_status.m_oem_diag_code); + + WSContentSend_P(PSTR("{s}Hot Water Setpoint{m}%d{e}"), + (int)sns_ot_boiler_status.m_hotWaterSetpoint_read); + + WSContentSend_P(PSTR("{s}Flame Modulation{m}%d{e}"), + (int)sns_ot_boiler_status.m_flame_modulation_read); + + WSContentSend_P(PSTR("{s}Boiler Temp/Setpnt{m}%d / %d{e}"), + (int)sns_ot_boiler_status.m_boiler_temperature_read, + (int)sns_ot_boiler_status.m_boilerSetpoint); + + if (OpenTherm::isCentralHeatingActive(sns_ot_boiler_status.m_slave_raw_status)) + { + WSContentSend_P(PSTR("{s}Central Heating is ACTIVE{m}{e}")); + } + + if (sns_ot_boiler_status.m_enableHotWater) + { + WSContentSend_P(PSTR("{s}Hot Water is Enabled{m}{e}")); + } + + if (OpenTherm::isHotWaterActive(sns_ot_boiler_status.m_slave_raw_status)) + { + WSContentSend_P(PSTR("{s}Hot Water is ACTIVE{m}{e}")); + } + + if (OpenTherm::isFlameOn(sns_ot_boiler_status.m_slave_raw_status)) + { + WSContentSend_P(PSTR("{s}Flame is ACTIVE{m}{e}")); + } + + if (sns_ot_boiler_status.m_enableCooling) + { + WSContentSend_P(PSTR("{s}Cooling is Enabled{m}{e}")); + } + + if (OpenTherm::isCoolingActive(sns_ot_boiler_status.m_slave_raw_status)) + { + WSContentSend_P(PSTR("{s}Cooling is ACTIVE{m}{e}")); + } + + if (OpenTherm::isDiagnostic(sns_ot_boiler_status.m_slave_raw_status)) + { + WSContentSend_P(PSTR("{s}Diagnostic Indication{m}{e}")); + } + +#endif // USE_WEBSERVER + } +} + +void sns_ot_start_handshake() +{ + if (!sns_ot_master) + { + return; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("[OTH]: perform handshake")); + + sns_ot_master->sendRequestAync( + OpenTherm::buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::SConfigSMemberIDcode, 0)); + + sns_ot_connection_status = OpenThermConnectionStatus::OTC_HANDSHAKE; +} + +void sns_ot_process_handshake(unsigned long response, int st) +{ + OpenThermResponseStatus status = (OpenThermResponseStatus)st; + + if (status != OpenThermResponseStatus::SUCCESS || !sns_ot_master->isValidResponse(response)) + { + AddLog_P2(LOG_LEVEL_ERROR, + PSTR("[OTH]: getSlaveConfiguration failed. Status=%s"), + sns_ot_master->statusToString(status)); + sns_ot_connection_status = OpenThermConnectionStatus::OTC_DISCONNECTED; + return; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("[OTH]: getLastResponseStatus SUCCESS. Slave Cfg: %lX"), response); + + sns_ot_boiler_status.m_slave_flags = (response & 0xFF00) >> 8; + + sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY; +} + +void sns_opentherm_CheckSettings(void) +{ + bool settingsValid = true; + + settingsValid &= Settings.ot_hot_water_setpoint >= OT_HOT_WATER_MIN; + settingsValid &= Settings.ot_hot_water_setpoint <= OT_HOT_WATER_MAX; + settingsValid &= Settings.ot_boiler_setpoint >= OT_BOILER_MIN; + settingsValid &= Settings.ot_boiler_setpoint <= OT_BOILER_MAX; + + if (!settingsValid) + { + Settings.ot_hot_water_setpoint = OT_HOT_WATER_DEFAULT; + Settings.ot_boiler_setpoint = OT_BOILER_DEFAULT; + Settings.ot_flags = + OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics | + OpenThermSettingsFlags::EnableHotWater; + } +} +/*********************************************************************************************\ + * Command Processing +\*********************************************************************************************/ +const char *sns_opentherm_flag_text(uint8_t mode) +{ + switch ((OpenThermSettingsFlags)mode) + { + case OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics: + return "CHOD"; + case OpenThermSettingsFlags::EnableHotWater: + return "DHW"; + case OpenThermSettingsFlags::EnableCentralHeating: + return "CH"; + case OpenThermSettingsFlags::EnableCooling: + return "COOL"; + case OpenThermSettingsFlags::EnableTemperatureCompensation: + return "OTC"; + case OpenThermSettingsFlags::EnableCentralHeating2: + return "CH2"; + default: + return "?"; + } +} + +uint8_t sns_opentherm_parse_flag(char *flag) +{ + if (!strncmp(flag, "CHOD", 4)) + { + return OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics; + } + else if (!strncmp(flag, "COOL", 4)) + { + return OpenThermSettingsFlags::EnableCooling; + } + else if (!strncmp(flag, "DHW", 3)) + { + return OpenThermSettingsFlags::EnableHotWater; + } + else if (!strncmp(flag, "OTC", 3)) + { + return OpenThermSettingsFlags::EnableTemperatureCompensation; + } + else if (!strncmp(flag, "CH2", 3)) + { + return OpenThermSettingsFlags::EnableCentralHeating2; + } + else if (!strncmp(flag, "CH", 2)) + { + return OpenThermSettingsFlags::EnableCentralHeating; + } + return 0; +} + +uint8_t sns_opentherm_read_flags(char *data, uint32_t len) +{ + uint8_t tokens = 1; + for (int i = 0; i < len; ++i) + { + if (data[i] == ',') + { + ++tokens; + } + } + uint8_t result = 0; + char sub_string[XdrvMailbox.data_len + 1]; + for (int i = 1; i <= tokens; ++i) + { + char *flag = subStr(sub_string, data, ",", i); + if (!flag) + { + break; + } + result |= sns_opentherm_parse_flag(flag); + } + return result; +} +#define D_PRFX_OTHERM "ot_" +// set the boiler temperature (CH). Sutable for the PID app. +// After restart will use the defaults from the settings +#define D_CMND_OTHERM_BOILER_SETPOINT "tboiler" +// set hot water (DHW) temperature. Do not write it in the flash memory. +// suitable for the temporary changes +#define D_CMND_OTHERM_DHW_SETPOINT "twater" +// This command will save CH and DHW setpoints into the settings. Those values will be used after system restart +// The reason to separate set and save is to reduce flash memory write count, especially if boiler temperature is controlled +// by the PID thermostat +#define D_CMND_OTHERM_SAVE_SETTINGS "save_setpoints" +// Get or set flags + +// EnableCentralHeatingOnDiagnostics -> CHOD +// EnableHotWater -> DHW +// EnableCentralHeating -> CH +// EnableCooling -> COOL +// EnableTemperatureCompensation -> OTC +// EnableCentralHeating2 -> CH2 +#define D_CMND_OTHERM_FLAGS "flags" + +// Get/Set boiler status m_enableCentralHeating value. It's equivalent of the EnableCentralHeating settings +// flag value, however, this command does not update the settings. +// Usefull to buld automations +// Please note, if you set it to "0" and EnableCentralHeatingOnDiagnostics is set +// boiler will follow the Diagnostics bit and won't turn CH off. When Diagnostics bit cleared, +// and "ot_ch" is "1", boiler will keep heating +#define D_CMND_SET_CENTRAL_HEATING_ENABLED "ch" + +const char kOpenThermCommands[] PROGMEM = D_PRFX_OTHERM "|" D_CMND_OTHERM_BOILER_SETPOINT "|" D_CMND_OTHERM_DHW_SETPOINT + "|" D_CMND_OTHERM_SAVE_SETTINGS "|" D_CMND_OTHERM_FLAGS "|" D_CMND_SET_CENTRAL_HEATING_ENABLED; + +void (*const OpenThermCommands[])(void) PROGMEM = { + &sns_opentherm_boiler_setpoint_cmd, + &sns_opentherm_hot_water_setpoint_cmd, + &sns_opentherm_save_settings_cmd, + &sns_opentherm_flags_cmd, + &sns_opentherm_set_central_heating_cmd}; + +void sns_opentherm_cmd(void) { } +void sns_opentherm_boiler_setpoint_cmd(void) +{ + bool query = strlen(XdrvMailbox.data) == 0; + if (!query) + { + sns_ot_boiler_status.m_boilerSetpoint = atof(XdrvMailbox.data); + } + ResponseCmndFloat(sns_ot_boiler_status.m_boilerSetpoint, Settings.flag2.temperature_resolution); +} + +void sns_opentherm_hot_water_setpoint_cmd(void) +{ + bool query = strlen(XdrvMailbox.data) == 0; + if (!query) + { + sns_ot_boiler_status.m_hotWaterSetpoint = atof(XdrvMailbox.data); + } + ResponseCmndFloat(sns_ot_boiler_status.m_hotWaterSetpoint, Settings.flag2.temperature_resolution); +} + +void sns_opentherm_save_settings_cmd(void) +{ + Settings.ot_hot_water_setpoint = (uint8_t)sns_ot_boiler_status.m_hotWaterSetpoint; + Settings.ot_boiler_setpoint = (uint8_t)sns_ot_boiler_status.m_boilerSetpoint; + ResponseCmndDone(); +} + +void sns_opentherm_flags_cmd(void) +{ + bool query = strlen(XdrvMailbox.data) == 0; + if (!query) + { + // Set flags value + Settings.ot_flags = sns_opentherm_read_flags(XdrvMailbox.data, XdrvMailbox.data_len); + // Reset boiler status to apply settings + sns_opentherm_init_boiler_status(); + } + bool addComma = false; + mqtt_data[0] = 0; + for (int pos = 0; pos < OT_FLAGS_COUNT; ++pos) + { + int mask = 1 << pos; + int mode = Settings.ot_flags & (uint8_t)mask; + if (mode > 0) + { + if (addComma) + { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s"), mqtt_data, sns_opentherm_flag_text(mode)); + addComma = true; + } + } +} + +void sns_opentherm_set_central_heating_cmd(void) +{ + bool query = strlen(XdrvMailbox.data) == 0; + if (!query) + { + sns_ot_boiler_status.m_enableCentralHeating = atoi(XdrvMailbox.data); + } + ResponseCmndNumber(sns_ot_boiler_status.m_enableCentralHeating ? 1 : 0); +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns69(uint8_t function) +{ + bool result = false; + if (FUNC_INIT == function) + { + if (sns_opentherm_Init()) + { + sns_opentherm_CheckSettings(); + } + } + + if (!sns_ot_master) + { + return result; + } + + switch (function) + { + case FUNC_LOOP: + sns_ot_master->process(); + break; + case FUNC_EVERY_100_MSECOND: + if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_READY && sns_ot_master->isReady()) + { + unsigned long request = sns_opentherm_get_next_request(&sns_ot_boiler_status); + if (-1 != request) + { + sns_ot_master->sendRequestAync(request); + sns_ot_connection_status = OpenThermConnectionStatus::OTC_INFLIGHT; + } + } + break; + case FUNC_EVERY_SECOND: + if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_DISCONNECTED) + { + // If disconnected, wait for the SNS_OT_DISCONNECT_COOLDOWN_SECONDS before the handshake + if (sns_ot_disconnect_cooldown == 0) + { + sns_ot_disconnect_cooldown = SNS_OT_DISCONNECT_COOLDOWN_SECONDS; + } + else if (--sns_ot_disconnect_cooldown == 0) + { + sns_ot_connection_status = OpenThermConnectionStatus::OTC_CONNECTING; + } + } + else if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_CONNECTING) + { + sns_ot_start_handshake(); + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kOpenThermCommands, OpenThermCommands); + break; + case FUNC_JSON_APPEND: + sns_opentherm_stat(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + sns_opentherm_stat(0); + break; +#endif // USE_WEBSERVER + } + + return result; +} + +#endif // USE_OPENTHERM diff --git a/tasmota/xsns_69_opentherm_protocol.ino b/tasmota/xsns_69_opentherm_protocol.ino new file mode 100644 index 000000000..1ebc5c14b --- /dev/null +++ b/tasmota/xsns_69_opentherm_protocol.ino @@ -0,0 +1,441 @@ +/* + xsns_69_opentherm_protocol.ino - OpenTherm protocol support for Tasmota + + Copyright (C) 2020 Yuriy Sannikov + + 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 . +*/ + +#ifdef USE_OPENTHERM + +#include "OpenTherm.h" + +// Temperature tolerance. If temperature setpoint difference is less than the value, +// OT (1)(Control setpoint) command will be skipped +#define OPENTHERM_BOILER_SETPOINT_TOLERANCE 1.0 + +typedef union { + uint8_t m_flags; + struct + { + uint8_t notSupported : 1; // If set, boiler does not support this command + uint8_t supported : 1; // Set if at least one response were successfull + uint8_t retryCount : 2; // Retry counter before notSupported flag being set + }; +} OpenThermParamFlags; + +typedef union { + float m_float; + uint8_t m_u8; + uint16_t m_u16; + unsigned long m_ul; + bool m_bool; +} ResponseStorage; + +typedef struct OpenThermCommandT +{ + const char *m_command_name; + uint8_t m_command_code; + OpenThermParamFlags m_flags; + ResponseStorage m_results[2]; + unsigned long (*m_ot_make_request)(OpenThermCommandT *self, OT_BOILER_STATUS_T *boilerStatus); + void (*m_ot_parse_response)(OpenThermCommandT *self, OT_BOILER_STATUS_T *boilerStatus, unsigned long response); + void (*m_ot_appent_telemetry)(OpenThermCommandT *self); +} OpenThermCommand; + +OpenThermCommand sns_opentherm_commands[] = { + {// Get/Set Slave Status Flags + .m_command_name = "SLAVE", + .m_command_code = 0, + // OpenTherm ID(0) should never go into the notSupported state due to some connectivity issues + // otherwice it may lose boiler control + .m_flags = {.supported = 1}, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_set_slave_flags, + .m_ot_parse_response = sns_opentherm_parse_slave_flags, + .m_ot_appent_telemetry = sns_opentherm_tele_slave_flags}, + {// Set boiler temperature + .m_command_name = "BTMP", + .m_command_code = 0, + // OpenTherm ID(1) also should never go into the notSupported state due to some connectivity issues + .m_flags = {.supported = 1}, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_set_boiler_temperature, + .m_ot_parse_response = sns_opentherm_parse_set_boiler_temperature, + .m_ot_appent_telemetry = sns_opentherm_tele_boiler_temperature}, + {// Set Hot Water temperature + .m_command_name = "HWTMP", + .m_command_code = 0, + // OpenTherm ID(56) may not be supported + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_set_boiler_dhw_temperature, + .m_ot_parse_response = sns_opentherm_parse_boiler_dhw_temperature, + .m_ot_appent_telemetry = sns_opentherm_tele_boiler_dhw_temperature}, + {// Read Application-specific fault flags and OEM fault code + .m_command_name = "ASFF", + .m_command_code = 0, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_flags, + .m_ot_parse_response = sns_opentherm_parse_flags, + .m_ot_appent_telemetry = sns_opentherm_tele_flags}, + {// Read An OEM-specific diagnostic/service code + .m_command_name = "OEMD", + .m_command_code = 0, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_oem_diag, + .m_ot_parse_response = sns_opentherm_parse_oem_diag, + .m_ot_appent_telemetry = sns_opentherm_tele_oem_diag}, + {// Read Flame modulation + .m_command_name = "FLM", + .m_command_code = (uint8_t)OpenThermMessageID::RelModLevel, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_flame_modulation, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + {// Read Boiler Temperature + .m_command_name = "TB", + .m_command_code = (uint8_t)OpenThermMessageID::Tboiler, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_boiler_temperature, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + {// Read DHW temperature + .m_command_name = "TDHW", + .m_command_code = (uint8_t)OpenThermMessageID::Tdhw, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_generic_float, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + {// Read Outside temperature + .m_command_name = "TOUT", + .m_command_code = (uint8_t)OpenThermMessageID::Toutside, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_generic_float, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + {// Read Return water temperature + .m_command_name = "TRET", + .m_command_code = (uint8_t)OpenThermMessageID::Tret, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_generic_float, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + {// Read DHW setpoint + .m_command_name = "DHWS", + .m_command_code = (uint8_t)OpenThermMessageID::TdhwSet, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_dhw_setpoint, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + {// Read max CH water setpoint + .m_command_name = "TMAX", + .m_command_code = (uint8_t)OpenThermMessageID::MaxTSet, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_generic_float, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + +}; + +/////////////////////////////////// Process Slave Status Flags & Control ////////////////////////////////////////////////// +unsigned long sns_opentherm_set_slave_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status) +{ + bool centralHeatingIsOn = status->m_enableCentralHeating; + + if (status->m_useDiagnosticIndicationAsHeatRequest) { + centralHeatingIsOn |= OpenTherm::isDiagnostic(status->m_slave_raw_status); + } + + if (self->m_results[1].m_bool != centralHeatingIsOn) { + AddLog_P2(LOG_LEVEL_INFO, + PSTR("[OTH]: Central Heating transitioning from %s to %s"), + self->m_results[1].m_bool ? "on" : "off", + status->m_enableCentralHeating ? "on" : "off"); + } + self->m_results[1].m_bool = centralHeatingIsOn; + + unsigned int data = centralHeatingIsOn | + (status->m_enableHotWater << 1) | + (status->m_enableCooling << 2) | + (status->m_enableOutsideTemperatureCompensation << 3) | + (status->m_enableCentralHeating2 << 4); + + data <<= 8; + + return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Status, data); +} + +void sns_opentherm_parse_slave_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + boilerStatus->m_slave_raw_status = response; + self->m_results[0].m_ul = response; +} + +#define OT_FLAG_TO_ON_OFF(status, flag) ((((status) & (flag)) != 0) ? 1 : 0) +void sns_opentherm_tele_slave_flags(struct OpenThermCommandT *self) +{ + unsigned long st = self->m_results[0].m_ul; + ResponseAppend_P(PSTR("{\"FAULT\":%d,\"CH\":%d,\"DHW\":%d,\"FL\":%d,\"COOL\":%d,\"CH2\":%d,\"DIAG\":%d,\"RAW\":%lu}"), + OT_FLAG_TO_ON_OFF(st, 0x01), + OT_FLAG_TO_ON_OFF(st, 0x02), + OT_FLAG_TO_ON_OFF(st, 0x04), + OT_FLAG_TO_ON_OFF(st, 0x08), + OT_FLAG_TO_ON_OFF(st, 0x10), + OT_FLAG_TO_ON_OFF(st, 0x20), + OT_FLAG_TO_ON_OFF(st, 0x40), + st); +} + +/////////////////////////////////// Set Boiler Temperature ////////////////////////////////////////////////// +unsigned long sns_opentherm_set_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status) +{ + // Assuming some boilers might write setpoint temperature into the Flash memory + // Having PID controlled appliance may produce a lot of small fluctuations in the setpoint value + // wearing out Boiler flash memory. + float diff = abs(status->m_boilerSetpoint - self->m_results[0].m_float); + // Ignore small changes in the boiler setpoint temperature + if (diff < OPENTHERM_BOILER_SETPOINT_TOLERANCE) + { + return -1; + } + AddLog_P2(LOG_LEVEL_INFO, + PSTR("[OTH]: Setting Boiler Temp. Old: %d, New: %d"), + (int)self->m_results[0].m_float, + (int)status->m_boilerSetpoint); + self->m_results[0].m_float = status->m_boilerSetpoint; + + unsigned int data = OpenTherm::temperatureToData(status->m_boilerSetpoint); + return OpenTherm::buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TSet, data); +} +void sns_opentherm_parse_set_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[1].m_float = OpenTherm::getFloat(response); +} +void sns_opentherm_tele_boiler_temperature(struct OpenThermCommandT *self) +{ + char requested[FLOATSZ]; + dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, requested); + char actual[FLOATSZ]; + dtostrfd(self->m_results[1].m_float, Settings.flag2.temperature_resolution, actual); + + // indicate fault if tepmerature demand and actual setpoint are greater then tolerance + bool isFault = abs(self->m_results[1].m_float - self->m_results[0].m_float) > OPENTHERM_BOILER_SETPOINT_TOLERANCE; + + ResponseAppend_P(PSTR("{\"FAULT\":%d,\"REQ\":%s,\"ACT\": %s}"), + (int)isFault, + requested, + actual); +} + +/////////////////////////////////// Set Domestic Hot Water Temperature ////////////////////////////////////////////////// +unsigned long sns_opentherm_set_boiler_dhw_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status) +{ + // The same consideration as for the boiler temperature + float diff = abs(status->m_hotWaterSetpoint - self->m_results[0].m_float); + // Ignore small changes in the boiler setpoint temperature + if (diff < OPENTHERM_BOILER_SETPOINT_TOLERANCE) + { + return -1; + } + AddLog_P2(LOG_LEVEL_INFO, + PSTR("[OTH]: Setting Hot Water Temp. Old: %d, New: %d"), + (int)self->m_results[0].m_float, + (int)status->m_hotWaterSetpoint); + + self->m_results[0].m_float = status->m_hotWaterSetpoint; + + unsigned int data = OpenTherm::temperatureToData(status->m_hotWaterSetpoint); + return OpenTherm::buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TdhwSet, data); +} +void sns_opentherm_parse_boiler_dhw_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[1].m_float = OpenTherm::getFloat(response); +} +void sns_opentherm_tele_boiler_dhw_temperature(struct OpenThermCommandT *self) +{ + char requested[FLOATSZ]; + dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, requested); + char actual[FLOATSZ]; + dtostrfd(self->m_results[1].m_float, Settings.flag2.temperature_resolution, actual); + + ResponseAppend_P(PSTR("{\"REQ\":%s,\"ACT\": %s}"), + requested, + actual); +} + +/////////////////////////////////// App Specific Fault Flags ////////////////////////////////////////////////// +unsigned long sns_opentherm_get_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *) +{ + return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0); +} + +void sns_opentherm_parse_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + uint8_t fault_code = (response >> 8) & 0xFF; + uint8_t oem_fault_code = response & 0xFF; + boilerStatus->m_fault_code = fault_code; + boilerStatus->m_oem_fault_code = fault_code; + self->m_results[0].m_u8 = fault_code; + self->m_results[1].m_u8 = oem_fault_code; +} + +void sns_opentherm_tele_flags(struct OpenThermCommandT *self) +{ + ResponseAppend_P(PSTR("{\"FC\":%d,\"OFC\":%d}"), + (int)self->m_results[0].m_u8, + (int)self->m_results[1].m_u8); +} + +/////////////////////////////////// OEM Diag Code ////////////////////////////////////////////////// +unsigned long sns_opentherm_get_oem_diag(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *) +{ + return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::OEMDiagnosticCode, 0); +} + +void sns_opentherm_parse_oem_diag(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + uint16_t diag_code = (uint16_t)response & 0xFFFF; + boilerStatus->m_oem_diag_code = diag_code; + self->m_results[0].m_u16 = diag_code; +} + +void sns_opentherm_tele_oem_diag(struct OpenThermCommandT *self) +{ + ResponseAppend_P(PSTR("%d"), (int)self->m_results[0].m_u16); +} + +/////////////////////////////////// Generic Single Float ///////////////////////////////////////////////// +unsigned long sns_opentherm_get_generic_float(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *) +{ + return OpenTherm::buildRequest(OpenThermRequestType::READ, (OpenThermMessageID)self->m_command_code, 0); +} + +void sns_opentherm_parse_generic_float(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[0].m_float = OpenTherm::getFloat(response); +} + +void sns_opentherm_tele_generic_float(struct OpenThermCommandT *self) +{ + char str[FLOATSZ]; + dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, str); + ResponseAppend_P(PSTR("%s"), str); +} + +/////////////////////////////////// Specific Floats Rerports to the ///////////////////////////////////////////////// +void sns_opentherm_parse_dhw_setpoint(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[0].m_float = OpenTherm::getFloat(response); + boilerStatus->m_hotWaterSetpoint_read = self->m_results[0].m_float; +} + +void sns_opentherm_parse_flame_modulation(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[0].m_float = OpenTherm::getFloat(response); + boilerStatus->m_flame_modulation_read = self->m_results[0].m_float; +} + +void sns_opentherm_parse_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[0].m_float = OpenTherm::getFloat(response); + boilerStatus->m_boiler_temperature_read = self->m_results[0].m_float; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SNS_OT_COMMANDS_COUNT (sizeof(sns_opentherm_commands) / sizeof(OpenThermCommand)) +int sns_opentherm_current_command = SNS_OT_COMMANDS_COUNT; + +unsigned long sns_opentherm_get_next_request(struct OT_BOILER_STATUS_T *boilerStatus) +{ + // get next and loop the command + if (++sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT) + { + sns_opentherm_current_command = 0; + } + + struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command]; + // Return error if command known as not supported + if (cmd->m_flags.notSupported) + { + return -1; + } + // Retrurn OT compatible request + return cmd->m_ot_make_request(cmd, boilerStatus); +} + +void sns_opentherm_check_retry_request() +{ + if (sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT) + { + return; + } + struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command]; + + bool canRetry = ++cmd->m_flags.retryCount < 3; + // In case of last retry and if this command never respond successfully, set notSupported flag + if (!canRetry && !cmd->m_flags.supported) + { + cmd->m_flags.notSupported = true; + AddLog_P2(LOG_LEVEL_ERROR, + PSTR("[OTH]: command %s is not supported by the boiler. Last status: %s"), + cmd->m_command_name, + sns_ot_master->statusToString(sns_ot_master->getLastResponseStatus())); + } +} + +void sns_opentherm_process_success_response(struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + if (sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT) + { + return; + } + struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command]; + // mark command as supported + cmd->m_flags.supported = true; + + cmd->m_ot_parse_response(cmd, boilerStatus, response); +} + +void sns_opentherm_dump_telemetry() +{ + bool add_coma = false; + for (int i = 0; i < SNS_OT_COMMANDS_COUNT; ++i) + { + struct OpenThermCommandT *cmd = &sns_opentherm_commands[i]; + if (!cmd->m_flags.supported) + { + continue; + } + + ResponseAppend_P(PSTR("%s\"%s\":"), add_coma ? "," : "", cmd->m_command_name); + + cmd->m_ot_appent_telemetry(cmd); + + add_coma = true; + } +} +#endif \ No newline at end of file diff --git a/tools/decode-status.py b/tools/decode-status.py index 2450d1fb5..c1e42b515 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -126,7 +126,7 @@ a_setoption = [[ "Enable Weekend Energy Tariff", "Select different Modbus registers for Active Energy", "Enable hardware energy total counter as reference", - "Enable HTTP CORS", + "Detach buttons from relays and enable MQTT action state for multipress", "Enable internal pullup for single DS18x20 sensor", "GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1)", "Enable incrementing bootcount when deepsleep is enabled", @@ -146,7 +146,7 @@ a_setoption = [[ "Distinct MQTT topics per device for Zigbee", "Disable non-json MQTT response", "Enable light fading at start/power on", - "","", + "Set PWM Mode from regular PWM to ColorTemp control","", "","","","", "","","","", "","","","", @@ -202,12 +202,12 @@ a_features = [[ ],[ "USE_KEELOQ","USE_HRXL","USE_SONOFF_D1","USE_HDC1080", "USE_IAQ","USE_DISPLAY_SEVENSEG","USE_AS3935","USE_PING", + "USE_WINDMETER","USE_OPENTHERM","","", "","","","", "","","","", "","","","", "","","","", - "","","","", - "","","","" + "","","","USE_WEBCAM" ]] usage = "usage: decode-status {-d | -f} arg" @@ -241,7 +241,7 @@ else: obj = json.load(fp) def StartDecode(): - print ("\n*** decode-status.py v20200411 by Theo Arends and Jacek Ziolkowski ***") + print ("\n*** decode-status.py v20200507 by Theo Arends and Jacek Ziolkowski ***") # print("Decoding\n{}".format(obj))