diff --git a/.github/workflows/Tasmota_build.yml b/.github/workflows/Tasmota_build.yml new file mode 100644 index 000000000..ee4bd9b21 --- /dev/null +++ b/.github/workflows/Tasmota_build.yml @@ -0,0 +1,1502 @@ +name: Build_firmware + +on: + push: + branches: development + +jobs: + tasmota_pull: + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v1 + - name: Use latest Tasmota development + run: | + git config --local user.name "Platformio BUILD" + git switch -c master + git remote add -f Tasmota "https://github.com/arendst/Tasmota.git" + git merge Tasmota/development --allow-unrelated-histories + - name: Push Tasmota # Push updates of latest Tasmota development to repo + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: 'development' + force: true + + + tasmota: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-minimal: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-lite: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-knx: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-sensors: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-display: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-ir: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-ircustom: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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-ircustom + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-zbbridge: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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-zbbridge + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + tasmota-BG: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-BR: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-CN: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-CZ: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-DE: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-ES: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-FR: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-GR: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-HE: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-HU: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-IT: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-KO: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-NL: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-PL: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-PT: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-RO: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-RU: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-SE: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-SK: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-TR: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-TW: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota-UK: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32 + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-minimal: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-minimal + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-lite: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-lite + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-webcam: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-webcam + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-knx: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-knx + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-sensors: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-sensors + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-display: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-display + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-ir: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-ir + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-ircustom: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-ircustom + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-BG: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-BG + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-BR: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-BR + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-CN: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-CN + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-CZ: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-CZ + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-DE: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-DE + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-ES: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-ES + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-FR: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-FR + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-GR: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-GR + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-HE: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-HE + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-HU: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-HU + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-IT: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-IT + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-KO: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-KO + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-NL: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-NL + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-PL: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-PL + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-PT: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-PT + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-RO: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-RO + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-RU: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-RU + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-SE: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-SE + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-SK: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-SK + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-TR: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-TR + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-TW: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-TW + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + tasmota32-UK: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + 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 tasmota32-UK + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + + Upload: + needs: [tasmota-UK, tasmota32-ircustom, tasmota32-UK, tasmota32-TW, tasmota32-TR] + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v1 + - uses: actions/download-artifact@v2 + with: + name: firmware + path: ./mv_firmware + - name: Display structure of downloaded files + run: ls -R + working-directory: ./mv_firmware + - name: Move firmware files in sub-folders + run: | + mkdir -p ./firmware/tasmota/languages + mkdir -p ./firmware/tasmota32/languages + mkdir -p ./firmware/tasmota32/ESP32_needed_files/ + [ ! -f ./mv_firmware/tasmota.* ] || mv ./mv_firmware/tasmota.* ./firmware/tasmota/ + [ ! -f ./mv_firmware/tasmota-sensors.* ] || mv ./mv_firmware/tasmota-sensors.* ./firmware/tasmota/ + [ ! -f ./mv_firmware/tasmota-minimal.* ] || mv ./mv_firmware/tasmota-minimal.* ./firmware/tasmota/ + [ ! -f ./mv_firmware/tasmota-lite.* ] || mv ./mv_firmware/tasmota-lite.* ./firmware/tasmota/ + [ ! -f ./mv_firmware/tasmota-ir*.* ] || mv ./mv_firmware/tasmota-ir*.* ./firmware/tasmota/ + [ ! -f ./mv_firmware/tasmota-display.* ] || mv ./mv_firmware/tasmota-display.* ./firmware/tasmota/ + [ ! -f ./mv_firmware/tasmota-knx.* ] || mv ./mv_firmware/tasmota-knx.* ./firmware/tasmota/ + [ ! -f ./mv_firmware/tasmota-zbbridge.* ] || mv ./mv_firmware/tasmota-zbbridge.* ./firmware/tasmota/ + [ ! -f ./mv_firmware/tasmota32.* ] || mv ./mv_firmware/tasmota32.* ./firmware/tasmota32/ + [ ! -f ./mv_firmware/tasmota32-sensors.* ] || mv ./mv_firmware/tasmota32-sensors.* ./firmware/tasmota32/ + [ ! -f ./mv_firmware/tasmota32-minimal.* ] || mv ./mv_firmware/tasmota32-minimal.* ./firmware/tasmota32/ + [ ! -f ./mv_firmware/tasmota32-lite.* ] || mv ./mv_firmware/tasmota32-lite.* ./firmware/tasmota32/ + [ ! -f ./mv_firmware/tasmota32-ir*.* ] || mv ./mv_firmware/tasmota32-ir*.* ./firmware/tasmota32/ + [ ! -f ./mv_firmware/tasmota32-display.* ] || mv ./mv_firmware/tasmota32-display.* ./firmware/tasmota32/ + [ ! -f ./mv_firmware/tasmota32-web*.* ] || mv ./mv_firmware/tasmota32-web*.* ./firmware/tasmota32/ + [ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/ + [ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/ + [ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/ + [ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/ + [ ! -f ./FIRMWARE.md ] || mv -f ./FIRMWARE.md ./README.md + - name: Commit files # transfer the new binaries back into the repository + run: | + git config --local user.name "Platformio BUILD" + git rm -r --cached . + git add ./README.md + git add -f ./firmware/*.* + git commit -m "Tasmota ESP Binaries http://tasmota.com" + - name: Push changes # push the firmware files to branch firmware + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: 'firmware' + force: true diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile new file mode 100644 index 000000000..909bcf681 --- /dev/null +++ b/.gitpod.Dockerfile @@ -0,0 +1,5 @@ +FROM gitpod/workspace-full + +USER gitpod + +RUN pip3 install -U platformio && brew install uncrustify diff --git a/.gitpod.yml b/.gitpod.yml index 8ac16a8ad..228c1dbf3 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,3 +1,13 @@ tasks: - - before: pip3 install -U platformio - command: platformio run -e tasmota + - command: platformio run -e tasmota + +image: + file: .gitpod.Dockerfile + +vscode: + extensions: + - ms-vscode.cpptools@0.26.3:u3GsZ5PK12Ddr79vh4TWgQ== + - eamodio.gitlens@10.2.1:e0IYyp0efFqVsrZwsIe8CA== + - LaurentTreguier.uncrustify@2.18.0:/k8Osjj/XSuz09F+pEu7wg== + - Atishay-Jain.All-Autocomplete@0.0.23:fbZNfSpnd8XkAHGfAPS2rA== + - 2gua.rainbow-brackets@0.0.6:Tbu8dTz0i+/bgcKQTQ5b8g== diff --git a/I2CDEVICES.md b/I2CDEVICES.md index 5d60c5fb4..71ceba8ac 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -75,4 +75,5 @@ Index | Define | Driver | Device | Address(es) | Description 50 | USE_VEML7700 | xsns_71 | VEML7700 | 0x10 | Ambient light intensity sensor 51 | USE_MCP9808 | xsns_72 | MCP9808 | 0x18 - 0x1F | Temperature sensor 52 | USE_HP303B | xsns_73 | HP303B | 0x76 - 0x77 | Pressure and temperature sensor - 53 | USE_MLX90640 | xdrv_84 | MLX90640 | 0x33 | IR array temperature sensor \ No newline at end of file + 53 | USE_MLX90640 | xdrv_84 | MLX90640 | 0x33 | IR array temperature sensor + 54 | USE_VL53L1X | xsns_77 | VL53L1X | 0x29 | Time-of-flight (ToF) distance sensor diff --git a/README.md b/README.md index 0bae6a0ef..4eb2a88a7 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ _Written for PlatformIO with limited support for Arduino IDE._ [![Chat](https://img.shields.io/discord/479389167382691863.svg)](https://discord.gg/Ks2Kzd4) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/arendst/Tasmota.svg)](http://isitmaintained.com/project/arendst/Tasmota "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/arendst/Tasmota.svg)](http://isitmaintained.com/project/arendst/Tasmota "Percentage of issues still open") +[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/arendst/Tasmota) If you like **Tasmota**, give it a star, or fork it and contribute! diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1cfdd3275..29ad008f8 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -60,9 +60,18 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota - Fix energy total counters (#9263, #9266) - Fix crash in ``ZbRestore`` - Fix reset BMP sensors when executing command ``SaveData`` and define USE_DEEPSLEEP enabled (#9300) +- Fix ``status 0`` message when using define USE_MQTT_TLS due to small log buffer (#9305) +- Fix ``status 13`` exception 9 when more than one shutter is configured +- Fix ``status 13`` json message +- Fix Shelly 2.5 higher temperature regression from 8.2.0.1 (#7991) +- Change replace ArduinoJson with JSMN for JSON parsing +- Change ``WakeUp`` uses 256 steps instead of 100 (#9241) - Add command ``SetOption110 1`` to disable Zigbee auto-config when pairing new devices - Add command ``SetOption111 1`` to enable frequency output for buzzer GPIO (#8994) +- Add command ``SetOption112 1`` to enable friendly name in zigbee topic (use with SetOption89) - Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication +- Add ``#define MQTT_LWT_OFFLINE`` and ``#define MQTT_LWT_ONLINE`` to user_config.h (#9395) - Add new shutter modes (#9244) - Add Zigbee auto-config when pairing - Add support for MLX90640 IR array temperature sensor by Christian Baars +- Add support for VL53L1X time of flight sensor by Johann Obermeier diff --git a/lib/ArduinoJson-5.13.4/ArduinoJson.h b/lib/ArduinoJson-5.13.4/ArduinoJson.h deleted file mode 100644 index 9f78b9f18..000000000 --- a/lib/ArduinoJson-5.13.4/ArduinoJson.h +++ /dev/null @@ -1,5 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#include "src/ArduinoJson.h" diff --git a/lib/ArduinoJson-5.13.4/CHANGELOG.md b/lib/ArduinoJson-5.13.4/CHANGELOG.md deleted file mode 100644 index 3616b176a..000000000 --- a/lib/ArduinoJson-5.13.4/CHANGELOG.md +++ /dev/null @@ -1,483 +0,0 @@ -ArduinoJson: change log -======================= - -v5.13.4 -------- - -* Removed spurious files in the Particle library - -v5.13.3 -------- - -* Improved float serialization when `-fsingle-precision-constant` is used -* Fixed `JsonVariant::is()` that returned true for empty strings -* Fixed `JsonVariant::is()` (closes #763) - -v5.13.2 -------- - -* Fixed `JsonBuffer::parse()` not respecting nesting limit correctly (issue #693) -* Fixed inconsistencies in nesting level counting (PR #695 from Zhenyu Wu) -* Fixed null values that could be pass to `strcmp()` (PR #745 from Mike Karlesky) -* Added macros `ARDUINOJSON_VERSION`, `ARDUINOJSON_VERSION_MAJOR`... - -v5.13.1 -------- - -* Fixed `JsonVariant::operator|(int)` that returned the default value if the variant contained a double (issue #675) -* Allowed non-quoted key to contain underscores (issue #665) - -v5.13.0 -------- - -* Changed the rules of string duplication (issue #658) -* `RawJson()` accepts any kind of string and obeys to the same rules for duplication -* Changed the return type of `strdup()` to `const char*` to prevent double duplication -* Marked `strdup()` as deprecated - -> ### New rules for string duplication -> -> | type | duplication | -> |:---------------------------|:------------| -> | const char* | no | -> | char* | ~~no~~ yes | -> | String | yes | -> | std::string | yes | -> | const __FlashStringHelper* | yes | -> -> These new rules make `JsonBuffer::strdup()` useless. - -v5.12.0 -------- - -* Added `JsonVariant::operator|` to return a default value (see below) -* Added a clear error message when compiled as C instead of C++ (issue #629) -* Added detection of MPLAB XC compiler (issue #629) -* Added detection of Keil ARM Compiler (issue #629) -* Added an example that shows how to save and load a configuration file -* Reworked all other examples - -> ### How to use the new feature? -> -> If you have a block like this: -> -> ```c++ -> const char* ssid = root["ssid"]; -> if (!ssid) -> ssid = "default ssid"; -> ``` -> -> You can simplify like that: -> -> ```c++ -> const char* ssid = root["ssid"] | "default ssid"; -> ``` - -v5.11.2 -------- - -* Fixed `DynamicJsonBuffer::clear()` not resetting allocation size (issue #561) -* Fixed incorrect rounding for float values (issue #588) - -v5.11.1 -------- - -* Removed dependency on `PGM_P` as Particle 0.6.2 doesn't define it (issue #546) -* Fixed warning "dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]" -* Fixed warning "floating constant exceeds range of 'float' [-Woverflow]" (issue #544) -* Fixed warning "this statement may fall through" [-Wimplicit-fallthrough=] (issue #539) -* Removed `ARDUINOJSON_DOUBLE_IS_64BITS` as it became useless. -* Fixed too many decimals places in float serialization (issue #543) - -v5.11.0 -------- - -* Made `JsonBuffer` non-copyable (PR #524 by @luisrayas3) -* Added `StaticJsonBuffer::clear()` -* Added `DynamicJsonBuffer::clear()` - -v5.10.1 -------- - -* Fixed IntelliSense errors in Visual Micro (issue #483) -* Fixed compilation in IAR Embedded Workbench (issue #515) -* Fixed reading "true" as a float (issue #516) -* Added `ARDUINOJSON_DOUBLE_IS_64BITS` -* Added `ARDUINOJSON_EMBEDDED_MODE` - -v5.10.0 -------- - -* Removed configurable number of decimal places (issues #288, #427 and #506) -* Changed exponentiation thresholds to `1e7` and `1e-5` (issues #288, #427 and #506) -* `JsonVariant::is()` now returns `true` for integers -* Fixed error `IsBaseOf is not a member of ArduinoJson::TypeTraits` (issue #495) -* Fixed error `forming reference to reference` (issue #495) - -> ### BREAKING CHANGES :warning: -> -> | Old syntax | New syntax | -> |:--------------------------------|:--------------------| -> | `double_with_n_digits(3.14, 2)` | `3.14` | -> | `float_with_n_digits(3.14, 2)` | `3.14f` | -> | `obj.set("key", 3.14, 2)` | `obj["key"] = 3.14` | -> | `arr.add(3.14, 2)` | `arr.add(3.14)` | -> -> | Input | Old output | New output | -> |:----------|:-----------|:-----------| -> | `3.14159` | `3.14` | `3.14159` | -> | `42.0` | `42.00` | `42` | -> | `0.0` | `0.00` | `0` | -> -> | Expression | Old result | New result | -> |:-------------------------------|:-----------|:-----------| -> | `JsonVariant(42).is()` | `true` | `true` | -> | `JsonVariant(42).is()` | `false` | `true` | -> | `JsonVariant(42).is()` | `false` | `true` | - -v5.9.0 ------- - -* Added `JsonArray::remove(iterator)` (issue #479) -* Added `JsonObject::remove(iterator)` -* Renamed `JsonArray::removeAt(size_t)` into `remove(size_t)` -* Renamed folder `include/` to `src/` -* Fixed warnings `floating constant exceeds range of float`and `floating constant truncated to zero` (issue #483) -* Removed `Print` class and converted `printTo()` to a template method (issue #276) -* Removed example `IndentedPrintExample.ino` -* Now compatible with Particle 0.6.1, thanks to Jacob Nite (issue #294 and PR #461 by @foodbag) - -v5.8.4 ------- - -* Added custom implementation of `strtod()` (issue #453) -* Added custom implementation of `strtol()` (issue #465) -* `char` is now treated as an integral type (issue #337, #370) - -v5.8.3 ------- - -* Fixed an access violation in `DynamicJsonBuffer` when memory allocation fails (issue #433) -* Added operators `==` and `!=` for two `JsonVariant`s (issue #436) -* Fixed `JsonVariant::operator[const FlashStringHelper*]` (issue #441) - -v5.8.2 ------- - -* Fixed parsing of comments (issue #421) -* Fixed ignored `Stream` timeout (issue #422) -* Made sure we don't read more that necessary (issue #422) -* Fixed error when the key of a `JsonObject` is a `char[]` (issue #423) -* Reduced code size when using `const` references -* Fixed error with string of type `unsigned char*` (issue #428) -* Added `deprecated` attribute on `asArray()`, `asObject()` and `asString()` (issue #420) - -v5.8.1 ------- - -* Fixed error when assigning a `volatile int` to a `JsonVariant` (issue #415) -* Fixed errors with Variable Length Arrays (issue #416) -* Fixed error when both `ARDUINOJSON_ENABLE_STD_STREAM` and `ARDUINOJSON_ENABLE_ARDUINO_STREAM` are set to `1` -* Fixed error "Stream does not name a type" (issue #412) - -v5.8.0 ------- - -* Added operator `==` to compare `JsonVariant` and strings (issue #402) -* Added support for `Stream` (issue #300) -* Reduced memory consumption by not duplicating spaces and comments - -> ### BREAKING CHANGES :warning: -> -> `JsonBuffer::parseObject()` and `JsonBuffer::parseArray()` have been pulled down to the derived classes `DynamicJsonBuffer` and `StaticJsonBufferBase`. -> -> This means that if you have code like: -> -> ```c++ -> void myFunction(JsonBuffer& jsonBuffer); -> ``` -> -> you need to replace it with one of the following: -> -> ```c++ -> void myFunction(DynamicJsonBuffer& jsonBuffer); -> void myFunction(StaticJsonBufferBase& jsonBuffer); -> template void myFunction(TJsonBuffer& jsonBuffer); -> ``` - -v5.7.3 ------- - -* Added an `printTo(char[N])` and `prettyPrintTo(char[N])` (issue #292) -* Added ability to set a nested value like this: `root["A"]["B"] = "C"` (issue #352) -* Renamed `*.ipp` to `*Impl.hpp` because they were ignored by Arduino IDE (issue #396) - -v5.7.2 ------- - -* Made PROGMEM available on more platforms (issue #381) -* Fixed PROGMEM causing an exception on ESP8266 (issue #383) - -v5.7.1 ------- - -* Added support for PROGMEM (issue #76) -* Fixed compilation error when index is not an `int` (issue #381) - -v5.7.0 ------- - -* Templatized all functions using `String` or `std::string` -* Removed `ArduinoJson::String` -* Removed `JsonVariant::defaultValue()` -* Removed non-template `JsonObject::get()` and `JsonArray.get()` -* Fixed support for `StringSumHelper` (issue #184) -* Replaced `ARDUINOJSON_USE_ARDUINO_STRING` by `ARDUINOJSON_ENABLE_STD_STRING` and `ARDUINOJSON_ENABLE_ARDUINO_STRING` (issue #378) -* Added example `StringExample.ino` to show where `String` can be used -* Increased default nesting limit to 50 when compiled for a computer (issue #349) - -> ### BREAKING CHANGES :warning: -> -> The non-template functions `JsonObject::get()` and `JsonArray.get()` have been removed. This means that you need to explicitely tell the type you expect in return. -> -> Old code: -> -> ```c++ -> #define ARDUINOJSON_USE_ARDUINO_STRING 0 -> JsonVariant value1 = myObject.get("myKey"); -> JsonVariant value2 = myArray.get(0); -> ``` -> -> New code: -> -> ```c++ -> #define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 -> #define ARDUINOJSON_ENABLE_STD_STRING 1 -> JsonVariant value1 = myObject.get("myKey"); -> JsonVariant value2 = myArray.get(0); -> ``` - -v5.6.7 ------- - -* Fixed `array[idx].as()` and `object[key].as()` -* Fixed return value of `JsonObject::set()` (issue #350) -* Fixed undefined behavior in `Prettyfier` and `Print` (issue #354) -* Fixed parser that incorrectly rejected floats containing a `+` (issue #349) - -v5.6.6 ------- - -* Fixed `-Wparentheses` warning introduced in v5.6.5 (PR #335 by @nuket) -* Added `.mbedignore` for ARM mbdeb (PR #334 by @nuket) -* Fixed `JsonVariant::success()` which didn't propagate `JsonArray::success()` nor `JsonObject::success()` (issue #342). - -v5.6.5 ------- - -* `as()` now returns `true` when input is `null` (issue #330) - -v5.6.4 ------- - -* Fixed error in float serialization (issue #324) - -v5.6.3 ------- - -* Improved speed of float serialization (about twice faster) -* Added `as()` as a synonym for `as()`... (issue #291) -* Fixed `call of overloaded isinf(double&) is ambiguous` (issue #284) - -v5.6.2 ------- - -* Fixed build when another lib does `#undef isnan` (issue #284) - -v5.6.1 ------- - -* Added missing `#pragma once` (issue #310) - -v5.6.0 ------- - -* ArduinoJson is now a header-only library (issue #199) - -v5.5.1 ------- - -* Fixed compilation error with Intel Galileo (issue #299) - -v5.5.0 ------- - -* Added `JsonVariant::success()` (issue #279) -* Renamed `JsonVariant::invalid()` to `JsonVariant::defaultValue()` - -v5.4.0 ------- - -* Changed `::String` to `ArduinoJson::String` (issue #275) -* Changed `::Print` to `ArduinoJson::Print` too - -v5.3.0 ------- - -* Added custom implementation of `ftoa` (issues #266, #267, #269 and #270) -* Added `JsonVariant JsonBuffer::parse()` (issue #265) -* Fixed `unsigned long` printed as `signed long` (issue #170) - -v5.2.0 ------- - -* Added `JsonVariant::as()` as a synonym for `JsonVariant::as()` (issue #257) -* Added example `JsonHttpClient` (issue #256) -* Added `JsonArray::copyTo()` and `JsonArray::copyFrom()` (issue #254) -* Added `RawJson()` to insert pregenerated JSON portions (issue #259) - -v5.1.1 ------- - -* Removed `String` duplication when one replaces a value in a `JsonObject` (PR #232 by @ulion) - -v5.1.0 ------- - -* Added support of `long long` (issue #171) -* Moved all build settings to `ArduinoJson/Configuration.hpp` - -> ### BREAKING CHANGE :warning: -> -> If you defined `ARDUINOJSON_ENABLE_STD_STREAM`, you now need to define it to `1`. - -v5.0.8 ------- - -* Made the library compatible with [PlatformIO](http://platformio.org/) (issue #181) -* Fixed `JsonVariant::is()` that was incorrectly returning false (issue #214) - -v5.0.7 ------- - -* Made library easier to use from a CMake project: simply `add_subdirectory(ArduinoJson/src)` -* Changed `String` to be a `typedef` of `std::string` (issues #142 and #161) - -> ### BREAKING CHANGES :warning: -> -> - `JsonVariant(true).as()` now returns `"true"` instead of `"1"` -> - `JsonVariant(false).as()` now returns `"false"` instead of `"0"` - -v5.0.6 ------- - -* Added parameter to `DynamicJsonBuffer` constructor to set initial size (issue #152) -* Fixed warning about library category in Arduino 1.6.6 (issue #147) -* Examples: Added a loop to wait for serial port to be ready (issue #156) - -v5.0.5 ------- - -* Added overload `JsonObjectSuscript::set(value, decimals)` (issue #143) -* Use `float` instead of `double` to reduce the size of `JsonVariant` (issue #134) - -v5.0.4 ------- - -* Fixed ambiguous overload with `JsonArraySubscript` and `JsonObjectSubscript` (issue #122) - -v5.0.3 ------- - -* Fixed `printTo(String)` which wrote numbers instead of strings (issue #120) -* Fixed return type of `JsonArray::is()` and some others (issue #121) - -v5.0.2 ------- - -* Fixed segmentation fault in `parseObject(String)` and `parseArray(String)`, when the - `StaticJsonBuffer` is too small to hold a copy of the string -* Fixed Clang warning "register specifier is deprecated" (issue #102) -* Fixed GCC warning "declaration shadows a member" (issue #103) -* Fixed memory alignment, which made ESP8266 crash (issue #104) -* Fixed compilation on Visual Studio 2010 and 2012 (issue #107) - -v5.0.1 ------- - -* Fixed compilation with Arduino 1.0.6 (issue #99) - -v5.0.0 ------- - -* Added support of `String` class (issues #55, #56, #70, #77) -* Added `JsonBuffer::strdup()` to make a copy of a string (issues #10, #57) -* Implicitly call `strdup()` for `String` but not for `char*` (issues #84, #87) -* Added support of non standard JSON input (issue #44) -* Added support of comments in JSON input (issue #88) -* Added implicit cast between numerical types (issues #64, #69, #93) -* Added ability to read number values as string (issue #90) -* Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators (issue #66) -* Switched to new the library layout (requires Arduino 1.0.6 or above) - -> ### BREAKING CHANGES :warning: -> -> - `JsonObject::add()` was renamed to `set()` -> - `JsonArray::at()` and `JsonObject::at()` were renamed to `get()` -> - Number of digits of floating point value are now set with `double_with_n_digits()` - -**Personal note about the `String` class**: -Support of the `String` class has been added to the library because many people use it in their programs. -However, you should not see this as an invitation to use the `String` class. -The `String` class is **bad** because it uses dynamic memory allocation. -Compared to static allocation, it compiles to a bigger, slower program, and is less predictable. -You certainly don't want that in an embedded environment! - -v4.6 ----- - -* Fixed segmentation fault in `DynamicJsonBuffer` when memory allocation fails (issue #92) - -v4.5 ----- - -* Fixed buffer overflow when input contains a backslash followed by a terminator (issue #81) - -**Upgrading is recommended** since previous versions contain a potential security risk. - -Special thanks to [Giancarlo Canales Barreto](https://github.com/gcanalesb) for finding this nasty bug. - -v4.4 ----- - -* Added `JsonArray::measureLength()` and `JsonObject::measureLength()` (issue #75) - -v4.3 ----- - -* Added `JsonArray::removeAt()` to remove an element of an array (issue #58) -* Fixed stack-overflow in `DynamicJsonBuffer` when parsing huge JSON files (issue #65) -* Fixed wrong return value of `parseArray()` and `parseObject()` when allocation fails (issue #68) - -v4.2 ----- - -* Switched back to old library layout (issues #39, #43 and #45) -* Removed global new operator overload (issue #40, #45 and #46) -* Added an example with EthernetServer - -v4.1 ----- - -* Added DynamicJsonBuffer (issue #19) - -v4.0 ----- - -* Unified parser and generator API (issue #23) -* Updated library layout, now requires Arduino 1.0.6 or newer - -> ### BREAKING CHANGES :warning: -> -> API changed significantly since v3, see [Migrating code to the new API](https://arduinojson.org/doc/migration/). - diff --git a/lib/ArduinoJson-5.13.4/LICENSE.md b/lib/ArduinoJson-5.13.4/LICENSE.md deleted file mode 100644 index f0c4b5ae7..000000000 --- a/lib/ArduinoJson-5.13.4/LICENSE.md +++ /dev/null @@ -1,10 +0,0 @@ -The MIT License (MIT) ---------------------- - -Copyright © 2014-2018 Benoit BLANCHON - -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/ArduinoJson-5.13.4/README.md b/lib/ArduinoJson-5.13.4/README.md deleted file mode 100644 index 8ddc698fc..000000000 --- a/lib/ArduinoJson-5.13.4/README.md +++ /dev/null @@ -1,110 +0,0 @@ -![ArduinoJson](banner.svg) - ---- - -[![Build status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/master?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/master) [![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=master)](https://travis-ci.org/bblanchon/ArduinoJson) [![Coverage Status](https://img.shields.io/coveralls/bblanchon/ArduinoJson.svg)](https://coveralls.io/r/bblanchon/ArduinoJson?branch=master) [![Star this project](http://githubbadges.com/star.svg?user=bblanchon&repo=ArduinoJson&style=flat&color=fff&background=007ec6)](https://github.com/bblanchon/ArduinoJson) - -ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things). - -## Features - -* JSON decoding (comments are supported) -* JSON encoding (with optional indentation) -* Elegant API, easy to use -* Fixed memory allocation (zero malloc) -* No data duplication (zero copy) -* Portable (written in C++98, can be used in any C++ project) -* Self-contained (no external dependency) -* Small footprint -* Input and output streams -* [100% code coverage](https://coveralls.io/github/bblanchon/ArduinoJson) -* [Header-only library](https://en.wikipedia.org/wiki/Header-only) -* [MIT License](https://en.wikipedia.org/wiki/MIT_License) -* [Comprehensive documentation](https://arduinojson.org?utm_source=github&utm_medium=readme) - -## Compatibility - -ArduinoJson works on the following hardware: - -* Arduino boards: [Uno](https://www.arduino.cc/en/Main/ArduinoBoardUno), [Due](https://www.arduino.cc/en/Main/ArduinoBoardDue), [Mini](https://www.arduino.cc/en/Main/ArduinoBoardMini), [Micro](https://www.arduino.cc/en/Main/ArduinoBoardMicro), [Yun](https://www.arduino.cc/en/Main/ArduinoBoardYun)... -* Espressif chips: [ESP8266](https://en.wikipedia.org/wiki/ESP8266), [ESP32](https://en.wikipedia.org/wiki/ESP32) -* WeMos boards: [D1](https://wiki.wemos.cc/products:d1:d1), [D1 mini](https://wiki.wemos.cc/products:d1:d1_mini), ... -* RedBearLab boards: [BLE Nano](http://redbearlab.com/blenano/), [BLE Mini](http://redbearlab.com/blemini/), [WiFi Micro](https://redbear.cc/product/wifi/wifi-micro.html), [LOLIN32](https://wiki.wemos.cc/products:lolin32:lolin32)... -* [Teensy](https://www.pjrc.com/teensy/) boards -* Intel boards: Edison, Galileo... -* Particle boards: [Photon](https://www.particle.io/products/hardware/photon-wifi-dev-kit), [Electron](https://www.particle.io/products/hardware/electron-cellular-dev-kit)... -* Texas Instruments boards: [MSP430](http://www.ti.com/microcontrollers/msp430-ultra-low-power-mcus/overview/overview.html)... - -ArduinoJson compiles with zero warning on the following compilers, IDEs, and platforms: - -* [Arduino IDE](https://www.arduino.cc/en/Main/Software) -* [PlatformIO](http://platformio.org/) -* [Energia](http://energia.nu/) -* [Visual Micro](http://www.visualmicro.com/) -* [Atmel Studio](http://www.atmel.com/microsite/atmel-studio/) -* [IAR Embedded Workbench](https://www.iar.com/iar-embedded-workbench/) -* [Atollic TrueSTUDIO](https://atollic.com/truestudio/) -* [Keil uVision](http://www.keil.com/) -* [MPLAB X IDE](http://www.microchip.com/mplab/mplab-x-ide) -* [GCC](https://gcc.gnu.org/) -* [Clang](https://clang.llvm.org/) -* [Visual Studio](https://www.visualstudio.com/) - -## Quickstart - -### Deserialization - -Here is a program that parses a JSON document with ArduinoJson. - -```c++ -char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; - -StaticJsonBuffer<200> jsonBuffer; - -JsonObject& root = jsonBuffer.parseObject(json); - -const char* sensor = root["sensor"]; -long time = root["time"]; -double latitude = root["data"][0]; -double longitude = root["data"][1]; -``` - -See the [tutorial on arduinojson.org](https://arduinojson.org/doc/decoding/?utm_source=github&utm_medium=readme) - -### Serialization - -Here is a program that generates a JSON document with ArduinoJson: - -```c++ -StaticJsonBuffer<200> jsonBuffer; - -JsonObject& root = jsonBuffer.createObject(); -root["sensor"] = "gps"; -root["time"] = 1351824120; - -JsonArray& data = root.createNestedArray("data"); -data.add(48.756080); -data.add(2.302038); - -root.printTo(Serial); -// This prints: -// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} -``` - -See the [tutorial on arduinojson.org](https://arduinojson.org/doc/encoding/?utm_source=github&utm_medium=readme) - -## Documentation - -The documentation is available on [arduinojson.org](https://arduinojson.org/?utm_source=github&utm_medium=readme), here are some shortcuts: - -* The [Examples](https://arduinojson.org/example/?utm_source=github&utm_medium=readme) show how to use the library in various situations. -* The [API Reference](https://arduinojson.org/api/?utm_source=github&utm_medium=readme) contains the description of each class and function. -* The [FAQ](https://arduinojson.org/faq/?utm_source=github&utm_medium=readme) has the answer to virtually every question. -* The [ArduinoJson Assistant](https://arduinojson.org/assistant/?utm_source=github&utm_medium=readme) writes programs for you! - ---- - -Do you like this library? Please [star this project on GitHub](https://github.com/bblanchon/ArduinoJson/stargazers)! - -What? You don't like it but you *love* it? -We don't take donations anymore, but [we sell a book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme), so you can help and learn at the same time! \ No newline at end of file diff --git a/lib/ArduinoJson-5.13.4/examples/JsonConfigFile/JsonConfigFile.ino b/lib/ArduinoJson-5.13.4/examples/JsonConfigFile/JsonConfigFile.ino deleted file mode 100644 index 2ccf7d673..000000000 --- a/lib/ArduinoJson-5.13.4/examples/JsonConfigFile/JsonConfigFile.ino +++ /dev/null @@ -1,144 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License -// -// This example shows how to store your project configuration in a file. -// It uses the SD library but can be easily modified for any other file-system. -// -// The file contains a JSON document with the following content: -// { -// "hostname": "examples.com", -// "port": 2731 -// } - -#include -#include -#include - -// Configuration that we'll store on disk -struct Config { - char hostname[64]; - int port; -}; - -const char *filename = "/config.txt"; // <- SD library uses 8.3 filenames -Config config; // <- global configuration object - -// Loads the configuration from a file -void loadConfiguration(const char *filename, Config &config) { - // Open file for reading - File file = SD.open(filename); - - // Allocate the memory pool on the stack. - // Don't forget to change the capacity to match your JSON document. - // Use arduinojson.org/assistant to compute the capacity. - StaticJsonBuffer<512> jsonBuffer; - - // Parse the root object - JsonObject &root = jsonBuffer.parseObject(file); - - if (!root.success()) - Serial.println(F("Failed to read file, using default configuration")); - - // Copy values from the JsonObject to the Config - config.port = root["port"] | 2731; - strlcpy(config.hostname, // <- destination - root["hostname"] | "example.com", // <- source - sizeof(config.hostname)); // <- destination's capacity - - // Close the file (File's destructor doesn't close the file) - file.close(); -} - -// Saves the configuration to a file -void saveConfiguration(const char *filename, const Config &config) { - // Delete existing file, otherwise the configuration is appended to the file - SD.remove(filename); - - // Open file for writing - File file = SD.open(filename, FILE_WRITE); - if (!file) { - Serial.println(F("Failed to create file")); - return; - } - - // Allocate the memory pool on the stack - // Don't forget to change the capacity to match your JSON document. - // Use https://arduinojson.org/assistant/ to compute the capacity. - StaticJsonBuffer<256> jsonBuffer; - - // Parse the root object - JsonObject &root = jsonBuffer.createObject(); - - // Set the values - root["hostname"] = config.hostname; - root["port"] = config.port; - - // Serialize JSON to file - if (root.printTo(file) == 0) { - Serial.println(F("Failed to write to file")); - } - - // Close the file (File's destructor doesn't close the file) - file.close(); -} - -// Prints the content of a file to the Serial -void printFile(const char *filename) { - // Open file for reading - File file = SD.open(filename); - if (!file) { - Serial.println(F("Failed to read file")); - return; - } - - // Extract each characters by one by one - while (file.available()) { - Serial.print((char)file.read()); - } - Serial.println(); - - // Close the file (File's destructor doesn't close the file) - file.close(); -} - -void setup() { - // Initialize serial port - Serial.begin(9600); - while (!Serial) continue; - - // Initialize SD library - while (!SD.begin()) { - Serial.println(F("Failed to initialize SD library")); - delay(1000); - } - - // Should load default config if run for the first time - Serial.println(F("Loading configuration...")); - loadConfiguration(filename, config); - - // Create configuration file - Serial.println(F("Saving configuration...")); - saveConfiguration(filename, config); - - // Dump config file - Serial.println(F("Print config file...")); - printFile(filename); -} - -void loop() { - // not used in this example -} - -// See also -// -------- -// -// https://arduinojson.org/ contains the documentation for all the functions -// used above. It also includes an FAQ that will help you solve any -// serialization or deserialization problem. -// -// The book "Mastering ArduinoJson" contains a case study of a project that has -// a complex configuration with nested members. -// Contrary to this example, the project in the book uses the SPIFFS filesystem. -// Learn more at https://arduinojson.org/book/ -// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/examples/JsonGeneratorExample/JsonGeneratorExample.ino b/lib/ArduinoJson-5.13.4/examples/JsonGeneratorExample/JsonGeneratorExample.ino deleted file mode 100644 index 7b38227b3..000000000 --- a/lib/ArduinoJson-5.13.4/examples/JsonGeneratorExample/JsonGeneratorExample.ino +++ /dev/null @@ -1,81 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License -// -// This example shows how to generate a JSON document with ArduinoJson. - -#include - -void setup() { - // Initialize Serial port - Serial.begin(9600); - while (!Serial) continue; - - // Memory pool for JSON object tree. - // - // Inside the brackets, 200 is the size of the pool in bytes. - // Don't forget to change this value to match your JSON document. - // Use arduinojson.org/assistant to compute the capacity. - StaticJsonBuffer<200> jsonBuffer; - - // StaticJsonBuffer allocates memory on the stack, it can be - // replaced by DynamicJsonBuffer which allocates in the heap. - // - // DynamicJsonBuffer jsonBuffer(200); - - // Create the root of the object tree. - // - // It's a reference to the JsonObject, the actual bytes are inside the - // JsonBuffer with all the other nodes of the object tree. - // Memory is freed when jsonBuffer goes out of scope. - JsonObject& root = jsonBuffer.createObject(); - - // Add values in the object - // - // Most of the time, you can rely on the implicit casts. - // In other case, you can do root.set("time", 1351824120); - root["sensor"] = "gps"; - root["time"] = 1351824120; - - // Add a nested array. - // - // It's also possible to create the array separately and add it to the - // JsonObject but it's less efficient. - JsonArray& data = root.createNestedArray("data"); - data.add(48.756080); - data.add(2.302038); - - root.printTo(Serial); - // This prints: - // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} - - Serial.println(); - - root.prettyPrintTo(Serial); - // This prints: - // { - // "sensor": "gps", - // "time": 1351824120, - // "data": [ - // 48.756080, - // 2.302038 - // ] - // } -} - -void loop() { - // not used in this example -} - -// See also -// -------- -// -// https://arduinojson.org/ contains the documentation for all the functions -// used above. It also includes an FAQ that will help you solve any -// serialization problem. -// -// The book "Mastering ArduinoJson" contains a tutorial on serialization. -// It begins with a simple example, like the one above, and then adds more -// features like serializing directly to a file or an HTTP request. -// Learn more at https://arduinojson.org/book/ -// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/examples/JsonHttpClient/JsonHttpClient.ino b/lib/ArduinoJson-5.13.4/examples/JsonHttpClient/JsonHttpClient.ino deleted file mode 100644 index 4ce1c20d1..000000000 --- a/lib/ArduinoJson-5.13.4/examples/JsonHttpClient/JsonHttpClient.ino +++ /dev/null @@ -1,112 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License -// -// This example shows how to parse a JSON document in an HTTP response. -// It uses the Ethernet library, but can be easily adapted for Wifi. -// -// It performs a GET resquest on arduinojson.org/example.json -// Here is the expected response: -// { -// "sensor": "gps", -// "time": 1351824120, -// "data": [ -// 48.756080, -// 2.302038 -// ] -// } - -#include -#include -#include - -void setup() { - // Initialize Serial port - Serial.begin(9600); - while (!Serial) continue; - - // Initialize Ethernet library - byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; - if (!Ethernet.begin(mac)) { - Serial.println(F("Failed to configure Ethernet")); - return; - } - delay(1000); - - Serial.println(F("Connecting...")); - - // Connect to HTTP server - EthernetClient client; - client.setTimeout(10000); - if (!client.connect("arduinojson.org", 80)) { - Serial.println(F("Connection failed")); - return; - } - - Serial.println(F("Connected!")); - - // Send HTTP request - client.println(F("GET /example.json HTTP/1.0")); - client.println(F("Host: arduinojson.org")); - client.println(F("Connection: close")); - if (client.println() == 0) { - Serial.println(F("Failed to send request")); - return; - } - - // Check HTTP status - char status[32] = {0}; - client.readBytesUntil('\r', status, sizeof(status)); - if (strcmp(status, "HTTP/1.1 200 OK") != 0) { - Serial.print(F("Unexpected response: ")); - Serial.println(status); - return; - } - - // Skip HTTP headers - char endOfHeaders[] = "\r\n\r\n"; - if (!client.find(endOfHeaders)) { - Serial.println(F("Invalid response")); - return; - } - - // Allocate JsonBuffer - // Use arduinojson.org/assistant to compute the capacity. - const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60; - DynamicJsonBuffer jsonBuffer(capacity); - - // Parse JSON object - JsonObject& root = jsonBuffer.parseObject(client); - if (!root.success()) { - Serial.println(F("Parsing failed!")); - return; - } - - // Extract values - Serial.println(F("Response:")); - Serial.println(root["sensor"].as()); - Serial.println(root["time"].as()); - Serial.println(root["data"][0].as()); - Serial.println(root["data"][1].as()); - - // Disconnect - client.stop(); -} - -void loop() { - // not used in this example -} - -// See also -// -------- -// -// https://arduinojson.org/ contains the documentation for all the functions -// used above. It also includes an FAQ that will help you solve any -// serialization problem. -// -// The book "Mastering ArduinoJson" contains a tutorial on deserialization -// showing how to parse the response from Yahoo Weather. In the last chapter, -// it shows how to parse the huge documents from OpenWeatherMap -// and Weather Underground. -// Learn more at https://arduinojson.org/book/ -// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/examples/JsonParserExample/JsonParserExample.ino b/lib/ArduinoJson-5.13.4/examples/JsonParserExample/JsonParserExample.ino deleted file mode 100644 index 6c16211b5..000000000 --- a/lib/ArduinoJson-5.13.4/examples/JsonParserExample/JsonParserExample.ino +++ /dev/null @@ -1,78 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License -// -// This example shows how to deserialize a JSON document with ArduinoJson. - -#include - -void setup() { - // Initialize serial port - Serial.begin(9600); - while (!Serial) continue; - - // Memory pool for JSON object tree. - // - // Inside the brackets, 200 is the size of the pool in bytes. - // Don't forget to change this value to match your JSON document. - // Use arduinojson.org/assistant to compute the capacity. - StaticJsonBuffer<200> jsonBuffer; - - // StaticJsonBuffer allocates memory on the stack, it can be - // replaced by DynamicJsonBuffer which allocates in the heap. - // - // DynamicJsonBuffer jsonBuffer(200); - - // JSON input string. - // - // It's better to use a char[] as shown here. - // If you use a const char* or a String, ArduinoJson will - // have to make a copy of the input in the JsonBuffer. - char json[] = - "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; - - // Root of the object tree. - // - // It's a reference to the JsonObject, the actual bytes are inside the - // JsonBuffer with all the other nodes of the object tree. - // Memory is freed when jsonBuffer goes out of scope. - JsonObject& root = jsonBuffer.parseObject(json); - - // Test if parsing succeeds. - if (!root.success()) { - Serial.println("parseObject() failed"); - return; - } - - // Fetch values. - // - // Most of the time, you can rely on the implicit casts. - // In other case, you can do root["time"].as(); - const char* sensor = root["sensor"]; - long time = root["time"]; - double latitude = root["data"][0]; - double longitude = root["data"][1]; - - // Print values. - Serial.println(sensor); - Serial.println(time); - Serial.println(latitude, 6); - Serial.println(longitude, 6); -} - -void loop() { - // not used in this example -} - -// See also -// -------- -// -// https://arduinojson.org/ contains the documentation for all the functions -// used above. It also includes an FAQ that will help you solve any -// deserialization problem. -// -// The book "Mastering ArduinoJson" contains a tutorial on deserialization. -// It begins with a simple example, like the one above, and then adds more -// features like deserializing directly from a file or an HTTP request. -// Learn more at https://arduinojson.org/book/ -// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/examples/JsonServer/JsonServer.ino b/lib/ArduinoJson-5.13.4/examples/JsonServer/JsonServer.ino deleted file mode 100644 index e693ae176..000000000 --- a/lib/ArduinoJson-5.13.4/examples/JsonServer/JsonServer.ino +++ /dev/null @@ -1,109 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License -// -// This example shows how to implement an HTTP server that sends JSON document -// in the responses. -// It uses the Ethernet library but can be easily adapted for Wifi. -// -// It sends the value of the analog and digital pins. -// The JSON document looks like the following: -// { -// "analog": [ 0, 1, 2, 3, 4, 5 ], -// "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] -// } - -#include -#include -#include - -byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; -EthernetServer server(80); - -void setup() { - // Initialize serial port - Serial.begin(9600); - while (!Serial) continue; - - // Initialize Ethernet libary - if (!Ethernet.begin(mac)) { - Serial.println(F("Failed to initialize Ethernet library")); - return; - } - - // Start to listen - server.begin(); - - Serial.println(F("Server is ready.")); - Serial.print(F("Please connect to http://")); - Serial.println(Ethernet.localIP()); -} - -void loop() { - // Wait for an incomming connection - EthernetClient client = server.available(); - - // Do we have a client? - if (!client) return; - - Serial.println(F("New client")); - - // Read the request (we ignore the content in this example) - while (client.available()) client.read(); - - // Allocate JsonBuffer - // Use arduinojson.org/assistant to compute the capacity. - StaticJsonBuffer<500> jsonBuffer; - - // Create the root object - JsonObject& root = jsonBuffer.createObject(); - - // Create the "analog" array - JsonArray& analogValues = root.createNestedArray("analog"); - for (int pin = 0; pin < 6; pin++) { - // Read the analog input - int value = analogRead(pin); - - // Add the value at the end of the array - analogValues.add(value); - } - - // Create the "digital" array - JsonArray& digitalValues = root.createNestedArray("digital"); - for (int pin = 0; pin < 14; pin++) { - // Read the digital input - int value = digitalRead(pin); - - // Add the value at the end of the array - digitalValues.add(value); - } - - Serial.print(F("Sending: ")); - root.printTo(Serial); - Serial.println(); - - // Write response headers - client.println("HTTP/1.0 200 OK"); - client.println("Content-Type: application/json"); - client.println("Connection: close"); - client.println(); - - // Write JSON document - root.prettyPrintTo(client); - - // Disconnect - client.stop(); -} - -// See also -// -------- -// -// https://arduinojson.org/ contains the documentation for all the functions -// used above. It also includes an FAQ that will help you solve any -// serialization problem. -// -// The book "Mastering ArduinoJson" contains a tutorial on serialization. -// It begins with a simple example, then adds more features like serializing -// directly to a file or an HTTP client. -// Learn more at https://arduinojson.org/book/ -// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/examples/JsonUdpBeacon/JsonUdpBeacon.ino b/lib/ArduinoJson-5.13.4/examples/JsonUdpBeacon/JsonUdpBeacon.ino deleted file mode 100644 index b2328a62d..000000000 --- a/lib/ArduinoJson-5.13.4/examples/JsonUdpBeacon/JsonUdpBeacon.ino +++ /dev/null @@ -1,101 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License -// -// This example shows how to send a JSON document to a UDP socket. -// At regular interval, it sends a UDP packet that contains the status of -// analog and digital pins. -// The JSON document looks like the following: -// { -// "analog": [ 0, 1, 2, 3, 4, 5 ], -// "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] -// } -// -// If you want to test this program, you need to be able to receive the UDP -// packets. -// For example, you can run netcat on your computer -// $ ncat -ulp 8888 -// See https://nmap.org/ncat/ - -#include -#include -#include - -byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; -IPAddress remoteIp(192, 168, 0, 108); // <- EDIT!!!! -unsigned short remotePort = 8888; -unsigned short localPort = 8888; -EthernetUDP udp; - -void setup() { - // Initialize serial port - Serial.begin(9600); - while (!Serial) continue; - - // Initialize Ethernet libary - if (!Ethernet.begin(mac)) { - Serial.println(F("Failed to initialize Ethernet library")); - return; - } - - // Enable UDP - udp.begin(localPort); -} - -void loop() { - // Allocate JsonBuffer - // Use arduinojson.org/assistant to compute the capacity. - StaticJsonBuffer<500> jsonBuffer; - - // Create the root object - JsonObject& root = jsonBuffer.createObject(); - - // Create the "analog" array - JsonArray& analogValues = root.createNestedArray("analog"); - for (int pin = 0; pin < 6; pin++) { - // Read the analog input - int value = analogRead(pin); - - // Add the value at the end of the array - analogValues.add(value); - } - - // Create the "digital" array - JsonArray& digitalValues = root.createNestedArray("digital"); - for (int pin = 0; pin < 14; pin++) { - // Read the digital input - int value = digitalRead(pin); - - // Add the value at the end of the array - digitalValues.add(value); - } - - // Log - Serial.print(F("Sending to ")); - Serial.print(remoteIp); - Serial.print(F(" on port ")); - Serial.println(remotePort); - root.printTo(Serial); - - // Send UDP packet - udp.beginPacket(remoteIp, remotePort); - root.printTo(udp); - udp.println(); - udp.endPacket(); - - // Wait - delay(10000); -} - -// See also -// -------- -// -// https://arduinojson.org/ contains the documentation for all the functions -// used above. It also includes an FAQ that will help you solve any -// serialization problem. -// -// The book "Mastering ArduinoJson" contains a tutorial on serialization. -// It begins with a simple example, then adds more features like serializing -// directly to a file or any stream. -// Learn more at https://arduinojson.org/book/ -// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/examples/ProgmemExample/ProgmemExample.ino b/lib/ArduinoJson-5.13.4/examples/ProgmemExample/ProgmemExample.ino deleted file mode 100644 index ddde8fd1d..000000000 --- a/lib/ArduinoJson-5.13.4/examples/ProgmemExample/ProgmemExample.ino +++ /dev/null @@ -1,70 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License -// -// This example shows the different ways you can use Flash strings with -// ArduinoJson. -// -// Use Flash strings sparingly, because ArduinoJson duplicates them in the -// JsonBuffer. Prefer plain old char*, as they are more efficient in term of -// code size, speed, and memory usage. - -#include - -void setup() { -#ifdef PROGMEM // <- check that Flash strings are supported - - DynamicJsonBuffer jsonBuffer; - - // You can use a Flash String as your JSON input. - // WARNING: the content of the Flash String will be duplicated in the - // JsonBuffer. - JsonObject& root = - jsonBuffer.parseObject(F("{\"sensor\":\"gps\",\"time\":1351824120," - "\"data\":[48.756080,2.302038]}")); - - // You can use a Flash String to get an element of a JsonObject - // No duplication is done. - long time = root[F("time")]; - - // You can use a Flash String to set an element of a JsonObject - // WARNING: the content of the Flash String will be duplicated in the - // JsonBuffer. - root[F("time")] = time; - - // You can set a Flash String to a JsonObject or JsonArray: - // WARNING: the content of the Flash String will be duplicated in the - // JsonBuffer. - root["sensor"] = F("gps"); - - // It works with RawJson too: - root["sensor"] = RawJson(F("\"gps\"")); - - // You can compare the content of a JsonVariant to a Flash String - if (root["sensor"] == F("gps")) { - // ... - } - -#else - -#warning PROGMEM is not supported on this platform - -#endif -} - -void loop() { - // not used in this example -} - -// See also -// -------- -// -// https://arduinojson.org/ contains the documentation for all the functions -// used above. It also includes an FAQ that will help you solve any memory -// problem. -// -// The book "Mastering ArduinoJson" contains a quick C++ course that explains -// how your microcontroller stores strings in memory. It also tells why you -// should not abuse Flash strings with ArduinoJson. -// Learn more at https://arduinojson.org/book/ -// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/examples/StringExample/StringExample.ino b/lib/ArduinoJson-5.13.4/examples/StringExample/StringExample.ino deleted file mode 100644 index fc7503d0e..000000000 --- a/lib/ArduinoJson-5.13.4/examples/StringExample/StringExample.ino +++ /dev/null @@ -1,74 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License -// -// This example shows the different ways you can use String with ArduinoJson. -// -// Use String objects sparingly, because ArduinoJson duplicates them in the -// JsonBuffer. Prefer plain old char[], as they are more efficient in term of -// code size, speed, and memory usage. - -#include - -void setup() { - DynamicJsonBuffer jsonBuffer; - - // You can use a String as your JSON input. - // WARNING: the content of the String will be duplicated in the JsonBuffer. - String input = - "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; - JsonObject& root = jsonBuffer.parseObject(input); - - // You can use a String to get an element of a JsonObject - // No duplication is done. - long time = root[String("time")]; - - // You can use a String to set an element of a JsonObject - // WARNING: the content of the String will be duplicated in the JsonBuffer. - root[String("time")] = time; - - // You can get a String from a JsonObject or JsonArray: - // No duplication is done, at least not in the JsonBuffer. - String sensor = root["sensor"]; - - // Unfortunately, the following doesn't work (issue #118): - // sensor = root["sensor"]; // <- error "ambiguous overload for 'operator='" - // As a workaround, you need to replace by: - sensor = root["sensor"].as(); - - // You can set a String to a JsonObject or JsonArray: - // WARNING: the content of the String will be duplicated in the JsonBuffer. - root["sensor"] = sensor; - - // It works with RawJson too: - root["sensor"] = RawJson(sensor); - - // You can also concatenate strings - // WARNING: the content of the String will be duplicated in the JsonBuffer. - root[String("sen") + "sor"] = String("gp") + "s"; - - // You can compare the content of a JsonObject with a String - if (root["sensor"] == sensor) { - // ... - } - - // Lastly, you can print the resulting JSON to a String - String output; - root.printTo(output); -} - -void loop() { - // not used in this example -} - -// See also -// -------- -// -// https://arduinojson.org/ contains the documentation for all the functions -// used above. It also includes an FAQ that will help you solve any problem. -// -// The book "Mastering ArduinoJson" contains a quick C++ course that explains -// how your microcontroller stores strings in memory. On several occasions, it -// shows how you can avoid String in your program. -// Learn more at https://arduinojson.org/book/ -// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/keywords.txt b/lib/ArduinoJson-5.13.4/keywords.txt deleted file mode 100644 index 833cddb73..000000000 --- a/lib/ArduinoJson-5.13.4/keywords.txt +++ /dev/null @@ -1,15 +0,0 @@ -JsonArray KEYWORD1 -JsonObject KEYWORD1 -JsonVariant KEYWORD1 -StaticJsonBuffer KEYWORD1 -DynamicJsonBuffer KEYWORD1 -add KEYWORD2 -createArray KEYWORD2 -createNestedArray KEYWORD2 -createNestedObject KEYWORD2 -createObject KEYWORD2 -parseArray KEYWORD2 -parseObject KEYWORD2 -prettyPrintTo KEYWORD2 -printTo KEYWORD2 -success KEYWORD2 diff --git a/lib/ArduinoJson-5.13.4/library.properties b/lib/ArduinoJson-5.13.4/library.properties deleted file mode 100644 index 0829de12e..000000000 --- a/lib/ArduinoJson-5.13.4/library.properties +++ /dev/null @@ -1,11 +0,0 @@ -name=ArduinoJson -version=5.13.4 -author=Benoit Blanchon -maintainer=Benoit Blanchon -sentence=An efficient and elegant JSON library for Arduino. -paragraph=ArduinoJson supports serialization, deserialization, fixed allocation, zero-copy, streams, and more. It is the most popular Arduino library on GitHub. Check out arduinojson.org for a comprehensive documentation. -category=Data Processing -url=https://arduinojson.org/?utm_source=meta&utm_medium=library.properties -architectures=* -repository=https://github.com/bblanchon/ArduinoJson.git -license=MIT diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson.h b/lib/ArduinoJson-5.13.4/src/ArduinoJson.h deleted file mode 100644 index 3782aeabc..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson.h +++ /dev/null @@ -1,17 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#ifdef __cplusplus - -#include "ArduinoJson.hpp" - -using namespace ArduinoJson; - -#else - -#error ArduinoJson requires a C++ compiler, please change file extension to .cc or .cpp - -#endif diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson.hpp deleted file mode 100644 index c493c06a9..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "ArduinoJson/version.hpp" - -#include "ArduinoJson/DynamicJsonBuffer.hpp" -#include "ArduinoJson/JsonArray.hpp" -#include "ArduinoJson/JsonObject.hpp" -#include "ArduinoJson/StaticJsonBuffer.hpp" - -#include "ArduinoJson/Deserialization/JsonParserImpl.hpp" -#include "ArduinoJson/JsonArrayImpl.hpp" -#include "ArduinoJson/JsonBufferImpl.hpp" -#include "ArduinoJson/JsonObjectImpl.hpp" -#include "ArduinoJson/JsonVariantImpl.hpp" -#include "ArduinoJson/Serialization/JsonSerializerImpl.hpp" diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Configuration.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Configuration.hpp deleted file mode 100644 index 82483adfa..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Configuration.hpp +++ /dev/null @@ -1,151 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -// Small or big machine? -#ifndef ARDUINOJSON_EMBEDDED_MODE -#if defined(ARDUINO) || defined(__IAR_SYSTEMS_ICC__) || defined(__XC) || \ - defined(__ARMCC_VERSION) -#define ARDUINOJSON_EMBEDDED_MODE 1 -#else -#define ARDUINOJSON_EMBEDDED_MODE 0 -#endif -#endif - -#if ARDUINOJSON_EMBEDDED_MODE - -// Store floats by default to reduce the memory usage (issue #134) -#ifndef ARDUINOJSON_USE_DOUBLE -#define ARDUINOJSON_USE_DOUBLE 0 -#endif - -// Store longs by default, because they usually match the size of a float. -#ifndef ARDUINOJSON_USE_LONG_LONG -#define ARDUINOJSON_USE_LONG_LONG 0 -#endif -#ifndef ARDUINOJSON_USE_INT64 -#define ARDUINOJSON_USE_INT64 0 -#endif - -// Embedded systems usually don't have std::string -#ifndef ARDUINOJSON_ENABLE_STD_STRING -#define ARDUINOJSON_ENABLE_STD_STRING 0 -#endif - -// Embedded systems usually don't have std::stream -#ifndef ARDUINOJSON_ENABLE_STD_STREAM -#define ARDUINOJSON_ENABLE_STD_STREAM 0 -#endif - -// Limit nesting as the stack is likely to be small -#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT -#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10 -#endif - -#else // ARDUINOJSON_EMBEDDED_MODE - -// On a computer we have plenty of memory so we can use doubles -#ifndef ARDUINOJSON_USE_DOUBLE -#define ARDUINOJSON_USE_DOUBLE 1 -#endif - -// Use long long when available -#ifndef ARDUINOJSON_USE_LONG_LONG -#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) -#define ARDUINOJSON_USE_LONG_LONG 1 -#else -#define ARDUINOJSON_USE_LONG_LONG 0 -#endif -#endif - -// Use _int64 on old versions of Visual Studio -#ifndef ARDUINOJSON_USE_INT64 -#if defined(_MSC_VER) && _MSC_VER <= 1700 -#define ARDUINOJSON_USE_INT64 1 -#else -#define ARDUINOJSON_USE_INT64 0 -#endif -#endif - -// On a computer, we can use std::string -#ifndef ARDUINOJSON_ENABLE_STD_STRING -#define ARDUINOJSON_ENABLE_STD_STRING 1 -#endif - -// On a computer, we can assume std::stream -#ifndef ARDUINOJSON_ENABLE_STD_STREAM -#define ARDUINOJSON_ENABLE_STD_STREAM 1 -#endif - -// On a computer, the stack is large so we can increase nesting limit -#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT -#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 50 -#endif - -#endif // ARDUINOJSON_EMBEDDED_MODE - -#ifdef ARDUINO - -// Enable support for Arduino String -#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING -#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 -#endif - -// Enable support for Arduino Stream -#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM -#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1 -#endif - -#else // ARDUINO - -// Disable support for Arduino String -#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING -#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 -#endif - -// Disable support for Arduino Stream -#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM -#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0 -#endif - -#endif // ARDUINO - -#ifndef ARDUINOJSON_ENABLE_PROGMEM -#ifdef PROGMEM -#define ARDUINOJSON_ENABLE_PROGMEM 1 -#else -#define ARDUINOJSON_ENABLE_PROGMEM 0 -#endif -#endif - -#ifndef ARDUINOJSON_ENABLE_ALIGNMENT -#ifdef ARDUINO_ARCH_AVR -// alignment isn't needed for 8-bit AVR -#define ARDUINOJSON_ENABLE_ALIGNMENT 0 -#else -// but most processors need pointers to be align on word size -#define ARDUINOJSON_ENABLE_ALIGNMENT 1 -#endif -#endif - -// Enable deprecated functions by default -#ifndef ARDUINOJSON_ENABLE_DEPRECATED -#define ARDUINOJSON_ENABLE_DEPRECATED 1 -#endif - -// Control the exponentiation threshold for big numbers -// CAUTION: cannot be more that 1e9 !!!! -#ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD -#define ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD 1e7 -#endif - -// Control the exponentiation threshold for small numbers -#ifndef ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD -#define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5 -#endif - -#if ARDUINOJSON_USE_LONG_LONG && ARDUINOJSON_USE_INT64 -#error ARDUINOJSON_USE_LONG_LONG and ARDUINOJSON_USE_INT64 cannot be set together -#endif diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/Encoding.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/Encoding.hpp deleted file mode 100644 index a0efa2c74..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/Encoding.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -class Encoding { - public: - // Optimized for code size on a 8-bit AVR - static char escapeChar(char c) { - const char *p = escapeTable(false); - while (p[0] && p[1] != c) { - p += 2; - } - return p[0]; - } - - // Optimized for code size on a 8-bit AVR - static char unescapeChar(char c) { - const char *p = escapeTable(true); - for (;;) { - if (p[0] == '\0') return c; - if (p[0] == c) return p[1]; - p += 2; - } - } - - private: - static const char *escapeTable(bool excludeIdenticals) { - return &"\"\"\\\\b\bf\fn\nr\rt\t"[excludeIdenticals ? 4 : 0]; - } -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonBufferAllocated.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonBufferAllocated.hpp deleted file mode 100644 index 443aae4df..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonBufferAllocated.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../JsonBuffer.hpp" - -namespace ArduinoJson { -namespace Internals { - -class JsonBufferAllocated { - public: - void *operator new(size_t n, JsonBuffer *jsonBuffer) throw() { - if (!jsonBuffer) return NULL; - return jsonBuffer->alloc(n); - } - - void operator delete(void *, JsonBuffer *)throw(); -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonFloat.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonFloat.hpp deleted file mode 100644 index 0ed42140f..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonFloat.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../Configuration.hpp" - -namespace ArduinoJson { -namespace Internals { - -#if ARDUINOJSON_USE_DOUBLE -typedef double JsonFloat; -#else -typedef float JsonFloat; -#endif -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonInteger.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonInteger.hpp deleted file mode 100644 index c8ddd00b4..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonInteger.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../Configuration.hpp" - -namespace ArduinoJson { -namespace Internals { - -#if ARDUINOJSON_USE_LONG_LONG -typedef long long JsonInteger; -typedef unsigned long long JsonUInt; -#elif ARDUINOJSON_USE_INT64 -typedef __int64 JsonInteger; -typedef unsigned _int64 JsonUInt; -#else -typedef long JsonInteger; -typedef unsigned long JsonUInt; -#endif -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantAs.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantAs.hpp deleted file mode 100644 index 8f202c5eb..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantAs.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A metafunction that returns the type of the value returned by -// JsonVariant::as() -template -struct JsonVariantAs { - typedef T type; -}; - -template <> -struct JsonVariantAs { - typedef const char* type; -}; - -template <> -struct JsonVariantAs { - typedef JsonArray& type; -}; - -template <> -struct JsonVariantAs { - typedef const JsonArray& type; -}; - -template <> -struct JsonVariantAs { - typedef JsonObject& type; -}; - -template <> -struct JsonVariantAs { - typedef const JsonObject& type; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantContent.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantContent.hpp deleted file mode 100644 index c525a6060..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantContent.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "JsonFloat.hpp" -#include "JsonInteger.hpp" - -namespace ArduinoJson { - -// Forward declarations -class JsonArray; -class JsonObject; - -namespace Internals { -// A union that defines the actual content of a JsonVariant. -// The enum JsonVariantType determines which member is in use. -union JsonVariantContent { - JsonFloat asFloat; // used for double and float - JsonUInt asInteger; // used for bool, char, short, int and longs - const char* asString; // asString can be null - JsonArray* asArray; // asArray cannot be null - JsonObject* asObject; // asObject cannot be null -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantDefault.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantDefault.hpp deleted file mode 100644 index 57ecc83ee..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantDefault.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -template -struct JsonVariantDefault { - static T get() { - return T(); - } -}; - -template -struct JsonVariantDefault : JsonVariantDefault {}; - -template -struct JsonVariantDefault : JsonVariantDefault {}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantType.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantType.hpp deleted file mode 100644 index 21f890e52..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantType.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -class JsonArray; -class JsonObject; - -namespace Internals { - -// Enumerated type to know the current type of a JsonVariant. -// The value determines which member of JsonVariantContent is used. -enum JsonVariantType { - JSON_UNDEFINED, // JsonVariant has not been initialized - JSON_UNPARSED, // JsonVariant contains an unparsed string - JSON_STRING, // JsonVariant stores a const char* - JSON_BOOLEAN, // JsonVariant stores a bool - JSON_POSITIVE_INTEGER, // JsonVariant stores an JsonUInt - JSON_NEGATIVE_INTEGER, // JsonVariant stores an JsonUInt that must be negated - JSON_ARRAY, // JsonVariant stores a pointer to a JsonArray - JSON_OBJECT, // JsonVariant stores a pointer to a JsonObject - JSON_FLOAT // JsonVariant stores a JsonFloat -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/List.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/List.hpp deleted file mode 100644 index 506308cc3..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/List.hpp +++ /dev/null @@ -1,94 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../JsonBuffer.hpp" -#include "ListConstIterator.hpp" -#include "ListIterator.hpp" - -namespace ArduinoJson { -namespace Internals { - -// A singly linked list of T. -// The linked list is composed of ListNode. -// It is derived by JsonArray and JsonObject -template -class List { - public: - typedef T value_type; - typedef ListNode node_type; - typedef ListIterator iterator; - typedef ListConstIterator const_iterator; - - // Creates an empty List attached to a JsonBuffer. - // The JsonBuffer allows to allocate new nodes. - // When buffer is NULL, the List is not able to grow and success() returns - // false. This is used to identify bad memory allocations and parsing - // failures. - explicit List(JsonBuffer *buffer) : _buffer(buffer), _firstNode(NULL) {} - - // Returns true if the object is valid - // Would return false in the following situation: - // - the memory allocation failed (StaticJsonBuffer was too small) - // - the JSON parsing failed - bool success() const { - return _buffer != NULL; - } - - // Returns the numbers of elements in the list. - // For a JsonObject, it would return the number of key-value pairs - size_t size() const { - size_t nodeCount = 0; - for (node_type *node = _firstNode; node; node = node->next) nodeCount++; - return nodeCount; - } - - iterator add() { - node_type *newNode = new (_buffer) node_type(); - - if (_firstNode) { - node_type *lastNode = _firstNode; - while (lastNode->next) lastNode = lastNode->next; - lastNode->next = newNode; - } else { - _firstNode = newNode; - } - - return iterator(newNode); - } - - iterator begin() { - return iterator(_firstNode); - } - iterator end() { - return iterator(NULL); - } - - const_iterator begin() const { - return const_iterator(_firstNode); - } - const_iterator end() const { - return const_iterator(NULL); - } - - void remove(iterator it) { - node_type *nodeToRemove = it._node; - if (!nodeToRemove) return; - if (nodeToRemove == _firstNode) { - _firstNode = nodeToRemove->next; - } else { - for (node_type *node = _firstNode; node; node = node->next) - if (node->next == nodeToRemove) node->next = nodeToRemove->next; - } - } - - protected: - JsonBuffer *_buffer; - - private: - node_type *_firstNode; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListConstIterator.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListConstIterator.hpp deleted file mode 100644 index a6af685e5..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListConstIterator.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "ListNode.hpp" - -namespace ArduinoJson { -namespace Internals { - -// A read-only forward itertor for List -template -class ListConstIterator { - public: - explicit ListConstIterator(const ListNode *node = NULL) : _node(node) {} - - const T &operator*() const { - return _node->content; - } - const T *operator->() { - return &_node->content; - } - - bool operator==(const ListConstIterator &other) const { - return _node == other._node; - } - - bool operator!=(const ListConstIterator &other) const { - return _node != other._node; - } - - ListConstIterator &operator++() { - if (_node) _node = _node->next; - return *this; - } - - ListConstIterator &operator+=(size_t distance) { - while (_node && distance) { - _node = _node->next; - --distance; - } - return *this; - } - - private: - const ListNode *_node; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListIterator.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListIterator.hpp deleted file mode 100644 index 01fa287f7..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListIterator.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "ListConstIterator.hpp" -#include "ListNode.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -class List; - -// A read-write forward iterator for List -template -class ListIterator { - friend class List; - - public: - explicit ListIterator(ListNode *node = NULL) : _node(node) {} - - T &operator*() const { - return _node->content; - } - T *operator->() { - return &_node->content; - } - - bool operator==(const ListIterator &other) const { - return _node == other._node; - } - - bool operator!=(const ListIterator &other) const { - return _node != other._node; - } - - ListIterator &operator++() { - if (_node) _node = _node->next; - return *this; - } - - ListIterator &operator+=(size_t distance) { - while (_node && distance) { - _node = _node->next; - --distance; - } - return *this; - } - - operator ListConstIterator() const { - return ListConstIterator(_node); - } - - private: - ListNode *_node; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListNode.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListNode.hpp deleted file mode 100644 index c0907120e..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListNode.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include // for NULL - -#include "JsonBufferAllocated.hpp" - -namespace ArduinoJson { -namespace Internals { - -// A node for a singly-linked list. -// Used by List and its iterators. -template -struct ListNode : public Internals::JsonBufferAllocated { - ListNode() throw() : next(NULL) {} - - ListNode *next; - T content; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/NonCopyable.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/NonCopyable.hpp deleted file mode 100644 index 73f3d8edb..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/NonCopyable.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A type that cannot be copied -class NonCopyable { - protected: - NonCopyable() {} - - private: - // copy constructor is private - NonCopyable(const NonCopyable&); - - // copy operator is private - NonCopyable& operator=(const NonCopyable&); -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ReferenceType.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ReferenceType.hpp deleted file mode 100644 index 1e491172f..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ReferenceType.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A type that is meant to be used by reference only (JsonArray and JsonObject) -class ReferenceType { - public: - bool operator==(const ReferenceType& other) const { - // two JsonArray are equal if they are the same instance - // (we don't compare the content) - return this == &other; - } - - bool operator!=(const ReferenceType& other) const { - return this != &other; - } -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ValueSaver.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ValueSaver.hpp deleted file mode 100644 index 9750f1ac5..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ValueSaver.hpp +++ /dev/null @@ -1,52 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../JsonBuffer.hpp" -#include "../JsonVariant.hpp" -#include "../StringTraits/StringTraits.hpp" -#include "../TypeTraits/EnableIf.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -struct ValueSaver { - template - static bool save(JsonBuffer*, Destination& destination, Source source) { - destination = source; - return true; - } -}; - -template -struct ValueSaver< - Source, typename EnableIf::should_duplicate>::type> { - template - static bool save(JsonBuffer* buffer, Destination& dest, Source source) { - if (!StringTraits::is_null(source)) { - typename StringTraits::duplicate_t dup = - StringTraits::duplicate(source, buffer); - if (!dup) return false; - dest = dup; - } else { - dest = reinterpret_cast(0); - } - return true; - } -}; - -// const char*, const signed char*, const unsigned char* -template -struct ValueSaver< - Char*, typename EnableIf::should_duplicate>::type> { - template - static bool save(JsonBuffer*, Destination& dest, Char* source) { - dest = reinterpret_cast(source); - return true; - } -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/Comments.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/Comments.hpp deleted file mode 100644 index c2c48ebcc..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/Comments.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { -template -void skipSpacesAndComments(TInput& input) { - for (;;) { - switch (input.current()) { - // spaces - case ' ': - case '\t': - case '\r': - case '\n': - input.move(); - continue; - - // comments - case '/': - switch (input.next()) { - // C-style block comment - case '*': - input.move(); // skip '/' - // no need to skip '*' - for (;;) { - input.move(); - if (input.current() == '\0') return; - if (input.current() == '*' && input.next() == '/') { - input.move(); // skip '*' - input.move(); // skip '/' - break; - } - } - break; - - // C++-style line comment - case '/': - // not need to skip "//" - for (;;) { - input.move(); - if (input.current() == '\0') return; - if (input.current() == '\n') break; - } - break; - - // not a comment, just a '/' - default: - return; - } - break; - - default: - return; - } - } -} -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParser.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParser.hpp deleted file mode 100644 index 4cbaf454c..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParser.hpp +++ /dev/null @@ -1,102 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../JsonBuffer.hpp" -#include "../JsonVariant.hpp" -#include "../TypeTraits/IsConst.hpp" -#include "StringWriter.hpp" - -namespace ArduinoJson { -namespace Internals { - -// Parse JSON string to create JsonArrays and JsonObjects -// This internal class is not indended to be used directly. -// Instead, use JsonBuffer.parseArray() or .parseObject() -template -class JsonParser { - public: - JsonParser(JsonBuffer *buffer, TReader reader, TWriter writer, - uint8_t nestingLimit) - : _buffer(buffer), - _reader(reader), - _writer(writer), - _nestingLimit(nestingLimit) {} - - JsonArray &parseArray(); - JsonObject &parseObject(); - - JsonVariant parseVariant() { - JsonVariant result; - parseAnythingTo(&result); - return result; - } - - private: - JsonParser &operator=(const JsonParser &); // non-copiable - - static bool eat(TReader &, char charToSkip); - FORCE_INLINE bool eat(char charToSkip) { - return eat(_reader, charToSkip); - } - - const char *parseString(); - bool parseAnythingTo(JsonVariant *destination); - - inline bool parseArrayTo(JsonVariant *destination); - inline bool parseObjectTo(JsonVariant *destination); - inline bool parseStringTo(JsonVariant *destination); - - static inline bool isBetween(char c, char min, char max) { - return min <= c && c <= max; - } - - static inline bool canBeInNonQuotedString(char c) { - return isBetween(c, '0', '9') || isBetween(c, '_', 'z') || - isBetween(c, 'A', 'Z') || c == '+' || c == '-' || c == '.'; - } - - static inline bool isQuote(char c) { - return c == '\'' || c == '\"'; - } - - JsonBuffer *_buffer; - TReader _reader; - TWriter _writer; - uint8_t _nestingLimit; -}; - -template -struct JsonParserBuilder { - typedef typename StringTraits::Reader InputReader; - typedef JsonParser TParser; - - static TParser makeParser(TJsonBuffer *buffer, TString &json, - uint8_t nestingLimit) { - return TParser(buffer, InputReader(json), *buffer, nestingLimit); - } -}; - -template -struct JsonParserBuilder::value>::type> { - typedef typename StringTraits::Reader TReader; - typedef StringWriter TWriter; - typedef JsonParser TParser; - - static TParser makeParser(TJsonBuffer *buffer, TChar *json, - uint8_t nestingLimit) { - return TParser(buffer, TReader(json), TWriter(json), nestingLimit); - } -}; - -template -inline typename JsonParserBuilder::TParser makeParser( - TJsonBuffer *buffer, TString &json, uint8_t nestingLimit) { - return JsonParserBuilder::makeParser(buffer, json, - nestingLimit); -} -} // namespace Internals -} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParserImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParserImpl.hpp deleted file mode 100644 index 504267355..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParserImpl.hpp +++ /dev/null @@ -1,189 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Comments.hpp" -#include "JsonParser.hpp" - -template -inline bool ArduinoJson::Internals::JsonParser::eat( - TReader &reader, char charToSkip) { - skipSpacesAndComments(reader); - if (reader.current() != charToSkip) return false; - reader.move(); - return true; -} - -template -inline bool -ArduinoJson::Internals::JsonParser::parseAnythingTo( - JsonVariant *destination) { - skipSpacesAndComments(_reader); - - switch (_reader.current()) { - case '[': - return parseArrayTo(destination); - - case '{': - return parseObjectTo(destination); - - default: - return parseStringTo(destination); - } -} - -template -inline ArduinoJson::JsonArray & -ArduinoJson::Internals::JsonParser::parseArray() { - if (_nestingLimit == 0) return JsonArray::invalid(); - _nestingLimit--; - - // Create an empty array - JsonArray &array = _buffer->createArray(); - - // Check opening braket - if (!eat('[')) goto ERROR_MISSING_BRACKET; - if (eat(']')) goto SUCCESS_EMPTY_ARRAY; - - // Read each value - for (;;) { - // 1 - Parse value - JsonVariant value; - if (!parseAnythingTo(&value)) goto ERROR_INVALID_VALUE; - if (!array.add(value)) goto ERROR_NO_MEMORY; - - // 2 - More values? - if (eat(']')) goto SUCCES_NON_EMPTY_ARRAY; - if (!eat(',')) goto ERROR_MISSING_COMMA; - } - -SUCCESS_EMPTY_ARRAY: -SUCCES_NON_EMPTY_ARRAY: - _nestingLimit++; - return array; - -ERROR_INVALID_VALUE: -ERROR_MISSING_BRACKET: -ERROR_MISSING_COMMA: -ERROR_NO_MEMORY: - return JsonArray::invalid(); -} - -template -inline bool ArduinoJson::Internals::JsonParser::parseArrayTo( - JsonVariant *destination) { - JsonArray &array = parseArray(); - if (!array.success()) return false; - - *destination = array; - return true; -} - -template -inline ArduinoJson::JsonObject & -ArduinoJson::Internals::JsonParser::parseObject() { - if (_nestingLimit == 0) return JsonObject::invalid(); - _nestingLimit--; - - // Create an empty object - JsonObject &object = _buffer->createObject(); - - // Check opening brace - if (!eat('{')) goto ERROR_MISSING_BRACE; - if (eat('}')) goto SUCCESS_EMPTY_OBJECT; - - // Read each key value pair - for (;;) { - // 1 - Parse key - const char *key = parseString(); - if (!key) goto ERROR_INVALID_KEY; - if (!eat(':')) goto ERROR_MISSING_COLON; - - // 2 - Parse value - JsonVariant value; - if (!parseAnythingTo(&value)) goto ERROR_INVALID_VALUE; - if (!object.set(key, value)) goto ERROR_NO_MEMORY; - - // 3 - More keys/values? - if (eat('}')) goto SUCCESS_NON_EMPTY_OBJECT; - if (!eat(',')) goto ERROR_MISSING_COMMA; - } - -SUCCESS_EMPTY_OBJECT: -SUCCESS_NON_EMPTY_OBJECT: - _nestingLimit++; - return object; - -ERROR_INVALID_KEY: -ERROR_INVALID_VALUE: -ERROR_MISSING_BRACE: -ERROR_MISSING_COLON: -ERROR_MISSING_COMMA: -ERROR_NO_MEMORY: - return JsonObject::invalid(); -} - -template -inline bool ArduinoJson::Internals::JsonParser::parseObjectTo( - JsonVariant *destination) { - JsonObject &object = parseObject(); - if (!object.success()) return false; - - *destination = object; - return true; -} - -template -inline const char * -ArduinoJson::Internals::JsonParser::parseString() { - typename RemoveReference::type::String str = _writer.startString(); - - skipSpacesAndComments(_reader); - char c = _reader.current(); - - if (isQuote(c)) { // quotes - _reader.move(); - char stopChar = c; - for (;;) { - c = _reader.current(); - if (c == '\0') break; - _reader.move(); - - if (c == stopChar) break; - - if (c == '\\') { - // replace char - c = Encoding::unescapeChar(_reader.current()); - if (c == '\0') break; - _reader.move(); - } - - str.append(c); - } - } else { // no quotes - for (;;) { - if (!canBeInNonQuotedString(c)) break; - _reader.move(); - str.append(c); - c = _reader.current(); - } - } - - return str.c_str(); -} - -template -inline bool ArduinoJson::Internals::JsonParser::parseStringTo( - JsonVariant *destination) { - bool hasQuotes = isQuote(_reader.current()); - const char *value = parseString(); - if (value == NULL) return false; - if (hasQuotes) { - *destination = value; - } else { - *destination = RawJson(value); - } - return true; -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/StringWriter.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/StringWriter.hpp deleted file mode 100644 index fd5507ea5..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/StringWriter.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -template -class StringWriter { - public: - class String { - public: - String(TChar** ptr) : _writePtr(ptr), _startPtr(*ptr) {} - - void append(char c) { - *(*_writePtr)++ = TChar(c); - } - - const char* c_str() const { - *(*_writePtr)++ = 0; - return reinterpret_cast(_startPtr); - } - - private: - TChar** _writePtr; - TChar* _startPtr; - }; - - StringWriter(TChar* buffer) : _ptr(buffer) {} - - String startString() { - return String(&_ptr); - } - - private: - TChar* _ptr; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/DynamicJsonBuffer.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/DynamicJsonBuffer.hpp deleted file mode 100644 index bdbd5dd90..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/DynamicJsonBuffer.hpp +++ /dev/null @@ -1,170 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "JsonBufferBase.hpp" - -#include - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnon-virtual-dtor" -#elif defined(__GNUC__) -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#pragma GCC diagnostic push -#endif -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" -#endif - -namespace ArduinoJson { -namespace Internals { -class DefaultAllocator { - public: - void* allocate(size_t size) { - return malloc(size); - } - void deallocate(void* pointer) { - free(pointer); - } -}; - -template -class DynamicJsonBufferBase - : public JsonBufferBase > { - struct Block; - struct EmptyBlock { - Block* next; - size_t capacity; - size_t size; - }; - struct Block : EmptyBlock { - uint8_t data[1]; - }; - - public: - enum { EmptyBlockSize = sizeof(EmptyBlock) }; - - DynamicJsonBufferBase(size_t initialSize = 256) - : _head(NULL), _nextBlockCapacity(initialSize) {} - - ~DynamicJsonBufferBase() { - clear(); - } - - // Gets the number of bytes occupied in the buffer - size_t size() const { - size_t total = 0; - for (const Block* b = _head; b; b = b->next) total += b->size; - return total; - } - - // Allocates the specified amount of bytes in the buffer - virtual void* alloc(size_t bytes) { - alignNextAlloc(); - return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes); - } - - // Resets the buffer. - // USE WITH CAUTION: this invalidates all previously allocated data - void clear() { - Block* currentBlock = _head; - while (currentBlock != NULL) { - _nextBlockCapacity = currentBlock->capacity; - Block* nextBlock = currentBlock->next; - _allocator.deallocate(currentBlock); - currentBlock = nextBlock; - } - _head = 0; - } - - class String { - public: - String(DynamicJsonBufferBase* parent) - : _parent(parent), _start(NULL), _length(0) {} - - void append(char c) { - if (_parent->canAllocInHead(1)) { - char* end = static_cast(_parent->allocInHead(1)); - *end = c; - if (_length == 0) _start = end; - } else { - char* newStart = - static_cast(_parent->allocInNewBlock(_length + 1)); - if (_start && newStart) memcpy(newStart, _start, _length); - if (newStart) newStart[_length] = c; - _start = newStart; - } - _length++; - } - - const char* c_str() { - append(0); - return _start; - } - - private: - DynamicJsonBufferBase* _parent; - char* _start; - size_t _length; - }; - - String startString() { - return String(this); - } - - private: - void alignNextAlloc() { - if (_head) _head->size = this->round_size_up(_head->size); - } - - bool canAllocInHead(size_t bytes) const { - return _head != NULL && _head->size + bytes <= _head->capacity; - } - - void* allocInHead(size_t bytes) { - void* p = _head->data + _head->size; - _head->size += bytes; - return p; - } - - void* allocInNewBlock(size_t bytes) { - size_t capacity = _nextBlockCapacity; - if (bytes > capacity) capacity = bytes; - if (!addNewBlock(capacity)) return NULL; - _nextBlockCapacity *= 2; - return allocInHead(bytes); - } - - bool addNewBlock(size_t capacity) { - size_t bytes = EmptyBlockSize + capacity; - Block* block = static_cast(_allocator.allocate(bytes)); - if (block == NULL) return false; - block->capacity = capacity; - block->size = 0; - block->next = _head; - _head = block; - return true; - } - - TAllocator _allocator; - Block* _head; - size_t _nextBlockCapacity; -}; -} - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#pragma GCC diagnostic pop -#endif -#endif - -// Implements a JsonBuffer with dynamic memory allocation. -// You are strongly encouraged to consider using StaticJsonBuffer which is much -// more suitable for embedded systems. -typedef Internals::DynamicJsonBufferBase - DynamicJsonBuffer; -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArray.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArray.hpp deleted file mode 100644 index 2acd2a1a5..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArray.hpp +++ /dev/null @@ -1,227 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Data/JsonBufferAllocated.hpp" -#include "Data/List.hpp" -#include "Data/ReferenceType.hpp" -#include "Data/ValueSaver.hpp" -#include "JsonVariant.hpp" -#include "Serialization/JsonPrintable.hpp" -#include "StringTraits/StringTraits.hpp" -#include "TypeTraits/EnableIf.hpp" -#include "TypeTraits/IsArray.hpp" -#include "TypeTraits/IsFloatingPoint.hpp" -#include "TypeTraits/IsSame.hpp" - -// Returns the size (in bytes) of an array with n elements. -// Can be very handy to determine the size of a StaticJsonBuffer. -#define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \ - (sizeof(JsonArray) + (NUMBER_OF_ELEMENTS) * sizeof(JsonArray::node_type)) - -namespace ArduinoJson { - -// Forward declarations -class JsonObject; -class JsonBuffer; -namespace Internals { -class JsonArraySubscript; -} - -// An array of JsonVariant. -// -// The constructor is private, instances must be created via -// JsonBuffer::createArray() or JsonBuffer::parseArray(). -// A JsonArray can be serialized to a JSON string via JsonArray::printTo(). -// It can also be deserialized from a JSON string via JsonBuffer::parseArray(). -class JsonArray : public Internals::JsonPrintable, - public Internals::ReferenceType, - public Internals::NonCopyable, - public Internals::List, - public Internals::JsonBufferAllocated { - public: - // Create an empty JsonArray attached to the specified JsonBuffer. - // You should not call this constructor directly. - // Instead, use JsonBuffer::createArray() or JsonBuffer::parseArray(). - explicit JsonArray(JsonBuffer *buffer) throw() - : Internals::List(buffer) {} - - // Gets the value at the specified index - const Internals::JsonArraySubscript operator[](size_t index) const; - - // Gets or sets the value at specified index - Internals::JsonArraySubscript operator[](size_t index); - - // Adds the specified value at the end of the array. - // - // bool add(TValue); - // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // std::string, String, JsonArray, JsonObject - template - bool add(const T &value) { - return add_impl(value); - } - // - // bool add(TValue); - // TValue = char*, const char*, const FlashStringHelper* - template - bool add(T *value) { - return add_impl(value); - } - // - // bool add(TValue value, uint8_t decimals); - // TValue = float, double - template - DEPRECATED("Second argument is not supported anymore") - bool add(T value, uint8_t) { - return add_impl(JsonVariant(value)); - } - - // Sets the value at specified index. - // - // bool add(size_t index, const TValue&); - // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // std::string, String, JsonArray, JsonObject - template - bool set(size_t index, const T &value) { - return set_impl(index, value); - } - // - // bool add(size_t index, TValue); - // TValue = char*, const char*, const FlashStringHelper* - template - bool set(size_t index, T *value) { - return set_impl(index, value); - } - // - // bool set(size_t index, TValue value, uint8_t decimals); - // TValue = float, double - template - typename Internals::EnableIf::value, bool>::type - set(size_t index, T value, uint8_t decimals) { - return set_impl(index, JsonVariant(value, decimals)); - } - - // Gets the value at the specified index. - template - typename Internals::JsonVariantAs::type get(size_t index) const { - const_iterator it = begin() += index; - return it != end() ? it->as() : Internals::JsonVariantDefault::get(); - } - - // Check the type of the value at specified index. - template - bool is(size_t index) const { - const_iterator it = begin() += index; - return it != end() ? it->is() : false; - } - - // Creates a JsonArray and adds a reference at the end of the array. - // It's a shortcut for JsonBuffer::createArray() and JsonArray::add() - JsonArray &createNestedArray(); - - // Creates a JsonObject and adds a reference at the end of the array. - // It's a shortcut for JsonBuffer::createObject() and JsonArray::add() - JsonObject &createNestedObject(); - - // Removes element at specified index. - void remove(size_t index) { - remove(begin() += index); - } - using Internals::List::remove; - - // Returns a reference an invalid JsonArray. - // This object is meant to replace a NULL pointer. - // This is used when memory allocation or JSON parsing fail. - static JsonArray &invalid() { - static JsonArray instance(NULL); - return instance; - } - - // Imports a 1D array - template - bool copyFrom(T (&array)[N]) { - return copyFrom(array, N); - } - - // Imports a 1D array - template - bool copyFrom(T *array, size_t len) { - bool ok = true; - for (size_t i = 0; i < len; i++) { - ok &= add(array[i]); - } - return ok; - } - - // Imports a 2D array - template - bool copyFrom(T (&array)[N1][N2]) { - bool ok = true; - for (size_t i = 0; i < N1; i++) { - JsonArray &nestedArray = createNestedArray(); - for (size_t j = 0; j < N2; j++) { - ok &= nestedArray.add(array[i][j]); - } - } - return ok; - } - - // Exports a 1D array - template - size_t copyTo(T (&array)[N]) const { - return copyTo(array, N); - } - - // Exports a 1D array - template - size_t copyTo(T *array, size_t len) const { - size_t i = 0; - for (const_iterator it = begin(); it != end() && i < len; ++it) - array[i++] = *it; - return i; - } - - // Exports a 2D array - template - void copyTo(T (&array)[N1][N2]) const { - size_t i = 0; - for (const_iterator it = begin(); it != end() && i < N1; ++it) { - it->as().copyTo(array[i++]); - } - } - -#if ARDUINOJSON_ENABLE_DEPRECATED - DEPRECATED("use remove() instead") - FORCE_INLINE void removeAt(size_t index) { - return remove(index); - } -#endif - - private: - template - bool set_impl(size_t index, TValueRef value) { - iterator it = begin() += index; - if (it == end()) return false; - return Internals::ValueSaver::save(_buffer, *it, value); - } - - template - bool add_impl(TValueRef value) { - iterator it = Internals::List::add(); - if (it == end()) return false; - return Internals::ValueSaver::save(_buffer, *it, value); - } -}; - -namespace Internals { -template <> -struct JsonVariantDefault { - static JsonArray &get() { - return JsonArray::invalid(); - } -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArrayImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArrayImpl.hpp deleted file mode 100644 index 924b7ea7a..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArrayImpl.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "JsonArray.hpp" -#include "JsonArraySubscript.hpp" -#include "JsonObject.hpp" - -namespace ArduinoJson { - -inline JsonArray &JsonArray::createNestedArray() { - if (!_buffer) return JsonArray::invalid(); - JsonArray &array = _buffer->createArray(); - add(array); - return array; -} - -inline JsonObject &JsonArray::createNestedObject() { - if (!_buffer) return JsonObject::invalid(); - JsonObject &object = _buffer->createObject(); - add(object); - return object; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArraySubscript.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArraySubscript.hpp deleted file mode 100644 index afb4dc1ec..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArraySubscript.hpp +++ /dev/null @@ -1,122 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Configuration.hpp" -#include "JsonVariantBase.hpp" - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4522) -#endif - -namespace ArduinoJson { -namespace Internals { -class JsonArraySubscript : public JsonVariantBase { - public: - FORCE_INLINE JsonArraySubscript(JsonArray& array, size_t index) - : _array(array), _index(index) {} - - FORCE_INLINE JsonArraySubscript& operator=(const JsonArraySubscript& src) { - _array.set(_index, src); - return *this; - } - - // Replaces the value - // - // operator=(const TValue&) - // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // std::string, String, JsonArray, JsonObject - template - FORCE_INLINE JsonArraySubscript& operator=(const T& src) { - _array.set(_index, src); - return *this; - } - // - // operator=(TValue) - // TValue = char*, const char*, const FlashStringHelper* - template - FORCE_INLINE JsonArraySubscript& operator=(T* src) { - _array.set(_index, src); - return *this; - } - - FORCE_INLINE bool success() const { - return _index < _array.size(); - } - - template - FORCE_INLINE typename JsonVariantAs::type as() const { - return _array.get(_index); - } - - template - FORCE_INLINE bool is() const { - return _array.is(_index); - } - - // Replaces the value - // - // bool set(const TValue&) - // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // std::string, String, JsonArray, JsonObject - template - FORCE_INLINE bool set(const TValue& value) { - return _array.set(_index, value); - } - // - // bool set(TValue) - // TValue = char*, const char*, const FlashStringHelper* - template - FORCE_INLINE bool set(TValue* value) { - return _array.set(_index, value); - } - // - // bool set(TValue, uint8_t decimals); - // TValue = float, double - template - DEPRECATED("Second argument is not supported anymore") - FORCE_INLINE bool set(const TValue& value, uint8_t) { - return _array.set(_index, value); - } - - private: - JsonArray& _array; - const size_t _index; -}; - -template -inline JsonArraySubscript JsonVariantSubscripts::operator[]( - size_t index) { - return impl()->template as()[index]; -} - -template -inline const JsonArraySubscript JsonVariantSubscripts::operator[]( - size_t index) const { - return impl()->template as()[index]; -} - -#if ARDUINOJSON_ENABLE_STD_STREAM -inline std::ostream& operator<<(std::ostream& os, - const JsonArraySubscript& source) { - return source.printTo(os); -} -#endif -} - -inline Internals::JsonArraySubscript JsonArray::operator[](size_t index) { - return Internals::JsonArraySubscript(*this, index); -} - -inline const Internals::JsonArraySubscript JsonArray::operator[]( - size_t index) const { - return Internals::JsonArraySubscript(*const_cast(this), index); -} -} - -#ifdef _MSC_VER -#pragma warning(pop) -#endif diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBuffer.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBuffer.hpp deleted file mode 100644 index 26101e086..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBuffer.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include // for size_t -#include // for uint8_t -#include - -#include "Data/NonCopyable.hpp" -#include "JsonVariant.hpp" -#include "TypeTraits/EnableIf.hpp" -#include "TypeTraits/IsArray.hpp" - -namespace ArduinoJson { -class JsonArray; -class JsonObject; - -// Entry point for using the library. -// -// Handle the memory management (done in derived classes) and calls the parser. -// This abstract class is implemented by StaticJsonBuffer which implements a -// fixed memory allocation. -class JsonBuffer : Internals::NonCopyable { - public: - // Allocates an empty JsonArray. - // - // Returns a reference to the new JsonArray or JsonArray::invalid() if the - // allocation fails. - JsonArray &createArray(); - - // Allocates an empty JsonObject. - // - // Returns a reference to the new JsonObject or JsonObject::invalid() if the - // allocation fails. - JsonObject &createObject(); - - // Duplicates a string - // - // const char* strdup(TValue); - // TValue = const std::string&, const String&, - template - DEPRECATED("char* are duplicated, you don't need strdup() anymore") - typename Internals::EnableIf::value, - const char *>::type strdup(const TString &src) { - return Internals::StringTraits::duplicate(src, this); - } - // - // const char* strdup(TValue); - // TValue = char*, const char*, const FlashStringHelper* - template - DEPRECATED("char* are duplicated, you don't need strdup() anymore") - const char *strdup(TString *src) { - return Internals::StringTraits::duplicate(src, this); - } - - // Allocates n bytes in the JsonBuffer. - // Return a pointer to the allocated memory or NULL if allocation fails. - virtual void *alloc(size_t size) = 0; - - protected: - // CAUTION: NO VIRTUAL DESTRUCTOR! - // If we add a virtual constructor the Arduino compiler will add malloc() - // and free() to the binary, adding 706 useless bytes. - ~JsonBuffer() {} - - // Preserve aligment if necessary - static FORCE_INLINE size_t round_size_up(size_t bytes) { -#if ARDUINOJSON_ENABLE_ALIGNMENT - const size_t x = sizeof(void *) - 1; - return (bytes + x) & ~x; -#else - return bytes; -#endif - } -}; -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferBase.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferBase.hpp deleted file mode 100644 index 1e771bfdb..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferBase.hpp +++ /dev/null @@ -1,127 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Deserialization/JsonParser.hpp" - -namespace ArduinoJson { -namespace Internals { -template -class JsonBufferBase : public JsonBuffer { - public: - // Allocates and populate a JsonArray from a JSON string. - // - // The First argument is a pointer to the JSON string, the memory must be - // writable - // because the parser will insert null-terminators and replace escaped chars. - // - // The second argument set the nesting limit - // - // Returns a reference to the new JsonObject or JsonObject::invalid() if the - // allocation fails. - // With this overload, the JsonBuffer will make a copy of the string - // - // JsonArray& parseArray(TString); - // TString = const std::string&, const String& - template - typename Internals::EnableIf::value, - JsonArray &>::type - parseArray(const TString &json, - uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { - return Internals::makeParser(that(), json, nestingLimit).parseArray(); - } - // - // JsonArray& parseArray(TString); - // TString = const char*, const char[N], const FlashStringHelper* - template - JsonArray &parseArray( - TString *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { - return Internals::makeParser(that(), json, nestingLimit).parseArray(); - } - // - // JsonArray& parseArray(TString); - // TString = std::istream&, Stream& - template - JsonArray &parseArray( - TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { - return Internals::makeParser(that(), json, nestingLimit).parseArray(); - } - - // Allocates and populate a JsonObject from a JSON string. - // - // The First argument is a pointer to the JSON string, the memory must be - // writable - // because the parser will insert null-terminators and replace escaped chars. - // - // The second argument set the nesting limit - // - // Returns a reference to the new JsonObject or JsonObject::invalid() if the - // allocation fails. - // - // JsonObject& parseObject(TString); - // TString = const std::string&, const String& - template - typename Internals::EnableIf::value, - JsonObject &>::type - parseObject(const TString &json, - uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { - return Internals::makeParser(that(), json, nestingLimit).parseObject(); - } - // - // JsonObject& parseObject(TString); - // TString = const char*, const char[N], const FlashStringHelper* - template - JsonObject &parseObject( - TString *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { - return Internals::makeParser(that(), json, nestingLimit).parseObject(); - } - // - // JsonObject& parseObject(TString); - // TString = std::istream&, Stream& - template - JsonObject &parseObject( - TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { - return Internals::makeParser(that(), json, nestingLimit).parseObject(); - } - - // Generalized version of parseArray() and parseObject(), also works for - // integral types. - // - // JsonVariant parse(TString); - // TString = const std::string&, const String& - template - typename Internals::EnableIf::value, - JsonVariant>::type - parse(const TString &json, - uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { - return Internals::makeParser(that(), json, nestingLimit).parseVariant(); - } - // - // JsonVariant parse(TString); - // TString = const char*, const char[N], const FlashStringHelper* - template - JsonVariant parse(TString *json, - uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { - return Internals::makeParser(that(), json, nestingLimit).parseVariant(); - } - // - // JsonVariant parse(TString); - // TString = std::istream&, Stream& - template - JsonVariant parse(TString &json, - uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { - return Internals::makeParser(that(), json, nestingLimit).parseVariant(); - } - - protected: - ~JsonBufferBase() {} - - private: - TDerived *that() { - return static_cast(this); - } -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferImpl.hpp deleted file mode 100644 index cdea374bb..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferImpl.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Deserialization/JsonParser.hpp" - -inline ArduinoJson::JsonArray &ArduinoJson::JsonBuffer::createArray() { - JsonArray *ptr = new (this) JsonArray(this); - return ptr ? *ptr : JsonArray::invalid(); -} - -inline ArduinoJson::JsonObject &ArduinoJson::JsonBuffer::createObject() { - JsonObject *ptr = new (this) JsonObject(this); - return ptr ? *ptr : JsonObject::invalid(); -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObject.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObject.hpp deleted file mode 100644 index caf698a3e..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObject.hpp +++ /dev/null @@ -1,328 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Data/JsonBufferAllocated.hpp" -#include "Data/List.hpp" -#include "Data/ReferenceType.hpp" -#include "Data/ValueSaver.hpp" -#include "JsonPair.hpp" -#include "Serialization/JsonPrintable.hpp" -#include "StringTraits/StringTraits.hpp" -#include "TypeTraits/EnableIf.hpp" -#include "TypeTraits/IsArray.hpp" -#include "TypeTraits/IsFloatingPoint.hpp" -#include "TypeTraits/IsSame.hpp" - -// Returns the size (in bytes) of an object with n elements. -// Can be very handy to determine the size of a StaticJsonBuffer. -#define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \ - (sizeof(JsonObject) + (NUMBER_OF_ELEMENTS) * sizeof(JsonObject::node_type)) - -namespace ArduinoJson { - -// Forward declarations -class JsonArray; -class JsonBuffer; -namespace Internals { -template -class JsonObjectSubscript; -} - -// A dictionary of JsonVariant indexed by string (char*) -// -// The constructor is private, instances must be created via -// JsonBuffer::createObject() or JsonBuffer::parseObject(). -// A JsonObject can be serialized to a JSON string via JsonObject::printTo(). -// It can also be deserialized from a JSON string via JsonBuffer::parseObject(). -class JsonObject : public Internals::JsonPrintable, - public Internals::ReferenceType, - public Internals::NonCopyable, - public Internals::List, - public Internals::JsonBufferAllocated { - public: - // Create an empty JsonArray attached to the specified JsonBuffer. - // You should not use this constructor directly. - // Instead, use JsonBuffer::createObject() or JsonBuffer.parseObject(). - explicit JsonObject(JsonBuffer* buffer) throw() - : Internals::List(buffer) {} - - // Gets or sets the value associated with the specified key. - // - // JsonObjectSubscript operator[](TKey) - // TKey = const std::string&, const String& - template - Internals::JsonObjectSubscript operator[]( - const TString& key) { - return Internals::JsonObjectSubscript(*this, key); - } - // - // JsonObjectSubscript operator[](TKey) - // TKey = char*, const char*, char[], const char[N], const FlashStringHelper* - template - Internals::JsonObjectSubscript operator[](TString* key) { - return Internals::JsonObjectSubscript(*this, key); - } - - // Gets the value associated with the specified key. - // - // const JsonObjectSubscript operator[](TKey) const; - // TKey = const std::string&, const String& - template - const Internals::JsonObjectSubscript operator[]( - const TString& key) const { - return Internals::JsonObjectSubscript( - *const_cast(this), key); - } - // - // const JsonObjectSubscript operator[](TKey) const; - // TKey = const char*, const char[N], const FlashStringHelper* - template - const Internals::JsonObjectSubscript operator[]( - TString* key) const { - return Internals::JsonObjectSubscript( - *const_cast(this), key); - } - - // Sets the specified key with the specified value. - // - // bool set(TKey, TValue); - // TKey = const std::string&, const String& - // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // std::string, String, JsonArray, JsonObject - template - bool set(const TString& key, const TValue& value) { - return set_impl(key, value); - } - // - // bool set(TKey, TValue); - // TKey = const std::string&, const String& - // TValue = char*, const char*, const FlashStringHelper* - template - bool set(const TString& key, TValue* value) { - return set_impl(key, value); - } - // - // bool set(TKey, const TValue&); - // TKey = char*, const char*, const FlashStringHelper* - // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // std::string, String, JsonArray, JsonObject - template - bool set(TString* key, const TValue& value) { - return set_impl(key, value); - } - // - // bool set(TKey, TValue); - // TKey = char*, const char*, const FlashStringHelper* - // TValue = char*, const char*, const FlashStringHelper* - template - bool set(TString* key, TValue* value) { - return set_impl(key, value); - } - // - // bool set(TKey, TValue, uint8_t decimals); - // TKey = const std::string&, const String& - // TValue = float, double - template - DEPRECATED("Second argument is not supported anymore") - typename Internals::EnableIf::value, - bool>::type - set(const TString& key, TValue value, uint8_t) { - return set_impl(key, - JsonVariant(value)); - } - // - // bool set(TKey, TValue, uint8_t decimals); - // TKey = char*, const char*, const FlashStringHelper* - // TValue = float, double - template - DEPRECATED("Second argument is not supported anymore") - typename Internals::EnableIf::value, - bool>::type - set(TString* key, TValue value, uint8_t) { - return set_impl(key, JsonVariant(value)); - } - - // Gets the value associated with the specified key. - // - // TValue get(TKey) const; - // TKey = const std::string&, const String& - // TValue = bool, char, long, int, short, float, double, - // std::string, String, JsonArray, JsonObject - template - typename Internals::JsonVariantAs::type get( - const TString& key) const { - return get_impl(key); - } - // - // TValue get(TKey) const; - // TKey = char*, const char*, const FlashStringHelper* - // TValue = bool, char, long, int, short, float, double, - // std::string, String, JsonArray, JsonObject - template - typename Internals::JsonVariantAs::type get(TString* key) const { - return get_impl(key); - } - - // Checks the type of the value associated with the specified key. - // - // - // bool is(TKey) const; - // TKey = const std::string&, const String& - // TValue = bool, char, long, int, short, float, double, - // std::string, String, JsonArray, JsonObject - template - bool is(const TString& key) const { - return is_impl(key); - } - // - // bool is(TKey) const; - // TKey = char*, const char*, const FlashStringHelper* - // TValue = bool, char, long, int, short, float, double, - // std::string, String, JsonArray, JsonObject - template - bool is(TString* key) const { - return is_impl(key); - } - - // Creates and adds a JsonArray. - // - // JsonArray& createNestedArray(TKey); - // TKey = const std::string&, const String& - template - JsonArray& createNestedArray(const TString& key) { - return createNestedArray_impl(key); - } - // JsonArray& createNestedArray(TKey); - // TKey = char*, const char*, char[], const char[], const FlashStringHelper* - template - JsonArray& createNestedArray(TString* key) { - return createNestedArray_impl(key); - } - - // Creates and adds a JsonObject. - // - // JsonObject& createNestedObject(TKey); - // TKey = const std::string&, const String& - template - JsonObject& createNestedObject(const TString& key) { - return createNestedObject_impl(key); - } - // - // JsonObject& createNestedObject(TKey); - // TKey = char*, const char*, char[], const char[], const FlashStringHelper* - template - JsonObject& createNestedObject(TString* key) { - return createNestedObject_impl(key); - } - - // Tells weither the specified key is present and associated with a value. - // - // bool containsKey(TKey); - // TKey = const std::string&, const String& - template - bool containsKey(const TString& key) const { - return findKey(key) != end(); - } - // - // bool containsKey(TKey); - // TKey = char*, const char*, char[], const char[], const FlashStringHelper* - template - bool containsKey(TString* key) const { - return findKey(key) != end(); - } - - // Removes the specified key and the associated value. - // - // void remove(TKey); - // TKey = const std::string&, const String& - template - void remove(const TString& key) { - remove(findKey(key)); - } - // - // void remove(TKey); - // TKey = char*, const char*, char[], const char[], const FlashStringHelper* - template - void remove(TString* key) { - remove(findKey(key)); - } - // - // void remove(iterator) - using Internals::List::remove; - - // Returns a reference an invalid JsonObject. - // This object is meant to replace a NULL pointer. - // This is used when memory allocation or JSON parsing fail. - static JsonObject& invalid() { - static JsonObject instance(NULL); - return instance; - } - - private: - // Returns the list node that matches the specified key. - template - iterator findKey(TStringRef key) { - iterator it; - for (it = begin(); it != end(); ++it) { - if (Internals::StringTraits::equals(key, it->key)) break; - } - return it; - } - template - const_iterator findKey(TStringRef key) const { - return const_cast(this)->findKey(key); - } - - template - typename Internals::JsonVariantAs::type get_impl( - TStringRef key) const { - const_iterator it = findKey(key); - return it != end() ? it->value.as() - : Internals::JsonVariantDefault::get(); - } - - template - bool set_impl(TStringRef key, TValueRef value) { - // ignore null key - if (Internals::StringTraits::is_null(key)) return false; - - // search a matching key - iterator it = findKey(key); - if (it == end()) { - // add the key - it = Internals::List::add(); - if (it == end()) return false; - bool key_ok = - Internals::ValueSaver::save(_buffer, it->key, key); - if (!key_ok) return false; - } - - // save the value - return Internals::ValueSaver::save(_buffer, it->value, value); - } - - template - bool is_impl(TStringRef key) const { - const_iterator it = findKey(key); - return it != end() ? it->value.is() : false; - } - - template - JsonArray& createNestedArray_impl(TStringRef key); - - template - JsonObject& createNestedObject_impl(TStringRef key); -}; - -namespace Internals { -template <> -struct JsonVariantDefault { - static JsonObject& get() { - return JsonObject::invalid(); - } -}; -} // namespace Internals -} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectImpl.hpp deleted file mode 100644 index e7689b507..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectImpl.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "JsonArray.hpp" -#include "JsonObject.hpp" -#include "JsonObjectSubscript.hpp" - -namespace ArduinoJson { - -template -inline JsonArray &JsonObject::createNestedArray_impl(TStringRef key) { - if (!_buffer) return JsonArray::invalid(); - JsonArray &array = _buffer->createArray(); - set(key, array); - return array; -} - -template -inline JsonObject &JsonObject::createNestedObject_impl(TStringRef key) { - if (!_buffer) return JsonObject::invalid(); - JsonObject &object = _buffer->createObject(); - set(key, object); - return object; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectSubscript.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectSubscript.hpp deleted file mode 100644 index 6ac476370..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectSubscript.hpp +++ /dev/null @@ -1,110 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Configuration.hpp" -#include "JsonVariantBase.hpp" -#include "TypeTraits/EnableIf.hpp" - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4522) -#endif - -namespace ArduinoJson { -namespace Internals { - -template -class JsonObjectSubscript - : public JsonVariantBase > { - typedef JsonObjectSubscript this_type; - - public: - FORCE_INLINE JsonObjectSubscript(JsonObject& object, TStringRef key) - : _object(object), _key(key) {} - - FORCE_INLINE this_type& operator=(const this_type& src) { - _object.set(_key, src); - return *this; - } - - // Set the specified value - // - // operator=(const TValue&); - // TValue = bool, char, long, int, short, float, double, - // std::string, String, JsonArray, JsonObject - template - FORCE_INLINE typename EnableIf::value, this_type&>::type - operator=(const TValue& src) { - _object.set(_key, src); - return *this; - } - // - // operator=(TValue); - // TValue = char*, const char*, const FlashStringHelper* - template - FORCE_INLINE this_type& operator=(TValue* src) { - _object.set(_key, src); - return *this; - } - - FORCE_INLINE bool success() const { - return _object.containsKey(_key); - } - - template - FORCE_INLINE typename JsonVariantAs::type as() const { - return _object.get(_key); - } - - template - FORCE_INLINE bool is() const { - return _object.is(_key); - } - - // Sets the specified value. - // - // bool set(const TValue&); - // TValue = bool, char, long, int, short, float, double, RawJson, JsonVariant, - // std::string, String, JsonArray, JsonObject - template - FORCE_INLINE typename EnableIf::value, bool>::type set( - const TValue& value) { - return _object.set(_key, value); - } - // - // bool set(TValue); - // TValue = char*, const char, const FlashStringHelper* - template - FORCE_INLINE bool set(const TValue* value) { - return _object.set(_key, value); - } - // - // bool set(TValue, uint8_t decimals); - // TValue = float, double - template - DEPRECATED("Second argument is not supported anymore") - FORCE_INLINE bool set(const TValue& value, uint8_t) { - return _object.set(_key, value); - } - - private: - JsonObject& _object; - TStringRef _key; -}; - -#if ARDUINOJSON_ENABLE_STD_STREAM -template -inline std::ostream& operator<<(std::ostream& os, - const JsonObjectSubscript& source) { - return source.printTo(os); -} -#endif -} -} - -#ifdef _MSC_VER -#pragma warning(pop) -#endif diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonPair.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonPair.hpp deleted file mode 100644 index 417243045..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonPair.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "JsonVariant.hpp" - -namespace ArduinoJson { - -// A key value pair for JsonObject. -struct JsonPair { - const char* key; - JsonVariant value; -}; -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariant.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariant.hpp deleted file mode 100644 index 43c51b770..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariant.hpp +++ /dev/null @@ -1,357 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include -#include // for uint8_t - -#include "Data/JsonVariantContent.hpp" -#include "Data/JsonVariantDefault.hpp" -#include "Data/JsonVariantType.hpp" -#include "JsonVariantBase.hpp" -#include "RawJson.hpp" -#include "Serialization/JsonPrintable.hpp" -#include "TypeTraits/EnableIf.hpp" -#include "TypeTraits/IsChar.hpp" -#include "TypeTraits/IsFloatingPoint.hpp" -#include "TypeTraits/IsIntegral.hpp" -#include "TypeTraits/IsSame.hpp" -#include "TypeTraits/IsSignedIntegral.hpp" -#include "TypeTraits/IsUnsignedIntegral.hpp" -#include "TypeTraits/RemoveConst.hpp" -#include "TypeTraits/RemoveReference.hpp" - -namespace ArduinoJson { - -// Forward declarations. -class JsonArray; -class JsonObject; - -// A variant that can be a any value serializable to a JSON value. -// -// It can be set to: -// - a boolean -// - a char, short, int or a long (signed or unsigned) -// - a string (const char*) -// - a reference to a JsonArray or JsonObject -class JsonVariant : public Internals::JsonVariantBase { - template - friend class Internals::JsonSerializer; - - public: - // Creates an uninitialized JsonVariant - JsonVariant() : _type(Internals::JSON_UNDEFINED) {} - - // Create a JsonVariant containing a boolean value. - // It will be serialized as "true" or "false" in JSON. - JsonVariant(bool value) { - using namespace Internals; - _type = JSON_BOOLEAN; - _content.asInteger = static_cast(value); - } - - // Create a JsonVariant containing a floating point value. - // JsonVariant(double value); - // JsonVariant(float value); - template - JsonVariant(T value, typename Internals::EnableIf< - Internals::IsFloatingPoint::value>::type * = 0) { - using namespace Internals; - _type = JSON_FLOAT; - _content.asFloat = static_cast(value); - } - template - DEPRECATED("Second argument is not supported anymore") - JsonVariant(T value, uint8_t, - typename Internals::EnableIf< - Internals::IsFloatingPoint::value>::type * = 0) { - using namespace Internals; - _type = JSON_FLOAT; - _content.asFloat = static_cast(value); - } - - // Create a JsonVariant containing an integer value. - // JsonVariant(char) - // JsonVariant(signed short) - // JsonVariant(signed int) - // JsonVariant(signed long) - // JsonVariant(signed char) - template - JsonVariant( - T value, - typename Internals::EnableIf::value || - Internals::IsSame::value>::type * = - 0) { - using namespace Internals; - if (value >= 0) { - _type = JSON_POSITIVE_INTEGER; - _content.asInteger = static_cast(value); - } else { - _type = JSON_NEGATIVE_INTEGER; - _content.asInteger = static_cast(-value); - } - } - // JsonVariant(unsigned short) - // JsonVariant(unsigned int) - // JsonVariant(unsigned long) - template - JsonVariant(T value, - typename Internals::EnableIf< - Internals::IsUnsignedIntegral::value>::type * = 0) { - using namespace Internals; - _type = JSON_POSITIVE_INTEGER; - _content.asInteger = static_cast(value); - } - - // Create a JsonVariant containing a string. - // JsonVariant(const char*); - // JsonVariant(const signed char*); - // JsonVariant(const unsigned char*); - template - JsonVariant( - const TChar *value, - typename Internals::EnableIf::value>::type * = - 0) { - _type = Internals::JSON_STRING; - _content.asString = reinterpret_cast(value); - } - - // Create a JsonVariant containing an unparsed string - JsonVariant(Internals::RawJsonString value) { - _type = Internals::JSON_UNPARSED; - _content.asString = value; - } - - // Create a JsonVariant containing a reference to an array. - // CAUTION: we are lying about constness, because the array can be modified if - // the variant is converted back to a JsonArray& - JsonVariant(const JsonArray &array); - - // Create a JsonVariant containing a reference to an object. - // CAUTION: we are lying about constness, because the object can be modified - // if the variant is converted back to a JsonObject& - JsonVariant(const JsonObject &object); - - // Get the variant as the specified type. - // - // char as() const; - // signed char as() const; - // signed short as() const; - // signed int as() const; - // signed long as() const; - // unsigned char as() const; - // unsigned short as() const; - // unsigned int as() const; - // unsigned long as() const; - template - const typename Internals::EnableIf::value, T>::type - as() const { - return variantAsInteger(); - } - // bool as() const - template - const typename Internals::EnableIf::value, T>::type - as() const { - return variantAsInteger() != 0; - } - // - // double as() const; - // float as() const; - template - const typename Internals::EnableIf::value, - T>::type - as() const { - return variantAsFloat(); - } - // - // const char* as() const; - // const char* as() const; - template - typename Internals::EnableIf::value || - Internals::IsSame::value, - const char *>::type - as() const { - return variantAsString(); - } - // - // std::string as() const; - // String as() const; - template - typename Internals::EnableIf::has_append, T>::type - as() const { - const char *cstr = variantAsString(); - if (cstr) return T(cstr); - T s; - printTo(s); - return s; - } - // - // JsonArray& as const; - // JsonArray& as const; - template - typename Internals::EnableIf< - Internals::IsSame::type, - JsonArray>::value, - JsonArray &>::type - as() const { - return variantAsArray(); - } - // - // const JsonArray& as const; - template - typename Internals::EnableIf< - Internals::IsSame::type, - const JsonArray>::value, - const JsonArray &>::type - as() const { - return variantAsArray(); - } - // - // JsonObject& as const; - // JsonObject& as const; - template - typename Internals::EnableIf< - Internals::IsSame::type, - JsonObject>::value, - JsonObject &>::type - as() const { - return variantAsObject(); - } - // - // JsonObject& as const; - // JsonObject& as const; - template - typename Internals::EnableIf< - Internals::IsSame::type, - const JsonObject>::value, - const JsonObject &>::type - as() const { - return variantAsObject(); - } - // - // JsonVariant as const; - template - typename Internals::EnableIf::value, - T>::type - as() const { - return *this; - } - - // Tells weither the variant has the specified type. - // Returns true if the variant has type type T, false otherwise. - // - // bool is() const; - // bool is() const; - // bool is() const; - // bool is() const; - // bool is() const; - // bool is() const; - // bool is() const; - // bool is() const; - // bool is() const; - template - typename Internals::EnableIf::value, bool>::type is() - const { - return variantIsInteger(); - } - // - // bool is() const; - // bool is() const; - template - typename Internals::EnableIf::value, bool>::type - is() const { - return variantIsFloat(); - } - // - // bool is() const - template - typename Internals::EnableIf::value, bool>::type - is() const { - return variantIsBoolean(); - } - // - // bool is() const; - // bool is() const; - // bool is() const; - template - typename Internals::EnableIf::value || - Internals::IsSame::value || - Internals::StringTraits::has_append, - bool>::type - is() const { - return variantIsString(); - } - // - // bool is const; - // bool is const; - // bool is const; - template - typename Internals::EnableIf< - Internals::IsSame::type>::type, - JsonArray>::value, - bool>::type - is() const { - return variantIsArray(); - } - // - // bool is const; - // bool is const; - // bool is const; - template - typename Internals::EnableIf< - Internals::IsSame::type>::type, - JsonObject>::value, - bool>::type - is() const { - return variantIsObject(); - } - - // Returns true if the variant has a value - bool success() const { - return _type != Internals::JSON_UNDEFINED; - } - - private: - JsonArray &variantAsArray() const; - JsonObject &variantAsObject() const; - const char *variantAsString() const; - template - T variantAsFloat() const; - template - T variantAsInteger() const; - bool variantIsBoolean() const; - bool variantIsFloat() const; - bool variantIsInteger() const; - bool variantIsArray() const { - return _type == Internals::JSON_ARRAY; - } - bool variantIsObject() const { - return _type == Internals::JSON_OBJECT; - } - bool variantIsString() const { - return _type == Internals::JSON_STRING || - (_type == Internals::JSON_UNPARSED && _content.asString && - !strcmp("null", _content.asString)); - } - - // The current type of the variant - Internals::JsonVariantType _type; - - // The various alternatives for the value of the variant. - Internals::JsonVariantContent _content; -}; - -DEPRECATED("Decimal places are ignored, use the float value instead") -inline JsonVariant float_with_n_digits(float value, uint8_t) { - return JsonVariant(value); -} - -DEPRECATED("Decimal places are ignored, use the double value instead") -inline JsonVariant double_with_n_digits(double value, uint8_t) { - return JsonVariant(value); -} -} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantBase.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantBase.hpp deleted file mode 100644 index 44acf2e14..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantBase.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "JsonVariantCasts.hpp" -#include "JsonVariantComparisons.hpp" -#include "JsonVariantOr.hpp" -#include "JsonVariantSubscripts.hpp" -#include "Serialization/JsonPrintable.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -class JsonVariantBase : public JsonPrintable, - public JsonVariantCasts, - public JsonVariantComparisons, - public JsonVariantOr, - public JsonVariantSubscripts, - public JsonVariantTag {}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantCasts.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantCasts.hpp deleted file mode 100644 index 68f5bd7dd..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantCasts.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Data/JsonVariantAs.hpp" -#include "Polyfills/attributes.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -class JsonVariantCasts { - public: -#if ARDUINOJSON_ENABLE_DEPRECATED - DEPRECATED("use as() instead") - FORCE_INLINE JsonArray &asArray() const { - return impl()->template as(); - } - - DEPRECATED("use as() instead") - FORCE_INLINE JsonObject &asObject() const { - return impl()->template as(); - } - - DEPRECATED("use as() instead") - FORCE_INLINE const char *asString() const { - return impl()->template as(); - } -#endif - - // Gets the variant as an array. - // Returns a reference to the JsonArray or JsonArray::invalid() if the - // variant - // is not an array. - FORCE_INLINE operator JsonArray &() const { - return impl()->template as(); - } - - // Gets the variant as an object. - // Returns a reference to the JsonObject or JsonObject::invalid() if the - // variant is not an object. - FORCE_INLINE operator JsonObject &() const { - return impl()->template as(); - } - - template - FORCE_INLINE operator T() const { - return impl()->template as(); - } - - private: - const TImpl *impl() const { - return static_cast(this); - } -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantComparisons.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantComparisons.hpp deleted file mode 100644 index 47f9d6322..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantComparisons.hpp +++ /dev/null @@ -1,139 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "StringTraits/StringTraits.hpp" -#include "TypeTraits/EnableIf.hpp" -#include "TypeTraits/IsVariant.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -class JsonVariantComparisons { - public: - template - friend bool operator==(const JsonVariantComparisons &variant, - TComparand comparand) { - return variant.equals(comparand); - } - - template - friend typename EnableIf::value, bool>::type - operator==(TComparand comparand, const JsonVariantComparisons &variant) { - return variant.equals(comparand); - } - - template - friend bool operator!=(const JsonVariantComparisons &variant, - TComparand comparand) { - return !variant.equals(comparand); - } - - template - friend typename EnableIf::value, bool>::type - operator!=(TComparand comparand, const JsonVariantComparisons &variant) { - return !variant.equals(comparand); - } - - template - friend bool operator<=(const JsonVariantComparisons &left, TComparand right) { - return left.as() <= right; - } - - template - friend bool operator<=(TComparand comparand, - const JsonVariantComparisons &variant) { - return comparand <= variant.as(); - } - - template - friend bool operator>=(const JsonVariantComparisons &variant, - TComparand comparand) { - return variant.as() >= comparand; - } - - template - friend bool operator>=(TComparand comparand, - const JsonVariantComparisons &variant) { - return comparand >= variant.as(); - } - - template - friend bool operator<(const JsonVariantComparisons &varian, - TComparand comparand) { - return varian.as() < comparand; - } - - template - friend bool operator<(TComparand comparand, - const JsonVariantComparisons &variant) { - return comparand < variant.as(); - } - - template - friend bool operator>(const JsonVariantComparisons &variant, - TComparand comparand) { - return variant.as() > comparand; - } - - template - friend bool operator>(TComparand comparand, - const JsonVariantComparisons &variant) { - return comparand > variant.as(); - } - - private: - const TImpl *impl() const { - return static_cast(this); - } - - template - const typename JsonVariantAs::type as() const { - return impl()->template as(); - } - - template - bool is() const { - return impl()->template is(); - } - - template - typename EnableIf::has_equals, bool>::type equals( - const TString &comparand) const { - const char *value = as(); - return StringTraits::equals(comparand, value); - } - - template - typename EnableIf::value && - !StringTraits::has_equals, - bool>::type - equals(const TComparand &comparand) const { - return as() == comparand; - } - - template - bool equals(const JsonVariantComparisons &right) const { - using namespace Internals; - if (is() && right.template is()) - return as() == right.template as(); - if (is() && right.template is()) - return as() == right.template as(); - if (is() && right.template is()) - return as() == right.template as(); - if (is() && right.template is()) - return as() == right.template as(); - if (is() && right.template is()) - return as() == right.template as(); - if (is() && right.template is()) - return StringTraits::equals(as(), - right.template as()); - - return false; - } -}; -} // namespace Internals -} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantImpl.hpp deleted file mode 100644 index 31f96ce1a..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantImpl.hpp +++ /dev/null @@ -1,126 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Configuration.hpp" -#include "JsonArray.hpp" -#include "JsonObject.hpp" -#include "JsonVariant.hpp" -#include "Polyfills/isFloat.hpp" -#include "Polyfills/isInteger.hpp" -#include "Polyfills/parseFloat.hpp" -#include "Polyfills/parseInteger.hpp" - -#include // for strcmp - -namespace ArduinoJson { - -inline JsonVariant::JsonVariant(const JsonArray &array) { - if (array.success()) { - _type = Internals::JSON_ARRAY; - _content.asArray = const_cast(&array); - } else { - _type = Internals::JSON_UNDEFINED; - } -} - -inline JsonVariant::JsonVariant(const JsonObject &object) { - if (object.success()) { - _type = Internals::JSON_OBJECT; - _content.asObject = const_cast(&object); - } else { - _type = Internals::JSON_UNDEFINED; - } -} - -inline JsonArray &JsonVariant::variantAsArray() const { - if (_type == Internals::JSON_ARRAY) return *_content.asArray; - return JsonArray::invalid(); -} - -inline JsonObject &JsonVariant::variantAsObject() const { - if (_type == Internals::JSON_OBJECT) return *_content.asObject; - return JsonObject::invalid(); -} - -template -inline T JsonVariant::variantAsInteger() const { - using namespace Internals; - switch (_type) { - case JSON_UNDEFINED: - return 0; - case JSON_POSITIVE_INTEGER: - case JSON_BOOLEAN: - return T(_content.asInteger); - case JSON_NEGATIVE_INTEGER: - return T(~_content.asInteger + 1); - case JSON_STRING: - case JSON_UNPARSED: - return parseInteger(_content.asString); - default: - return T(_content.asFloat); - } -} - -inline const char *JsonVariant::variantAsString() const { - using namespace Internals; - if (_type == JSON_UNPARSED && _content.asString && - !strcmp("null", _content.asString)) - return NULL; - if (_type == JSON_STRING || _type == JSON_UNPARSED) return _content.asString; - return NULL; -} - -template -inline T JsonVariant::variantAsFloat() const { - using namespace Internals; - switch (_type) { - case JSON_UNDEFINED: - return 0; - case JSON_POSITIVE_INTEGER: - case JSON_BOOLEAN: - return static_cast(_content.asInteger); - case JSON_NEGATIVE_INTEGER: - return -static_cast(_content.asInteger); - case JSON_STRING: - case JSON_UNPARSED: - return parseFloat(_content.asString); - default: - return static_cast(_content.asFloat); - } -} - -inline bool JsonVariant::variantIsBoolean() const { - using namespace Internals; - if (_type == JSON_BOOLEAN) return true; - - if (_type != JSON_UNPARSED || _content.asString == NULL) return false; - - return !strcmp(_content.asString, "true") || - !strcmp(_content.asString, "false"); -} - -inline bool JsonVariant::variantIsInteger() const { - using namespace Internals; - - return _type == JSON_POSITIVE_INTEGER || _type == JSON_NEGATIVE_INTEGER || - (_type == JSON_UNPARSED && isInteger(_content.asString)); -} - -inline bool JsonVariant::variantIsFloat() const { - using namespace Internals; - - return _type == JSON_FLOAT || _type == JSON_POSITIVE_INTEGER || - _type == JSON_NEGATIVE_INTEGER || - (_type == JSON_UNPARSED && isFloat(_content.asString)); -} - -#if ARDUINOJSON_ENABLE_STD_STREAM -inline std::ostream &operator<<(std::ostream &os, const JsonVariant &source) { - return source.printTo(os); -} -#endif - -} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantOr.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantOr.hpp deleted file mode 100644 index d8022fcb2..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantOr.hpp +++ /dev/null @@ -1,52 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Data/JsonVariantAs.hpp" -#include "Polyfills/attributes.hpp" -#include "TypeTraits/EnableIf.hpp" -#include "TypeTraits/IsIntegral.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -class JsonVariantOr { - public: - // Returns the default value if the JsonVariant is undefined of incompatible - template - typename EnableIf::value, T>::type operator|( - const T &defaultValue) const { - if (impl()->template is()) - return impl()->template as(); - else - return defaultValue; - } - - // Returns the default value if the JsonVariant is undefined of incompatible - // Special case for string: null is treated as undefined - const char *operator|(const char *defaultValue) const { - const char *value = impl()->template as(); - return value ? value : defaultValue; - } - - // Returns the default value if the JsonVariant is undefined of incompatible - // Special case for integers: we also accept double - template - typename EnableIf::value, Integer>::type operator|( - const Integer &defaultValue) const { - if (impl()->template is()) - return impl()->template as(); - else - return defaultValue; - } - - private: - const TImpl *impl() const { - return static_cast(this); - } -}; -} // namespace Internals -} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantSubscripts.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantSubscripts.hpp deleted file mode 100644 index 279ee019f..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantSubscripts.hpp +++ /dev/null @@ -1,86 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "Data/JsonVariantAs.hpp" -#include "Polyfills/attributes.hpp" -#include "StringTraits/StringTraits.hpp" -#include "TypeTraits/EnableIf.hpp" - -namespace ArduinoJson { -namespace Internals { - -// Forward declarations. -class JsonArraySubscript; -template -class JsonObjectSubscript; - -template -class JsonVariantSubscripts { - public: - // Mimics an array or an object. - // Returns the size of the array or object if the variant has that type. - // Returns 0 if the variant is neither an array nor an object - size_t size() const { - return impl()->template as().size() + - impl()->template as().size(); - } - - // Mimics an array. - // Returns the element at specified index if the variant is an array. - // Returns JsonVariant::invalid() if the variant is not an array. - FORCE_INLINE const JsonArraySubscript operator[](size_t index) const; - FORCE_INLINE JsonArraySubscript operator[](size_t index); - - // Mimics an object. - // Returns the value associated with the specified key if the variant is - // an object. - // Return JsonVariant::invalid() if the variant is not an object. - // - // const JsonObjectSubscript operator[](TKey) const; - // TKey = const std::string&, const String& - template - FORCE_INLINE - typename EnableIf::has_equals, - const JsonObjectSubscript >::type - operator[](const TString &key) const { - return impl()->template as()[key]; - } - // - // const JsonObjectSubscript operator[](TKey) const; - // TKey = const std::string&, const String& - template - FORCE_INLINE typename EnableIf::has_equals, - JsonObjectSubscript >::type - operator[](const TString &key) { - return impl()->template as()[key]; - } - // - // JsonObjectSubscript operator[](TKey); - // TKey = const char*, const char[N], const FlashStringHelper* - template - FORCE_INLINE typename EnableIf::has_equals, - JsonObjectSubscript >::type - operator[](const TString *key) { - return impl()->template as()[key]; - } - // - // JsonObjectSubscript operator[](TKey); - // TKey = const char*, const char[N], const FlashStringHelper* - template - FORCE_INLINE - typename EnableIf::has_equals, - const JsonObjectSubscript >::type - operator[](const TString *key) const { - return impl()->template as()[key]; - } - - private: - const TImpl *impl() const { - return static_cast(this); - } -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/attributes.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/attributes.hpp deleted file mode 100644 index b49091ddc..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/attributes.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#ifdef _MSC_VER // Visual Studio - -#define FORCE_INLINE // __forceinline causes C4714 when returning std::string -#define NO_INLINE __declspec(noinline) -#define DEPRECATED(msg) __declspec(deprecated(msg)) - -#elif defined(__GNUC__) // GCC or Clang - -#define FORCE_INLINE __attribute__((always_inline)) -#define NO_INLINE __attribute__((noinline)) -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) -#define DEPRECATED(msg) __attribute__((deprecated(msg))) -#else -#define DEPRECATED(msg) __attribute__((deprecated)) -#endif - -#else // Other compilers - -#define FORCE_INLINE -#define NO_INLINE -#define DEPRECATED(msg) - -#endif diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/ctype.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/ctype.hpp deleted file mode 100644 index 2d52703cd..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/ctype.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -inline bool isdigit(char c) { - return '0' <= c && c <= '9'; -} - -inline bool issign(char c) { - return '-' == c || c == '+'; -} -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isFloat.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isFloat.hpp deleted file mode 100644 index 973b89fe9..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isFloat.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include // for strcmp -#include "./ctype.hpp" - -namespace ArduinoJson { -namespace Internals { - -inline bool isFloat(const char* s) { - if (!s) return false; - - if (!strcmp(s, "NaN")) return true; - if (issign(*s)) s++; - if (!strcmp(s, "Infinity")) return true; - if (*s == '\0') return false; - - while (isdigit(*s)) s++; - - if (*s == '.') { - s++; - while (isdigit(*s)) s++; - } - - if (*s == 'e' || *s == 'E') { - s++; - if (issign(*s)) s++; - if (!isdigit(*s)) return false; - while (isdigit(*s)) s++; - } - - return *s == '\0'; -} -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isInteger.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isInteger.hpp deleted file mode 100644 index 8049079a7..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isInteger.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "./ctype.hpp" - -namespace ArduinoJson { -namespace Internals { - -inline bool isInteger(const char* s) { - if (!s || !*s) return false; - if (issign(*s)) s++; - while (isdigit(*s)) s++; - return *s == '\0'; -} -} // namespace Internals -} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/math.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/math.hpp deleted file mode 100644 index 48773edd2..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/math.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { -template -bool isNaN(T x) { - return x != x; -} - -template -bool isInfinity(T x) { - return x != 0.0 && x * 2 == x; -} -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseFloat.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseFloat.hpp deleted file mode 100644 index 49b0f6fcd..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseFloat.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../TypeTraits/FloatTraits.hpp" -#include "./ctype.hpp" -#include "./math.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -inline T parseFloat(const char* s) { - typedef FloatTraits traits; - typedef typename traits::mantissa_type mantissa_t; - typedef typename traits::exponent_type exponent_t; - - if (!s) return 0; // NULL - - bool negative_result = false; - switch (*s) { - case '-': - negative_result = true; - s++; - break; - case '+': - s++; - break; - } - - if (*s == 't') return 1; // true - if (*s == 'n' || *s == 'N') return traits::nan(); - if (*s == 'i' || *s == 'I') - return negative_result ? -traits::inf() : traits::inf(); - - mantissa_t mantissa = 0; - exponent_t exponent_offset = 0; - - while (isdigit(*s)) { - if (mantissa < traits::mantissa_max / 10) - mantissa = mantissa * 10 + (*s - '0'); - else - exponent_offset++; - s++; - } - - if (*s == '.') { - s++; - while (isdigit(*s)) { - if (mantissa < traits::mantissa_max / 10) { - mantissa = mantissa * 10 + (*s - '0'); - exponent_offset--; - } - s++; - } - } - - int exponent = 0; - if (*s == 'e' || *s == 'E') { - s++; - bool negative_exponent = false; - if (*s == '-') { - negative_exponent = true; - s++; - } else if (*s == '+') { - s++; - } - - while (isdigit(*s)) { - exponent = exponent * 10 + (*s - '0'); - if (exponent + exponent_offset > traits::exponent_max) { - if (negative_exponent) - return negative_result ? -0.0f : 0.0f; - else - return negative_result ? -traits::inf() : traits::inf(); - } - s++; - } - if (negative_exponent) exponent = -exponent; - } - exponent += exponent_offset; - - T result = traits::make_float(static_cast(mantissa), exponent); - - return negative_result ? -result : result; -} -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseInteger.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseInteger.hpp deleted file mode 100644 index e8f197494..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseInteger.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include - -#include "../Configuration.hpp" -#include "./ctype.hpp" - -namespace ArduinoJson { -namespace Internals { -template -T parseInteger(const char *s) { - if (!s) return 0; // NULL - - if (*s == 't') return 1; // "true" - - T result = 0; - bool negative_result = false; - - switch (*s) { - case '-': - negative_result = true; - s++; - break; - case '+': - s++; - break; - } - - while (isdigit(*s)) { - result = T(result * 10 + T(*s - '0')); - s++; - } - - return negative_result ? T(~result + 1) : result; -} -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/RawJson.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/RawJson.hpp deleted file mode 100644 index 4beb980ee..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/RawJson.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { - -namespace Internals { -// A special type of data that can be used to insert pregenerated JSON portions. -template -class RawJsonString { - public: - explicit RawJsonString(T str) : _str(str) {} - operator T() const { - return _str; - } - - private: - T _str; -}; - -template -struct StringTraits, void> { - static bool is_null(RawJsonString source) { - return StringTraits::is_null(static_cast(source)); - } - - typedef RawJsonString duplicate_t; - - template - static duplicate_t duplicate(RawJsonString source, Buffer* buffer) { - return duplicate_t(StringTraits::duplicate(source, buffer)); - } - - static const bool has_append = false; - static const bool has_equals = false; - static const bool should_duplicate = StringTraits::should_duplicate; -}; -} - -template -inline Internals::RawJsonString RawJson(T str) { - return Internals::RawJsonString(str); -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DummyPrint.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DummyPrint.hpp deleted file mode 100644 index 9fdf2d6a0..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DummyPrint.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A dummy Print implementation used in JsonPrintable::measureLength() -class DummyPrint { - public: - size_t print(char) { - return 1; - } - - size_t print(const char* s) { - return strlen(s); - } -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp deleted file mode 100644 index 41be6392c..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../StringTraits/StringTraits.hpp" - -namespace ArduinoJson { -namespace Internals { - -// A Print implementation that allows to write in a String -template -class DynamicStringBuilder { - public: - DynamicStringBuilder(TString &str) : _str(str) {} - - size_t print(char c) { - StringTraits::append(_str, c); - return 1; - } - - size_t print(const char *s) { - size_t initialLen = _str.length(); - StringTraits::append(_str, s); - return _str.length() - initialLen; - } - - private: - DynamicStringBuilder &operator=(const DynamicStringBuilder &); - - TString &_str; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/FloatParts.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/FloatParts.hpp deleted file mode 100644 index c14e3b553..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/FloatParts.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../Configuration.hpp" -#include "../Polyfills/math.hpp" -#include "../TypeTraits/FloatTraits.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -struct FloatParts { - uint32_t integral; - uint32_t decimal; - int16_t exponent; - int8_t decimalPlaces; - - FloatParts(TFloat value) { - uint32_t maxDecimalPart = sizeof(TFloat) >= 8 ? 1000000000 : 1000000; - decimalPlaces = sizeof(TFloat) >= 8 ? 9 : 6; - - exponent = normalize(value); - - integral = uint32_t(value); - // reduce number of decimal places by the number of integral places - for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) { - maxDecimalPart /= 10; - decimalPlaces--; - } - - TFloat remainder = (value - TFloat(integral)) * TFloat(maxDecimalPart); - - decimal = uint32_t(remainder); - remainder = remainder - TFloat(decimal); - - // rounding: - // increment by 1 if remainder >= 0.5 - decimal += uint32_t(remainder * 2); - if (decimal >= maxDecimalPart) { - decimal = 0; - integral++; - if (exponent && integral >= 10) { - exponent++; - integral = 1; - } - } - - // remove trailing zeros - while (decimal % 10 == 0 && decimalPlaces > 0) { - decimal /= 10; - decimalPlaces--; - } - } - - static int16_t normalize(TFloat& value) { - typedef FloatTraits traits; - int16_t powersOf10 = 0; - - int8_t index = sizeof(TFloat) == 8 ? 8 : 5; - int bit = 1 << index; - - if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) { - for (; index >= 0; index--) { - if (value >= traits::positiveBinaryPowerOfTen(index)) { - value *= traits::negativeBinaryPowerOfTen(index); - powersOf10 = int16_t(powersOf10 + bit); - } - bit >>= 1; - } - } - - if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) { - for (; index >= 0; index--) { - if (value < traits::negativeBinaryPowerOfTenPlusOne(index)) { - value *= traits::positiveBinaryPowerOfTen(index); - powersOf10 = int16_t(powersOf10 - bit); - } - bit >>= 1; - } - } - - return powersOf10; - } -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/IndentedPrint.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/IndentedPrint.hpp deleted file mode 100644 index 864f9aaa4..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/IndentedPrint.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// Decorator on top of Print to allow indented output. -// This class is used by JsonPrintable::prettyPrintTo() but can also be used -// for your own purpose, like logging. -template -class IndentedPrint { - public: - explicit IndentedPrint(Print &p) : sink(&p) { - level = 0; - tabSize = 2; - isNewLine = true; - } - - size_t print(char c) { - size_t n = 0; - if (isNewLine) n += writeTabs(); - n += sink->print(c); - isNewLine = c == '\n'; - return n; - } - - size_t print(const char *s) { - // TODO: optimize - size_t n = 0; - while (*s) n += print(*s++); - return n; - } - - // Adds one level of indentation - void indent() { - if (level < MAX_LEVEL) level++; - } - - // Removes one level of indentation - void unindent() { - if (level > 0) level--; - } - - // Set the number of space printed for each level of indentation - void setTabSize(uint8_t n) { - if (n < MAX_TAB_SIZE) tabSize = n & MAX_TAB_SIZE; - } - - private: - Print *sink; - uint8_t level : 4; - uint8_t tabSize : 3; - bool isNewLine : 1; - - size_t writeTabs() { - size_t n = 0; - for (int i = 0; i < level * tabSize; i++) n += sink->print(' '); - return n; - } - - static const int MAX_LEVEL = 15; // because it's only 4 bits - static const int MAX_TAB_SIZE = 7; // because it's only 3 bits -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonPrintable.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonPrintable.hpp deleted file mode 100644 index 43d413a85..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonPrintable.hpp +++ /dev/null @@ -1,117 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../Configuration.hpp" -#include "../TypeTraits/EnableIf.hpp" -#include "DummyPrint.hpp" -#include "DynamicStringBuilder.hpp" -#include "IndentedPrint.hpp" -#include "JsonSerializer.hpp" -#include "JsonWriter.hpp" -#include "Prettyfier.hpp" -#include "StaticStringBuilder.hpp" - -#if ARDUINOJSON_ENABLE_STD_STREAM -#include "StreamPrintAdapter.hpp" -#endif - -namespace ArduinoJson { -namespace Internals { - -// Implements all the overloads of printTo() and prettyPrintTo() -// Caution: this class use a template parameter to avoid virtual methods. -// This is a bit curious but allows to reduce the size of JsonVariant, JsonArray -// and JsonObject. -template -class JsonPrintable { - public: - template - typename EnableIf::has_append, size_t>::type printTo( - Print &print) const { - JsonWriter writer(print); - JsonSerializer >::serialize(downcast(), writer); - return writer.bytesWritten(); - } - -#if ARDUINOJSON_ENABLE_STD_STREAM - std::ostream &printTo(std::ostream &os) const { - StreamPrintAdapter adapter(os); - printTo(adapter); - return os; - } -#endif - - size_t printTo(char *buffer, size_t bufferSize) const { - StaticStringBuilder sb(buffer, bufferSize); - return printTo(sb); - } - - template - size_t printTo(char (&buffer)[N]) const { - return printTo(buffer, N); - } - - template - typename EnableIf::has_append, size_t>::type printTo( - TString &str) const { - DynamicStringBuilder sb(str); - return printTo(sb); - } - - template - size_t prettyPrintTo(IndentedPrint &print) const { - Prettyfier p(print); - return printTo(p); - } - - size_t prettyPrintTo(char *buffer, size_t bufferSize) const { - StaticStringBuilder sb(buffer, bufferSize); - return prettyPrintTo(sb); - } - - template - size_t prettyPrintTo(char (&buffer)[N]) const { - return prettyPrintTo(buffer, N); - } - - template - typename EnableIf::has_append, size_t>::type - prettyPrintTo(Print &print) const { - IndentedPrint indentedPrint(print); - return prettyPrintTo(indentedPrint); - } - - template - typename EnableIf::has_append, size_t>::type - prettyPrintTo(TString &str) const { - DynamicStringBuilder sb(str); - return prettyPrintTo(sb); - } - - size_t measureLength() const { - DummyPrint dp; - return printTo(dp); - } - - size_t measurePrettyLength() const { - DummyPrint dp; - return prettyPrintTo(dp); - } - - private: - const T &downcast() const { - return *static_cast(this); - } -}; - -#if ARDUINOJSON_ENABLE_STD_STREAM -template -inline std::ostream &operator<<(std::ostream &os, const JsonPrintable &v) { - return v.printTo(os); -} -#endif -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializer.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializer.hpp deleted file mode 100644 index 0cb537f7d..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializer.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "JsonWriter.hpp" - -namespace ArduinoJson { - -class JsonArray; -class JsonObject; -class JsonVariant; - -namespace Internals { - -class JsonArraySubscript; -template -class JsonObjectSubscript; - -template -class JsonSerializer { - public: - static void serialize(const JsonArray &, Writer &); - static void serialize(const JsonArraySubscript &, Writer &); - static void serialize(const JsonObject &, Writer &); - template - static void serialize(const JsonObjectSubscript &, Writer &); - static void serialize(const JsonVariant &, Writer &); -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp deleted file mode 100644 index 0faae2769..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp +++ /dev/null @@ -1,103 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../JsonArray.hpp" -#include "../JsonArraySubscript.hpp" -#include "../JsonObject.hpp" -#include "../JsonObjectSubscript.hpp" -#include "../JsonVariant.hpp" -#include "JsonSerializer.hpp" - -template -inline void ArduinoJson::Internals::JsonSerializer::serialize( - const JsonArray& array, Writer& writer) { - writer.beginArray(); - - JsonArray::const_iterator it = array.begin(); - while (it != array.end()) { - serialize(*it, writer); - - ++it; - if (it == array.end()) break; - - writer.writeComma(); - } - - writer.endArray(); -} - -template -inline void ArduinoJson::Internals::JsonSerializer::serialize( - const JsonArraySubscript& arraySubscript, Writer& writer) { - serialize(arraySubscript.as(), writer); -} - -template -inline void ArduinoJson::Internals::JsonSerializer::serialize( - const JsonObject& object, Writer& writer) { - writer.beginObject(); - - JsonObject::const_iterator it = object.begin(); - while (it != object.end()) { - writer.writeString(it->key); - writer.writeColon(); - serialize(it->value, writer); - - ++it; - if (it == object.end()) break; - - writer.writeComma(); - } - - writer.endObject(); -} - -template -template -inline void ArduinoJson::Internals::JsonSerializer::serialize( - const JsonObjectSubscript& objectSubscript, Writer& writer) { - serialize(objectSubscript.template as(), writer); -} - -template -inline void ArduinoJson::Internals::JsonSerializer::serialize( - const JsonVariant& variant, Writer& writer) { - switch (variant._type) { - case JSON_FLOAT: - writer.writeFloat(variant._content.asFloat); - return; - - case JSON_ARRAY: - serialize(*variant._content.asArray, writer); - return; - - case JSON_OBJECT: - serialize(*variant._content.asObject, writer); - return; - - case JSON_STRING: - writer.writeString(variant._content.asString); - return; - - case JSON_UNPARSED: - writer.writeRaw(variant._content.asString); - return; - - case JSON_NEGATIVE_INTEGER: - writer.writeRaw('-'); // Falls through. - - case JSON_POSITIVE_INTEGER: - writer.writeInteger(variant._content.asInteger); - return; - - case JSON_BOOLEAN: - writer.writeBoolean(variant._content.asInteger != 0); - return; - - default: // JSON_UNDEFINED - return; - } -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonWriter.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonWriter.hpp deleted file mode 100644 index 146d51dcb..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonWriter.hpp +++ /dev/null @@ -1,155 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include -#include "../Data/Encoding.hpp" -#include "../Data/JsonInteger.hpp" -#include "../Polyfills/attributes.hpp" -#include "../Serialization/FloatParts.hpp" - -namespace ArduinoJson { -namespace Internals { - -// Writes the JSON tokens to a Print implementation -// This class is used by: -// - JsonArray::writeTo() -// - JsonObject::writeTo() -// - JsonVariant::writeTo() -// Its derived by PrettyJsonWriter that overrides some members to add -// indentation. -template -class JsonWriter { - public: - explicit JsonWriter(Print &sink) : _sink(sink), _length(0) {} - - // Returns the number of bytes sent to the Print implementation. - // This is very handy for implementations of printTo() that must return the - // number of bytes written. - size_t bytesWritten() const { - return _length; - } - - void beginArray() { - writeRaw('['); - } - void endArray() { - writeRaw(']'); - } - - void beginObject() { - writeRaw('{'); - } - void endObject() { - writeRaw('}'); - } - - void writeColon() { - writeRaw(':'); - } - void writeComma() { - writeRaw(','); - } - - void writeBoolean(bool value) { - writeRaw(value ? "true" : "false"); - } - - void writeString(const char *value) { - if (!value) { - writeRaw("null"); - } else { - writeRaw('\"'); - while (*value) writeChar(*value++); - writeRaw('\"'); - } - } - - void writeChar(char c) { - char specialChar = Encoding::escapeChar(c); - if (specialChar) { - writeRaw('\\'); - writeRaw(specialChar); - } else { - writeRaw(c); - } - } - - template - void writeFloat(TFloat value) { - if (isNaN(value)) return writeRaw("NaN"); - - if (value < 0.0) { - writeRaw('-'); - value = -value; - } - - if (isInfinity(value)) return writeRaw("Infinity"); - - FloatParts parts(value); - - writeInteger(parts.integral); - if (parts.decimalPlaces) writeDecimals(parts.decimal, parts.decimalPlaces); - - if (parts.exponent < 0) { - writeRaw("e-"); - writeInteger(-parts.exponent); - } - - if (parts.exponent > 0) { - writeRaw('e'); - writeInteger(parts.exponent); - } - } - - template - void writeInteger(UInt value) { - char buffer[22]; - char *end = buffer + sizeof(buffer) - 1; - char *ptr = end; - - *ptr = 0; - do { - *--ptr = char(value % 10 + '0'); - value = UInt(value / 10); - } while (value); - - writeRaw(ptr); - } - - void writeDecimals(uint32_t value, int8_t width) { - // buffer should be big enough for all digits, the dot and the null - // terminator - char buffer[16]; - char *ptr = buffer + sizeof(buffer) - 1; - - // write the string in reverse order - *ptr = 0; - while (width--) { - *--ptr = char(value % 10 + '0'); - value /= 10; - } - *--ptr = '.'; - - // and dump it in the right order - writeRaw(ptr); - } - - void writeRaw(const char *s) { - _length += _sink.print(s); - } - void writeRaw(char c) { - _length += _sink.print(c); - } - - protected: - Print &_sink; - size_t _length; - - private: - JsonWriter &operator=(const JsonWriter &); // cannot be assigned -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/Prettyfier.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/Prettyfier.hpp deleted file mode 100644 index 8b4f0d2eb..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/Prettyfier.hpp +++ /dev/null @@ -1,133 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "IndentedPrint.hpp" - -namespace ArduinoJson { -namespace Internals { - -// Converts a compact JSON string into an indented one. -template -class Prettyfier { - public: - explicit Prettyfier(IndentedPrint& p) : _sink(p) { - _previousChar = 0; - _inString = false; - } - - size_t print(char c) { - size_t n = _inString ? handleStringChar(c) : handleMarkupChar(c); - _previousChar = c; - return n; - } - - size_t print(const char* s) { - // TODO: optimize - size_t n = 0; - while (*s) n += print(*s++); - return n; - } - - private: - Prettyfier& operator=(const Prettyfier&); // cannot be assigned - - bool inEmptyBlock() { - return _previousChar == '{' || _previousChar == '['; - } - - size_t handleStringChar(char c) { - bool isQuote = c == '"' && _previousChar != '\\'; - - if (isQuote) _inString = false; - - return _sink.print(c); - } - - size_t handleMarkupChar(char c) { - switch (c) { - case '{': - case '[': - return writeBlockOpen(c); - - case '}': - case ']': - return writeBlockClose(c); - - case ':': - return writeColon(); - - case ',': - return writeComma(); - - case '"': - return writeQuoteOpen(); - - default: - return writeNormalChar(c); - } - } - - size_t writeBlockClose(char c) { - size_t n = 0; - n += unindentIfNeeded(); - n += _sink.print(c); - return n; - } - - size_t writeBlockOpen(char c) { - size_t n = 0; - n += indentIfNeeded(); - n += _sink.print(c); - return n; - } - - size_t writeColon() { - size_t n = 0; - n += _sink.print(": "); - return n; - } - - size_t writeComma() { - size_t n = 0; - n += _sink.print(",\r\n"); - return n; - } - - size_t writeQuoteOpen() { - _inString = true; - size_t n = 0; - n += indentIfNeeded(); - n += _sink.print('"'); - return n; - } - - size_t writeNormalChar(char c) { - size_t n = 0; - n += indentIfNeeded(); - n += _sink.print(c); - return n; - } - - size_t indentIfNeeded() { - if (!inEmptyBlock()) return 0; - - _sink.indent(); - return _sink.print("\r\n"); - } - - size_t unindentIfNeeded() { - if (inEmptyBlock()) return 0; - - _sink.unindent(); - return _sink.print("\r\n"); - } - - char _previousChar; - IndentedPrint& _sink; - bool _inString; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StaticStringBuilder.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StaticStringBuilder.hpp deleted file mode 100644 index 9617bbd97..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StaticStringBuilder.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A Print implementation that allows to write in a char[] -class StaticStringBuilder { - public: - StaticStringBuilder(char *buf, size_t size) : end(buf + size - 1), p(buf) { - *p = '\0'; - } - - size_t print(char c) { - if (p >= end) return 0; - *p++ = c; - *p = '\0'; - return 1; - } - - size_t print(const char *s) { - char *begin = p; - while (p < end && *s) *p++ = *s++; - *p = '\0'; - return size_t(p - begin); - } - - private: - char *end; - char *p; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp deleted file mode 100644 index 60f0af4a3..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../Configuration.hpp" - -#if ARDUINOJSON_ENABLE_STD_STREAM - -#include - -namespace ArduinoJson { -namespace Internals { - -class StreamPrintAdapter { - public: - explicit StreamPrintAdapter(std::ostream& os) : _os(os) {} - - size_t print(char c) { - _os << c; - return 1; - } - - size_t print(const char* s) { - _os << s; - return strlen(s); - } - - private: - // cannot be assigned - StreamPrintAdapter& operator=(const StreamPrintAdapter&); - - std::ostream& _os; -}; -} -} - -#endif // ARDUINOJSON_ENABLE_STD_STREAM diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StaticJsonBuffer.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StaticJsonBuffer.hpp deleted file mode 100644 index 267d9d018..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StaticJsonBuffer.hpp +++ /dev/null @@ -1,126 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "JsonBufferBase.hpp" - -namespace ArduinoJson { -namespace Internals { - -class StaticJsonBufferBase : public JsonBufferBase { - public: - class String { - public: - String(StaticJsonBufferBase* parent) : _parent(parent) { - _start = parent->_buffer + parent->_size; - } - - void append(char c) { - if (_parent->canAlloc(1)) { - char* last = static_cast(_parent->doAlloc(1)); - *last = c; - } - } - - const char* c_str() const { - if (_parent->canAlloc(1)) { - char* last = static_cast(_parent->doAlloc(1)); - *last = '\0'; - return _start; - } else { - return NULL; - } - } - - private: - StaticJsonBufferBase* _parent; - char* _start; - }; - - StaticJsonBufferBase(char* buffer, size_t capa) - : _buffer(buffer), _capacity(capa), _size(0) {} - - // Gets the capacity of the buffer in bytes - size_t capacity() const { - return _capacity; - } - - // Gets the current usage of the buffer in bytes - size_t size() const { - return _size; - } - - // Allocates the specified amount of bytes in the buffer - virtual void* alloc(size_t bytes) { - alignNextAlloc(); - if (!canAlloc(bytes)) return NULL; - return doAlloc(bytes); - } - - // Resets the buffer. - // USE WITH CAUTION: this invalidates all previously allocated data - void clear() { - _size = 0; - } - - String startString() { - return String(this); - } - - protected: - ~StaticJsonBufferBase() {} - - private: - void alignNextAlloc() { - _size = round_size_up(_size); - } - - bool canAlloc(size_t bytes) const { - return _size + bytes <= _capacity; - } - - void* doAlloc(size_t bytes) { - void* p = &_buffer[_size]; - _size += bytes; - return p; - } - - char* _buffer; - size_t _capacity; - size_t _size; -}; -} - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnon-virtual-dtor" -#elif defined(__GNUC__) -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#pragma GCC diagnostic push -#endif -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" -#endif - -// Implements a JsonBuffer with fixed memory allocation. -// The template paramenter CAPACITY specifies the capacity of the buffer in -// bytes. -template -class StaticJsonBuffer : public Internals::StaticJsonBufferBase { - public: - explicit StaticJsonBuffer() - : Internals::StaticJsonBufferBase(_buffer, CAPACITY) {} - - private: - char _buffer[CAPACITY]; -}; -} - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#pragma GCC diagnostic pop -#endif -#endif diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/ArduinoStream.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/ArduinoStream.hpp deleted file mode 100644 index 5db0852b8..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/ArduinoStream.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#if ARDUINOJSON_ENABLE_ARDUINO_STREAM - -#include - -namespace ArduinoJson { -namespace Internals { - -struct ArduinoStreamTraits { - class Reader { - Stream& _stream; - char _current, _next; - - public: - Reader(Stream& stream) : _stream(stream), _current(0), _next(0) {} - - void move() { - _current = _next; - _next = 0; - } - - char current() { - if (!_current) _current = read(); - return _current; - } - - char next() { - // assumes that current() has been called - if (!_next) _next = read(); - return _next; - } - - private: - char read() { - // don't use _stream.read() as it ignores the timeout - char c = 0; - _stream.readBytes(&c, 1); - return c; - } - }; - - static const bool has_append = false; - static const bool has_equals = false; -}; - -template -struct StringTraits< - TStream, - // match any type that is derived from Stream: - typename EnableIf< - IsBaseOf::type>::value>::type> - : ArduinoStreamTraits {}; -} -} - -#endif diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/CharPointer.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/CharPointer.hpp deleted file mode 100644 index 98896ccfb..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/CharPointer.hpp +++ /dev/null @@ -1,64 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -template -struct CharPointerTraits { - class Reader { - const TChar* _ptr; - - public: - Reader(const TChar* ptr) - : _ptr(ptr ? ptr : reinterpret_cast("")) {} - - void move() { - ++_ptr; - } - - char current() const { - return char(_ptr[0]); - } - - char next() const { - return char(_ptr[1]); - } - }; - - static bool equals(const TChar* str, const char* expected) { - const char* actual = reinterpret_cast(str); - if (!actual || !expected) return actual == expected; - return strcmp(actual, expected) == 0; - } - - static bool is_null(const TChar* str) { - return !str; - } - - typedef const char* duplicate_t; - - template - static duplicate_t duplicate(const TChar* str, Buffer* buffer) { - if (!str) return NULL; - size_t size = strlen(reinterpret_cast(str)) + 1; - void* dup = buffer->alloc(size); - if (dup != NULL) memcpy(dup, str, size); - return static_cast(dup); - } - - static const bool has_append = false; - static const bool has_equals = true; - static const bool should_duplicate = !IsConst::value; -}; - -// char*, unsigned char*, signed char* -// const char*, const unsigned char*, const signed char* -template -struct StringTraits::value>::type> - : CharPointerTraits {}; -} // namespace Internals -} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/FlashString.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/FlashString.hpp deleted file mode 100644 index 0701b9ba2..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/FlashString.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#if ARDUINOJSON_ENABLE_PROGMEM - -namespace ArduinoJson { -namespace Internals { -template <> -struct StringTraits { - class Reader { - const char* _ptr; - - public: - Reader(const __FlashStringHelper* ptr) - : _ptr(reinterpret_cast(ptr)) {} - - void move() { - _ptr++; - } - - char current() const { - return pgm_read_byte_near(_ptr); - } - - char next() const { - return pgm_read_byte_near(_ptr + 1); - } - }; - - static bool equals(const __FlashStringHelper* str, const char* expected) { - const char* actual = reinterpret_cast(str); - if (!actual || !expected) return actual == expected; - return strcmp_P(expected, actual) == 0; - } - - static bool is_null(const __FlashStringHelper* str) { - return !str; - } - - typedef const char* duplicate_t; - - template - static duplicate_t duplicate(const __FlashStringHelper* str, Buffer* buffer) { - if (!str) return NULL; - size_t size = strlen_P((const char*)str) + 1; - void* dup = buffer->alloc(size); - if (dup != NULL) memcpy_P(dup, (const char*)str, size); - return static_cast(dup); - } - - static const bool has_append = false; - static const bool has_equals = true; - static const bool should_duplicate = true; -}; -} // namespace Internals -} // namespace ArduinoJson - -#endif diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdStream.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdStream.hpp deleted file mode 100644 index 227c74406..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdStream.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#if ARDUINOJSON_ENABLE_STD_STREAM - -#include - -namespace ArduinoJson { -namespace Internals { - -struct StdStreamTraits { - class Reader { - std::istream& _stream; - char _current, _next; - - public: - Reader(std::istream& stream) : _stream(stream), _current(0), _next(0) {} - - void move() { - _current = _next; - _next = 0; - } - - char current() { - if (!_current) _current = read(); - return _current; - } - - char next() { - // assumes that current() has been called - if (!_next) _next = read(); - return _next; - } - - private: - Reader& operator=(const Reader&); // Visual Studio C4512 - - char read() { - return _stream.eof() ? '\0' : static_cast(_stream.get()); - } - }; - - static const bool has_append = false; - static const bool has_equals = false; -}; - -template -struct StringTraits< - TStream, - // match any type that is derived from std::istream: - typename EnableIf::type>::value>::type> - : StdStreamTraits {}; -} -} - -#endif diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdString.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdString.hpp deleted file mode 100644 index 35f4461d8..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdString.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#if ARDUINOJSON_ENABLE_STD_STRING || ARDUINOJSON_ENABLE_ARDUINO_STRING - -#if ARDUINOJSON_ENABLE_ARDUINO_STRING -#include -#endif - -#if ARDUINOJSON_ENABLE_STD_STRING -#include -#endif - -namespace ArduinoJson { -namespace Internals { - -template -struct StdStringTraits { - typedef const char* duplicate_t; - - template - static duplicate_t duplicate(const TString& str, Buffer* buffer) { - if (!str.c_str()) return NULL; // <- Arduino string can return NULL - size_t size = str.length() + 1; - void* dup = buffer->alloc(size); - if (dup != NULL) memcpy(dup, str.c_str(), size); - return static_cast(dup); - } - - static bool is_null(const TString& str) { - // Arduino's String::c_str() can return NULL - return !str.c_str(); - } - - struct Reader : CharPointerTraits::Reader { - Reader(const TString& str) : CharPointerTraits::Reader(str.c_str()) {} - }; - - static bool equals(const TString& str, const char* expected) { - // Arduino's String::c_str() can return NULL - const char* actual = str.c_str(); - if (!actual || !expected) return actual == expected; - return 0 == strcmp(actual, expected); - } - - static void append(TString& str, char c) { - str += c; - } - - static void append(TString& str, const char* s) { - str += s; - } - - static const bool has_append = true; - static const bool has_equals = true; - static const bool should_duplicate = true; -}; - -#if ARDUINOJSON_ENABLE_ARDUINO_STRING -template <> -struct StringTraits : StdStringTraits {}; -template <> -struct StringTraits : StdStringTraits { -}; -#endif - -#if ARDUINOJSON_ENABLE_STD_STRING -template <> -struct StringTraits : StdStringTraits {}; -#endif -} // namespace Internals -} // namespace ArduinoJson - -#endif diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StringTraits.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StringTraits.hpp deleted file mode 100644 index dd5694b2e..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StringTraits.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include -#include "../Configuration.hpp" -#include "../TypeTraits/EnableIf.hpp" -#include "../TypeTraits/IsBaseOf.hpp" -#include "../TypeTraits/IsChar.hpp" -#include "../TypeTraits/IsConst.hpp" -#include "../TypeTraits/RemoveReference.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -struct StringTraits { - static const bool has_append = false; - static const bool has_equals = false; -}; - -template -struct StringTraits : StringTraits {}; - -template -struct StringTraits : StringTraits {}; -} -} - -#include "ArduinoStream.hpp" -#include "CharPointer.hpp" -#include "FlashString.hpp" -#include "StdStream.hpp" -#include "StdString.hpp" diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/EnableIf.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/EnableIf.hpp deleted file mode 100644 index 83fc5e07f..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/EnableIf.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that return the type T if Condition is true. -template -struct EnableIf {}; - -template -struct EnableIf { - typedef T type; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/FloatTraits.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/FloatTraits.hpp deleted file mode 100644 index 648cc82fd..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/FloatTraits.hpp +++ /dev/null @@ -1,171 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include -#include // for size_t -#include "../Configuration.hpp" -#include "../Polyfills/math.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -struct FloatTraits {}; - -template -struct FloatTraits { - typedef int64_t mantissa_type; - static const short mantissa_bits = 52; - static const mantissa_type mantissa_max = - (static_cast(1) << mantissa_bits) - 1; - - typedef int16_t exponent_type; - static const exponent_type exponent_max = 308; - - template - static T make_float(T m, TExponent e) { - if (e > 0) { - for (uint8_t index = 0; e != 0; index++) { - if (e & 1) m *= positiveBinaryPowerOfTen(index); - e >>= 1; - } - } else { - e = TExponent(-e); - for (uint8_t index = 0; e != 0; index++) { - if (e & 1) m *= negativeBinaryPowerOfTen(index); - e >>= 1; - } - } - return m; - } - - static T positiveBinaryPowerOfTen(int index) { - static T factors[] = { - 1e1, - 1e2, - 1e4, - 1e8, - 1e16, - forge(0x4693B8B5, 0xB5056E17), // 1e32 - forge(0x4D384F03, 0xE93FF9F5), // 1e64 - forge(0x5A827748, 0xF9301D32), // 1e128 - forge(0x75154FDD, 0x7F73BF3C) // 1e256 - }; - return factors[index]; - } - - static T negativeBinaryPowerOfTen(int index) { - static T factors[] = { - forge(0x3FB99999, 0x9999999A), // 1e-1 - forge(0x3F847AE1, 0x47AE147B), // 1e-2 - forge(0x3F1A36E2, 0xEB1C432D), // 1e-4 - forge(0x3E45798E, 0xE2308C3A), // 1e-8 - forge(0x3C9CD2B2, 0x97D889BC), // 1e-16 - forge(0x3949F623, 0xD5A8A733), // 1e-32 - forge(0x32A50FFD, 0x44F4A73D), // 1e-64 - forge(0x255BBA08, 0xCF8C979D), // 1e-128 - forge(0x0AC80628, 0x64AC6F43) // 1e-256 - }; - return factors[index]; - } - - static T negativeBinaryPowerOfTenPlusOne(int index) { - static T factors[] = { - 1e0, - forge(0x3FB99999, 0x9999999A), // 1e-1 - forge(0x3F50624D, 0xD2F1A9FC), // 1e-3 - forge(0x3E7AD7F2, 0x9ABCAF48), // 1e-7 - forge(0x3CD203AF, 0x9EE75616), // 1e-15 - forge(0x398039D6, 0x65896880), // 1e-31 - forge(0x32DA53FC, 0x9631D10D), // 1e-63 - forge(0x25915445, 0x81B7DEC2), // 1e-127 - forge(0x0AFE07B2, 0x7DD78B14) // 1e-255 - }; - return factors[index]; - } - - static T nan() { - return forge(0x7ff80000, 0x00000000); - } - - static T inf() { - return forge(0x7ff00000, 0x00000000); - } - - // constructs a double floating point values from its binary representation - // we use this function to workaround platforms with single precision literals - // (for example, when -fsingle-precision-constant is passed to GCC) - static T forge(uint32_t msb, uint32_t lsb) { - union { - uint64_t integerBits; - T floatBits; - }; - integerBits = (uint64_t(msb) << 32) | lsb; - return floatBits; - } -}; - -template -struct FloatTraits { - typedef int32_t mantissa_type; - static const short mantissa_bits = 23; - static const mantissa_type mantissa_max = - (static_cast(1) << mantissa_bits) - 1; - - typedef int8_t exponent_type; - static const exponent_type exponent_max = 38; - - template - static T make_float(T m, TExponent e) { - if (e > 0) { - for (uint8_t index = 0; e != 0; index++) { - if (e & 1) m *= positiveBinaryPowerOfTen(index); - e >>= 1; - } - } else { - e = -e; - for (uint8_t index = 0; e != 0; index++) { - if (e & 1) m *= negativeBinaryPowerOfTen(index); - e >>= 1; - } - } - return m; - } - - static T positiveBinaryPowerOfTen(int index) { - static T factors[] = {1e1f, 1e2f, 1e4f, 1e8f, 1e16f, 1e32f}; - return factors[index]; - } - - static T negativeBinaryPowerOfTen(int index) { - static T factors[] = {1e-1f, 1e-2f, 1e-4f, 1e-8f, 1e-16f, 1e-32f}; - return factors[index]; - } - - static T negativeBinaryPowerOfTenPlusOne(int index) { - static T factors[] = {1e0f, 1e-1f, 1e-3f, 1e-7f, 1e-15f, 1e-31f}; - return factors[index]; - } - - static T forge(uint32_t bits) { - union { - uint32_t integerBits; - T floatBits; - }; - integerBits = bits; - return floatBits; - } - - static T nan() { - return forge(0x7fc00000); - } - - static T inf() { - return forge(0x7f800000); - } -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsArray.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsArray.hpp deleted file mode 100644 index 259923115..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsArray.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that return the type T without the const modifier -template -struct IsArray { - static const bool value = false; -}; -template -struct IsArray { - static const bool value = true; -}; -template -struct IsArray { - static const bool value = true; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsBaseOf.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsBaseOf.hpp deleted file mode 100644 index bf24e965e..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsBaseOf.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that returns true if Derived inherits from TBase is an -// integral type. -template -class IsBaseOf { - protected: // <- to avoid GCC's "all member functions in class are private" - typedef char Yes[1]; - typedef char No[2]; - - static Yes &probe(const TBase *); - static No &probe(...); - - public: - enum { - value = sizeof(probe(reinterpret_cast(0))) == sizeof(Yes) - }; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsChar.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsChar.hpp deleted file mode 100644 index d97cec213..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsChar.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "IsSame.hpp" - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that returns true if T is a charater -template -struct IsChar { - static const bool value = IsSame::value || - IsSame::value || - IsSame::value; -}; - -template -struct IsChar : IsChar {}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsConst.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsConst.hpp deleted file mode 100644 index 512ee5ca0..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsConst.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that return the type T without the const modifier -template -struct IsConst { - static const bool value = false; -}; - -template -struct IsConst { - static const bool value = true; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp deleted file mode 100644 index e41a6824c..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "IsSame.hpp" - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that returns true if T is a floating point type -template -struct IsFloatingPoint { - static const bool value = IsSame::value || IsSame::value; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsIntegral.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsIntegral.hpp deleted file mode 100644 index 17ae5f284..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsIntegral.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "IsSame.hpp" -#include "IsSignedIntegral.hpp" -#include "IsUnsignedIntegral.hpp" - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that returns true if T is an integral type. -template -struct IsIntegral { - static const bool value = IsSignedIntegral::value || - IsUnsignedIntegral::value || - IsSame::value; - // CAUTION: differs from std::is_integral as it doesn't include bool -}; - -template -struct IsIntegral : IsIntegral {}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSame.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSame.hpp deleted file mode 100644 index 06567c93b..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSame.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that returns true if types T and U are the same. -template -struct IsSame { - static const bool value = false; -}; - -template -struct IsSame { - static const bool value = true; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp deleted file mode 100644 index 7334eb9c7..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../Configuration.hpp" -#include "IsSame.hpp" - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that returns true if T is an integral type. -template -struct IsSignedIntegral { - static const bool value = - IsSame::value || IsSame::value || - IsSame::value || IsSame::value || -#if ARDUINOJSON_USE_LONG_LONG - IsSame::value || -#endif -#if ARDUINOJSON_USE_INT64 - IsSame::value || -#endif - false; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp deleted file mode 100644 index 938423f5c..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../Configuration.hpp" -#include "IsSame.hpp" - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that returns true if T is an integral type. -template -struct IsUnsignedIntegral { - static const bool value = - IsSame::value || IsSame::value || - IsSame::value || IsSame::value || -#if ARDUINOJSON_USE_LONG_LONG - IsSame::value || -#endif -#if ARDUINOJSON_USE_INT64 - IsSame::value || -#endif - false; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsVariant.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsVariant.hpp deleted file mode 100644 index f8b299f7a..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsVariant.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "IsBaseOf.hpp" - -namespace ArduinoJson { -namespace Internals { - -class JsonVariantTag {}; - -template -struct IsVariant : IsBaseOf {}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveConst.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveConst.hpp deleted file mode 100644 index 39d4cb5a5..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveConst.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that return the type T without the const modifier -template -struct RemoveConst { - typedef T type; -}; -template -struct RemoveConst { - typedef T type; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveReference.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveReference.hpp deleted file mode 100644 index 395a12889..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveReference.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { - -// A meta-function that return the type T without the reference modifier. -template -struct RemoveReference { - typedef T type; -}; -template -struct RemoveReference { - typedef T type; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/version.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/version.hpp deleted file mode 100644 index 34c78461d..000000000 --- a/lib/ArduinoJson-5.13.4/src/ArduinoJson/version.hpp +++ /dev/null @@ -1,10 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#define ARDUINOJSON_VERSION "5.13.4" -#define ARDUINOJSON_VERSION_MAJOR 5 -#define ARDUINOJSON_VERSION_MINOR 13 -#define ARDUINOJSON_VERSION_REVISION 4 diff --git a/lib/C2Programmer-1.0.0/src/c2.cpp b/lib/C2Programmer-1.0.0/src/c2.cpp index 22bfbaedd..fd7e41af0 100644 --- a/lib/C2Programmer-1.0.0/src/c2.cpp +++ b/lib/C2Programmer-1.0.0/src/c2.cpp @@ -204,13 +204,33 @@ uint8_t c2_reset() { return C2_SUCCESS; } -uint8_t c2_programming_init() { +uint8_t c2_programming_init(uint8_t devid) { c2_reset(); c2_address_write(C2FPCTL); C2_DATA_WRITE_AND_CHECK(C2FPCTL_ENABLE0, 1); C2_DATA_WRITE_AND_CHECK(C2FPCTL_CORE_HALT, 1); C2_DATA_WRITE_AND_CHECK(C2FPCTL_ENABLE1, 1) C2_DELAY_MS(21); + + // device specific initialization, see https://www.silabs.com/documents/public/application-notes/AN127.pdf + switch (devid) { + case C2_DEVID_UNKNOWN: + break; + case C2_DEVID_EFM8BB1: + case C2_DEVID_EFM8BB2: + case C2_DEVID_EFM8BB3: // C2_DEVID_EFM8LB1 is the same + c2_address_write(0xFF); + C2_DATA_WRITE_AND_CHECK(0x80, 1); + C2_DELAY_US(5); + c2_address_write(0xEF); + C2_DATA_WRITE_AND_CHECK(0x02, 1); + c2_address_write(0xA9); + C2_DATA_WRITE_AND_CHECK(0x00, 1); + break; + default: + return C2_BROKEN_LINK; + } + return C2_SUCCESS; } diff --git a/lib/C2Programmer-1.0.0/src/c2.h b/lib/C2Programmer-1.0.0/src/c2.h index bc744d02f..0b80c95f3 100644 --- a/lib/C2Programmer-1.0.0/src/c2.h +++ b/lib/C2Programmer-1.0.0/src/c2.h @@ -103,6 +103,13 @@ inline void C2D_enable(bool oe) { #define C2_INBUSY 0x02 #define C2_OUTREADY 0x01 +// Device families (https://www.silabs.com/documents/public/application-notes/AN127.pdf) +#define C2_DEVID_UNKNOWN 0x00 +#define C2_DEVID_EFM8BB1 0x30 +#define C2_DEVID_EFM8BB2 0x32 +#define C2_DEVID_EFM8BB3 0x34 +#define C2_DEVID_EFM8LB1 0x34 + // Layer 1: C2 Programmig Interface (PI) Register access void c2_address_write(uint8_t address); uint8_t c2_address_read(); @@ -125,7 +132,7 @@ inline uint8_t c2_data_read(uint8_t &d, uint8_t bytes=1) { // Layer 2: Operations uint8_t c2_reset(); -uint8_t c2_programming_init(); +uint8_t c2_programming_init(uint8_t devid); uint8_t c2_block_write(uint32_t address, uint8_t *data, uint8_t len); uint8_t c2_block_read(uint32_t address, uint8_t *data, uint8_t len); uint8_t c2_eeprom_read(uint32_t address, uint8_t *data, uint8_t len); diff --git a/lib/OneWire-2.3.3.06/OneWire.cpp b/lib/OneWire-Stickbreaker-20190506-1.1/OneWire.cpp similarity index 86% rename from lib/OneWire-2.3.3.06/OneWire.cpp rename to lib/OneWire-Stickbreaker-20190506-1.1/OneWire.cpp index 5c9945d51..4476ff53a 100644 --- a/lib/OneWire-2.3.3.06/OneWire.cpp +++ b/lib/OneWire-Stickbreaker-20190506-1.1/OneWire.cpp @@ -32,6 +32,17 @@ private email about OneWire). OneWire is now very mature code. No changes other than adding definitions for newer hardware support are anticipated. +======= +Version 2.3.3 ESP32 Stickbreaker 06MAY2019 + Add a #ifdef to isolate ESP32 mods +Version 2.3.1 ESP32 everslick 30APR2018 + add IRAM_ATTR attribute to write_bit/read_bit to fix icache miss delay + https://github.com/espressif/arduino-esp32/issues/1335 + +Version 2.3 ESP32 stickbreaker 28DEC2017 + adjust to use portENTER_CRITICAL(&mux) instead of noInterrupts(); + adjust to use portEXIT_CRITICAL(&mux) instead of Interrupts(); + Version 2.3: Unknown chip fallback mode, Roger Clark Teensy-LC compatibility, Paul Stoffregen @@ -141,14 +152,18 @@ sample code bearing this copyright. #include "OneWire.h" +#ifdef ARDUINO_ARCH_ESP32 +#define noInterrupts() {portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;portENTER_CRITICAL(&mux) +#define interrupts() portEXIT_CRITICAL(&mux);} +#endif OneWire::OneWire(uint8_t pin) { - pinMode(pin, INPUT); - bitmask = PIN_TO_BITMASK(pin); - baseReg = PIN_TO_BASEREG(pin); + pinMode(pin, INPUT); + bitmask = PIN_TO_BITMASK(pin); + baseReg = PIN_TO_BASEREG(pin); #if ONEWIRE_SEARCH - reset_search(); + reset_search(); #endif } @@ -159,60 +174,65 @@ OneWire::OneWire(uint8_t pin) // // Returns 1 if a device asserted a presence pulse, 0 otherwise. // +#ifdef ARDUINO_ARCH_ESP32 +uint8_t IRAM_ATTR OneWire::reset(void) +#else uint8_t OneWire::reset(void) +#endif { - IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; - volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; - uint8_t r; - uint8_t retries = 125; - - noInterrupts(); - DIRECT_MODE_INPUT(reg, mask); - interrupts(); - // wait until the wire is high... just in case - do { - if (--retries == 0) return 0; - delayMicroseconds(2); - } while ( !DIRECT_READ(reg, mask)); - - noInterrupts(); - DIRECT_WRITE_LOW(reg, mask); - DIRECT_MODE_OUTPUT(reg, mask); // drive output low - interrupts(); - delayMicroseconds(480); - noInterrupts(); - DIRECT_MODE_INPUT(reg, mask); // allow it to float - delayMicroseconds(70); - r = !DIRECT_READ(reg, mask); - interrupts(); - delayMicroseconds(410); - return r; + IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; + volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; + uint8_t r; + uint8_t retries = 125; + noInterrupts(); + DIRECT_MODE_INPUT(reg, mask); + interrupts(); + // wait until the wire is high... just in case + do { + if (--retries == 0) return 0; + delayMicroseconds(2); + } while ( !DIRECT_READ(reg, mask)); + + noInterrupts(); + DIRECT_WRITE_LOW(reg, mask); + DIRECT_MODE_OUTPUT(reg, mask); // drive output low + delayMicroseconds(480); + DIRECT_MODE_INPUT(reg, mask); // allow it to float + delayMicroseconds(70); + r = !DIRECT_READ(reg, mask); + interrupts(); + delayMicroseconds(410); + return r; } // // Write a bit. Port and bit is used to cut lookup time and provide // more certain timing. // +#ifdef ARDUINO_ARCH_ESP32 +void IRAM_ATTR OneWire::write_bit(uint8_t v) +#else void OneWire::write_bit(uint8_t v) +#endif { IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; if (v & 1) { - noInterrupts(); + noInterrupts(); DIRECT_WRITE_LOW(reg, mask); DIRECT_MODE_OUTPUT(reg, mask); // drive output low delayMicroseconds(10); DIRECT_WRITE_HIGH(reg, mask); // drive output high - interrupts(); + interrupts(); delayMicroseconds(55); } else { - noInterrupts(); + noInterrupts(); DIRECT_WRITE_LOW(reg, mask); DIRECT_MODE_OUTPUT(reg, mask); // drive output low delayMicroseconds(65); DIRECT_WRITE_HIGH(reg, mask); // drive output high - interrupts(); + interrupts(); delayMicroseconds(5); } } @@ -221,20 +241,24 @@ void OneWire::write_bit(uint8_t v) // Read a bit. Port and bit is used to cut lookup time and provide // more certain timing. // +#ifdef ARDUINO_ARCH_ESP32 +uint8_t IRAM_ATTR OneWire::read_bit(void) +#else uint8_t OneWire::read_bit(void) +#endif { IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; uint8_t r; - noInterrupts(); + noInterrupts(); DIRECT_MODE_OUTPUT(reg, mask); DIRECT_WRITE_LOW(reg, mask); delayMicroseconds(3); DIRECT_MODE_INPUT(reg, mask); // let pin float, pull up will raise delayMicroseconds(10); r = DIRECT_READ(reg, mask); - interrupts(); + interrupts(); delayMicroseconds(53); return r; } @@ -247,17 +271,17 @@ uint8_t OneWire::read_bit(void) // other mishap. // void OneWire::write(uint8_t v, uint8_t power /* = 0 */) { - uint8_t bitMask; + uint8_t bitMask; - for (bitMask = 0x01; bitMask; bitMask <<= 1) { - OneWire::write_bit( (bitMask & v)?1:0); - } - if ( !power) { - noInterrupts(); - DIRECT_MODE_INPUT(baseReg, bitmask); - DIRECT_WRITE_LOW(baseReg, bitmask); - interrupts(); - } + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + OneWire::write_bit( (bitMask & v)?1:0); + } + if ( !power) { + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + DIRECT_WRITE_LOW(baseReg, bitmask); + interrupts(); + } } void OneWire::write_bytes(const uint8_t *buf, uint16_t count, bool power /* = 0 */) { @@ -279,7 +303,7 @@ uint8_t OneWire::read() { uint8_t r = 0; for (bitMask = 0x01; bitMask; bitMask <<= 1) { - if ( OneWire::read_bit()) r |= bitMask; + if ( OneWire::read_bit()) r |= bitMask; } return r; } @@ -311,9 +335,9 @@ void OneWire::skip() void OneWire::depower() { - noInterrupts(); - DIRECT_MODE_INPUT(baseReg, bitmask); - interrupts(); + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + interrupts(); } #if ONEWIRE_SEARCH @@ -391,13 +415,12 @@ uint8_t OneWire::search(uint8_t *newAddr, bool search_mode /* = true */) LastFamilyDiscrepancy = 0; return FALSE; } - // issue the search command if (search_mode == true) { write(0xF0); // NORMAL SEARCH } else { write(0xEC); // CONDITIONAL SEARCH - } + } // loop to do the search do @@ -405,7 +428,7 @@ uint8_t OneWire::search(uint8_t *newAddr, bool search_mode /* = true */) // read a bit and its complement id_bit = read_bit(); cmp_id_bit = read_bit(); - + // check for no devices on 1-wire if ((id_bit == 1) && (cmp_id_bit == 1)) break; @@ -459,7 +482,6 @@ uint8_t OneWire::search(uint8_t *newAddr, bool search_mode /* = true */) } } while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 - // if the search was successful then if (!(id_bit_number < 65)) { @@ -524,12 +546,12 @@ static const uint8_t PROGMEM dscrc_table[] = { // uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len) { - uint8_t crc = 0; + uint8_t crc = 0; - while (len--) { - crc = pgm_read_byte(dscrc_table + (crc ^ *addr++)); - } - return crc; + while (len--) { + crc = pgm_read_byte(dscrc_table + (crc ^ *addr++)); + } + return crc; } #else // @@ -538,22 +560,22 @@ uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len) // uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len) { - uint8_t crc = 0; + uint8_t crc = 0; - while (len--) { + while (len--) { #if defined(__AVR__) - crc = _crc_ibutton_update(crc, *addr++); + crc = _crc_ibutton_update(crc, *addr++); #else - uint8_t inbyte = *addr++; - for (uint8_t i = 8; i; i--) { - uint8_t mix = (crc ^ inbyte) & 0x01; - crc >>= 1; - if (mix) crc ^= 0x8C; - inbyte >>= 1; - } + uint8_t inbyte = *addr++; + for (uint8_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) crc ^= 0x8C; + inbyte >>= 1; + } #endif - } - return crc; + } + return crc; } #endif @@ -594,4 +616,10 @@ uint16_t OneWire::crc16(const uint8_t* input, uint16_t len, uint16_t crc) } #endif + +#ifdef ARDUINO_ARCH_ESP32 +#undef noInterrupts() +#undef interrupts() +#endif + #endif diff --git a/lib/OneWire-2.3.3.06/OneWire.h b/lib/OneWire-Stickbreaker-20190506-1.1/OneWire.h similarity index 95% rename from lib/OneWire-2.3.3.06/OneWire.h rename to lib/OneWire-Stickbreaker-20190506-1.1/OneWire.h index 47bf7c1cb..119ac5413 100644 --- a/lib/OneWire-2.3.3.06/OneWire.h +++ b/lib/OneWire-Stickbreaker-20190506-1.1/OneWire.h @@ -275,18 +275,18 @@ void directModeOutput(IO_REG_TYPE pin) #include "portable.h" #include "avr/pgmspace.h" -#define GPIO_ID(pin) (g_APinDescription[pin].ulGPIOId) -#define GPIO_TYPE(pin) (g_APinDescription[pin].ulGPIOType) -#define GPIO_BASE(pin) (g_APinDescription[pin].ulGPIOBase) -#define DIR_OFFSET_SS 0x01 -#define DIR_OFFSET_SOC 0x04 -#define EXT_PORT_OFFSET_SS 0x0A -#define EXT_PORT_OFFSET_SOC 0x50 +#define GPIO_ID(pin) (g_APinDescription[pin].ulGPIOId) +#define GPIO_TYPE(pin) (g_APinDescription[pin].ulGPIOType) +#define GPIO_BASE(pin) (g_APinDescription[pin].ulGPIOBase) +#define DIR_OFFSET_SS 0x01 +#define DIR_OFFSET_SOC 0x04 +#define EXT_PORT_OFFSET_SS 0x0A +#define EXT_PORT_OFFSET_SOC 0x50 /* GPIO registers base address */ -#define PIN_TO_BASEREG(pin) ((volatile uint32_t *)g_APinDescription[pin].ulGPIOBase) -#define PIN_TO_BITMASK(pin) pin -#define IO_REG_TYPE uint32_t +#define PIN_TO_BASEREG(pin) ((volatile uint32_t *)g_APinDescription[pin].ulGPIOBase) +#define PIN_TO_BITMASK(pin) pin +#define IO_REG_TYPE uint32_t #define IO_REG_BASE_ATTR #define IO_REG_MASK_ATTR @@ -307,7 +307,7 @@ void directModeInput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) { if (SS_GPIO == GPIO_TYPE(pin)) { WRITE_ARC_REG(READ_ARC_REG((((IO_REG_TYPE)base) + DIR_OFFSET_SS)) & ~(0x01 << GPIO_ID(pin)), - ((IO_REG_TYPE)(base) + DIR_OFFSET_SS)); + ((IO_REG_TYPE)(base) + DIR_OFFSET_SS)); } else { MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) &= ~(0x01 << GPIO_ID(pin)); } @@ -318,7 +318,7 @@ void directModeOutput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) { if (SS_GPIO == GPIO_TYPE(pin)) { WRITE_ARC_REG(READ_ARC_REG(((IO_REG_TYPE)(base) + DIR_OFFSET_SS)) | (0x01 << GPIO_ID(pin)), - ((IO_REG_TYPE)(base) + DIR_OFFSET_SS)); + ((IO_REG_TYPE)(base) + DIR_OFFSET_SS)); } else { MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) |= (0x01 << GPIO_ID(pin)); } @@ -344,11 +344,11 @@ void directWriteHigh(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) } } -#define DIRECT_READ(base, pin) directRead(base, pin) -#define DIRECT_MODE_INPUT(base, pin) directModeInput(base, pin) -#define DIRECT_MODE_OUTPUT(base, pin) directModeOutput(base, pin) -#define DIRECT_WRITE_LOW(base, pin) directWriteLow(base, pin) -#define DIRECT_WRITE_HIGH(base, pin) directWriteHigh(base, pin) +#define DIRECT_READ(base, pin) directRead(base, pin) +#define DIRECT_MODE_INPUT(base, pin) directModeInput(base, pin) +#define DIRECT_MODE_OUTPUT(base, pin) directModeOutput(base, pin) +#define DIRECT_WRITE_LOW(base, pin) directWriteLow(base, pin) +#define DIRECT_WRITE_HIGH(base, pin) directWriteHigh(base, pin) #elif defined(__riscv) diff --git a/lib/OneWire-Stickbreaker-20190506-1.1/README.md b/lib/OneWire-Stickbreaker-20190506-1.1/README.md new file mode 100644 index 000000000..fcc566fbf --- /dev/null +++ b/lib/OneWire-Stickbreaker-20190506-1.1/README.md @@ -0,0 +1,11 @@ +# OneWire library + A modification of the Arduino OneWire library maintained by @PaulStoffregen. This modifications supports the ESP32 under the Arduino-esp32 Environment. + + No changes are required for compatibility with Arduino coding. + +Original Source is Paul's 2.3 version. Forked 28DEC2017 + +@stickbreaker +V2.3.1 30APR2018 add IRAM_ATTR to read_bit() write_bit() to solve ICache miss timing failure. + thanks @everslick re: https://github.com/espressif/arduino-esp32/issues/1335 +V2.3 28DEC2017 original mods to support ESP32 diff --git a/lib/OneWire-2.3.3.06/examples/DS18x20_Temperature/DS18x20_Temperature.pde b/lib/OneWire-Stickbreaker-20190506-1.1/examples/DS18x20_Temperature/DS18x20_Temperature.pde similarity index 100% rename from lib/OneWire-2.3.3.06/examples/DS18x20_Temperature/DS18x20_Temperature.pde rename to lib/OneWire-Stickbreaker-20190506-1.1/examples/DS18x20_Temperature/DS18x20_Temperature.pde diff --git a/lib/OneWire-2.3.3.06/examples/DS2408_Switch/DS2408_Switch.pde b/lib/OneWire-Stickbreaker-20190506-1.1/examples/DS2408_Switch/DS2408_Switch.pde similarity index 100% rename from lib/OneWire-2.3.3.06/examples/DS2408_Switch/DS2408_Switch.pde rename to lib/OneWire-Stickbreaker-20190506-1.1/examples/DS2408_Switch/DS2408_Switch.pde diff --git a/lib/OneWire-2.3.3.06/examples/DS250x_PROM/DS250x_PROM.pde b/lib/OneWire-Stickbreaker-20190506-1.1/examples/DS250x_PROM/DS250x_PROM.pde similarity index 100% rename from lib/OneWire-2.3.3.06/examples/DS250x_PROM/DS250x_PROM.pde rename to lib/OneWire-Stickbreaker-20190506-1.1/examples/DS250x_PROM/DS250x_PROM.pde diff --git a/lib/OneWire-2.3.3.06/keywords.txt b/lib/OneWire-Stickbreaker-20190506-1.1/keywords.txt similarity index 100% rename from lib/OneWire-2.3.3.06/keywords.txt rename to lib/OneWire-Stickbreaker-20190506-1.1/keywords.txt diff --git a/lib/OneWire-2.3.3.06/library.json b/lib/OneWire-Stickbreaker-20190506-1.1/library.json similarity index 100% rename from lib/OneWire-2.3.3.06/library.json rename to lib/OneWire-Stickbreaker-20190506-1.1/library.json diff --git a/lib/OneWire-2.3.3.06/library.properties b/lib/OneWire-Stickbreaker-20190506-1.1/library.properties similarity index 83% rename from lib/OneWire-2.3.3.06/library.properties rename to lib/OneWire-Stickbreaker-20190506-1.1/library.properties index cd8fc05f7..2a8b08e53 100644 --- a/lib/OneWire-2.3.3.06/library.properties +++ b/lib/OneWire-Stickbreaker-20190506-1.1/library.properties @@ -3,8 +3,8 @@ version=2.3.3 author=Jim Studt, Tom Pollard, Robin James, Glenn Trewitt, Jason Dangel, Guillermo Lovato, Paul Stoffregen, Scott Roberts, Bertrik Sikken, Mark Tillotson, Ken Butcher, Roger Clark, Love Nystrom maintainer=Paul Stoffregen sentence=Access 1-wire temperature sensors, memory and other chips. -paragraph= +paragraph= Mod of Paul Stoffregen code to support ESP32 category=Communication url=http://www.pjrc.com/teensy/td_libs_OneWire.html -architectures=* +architectures=esp32 diff --git a/lib/jsmn-shadinger-1.0/README.md b/lib/jsmn-shadinger-1.0/README.md new file mode 100644 index 000000000..bf5b59565 --- /dev/null +++ b/lib/jsmn-shadinger-1.0/README.md @@ -0,0 +1,195 @@ +# JSMN lightweight JSON parser for Tasmota + +Intro: this library uses the JSMN in-place JSON parser. +See https://github.com/zserge/jsmn and https://zserge.com/jsmn/ + +It is proposed as a replacement for ArduinoJson. It has less features, does only parsing but does it in a very efficient way. + +## Benefits + +First, the memory impact is very low: 4 bytes per token and no need to add an extra buffer for values. +Second, the code is much smaller than ArduinoJson by 5-7KB. + +## How to use + +`{"Device":"0x1234","Power":true,"Temperature":25.5}` + +The JSON above is split into 8 tokens, and requires 32 bytes of dynamic memory. + +- Token 0: `OBJECT`, size=3: `{"Device":"0x1234","Power":true,"Temperature":25.5}` +- Token 1: `KEY`: `Device` +- Token 2: `STRING`: `0x1234` +- Token 3: `KEY`: `Power` +- Token 4: `BOOL_TRUE`: `true` +- Token 5: `KEY`: `Temperature` +- Token 6: `FLOAT`: `25.5` +- Token 7: `INVALID` + +If what you need is to parse a JSON Object for values with default values: +``` +#include "JsonParser.h" + +char json_buffer[] = "{\"Device\":\"0x1234\",\"Power\":true,\"Temperature\":25.6}"; +JsonParser parser(json_buffer); +JsonParserObject root = parser.getRootObject(); +if (!root) { ResponseCmndChar_P(PSTR("Invalid JSON")); return; } + +uint16_t d = root.getUInt(PSTR("DEVICE"), 0xFFFF); // case insensitive +bool b = root.getBool(PSTR("Power"), false); +float f = root.getFloat(PSTR("Temperature), -100); +``` + +Alternative pattern, if you want to test the existence of the attribute first: +``` +#include "JsonParser.h" + +char json_buffer[] = "{\"Device\":\"0x1234\",\"Power\":true,\"Temperature\":25.6}"; +JsonParser parser(json_buffer); +JsonParserObject root = parser.getRootObject(); +if (!root) { ResponseCmndChar_P(PSTR("Invalid JSON")); return; } + +JsonParserToken val = root[PSTR("DEVICE")]; +if (val) { + d = val.getUInt(); +} +val = root[PSTR("Power")]; +if (val) { + b = val.getBool(); +} +val = root[PSTR("Temperature)]; +if (val) { + f = val.getFloat(); +} +``` + +WARNING: never use the following form: +``` +JsonParserObject root = JsonParser(json_buffer).getRootObject(); +``` +In this case, the `JsonParser` object is temporary and destroyed at the end of the expression. Setting the JsonParser to a local variable ensures that the lifetime of the object is extended to the end of the scope. + +## Types and conversion + +JSMN relies on the concept of JSON Tokens `JsonParserToken`. Tokens do not hold any data, but point to token boundaries in JSON string instead. Every jsmn token has a type, which indicates the type of corresponding JSON token. JSMN for Tasmota extends the type from JSMN to ease further parsing. + +Types are: +- `INVALID` invalid token or end of stream, see Error Handling below +- `OBJECT` a JSON sub-object, `size()` contains the number of key/values of the object +- `ARRAY` a JSON array, `size()` contains the number of sub values +- `STRING` a JSON string, return the sub-string, unescaped, without surrounding quotes. UTF-8 is supported. +- `PRIMITIVE` an unrecognized JSON token, consider as an error +- `KEY` a JSON key in an object as a string +- `NULL` a JSON `null` value, automatically converted to `0` or `""` +- `BOOL_FALSE` a JSON `false` value, automatically converted to `0` or `""` +- `BOOL_TRUE` a JSON `true` value, automatically converted to `1` or `""` +- `UINT` a JSON unsigned int +- `INT` a JSON negative int +- `FLOAT` a JSON floating point value, i.e. a numerical value containing a decimal ot `.` + +Note: values are converted in this priority: 1/ `UINT`, 2/ `INT`, 3/ `FLOAT`. + +`JsonParserToken` support the following getters: +- `getBool()`: returns true if 'true' or non-zero int (default false) +- `getUInt()`: converts to unsigned int (default 0), boolean true is 1 +- `getInt()`: converts to signed int (default 0), boolean true is 1 +- `getULong()`: converts to unsigned 64 bits (default 0), boolean is 1 +- `getStr()`: converts to string (default "") + +There are variants of the same function for which you can choose the default values. Remember that using a getter if the token type is INVALID returns the default value. + +Conversion to `OBJECT` or `ARRAY`: +- `getObject()`: converts token to `OBJECT` or `INVALID` +- `getArray()`: converts token to `ARRAY` or `INVALID` + +For `JsonParserKey`: +- `getValue()`: returns the value token associated to the key + +## Checking Token types + +Type checking for `JsonParserToken`: +- `isSingleToken()` returns `true` for a single level token, `false` for `OBJECT`, `ARRAY` and `INVALID` +- `isKey()` returns `true` for a `KEY` within an `OBJECT` +- `isStr()` returns `true` for `STRING` (note: other types can still be read as strings with `getStr()`) +- `isNull()` returns `true` for `NULL` +- `isBool()` returns `true` for `BOOL_FALSE` or `BOOL_TRUE` +- `isUInt()` returns `true` for `UINT` (see below for number conversions) +- `isInt()` returns `true` for `INT` (see below for number conversions) +- `isFloat()` returns `true` for `FLOAT` (see below for number conversions) +- `isNum()` returns `true` for any numerical value, i.e. `UINT`, `INT` or `FLOAT` +- `isObject()` returns `true` for `OBJECT` +- `isArray()` returns `true` for `ARRAY` +- `isValid()` returns `true`for any type except `INVALID` + +JSMN for Tasmota provides sub-classes: +- `JsonParserKey` of type `KEY` or `INVALID`, used as a return value for Object iterators +- `JsonParserObject` of type `OBJECT` or `INVALID`, providing iterators +- `JsonParserArray` of type `ARRAY` or `INVALID`, providing iterators + +Converting from Token to Object or Array is done with `token.getObject()` or `token.getArray()`. If the conversion is invalid, the resulting object has type `INVALID` (see Error Handling). + +## Iterators and accessors for Objects and Arrays + +The `JsonParserObject` provides an easy to use iterator: +``` +JsonParserToken obj = <...> +for (auto key : obj) { + // key is of type JsonParserKey + JsonParserToken valie = key.getValue(); // retrieve the value associated to key +} +``` + +If the object contains only one element, you can retrieve it with `obj.getFirstElement()`. + +You can access any key with `obj["Key"]`. Note: the search is on purpose **case insensitive** as it is the norm in Tasmota. The search string can be in PROGMEM. If the token is not found, it is of type `INVALID`. + +The `JsonParserArray` provides an easy to use iterator: +``` +JsonParserArray arr = <...> +for (auto elt : arr) { + // elt is of type JsonParserToken +} +``` + +You can access any element in the array with the `[]` operator. Ex: `arr[0]` fof first element. If the index is invalid, the token has type `INVALID`. + +## Memory + +The `JsonParserToken` fits in 32 bits, so it can be freely returned or copied without any penalty of object copying. Hence it doesn't need the `const` modifier either, since it is always passed by value. + +## Error handling + +This library uses a `zero error` pattern. This means that calls never report any error nor throw exceptions. If something wrong happens (bad JSON, token not found...), function return an **Invalid Token**. You can call any function on an Invalid Token, they will always return the same Invalid Token (aka fixed point). + +You can easily test if the current token is invalid with the following: + +Short version: +``` +if (token) { /* token is valid */ } +``` + +Long version: +``` +if (token->isValiid()) { /* token is valid */ } +``` + +This pattern allows to cascade calls and check only the final result: +``` +char json_buffer[] = ""; +JsonParserObject json = JsonParser(json_buffer).getRootObject(); +JsonParserToken zbstatus_tok = json["ZbStatus"]; +JsonParserObject zbstatus = zbstatus_tok.getObject(); +if (zbstatus) { /* do some stuff */ + // reaching this point means: JSON is valid, there is a root object, there is a `ZbStatus` key and it contains a sub-object +} +``` + +Warning: there is an explicit convert to `bool` to allow the short version. Be careful, `(bool)token` is equivalent to `token->isValid()`, it is **NOT** equivalent to `token->getBool()`. + +## Limits + +Please keep in mind the current limits for this library: +- Maximum JSON buffer size 2047 bytes +- Maximum 63 JSON tokens +- No support for exponent in floats (i.e. `1.0e3` is invalid) + +These limits shouldn't be a problem since buffers in Tasmota are limited to 1.2KB. The support for exponent in floats is commented out and can be added if needed (slight increase in Flash size) \ No newline at end of file diff --git a/lib/jsmn-shadinger-1.0/library.properties b/lib/jsmn-shadinger-1.0/library.properties new file mode 100644 index 000000000..674aa76e7 --- /dev/null +++ b/lib/jsmn-shadinger-1.0/library.properties @@ -0,0 +1,8 @@ +name=JSMN JSON parser customized and optimized for ESP8266 and Tasmota +version=1.0 +author=Serge Zaitsev, Stephan Hadinger +maintainer=Stephan +sentence=Lightweight in-place JSON parser +paragraph= +url=https://github.com/zserge/jsmn +architectures=esp8266 diff --git a/lib/jsmn-shadinger-1.0/src/JsonGenerator.cpp b/lib/jsmn-shadinger-1.0/src/JsonGenerator.cpp new file mode 100644 index 000000000..a9554a55a --- /dev/null +++ b/lib/jsmn-shadinger-1.0/src/JsonGenerator.cpp @@ -0,0 +1,185 @@ +/* + JsonGenerator.cpp - lightweight JSON parser + + Copyright (C) 2020 Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "JsonGenerator.h" + +/*********************************************************************************************\ + * JSON Generator for Arrays +\*********************************************************************************************/ +void JsonGeneratorArray::pre(void) { + // remove trailing ']' + val.remove(val.length()-1); + if (val.length() > 1) { // if not empty, prefix with comma + val += ','; + } +} + +// void JsonGeneratorArray::post(void) { +// val += ']'; +// } + +// Add signed int (32 bits) +void JsonGeneratorArray::add(int32_t uval32) { + pre(); + val += uval32; + post(); +} + +// Add unsigned int (32 bits) +void JsonGeneratorArray::add(uint32_t uval32) { + pre(); + val += uval32; + post(); +} + +// Add a raw string, that will not be escaped. +// Can be used to add a sub-array, sub-object or 'null', 'true', 'false' values +void JsonGeneratorArray::addStrRaw(const char * sval) { + pre(); + val += sval; + post(); +} + +// Add a JSON String (will be escaped) +void JsonGeneratorArray::addStr(const char * sval) { + pre(); + val += '"'; + val += EscapeJSONString(sval).c_str(); + val += '"'; + post(); +} + +/*********************************************************************************************\ + * JSON Generator for Objects +\*********************************************************************************************/ +void JsonGeneratorObject::pre(const char * key) { + // remove trailing '}' + val.remove(val.length()-1); + if (val.length() > 1) { // if not empty, prefix with comma + val += ','; + } + val += '"'; + val += EscapeJSONString(key); + val += '"'; + val += ':'; +} + +// void JsonGeneratorObject::post(void) { +// val += '}'; +// } + +// Add signed int (32 bits) +void JsonGeneratorObject::add(const char* key, int32_t uval32) { + pre(key); + val += uval32; + post(); +} + +// Add unsigned int (32 bits) +void JsonGeneratorObject::add(const char* key, uint32_t uval32) { + pre(key); + val += uval32; + post(); +} + +void JsonGeneratorObject::add(const char* key, const String & str) { + pre(key); + val += '"'; + val += EscapeJSONString(str.c_str()).c_str(); + val += '"'; + post(); +} + +// Add a raw string, that will not be escaped. +// Can be used to add a sub-array, sub-object or 'null', 'true', 'false' values +void JsonGeneratorObject::addStrRaw(const char* key, const char * sval) { + pre(key); + val += sval; + post(); +} + +// Add a JSON String (will be escaped) +void JsonGeneratorObject::addStr(const char* key, const char * sval) { + pre(key); + val += '"'; + val += EscapeJSONString(sval).c_str(); + val += '"'; + post(); +} + +/*********************************************************************************************\ + * JSON Generator for Arrays +\*********************************************************************************************/ +// does the character needs to be escaped, and if so with which character +static char EscapeJSONChar(char c) { + if ((c == '\"') || (c == '\\')) { + return c; + } + if (c == '\n') { return 'n'; } + if (c == '\t') { return 't'; } + if (c == '\r') { return 'r'; } + if (c == '\f') { return 'f'; } + if (c == '\b') { return 'b'; } + return 0; +} + +String EscapeJSONString(const char *str) { + // As this function is used in ResponseCmndChar() and ResponseCmndIdxChar() + // it needs to be PROGMEM safe! + String r(""); + if (nullptr == str) { return r; } + + bool needs_escape = false; + size_t len_out = 1; + const char* c = str; + char ch = '.'; + while (ch != '\0') { + ch = pgm_read_byte(c++); + if (EscapeJSONChar(ch)) { + len_out++; + needs_escape = true; + } + len_out++; + } + + if (needs_escape) { + // we need to escape some chars + // allocate target buffer + r.reserve(len_out); + c = str; + char *d = r.begin(); + char ch = '.'; + while (ch != '\0') { + ch = pgm_read_byte(c++); + char c2 = EscapeJSONChar(ch); + if (c2) { + *d++ = '\\'; + *d++ = c2; + } else { + *d++ = ch; + } + } + *d = 0; // add NULL terminator + r = (char*) r.begin(); // assign the buffer to the string + } else { + r = FPSTR(str); + } + + return r; +} \ No newline at end of file diff --git a/lib/jsmn-shadinger-1.0/src/JsonGenerator.h b/lib/jsmn-shadinger-1.0/src/JsonGenerator.h new file mode 100644 index 000000000..8670aabbd --- /dev/null +++ b/lib/jsmn-shadinger-1.0/src/JsonGenerator.h @@ -0,0 +1,72 @@ +/* + JsonGenerator.h - lightweight JSON generator + + Copyright (C) 2020 Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __JSON_GENERATOR__ +#define __JSON_GENERATOR__ + +#include +#include +#include + +extern String EscapeJSONString(const char *str); + +/*********************************************************************************************\ + * JSON Generator for Arrays +\*********************************************************************************************/ +class JsonGeneratorArray { +public: + + JsonGeneratorArray(): val("[]") {} // start with empty array + + void add(uint32_t uval32); + void add(int32_t uval32); + void addStrRaw(const char * sval); + void addStr(const char * sval); + + inline String &toString(void) { return val; } + +protected: + void pre(void); + void post(void) { val += ']'; } + String val; +}; + +/*********************************************************************************************\ + * JSON Generator for Objects +\*********************************************************************************************/ +class JsonGeneratorObject { +public: + + JsonGeneratorObject(): val("{}") {} // start with empty object + + void add(const char* key, uint32_t uval32); + void add(const char* key, int32_t uval32); + void add(const char* key, const String & str); + void addStrRaw(const char* key, const char * sval); + void addStr(const char* key, const char * sval); + + inline String &toString(void) { return val; } + +protected: + void pre(const char * key); + void post(void) { val += '}'; } + String val; +}; + +#endif // __JSON_PARSER__ \ No newline at end of file diff --git a/lib/jsmn-shadinger-1.0/src/JsonParser.cpp b/lib/jsmn-shadinger-1.0/src/JsonParser.cpp new file mode 100644 index 000000000..22d89424e --- /dev/null +++ b/lib/jsmn-shadinger-1.0/src/JsonParser.cpp @@ -0,0 +1,533 @@ +/* + JsonParser.cpp - lightweight JSON parser + + Copyright (C) 2020 Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "JsonParser.h" +#include + +/*********************************************************************************************\ + * Utilities +\*********************************************************************************************/ + +const char * k_current_json_buffer = ""; + +/*********************************************************************************************\ + * Lightweight String to Float, because atof() or strtof() takes 10KB + * + * To remove code, exponents are not parsed + * (commented out below, just in case we need them after all) +\*********************************************************************************************/ +// Inspired from https://searchcode.com/codesearch/view/22115068/ +float json_strtof(const char* s) { + const char* p = s; + float value = 0.; + int32_t sign = +1; + float factor; + // unsigned int expo; + + while (isspace(*p)){ // skip any leading white-spaces + p++; + } + + switch (*p) { + case '-': sign = -1; + case '+': p++; + default : break; + } + + while ((unsigned int)(*p - '0') < 10u) { + value = value*10 + (*p++ - '0'); + } + + if (*p == '.' ) { + factor = 1.0f; + + p++; + while ((unsigned int)(*p - '0') < 10u) { + factor *= 0.1f; + value += (*p++ - '0') * factor; + } + } + +// if ( (*p | 32) == 'e' ) { +// expo = 0; +// factor = 10.L; + +// switch (*++p) { // ja hier weiß ich nicht, was mindestens nach einem 'E' folgenden MUSS. +// case '-': factor = 0.1; +// case '+': p++; +// break; +// case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': +// break; +// default : value = 0.L; +// p = s; +// goto done; +// } + +// while ( (unsigned int)(*p - '0') < 10u ) +// expo = 10 * expo + (*p++ - '0'); + +// while ( 1 ) { +// if ( expo & 1 ) +// value *= factor; +// if ( (expo >>= 1) == 0 ) +// break; +// factor *= factor; +// } +// } + +// done: + // if ( endptr != NULL ) + // *endptr = (char*)p; + + return value * sign; +} + +/*********************************************************************************************\ + * Read-only JSON token object, fits in 32 bits +\*********************************************************************************************/ + +void JsonParserToken::skipToken(void) { + // printf("skipToken type = %d %s\n", t->type, json_string + t->start); + switch (t->type) { + case JSMN_OBJECT: + skipObject(); + break; + case JSMN_ARRAY: + skipArray(); + break; + case JSMN_STRING: + case JSMN_PRIMITIVE: + case JSMN_KEY: + case JSMN_NULL: + case JSMN_BOOL_FALSE: + case JSMN_BOOL_TRUE: + case JSMN_FLOAT: + case JSMN_INT: + case JSMN_UINT: + t++; // skip 1 token + break; + case JSMN_INVALID: + default: + break; // end of stream, stop advancing + } +} + +void JsonParserToken::skipArray(void) { + if (t->type == JSMN_ARRAY) { + size_t obj_size = t->size; + t++; // array root + if (t->type == JSMN_INVALID) { return; } + for (uint32_t i=0; itype == JSMN_OBJECT) { + size_t obj_size = t->size; + t++; // object root + if (t->type == JSMN_INVALID) { return; } + for (uint32_t i=0; itype == JSMN_INVALID) { return; } + skipToken(); + } + } +} + +/*********************************************************************************************\ + * JsonParserArray +\*********************************************************************************************/ + +JsonParserArray::JsonParserArray(const jsmntok_t * token) : JsonParserToken(token) { + if (t->type != JSMN_ARRAY) { + t = &token_bad; + } +} +JsonParserArray::JsonParserArray(const JsonParserToken token) : JsonParserToken(token.t) { + if (t->type != JSMN_ARRAY) { + t = &token_bad; + } +} + +JsonParserArray::const_iterator::const_iterator(const JsonParserArray t): tok(t), remaining(0) { + if (tok.t == &token_bad) { tok.t = nullptr; } + if (nullptr != tok.t) { + // ASSERT type == JSMN_ARRAY by constructor + remaining = tok.t->size; + tok.nextOne(); // skip array root token + } +} + +JsonParserArray::const_iterator JsonParserArray::const_iterator::const_iterator::operator++() { + if (remaining <= 1) { tok.t = nullptr; } + else { + remaining--; + tok.skipToken(); // munch value + if (tok.t->type == JSMN_INVALID) { tok.t = nullptr; } // unexpected end of stream + } + return *this; +} + +JsonParserToken JsonParserArray::operator[](int32_t i) const { + if ((i >= 0) && (i < t->size)) { + uint32_t index = 0; + for (const auto elt : *this) { + if (i == index) { + return elt; + } + index++; + } + } + // fallback + return JsonParserToken(&token_bad); +} + +/*********************************************************************************************\ + * JsonParserObject +\*********************************************************************************************/ + +JsonParserObject::JsonParserObject(const jsmntok_t * token) : JsonParserToken(token) { + if (t->type != JSMN_OBJECT) { + t = &token_bad; + } +} +JsonParserObject::JsonParserObject(const JsonParserToken token) : JsonParserToken(token.t) { + if (t->type != JSMN_OBJECT) { + t = &token_bad; + } +} + +JsonParserKey JsonParserObject::getFirstElement(void) const { + if (t->size > 0) { + return JsonParserKey(t+1); // return next element and cast to Key + } else { + return JsonParserKey(&token_bad); + } +} + +JsonParserObject::const_iterator::const_iterator(const JsonParserObject t): tok(t), remaining(0) { + if (tok.t == &token_bad) { tok.t = nullptr; } + if (nullptr != tok.t) { + // ASSERT type == JSMN_OBJECT by constructor + remaining = tok.t->size; + tok.nextOne(); + } +} + +JsonParserObject::const_iterator JsonParserObject::const_iterator::operator++() { + if (remaining <= 1) { tok.t = nullptr; } + else { + remaining--; + tok.nextOne(); // munch key + if (tok.t->type == JSMN_INVALID) { tok.t = nullptr; } // unexpected end of stream + tok.skipToken(); // munch value + if (tok.t->type == JSMN_INVALID) { tok.t = nullptr; } // unexpected end of stream + } + return *this; +} + +/*********************************************************************************************\ + * JsonParserKey +\*********************************************************************************************/ + + +JsonParserKey::JsonParserKey(const jsmntok_t * token) : JsonParserToken(token) { + if (t->type != JSMN_KEY) { + t = &token_bad; + } +} +JsonParserKey::JsonParserKey(const JsonParserToken token) : JsonParserToken(token.t) { + if (t->type != JSMN_KEY) { + t = &token_bad; + } +} + +JsonParserToken JsonParserKey::getValue(void) const { + return JsonParserToken(t+1); +} + +/*********************************************************************************************\ + * Implementation for JSON Parser +\*********************************************************************************************/ + +// fall-back token object when parsing failed +const jsmntok_t token_bad = { JSMN_INVALID, 0, 0, 0 }; + +JsonParser::JsonParser(char * json_in) : + _size(0), + _token_len(0), + _tokens(nullptr), + _json(nullptr) +{ + parse(json_in); +} + +JsonParser::~JsonParser() { + this->free(); +} + +const JsonParserObject JsonParser::getRootObject(void) const { + return JsonParserObject(&_tokens[0]); +} + +const JsonParserToken JsonParser::operator[](int32_t i) const { +if ((_token_len > 0) && (i < _token_len)) { + return JsonParserToken(&_tokens[i]); + } else { + return JsonParserToken(&token_bad); + } +} + +// pointer arithmetic +// ptrdiff_t JsonParser::index(JsonParserToken token) const { +// return token.t - _tokens; +// } + +bool JsonParserToken::getBool(bool val) const { + if (t->type == JSMN_INVALID) { return val; } + if (t->type == JSMN_BOOL_TRUE) return true; + if (t->type == JSMN_BOOL_FALSE) return false; + if (isSingleToken()) return strtol(&k_current_json_buffer[t->start], nullptr, 0) != 0; + return false; +} +int32_t JsonParserToken::getInt(int32_t val) const { + if (t->type == JSMN_INVALID) { return val; } + if (t->type == JSMN_BOOL_TRUE) return 1; + if (isSingleToken()) return strtol(&k_current_json_buffer[t->start], nullptr, 0); + return 0; +} +uint32_t JsonParserToken::getUInt(uint32_t val) const { + if (t->type == JSMN_INVALID) { return val; } + if (t->type == JSMN_BOOL_TRUE) return 1; + if (isSingleToken()) return strtoul(&k_current_json_buffer[t->start], nullptr, 0); + return 0; +} +uint64_t JsonParserToken::getULong(uint64_t val) const { + if (t->type == JSMN_INVALID) { return val; } + if (t->type == JSMN_BOOL_TRUE) return 1; + if (isSingleToken()) return strtoull(&k_current_json_buffer[t->start], nullptr, 0); + return 0; +} +float JsonParserToken::getFloat(float val) const { + if (t->type == JSMN_INVALID) { return val; } + if (t->type == JSMN_BOOL_TRUE) return 1; + if (isSingleToken()) return json_strtof(&k_current_json_buffer[t->start]); + return 0; +} +const char * JsonParserToken::getStr(const char * val) const { + if (t->type == JSMN_INVALID) { return val; } + if (t->type == JSMN_NULL) return ""; + return (t->type >= JSMN_STRING) ? &k_current_json_buffer[t->start] : val; +} + + +JsonParserObject JsonParserToken::getObject(void) const { return JsonParserObject(*this); } +JsonParserArray JsonParserToken::getArray(void) const { return JsonParserArray(*this); } + + +bool JsonParserToken::getBool(void) const { return getBool(false); } +int32_t JsonParserToken::getInt(void) const { return getInt(0); } +uint32_t JsonParserToken::getUInt(void) const { return getUInt(0); } +uint64_t JsonParserToken::getULong(void) const { return getULong(0); } +float JsonParserToken::getFloat(void) const { return getFloat(0); } +const char * JsonParserToken::getStr(void) const { return getStr(""); } + +int32_t JsonParserObject::getInt(const char * needle, int32_t val) const { + return (*this)[needle].getInt(val); +} +uint32_t JsonParserObject::getUInt(const char * needle, uint32_t val) const { + return (*this)[needle].getUInt(val); +} +uint64_t JsonParserObject::getULong(const char * needle, uint64_t val) const { + return (*this)[needle].getULong(val); +} +float JsonParserObject::getFloat(const char * needle, float val) const { + return (*this)[needle].getFloat(val); +} +const char * JsonParserObject::getStr(const char * needle, const char * val) const { + return (*this)[needle].getStr(val); +} +const char * JsonParserObject::getStr(const char * needle) const { + return getStr(needle, ""); +} + +void JsonParser::parse(char * json_in) { + k_current_json_buffer = ""; + if (nullptr == json_in) { return; } + _json = json_in; + k_current_json_buffer = _json; + size_t json_len = strlen(json_in); + if (_size == 0) { + // first run is used to count tokens before allocation + jsmn_init(&this->_parser); + int32_t _token_len = jsmn_parse(&this->_parser, json_in, json_len, nullptr, 0); + if (_token_len <= 0) { return; } + _size = _token_len + 1; + } + allocate(); + jsmn_init(&this->_parser); + _token_len = jsmn_parse(&this->_parser, json_in, json_len, _tokens, _size); + // TODO error checking + if (_token_len >= 0) { + postProcess(json_len); + } +} + +// post process the parsing by pre-munching extended types +void JsonParser::postProcess(size_t json_len) { + // add an end marker + if (_size > _token_len) { + _tokens[_token_len].type = JSMN_INVALID; + _tokens[_token_len].start = json_len; + _tokens[_token_len].len = 0; + _tokens[_token_len].size = 0; + } + for (uint32_t i=0; i<_token_len; i++) { + jsmntok_t & tok = _tokens[i]; + + if (tok.type >= JSMN_STRING) { + // we modify to null-terminate the primitive + _json[tok.start + tok.len] = 0; + } + + if (tok.type == JSMN_STRING) { + if (tok.size == 1) { tok.type = JSMN_KEY; } + else { json_unescape(&_json[tok.start]); } + } else if (tok.type == JSMN_PRIMITIVE) { + if (tok.len >= 0) { + // non-null string + char c0 = _json[tok.start]; + switch (c0) { + case 'n': + case 'N': + tok.type = JSMN_NULL; + break; + case 't': + case 'T': + tok.type = JSMN_BOOL_TRUE; + break; + case 'f': + case 'F': + tok.type = JSMN_BOOL_FALSE; + break; + case '-': + case '0'...'9': + // look if there is a '.' in the string + if (nullptr != memchr(&_json[tok.start], '.', tok.len)) { + tok.type = JSMN_FLOAT; + } else if (c0 == '-') { + tok.type = JSMN_INT; + } else { + tok.type = JSMN_UINT; + } + break; + default: + tok.type = JSMN_PRIMITIVE; + break; + } + } else { + tok.type = JSMN_PRIMITIVE; + } + } + } +} + +JsonParserToken JsonParserObject::operator[](const char * needle) const { + // key can be in PROGMEM + if ((!this->isValid()) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { + return JsonParserToken(&token_bad); + } + // if needle == "?" then we return the first valid key + bool wildcard = (strcmp_P("?", needle) == 0); + + for (const auto key : *this) { + if (wildcard) { return key.getValue(); } + if (0 == strcasecmp_P(key.getStr(), needle)) { return key.getValue(); } + } + // if not found + return JsonParserToken(&token_bad); +} + +JsonParserToken JsonParserObject::operator[](const String & needle) const { + return (*this)[needle.c_str()]; +} + +JsonParserToken JsonParserObject::findStartsWith(const char * needle) const { + // key can be in PROGMEM + if ((!this->isValid()) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { + return JsonParserToken(&token_bad); + } + + String needle_s((const __FlashStringHelper *)needle); + needle_s.toLowerCase(); + + for (const auto key : *this) { + String key_s(key.getStr()); + key_s.toLowerCase(); + + if (key_s.startsWith(needle_s)) { + return key.getValue(); + } + } + // if not found + return JsonParserToken(&token_bad); +} + +const char * JsonParserObject::findConstCharNull(const char * needle) const { + const char * r = (*this)[needle].getStr(); + if (*r == 0) { r = nullptr; } // if empty string + return r; +} + +// JsonParserToken JsonParser::find(JsonParserObject obj, const char *needle, bool case_sensitive) const { +// // key can be in PROGMEM +// if ((!obj.isValid()) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { +// return JsonParserToken(&token_bad); +// } +// // if needle == "?" then we return the first valid key +// bool wildcard = (strcmp_P("?", needle) == 0); + +// for (const auto key : obj) { +// if (wildcard) { return key.getValue(); } +// if (case_sensitive) { +// if (0 == strcmp_P(this->getStr(key), needle)) { return key.getValue(); } +// } else { +// if (0 == strcasecmp_P(this->getStr(key), needle)) { return key.getValue(); } +// } +// } +// // if not found +// return JsonParserToken(&token_bad); +// } + +void JsonParser::free(void) { + if (nullptr != _tokens) { + delete[] _tokens; // TODO + _tokens = nullptr; + } +} + +void JsonParser::allocate(void) { + this->free(); + if (_size != 0) { + _tokens = new jsmntok_t[_size]; + } +} diff --git a/lib/jsmn-shadinger-1.0/src/JsonParser.h b/lib/jsmn-shadinger-1.0/src/JsonParser.h new file mode 100644 index 000000000..85d401407 --- /dev/null +++ b/lib/jsmn-shadinger-1.0/src/JsonParser.h @@ -0,0 +1,266 @@ +/* + JsonParser.h - lightweight JSON parser + + Copyright (C) 2020 Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __JSON_PARSER__ +#define __JSON_PARSER__ + +#include "jsmn.h" +#include +#include +#include + +// #define strcmp_P(x, y) strcmp(x,y) +// #define strcasecmp_P(x,y) strcasecmp(x,y) +// #define pgm_read_byte(x) (*(uint8_t*)(x)) +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/*********************************************************************************************\ + * Utilities +\*********************************************************************************************/ + +// The code uses a zero-error approach. Functions never return an error code nor an exception. +// In case an operation fails, it returns an "Invalid Token". +// To know if a token is valid, use the `isValid()` method or just use it in an if statement. +// +// Internally, the bad token is a pointer to a constant token of type JSMN_INVALID +// fall-back token object when parsing failed +const extern jsmntok_t token_bad; + +// To reduce code size, the current buffer is stored in a global variable. +// This prevents all calls to add this parameter on the stack and reduces code size. +// The caveat is that code is not re-entrant. +// If you ever intermix two or more JSON parsers, use `parser->setCurrent()` before further calls +// +// the current json buffer being used, for convenience +// Warning: this makes code non-reentrant. +extern const char * k_current_json_buffer; + +/*********************************************************************************************\ + * Read-only JSON token object, fits in 32 bits +\*********************************************************************************************/ +// forward class declarations +class JsonParserObject; +class JsonParserArray; + +class JsonParserToken { +public: + + // constructor + // If parameter is null, we use the Invalid Token instead. + JsonParserToken(const jsmntok_t * token) : t(token) { + if (nullptr == t) { t = &token_bad; } + } + JsonParserToken() : t(&token_bad) { } + // no explicit destructor (not needed) + + inline bool isValid(void) const { return t->type != JSMN_INVALID; } + inline size_t size(void) const { return t->size; } + + inline bool isSingleToken(void) const { return (t->type >= JSMN_STRING); } + inline bool isKey(void) const { return (t->type == JSMN_KEY); } + inline bool isStr(void) const { return (t->type == JSMN_STRING); } + inline bool isNull(void) const { return (t->type == JSMN_NULL); } + inline bool isBool(void) const { return (t->type == JSMN_BOOL_TRUE) || (t->type == JSMN_BOOL_FALSE); } + inline bool isFloat(void) const { return (t->type == JSMN_FLOAT); } + inline bool isInt(void) const { return (t->type == JSMN_INT); } + inline bool isUint(void) const { return (t->type == JSMN_UINT); } + inline bool isNum(void) const { return (t->type >= JSMN_FLOAT) && (t->type <= JSMN_UINT); } + inline bool isObject(void) const { return (t->type == JSMN_OBJECT); } + inline bool isArray(void) const { return (t->type == JSMN_ARRAY); } + + // move to token immediately after in the buffer + void nextOne(void) { if (t->type != JSMN_INVALID) { t++; } } + + // conversion operators + // Warning - bool does not test for Boolean value but for validity, i.e. equivalent to token.valid() + inline explicit operator bool() const { return t->type != JSMN_INVALID; }; + + // all the following conversion will try to give a meaninful value + // if the content is not of the right type or the token is invalid, returns the 'default' + bool getBool(void) const; // true if 'true' or non-zero int (default false) + int32_t getInt(void) const; // convert to int (default 0) + uint32_t getUInt(void) const; // convert to unsigned int (default 0) + uint64_t getULong(void) const; // convert to unsigned 64 bits (default 0) + float getFloat(void) const; // convert to float (default 0), does not support exponent + const char * getStr(void) const; // convert to string (default "") + + // same as above, but you can choose the default value + bool getBool(bool val) const; + int32_t getInt(int32_t val) const; + uint32_t getUInt(uint32_t val) const; + uint64_t getULong(uint64_t val) const; + float getFloat(float val) const; + const char * getStr(const char * val) const; + + // convert to JsonParserObject or JsonParserArray, or Invalid Token if not allowed + JsonParserObject getObject(void) const; + JsonParserArray getArray(void) const; + +public: + // the following should be 'protected' but then it can't be accessed by iterators + const jsmntok_t * t; + // skip the next Token as a whole (i.e. skip an entire array) + void skipToken(void); + +protected: + + // skip the next token knowing it's an array + void skipArray(void); + + // skip the next token knowing it's an object + void skipObject(void); +}; + +/*********************************************************************************************\ + * Subclass for Key +\*********************************************************************************************/ +class JsonParserKey : public JsonParserToken { +public: + JsonParserKey(const jsmntok_t * token); + explicit JsonParserKey(const JsonParserToken token); + + // get the value token associated to the key + JsonParserToken getValue(void) const; +}; + +/*********************************************************************************************\ + * Subclass for Object +\*********************************************************************************************/ +class JsonParserObject : public JsonParserToken { +public: + JsonParserObject(const jsmntok_t * token); + JsonParserObject(const JsonParserToken token); + JsonParserObject() : JsonParserToken() { } + + // find key with name, case-insensitive, '?' matches any key. Returns Invalid Token if not found + JsonParserToken operator[](const char * needle) const; + JsonParserToken operator[](const String & needle) const; + // find a key starting with `needle`, case insensitive + JsonParserToken findStartsWith(const char * needle) const; + // find a key, case-insensitive, return nullptr if not found (instead of "") + const char * findConstCharNull(const char * needle) const; + + // all-in-one methods: search for key (case insensitive), convert value and set default + int32_t getInt(const char *, int32_t) const; + uint32_t getUInt(const char *, uint32_t) const; + uint64_t getULong(const char *, uint64_t) const; + float getFloat(const char *, float) const; + const char * getStr(const char *, const char *) const; + const char * getStr(const char *) const; + + // get first element (key) + JsonParserKey getFirstElement(void) const; + + // + // const iterator + // + class const_iterator { + public: + const_iterator(const JsonParserObject t); + const_iterator operator++(); + bool operator!=(const_iterator & other) const { return tok.t != other.tok.t; } + const JsonParserKey operator*() const { return JsonParserKey(tok); } + private: + JsonParserToken tok; + size_t remaining; + }; + const_iterator begin() const { return const_iterator(*this); } // start with 'head' + const_iterator end() const { return const_iterator(JsonParserObject(&token_bad)); } // end with null pointer +}; + +/*********************************************************************************************\ + * Subclass for Array +\*********************************************************************************************/ +class JsonParserArray : public JsonParserToken { +public: + JsonParserArray(const jsmntok_t * token); + JsonParserArray(const JsonParserToken token); + JsonParserArray() : JsonParserToken() { } + + // get the element if index `i` from 0 to `size() - 1` + JsonParserToken operator[](int32_t i) const; + + // + // const iterator + // + class const_iterator { + public: + const_iterator(const JsonParserArray t); + const_iterator operator++(); + bool operator!=(const_iterator & other) const { return tok.t != other.tok.t; } + const JsonParserToken operator*() const { return tok; } + private: + JsonParserToken tok; + size_t remaining; + }; + const_iterator begin() const { return const_iterator(*this); } // start with 'head' + const_iterator end() const { return const_iterator(JsonParserArray(&token_bad)); } // end with null pointer +}; + +/*********************************************************************************************\ + * JSON Parser +\*********************************************************************************************/ + +class JsonParser { +public: + // constructor, parse the json buffer + // Warning: the buffer is modified in the process (in-place parsing) + // Input: `json_in` can be nullptr, but CANNOT be in PROGMEM (remember we need to change characters in-place) + JsonParser(char * json_in); + + // destructor + ~JsonParser(); + + // set the current buffer for attribute access (i.e. set the global) + void setCurrent(void) { k_current_json_buffer = _json; } + + // test if the parsing was successful + inline explicit operator bool() const { return _token_len > 0; } + + const JsonParserToken getRoot(void) { return JsonParserToken(&_tokens[0]); } + // const JsonParserObject getRootObject(void) { return JsonParserObject(&_tokens[0]); } + const JsonParserObject getRootObject(void) const; + + // pointer arithmetic + // ptrdiff_t index(JsonParserToken token) const; + +protected: + uint16_t _size; // size of tokens buffer + int16_t _token_len; // how many tokens have been parsed + jsmntok_t * _tokens; // pointer to token buffer + jsmn_parser _parser; // jmsn_parser structure + char * _json; // json buffer + + // disallocate token buffer + void free(void); + + // allocate token buffer of size _size + void allocate(void); + + // access tokens by index + const JsonParserToken operator[](int32_t i) const; + // parse + void parse(char * json_in); + // post-process parsing: insert NULL chars to split strings, compute a more precise token type + void postProcess(size_t json_len); +}; + +#endif // __JSON_PARSER__ \ No newline at end of file diff --git a/lib/jsmn-shadinger-1.0/src/jsmn.cpp b/lib/jsmn-shadinger-1.0/src/jsmn.cpp new file mode 100644 index 000000000..905d102f4 --- /dev/null +++ b/lib/jsmn-shadinger-1.0/src/jsmn.cpp @@ -0,0 +1,453 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * 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. + */ + +#include "jsmn.h" + +#define JSMN_STRICT // force strict mode + +const uint32_t JSMN_START_MAX = (1U << JSMN_START_B) - 1; +const uint32_t JSMN_LEN_MAX = (1U << JSMN_LEN_B) - 1; + +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = JSMN_START_MAX; + tok->len = JSMN_LEN_MAX; + tok->size = 0; + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int len) { + token->type = type; + token->start = start; + token->len = len; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos - start); + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos - start - 1); + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if ((token->start != JSMN_START_MAX) && (token->len == JSMN_LEN_MAX)) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->len = parser->pos + 1 - token->start; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if ((token->start != JSMN_START_MAX) && (token->len == JSMN_LEN_MAX)) { + parser->toksuper = i; + break; + } + } + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if ((tokens[i].start != JSMN_START_MAX) && (tokens[i].len == JSMN_LEN_MAX)) { + parser->toksuper = i; + break; + } + } + } + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': +// Add uppercase variants + case 'T': + case 'F': + case 'N': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if ((tokens[i].start != JSMN_START_MAX) && (tokens[i].len == JSMN_LEN_MAX)) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +// +// Json in-place string unescape +// inpired from https://github.com/mjansson/json/blob/master/json.h +// +//! Define a bitmask with the given number of bits set to 1 +#define JSON_BITMASK(numbits) ((1U << (numbits)) - 1) + +static uint32_t json_get_num_bytes_as_utf8(uint32_t val) { + if (val >= 0x04000000) return 6; + else if (val >= 0x00200000) return 5; + else if (val >= 0x00010000) return 4; + else if (val >= 0x00000800) return 3; + else if (val >= 0x00000080) return 2; + return 1; +} + +static uint32_t json_encode_utf8(char* str, uint32_t val) { + if (val < 0x80) { + *str = (char)val; + return 1; + } + + //Get number of _extra_ bytes + uint32_t num = json_get_num_bytes_as_utf8(val) - 1; + + *str++ = (char)((0x80U | (JSON_BITMASK(num) << (7U - num))) | + ((val >> (6U * num)) & JSON_BITMASK(6U - num))); + for (uint32_t j = 1; j <= num; ++j) + *str++ = (char)(0x80U | ((val >> (6U * (num - j))) & 0x3F)); + + return num + 1; +} + +void json_unescape(char* string) { + size_t outlength = 0; + uint32_t hexval, numbytes; + + char c; + for (uint32_t i = 0; (c = string[i]) != 0; i++) { + if ('\\' == c) { + c = string[++i]; + switch (c) { + case 0: + return; // end of stream + case '\"': + case '/': + case '\\': + string[outlength++] = c; + break; + + case 'b': + string[outlength++] = '\b'; + break; + case 'f': + string[outlength++] = '\f'; + break; + case 'r': + string[outlength++] = '\r'; + break; + case 'n': + string[outlength++] = '\n'; + break; + case 't': + string[outlength++] = '\t'; + break; + + case 'u': + { + uint32_t hexval = 0; + for (uint32_t j = 0; j < 4; ++j) { + char val = string[++i]; + if (0 == val) { return; } // we reached end of string + uint32_t uival = 0; + if ((val >= 'a') && (val <= 'f')) + uival = 10 + (val - 'a'); + else if ((val >= 'A') && (val <= 'F')) + uival = 10 + (val - 'A'); + else if ((val >= '0') && (val <= '9')) + uival = val - '0'; + hexval |= uival << (3 - j); + } + numbytes = json_get_num_bytes_as_utf8(hexval); + outlength += json_encode_utf8(string + outlength, hexval); + } + break; + + default: + break; + } + } + else { + string[outlength++] = c; + } + } +} \ No newline at end of file diff --git a/lib/jsmn-shadinger-1.0/src/jsmn.h b/lib/jsmn-shadinger-1.0/src/jsmn.h new file mode 100644 index 000000000..36fd11db8 --- /dev/null +++ b/lib/jsmn-shadinger-1.0/src/jsmn.h @@ -0,0 +1,118 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * 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 JSMN_H +#define JSMN_H + +#include +#include + +// #ifdef JSMN_STATIC +// #define JSMN_API static +// #else +#define JSMN_API extern +// #endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + // end market + JSMN_INVALID = 0, // type == 0 is invalid + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4, + // new types created during post-processing + JSMN_KEY = 5, // JSMN_STRING with size 1 + JSMN_NULL = 6, // JSMN_PRIMITIVE starting with 'n' + JSMN_BOOL_FALSE = 7, // JSMN_PRIMITIVE starting with 'f' or 'F' + JSMN_BOOL_TRUE = 8, // JSMN_PRIMITIVE starting with 't' or 'T' + JSMN_FLOAT = 9, // JSMN_PRIMITIVE starting with '.', '-', '0-9' and containing a '.' + JSMN_INT = 10, // JSMN_PRIMITIVE starting with '-', '0-9' and not containing a '.' + JSMN_UINT = 11, // JSMN_PRIMITIVE starting with '0-9' and not containing a '.' +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +// size of bitfield, sum is 32 +#define JSMN_TYPE_B 4 +#define JSMN_SIZE_B 6 // max 63 items per level (ex: max 63 keys per object) +#define JSMN_START_B 11 // max 2KB input buffer +#define JSMN_LEN_B 11 // max 2KB per item + +typedef struct jsmntok { + jsmntype_t type : JSMN_TYPE_B; + unsigned int size : JSMN_SIZE_B; + unsigned int start : JSMN_START_B; + unsigned int len : JSMN_LEN_B; +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +/** + * + * In-place json unescape + * + */ +void json_unescape(char* string); + +#endif /* JSMN_H */ diff --git a/lib/jsmn-shadinger-1.0/test/test-json.cpp b/lib/jsmn-shadinger-1.0/test/test-json.cpp new file mode 100644 index 000000000..ccfbf45fd --- /dev/null +++ b/lib/jsmn-shadinger-1.0/test/test-json.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include "../src/JsonParser.h" + + +static char test_simple[] = "{\"Device\":\"0x9C33\",\"Illuminance\":42,\"Occupancy\":1,\"Endpoint\":1,\"LinkQuality\":59}"; +static char test_moderate[] = "{\"ZbReceived\":{\"Prez\":{\"Device\":\"0x9C33\",\"Illuminance\":42,\"Occupancy\":1,\"Endpoint\":1,\"LinkQuality\":59}}}"; +static char test_complex[] = "{\"ZbStatus3\":[{\"Device\":\"0x7869\",\"INT\":-3,\"Name\":\"Tilt\",\"IEEEAddr\":\"0x00158D00031310F4\",\"ModelId\":\"lumi.vibration.aq1\",\"Manufacturer\":\"LUMI\",\"Endpoints\":{\"0x01\":{\"ProfileId\":\"0x0104\",\"ClustersIn\":[\"0x0000\",\"0x0003\",\"0x0019\",\"0x0101\"],\"ClustersOut\":[\"0x0000\",\"0x0004\",\"0x0003\",\"0x0005\",\"0x0019\",\"0x0101\"]},\"0x02\":{\"ProfileId\":\"0x0000\\ta\",\"ClustersIn\":[2],\"ClustersOut\":[-3,0.4,5.8]}}}]}"; + +int main(int argc, char* argv[]) { + printf("Starting... sizeof = %lu / %lu\n", sizeof(jsmntok_t), sizeof(JsonParserToken)); + + // char * json_str = test_complex; + char * json_str = test_simple; + + // JsonParser parser(64); // size for 64 tokens + + int r = parser.parse(json_str); + + printf("r = %d\n", r); + + for (uint32_t i=0; istart; + uint32_t len = token.t->len; + printf("Tok[%2d]= type=%s, start=%d, len=%d, size=%d, str ='%s'\n", i, JSMNTypeName(token.t->type), start, len, token.t->size, (token.t->type >= JSMN_STRING || 1) ? &json_str[start] : ""); + } + printf("==================\n"); + JsonParserObject root = parser.getRootObject(); + + for (const auto key : root) { + // printf("Index = %ld\n", parser.index(key)); + JsonParserToken value = key.getValue(); + printf("Key = %s, Val type = %s\n", parser.getStr(key), JSMNTypeName(value.t->type)); + if (value.isArray()) { + for (const auto arr_val : JsonParserArray(value)) { + printf("Array = %s, type = %s\n", parser.getStr(arr_val), JSMNTypeName(arr_val.t->type)); + } + } else { + printf("Value = %s\n", parser.getStr(value)); + } + } + + // root.nextOne(); + // printf("Index = %ld\n", parser.index(root)); + // root.skipObject(); + // printf("Index = %ld\n", parser.index(root)); + + JsonParserToken oc = parser.GetCaseInsensitive(root, "occupancy"); + printf("Looking for 'Occupancy': %s, %d\n", parser.getStr(oc), parser.getInt(oc)); + JsonParserToken oc2 = parser.GetCaseInsensitive(root, "occupanc"); + printf("Looking for 'Occupanc': %s, %d\n", parser.getStr(oc2), parser.getInt(oc2)); +} diff --git a/lib/vl53l1x-arduino-1.01/LICENSE.txt b/lib/vl53l1x-arduino-1.01/LICENSE.txt new file mode 100644 index 000000000..8a2abe9ed --- /dev/null +++ b/lib/vl53l1x-arduino-1.01/LICENSE.txt @@ -0,0 +1,42 @@ +Most of the functionality of this library is based on the VL53L1X API provided +provided by ST (STSW-IMG007), and some of the explanatory comments are quoted +or paraphrased from the API source code, API user manual (UM2356), and VL53L1X +datasheet. Therefore, the license terms for the API source code (BSD 3-clause +"New" or "Revised" License) also apply to this derivative work, as specified +below. + +For more information, see + +https://www.pololu.com/ +https://forum.pololu.com/ + +-------------------------------------------------------------------------------- + +Copyright (c) 2017, STMicroelectronics +Copyright (c) 2018, Pololu 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: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder 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 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/lib/vl53l1x-arduino-1.01/README.md b/lib/vl53l1x-arduino-1.01/README.md new file mode 100644 index 000000000..41ec21d9f --- /dev/null +++ b/lib/vl53l1x-arduino-1.01/README.md @@ -0,0 +1,167 @@ +# VL53L1X library for Arduino + +Version: 1.0.1
+Release date: 2018-09-19
+[![Build Status](https://travis-ci.org/pololu/vl53l1x-arduino.svg?branch=master)](https://travis-ci.org/pololu/vl53l1x-arduino)
+[www.pololu.com](https://www.pololu.com/) + +## Summary + +This is a library for the Arduino IDE that helps interface with ST's [VL53L1X time-of-flight distance sensor](https://www.pololu.com/product/3415). The library makes it simple to configure the sensor and read range data from it via I²C. + +## Supported platforms + +This library is designed to work with the Arduino IDE versions 1.6.x or later; we have not tested it with earlier versions. This library should support any Arduino-compatible board, including the [Pololu A-Star controllers](https://www.pololu.com/category/149/a-star-programmable-controllers). + +## Getting started + +### Hardware + +A [VL53L1X carrier](https://www.pololu.com/product/3415) can be purchased from Pololu's website. Before continuing, careful reading of the [product page](https://www.pololu.com/product/3415) as well as the VL53L1X datasheet is recommended. + +Make the following connections between the Arduino and the VL53L1X board: + +#### 5V Arduino boards + +(including Arduino Uno, Leonardo, Mega; Pololu A-Star 32U4) + + Arduino VL53L1X board + ------- ------------- + 5V - VIN + GND - GND + SDA - SDA + SCL - SCL + +#### 3.3V Arduino boards + +(including Arduino Due) + + Arduino VL53L1X board + ------- ------------- + 3V3 - VIN + GND - GND + SDA - SDA + SCL - SCL + +### Software + +If you are using version 1.6.2 or later of the [Arduino software (IDE)](http://www.arduino.cc/en/Main/Software), you can use the Library Manager to install this library: + +1. In the Arduino IDE, open the "Sketch" menu, select "Include Library", then "Manage Libraries...". +2. Search for "VL53L1X". +3. Click the VL53L1X entry in the list. +4. Click "Install". + +If this does not work, you can manually install the library: + +1. Download the [latest release archive from GitHub](https://github.com/pololu/vl53l1x-arduino/releases) and decompress it. +2. Rename the folder "vl53l1x-arduino-master" to "VL53L1X". +3. Move the "VL53L1X" folder into the "libraries" directory inside your Arduino sketchbook directory. You can view your sketchbook location by opening the "File" menu and selecting "Preferences" in the Arduino IDE. If there is not already a "libraries" folder in that location, you should make the folder yourself. +4. After installing the library, restart the Arduino IDE. + +## Examples + +Several example sketches are available that show how to use the library. You can access them from the Arduino IDE by opening the "File" menu, selecting "Examples", and then selecting "VL53L1X". If you cannot find these examples, the library was probably installed incorrectly and you should retry the installation instructions above. + +## ST's VL53L1X API and this library + +Most of the functionality of this library is based on the [VL53L1X API](http://www.st.com/content/st_com/en/products/embedded-software/proximity-sensors-software/stsw-img007.html) provided by ST (STSW-IMG007), and some of the explanatory comments in the code are quoted or paraphrased from the API source code, API user manual (UM2356), and the VL53L1X datasheet. For more explanation about the library code and how it was derived from the API, see the comments in VL53L1X.cpp. + +This library is intended to provide a quicker and easier way to get started using the VL53L1X with an Arduino-compatible controller, in contrast to using ST's API on the Arduino. The library has a more streamlined interface, as well as smaller storage and memory footprints. However, it does not currently implement some of the more advanced functionality available in the API (for example, calibrating the sensor to work well under a cover glass or selecting a smaller region of interest (ROI)), and it has less robust error checking. For advanced applications, especially when storage and memory are less of an issue, consider using the VL53L1X API directly. We have an [implementation of ST's VL53L1X API for Arduino](https://github.com/pololu/vl53l1x-st-api-arduino) available. + +## Library reference + +* `RangingData ranging_data`
+ This struct contains information about the last ranging measurement. Its members are: + * `uint16_t range_mm`
+ Range reading from the last measurement, in millimeters. (This reading can also be obtained as the return value of `read()`.) + * `RangeStatus range_status`
+ Status of the last measurement; see the definition of the `RangeStatus` enumeration type in VL53L1X.h (or the API user manual and source code) for descriptions of the possible statuses. A status of `VL53L1X::RangeValid` means there were no problems with the measurement. + * `float peak_signal_count_rate_MCPS`
+ Peak signal count rate of the last measurement, in units of mega counts per second. + * `float ambient_count_rate_MCPS`
+ Ambient count rate of the last measurement, in units of mega counts per second. + +* `uint8_t last_status`
+ The status of the last I²C write transmission. See the [`Wire.endTransmission()` documentation](http://arduino.cc/en/Reference/WireEndTransmission) for return values. + +* `VL53L1X()`
+ Constructor. + +* `void setAddress(uint8_t new_addr)`
+ Changes the I²C slave device address of the VL53L1X to the given value (7-bit). + +* `uint8_t getAddress()`
+ Returns the current I²C address. + +* `bool init(bool io_2v8 = true)`
+ Iniitializes and configures the sensor. If the optional argument `io_2v8` is true (the default if not specified), the sensor is configured for 2V8 mode (2.8 V I/O); if false, the sensor is left in 1V8 mode. The return value is a boolean indicating whether the initialization completed successfully. + +* `void writeReg(uint16_t reg, uint8_t value)`
+ Writes an 8-bit sensor register with the given value. + + Register address constants are defined by the `regAddr` enumeration type in VL53L1X.h.
+ Example use: `sensor.writeReg(VL53L1X::SOFT_RESET, 0x00);` + +* `void writeReg16Bit(uint16_t reg, uint16_t value)`
+ Writes a 16-bit sensor register with the given value. + +* `void writeReg32Bit(uint16_t reg, uint32_t value)`
+ Writes a 32-bit sensor register with the given value. + +* `uint8_t readReg(uint16_t reg)`
+ Reads an 8-bit sensor register and returns the value read. + +* `uint16_t readReg16Bit(uint16_t reg)`
+ Reads a 16-bit sensor register and returns the value read. + +* `uint32_t readReg32Bit(uint16_t reg)`
+ Reads a 32-bit sensor register and returns the value read. + +* `bool setDistanceMode(DistanceMode mode)`
+ Sets the distance mode of the sensor (`VL53L1X::Short`, `VL53L1X::Medium`, or `VL53L1X::Long`). Shorter distance modes are less affected by ambient light but have lower maximum ranges. See the datasheet for more information. The return value is a boolean indicating whether the requested mode was valid. + +* `DistanceMode getDistanceMode()`
+ Returns the previously set distance mode. + +* `bool setMeasurementTimingBudget(uint32_t budget_us)`
+ Sets the measurement timing budget to the given value in microseconds. This is the time allowed for one range measurement; a longer timing budget allows for more accurate measurements. The minimum budget is 20 ms (20000 us) in short distance mode and 33 ms for medium and long distance modes. See the VL53L1X datasheet for more information on range and timing limits. The return value is a boolean indicating whether the requested budget was valid. + +* `uint32_t getMeasurementTimingBudget()`
+ Returns the current measurement timing budget in microseconds. + +* `void startContinuous(uint32_t period_ms)`
+ Starts continuous ranging measurements. The specified inter-measurement period in milliseconds determines how often the sensor takes a measurement; if it is shorter than the timing budget, the sensor will start a new measurement as soon as the previous one finishes. + +* `void stopContinuous()`
+ Stops continuous mode. + +* `uint16_t read(bool blocking = true)`
+ After continuous ranging measurements have been started, calling this function returns a range reading in millimeters and updates the `ranging_data` struct with details about the last measurement. If the optional argument `blocking` is true (the default if not specified), this function will wait until data from a new measurement is available before returning. + + If you do not want this function to block, you can use the `dataReady()` function to check if new data is available before calling `read(false)`. Calling `read(false)` before new data is available will return a reading of 0, and `ranging_data.range_status` will have the value `VL53L1X::None`, indicating that there has been no update. + +* `uint16_t readRangeContinuousMillimeters(bool blocking = true)`
+ Alias of `read()` for convenience. + +* `bool dataReady()`
+ Returns a boolean indicating whether data from a new measurement is available from the sensor. + +* `static const char * rangeStatusToString(RangeStatus status)`
+ Converts a `RangeStatus` into a readable string describing that status. + + Note that on an AVR, the strings in this function are stored in RAM (dynamic memory), which makes working with them easier but uses up 200+ bytes of RAM (many AVR-based Arduinos only have about 2000 bytes of RAM). You can avoid this memory usage if you do not call this function in your sketch. + +* `void setTimeout(uint16_t timeout)`
+ Sets a timeout period in milliseconds after which read operations will abort if the sensor is not ready. A value of 0 disables the timeout. + +* `uint16_t getTimeout()`
+ Returns the current timeout period setting. + +* `bool timeoutOccurred()`
+ Indicates whether a read timeout has occurred since the last call to `timeoutOccurred()`. + +## Version history + +* 1.0.1 (2018-09-19): Fix Arduino 101 hanging in init(). +* 1.0.0 (2018-05-31): Original release. diff --git a/lib/vl53l1x-arduino-1.01/VL53L1X.cpp b/lib/vl53l1x-arduino-1.01/VL53L1X.cpp new file mode 100644 index 000000000..78d93e2d2 --- /dev/null +++ b/lib/vl53l1x-arduino-1.01/VL53L1X.cpp @@ -0,0 +1,788 @@ +// Most of the functionality of this library is based on the VL53L1X API +// provided by ST (STSW-IMG007), and some of the explanatory comments are quoted +// or paraphrased from the API source code, API user manual (UM2356), and +// VL53L1X datasheet. + +#include +#include + +// Constructors //////////////////////////////////////////////////////////////// + +VL53L1X::VL53L1X() + : address(AddressDefault) + , io_timeout(0) // no timeout + , did_timeout(false) + , calibrated(false) + , saved_vhv_init(0) + , saved_vhv_timeout(0) + , distance_mode(Unknown) +{ +} + +// Public Methods ////////////////////////////////////////////////////////////// + +void VL53L1X::setAddress(uint8_t new_addr) +{ + writeReg(I2C_SLAVE__DEVICE_ADDRESS, new_addr & 0x7F); + address = new_addr; +} + +// Initialize sensor using settings taken mostly from VL53L1_DataInit() and +// VL53L1_StaticInit(). +// If io_2v8 (optional) is true or not given, the sensor is configured for 2V8 +// mode. +bool VL53L1X::init(bool io_2v8) +{ + // check model ID and module type registers (values specified in datasheet) + if (readReg16Bit(IDENTIFICATION__MODEL_ID) != 0xEACC) { return false; } + + // VL53L1_software_reset() begin + + writeReg(SOFT_RESET, 0x00); + delayMicroseconds(100); + writeReg(SOFT_RESET, 0x01); + + // give it some time to boot; otherwise the sensor NACKs during the readReg() + // call below and the Arduino 101 doesn't seem to handle that well + delay(1); + + // VL53L1_poll_for_boot_completion() begin + + startTimeout(); + + // check last_status in case we still get a NACK to try to deal with it correctly + while ((readReg(FIRMWARE__SYSTEM_STATUS) & 0x01) == 0 || last_status != 0) + { + if (checkTimeoutExpired()) + { + did_timeout = true; + return false; + } + } + // VL53L1_poll_for_boot_completion() end + + // VL53L1_software_reset() end + + // VL53L1_DataInit() begin + + // sensor uses 1V8 mode for I/O by default; switch to 2V8 mode if necessary + if (io_2v8) + { + writeReg(PAD_I2C_HV__EXTSUP_CONFIG, + readReg(PAD_I2C_HV__EXTSUP_CONFIG) | 0x01); + } + + // store oscillator info for later use + fast_osc_frequency = readReg16Bit(OSC_MEASURED__FAST_OSC__FREQUENCY); + osc_calibrate_val = readReg16Bit(RESULT__OSC_CALIBRATE_VAL); + + // VL53L1_DataInit() end + + // VL53L1_StaticInit() begin + + // Note that the API does not actually apply the configuration settings below + // when VL53L1_StaticInit() is called: it keeps a copy of the sensor's + // register contents in memory and doesn't actually write them until a + // measurement is started. Writing the configuration here means we don't have + // to keep it all in memory and avoids a lot of redundant writes later. + + // the API sets the preset mode to LOWPOWER_AUTONOMOUS here: + // VL53L1_set_preset_mode() begin + + // VL53L1_preset_mode_standard_ranging() begin + + // values labeled "tuning parm default" are from vl53l1_tuning_parm_defaults.h + // (API uses these in VL53L1_init_tuning_parm_storage_struct()) + + // static config + // API resets PAD_I2C_HV__EXTSUP_CONFIG here, but maybe we don't want to do + // that? (seems like it would disable 2V8 mode) + writeReg16Bit(DSS_CONFIG__TARGET_TOTAL_RATE_MCPS, TargetRate); // should already be this value after reset + writeReg(GPIO__TIO_HV_STATUS, 0x02); + writeReg(SIGMA_ESTIMATOR__EFFECTIVE_PULSE_WIDTH_NS, 8); // tuning parm default + writeReg(SIGMA_ESTIMATOR__EFFECTIVE_AMBIENT_WIDTH_NS, 16); // tuning parm default + writeReg(ALGO__CROSSTALK_COMPENSATION_VALID_HEIGHT_MM, 0x01); + writeReg(ALGO__RANGE_IGNORE_VALID_HEIGHT_MM, 0xFF); + writeReg(ALGO__RANGE_MIN_CLIP, 0); // tuning parm default + writeReg(ALGO__CONSISTENCY_CHECK__TOLERANCE, 2); // tuning parm default + + // general config + writeReg16Bit(SYSTEM__THRESH_RATE_HIGH, 0x0000); + writeReg16Bit(SYSTEM__THRESH_RATE_LOW, 0x0000); + writeReg(DSS_CONFIG__APERTURE_ATTENUATION, 0x38); + + // timing config + // most of these settings will be determined later by distance and timing + // budget configuration + writeReg16Bit(RANGE_CONFIG__SIGMA_THRESH, 360); // tuning parm default + writeReg16Bit(RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS, 192); // tuning parm default + + // dynamic config + + writeReg(SYSTEM__GROUPED_PARAMETER_HOLD_0, 0x01); + writeReg(SYSTEM__GROUPED_PARAMETER_HOLD_1, 0x01); + writeReg(SD_CONFIG__QUANTIFIER, 2); // tuning parm default + + // VL53L1_preset_mode_standard_ranging() end + + // from VL53L1_preset_mode_timed_ranging_* + // GPH is 0 after reset, but writing GPH0 and GPH1 above seem to set GPH to 1, + // and things don't seem to work if we don't set GPH back to 0 (which the API + // does here). + writeReg(SYSTEM__GROUPED_PARAMETER_HOLD, 0x00); + writeReg(SYSTEM__SEED_CONFIG, 1); // tuning parm default + + // from VL53L1_config_low_power_auto_mode + writeReg(SYSTEM__SEQUENCE_CONFIG, 0x8B); // VHV, PHASECAL, DSS1, RANGE + writeReg16Bit(DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT, 200 << 8); + writeReg(DSS_CONFIG__ROI_MODE_CONTROL, 2); // REQUESTED_EFFFECTIVE_SPADS + + // VL53L1_set_preset_mode() end + + // default to long range, 50 ms timing budget + // note that this is different than what the API defaults to + setDistanceMode(Long); + setMeasurementTimingBudget(50000); + + // VL53L1_StaticInit() end + + // the API triggers this change in VL53L1_init_and_start_range() once a + // measurement is started; assumes MM1 and MM2 are disabled + writeReg16Bit(ALGO__PART_TO_PART_RANGE_OFFSET_MM, + readReg16Bit(MM_CONFIG__OUTER_OFFSET_MM) * 4); + + return true; +} + +// Write an 8-bit register +void VL53L1X::writeReg(uint16_t reg, uint8_t value) +{ + Wire.beginTransmission(address); + Wire.write((reg >> 8) & 0xFF); // reg high byte + Wire.write( reg & 0xFF); // reg low byte + Wire.write(value); + last_status = Wire.endTransmission(); +} + +// Write a 16-bit register +void VL53L1X::writeReg16Bit(uint16_t reg, uint16_t value) +{ + Wire.beginTransmission(address); + Wire.write((reg >> 8) & 0xFF); // reg high byte + Wire.write( reg & 0xFF); // reg low byte + Wire.write((value >> 8) & 0xFF); // value high byte + Wire.write( value & 0xFF); // value low byte + last_status = Wire.endTransmission(); +} + +// Write a 32-bit register +void VL53L1X::writeReg32Bit(uint16_t reg, uint32_t value) +{ + Wire.beginTransmission(address); + Wire.write((reg >> 8) & 0xFF); // reg high byte + Wire.write( reg & 0xFF); // reg low byte + Wire.write((value >> 24) & 0xFF); // value highest byte + Wire.write((value >> 16) & 0xFF); + Wire.write((value >> 8) & 0xFF); + Wire.write( value & 0xFF); // value lowest byte + last_status = Wire.endTransmission(); +} + +// Read an 8-bit register +uint8_t VL53L1X::readReg(regAddr reg) +{ + uint8_t value; + + Wire.beginTransmission(address); + Wire.write((reg >> 8) & 0xFF); // reg high byte + Wire.write( reg & 0xFF); // reg low byte + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, (uint8_t)1); + value = Wire.read(); + + return value; +} + +// Read a 16-bit register +uint16_t VL53L1X::readReg16Bit(uint16_t reg) +{ + uint16_t value; + + Wire.beginTransmission(address); + Wire.write((reg >> 8) & 0xFF); // reg high byte + Wire.write( reg & 0xFF); // reg low byte + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, (uint8_t)2); + value = (uint16_t)Wire.read() << 8; // value high byte + value |= Wire.read(); // value low byte + + return value; +} + +// Read a 32-bit register +uint32_t VL53L1X::readReg32Bit(uint16_t reg) +{ + uint32_t value; + + Wire.beginTransmission(address); + Wire.write((reg >> 8) & 0xFF); // reg high byte + Wire.write( reg & 0xFF); // reg low byte + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, (uint8_t)4); + value = (uint32_t)Wire.read() << 24; // value highest byte + value |= (uint32_t)Wire.read() << 16; + value |= (uint16_t)Wire.read() << 8; + value |= Wire.read(); // value lowest byte + + return value; +} + +// set distance mode to Short, Medium, or Long +// based on VL53L1_SetDistanceMode() +bool VL53L1X::setDistanceMode(DistanceMode mode) +{ + // save existing timing budget + uint32_t budget_us = getMeasurementTimingBudget(); + + switch (mode) + { + case Short: + // from VL53L1_preset_mode_standard_ranging_short_range() + + // timing config + writeReg(RANGE_CONFIG__VCSEL_PERIOD_A, 0x07); + writeReg(RANGE_CONFIG__VCSEL_PERIOD_B, 0x05); + writeReg(RANGE_CONFIG__VALID_PHASE_HIGH, 0x38); + + // dynamic config + writeReg(SD_CONFIG__WOI_SD0, 0x07); + writeReg(SD_CONFIG__WOI_SD1, 0x05); + writeReg(SD_CONFIG__INITIAL_PHASE_SD0, 6); // tuning parm default + writeReg(SD_CONFIG__INITIAL_PHASE_SD1, 6); // tuning parm default + + break; + + case Medium: + // from VL53L1_preset_mode_standard_ranging() + + // timing config + writeReg(RANGE_CONFIG__VCSEL_PERIOD_A, 0x0B); + writeReg(RANGE_CONFIG__VCSEL_PERIOD_B, 0x09); + writeReg(RANGE_CONFIG__VALID_PHASE_HIGH, 0x78); + + // dynamic config + writeReg(SD_CONFIG__WOI_SD0, 0x0B); + writeReg(SD_CONFIG__WOI_SD1, 0x09); + writeReg(SD_CONFIG__INITIAL_PHASE_SD0, 10); // tuning parm default + writeReg(SD_CONFIG__INITIAL_PHASE_SD1, 10); // tuning parm default + + break; + + case Long: // long + // from VL53L1_preset_mode_standard_ranging_long_range() + + // timing config + writeReg(RANGE_CONFIG__VCSEL_PERIOD_A, 0x0F); + writeReg(RANGE_CONFIG__VCSEL_PERIOD_B, 0x0D); + writeReg(RANGE_CONFIG__VALID_PHASE_HIGH, 0xB8); + + // dynamic config + writeReg(SD_CONFIG__WOI_SD0, 0x0F); + writeReg(SD_CONFIG__WOI_SD1, 0x0D); + writeReg(SD_CONFIG__INITIAL_PHASE_SD0, 14); // tuning parm default + writeReg(SD_CONFIG__INITIAL_PHASE_SD1, 14); // tuning parm default + + break; + + default: + // unrecognized mode - do nothing + return false; + } + + // reapply timing budget + setMeasurementTimingBudget(budget_us); + + // save mode so it can be returned by getDistanceMode() + distance_mode = mode; + + return true; +} + +// Set the measurement timing budget in microseconds, which is the time allowed +// for one measurement. A longer timing budget allows for more accurate +// measurements. +// based on VL53L1_SetMeasurementTimingBudgetMicroSeconds() +bool VL53L1X::setMeasurementTimingBudget(uint32_t budget_us) +{ + // assumes PresetMode is LOWPOWER_AUTONOMOUS + + if (budget_us <= TimingGuard) { return false; } + + uint32_t range_config_timeout_us = budget_us -= TimingGuard; + if (range_config_timeout_us > 1100000) { return false; } // FDA_MAX_TIMING_BUDGET_US * 2 + + range_config_timeout_us /= 2; + + // VL53L1_calc_timeout_register_values() begin + + uint32_t macro_period_us; + + // "Update Macro Period for Range A VCSEL Period" + macro_period_us = calcMacroPeriod(readReg(RANGE_CONFIG__VCSEL_PERIOD_A)); + + // "Update Phase timeout - uses Timing A" + // Timeout of 1000 is tuning parm default (TIMED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT) + // via VL53L1_get_preset_mode_timing_cfg(). + uint32_t phasecal_timeout_mclks = timeoutMicrosecondsToMclks(1000, macro_period_us); + if (phasecal_timeout_mclks > 0xFF) { phasecal_timeout_mclks = 0xFF; } + writeReg(PHASECAL_CONFIG__TIMEOUT_MACROP, phasecal_timeout_mclks); + + // "Update MM Timing A timeout" + // Timeout of 1 is tuning parm default (LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US_DEFAULT) + // via VL53L1_get_preset_mode_timing_cfg(). With the API, the register + // actually ends up with a slightly different value because it gets assigned, + // retrieved, recalculated with a different macro period, and reassigned, + // but it probably doesn't matter because it seems like the MM ("mode + // mitigation"?) sequence steps are disabled in low power auto mode anyway. + writeReg16Bit(MM_CONFIG__TIMEOUT_MACROP_A, encodeTimeout( + timeoutMicrosecondsToMclks(1, macro_period_us))); + + // "Update Range Timing A timeout" + writeReg16Bit(RANGE_CONFIG__TIMEOUT_MACROP_A, encodeTimeout( + timeoutMicrosecondsToMclks(range_config_timeout_us, macro_period_us))); + + // "Update Macro Period for Range B VCSEL Period" + macro_period_us = calcMacroPeriod(readReg(RANGE_CONFIG__VCSEL_PERIOD_B)); + + // "Update MM Timing B timeout" + // (See earlier comment about MM Timing A timeout.) + writeReg16Bit(MM_CONFIG__TIMEOUT_MACROP_B, encodeTimeout( + timeoutMicrosecondsToMclks(1, macro_period_us))); + + // "Update Range Timing B timeout" + writeReg16Bit(RANGE_CONFIG__TIMEOUT_MACROP_B, encodeTimeout( + timeoutMicrosecondsToMclks(range_config_timeout_us, macro_period_us))); + + // VL53L1_calc_timeout_register_values() end + + return true; +} + +// Get the measurement timing budget in microseconds +// based on VL53L1_SetMeasurementTimingBudgetMicroSeconds() +uint32_t VL53L1X::getMeasurementTimingBudget() +{ + // assumes PresetMode is LOWPOWER_AUTONOMOUS and these sequence steps are + // enabled: VHV, PHASECAL, DSS1, RANGE + + // VL53L1_get_timeouts_us() begin + + // "Update Macro Period for Range A VCSEL Period" + uint32_t macro_period_us = calcMacroPeriod(readReg(RANGE_CONFIG__VCSEL_PERIOD_A)); + + // "Get Range Timing A timeout" + + uint32_t range_config_timeout_us = timeoutMclksToMicroseconds(decodeTimeout( + readReg16Bit(RANGE_CONFIG__TIMEOUT_MACROP_A)), macro_period_us); + + // VL53L1_get_timeouts_us() end + + return 2 * range_config_timeout_us + TimingGuard; +} + +// Start continuous ranging measurements, with the given inter-measurement +// period in milliseconds determining how often the sensor takes a measurement. +void VL53L1X::startContinuous(uint32_t period_ms) +{ + // from VL53L1_set_inter_measurement_period_ms() + writeReg32Bit(SYSTEM__INTERMEASUREMENT_PERIOD, period_ms * osc_calibrate_val); + + writeReg(SYSTEM__INTERRUPT_CLEAR, 0x01); // sys_interrupt_clear_range + writeReg(SYSTEM__MODE_START, 0x40); // mode_range__timed +} + +// Stop continuous measurements +// based on VL53L1_stop_range() +void VL53L1X::stopContinuous() +{ + writeReg(SYSTEM__MODE_START, 0x80); // mode_range__abort + + // VL53L1_low_power_auto_data_stop_range() begin + + calibrated = false; + + // "restore vhv configs" + if (saved_vhv_init != 0) + { + writeReg(VHV_CONFIG__INIT, saved_vhv_init); + } + if (saved_vhv_timeout != 0) + { + writeReg(VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, saved_vhv_timeout); + } + + // "remove phasecal override" + writeReg(PHASECAL_CONFIG__OVERRIDE, 0x00); + + // VL53L1_low_power_auto_data_stop_range() end +} + +// Returns a range reading in millimeters when continuous mode is active +// (readRangeSingleMillimeters() also calls this function after starting a +// single-shot range measurement) +uint16_t VL53L1X::read(bool blocking) +{ + if (blocking) + { + startTimeout(); + while (!dataReady()) + { + if (checkTimeoutExpired()) + { + did_timeout = true; + ranging_data.range_status = None; + ranging_data.range_mm = 0; + ranging_data.peak_signal_count_rate_MCPS = 0; + ranging_data.ambient_count_rate_MCPS = 0; + return ranging_data.range_mm; + } + } + } + + readResults(); + + if (!calibrated) + { + setupManualCalibration(); + calibrated = true; + } + + updateDSS(); + + getRangingData(); + + writeReg(SYSTEM__INTERRUPT_CLEAR, 0x01); // sys_interrupt_clear_range + + return ranging_data.range_mm; +} + +// convert a RangeStatus to a readable string +// Note that on an AVR, these strings are stored in RAM (dynamic memory), which +// makes working with them easier but uses up 200+ bytes of RAM (many AVR-based +// Arduinos only have about 2000 bytes of RAM). You can avoid this memory usage +// if you do not call this function in your sketch. +const char * VL53L1X::rangeStatusToString(RangeStatus status) +{ + switch (status) + { + case RangeValid: + return "range valid"; + + case SigmaFail: + return "sigma fail"; + + case SignalFail: + return "signal fail"; + + case RangeValidMinRangeClipped: + return "range valid, min range clipped"; + + case OutOfBoundsFail: + return "out of bounds fail"; + + case HardwareFail: + return "hardware fail"; + + case RangeValidNoWrapCheckFail: + return "range valid, no wrap check fail"; + + case WrapTargetFail: + return "wrap target fail"; + + case XtalkSignalFail: + return "xtalk signal fail"; + + case SynchronizationInt: + return "synchronization int"; + + case MinRangeFail: + return "min range fail"; + + case None: + return "no update"; + + default: + return "unknown status"; + } +} + +// Did a timeout occur in one of the read functions since the last call to +// timeoutOccurred()? +bool VL53L1X::timeoutOccurred() +{ + bool tmp = did_timeout; + did_timeout = false; + return tmp; +} + +// Private Methods ///////////////////////////////////////////////////////////// + +// "Setup ranges after the first one in low power auto mode by turning off +// FW calibration steps and programming static values" +// based on VL53L1_low_power_auto_setup_manual_calibration() +void VL53L1X::setupManualCalibration() +{ + // "save original vhv configs" + saved_vhv_init = readReg(VHV_CONFIG__INIT); + saved_vhv_timeout = readReg(VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND); + + // "disable VHV init" + writeReg(VHV_CONFIG__INIT, saved_vhv_init & 0x7F); + + // "set loop bound to tuning param" + writeReg(VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, + (saved_vhv_timeout & 0x03) + (3 << 2)); // tuning parm default (LOWPOWERAUTO_VHV_LOOP_BOUND_DEFAULT) + + // "override phasecal" + writeReg(PHASECAL_CONFIG__OVERRIDE, 0x01); + writeReg(CAL_CONFIG__VCSEL_START, readReg(PHASECAL_RESULT__VCSEL_START)); +} + +// read measurement results into buffer +void VL53L1X::readResults() +{ + Wire.beginTransmission(address); + Wire.write((RESULT__RANGE_STATUS >> 8) & 0xFF); // reg high byte + Wire.write( RESULT__RANGE_STATUS & 0xFF); // reg low byte + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, (uint8_t)17); + + results.range_status = Wire.read(); + + Wire.read(); // report_status: not used + + results.stream_count = Wire.read(); + + results.dss_actual_effective_spads_sd0 = (uint16_t)Wire.read() << 8; // high byte + results.dss_actual_effective_spads_sd0 |= Wire.read(); // low byte + + Wire.read(); // peak_signal_count_rate_mcps_sd0: not used + Wire.read(); + + results.ambient_count_rate_mcps_sd0 = (uint16_t)Wire.read() << 8; // high byte + results.ambient_count_rate_mcps_sd0 |= Wire.read(); // low byte + + Wire.read(); // sigma_sd0: not used + Wire.read(); + + Wire.read(); // phase_sd0: not used + Wire.read(); + + results.final_crosstalk_corrected_range_mm_sd0 = (uint16_t)Wire.read() << 8; // high byte + results.final_crosstalk_corrected_range_mm_sd0 |= Wire.read(); // low byte + + results.peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = (uint16_t)Wire.read() << 8; // high byte + results.peak_signal_count_rate_crosstalk_corrected_mcps_sd0 |= Wire.read(); // low byte +} + +// perform Dynamic SPAD Selection calculation/update +// based on VL53L1_low_power_auto_update_DSS() +void VL53L1X::updateDSS() +{ + uint16_t spadCount = results.dss_actual_effective_spads_sd0; + + if (spadCount != 0) + { + // "Calc total rate per spad" + + uint32_t totalRatePerSpad = + (uint32_t)results.peak_signal_count_rate_crosstalk_corrected_mcps_sd0 + + results.ambient_count_rate_mcps_sd0; + + // "clip to 16 bits" + if (totalRatePerSpad > 0xFFFF) { totalRatePerSpad = 0xFFFF; } + + // "shift up to take advantage of 32 bits" + totalRatePerSpad <<= 16; + + totalRatePerSpad /= spadCount; + + if (totalRatePerSpad != 0) + { + // "get the target rate and shift up by 16" + uint32_t requiredSpads = ((uint32_t)TargetRate << 16) / totalRatePerSpad; + + // "clip to 16 bit" + if (requiredSpads > 0xFFFF) { requiredSpads = 0xFFFF; } + + // "override DSS config" + writeReg16Bit(DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT, requiredSpads); + // DSS_CONFIG__ROI_MODE_CONTROL should already be set to REQUESTED_EFFFECTIVE_SPADS + + return; + } + } + + // If we reached this point, it means something above would have resulted in a + // divide by zero. + // "We want to gracefully set a spad target, not just exit with an error" + + // "set target to mid point" + writeReg16Bit(DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT, 0x8000); +} + +// get range, status, rates from results buffer +// based on VL53L1_GetRangingMeasurementData() +void VL53L1X::getRangingData() +{ + // VL53L1_copy_sys_and_core_results_to_range_results() begin + + uint16_t range = results.final_crosstalk_corrected_range_mm_sd0; + + // "apply correction gain" + // gain factor of 2011 is tuning parm default (VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR_DEFAULT) + // Basically, this appears to scale the result by 2011/2048, or about 98% + // (with the 1024 added for proper rounding). + ranging_data.range_mm = ((uint32_t)range * 2011 + 0x0400) / 0x0800; + + // VL53L1_copy_sys_and_core_results_to_range_results() end + + // set range_status in ranging_data based on value of RESULT__RANGE_STATUS register + // mostly based on ConvertStatusLite() + switch(results.range_status) + { + case 17: // MULTCLIPFAIL + case 2: // VCSELWATCHDOGTESTFAILURE + case 1: // VCSELCONTINUITYTESTFAILURE + case 3: // NOVHVVALUEFOUND + // from SetSimpleData() + ranging_data.range_status = HardwareFail; + break; + + case 13: // USERROICLIP + // from SetSimpleData() + ranging_data.range_status = MinRangeFail; + break; + + case 18: // GPHSTREAMCOUNT0READY + ranging_data.range_status = SynchronizationInt; + break; + + case 5: // RANGEPHASECHECK + ranging_data.range_status = OutOfBoundsFail; + break; + + case 4: // MSRCNOTARGET + ranging_data.range_status = SignalFail; + break; + + case 6: // SIGMATHRESHOLDCHECK + ranging_data.range_status = SigmaFail; + break; + + case 7: // PHASECONSISTENCY + ranging_data.range_status = WrapTargetFail; + break; + + case 12: // RANGEIGNORETHRESHOLD + ranging_data.range_status = XtalkSignalFail; + break; + + case 8: // MINCLIP + ranging_data.range_status = RangeValidMinRangeClipped; + break; + + case 9: // RANGECOMPLETE + // from VL53L1_copy_sys_and_core_results_to_range_results() + if (results.stream_count == 0) + { + ranging_data.range_status = RangeValidNoWrapCheckFail; + } + else + { + ranging_data.range_status = RangeValid; + } + break; + + default: + ranging_data.range_status = None; + } + + // from SetSimpleData() + ranging_data.peak_signal_count_rate_MCPS = + countRateFixedToFloat(results.peak_signal_count_rate_crosstalk_corrected_mcps_sd0); + ranging_data.ambient_count_rate_MCPS = + countRateFixedToFloat(results.ambient_count_rate_mcps_sd0); +} + +// Decode sequence step timeout in MCLKs from register value +// based on VL53L1_decode_timeout() +uint32_t VL53L1X::decodeTimeout(uint16_t reg_val) +{ + return ((uint32_t)(reg_val & 0xFF) << (reg_val >> 8)) + 1; +} + +// Encode sequence step timeout register value from timeout in MCLKs +// based on VL53L1_encode_timeout() +uint16_t VL53L1X::encodeTimeout(uint32_t timeout_mclks) +{ + // encoded format: "(LSByte * 2^MSByte) + 1" + + uint32_t ls_byte = 0; + uint16_t ms_byte = 0; + + if (timeout_mclks > 0) + { + ls_byte = timeout_mclks - 1; + + while ((ls_byte & 0xFFFFFF00) > 0) + { + ls_byte >>= 1; + ms_byte++; + } + + return (ms_byte << 8) | (ls_byte & 0xFF); + } + else { return 0; } +} + +// Convert sequence step timeout from macro periods to microseconds with given +// macro period in microseconds (12.12 format) +// based on VL53L1_calc_timeout_us() +uint32_t VL53L1X::timeoutMclksToMicroseconds(uint32_t timeout_mclks, uint32_t macro_period_us) +{ + return ((uint64_t)timeout_mclks * macro_period_us + 0x800) >> 12; +} + +// Convert sequence step timeout from microseconds to macro periods with given +// macro period in microseconds (12.12 format) +// based on VL53L1_calc_timeout_mclks() +uint32_t VL53L1X::timeoutMicrosecondsToMclks(uint32_t timeout_us, uint32_t macro_period_us) +{ + return (((uint32_t)timeout_us << 12) + (macro_period_us >> 1)) / macro_period_us; +} + +// Calculate macro period in microseconds (12.12 format) with given VCSEL period +// assumes fast_osc_frequency has been read and stored +// based on VL53L1_calc_macro_period_us() +uint32_t VL53L1X::calcMacroPeriod(uint8_t vcsel_period) +{ + // from VL53L1_calc_pll_period_us() + // fast osc frequency in 4.12 format; PLL period in 0.24 format + uint32_t pll_period_us = ((uint32_t)0x01 << 30) / fast_osc_frequency; + + // from VL53L1_decode_vcsel_period() + uint8_t vcsel_period_pclks = (vcsel_period + 1) << 1; + + // VL53L1_MACRO_PERIOD_VCSEL_PERIODS = 2304 + uint32_t macro_period_us = (uint32_t)2304 * pll_period_us; + macro_period_us >>= 6; + macro_period_us *= vcsel_period_pclks; + macro_period_us >>= 6; + + return macro_period_us; +} diff --git a/lib/vl53l1x-arduino-1.01/VL53L1X.h b/lib/vl53l1x-arduino-1.01/VL53L1X.h new file mode 100644 index 000000000..689e9b6fd --- /dev/null +++ b/lib/vl53l1x-arduino-1.01/VL53L1X.h @@ -0,0 +1,1384 @@ +#pragma once + +#include + +class VL53L1X +{ + public: + + // register addresses from API vl53l1x_register_map.h + enum regAddr : uint16_t + { + SOFT_RESET = 0x0000, + I2C_SLAVE__DEVICE_ADDRESS = 0x0001, + ANA_CONFIG__VHV_REF_SEL_VDDPIX = 0x0002, + ANA_CONFIG__VHV_REF_SEL_VQUENCH = 0x0003, + ANA_CONFIG__REG_AVDD1V2_SEL = 0x0004, + ANA_CONFIG__FAST_OSC__TRIM = 0x0005, + OSC_MEASURED__FAST_OSC__FREQUENCY = 0x0006, + OSC_MEASURED__FAST_OSC__FREQUENCY_HI = 0x0006, + OSC_MEASURED__FAST_OSC__FREQUENCY_LO = 0x0007, + VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND = 0x0008, + VHV_CONFIG__COUNT_THRESH = 0x0009, + VHV_CONFIG__OFFSET = 0x000A, + VHV_CONFIG__INIT = 0x000B, + GLOBAL_CONFIG__SPAD_ENABLES_REF_0 = 0x000D, + GLOBAL_CONFIG__SPAD_ENABLES_REF_1 = 0x000E, + GLOBAL_CONFIG__SPAD_ENABLES_REF_2 = 0x000F, + GLOBAL_CONFIG__SPAD_ENABLES_REF_3 = 0x0010, + GLOBAL_CONFIG__SPAD_ENABLES_REF_4 = 0x0011, + GLOBAL_CONFIG__SPAD_ENABLES_REF_5 = 0x0012, + GLOBAL_CONFIG__REF_EN_START_SELECT = 0x0013, + REF_SPAD_MAN__NUM_REQUESTED_REF_SPADS = 0x0014, + REF_SPAD_MAN__REF_LOCATION = 0x0015, + ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS = 0x0016, + ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS_HI = 0x0016, + ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS_LO = 0x0017, + ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS = 0x0018, + ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS_HI = 0x0018, + ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS_LO = 0x0019, + ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS = 0x001A, + ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS_HI = 0x001A, + ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS_LO = 0x001B, + REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS = 0x001C, + REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS_HI = 0x001C, + REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS_LO = 0x001D, + ALGO__PART_TO_PART_RANGE_OFFSET_MM = 0x001E, + ALGO__PART_TO_PART_RANGE_OFFSET_MM_HI = 0x001E, + ALGO__PART_TO_PART_RANGE_OFFSET_MM_LO = 0x001F, + MM_CONFIG__INNER_OFFSET_MM = 0x0020, + MM_CONFIG__INNER_OFFSET_MM_HI = 0x0020, + MM_CONFIG__INNER_OFFSET_MM_LO = 0x0021, + MM_CONFIG__OUTER_OFFSET_MM = 0x0022, + MM_CONFIG__OUTER_OFFSET_MM_HI = 0x0022, + MM_CONFIG__OUTER_OFFSET_MM_LO = 0x0023, + DSS_CONFIG__TARGET_TOTAL_RATE_MCPS = 0x0024, + DSS_CONFIG__TARGET_TOTAL_RATE_MCPS_HI = 0x0024, + DSS_CONFIG__TARGET_TOTAL_RATE_MCPS_LO = 0x0025, + DEBUG__CTRL = 0x0026, + TEST_MODE__CTRL = 0x0027, + CLK_GATING__CTRL = 0x0028, + NVM_BIST__CTRL = 0x0029, + NVM_BIST__NUM_NVM_WORDS = 0x002A, + NVM_BIST__START_ADDRESS = 0x002B, + HOST_IF__STATUS = 0x002C, + PAD_I2C_HV__CONFIG = 0x002D, + PAD_I2C_HV__EXTSUP_CONFIG = 0x002E, + GPIO_HV_PAD__CTRL = 0x002F, + GPIO_HV_MUX__CTRL = 0x0030, + GPIO__TIO_HV_STATUS = 0x0031, + GPIO__FIO_HV_STATUS = 0x0032, + ANA_CONFIG__SPAD_SEL_PSWIDTH = 0x0033, + ANA_CONFIG__VCSEL_PULSE_WIDTH_OFFSET = 0x0034, + ANA_CONFIG__FAST_OSC__CONFIG_CTRL = 0x0035, + SIGMA_ESTIMATOR__EFFECTIVE_PULSE_WIDTH_NS = 0x0036, + SIGMA_ESTIMATOR__EFFECTIVE_AMBIENT_WIDTH_NS = 0x0037, + SIGMA_ESTIMATOR__SIGMA_REF_MM = 0x0038, + ALGO__CROSSTALK_COMPENSATION_VALID_HEIGHT_MM = 0x0039, + SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_0 = 0x003A, + SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_1 = 0x003B, + ALGO__RANGE_IGNORE_THRESHOLD_MCPS = 0x003C, + ALGO__RANGE_IGNORE_THRESHOLD_MCPS_HI = 0x003C, + ALGO__RANGE_IGNORE_THRESHOLD_MCPS_LO = 0x003D, + ALGO__RANGE_IGNORE_VALID_HEIGHT_MM = 0x003E, + ALGO__RANGE_MIN_CLIP = 0x003F, + ALGO__CONSISTENCY_CHECK__TOLERANCE = 0x0040, + SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_2 = 0x0041, + SD_CONFIG__RESET_STAGES_MSB = 0x0042, + SD_CONFIG__RESET_STAGES_LSB = 0x0043, + GPH_CONFIG__STREAM_COUNT_UPDATE_VALUE = 0x0044, + GLOBAL_CONFIG__STREAM_DIVIDER = 0x0045, + SYSTEM__INTERRUPT_CONFIG_GPIO = 0x0046, + CAL_CONFIG__VCSEL_START = 0x0047, + CAL_CONFIG__REPEAT_RATE = 0x0048, + CAL_CONFIG__REPEAT_RATE_HI = 0x0048, + CAL_CONFIG__REPEAT_RATE_LO = 0x0049, + GLOBAL_CONFIG__VCSEL_WIDTH = 0x004A, + PHASECAL_CONFIG__TIMEOUT_MACROP = 0x004B, + PHASECAL_CONFIG__TARGET = 0x004C, + PHASECAL_CONFIG__OVERRIDE = 0x004D, + DSS_CONFIG__ROI_MODE_CONTROL = 0x004F, + SYSTEM__THRESH_RATE_HIGH = 0x0050, + SYSTEM__THRESH_RATE_HIGH_HI = 0x0050, + SYSTEM__THRESH_RATE_HIGH_LO = 0x0051, + SYSTEM__THRESH_RATE_LOW = 0x0052, + SYSTEM__THRESH_RATE_LOW_HI = 0x0052, + SYSTEM__THRESH_RATE_LOW_LO = 0x0053, + DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT = 0x0054, + DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_HI = 0x0054, + DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_LO = 0x0055, + DSS_CONFIG__MANUAL_BLOCK_SELECT = 0x0056, + DSS_CONFIG__APERTURE_ATTENUATION = 0x0057, + DSS_CONFIG__MAX_SPADS_LIMIT = 0x0058, + DSS_CONFIG__MIN_SPADS_LIMIT = 0x0059, + MM_CONFIG__TIMEOUT_MACROP_A = 0x005A, // added by Pololu for 16-bit accesses + MM_CONFIG__TIMEOUT_MACROP_A_HI = 0x005A, + MM_CONFIG__TIMEOUT_MACROP_A_LO = 0x005B, + MM_CONFIG__TIMEOUT_MACROP_B = 0x005C, // added by Pololu for 16-bit accesses + MM_CONFIG__TIMEOUT_MACROP_B_HI = 0x005C, + MM_CONFIG__TIMEOUT_MACROP_B_LO = 0x005D, + RANGE_CONFIG__TIMEOUT_MACROP_A = 0x005E, // added by Pololu for 16-bit accesses + RANGE_CONFIG__TIMEOUT_MACROP_A_HI = 0x005E, + RANGE_CONFIG__TIMEOUT_MACROP_A_LO = 0x005F, + RANGE_CONFIG__VCSEL_PERIOD_A = 0x0060, + RANGE_CONFIG__TIMEOUT_MACROP_B = 0x0061, // added by Pololu for 16-bit accesses + RANGE_CONFIG__TIMEOUT_MACROP_B_HI = 0x0061, + RANGE_CONFIG__TIMEOUT_MACROP_B_LO = 0x0062, + RANGE_CONFIG__VCSEL_PERIOD_B = 0x0063, + RANGE_CONFIG__SIGMA_THRESH = 0x0064, + RANGE_CONFIG__SIGMA_THRESH_HI = 0x0064, + RANGE_CONFIG__SIGMA_THRESH_LO = 0x0065, + RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS = 0x0066, + RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_HI = 0x0066, + RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_LO = 0x0067, + RANGE_CONFIG__VALID_PHASE_LOW = 0x0068, + RANGE_CONFIG__VALID_PHASE_HIGH = 0x0069, + SYSTEM__INTERMEASUREMENT_PERIOD = 0x006C, + SYSTEM__INTERMEASUREMENT_PERIOD_3 = 0x006C, + SYSTEM__INTERMEASUREMENT_PERIOD_2 = 0x006D, + SYSTEM__INTERMEASUREMENT_PERIOD_1 = 0x006E, + SYSTEM__INTERMEASUREMENT_PERIOD_0 = 0x006F, + SYSTEM__FRACTIONAL_ENABLE = 0x0070, + SYSTEM__GROUPED_PARAMETER_HOLD_0 = 0x0071, + SYSTEM__THRESH_HIGH = 0x0072, + SYSTEM__THRESH_HIGH_HI = 0x0072, + SYSTEM__THRESH_HIGH_LO = 0x0073, + SYSTEM__THRESH_LOW = 0x0074, + SYSTEM__THRESH_LOW_HI = 0x0074, + SYSTEM__THRESH_LOW_LO = 0x0075, + SYSTEM__ENABLE_XTALK_PER_QUADRANT = 0x0076, + SYSTEM__SEED_CONFIG = 0x0077, + SD_CONFIG__WOI_SD0 = 0x0078, + SD_CONFIG__WOI_SD1 = 0x0079, + SD_CONFIG__INITIAL_PHASE_SD0 = 0x007A, + SD_CONFIG__INITIAL_PHASE_SD1 = 0x007B, + SYSTEM__GROUPED_PARAMETER_HOLD_1 = 0x007C, + SD_CONFIG__FIRST_ORDER_SELECT = 0x007D, + SD_CONFIG__QUANTIFIER = 0x007E, + ROI_CONFIG__USER_ROI_CENTRE_SPAD = 0x007F, + ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE = 0x0080, + SYSTEM__SEQUENCE_CONFIG = 0x0081, + SYSTEM__GROUPED_PARAMETER_HOLD = 0x0082, + POWER_MANAGEMENT__GO1_POWER_FORCE = 0x0083, + SYSTEM__STREAM_COUNT_CTRL = 0x0084, + FIRMWARE__ENABLE = 0x0085, + SYSTEM__INTERRUPT_CLEAR = 0x0086, + SYSTEM__MODE_START = 0x0087, + RESULT__INTERRUPT_STATUS = 0x0088, + RESULT__RANGE_STATUS = 0x0089, + RESULT__REPORT_STATUS = 0x008A, + RESULT__STREAM_COUNT = 0x008B, + RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x008C, + RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x008C, + RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x008D, + RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x008E, + RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x008E, + RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x008F, + RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 = 0x0090, + RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI = 0x0090, + RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO = 0x0091, + RESULT__SIGMA_SD0 = 0x0092, + RESULT__SIGMA_SD0_HI = 0x0092, + RESULT__SIGMA_SD0_LO = 0x0093, + RESULT__PHASE_SD0 = 0x0094, + RESULT__PHASE_SD0_HI = 0x0094, + RESULT__PHASE_SD0_LO = 0x0095, + RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 = 0x0096, + RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI = 0x0096, + RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO = 0x0097, + RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 = 0x0098, + RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI = 0x0098, + RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO = 0x0099, + RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x009A, + RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x009A, + RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x009B, + RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x009C, + RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x009C, + RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x009D, + RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x009E, + RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x009E, + RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x009F, + RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 = 0x00A0, + RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI = 0x00A0, + RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO = 0x00A1, + RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 = 0x00A2, + RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI = 0x00A2, + RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO = 0x00A3, + RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 = 0x00A4, + RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI = 0x00A4, + RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO = 0x00A5, + RESULT__SIGMA_SD1 = 0x00A6, + RESULT__SIGMA_SD1_HI = 0x00A6, + RESULT__SIGMA_SD1_LO = 0x00A7, + RESULT__PHASE_SD1 = 0x00A8, + RESULT__PHASE_SD1_HI = 0x00A8, + RESULT__PHASE_SD1_LO = 0x00A9, + RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 = 0x00AA, + RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI = 0x00AA, + RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO = 0x00AB, + RESULT__SPARE_0_SD1 = 0x00AC, + RESULT__SPARE_0_SD1_HI = 0x00AC, + RESULT__SPARE_0_SD1_LO = 0x00AD, + RESULT__SPARE_1_SD1 = 0x00AE, + RESULT__SPARE_1_SD1_HI = 0x00AE, + RESULT__SPARE_1_SD1_LO = 0x00AF, + RESULT__SPARE_2_SD1 = 0x00B0, + RESULT__SPARE_2_SD1_HI = 0x00B0, + RESULT__SPARE_2_SD1_LO = 0x00B1, + RESULT__SPARE_3_SD1 = 0x00B2, + RESULT__THRESH_INFO = 0x00B3, + RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 = 0x00B4, + RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 = 0x00B4, + RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 = 0x00B5, + RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 = 0x00B6, + RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 = 0x00B7, + RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 = 0x00B8, + RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 = 0x00B8, + RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 = 0x00B9, + RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 = 0x00BA, + RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 = 0x00BB, + RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 = 0x00BC, + RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 = 0x00BC, + RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 = 0x00BD, + RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 = 0x00BE, + RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 = 0x00BF, + RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 = 0x00C0, + RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 = 0x00C0, + RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 = 0x00C1, + RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 = 0x00C2, + RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 = 0x00C3, + RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 = 0x00C4, + RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 = 0x00C4, + RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 = 0x00C5, + RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 = 0x00C6, + RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 = 0x00C7, + RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 = 0x00C8, + RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 = 0x00C8, + RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 = 0x00C9, + RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 = 0x00CA, + RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 = 0x00CB, + RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 = 0x00CC, + RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 = 0x00CC, + RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 = 0x00CD, + RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 = 0x00CE, + RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 = 0x00CF, + RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 = 0x00D0, + RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 = 0x00D0, + RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 = 0x00D1, + RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 = 0x00D2, + RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 = 0x00D3, + RESULT_CORE__SPARE_0 = 0x00D4, + PHASECAL_RESULT__REFERENCE_PHASE = 0x00D6, + PHASECAL_RESULT__REFERENCE_PHASE_HI = 0x00D6, + PHASECAL_RESULT__REFERENCE_PHASE_LO = 0x00D7, + PHASECAL_RESULT__VCSEL_START = 0x00D8, + REF_SPAD_CHAR_RESULT__NUM_ACTUAL_REF_SPADS = 0x00D9, + REF_SPAD_CHAR_RESULT__REF_LOCATION = 0x00DA, + VHV_RESULT__COLDBOOT_STATUS = 0x00DB, + VHV_RESULT__SEARCH_RESULT = 0x00DC, + VHV_RESULT__LATEST_SETTING = 0x00DD, + RESULT__OSC_CALIBRATE_VAL = 0x00DE, + RESULT__OSC_CALIBRATE_VAL_HI = 0x00DE, + RESULT__OSC_CALIBRATE_VAL_LO = 0x00DF, + ANA_CONFIG__POWERDOWN_GO1 = 0x00E0, + ANA_CONFIG__REF_BG_CTRL = 0x00E1, + ANA_CONFIG__REGDVDD1V2_CTRL = 0x00E2, + ANA_CONFIG__OSC_SLOW_CTRL = 0x00E3, + TEST_MODE__STATUS = 0x00E4, + FIRMWARE__SYSTEM_STATUS = 0x00E5, + FIRMWARE__MODE_STATUS = 0x00E6, + FIRMWARE__SECONDARY_MODE_STATUS = 0x00E7, + FIRMWARE__CAL_REPEAT_RATE_COUNTER = 0x00E8, + FIRMWARE__CAL_REPEAT_RATE_COUNTER_HI = 0x00E8, + FIRMWARE__CAL_REPEAT_RATE_COUNTER_LO = 0x00E9, + FIRMWARE__HISTOGRAM_BIN = 0x00EA, + GPH__SYSTEM__THRESH_HIGH = 0x00EC, + GPH__SYSTEM__THRESH_HIGH_HI = 0x00EC, + GPH__SYSTEM__THRESH_HIGH_LO = 0x00ED, + GPH__SYSTEM__THRESH_LOW = 0x00EE, + GPH__SYSTEM__THRESH_LOW_HI = 0x00EE, + GPH__SYSTEM__THRESH_LOW_LO = 0x00EF, + GPH__SYSTEM__ENABLE_XTALK_PER_QUADRANT = 0x00F0, + GPH__SPARE_0 = 0x00F1, + GPH__SD_CONFIG__WOI_SD0 = 0x00F2, + GPH__SD_CONFIG__WOI_SD1 = 0x00F3, + GPH__SD_CONFIG__INITIAL_PHASE_SD0 = 0x00F4, + GPH__SD_CONFIG__INITIAL_PHASE_SD1 = 0x00F5, + GPH__SD_CONFIG__FIRST_ORDER_SELECT = 0x00F6, + GPH__SD_CONFIG__QUANTIFIER = 0x00F7, + GPH__ROI_CONFIG__USER_ROI_CENTRE_SPAD = 0x00F8, + GPH__ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE = 0x00F9, + GPH__SYSTEM__SEQUENCE_CONFIG = 0x00FA, + GPH__GPH_ID = 0x00FB, + SYSTEM__INTERRUPT_SET = 0x00FC, + INTERRUPT_MANAGER__ENABLES = 0x00FD, + INTERRUPT_MANAGER__CLEAR = 0x00FE, + INTERRUPT_MANAGER__STATUS = 0x00FF, + MCU_TO_HOST_BANK__WR_ACCESS_EN = 0x0100, + POWER_MANAGEMENT__GO1_RESET_STATUS = 0x0101, + PAD_STARTUP_MODE__VALUE_RO = 0x0102, + PAD_STARTUP_MODE__VALUE_CTRL = 0x0103, + PLL_PERIOD_US = 0x0104, + PLL_PERIOD_US_3 = 0x0104, + PLL_PERIOD_US_2 = 0x0105, + PLL_PERIOD_US_1 = 0x0106, + PLL_PERIOD_US_0 = 0x0107, + INTERRUPT_SCHEDULER__DATA_OUT = 0x0108, + INTERRUPT_SCHEDULER__DATA_OUT_3 = 0x0108, + INTERRUPT_SCHEDULER__DATA_OUT_2 = 0x0109, + INTERRUPT_SCHEDULER__DATA_OUT_1 = 0x010A, + INTERRUPT_SCHEDULER__DATA_OUT_0 = 0x010B, + NVM_BIST__COMPLETE = 0x010C, + NVM_BIST__STATUS = 0x010D, + IDENTIFICATION__MODEL_ID = 0x010F, + IDENTIFICATION__MODULE_TYPE = 0x0110, + IDENTIFICATION__REVISION_ID = 0x0111, + IDENTIFICATION__MODULE_ID = 0x0112, + IDENTIFICATION__MODULE_ID_HI = 0x0112, + IDENTIFICATION__MODULE_ID_LO = 0x0113, + ANA_CONFIG__FAST_OSC__TRIM_MAX = 0x0114, + ANA_CONFIG__FAST_OSC__FREQ_SET = 0x0115, + ANA_CONFIG__VCSEL_TRIM = 0x0116, + ANA_CONFIG__VCSEL_SELION = 0x0117, + ANA_CONFIG__VCSEL_SELION_MAX = 0x0118, + PROTECTED_LASER_SAFETY__LOCK_BIT = 0x0119, + LASER_SAFETY__KEY = 0x011A, + LASER_SAFETY__KEY_RO = 0x011B, + LASER_SAFETY__CLIP = 0x011C, + LASER_SAFETY__MULT = 0x011D, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_0 = 0x011E, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_1 = 0x011F, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_2 = 0x0120, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_3 = 0x0121, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_4 = 0x0122, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_5 = 0x0123, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_6 = 0x0124, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_7 = 0x0125, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_8 = 0x0126, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_9 = 0x0127, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_10 = 0x0128, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_11 = 0x0129, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_12 = 0x012A, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_13 = 0x012B, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_14 = 0x012C, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_15 = 0x012D, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_16 = 0x012E, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_17 = 0x012F, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_18 = 0x0130, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_19 = 0x0131, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_20 = 0x0132, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_21 = 0x0133, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_22 = 0x0134, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_23 = 0x0135, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_24 = 0x0136, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_25 = 0x0137, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_26 = 0x0138, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_27 = 0x0139, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_28 = 0x013A, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_29 = 0x013B, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_30 = 0x013C, + GLOBAL_CONFIG__SPAD_ENABLES_RTN_31 = 0x013D, + ROI_CONFIG__MODE_ROI_CENTRE_SPAD = 0x013E, + ROI_CONFIG__MODE_ROI_XY_SIZE = 0x013F, + GO2_HOST_BANK_ACCESS__OVERRIDE = 0x0300, + MCU_UTIL_MULTIPLIER__MULTIPLICAND = 0x0400, + MCU_UTIL_MULTIPLIER__MULTIPLICAND_3 = 0x0400, + MCU_UTIL_MULTIPLIER__MULTIPLICAND_2 = 0x0401, + MCU_UTIL_MULTIPLIER__MULTIPLICAND_1 = 0x0402, + MCU_UTIL_MULTIPLIER__MULTIPLICAND_0 = 0x0403, + MCU_UTIL_MULTIPLIER__MULTIPLIER = 0x0404, + MCU_UTIL_MULTIPLIER__MULTIPLIER_3 = 0x0404, + MCU_UTIL_MULTIPLIER__MULTIPLIER_2 = 0x0405, + MCU_UTIL_MULTIPLIER__MULTIPLIER_1 = 0x0406, + MCU_UTIL_MULTIPLIER__MULTIPLIER_0 = 0x0407, + MCU_UTIL_MULTIPLIER__PRODUCT_HI = 0x0408, + MCU_UTIL_MULTIPLIER__PRODUCT_HI_3 = 0x0408, + MCU_UTIL_MULTIPLIER__PRODUCT_HI_2 = 0x0409, + MCU_UTIL_MULTIPLIER__PRODUCT_HI_1 = 0x040A, + MCU_UTIL_MULTIPLIER__PRODUCT_HI_0 = 0x040B, + MCU_UTIL_MULTIPLIER__PRODUCT_LO = 0x040C, + MCU_UTIL_MULTIPLIER__PRODUCT_LO_3 = 0x040C, + MCU_UTIL_MULTIPLIER__PRODUCT_LO_2 = 0x040D, + MCU_UTIL_MULTIPLIER__PRODUCT_LO_1 = 0x040E, + MCU_UTIL_MULTIPLIER__PRODUCT_LO_0 = 0x040F, + MCU_UTIL_MULTIPLIER__START = 0x0410, + MCU_UTIL_MULTIPLIER__STATUS = 0x0411, + MCU_UTIL_DIVIDER__START = 0x0412, + MCU_UTIL_DIVIDER__STATUS = 0x0413, + MCU_UTIL_DIVIDER__DIVIDEND = 0x0414, + MCU_UTIL_DIVIDER__DIVIDEND_3 = 0x0414, + MCU_UTIL_DIVIDER__DIVIDEND_2 = 0x0415, + MCU_UTIL_DIVIDER__DIVIDEND_1 = 0x0416, + MCU_UTIL_DIVIDER__DIVIDEND_0 = 0x0417, + MCU_UTIL_DIVIDER__DIVISOR = 0x0418, + MCU_UTIL_DIVIDER__DIVISOR_3 = 0x0418, + MCU_UTIL_DIVIDER__DIVISOR_2 = 0x0419, + MCU_UTIL_DIVIDER__DIVISOR_1 = 0x041A, + MCU_UTIL_DIVIDER__DIVISOR_0 = 0x041B, + MCU_UTIL_DIVIDER__QUOTIENT = 0x041C, + MCU_UTIL_DIVIDER__QUOTIENT_3 = 0x041C, + MCU_UTIL_DIVIDER__QUOTIENT_2 = 0x041D, + MCU_UTIL_DIVIDER__QUOTIENT_1 = 0x041E, + MCU_UTIL_DIVIDER__QUOTIENT_0 = 0x041F, + TIMER0__VALUE_IN = 0x0420, + TIMER0__VALUE_IN_3 = 0x0420, + TIMER0__VALUE_IN_2 = 0x0421, + TIMER0__VALUE_IN_1 = 0x0422, + TIMER0__VALUE_IN_0 = 0x0423, + TIMER1__VALUE_IN = 0x0424, + TIMER1__VALUE_IN_3 = 0x0424, + TIMER1__VALUE_IN_2 = 0x0425, + TIMER1__VALUE_IN_1 = 0x0426, + TIMER1__VALUE_IN_0 = 0x0427, + TIMER0__CTRL = 0x0428, + TIMER1__CTRL = 0x0429, + MCU_GENERAL_PURPOSE__GP_0 = 0x042C, + MCU_GENERAL_PURPOSE__GP_1 = 0x042D, + MCU_GENERAL_PURPOSE__GP_2 = 0x042E, + MCU_GENERAL_PURPOSE__GP_3 = 0x042F, + MCU_RANGE_CALC__CONFIG = 0x0430, + MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE = 0x0432, + MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE_HI = 0x0432, + MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE_LO = 0x0433, + MCU_RANGE_CALC__SPARE_4 = 0x0434, + MCU_RANGE_CALC__SPARE_4_3 = 0x0434, + MCU_RANGE_CALC__SPARE_4_2 = 0x0435, + MCU_RANGE_CALC__SPARE_4_1 = 0x0436, + MCU_RANGE_CALC__SPARE_4_0 = 0x0437, + MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC = 0x0438, + MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC_HI = 0x0438, + MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC_LO = 0x0439, + MCU_RANGE_CALC__ALGO_VCSEL_PERIOD = 0x043C, + MCU_RANGE_CALC__SPARE_5 = 0x043D, + MCU_RANGE_CALC__ALGO_TOTAL_PERIODS = 0x043E, + MCU_RANGE_CALC__ALGO_TOTAL_PERIODS_HI = 0x043E, + MCU_RANGE_CALC__ALGO_TOTAL_PERIODS_LO = 0x043F, + MCU_RANGE_CALC__ALGO_ACCUM_PHASE = 0x0440, + MCU_RANGE_CALC__ALGO_ACCUM_PHASE_3 = 0x0440, + MCU_RANGE_CALC__ALGO_ACCUM_PHASE_2 = 0x0441, + MCU_RANGE_CALC__ALGO_ACCUM_PHASE_1 = 0x0442, + MCU_RANGE_CALC__ALGO_ACCUM_PHASE_0 = 0x0443, + MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS = 0x0444, + MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_3 = 0x0444, + MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_2 = 0x0445, + MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_1 = 0x0446, + MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_0 = 0x0447, + MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS = 0x0448, + MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_3 = 0x0448, + MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_2 = 0x0449, + MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_1 = 0x044A, + MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_0 = 0x044B, + MCU_RANGE_CALC__SPARE_6 = 0x044C, + MCU_RANGE_CALC__SPARE_6_HI = 0x044C, + MCU_RANGE_CALC__SPARE_6_LO = 0x044D, + MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD = 0x044E, + MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD_HI = 0x044E, + MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD_LO = 0x044F, + MCU_RANGE_CALC__NUM_SPADS = 0x0450, + MCU_RANGE_CALC__NUM_SPADS_HI = 0x0450, + MCU_RANGE_CALC__NUM_SPADS_LO = 0x0451, + MCU_RANGE_CALC__PHASE_OUTPUT = 0x0452, + MCU_RANGE_CALC__PHASE_OUTPUT_HI = 0x0452, + MCU_RANGE_CALC__PHASE_OUTPUT_LO = 0x0453, + MCU_RANGE_CALC__RATE_PER_SPAD_MCPS = 0x0454, + MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_3 = 0x0454, + MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_2 = 0x0455, + MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_1 = 0x0456, + MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_0 = 0x0457, + MCU_RANGE_CALC__SPARE_7 = 0x0458, + MCU_RANGE_CALC__SPARE_8 = 0x0459, + MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS = 0x045A, + MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS_HI = 0x045A, + MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS_LO = 0x045B, + MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS = 0x045C, + MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS_HI = 0x045C, + MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS_LO = 0x045D, + MCU_RANGE_CALC__AMBIENT_RATE_MCPS = 0x045E, + MCU_RANGE_CALC__AMBIENT_RATE_MCPS_HI = 0x045E, + MCU_RANGE_CALC__AMBIENT_RATE_MCPS_LO = 0x045F, + MCU_RANGE_CALC__XTALK = 0x0460, + MCU_RANGE_CALC__XTALK_HI = 0x0460, + MCU_RANGE_CALC__XTALK_LO = 0x0461, + MCU_RANGE_CALC__CALC_STATUS = 0x0462, + MCU_RANGE_CALC__DEBUG = 0x0463, + MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS = 0x0464, + MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS_HI = 0x0464, + MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS_LO = 0x0465, + MCU_RANGE_CALC__SPARE_0 = 0x0468, + MCU_RANGE_CALC__SPARE_1 = 0x0469, + MCU_RANGE_CALC__SPARE_2 = 0x046A, + MCU_RANGE_CALC__SPARE_3 = 0x046B, + PATCH__CTRL = 0x0470, + PATCH__JMP_ENABLES = 0x0472, + PATCH__JMP_ENABLES_HI = 0x0472, + PATCH__JMP_ENABLES_LO = 0x0473, + PATCH__DATA_ENABLES = 0x0474, + PATCH__DATA_ENABLES_HI = 0x0474, + PATCH__DATA_ENABLES_LO = 0x0475, + PATCH__OFFSET_0 = 0x0476, + PATCH__OFFSET_0_HI = 0x0476, + PATCH__OFFSET_0_LO = 0x0477, + PATCH__OFFSET_1 = 0x0478, + PATCH__OFFSET_1_HI = 0x0478, + PATCH__OFFSET_1_LO = 0x0479, + PATCH__OFFSET_2 = 0x047A, + PATCH__OFFSET_2_HI = 0x047A, + PATCH__OFFSET_2_LO = 0x047B, + PATCH__OFFSET_3 = 0x047C, + PATCH__OFFSET_3_HI = 0x047C, + PATCH__OFFSET_3_LO = 0x047D, + PATCH__OFFSET_4 = 0x047E, + PATCH__OFFSET_4_HI = 0x047E, + PATCH__OFFSET_4_LO = 0x047F, + PATCH__OFFSET_5 = 0x0480, + PATCH__OFFSET_5_HI = 0x0480, + PATCH__OFFSET_5_LO = 0x0481, + PATCH__OFFSET_6 = 0x0482, + PATCH__OFFSET_6_HI = 0x0482, + PATCH__OFFSET_6_LO = 0x0483, + PATCH__OFFSET_7 = 0x0484, + PATCH__OFFSET_7_HI = 0x0484, + PATCH__OFFSET_7_LO = 0x0485, + PATCH__OFFSET_8 = 0x0486, + PATCH__OFFSET_8_HI = 0x0486, + PATCH__OFFSET_8_LO = 0x0487, + PATCH__OFFSET_9 = 0x0488, + PATCH__OFFSET_9_HI = 0x0488, + PATCH__OFFSET_9_LO = 0x0489, + PATCH__OFFSET_10 = 0x048A, + PATCH__OFFSET_10_HI = 0x048A, + PATCH__OFFSET_10_LO = 0x048B, + PATCH__OFFSET_11 = 0x048C, + PATCH__OFFSET_11_HI = 0x048C, + PATCH__OFFSET_11_LO = 0x048D, + PATCH__OFFSET_12 = 0x048E, + PATCH__OFFSET_12_HI = 0x048E, + PATCH__OFFSET_12_LO = 0x048F, + PATCH__OFFSET_13 = 0x0490, + PATCH__OFFSET_13_HI = 0x0490, + PATCH__OFFSET_13_LO = 0x0491, + PATCH__OFFSET_14 = 0x0492, + PATCH__OFFSET_14_HI = 0x0492, + PATCH__OFFSET_14_LO = 0x0493, + PATCH__OFFSET_15 = 0x0494, + PATCH__OFFSET_15_HI = 0x0494, + PATCH__OFFSET_15_LO = 0x0495, + PATCH__ADDRESS_0 = 0x0496, + PATCH__ADDRESS_0_HI = 0x0496, + PATCH__ADDRESS_0_LO = 0x0497, + PATCH__ADDRESS_1 = 0x0498, + PATCH__ADDRESS_1_HI = 0x0498, + PATCH__ADDRESS_1_LO = 0x0499, + PATCH__ADDRESS_2 = 0x049A, + PATCH__ADDRESS_2_HI = 0x049A, + PATCH__ADDRESS_2_LO = 0x049B, + PATCH__ADDRESS_3 = 0x049C, + PATCH__ADDRESS_3_HI = 0x049C, + PATCH__ADDRESS_3_LO = 0x049D, + PATCH__ADDRESS_4 = 0x049E, + PATCH__ADDRESS_4_HI = 0x049E, + PATCH__ADDRESS_4_LO = 0x049F, + PATCH__ADDRESS_5 = 0x04A0, + PATCH__ADDRESS_5_HI = 0x04A0, + PATCH__ADDRESS_5_LO = 0x04A1, + PATCH__ADDRESS_6 = 0x04A2, + PATCH__ADDRESS_6_HI = 0x04A2, + PATCH__ADDRESS_6_LO = 0x04A3, + PATCH__ADDRESS_7 = 0x04A4, + PATCH__ADDRESS_7_HI = 0x04A4, + PATCH__ADDRESS_7_LO = 0x04A5, + PATCH__ADDRESS_8 = 0x04A6, + PATCH__ADDRESS_8_HI = 0x04A6, + PATCH__ADDRESS_8_LO = 0x04A7, + PATCH__ADDRESS_9 = 0x04A8, + PATCH__ADDRESS_9_HI = 0x04A8, + PATCH__ADDRESS_9_LO = 0x04A9, + PATCH__ADDRESS_10 = 0x04AA, + PATCH__ADDRESS_10_HI = 0x04AA, + PATCH__ADDRESS_10_LO = 0x04AB, + PATCH__ADDRESS_11 = 0x04AC, + PATCH__ADDRESS_11_HI = 0x04AC, + PATCH__ADDRESS_11_LO = 0x04AD, + PATCH__ADDRESS_12 = 0x04AE, + PATCH__ADDRESS_12_HI = 0x04AE, + PATCH__ADDRESS_12_LO = 0x04AF, + PATCH__ADDRESS_13 = 0x04B0, + PATCH__ADDRESS_13_HI = 0x04B0, + PATCH__ADDRESS_13_LO = 0x04B1, + PATCH__ADDRESS_14 = 0x04B2, + PATCH__ADDRESS_14_HI = 0x04B2, + PATCH__ADDRESS_14_LO = 0x04B3, + PATCH__ADDRESS_15 = 0x04B4, + PATCH__ADDRESS_15_HI = 0x04B4, + PATCH__ADDRESS_15_LO = 0x04B5, + SPI_ASYNC_MUX__CTRL = 0x04C0, + CLK__CONFIG = 0x04C4, + GPIO_LV_MUX__CTRL = 0x04CC, + GPIO_LV_PAD__CTRL = 0x04CD, + PAD_I2C_LV__CONFIG = 0x04D0, + PAD_STARTUP_MODE__VALUE_RO_GO1 = 0x04D4, + HOST_IF__STATUS_GO1 = 0x04D5, + MCU_CLK_GATING__CTRL = 0x04D8, + TEST__BIST_ROM_CTRL = 0x04E0, + TEST__BIST_ROM_RESULT = 0x04E1, + TEST__BIST_ROM_MCU_SIG = 0x04E2, + TEST__BIST_ROM_MCU_SIG_HI = 0x04E2, + TEST__BIST_ROM_MCU_SIG_LO = 0x04E3, + TEST__BIST_RAM_CTRL = 0x04E4, + TEST__BIST_RAM_RESULT = 0x04E5, + TEST__TMC = 0x04E8, + TEST__PLL_BIST_MIN_THRESHOLD = 0x04F0, + TEST__PLL_BIST_MIN_THRESHOLD_HI = 0x04F0, + TEST__PLL_BIST_MIN_THRESHOLD_LO = 0x04F1, + TEST__PLL_BIST_MAX_THRESHOLD = 0x04F2, + TEST__PLL_BIST_MAX_THRESHOLD_HI = 0x04F2, + TEST__PLL_BIST_MAX_THRESHOLD_LO = 0x04F3, + TEST__PLL_BIST_COUNT_OUT = 0x04F4, + TEST__PLL_BIST_COUNT_OUT_HI = 0x04F4, + TEST__PLL_BIST_COUNT_OUT_LO = 0x04F5, + TEST__PLL_BIST_GONOGO = 0x04F6, + TEST__PLL_BIST_CTRL = 0x04F7, + RANGING_CORE__DEVICE_ID = 0x0680, + RANGING_CORE__REVISION_ID = 0x0681, + RANGING_CORE__CLK_CTRL1 = 0x0683, + RANGING_CORE__CLK_CTRL2 = 0x0684, + RANGING_CORE__WOI_1 = 0x0685, + RANGING_CORE__WOI_REF_1 = 0x0686, + RANGING_CORE__START_RANGING = 0x0687, + RANGING_CORE__LOW_LIMIT_1 = 0x0690, + RANGING_CORE__HIGH_LIMIT_1 = 0x0691, + RANGING_CORE__LOW_LIMIT_REF_1 = 0x0692, + RANGING_CORE__HIGH_LIMIT_REF_1 = 0x0693, + RANGING_CORE__QUANTIFIER_1_MSB = 0x0694, + RANGING_CORE__QUANTIFIER_1_LSB = 0x0695, + RANGING_CORE__QUANTIFIER_REF_1_MSB = 0x0696, + RANGING_CORE__QUANTIFIER_REF_1_LSB = 0x0697, + RANGING_CORE__AMBIENT_OFFSET_1_MSB = 0x0698, + RANGING_CORE__AMBIENT_OFFSET_1_LSB = 0x0699, + RANGING_CORE__AMBIENT_OFFSET_REF_1_MSB = 0x069A, + RANGING_CORE__AMBIENT_OFFSET_REF_1_LSB = 0x069B, + RANGING_CORE__FILTER_STRENGTH_1 = 0x069C, + RANGING_CORE__FILTER_STRENGTH_REF_1 = 0x069D, + RANGING_CORE__SIGNAL_EVENT_LIMIT_1_MSB = 0x069E, + RANGING_CORE__SIGNAL_EVENT_LIMIT_1_LSB = 0x069F, + RANGING_CORE__SIGNAL_EVENT_LIMIT_REF_1_MSB = 0x06A0, + RANGING_CORE__SIGNAL_EVENT_LIMIT_REF_1_LSB = 0x06A1, + RANGING_CORE__TIMEOUT_OVERALL_PERIODS_MSB = 0x06A4, + RANGING_CORE__TIMEOUT_OVERALL_PERIODS_LSB = 0x06A5, + RANGING_CORE__INVERT_HW = 0x06A6, + RANGING_CORE__FORCE_HW = 0x06A7, + RANGING_CORE__STATIC_HW_VALUE = 0x06A8, + RANGING_CORE__FORCE_CONTINUOUS_AMBIENT = 0x06A9, + RANGING_CORE__TEST_PHASE_SELECT_TO_FILTER = 0x06AA, + RANGING_CORE__TEST_PHASE_SELECT_TO_TIMING_GEN = 0x06AB, + RANGING_CORE__INITIAL_PHASE_VALUE_1 = 0x06AC, + RANGING_CORE__INITIAL_PHASE_VALUE_REF_1 = 0x06AD, + RANGING_CORE__FORCE_UP_IN = 0x06AE, + RANGING_CORE__FORCE_DN_IN = 0x06AF, + RANGING_CORE__STATIC_UP_VALUE_1 = 0x06B0, + RANGING_CORE__STATIC_UP_VALUE_REF_1 = 0x06B1, + RANGING_CORE__STATIC_DN_VALUE_1 = 0x06B2, + RANGING_CORE__STATIC_DN_VALUE_REF_1 = 0x06B3, + RANGING_CORE__MONITOR_UP_DN = 0x06B4, + RANGING_CORE__INVERT_UP_DN = 0x06B5, + RANGING_CORE__CPUMP_1 = 0x06B6, + RANGING_CORE__CPUMP_2 = 0x06B7, + RANGING_CORE__CPUMP_3 = 0x06B8, + RANGING_CORE__OSC_1 = 0x06B9, + RANGING_CORE__PLL_1 = 0x06BB, + RANGING_CORE__PLL_2 = 0x06BC, + RANGING_CORE__REFERENCE_1 = 0x06BD, + RANGING_CORE__REFERENCE_3 = 0x06BF, + RANGING_CORE__REFERENCE_4 = 0x06C0, + RANGING_CORE__REFERENCE_5 = 0x06C1, + RANGING_CORE__REGAVDD1V2 = 0x06C3, + RANGING_CORE__CALIB_1 = 0x06C4, + RANGING_CORE__CALIB_2 = 0x06C5, + RANGING_CORE__CALIB_3 = 0x06C6, + RANGING_CORE__TST_MUX_SEL1 = 0x06C9, + RANGING_CORE__TST_MUX_SEL2 = 0x06CA, + RANGING_CORE__TST_MUX = 0x06CB, + RANGING_CORE__GPIO_OUT_TESTMUX = 0x06CC, + RANGING_CORE__CUSTOM_FE = 0x06CD, + RANGING_CORE__CUSTOM_FE_2 = 0x06CE, + RANGING_CORE__SPAD_READOUT = 0x06CF, + RANGING_CORE__SPAD_READOUT_1 = 0x06D0, + RANGING_CORE__SPAD_READOUT_2 = 0x06D1, + RANGING_CORE__SPAD_PS = 0x06D2, + RANGING_CORE__LASER_SAFETY_2 = 0x06D4, + RANGING_CORE__NVM_CTRL__MODE = 0x0780, + RANGING_CORE__NVM_CTRL__PDN = 0x0781, + RANGING_CORE__NVM_CTRL__PROGN = 0x0782, + RANGING_CORE__NVM_CTRL__READN = 0x0783, + RANGING_CORE__NVM_CTRL__PULSE_WIDTH_MSB = 0x0784, + RANGING_CORE__NVM_CTRL__PULSE_WIDTH_LSB = 0x0785, + RANGING_CORE__NVM_CTRL__HV_RISE_MSB = 0x0786, + RANGING_CORE__NVM_CTRL__HV_RISE_LSB = 0x0787, + RANGING_CORE__NVM_CTRL__HV_FALL_MSB = 0x0788, + RANGING_CORE__NVM_CTRL__HV_FALL_LSB = 0x0789, + RANGING_CORE__NVM_CTRL__TST = 0x078A, + RANGING_CORE__NVM_CTRL__TESTREAD = 0x078B, + RANGING_CORE__NVM_CTRL__DATAIN_MMM = 0x078C, + RANGING_CORE__NVM_CTRL__DATAIN_LMM = 0x078D, + RANGING_CORE__NVM_CTRL__DATAIN_LLM = 0x078E, + RANGING_CORE__NVM_CTRL__DATAIN_LLL = 0x078F, + RANGING_CORE__NVM_CTRL__DATAOUT_MMM = 0x0790, + RANGING_CORE__NVM_CTRL__DATAOUT_LMM = 0x0791, + RANGING_CORE__NVM_CTRL__DATAOUT_LLM = 0x0792, + RANGING_CORE__NVM_CTRL__DATAOUT_LLL = 0x0793, + RANGING_CORE__NVM_CTRL__ADDR = 0x0794, + RANGING_CORE__NVM_CTRL__DATAOUT_ECC = 0x0795, + RANGING_CORE__RET_SPAD_EN_0 = 0x0796, + RANGING_CORE__RET_SPAD_EN_1 = 0x0797, + RANGING_CORE__RET_SPAD_EN_2 = 0x0798, + RANGING_CORE__RET_SPAD_EN_3 = 0x0799, + RANGING_CORE__RET_SPAD_EN_4 = 0x079A, + RANGING_CORE__RET_SPAD_EN_5 = 0x079B, + RANGING_CORE__RET_SPAD_EN_6 = 0x079C, + RANGING_CORE__RET_SPAD_EN_7 = 0x079D, + RANGING_CORE__RET_SPAD_EN_8 = 0x079E, + RANGING_CORE__RET_SPAD_EN_9 = 0x079F, + RANGING_CORE__RET_SPAD_EN_10 = 0x07A0, + RANGING_CORE__RET_SPAD_EN_11 = 0x07A1, + RANGING_CORE__RET_SPAD_EN_12 = 0x07A2, + RANGING_CORE__RET_SPAD_EN_13 = 0x07A3, + RANGING_CORE__RET_SPAD_EN_14 = 0x07A4, + RANGING_CORE__RET_SPAD_EN_15 = 0x07A5, + RANGING_CORE__RET_SPAD_EN_16 = 0x07A6, + RANGING_CORE__RET_SPAD_EN_17 = 0x07A7, + RANGING_CORE__SPAD_SHIFT_EN = 0x07BA, + RANGING_CORE__SPAD_DISABLE_CTRL = 0x07BB, + RANGING_CORE__SPAD_EN_SHIFT_OUT_DEBUG = 0x07BC, + RANGING_CORE__SPI_MODE = 0x07BD, + RANGING_CORE__GPIO_DIR = 0x07BE, + RANGING_CORE__VCSEL_PERIOD = 0x0880, + RANGING_CORE__VCSEL_START = 0x0881, + RANGING_CORE__VCSEL_STOP = 0x0882, + RANGING_CORE__VCSEL_1 = 0x0885, + RANGING_CORE__VCSEL_STATUS = 0x088D, + RANGING_CORE__STATUS = 0x0980, + RANGING_CORE__LASER_CONTINUITY_STATE = 0x0981, + RANGING_CORE__RANGE_1_MMM = 0x0982, + RANGING_CORE__RANGE_1_LMM = 0x0983, + RANGING_CORE__RANGE_1_LLM = 0x0984, + RANGING_CORE__RANGE_1_LLL = 0x0985, + RANGING_CORE__RANGE_REF_1_MMM = 0x0986, + RANGING_CORE__RANGE_REF_1_LMM = 0x0987, + RANGING_CORE__RANGE_REF_1_LLM = 0x0988, + RANGING_CORE__RANGE_REF_1_LLL = 0x0989, + RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_MMM = 0x098A, + RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LMM = 0x098B, + RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LLM = 0x098C, + RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LLL = 0x098D, + RANGING_CORE__RANGING_TOTAL_EVENTS_1_MMM = 0x098E, + RANGING_CORE__RANGING_TOTAL_EVENTS_1_LMM = 0x098F, + RANGING_CORE__RANGING_TOTAL_EVENTS_1_LLM = 0x0990, + RANGING_CORE__RANGING_TOTAL_EVENTS_1_LLL = 0x0991, + RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_MMM = 0x0992, + RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LMM = 0x0993, + RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LLM = 0x0994, + RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LLL = 0x0995, + RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_MM = 0x0996, + RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_LM = 0x0997, + RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_LL = 0x0998, + RANGING_CORE__AMBIENT_MISMATCH_MM = 0x0999, + RANGING_CORE__AMBIENT_MISMATCH_LM = 0x099A, + RANGING_CORE__AMBIENT_MISMATCH_LL = 0x099B, + RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_MMM = 0x099C, + RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LMM = 0x099D, + RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LLM = 0x099E, + RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LLL = 0x099F, + RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_MMM = 0x09A0, + RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LMM = 0x09A1, + RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LLM = 0x09A2, + RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LLL = 0x09A3, + RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_MMM = 0x09A4, + RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LMM = 0x09A5, + RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LLM = 0x09A6, + RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LLL = 0x09A7, + RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_MM = 0x09A8, + RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_LM = 0x09A9, + RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_LL = 0x09AA, + RANGING_CORE__AMBIENT_MISMATCH_REF_MM = 0x09AB, + RANGING_CORE__AMBIENT_MISMATCH_REF_LM = 0x09AC, + RANGING_CORE__AMBIENT_MISMATCH_REF_LL = 0x09AD, + RANGING_CORE__GPIO_CONFIG__A0 = 0x0A00, + RANGING_CORE__RESET_CONTROL__A0 = 0x0A01, + RANGING_CORE__INTR_MANAGER__A0 = 0x0A02, + RANGING_CORE__POWER_FSM_TIME_OSC__A0 = 0x0A06, + RANGING_CORE__VCSEL_ATEST__A0 = 0x0A07, + RANGING_CORE__VCSEL_PERIOD_CLIPPED__A0 = 0x0A08, + RANGING_CORE__VCSEL_STOP_CLIPPED__A0 = 0x0A09, + RANGING_CORE__CALIB_2__A0 = 0x0A0A, + RANGING_CORE__STOP_CONDITION__A0 = 0x0A0B, + RANGING_CORE__STATUS_RESET__A0 = 0x0A0C, + RANGING_CORE__READOUT_CFG__A0 = 0x0A0D, + RANGING_CORE__WINDOW_SETTING__A0 = 0x0A0E, + RANGING_CORE__VCSEL_DELAY__A0 = 0x0A1A, + RANGING_CORE__REFERENCE_2__A0 = 0x0A1B, + RANGING_CORE__REGAVDD1V2__A0 = 0x0A1D, + RANGING_CORE__TST_MUX__A0 = 0x0A1F, + RANGING_CORE__CUSTOM_FE_2__A0 = 0x0A20, + RANGING_CORE__SPAD_READOUT__A0 = 0x0A21, + RANGING_CORE__CPUMP_1__A0 = 0x0A22, + RANGING_CORE__SPARE_REGISTER__A0 = 0x0A23, + RANGING_CORE__VCSEL_CONT_STAGE5_BYPASS__A0 = 0x0A24, + RANGING_CORE__RET_SPAD_EN_18 = 0x0A25, + RANGING_CORE__RET_SPAD_EN_19 = 0x0A26, + RANGING_CORE__RET_SPAD_EN_20 = 0x0A27, + RANGING_CORE__RET_SPAD_EN_21 = 0x0A28, + RANGING_CORE__RET_SPAD_EN_22 = 0x0A29, + RANGING_CORE__RET_SPAD_EN_23 = 0x0A2A, + RANGING_CORE__RET_SPAD_EN_24 = 0x0A2B, + RANGING_CORE__RET_SPAD_EN_25 = 0x0A2C, + RANGING_CORE__RET_SPAD_EN_26 = 0x0A2D, + RANGING_CORE__RET_SPAD_EN_27 = 0x0A2E, + RANGING_CORE__RET_SPAD_EN_28 = 0x0A2F, + RANGING_CORE__RET_SPAD_EN_29 = 0x0A30, + RANGING_CORE__RET_SPAD_EN_30 = 0x0A31, + RANGING_CORE__RET_SPAD_EN_31 = 0x0A32, + RANGING_CORE__REF_SPAD_EN_0__EWOK = 0x0A33, + RANGING_CORE__REF_SPAD_EN_1__EWOK = 0x0A34, + RANGING_CORE__REF_SPAD_EN_2__EWOK = 0x0A35, + RANGING_CORE__REF_SPAD_EN_3__EWOK = 0x0A36, + RANGING_CORE__REF_SPAD_EN_4__EWOK = 0x0A37, + RANGING_CORE__REF_SPAD_EN_5__EWOK = 0x0A38, + RANGING_CORE__REF_EN_START_SELECT = 0x0A39, + RANGING_CORE__REGDVDD1V2_ATEST__EWOK = 0x0A41, + SOFT_RESET_GO1 = 0x0B00, + PRIVATE__PATCH_BASE_ADDR_RSLV = 0x0E00, + PREV_SHADOW_RESULT__INTERRUPT_STATUS = 0x0ED0, + PREV_SHADOW_RESULT__RANGE_STATUS = 0x0ED1, + PREV_SHADOW_RESULT__REPORT_STATUS = 0x0ED2, + PREV_SHADOW_RESULT__STREAM_COUNT = 0x0ED3, + PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0ED4, + PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0ED4, + PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0ED5, + PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x0ED6, + PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x0ED6, + PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x0ED7, + PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 = 0x0ED8, + PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI = 0x0ED8, + PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO = 0x0ED9, + PREV_SHADOW_RESULT__SIGMA_SD0 = 0x0EDA, + PREV_SHADOW_RESULT__SIGMA_SD0_HI = 0x0EDA, + PREV_SHADOW_RESULT__SIGMA_SD0_LO = 0x0EDB, + PREV_SHADOW_RESULT__PHASE_SD0 = 0x0EDC, + PREV_SHADOW_RESULT__PHASE_SD0_HI = 0x0EDC, + PREV_SHADOW_RESULT__PHASE_SD0_LO = 0x0EDD, + PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 = 0x0EDE, + PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI = 0x0EDE, + PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO = 0x0EDF, + PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 = 0x0EE0, + PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI = 0x0EE0, + PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO = 0x0EE1, + PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0EE2, + PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0EE2, + PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0EE3, + PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0EE4, + PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0EE4, + PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0EE5, + PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x0EE6, + PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x0EE6, + PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x0EE7, + PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 = 0x0EE8, + PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI = 0x0EE8, + PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO = 0x0EE9, + PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 = 0x0EEA, + PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI = 0x0EEA, + PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO = 0x0EEB, + PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 = 0x0EEC, + PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI = 0x0EEC, + PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO = 0x0EED, + PREV_SHADOW_RESULT__SIGMA_SD1 = 0x0EEE, + PREV_SHADOW_RESULT__SIGMA_SD1_HI = 0x0EEE, + PREV_SHADOW_RESULT__SIGMA_SD1_LO = 0x0EEF, + PREV_SHADOW_RESULT__PHASE_SD1 = 0x0EF0, + PREV_SHADOW_RESULT__PHASE_SD1_HI = 0x0EF0, + PREV_SHADOW_RESULT__PHASE_SD1_LO = 0x0EF1, + PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 = 0x0EF2, + PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI = 0x0EF2, + PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO = 0x0EF3, + PREV_SHADOW_RESULT__SPARE_0_SD1 = 0x0EF4, + PREV_SHADOW_RESULT__SPARE_0_SD1_HI = 0x0EF4, + PREV_SHADOW_RESULT__SPARE_0_SD1_LO = 0x0EF5, + PREV_SHADOW_RESULT__SPARE_1_SD1 = 0x0EF6, + PREV_SHADOW_RESULT__SPARE_1_SD1_HI = 0x0EF6, + PREV_SHADOW_RESULT__SPARE_1_SD1_LO = 0x0EF7, + PREV_SHADOW_RESULT__SPARE_2_SD1 = 0x0EF8, + PREV_SHADOW_RESULT__SPARE_2_SD1_HI = 0x0EF8, + PREV_SHADOW_RESULT__SPARE_2_SD1_LO = 0x0EF9, + PREV_SHADOW_RESULT__SPARE_3_SD1 = 0x0EFA, + PREV_SHADOW_RESULT__SPARE_3_SD1_HI = 0x0EFA, + PREV_SHADOW_RESULT__SPARE_3_SD1_LO = 0x0EFB, + PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 = 0x0EFC, + PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 = 0x0EFC, + PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 = 0x0EFD, + PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 = 0x0EFE, + PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 = 0x0EFF, + PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 = 0x0F00, + PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 = 0x0F00, + PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 = 0x0F01, + PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 = 0x0F02, + PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 = 0x0F03, + PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 = 0x0F04, + PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 = 0x0F04, + PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 = 0x0F05, + PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 = 0x0F06, + PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 = 0x0F07, + PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 = 0x0F08, + PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 = 0x0F08, + PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 = 0x0F09, + PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 = 0x0F0A, + PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 = 0x0F0B, + PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 = 0x0F0C, + PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 = 0x0F0C, + PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 = 0x0F0D, + PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 = 0x0F0E, + PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 = 0x0F0F, + PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 = 0x0F10, + PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 = 0x0F10, + PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 = 0x0F11, + PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 = 0x0F12, + PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 = 0x0F13, + PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 = 0x0F14, + PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 = 0x0F14, + PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 = 0x0F15, + PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 = 0x0F16, + PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 = 0x0F17, + PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 = 0x0F18, + PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 = 0x0F18, + PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 = 0x0F19, + PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 = 0x0F1A, + PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 = 0x0F1B, + PREV_SHADOW_RESULT_CORE__SPARE_0 = 0x0F1C, + RESULT__DEBUG_STATUS = 0x0F20, + RESULT__DEBUG_STAGE = 0x0F21, + GPH__SYSTEM__THRESH_RATE_HIGH = 0x0F24, + GPH__SYSTEM__THRESH_RATE_HIGH_HI = 0x0F24, + GPH__SYSTEM__THRESH_RATE_HIGH_LO = 0x0F25, + GPH__SYSTEM__THRESH_RATE_LOW = 0x0F26, + GPH__SYSTEM__THRESH_RATE_LOW_HI = 0x0F26, + GPH__SYSTEM__THRESH_RATE_LOW_LO = 0x0F27, + GPH__SYSTEM__INTERRUPT_CONFIG_GPIO = 0x0F28, + GPH__DSS_CONFIG__ROI_MODE_CONTROL = 0x0F2F, + GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT = 0x0F30, + GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_HI = 0x0F30, + GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_LO = 0x0F31, + GPH__DSS_CONFIG__MANUAL_BLOCK_SELECT = 0x0F32, + GPH__DSS_CONFIG__MAX_SPADS_LIMIT = 0x0F33, + GPH__DSS_CONFIG__MIN_SPADS_LIMIT = 0x0F34, + GPH__MM_CONFIG__TIMEOUT_MACROP_A_HI = 0x0F36, + GPH__MM_CONFIG__TIMEOUT_MACROP_A_LO = 0x0F37, + GPH__MM_CONFIG__TIMEOUT_MACROP_B_HI = 0x0F38, + GPH__MM_CONFIG__TIMEOUT_MACROP_B_LO = 0x0F39, + GPH__RANGE_CONFIG__TIMEOUT_MACROP_A_HI = 0x0F3A, + GPH__RANGE_CONFIG__TIMEOUT_MACROP_A_LO = 0x0F3B, + GPH__RANGE_CONFIG__VCSEL_PERIOD_A = 0x0F3C, + GPH__RANGE_CONFIG__VCSEL_PERIOD_B = 0x0F3D, + GPH__RANGE_CONFIG__TIMEOUT_MACROP_B_HI = 0x0F3E, + GPH__RANGE_CONFIG__TIMEOUT_MACROP_B_LO = 0x0F3F, + GPH__RANGE_CONFIG__SIGMA_THRESH = 0x0F40, + GPH__RANGE_CONFIG__SIGMA_THRESH_HI = 0x0F40, + GPH__RANGE_CONFIG__SIGMA_THRESH_LO = 0x0F41, + GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS = 0x0F42, + GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_HI = 0x0F42, + GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_LO = 0x0F43, + GPH__RANGE_CONFIG__VALID_PHASE_LOW = 0x0F44, + GPH__RANGE_CONFIG__VALID_PHASE_HIGH = 0x0F45, + FIRMWARE__INTERNAL_STREAM_COUNT_DIV = 0x0F46, + FIRMWARE__INTERNAL_STREAM_COUNTER_VAL = 0x0F47, + DSS_CALC__ROI_CTRL = 0x0F54, + DSS_CALC__SPARE_1 = 0x0F55, + DSS_CALC__SPARE_2 = 0x0F56, + DSS_CALC__SPARE_3 = 0x0F57, + DSS_CALC__SPARE_4 = 0x0F58, + DSS_CALC__SPARE_5 = 0x0F59, + DSS_CALC__SPARE_6 = 0x0F5A, + DSS_CALC__SPARE_7 = 0x0F5B, + DSS_CALC__USER_ROI_SPAD_EN_0 = 0x0F5C, + DSS_CALC__USER_ROI_SPAD_EN_1 = 0x0F5D, + DSS_CALC__USER_ROI_SPAD_EN_2 = 0x0F5E, + DSS_CALC__USER_ROI_SPAD_EN_3 = 0x0F5F, + DSS_CALC__USER_ROI_SPAD_EN_4 = 0x0F60, + DSS_CALC__USER_ROI_SPAD_EN_5 = 0x0F61, + DSS_CALC__USER_ROI_SPAD_EN_6 = 0x0F62, + DSS_CALC__USER_ROI_SPAD_EN_7 = 0x0F63, + DSS_CALC__USER_ROI_SPAD_EN_8 = 0x0F64, + DSS_CALC__USER_ROI_SPAD_EN_9 = 0x0F65, + DSS_CALC__USER_ROI_SPAD_EN_10 = 0x0F66, + DSS_CALC__USER_ROI_SPAD_EN_11 = 0x0F67, + DSS_CALC__USER_ROI_SPAD_EN_12 = 0x0F68, + DSS_CALC__USER_ROI_SPAD_EN_13 = 0x0F69, + DSS_CALC__USER_ROI_SPAD_EN_14 = 0x0F6A, + DSS_CALC__USER_ROI_SPAD_EN_15 = 0x0F6B, + DSS_CALC__USER_ROI_SPAD_EN_16 = 0x0F6C, + DSS_CALC__USER_ROI_SPAD_EN_17 = 0x0F6D, + DSS_CALC__USER_ROI_SPAD_EN_18 = 0x0F6E, + DSS_CALC__USER_ROI_SPAD_EN_19 = 0x0F6F, + DSS_CALC__USER_ROI_SPAD_EN_20 = 0x0F70, + DSS_CALC__USER_ROI_SPAD_EN_21 = 0x0F71, + DSS_CALC__USER_ROI_SPAD_EN_22 = 0x0F72, + DSS_CALC__USER_ROI_SPAD_EN_23 = 0x0F73, + DSS_CALC__USER_ROI_SPAD_EN_24 = 0x0F74, + DSS_CALC__USER_ROI_SPAD_EN_25 = 0x0F75, + DSS_CALC__USER_ROI_SPAD_EN_26 = 0x0F76, + DSS_CALC__USER_ROI_SPAD_EN_27 = 0x0F77, + DSS_CALC__USER_ROI_SPAD_EN_28 = 0x0F78, + DSS_CALC__USER_ROI_SPAD_EN_29 = 0x0F79, + DSS_CALC__USER_ROI_SPAD_EN_30 = 0x0F7A, + DSS_CALC__USER_ROI_SPAD_EN_31 = 0x0F7B, + DSS_CALC__USER_ROI_0 = 0x0F7C, + DSS_CALC__USER_ROI_1 = 0x0F7D, + DSS_CALC__MODE_ROI_0 = 0x0F7E, + DSS_CALC__MODE_ROI_1 = 0x0F7F, + SIGMA_ESTIMATOR_CALC__SPARE_0 = 0x0F80, + VHV_RESULT__PEAK_SIGNAL_RATE_MCPS = 0x0F82, + VHV_RESULT__PEAK_SIGNAL_RATE_MCPS_HI = 0x0F82, + VHV_RESULT__PEAK_SIGNAL_RATE_MCPS_LO = 0x0F83, + VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF = 0x0F84, + VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_3 = 0x0F84, + VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_2 = 0x0F85, + VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_1 = 0x0F86, + VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_0 = 0x0F87, + PHASECAL_RESULT__PHASE_OUTPUT_REF = 0x0F88, + PHASECAL_RESULT__PHASE_OUTPUT_REF_HI = 0x0F88, + PHASECAL_RESULT__PHASE_OUTPUT_REF_LO = 0x0F89, + DSS_RESULT__TOTAL_RATE_PER_SPAD = 0x0F8A, + DSS_RESULT__TOTAL_RATE_PER_SPAD_HI = 0x0F8A, + DSS_RESULT__TOTAL_RATE_PER_SPAD_LO = 0x0F8B, + DSS_RESULT__ENABLED_BLOCKS = 0x0F8C, + DSS_RESULT__NUM_REQUESTED_SPADS = 0x0F8E, + DSS_RESULT__NUM_REQUESTED_SPADS_HI = 0x0F8E, + DSS_RESULT__NUM_REQUESTED_SPADS_LO = 0x0F8F, + MM_RESULT__INNER_INTERSECTION_RATE = 0x0F92, + MM_RESULT__INNER_INTERSECTION_RATE_HI = 0x0F92, + MM_RESULT__INNER_INTERSECTION_RATE_LO = 0x0F93, + MM_RESULT__OUTER_COMPLEMENT_RATE = 0x0F94, + MM_RESULT__OUTER_COMPLEMENT_RATE_HI = 0x0F94, + MM_RESULT__OUTER_COMPLEMENT_RATE_LO = 0x0F95, + MM_RESULT__TOTAL_OFFSET = 0x0F96, + MM_RESULT__TOTAL_OFFSET_HI = 0x0F96, + MM_RESULT__TOTAL_OFFSET_LO = 0x0F97, + XTALK_CALC__XTALK_FOR_ENABLED_SPADS = 0x0F98, + XTALK_CALC__XTALK_FOR_ENABLED_SPADS_3 = 0x0F98, + XTALK_CALC__XTALK_FOR_ENABLED_SPADS_2 = 0x0F99, + XTALK_CALC__XTALK_FOR_ENABLED_SPADS_1 = 0x0F9A, + XTALK_CALC__XTALK_FOR_ENABLED_SPADS_0 = 0x0F9B, + XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS = 0x0F9C, + XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_3 = 0x0F9C, + XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_2 = 0x0F9D, + XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_1 = 0x0F9E, + XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_0 = 0x0F9F, + XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS = 0x0FA0, + XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_3 = 0x0FA0, + XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_2 = 0x0FA1, + XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_1 = 0x0FA2, + XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_0 = 0x0FA3, + XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS = 0x0FA4, + XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_3 = 0x0FA4, + XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_2 = 0x0FA5, + XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_1 = 0x0FA6, + XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_0 = 0x0FA7, + RANGE_RESULT__ACCUM_PHASE = 0x0FA8, + RANGE_RESULT__ACCUM_PHASE_3 = 0x0FA8, + RANGE_RESULT__ACCUM_PHASE_2 = 0x0FA9, + RANGE_RESULT__ACCUM_PHASE_1 = 0x0FAA, + RANGE_RESULT__ACCUM_PHASE_0 = 0x0FAB, + RANGE_RESULT__OFFSET_CORRECTED_RANGE = 0x0FAC, + RANGE_RESULT__OFFSET_CORRECTED_RANGE_HI = 0x0FAC, + RANGE_RESULT__OFFSET_CORRECTED_RANGE_LO = 0x0FAD, + SHADOW_PHASECAL_RESULT__VCSEL_START = 0x0FAE, + SHADOW_RESULT__INTERRUPT_STATUS = 0x0FB0, + SHADOW_RESULT__RANGE_STATUS = 0x0FB1, + SHADOW_RESULT__REPORT_STATUS = 0x0FB2, + SHADOW_RESULT__STREAM_COUNT = 0x0FB3, + SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0FB4, + SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0FB4, + SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0FB5, + SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x0FB6, + SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x0FB6, + SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x0FB7, + SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 = 0x0FB8, + SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI = 0x0FB8, + SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO = 0x0FB9, + SHADOW_RESULT__SIGMA_SD0 = 0x0FBA, + SHADOW_RESULT__SIGMA_SD0_HI = 0x0FBA, + SHADOW_RESULT__SIGMA_SD0_LO = 0x0FBB, + SHADOW_RESULT__PHASE_SD0 = 0x0FBC, + SHADOW_RESULT__PHASE_SD0_HI = 0x0FBC, + SHADOW_RESULT__PHASE_SD0_LO = 0x0FBD, + SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 = 0x0FBE, + SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI = 0x0FBE, + SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO = 0x0FBF, + SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 = 0x0FC0, + SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI = 0x0FC0, + SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO = 0x0FC1, + SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0FC2, + SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0FC2, + SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0FC3, + SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0FC4, + SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0FC4, + SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0FC5, + SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x0FC6, + SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x0FC6, + SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x0FC7, + SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 = 0x0FC8, + SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI = 0x0FC8, + SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO = 0x0FC9, + SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 = 0x0FCA, + SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI = 0x0FCA, + SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO = 0x0FCB, + SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 = 0x0FCC, + SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI = 0x0FCC, + SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO = 0x0FCD, + SHADOW_RESULT__SIGMA_SD1 = 0x0FCE, + SHADOW_RESULT__SIGMA_SD1_HI = 0x0FCE, + SHADOW_RESULT__SIGMA_SD1_LO = 0x0FCF, + SHADOW_RESULT__PHASE_SD1 = 0x0FD0, + SHADOW_RESULT__PHASE_SD1_HI = 0x0FD0, + SHADOW_RESULT__PHASE_SD1_LO = 0x0FD1, + SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 = 0x0FD2, + SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI = 0x0FD2, + SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO = 0x0FD3, + SHADOW_RESULT__SPARE_0_SD1 = 0x0FD4, + SHADOW_RESULT__SPARE_0_SD1_HI = 0x0FD4, + SHADOW_RESULT__SPARE_0_SD1_LO = 0x0FD5, + SHADOW_RESULT__SPARE_1_SD1 = 0x0FD6, + SHADOW_RESULT__SPARE_1_SD1_HI = 0x0FD6, + SHADOW_RESULT__SPARE_1_SD1_LO = 0x0FD7, + SHADOW_RESULT__SPARE_2_SD1 = 0x0FD8, + SHADOW_RESULT__SPARE_2_SD1_HI = 0x0FD8, + SHADOW_RESULT__SPARE_2_SD1_LO = 0x0FD9, + SHADOW_RESULT__SPARE_3_SD1 = 0x0FDA, + SHADOW_RESULT__THRESH_INFO = 0x0FDB, + SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 = 0x0FDC, + SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 = 0x0FDC, + SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 = 0x0FDD, + SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 = 0x0FDE, + SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 = 0x0FDF, + SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 = 0x0FE0, + SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 = 0x0FE0, + SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 = 0x0FE1, + SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 = 0x0FE2, + SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 = 0x0FE3, + SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 = 0x0FE4, + SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 = 0x0FE4, + SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 = 0x0FE5, + SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 = 0x0FE6, + SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 = 0x0FE7, + SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 = 0x0FE8, + SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 = 0x0FE8, + SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 = 0x0FE9, + SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 = 0x0FEA, + SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 = 0x0FEB, + SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 = 0x0FEC, + SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 = 0x0FEC, + SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 = 0x0FED, + SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 = 0x0FEE, + SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 = 0x0FEF, + SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 = 0x0FF0, + SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 = 0x0FF0, + SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 = 0x0FF1, + SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 = 0x0FF2, + SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 = 0x0FF3, + SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 = 0x0FF4, + SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 = 0x0FF4, + SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 = 0x0FF5, + SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 = 0x0FF6, + SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 = 0x0FF7, + SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 = 0x0FF8, + SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 = 0x0FF8, + SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 = 0x0FF9, + SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 = 0x0FFA, + SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 = 0x0FFB, + SHADOW_RESULT_CORE__SPARE_0 = 0x0FFC, + SHADOW_PHASECAL_RESULT__REFERENCE_PHASE_HI = 0x0FFE, + SHADOW_PHASECAL_RESULT__REFERENCE_PHASE_LO = 0x0FFF, + }; + + enum DistanceMode { Short, Medium, Long, Unknown }; + + enum RangeStatus : uint8_t + { + RangeValid = 0, + + // "sigma estimator check is above the internal defined threshold" + // (sigma = standard deviation of measurement) + SigmaFail = 1, + + // "signal value is below the internal defined threshold" + SignalFail = 2, + + // "Target is below minimum detection threshold." + RangeValidMinRangeClipped = 3, + + // "phase is out of bounds" + // (nothing detected in range; try a longer distance mode if applicable) + OutOfBoundsFail = 4, + + // "HW or VCSEL failure" + HardwareFail = 5, + + // "The Range is valid but the wraparound check has not been done." + RangeValidNoWrapCheckFail = 6, + + // "Wrapped target, not matching phases" + // "no matching phase in other VCSEL period timing." + WrapTargetFail = 7, + + // "Internal algo underflow or overflow in lite ranging." + // ProcessingFail = 8: not used in API + + // "Specific to lite ranging." + // should never occur with this lib (which uses low power auto ranging, + // as the API does) + XtalkSignalFail = 9, + + // "1st interrupt when starting ranging in back to back mode. Ignore + // data." + // should never occur with this lib + SynchronizationInt = 10, // (the API spells this "syncronisation") + + // "All Range ok but object is result of multiple pulses merging together. + // Used by RQL for merged pulse detection" + // RangeValid MergedPulse = 11: not used in API + + // "Used by RQL as different to phase fail." + // TargetPresentLackOfSignal = 12: + + // "Target is below minimum detection threshold." + MinRangeFail = 13, + + // "The reported range is invalid" + // RangeInvalid = 14: can't actually be returned by API (range can never become negative, even after correction) + + // "No Update." + None = 255, + }; + + struct RangingData + { + uint16_t range_mm; + RangeStatus range_status; + float peak_signal_count_rate_MCPS; + float ambient_count_rate_MCPS; + }; + + RangingData ranging_data; + + uint8_t last_status; // status of last I2C transmission + + VL53L1X(); + + void setAddress(uint8_t new_addr); + uint8_t getAddress() { return address; } + + bool init(bool io_2v8 = true); + + void writeReg(uint16_t reg, uint8_t value); + void writeReg16Bit(uint16_t reg, uint16_t value); + void writeReg32Bit(uint16_t reg, uint32_t value); + uint8_t readReg(regAddr reg); + uint16_t readReg16Bit(uint16_t reg); + uint32_t readReg32Bit(uint16_t reg); + + bool setDistanceMode(DistanceMode mode); + DistanceMode getDistanceMode() { return distance_mode; } + + bool setMeasurementTimingBudget(uint32_t budget_us); + uint32_t getMeasurementTimingBudget(); + + void startContinuous(uint32_t period_ms); + void stopContinuous(); + uint16_t read(bool blocking = true); + uint16_t readRangeContinuousMillimeters(bool blocking = true) { return read(blocking); } // alias of read() + + // check if sensor has new reading available + // assumes interrupt is active low (GPIO_HV_MUX__CTRL bit 4 is 1) + bool dataReady() { return (readReg(GPIO__TIO_HV_STATUS) & 0x01) == 0; } + + static const char * rangeStatusToString(RangeStatus status); + + void setTimeout(uint16_t timeout) { io_timeout = timeout; } + uint16_t getTimeout() { return io_timeout; } + bool timeoutOccurred(); + + private: + + // The Arduino two-wire interface uses a 7-bit number for the address, + // and sets the last bit correctly based on reads and writes + static const uint8_t AddressDefault = 0b0101001; + + // value used in measurement timing budget calculations + // assumes PresetMode is LOWPOWER_AUTONOMOUS + // + // vhv = LOWPOWER_AUTO_VHV_LOOP_DURATION_US + LOWPOWERAUTO_VHV_LOOP_BOUND + // (tuning parm default) * LOWPOWER_AUTO_VHV_LOOP_DURATION_US + // = 245 + 3 * 245 = 980 + // TimingGuard = LOWPOWER_AUTO_OVERHEAD_BEFORE_A_RANGING + + // LOWPOWER_AUTO_OVERHEAD_BETWEEN_A_B_RANGING + vhv + // = 1448 + 2100 + 980 = 4528 + static const uint32_t TimingGuard = 4528; + + // value in DSS_CONFIG__TARGET_TOTAL_RATE_MCPS register, used in DSS + // calculations + static const uint16_t TargetRate = 0x0A00; + + // for storing values read from RESULT__RANGE_STATUS (0x0089) + // through RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LOW + // (0x0099) + struct ResultBuffer + { + uint8_t range_status; + // uint8_t report_status: not used + uint8_t stream_count; + uint16_t dss_actual_effective_spads_sd0; + // uint16_t peak_signal_count_rate_mcps_sd0: not used + uint16_t ambient_count_rate_mcps_sd0; + // uint16_t sigma_sd0: not used + // uint16_t phase_sd0: not used + uint16_t final_crosstalk_corrected_range_mm_sd0; + uint16_t peak_signal_count_rate_crosstalk_corrected_mcps_sd0; + }; + + // making this static would save RAM for multiple instances as long as there + // aren't multiple sensors being read at the same time (e.g. on separate + // I2C buses) + ResultBuffer results; + + uint8_t address; + + uint16_t io_timeout; + bool did_timeout; + uint16_t timeout_start_ms; + + uint16_t fast_osc_frequency; + uint16_t osc_calibrate_val; + + bool calibrated; + uint8_t saved_vhv_init; + uint8_t saved_vhv_timeout; + + DistanceMode distance_mode; + + // Record the current time to check an upcoming timeout against + void startTimeout() { timeout_start_ms = millis(); } + + // Check if timeout is enabled (set to nonzero value) and has expired + bool checkTimeoutExpired() {return (io_timeout > 0) && ((uint16_t)(millis() - timeout_start_ms) > io_timeout); } + + void setupManualCalibration(); + void readResults(); + void updateDSS(); + void getRangingData(); + + static uint32_t decodeTimeout(uint16_t reg_val); + static uint16_t encodeTimeout(uint32_t timeout_mclks); + static uint32_t timeoutMclksToMicroseconds(uint32_t timeout_mclks, uint32_t macro_period_us); + static uint32_t timeoutMicrosecondsToMclks(uint32_t timeout_us, uint32_t macro_period_us); + uint32_t calcMacroPeriod(uint8_t vcsel_period); + + // Convert count rate from fixed point 9.7 format to float + float countRateFixedToFloat(uint16_t count_rate_fixed) { return (float)count_rate_fixed / (1 << 7); } +}; \ No newline at end of file diff --git a/lib/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino b/lib/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino new file mode 100644 index 000000000..e910466fa --- /dev/null +++ b/lib/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino @@ -0,0 +1,44 @@ +/* +This example shows how to take simple range measurements with the VL53L1X. The +range readings are in units of mm. +*/ + +#include +#include + +VL53L1X sensor; + +void setup() +{ + Serial.begin(115200); + Wire.begin(); + Wire.setClock(400000); // use 400 kHz I2C + + sensor.setTimeout(500); + if (!sensor.init()) + { + Serial.println("Failed to detect and initialize sensor!"); + while (1); + } + + // Use long distance mode and allow up to 50000 us (50 ms) for a measurement. + // You can change these settings to adjust the performance of the sensor, but + // the minimum timing budget is 20 ms for short distance mode and 33 ms for + // medium and long distance modes. See the VL53L1X datasheet for more + // information on range and timing limits. + sensor.setDistanceMode(VL53L1X::Long); + sensor.setMeasurementTimingBudget(50000); + + // Start continuous readings at a rate of one measurement every 50 ms (the + // inter-measurement period). This period should be at least as long as the + // timing budget. + sensor.startContinuous(50); +} + +void loop() +{ + Serial.print(sensor.read()); + if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); } + + Serial.println(); +} diff --git a/lib/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino b/lib/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino new file mode 100644 index 000000000..938b79bfc --- /dev/null +++ b/lib/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino @@ -0,0 +1,55 @@ +/* +This example takes range measurements with the VL53L1X and displays additional +details (status and signal/ambient rates) for each measurement, which can help +you determine whether the sensor is operating normally and the reported range is +valid. The range is in units of mm, and the rates are in units of MCPS (mega +counts per second). +*/ + +#include +#include + +VL53L1X sensor; + +void setup() +{ + Serial.begin(115200); + Wire.begin(); + Wire.setClock(400000); // use 400 kHz I2C + + sensor.setTimeout(500); + if (!sensor.init()) + { + Serial.println("Failed to detect and initialize sensor!"); + while (1); + } + + // Use long distance mode and allow up to 50000 us (50 ms) for a measurement. + // You can change these settings to adjust the performance of the sensor, but + // the minimum timing budget is 20 ms for short distance mode and 33 ms for + // medium and long distance modes. See the VL53L1X datasheet for more + // information on range and timing limits. + sensor.setDistanceMode(VL53L1X::Long); + sensor.setMeasurementTimingBudget(50000); + + // Start continuous readings at a rate of one measurement every 50 ms (the + // inter-measurement period). This period should be at least as long as the + // timing budget. + sensor.startContinuous(50); +} + +void loop() +{ + sensor.read(); + + Serial.print("range: "); + Serial.print(sensor.ranging_data.range_mm); + Serial.print("\tstatus: "); + Serial.print(VL53L1X::rangeStatusToString(sensor.ranging_data.range_status)); + Serial.print("\tpeak signal: "); + Serial.print(sensor.ranging_data.peak_signal_count_rate_MCPS); + Serial.print("\tambient: "); + Serial.print(sensor.ranging_data.ambient_count_rate_MCPS); + + Serial.println(); +} \ No newline at end of file diff --git a/lib/vl53l1x-arduino-1.01/keywords.txt b/lib/vl53l1x-arduino-1.01/keywords.txt new file mode 100644 index 000000000..32e3d3fa3 --- /dev/null +++ b/lib/vl53l1x-arduino-1.01/keywords.txt @@ -0,0 +1,41 @@ +VL53L1X KEYWORD1 + +setAddress KEYWORD2 +getAddress KEYWORD2 +init KEYWORD2 +writeReg KEYWORD2 +writeReg16Bit KEYWORD2 +writeReg32Bit KEYWORD2 +readReg KEYWORD2 +readReg16Bit KEYWORD2 +readReg32Bit KEYWORD2 +setDistanceMode KEYWORD2 +getDistanceMode KEYWORD2 +setMeasurementTimingBudget KEYWORD2 +getMeasurementTimingBudget KEYWORD2 +startContinuous KEYWORD2 +stopContinuous KEYWORD2 +read KEYWORD2 +readRangeContinuousMillimeters KEYWORD2 +rangeStatusToString KEYWORD2 +setTimeout KEYWORD2 +getTimeout KEYWORD2 +timeoutOccurred KEYWORD2 + +Short LITERAL1 +Medium LITERAL1 +Long LITERAL1 +Unknown LITERAL1 + +RangeValid LITERAL1 +SigmaFail LITERAL1 +SignalFail LITERAL1 +RangeValidMinRangeClipped LITERAL1 +OutOfBoundsFail LITERAL1 +HardwareFail LITERAL1 +RangeValidNoWrapCheckFail LITERAL1 +WrapTargetFail LITERAL1 +XtalkSignalFail LITERAL1 +SyncronisationInt LITERAL1 +MinRangeFail LITERAL1 +None LITERAL1 \ No newline at end of file diff --git a/lib/vl53l1x-arduino-1.01/library.properties b/lib/vl53l1x-arduino-1.01/library.properties new file mode 100644 index 000000000..1bf06dae9 --- /dev/null +++ b/lib/vl53l1x-arduino-1.01/library.properties @@ -0,0 +1,9 @@ +name=VL53L1X +version=1.0.1 +author=Pololu +maintainer=Pololu +sentence=VL53L1X distance sensor library +paragraph=This is a library for the Arduino IDE that helps interface with ST's VL53L1X distance sensor. +category=Sensors +url=https://github.com/pololu/vl53l1x-arduino +architectures=* diff --git a/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp index 85cb0663e..687661ac9 100755 --- a/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp +++ b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp @@ -654,7 +654,7 @@ bool ESP32_MailClient::readMail(IMAPData &imapData) { _sdOk = sdTest(); if (_sdOk) - if (!SD.exists(imapData._savePath.c_str())) + if (!imapData.fsp->exists(imapData._savePath.c_str())) createDirs(imapData._savePath); } else if (imapData._storageType == MailClientStorageType::SPIFFS) @@ -1788,6 +1788,7 @@ bool ESP32_MailClient::sendMail(SMTPData &smtpData) else { +/* if (!_sdOk) { if (smtpData._storageType == MailClientStorageType::SD) @@ -1798,13 +1799,18 @@ bool ESP32_MailClient::sendMail(SMTPData &smtpData) 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()); + else if (smtpData._storageType == MailClientStorageType::FFat) + file_existed = FFat.exists(smtpData._attach._filename[i].c_str()); +*/ + file_existed = smtpData.fsp->exists(smtpData._attach._filename[i].c_str()); if (file_existed) { smtpData._cbData._info = smtpData._attach._filename[i]; @@ -1820,10 +1826,15 @@ bool ESP32_MailClient::sendMail(SMTPData &smtpData) 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); + else if (smtpData._storageType == MailClientStorageType::FFat) + file = FFat.open(smtpData._attach._filename[i].c_str(), FILE_READ); + */ + file = smtpData.fsp->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); @@ -2036,6 +2047,7 @@ bool ESP32_MailClient::sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t _mosi = mosi; _ss = ss; _sdConfigSet = true; + SPI.begin(_sck, _miso, _mosi, _ss); return SD.begin(_ss, SPI); } @@ -2331,7 +2343,7 @@ bool ESP32_MailClient::waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandT delete[] midx; if (imapData._storageType == MailClientStorageType::SD) - if (!SD.exists(filepath.c_str())) + if (!imapData.fsp->exists(filepath.c_str())) createDirs(filepath); if (!imapData._headerSaved) @@ -2353,7 +2365,7 @@ bool ESP32_MailClient::waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandT } if (imapData._storageType == MailClientStorageType::SD) - file = SD.open(filepath.c_str(), FILE_WRITE); + file = imapData.fsp->open(filepath.c_str(), FILE_WRITE); else if (imapData._storageType == MailClientStorageType::SPIFFS) file = SPIFFS.open(filepath.c_str(), FILE_WRITE); } @@ -2922,7 +2934,7 @@ bool ESP32_MailClient::waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandT delete[] midx; if (imapData._storageType == MailClientStorageType::SD) - if (!SD.exists(filepath.c_str())) + if (!imapData.fsp->exists(filepath.c_str())) createDirs(filepath); if (!imapData._headerSaved) @@ -2944,7 +2956,7 @@ bool ESP32_MailClient::waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandT } if (imapData._storageType == MailClientStorageType::SD) - file = SD.open(filepath.c_str(), FILE_WRITE); + file = imapData.fsp->open(filepath.c_str(), FILE_WRITE); else if (imapData._storageType == MailClientStorageType::SPIFFS) file = SPIFFS.open(filepath.c_str(), FILE_WRITE); } @@ -3003,7 +3015,7 @@ bool ESP32_MailClient::waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandT delete[] midx; if (imapData._storageType == MailClientStorageType::SD) - if (!SD.exists(filepath.c_str())) + if (!imapData.fsp->exists(filepath.c_str())) createDirs(filepath); filepath += ESP32_MAIL_STR_202; @@ -3011,7 +3023,7 @@ bool ESP32_MailClient::waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandT filepath += imapData._messageDataInfo[mailIndex][messageDataIndex]._filename; if (imapData._storageType == MailClientStorageType::SD) - file = SD.open(filepath.c_str(), FILE_WRITE); + file = imapData.fsp->open(filepath.c_str(), FILE_WRITE); else if (imapData._storageType == MailClientStorageType::SPIFFS) file = SPIFFS.open(filepath.c_str(), FILE_WRITE); } @@ -3167,7 +3179,7 @@ bool ESP32_MailClient::waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandT { if (imapData._storageType == MailClientStorageType::SD) - file = SD.open(hpath.c_str(), FILE_WRITE); + file = imapData.fsp->open(hpath.c_str(), FILE_WRITE); else if (imapData._storageType == MailClientStorageType::SPIFFS) file = SPIFFS.open(hpath.c_str(), FILE_WRITE); @@ -3714,6 +3726,17 @@ void IMAPData::setFetchUID(const String &fetchUID) void IMAPData::setFileStorageType(uint8_t storageType) { _storageType = storageType; + switch (storageType) { + case MailClientStorageType::SPIFFS: + fsp = &SPIFFS; + break; + case MailClientStorageType::SD: + fsp = &SD; + break; + case MailClientStorageType::FFat: + fsp = &FFat; + break; + } } void IMAPData::setDownloadAttachment(bool download) @@ -4733,6 +4756,17 @@ void SMTPData::removeAttachFile(uint8_t index) void SMTPData::setFileStorageType(uint8_t storageType) { _storageType = storageType; + switch (storageType) { + case MailClientStorageType::SPIFFS: + fsp = &SPIFFS; + break; + case MailClientStorageType::SD: + fsp = &SD; + break; + case MailClientStorageType::FFat: + fsp = &FFat; + break; + } } void SMTPData::clearAttachData() diff --git a/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h index 943cd62f7..b6a8da775 100755 --- a/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h +++ b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h @@ -48,6 +48,7 @@ #include "RFC2047.h" #include "ESP32MailHTTPClient.h" #include "ESP32TimeHelper.h" +#include #define FORMAT_SPIFFS_IF_FAILED true @@ -90,6 +91,7 @@ struct MailClientStorageType { static const uint8_t SPIFFS = 0; static const uint8_t SD = 1; + static const uint8_t FFat = 2; }; static const char ESP32_MAIL_STR_1[] PROGMEM = "Content-Type: multipart/mixed; boundary=\""; @@ -1302,6 +1304,7 @@ private: std::string _host = ""; uint16_t _port = 993; uint8_t _storageType = 1; + FS *fsp = &SD; bool _unseen = false; std::string _loginEmail = ""; std::string _loginPassword = ""; @@ -1859,7 +1862,7 @@ protected: string _host = ""; uint16_t _port = 0; uint8_t _storageType = 1; - + FS *fsp = &SD; string _fromName = ""; string _senderEmail = ""; string _subject = ""; diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index ddd74a60d..444a5097c 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -36,7 +36,6 @@ default_envs = [common] -platform = ${core.platform} platform_packages = ${core.platform_packages} build_unflags = ${core.build_unflags} build_flags = ${core.build_flags} @@ -74,21 +73,18 @@ extra_scripts = ${scripts_defaults.extra_scripts} [core] ; Activate only (one set) if you want to override the standard core defined in platformio.ini !!! -;platform = ${tasmota_stage.platform} ;platform_packages = ${tasmota_stage.platform_packages} ;build_unflags = ${tasmota_stage.build_unflags} ;build_flags = ${tasmota_stage.build_flags} -;platform = ${core_stage.platform} -;platform_packages = ${core_stage.platform_packages} -;build_unflags = ${core_stage.build_unflags} -;build_flags = ${core_stage.build_flags} +platform_packages = ${core_stage.platform_packages} +build_unflags = ${core_stage.build_unflags} +build_flags = ${core_stage.build_flags} [tasmota_stage] ; *** Esp8266 core for Arduino version Tasmota stage -platform = espressif8266@2.6.2 -platform_packages = jason2866/framework-arduinoespressif8266 +platform_packages = framework-arduinoespressif8266@https://github.com/Jason2866/Arduino.git#2.7.4.2 build_unflags = ${esp_defaults.build_unflags} build_flags = ${esp82xx_defaults.build_flags} @@ -123,9 +119,8 @@ build_flags = ${esp82xx_defaults.build_flags} [core_stage] ; *** Esp8266 core version. Tasmota stage or Arduino stage version. Built with GCC 10.1 toolchain -platform = espressif8266@2.6.2 -platform_packages = framework-arduinoespressif8266 @ https://github.com/Jason2866/platform-espressif8266/releases/download/2.9.0/framework-arduinoespressif8266-3.20900.0.tar.gz - ;framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git +platform_packages = ;framework-arduinoespressif8266 @ https://github.com/Jason2866/platform-espressif8266/releases/download/2.9.0/framework-arduinoespressif8266-3.20900.0.tar.gz + framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git toolchain-xtensa @ ~2.100100.0 build_unflags = ${esp_defaults.build_unflags} -Wswitch-unreachable @@ -160,6 +155,17 @@ build_flags = ${esp82xx_defaults.build_flags} ; -lstdc++-exc +[core32] +; Activate Stage Core32 by removing ";" in next line, if you want to override the standard core32 +;platform_packages = ${core32_stage.platform_packages} + + +[core32_stage] +platform_packages = tool-esptoolpy@1.20800.0 + ; latest working commit + framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#c09ec5bd3d35ba7dfc135755ab300e2b45416def + + ; *** Debug version used for PlatformIO Home Project Inspection [env:tasmota-debug] build_type = debug diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini index 739ec2338..bdd07be81 100644 --- a/platformio_tasmota32.ini +++ b/platformio_tasmota32.ini @@ -39,9 +39,13 @@ default_envs = ${build_envs.default_envs} ; tasmota32-UK -[common32] +[core32] platform = espressif32@2.0.0 platform_packages = tool-esptoolpy@1.20800.0 + +[common32] +platform = ${core32.platform} +platform_packages = ${core32.platform_packages} board = esp32dev board_build.ldscript = esp32_out.ld board_build.partitions = esp32_partition_app1984k_spiffs64k.csv diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index c621487b8..1e58d4a87 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -13,12 +13,21 @@ - Fix energy total counters (#9263, #9266) - Fix crash in ``ZbRestore`` - Fix reset BMP sensors when executing command ``SaveData`` and define USE_DEEPSLEEP enabled (#9300) +- Fix ``status 0`` message when using define USE_MQTT_TLS due to small log buffer (#9305) +- Fix ``status 13`` exception 9 when more than one shutter is configured +- Fix ``status 13`` json message +- Fix Shelly 2.5 higher temperature regression from 8.2.0.1 (#7991) +- Change replace ArduinoJson with JSMN for JSON parsing +- Change ``WakeUp`` uses 256 steps instead of 100 (#9241) - Add command ``SetOption110 1`` to disable Zigbee auto-config when pairing new devices - Add command ``SetOption111 1`` to enable frequency output for buzzer GPIO (#8994) +- Add command ``SetOption112 1`` to enable friendly name in zigbee topic (use with SetOption89) - Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication +- Add ``#define MQTT_LWT_OFFLINE`` and ``#define MQTT_LWT_ONLINE`` to user_config.h (#9395) - Add new shutter modes (#9244) - Add Zigbee auto-config when pairing - Add support for MLX90640 IR array temperature sensor by Christian Baars +- Add support for VL53L1X time of flight sensor by Johann Obermeier ### 8.5.0 20200907 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 24b26b032..50c87660f 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -724,7 +724,7 @@ const char S_RSLT_POWER[] PROGMEM = D_RSLT_POWER; const char S_RSLT_RESULT[] PROGMEM = D_RSLT_RESULT; const char S_RSLT_WARNING[] PROGMEM = D_RSLT_WARNING; const char S_LWT[] PROGMEM = D_LWT; -const char S_OFFLINE[] PROGMEM = D_OFFLINE; +const char S_LWT_OFFLINE[] PROGMEM = MQTT_LWT_OFFLINE; // support.ino static const char kMonthNames[] = D_MONTH3LIST; diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index 1200ceab5..c7190998c 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -125,10 +125,10 @@ #define D_NOISE "Hluk" #define D_NONE "Žádný" #define D_OFF "Vyp." -#define D_OFFLINE "Neaktivní" +#define D_OFFLINE "Offline" // Don't translate, LWT message! Nepředkládat, LWT zpráva! #define D_OK "OK" #define D_ON "Zap." -#define D_ONLINE "Aktivní" +#define D_ONLINE "Online" // Don't translate, LWT message! Nepředkládat, LWT zpráva! #define D_PASSWORD "Heslo" #define D_PORT "Port" #define D_POWER_FACTOR "Účiník" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index f2673bda6..eed9062f0 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -369,8 +369,11 @@ //#define USE_ARDUINO_OTA // Add optional support for Arduino OTA (+13k code) // -- MQTT ---------------------------------------- -#define MQTT_TELE_RETAIN 0 // Tele messages may send retain flag (0 = off, 1 = on) -#define MQTT_CLEAN_SESSION 1 // Mqtt clean session connection (0 = No clean session, 1 = Clean session (default)) +#define MQTT_LWT_OFFLINE "Offline" // MQTT LWT offline topic message +#define MQTT_LWT_ONLINE "Online" // MQTT LWT online topic message + +#define MQTT_TELE_RETAIN 0 // Tele messages may send retain flag (0 = off, 1 = on) +#define MQTT_CLEAN_SESSION 1 // Mqtt clean session connection (0 = No clean session, 1 = Clean session (default)) // -- MQTT - Domoticz ----------------------------- #define USE_DOMOTICZ // Enable Domoticz (+6k code, +0.3k mem) @@ -539,6 +542,7 @@ // #define USE_SPS30 // [I2cDriver30] Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code) #define USE_ADE7953 // [I2cDriver7] Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5) // #define USE_VL53L0X // [I2cDriver31] Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code) +// #define USE_VL53L1X // [I2cDriver54] Enable support for VL53L1X sensor (I2C address 0x29) using Pololu VL53L1X library (+2k9 code) // #define USE_MLX90614 // [I2cDriver32] Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code) // #define USE_CHIRP // [I2cDriver33] Enable CHIRP soil moisture sensor (variable I2C address, default 0x20) // #define USE_PAJ7620 // [I2cDriver34] Enable PAJ7620 gesture sensor (I2C address 0x73) (+2.5k code) diff --git a/tasmota/sendemail.ino b/tasmota/sendemail.ino index 0a4c4de0e..bc080bd7f 100644 --- a/tasmota/sendemail.ino +++ b/tasmota/sendemail.ino @@ -330,6 +330,12 @@ String buffer; if (!buffer.startsWith(F("354"))) { goto exit; } + + buffer = F("MIME-Version: 1.0\r\n"); + client->print(buffer); + buffer = F("Content-Type: Multipart/mixed; boundary=frontier\r\n"); + client->print(buffer); + buffer = F("From: "); buffer += from; client->println(buffer); @@ -350,6 +356,7 @@ String buffer; AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); #endif + #ifdef USE_SCRIPT if (*msg=='*' && *(msg+1)==0) { g_client=client; @@ -378,9 +385,98 @@ exit: } void xsend_message_txt(char *msg) { - g_client->println(msg); -} +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),msg); +#endif +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + if (*msg=='@') { + msg++; + attach_File(msg); + } else if (*msg=='&') { + msg++; + attach_Array(msg); + } else { + g_client->print(F("--frontier\r\n")); + g_client->print(F("Content-Type: text/plain\r\n\r\n")); + g_client->println(msg); + g_client->print(F("\r\n--frontier\r\n")); + } #else + g_client->print(F("--frontier\r\n")); + g_client->print(F("Content-Type: text/plain\r\n\r\n")); + g_client->println(msg); + g_client->print(F("\r\n--frontier\r\n")); +#endif +} + +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +#include +extern FS *fsp; + +void attach_File(char *path) { + g_client->print(F("--frontier\r\n")); + g_client->print(F("Content-Type: text/plain\r\n")); + char buff[64]; + char *cp = path; + while (*cp=='/') cp++; + File file = fsp->open(path, "r"); + if (file) { + sprintf_P(buff,PSTR("Content-Disposition: attachment; filename=\"%s\"\r\n\r\n"), cp); + g_client->write(buff); + uint16_t flen = file.size(); + uint8_t fbuff[64]; + uint16_t blen = sizeof(fbuff); + while (flen>0) { + file.read(fbuff, blen); + flen -= blen; + g_client->write(fbuff, blen); + if (flenprint(F("\r\n\r\nfile not found!\r\n")); + } + g_client->print(F("\r\n--frontier\r\n")); +} + +float *get_array_by_name(char *name, uint16_t *alen); +void flt2char(float num, char *nbuff); + +void attach_Array(char *aname) { + float *array = 0; + uint16_t alen; + array = get_array_by_name(aname, &alen); + g_client->print(F("--frontier\r\n")); + g_client->print(F("Content-Type: text/plain\r\n")); + if (array && alen) { +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("array found %d"),alen); +#endif + char buff[64]; + sprintf_P(buff,PSTR("Content-Disposition: attachment; filename=\"%s.txt\"\r\n\r\n"), aname); + g_client->write(buff); + float *fp=array; + for (uint32_t cnt = 0; cntwrite(nbuff, strlen(nbuff)); + } + } else { + g_client->print(F("\r\n\r\narray not found!\r\n")); + } + g_client->print(F("\r\n--frontier\r\n")); +} + +#endif // defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + +#else + /* * Created by K. Suwatchai (Mobizt) @@ -413,6 +509,9 @@ void script_send_email_body(void(*func)(char *)); #define xPSTR(a) a //The Email Sending data object contains config and data to send SMTPData smtpData; +#define MAX_ATTCHMENTS 8 +char *attachments[MAX_ATTCHMENTS]; +uint8_t num_attachments; //Callback function to get the Email sending status //void sendCallback(SendStatus info); @@ -435,7 +534,7 @@ uint16_t SendMail(char *buffer) { // return if not enough memory uint32_t mem=ESP.getFreeHeap(); - AddLog_P2(LOG_LEVEL_INFO, PSTR("heap: %d"),mem); + //AddLog_P2(LOG_LEVEL_INFO, PSTR("heap: %d"),mem); if (memprint(F("\r\n\r\narray not found!\r\n")); + } +} void send_message_txt(char *txt) { - if (*txt=='&') { + if (*txt=='@') { txt++; smtpData.addAttachFile(txt); + } else if (*txt=='&') { + txt++; + attach_Array(txt); } else if (*txt=='$') { txt++; #if defined(ESP32) && defined(USE_WEBCAM) diff --git a/tasmota/settings.h b/tasmota/settings.h index 17229145a..b4598f987 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -131,7 +131,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t alexa_gen_1 : 1; // bit 27 (v8.4.0.3) - SetOption109 - Alexa gen1 mode - if you only have Echo Dot 2nd gen devices uint32_t zb_disable_autobind : 1; // bit 28 (v8.5.0.1) - SetOption110 - disable Zigbee auto-config when pairing new devices uint32_t buzzer_freq_mode : 1; // bit 29 (v8.5.0.1) - SetOption111 - Use frequency output for buzzer pin instead of on/off signal - uint32_t spare30 : 1; // bit 30 + uint32_t zb_topic_fname : 1; // bit 30 (v8.5.0.1) - SetOption112 - Use friendly name in zigbee topic (use with SetOption89) uint32_t spare31 : 1; // bit 31 }; } SysBitfield4; @@ -389,7 +389,7 @@ struct { uint8_t display_font; // 312 char ex_state_text[4][11]; // 313 - uint8_t ex_energy_power_delta; // 33F - Free since 6.6.0.20 + uint8_t tuyamcu_topic; // 33F Manage tuyaSend topic. ex_energy_power_delta on 6.6.0.20, replaced on 8.5.0.1 uint16_t domoticz_update_timer; // 340 uint16_t pwm_range; // 342 @@ -563,7 +563,7 @@ struct { uint16_t dimmer_hw_min; // E90 uint16_t dimmer_hw_max; // E92 uint32_t deepsleep; // E94 - uint16_t ex2_energy_power_delta; // E98 - Free since 8.4.0.3 + uint16_t hass_new_discovery; // E98 - ex2_energy_power_delta on 8.4.0.3, replaced on 8.5.0.1 uint8_t shutter_motordelay[MAX_SHUTTERS]; // E9A int8_t temp_comp; // E9E uint8_t weight_change; // E9F @@ -619,7 +619,8 @@ struct { uint16_t energy_power_delta[3]; // F44 uint16_t shutter_pwmrange[2][MAX_SHUTTERS]; // F4A - uint8_t free_f5a[90]; // F5A - Decrement if adding new Setting variables just above and below + + uint8_t free_f5a[89]; // F5A - Decrement if adding new Setting variables just above and below // Only 32 bit boundary variables below SysBitfield5 flag5; // FB4 diff --git a/tasmota/settings.ino b/tasmota/settings.ino index 66c1c6712..b56ec08c5 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -1356,8 +1356,8 @@ void SettingsDelta(void) Settings.ex_sbaudrate = 0; */ Settings.flag3.fast_power_cycle_disable = 0; - Settings.ex2_energy_power_delta = Settings.ex_energy_power_delta; - Settings.ex_energy_power_delta = 0; + Settings.hass_new_discovery = Settings.tuyamcu_topic; // replaced ex2_energy_power_delta on 8.5.0.1 + Settings.tuyamcu_topic = 0; // replaced ex_energy_power_delta on 8.5.0.1 } if (Settings.version < 0x06060015) { if ((EX_WIFI_SMARTCONFIG == Settings.ex_sta_config) || (EX_WIFI_WPSCONFIG == Settings.ex_sta_config)) { @@ -1514,7 +1514,7 @@ void SettingsDelta(void) Settings.fallback_module = FALLBACK_MODULE; } if (Settings.version < 0x08040003) { - Settings.energy_power_delta[0] = Settings.ex2_energy_power_delta; + Settings.energy_power_delta[0] = Settings.hass_new_discovery; // replaced ex2_energy_power_delta on 8.5.0.1 Settings.energy_power_delta[1] = 0; Settings.energy_power_delta[2] = 0; } diff --git a/tasmota/support.ino b/tasmota/support.ino index ca42dda28..db36944b3 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -116,7 +116,7 @@ String GetResetReason(void) /*********************************************************************************************\ * Miscellaneous \*********************************************************************************************/ - +/* String GetBinary(const void* ptr, size_t count) { uint32_t value = *(uint32_t*)ptr; value <<= (32 - count); @@ -128,6 +128,18 @@ String GetBinary(const void* ptr, size_t count) { } return result; } +*/ +String GetBinary8(uint8_t value, size_t count) { + if (count > 8) { count = 8; } + value <<= (8 - count); + String result; + result.reserve(count + 1); + for (uint32_t i = 0; i < count; i++) { + result += (value &0x80) ? '1' : '0'; + value <<= 1; + } + return result; +} // Get span until single character in string size_t strchrspn(const char *str1, int character) @@ -1486,59 +1498,44 @@ bool GetUsedInModule(uint32_t val, uint16_t *arr) return false; } -bool JsonTemplate(const char* dataBuf) +bool JsonTemplate(char* dataBuf) { // Old: {"NAME":"Shelly 2.5","GPIO":[56,0,17,0,21,83,0,0,6,82,5,22,156],"FLAG":2,"BASE":18} // New: {"NAME":"Shelly 2.5","GPIO":[320,0,32,0,224,193,0,0,640,192,608,225,3456,4736],"FLAG":0,"BASE":18} if (strlen(dataBuf) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks -#ifdef ESP8266 - StaticJsonBuffer<400> jb; // 331 from https://arduinojson.org/v5/assistant/ -#else - StaticJsonBuffer<999> jb; // 654 from https://arduinojson.org/v5/assistant/ -#endif - JsonObject& obj = jb.parseObject(dataBuf); - if (!obj.success()) { return false; } + JsonParser parser((char*) dataBuf); + JsonParserObject root = parser.getRootObject(); + if (!root) { return false; } // All parameters are optional allowing for partial changes - const char* name = obj[D_JSON_NAME]; - if (name != nullptr) { - SettingsUpdateText(SET_TEMPLATE_NAME, name); + JsonParserToken val = root[PSTR(D_JSON_NAME)]; + if (val) { + SettingsUpdateText(SET_TEMPLATE_NAME, val.getStr()); } - if (obj[D_JSON_GPIO].success()) { + JsonParserArray arr = root[PSTR(D_JSON_GPIO)]; + if (arr) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { #ifdef ESP8266 - if (!obj[D_JSON_GPIO][13].success()) { // Old template - - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TPL: Converting template ...")); - - uint8_t template8[sizeof(mytmplt8285)] = { GPIO_NONE }; - for (uint32_t i = 0; i < ARRAY_SIZE(template8) -1; i++) { - template8[i] = obj[D_JSON_GPIO][i] | 0; + Settings.user_template.gp.io[i] = arr[i].getUInt(); +#else // ESP32 + uint16_t gpio = arr[i].getUInt(); + if (gpio == (AGPIO(GPIO_NONE) +1)) { + gpio = AGPIO(GPIO_USER); } - if (obj[D_JSON_FLAG].success()) { - template8[ARRAY_SIZE(template8) -1] = (obj[D_JSON_FLAG] | 0) & 0x0F; - } - TemplateConvert(template8, Settings.user_template.gp.io); - Settings.user_template.flag.data = 0; - } else { + Settings.user_template.gp.io[i] = gpio; #endif - for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { - uint16_t gpio = obj[D_JSON_GPIO][i] | 0; - if (gpio == (AGPIO(GPIO_NONE) +1)) { - gpio = AGPIO(GPIO_USER); - } - Settings.user_template.gp.io[i] = gpio; - } - if (obj[D_JSON_FLAG].success()) { - Settings.user_template.flag.data = obj[D_JSON_FLAG] | 0; - } -#ifdef ESP8266 } -#endif } - if (obj[D_JSON_BASE].success()) { - uint32_t base = obj[D_JSON_BASE]; + val = root[PSTR(D_JSON_FLAG)]; + if (val) { + uint32_t flag = val.getUInt(); + memcpy(&Settings.user_template.flag, &flag, sizeof(gpio_flag)); + } + val = root[PSTR(D_JSON_BASE)]; + if (val) { + uint32_t base = val.getUInt(); if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; } Settings.user_template_base = base -1; // Default WEMOS } diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index b98287940..f38ec82ed 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -584,11 +584,12 @@ void CmndStatus(void) ResponseAppend_P(PSTR("{\"" D_STATUS13_SHUTTER "%d\":{\"Relay1\":%d,\"Relay2\":%d,\"Open\":%d,\"Close\":%d," "\"50perc\":%d,\"Delay\":%d,\"Opt\":\"%s\"," "\"Calib\":\"%d:%d:%d:%d:%d\"," - "\"Mode\":\"%d\"}"), + "\"Mode\":\"%d\"}}"), i, Settings.shutter_startrelay[i], Settings.shutter_startrelay[i] +1, Settings.shutter_opentime[i], Settings.shutter_closetime[i], - Settings.shutter_set50percent[i], Settings.shutter_motordelay[i], GetBinary(&Settings.shutter_options[i], 4).c_str(), + Settings.shutter_set50percent[i], Settings.shutter_motordelay[i], GetBinary8(Settings.shutter_options[i], 4).c_str(), Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], - Settings.shutter_mode); } + Settings.shutter_mode); + } ResponseJsonEnd(); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "13")); } diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 50da89faa..dc49a757f 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -604,10 +604,11 @@ void GetFeatures(void) #ifdef USE_MLX90640 feature6 |= 0x01000000; // xdrv_43_mlx90640.ino #endif -// feature6 |= 0x02000000; +#if defined(USE_I2C) && defined(USE_VL53L1X) + feature6 |= 0x02000000; // xsns_77_vl53l1x.ino +#endif // feature6 |= 0x04000000; // feature6 |= 0x08000000; - // feature6 |= 0x10000000; #if defined(ESP32) && defined(USE_TTGO_WATCH) feature6 |= 0x20000000; // xdrv_83_esp32watch.ino diff --git a/tasmota/support_json.ino b/tasmota/support_json.ino deleted file mode 100644 index b78c68eab..000000000 --- a/tasmota/support_json.ino +++ /dev/null @@ -1,115 +0,0 @@ -/* - support_json.ino - JSON support functions - - Copyright (C) 2020 Theo Arends and Stephan Hadinger - - 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 . -*/ - -/*********************************************************************************************\ - * JSON parsing -\*********************************************************************************************/ - -// does the character needs to be escaped, and if so with which character -char EscapeJSONChar(char c) { - if ((c == '\"') || (c == '\\')) { - return c; - } - if (c == '\n') { return 'n'; } - if (c == '\t') { return 't'; } - if (c == '\r') { return 'r'; } - if (c == '\f') { return 'f'; } - if (c == '\b') { return 'b'; } - return 0; -} - -String EscapeJSONString(const char *str) { - // As this function is used in ResponseCmndChar() and ResponseCmndIdxChar() - // it needs to be PROGMEM safe! - String r(""); - if (nullptr == str) { return r; } - - bool needs_escape = false; - size_t len_out = 1; - const char* c = str; - char ch = '.'; - while (ch != '\0') { - ch = pgm_read_byte(c++); - if (EscapeJSONChar(ch)) { - len_out++; - needs_escape = true; - } - len_out++; - } - - if (needs_escape) { - // we need to escape some chars - // allocate target buffer - r.reserve(len_out); - c = str; - char *d = r.begin(); - char ch = '.'; - while (ch != '\0') { - ch = pgm_read_byte(c++); - char c2 = EscapeJSONChar(ch); - if (c2) { - *d++ = '\\'; - *d++ = c2; - } else { - *d++ = ch; - } - } - *d = 0; // add NULL terminator - r = (char*) r.begin(); // assign the buffer to the string - } else { - r = FPSTR(str); - } - - return r; -} - -/*********************************************************************************************\ - * Find key - case insensitive -\*********************************************************************************************/ - -// Given a JsonObject, finds the value as JsonVariant for the key needle. -// The search is case-insensitive, and will find the first match in the order of keys in JSON -// -// If the key is not found, returns a nullptr -// Input: needle cannot be NULL but may be PROGMEM -const JsonVariant &GetCaseInsensitive(const JsonObject &json, const char *needle) { - // key can be in PROGMEM - // if needle == "?" then we return the first valid key - bool wildcard = strcmp_P("?", needle) == 0; - if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle)) || (!json.success())) { - return *(JsonVariant*)nullptr; - } - - for (JsonObject::const_iterator it=json.begin(); it!=json.end(); ++it) { - const char *key = it->key; - const JsonVariant &value = it->value; - - if (wildcard || (0 == strcasecmp_P(key, needle))) { - return value; - } - } - // if not found - return *(JsonVariant*)nullptr; -} - -// This function returns true if the JsonObject contains the specified key -// It's just a wrapper to the previous function but it can be tricky to test nullptr on an object ref -bool HasKeyCaseInsensitive(const JsonObject &json, const char *needle) { - return &GetCaseInsensitive(json, needle) != nullptr; -} diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index 3a9c7ab9e..9fbaf6629 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -359,10 +359,13 @@ const SerConfu8 kTasmotaSerialConfig[] PROGMEM = { enum TuyaSupportedFunctions { TUYA_MCU_FUNC_NONE, TUYA_MCU_FUNC_SWT1 = 1, TUYA_MCU_FUNC_SWT2, TUYA_MCU_FUNC_SWT3, TUYA_MCU_FUNC_SWT4, TUYA_MCU_FUNC_REL1 = 11, TUYA_MCU_FUNC_REL2, TUYA_MCU_FUNC_REL3, TUYA_MCU_FUNC_REL4, TUYA_MCU_FUNC_REL5, - TUYA_MCU_FUNC_REL6, TUYA_MCU_FUNC_REL7, TUYA_MCU_FUNC_REL8, TUYA_MCU_FUNC_DIMMER = 21, TUYA_MCU_FUNC_POWER = 31, - TUYA_MCU_FUNC_CURRENT, TUYA_MCU_FUNC_VOLTAGE, TUYA_MCU_FUNC_BATTERY_STATE, TUYA_MCU_FUNC_BATTERY_PERCENTAGE, + TUYA_MCU_FUNC_REL6, TUYA_MCU_FUNC_REL7, TUYA_MCU_FUNC_REL8, TUYA_MCU_FUNC_DIMMER = 21, TUYA_MCU_FUNC_DIMMER2, + TUYA_MCU_FUNC_CT, TUYA_MCU_FUNC_RGB, TUYA_MCU_FUNC_WHITE, TUYA_MCU_FUNC_MODESET, TUYA_MCU_FUNC_REPORT1, TUYA_MCU_FUNC_REPORT2, + TUYA_MCU_FUNC_POWER = 31, TUYA_MCU_FUNC_CURRENT, TUYA_MCU_FUNC_VOLTAGE, TUYA_MCU_FUNC_BATTERY_STATE, TUYA_MCU_FUNC_BATTERY_PERCENTAGE, TUYA_MCU_FUNC_REL1_INV = 41, TUYA_MCU_FUNC_REL2_INV, TUYA_MCU_FUNC_REL3_INV, TUYA_MCU_FUNC_REL4_INV, TUYA_MCU_FUNC_REL5_INV, - TUYA_MCU_FUNC_REL6_INV, TUYA_MCU_FUNC_REL7_INV, TUYA_MCU_FUNC_REL8_INV, TUYA_MCU_FUNC_LOWPOWER_MODE = 51, TUYA_MCU_FUNC_LAST = 255 + TUYA_MCU_FUNC_REL6_INV, TUYA_MCU_FUNC_REL7_INV, TUYA_MCU_FUNC_REL8_INV, TUYA_MCU_FUNC_LOWPOWER_MODE = 51, + TUYA_MCU_FUNC_FAN3 = 61, TUYA_MCU_FUNC_FAN4, TUYA_MCU_FUNC_FAN5, TUYA_MCU_FUNC_FAN6, + TUYA_MCU_FUNC_MOTOR_DIR = 97, TUYA_MCU_FUNC_ERROR = 98 , TUYA_MCU_FUNC_DUMMY = 99, TUYA_MCU_FUNC_LAST = 255 }; #endif // _TASMOTA_H_ diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 111df2e4a..5edb089b0 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -50,7 +50,8 @@ #include // Ota #include // Ota #include // Webserver, Updater -#include // WemoHue, IRremote, Domoticz +#include +#include #ifdef USE_ARDUINO_OTA #include // Arduino OTA #ifndef USE_DISCOVERY diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index 25da1c180..f94844bf9 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -113,6 +113,7 @@ //#define USE_SPS30 // Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code) #define USE_ADE7953 // Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5) //#define USE_VL53L0X // Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code) +//#define USE_VL53L1X // Enable support for VL53L1X sensor (I2C address 0x29) using Pololu VL53L1X library (+2k9 code) //#define USE_MLX90614 // Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code) //#define USE_CHIRP // Enable CHIRP soil moisture sensor (variable I2C address, default 0x20) //#define USE_PAJ7620 // Enable PAJ7620 gesture sensor (I2C address 0x73) (+2.5k code) diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h index 5d6d9570f..f639694fd 100644 --- a/tasmota/tasmota_globals.h +++ b/tasmota/tasmota_globals.h @@ -122,11 +122,11 @@ String EthernetMacAddress(void); #define WS2812_LEDS 30 // [Pixels] Number of LEDs #endif -#ifdef USE_MQTT_TLS - const uint16_t WEB_LOG_SIZE = 2000; // Max number of characters in weblog -#else +//#ifdef USE_MQTT_TLS // Set to 4000 on 20200922 per #9305 +// const uint16_t WEB_LOG_SIZE = 2000; // Max number of characters in weblog +//#else const uint16_t WEB_LOG_SIZE = 4000; // Max number of characters in weblog -#endif +//#endif #if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) #error "Arduino ESP8266 Core versions before 2.7.1 are not supported" @@ -144,6 +144,12 @@ String EthernetMacAddress(void); #ifndef MQTT_CLEAN_SESSION #define MQTT_CLEAN_SESSION 1 // 0 = No clean session, 1 = Clean session (default) #endif +#ifndef MQTT_LWT_OFFLINE +#define MQTT_LWT_OFFLINE "Offline" // MQTT LWT offline topic message +#endif +#ifndef MQTT_LWT_ONLINE +#define MQTT_LWT_ONLINE "Online" // MQTT LWT online topic message +#endif #ifndef MESSZ //#define MESSZ 1040 // Max number of characters in JSON message string (Hass discovery and nice MQTT_MAX_PACKET_SIZE = 1200) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index c878ae5ca..be71b3aad 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -1451,6 +1451,31 @@ void HandleRoot(void) #endif // USE_SONOFF_IFAN WSContentSend_P(PSTR("")); } +#ifdef USE_TUYA_MCU + if (IsModuleTuya()) { + uint8_t modeset = 0; + if (AsModuleTuyaMS()) { + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("
")); + snprintf_P(stemp, sizeof(stemp), PSTR("" D_JSON_IRHVAC_MODE "")); + WSContentSend_P(HTTP_DEVICE_CONTROL, 26, devices_present + 1, + (strlen(SettingsText(SET_BUTTON1 + devices_present))) ? SettingsText(SET_BUTTON1 + devices_present) : stemp, ""); + WSContentSend_P(PSTR("")); + modeset = 1; + } + if (IsTuyaFanCtrl()) { + uint8_t device = devices_present + modeset; + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("
")); + for (uint32_t i = device + 1; i <= (TuyaFanSpeeds() + device) + 1; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i - (device + 1)); + WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i, + (strlen(SettingsText(SET_BUTTON1 + i))) ? SettingsText(SET_BUTTON1 + i) : stemp, ""); + } + WSContentSend_P(PSTR("")); + } + } +#endif // USE_TUYA_MCU #ifdef USE_SONOFF_RF if (SONOFF_BRIDGE == my_module_type) { WSContentSend_P(HTTP_TABLE100); @@ -1521,6 +1546,33 @@ bool HandleRootStatusRefresh(void) } } else { #endif // USE_SONOFF_IFAN +#ifdef USE_TUYA_MCU + if (IsModuleTuya()) { + uint8_t FuncIdx = 0; + if (device <= devices_present) { + ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE); + } else { + if (AsModuleTuyaMS() && device == devices_present + 1) { + uint8_t dpId = TuyaGetDpId(TUYA_MCU_FUNC_MODESET); + snprintf_P(svalue, sizeof(svalue), PSTR("Tuyasend4 %d,%d"), dpId, !TuyaModeSet()); + ExecuteCommand(svalue, SRC_WEBGUI); + } + if (IsTuyaFanCtrl()) { + uint8_t dpId = 0; + for (uint32_t i = 0; i <= 3; i++) { // Tuya Function FAN3 to FAN6 + if (TuyaGetDpId(TUYA_MCU_FUNC_FAN3 + i) != 0) { + dpId = TuyaGetDpId(TUYA_MCU_FUNC_FAN3 + i); + } + } + if ((AsModuleTuyaMS() && device != devices_present + 1) || !AsModuleTuyaMS()) { + if (AsModuleTuyaMS()) {FuncIdx = 1;} + snprintf_P(svalue, sizeof(svalue), PSTR("Tuyasend2 %d,%d"), dpId, (device - (devices_present + FuncIdx) - 1)); + ExecuteCommand(svalue, SRC_WEBGUI); + } + } + } + } else { +#endif // USE_TUYA_MCU #ifdef USE_SHUTTER int32_t ShutterWebButton; if (ShutterWebButton = IsShutterWebButton(device)) { @@ -1535,6 +1587,9 @@ bool HandleRootStatusRefresh(void) #ifdef USE_SONOFF_IFAN } #endif // USE_SONOFF_IFAN +#ifdef USE_TUYA_MCU + } +#endif // USE_TUYA_MCU } #ifdef USE_LIGHT WebGetArg("d0", tmp, sizeof(tmp)); // 0 - 100 Dimmer value @@ -1615,8 +1670,22 @@ bool HandleRootStatusRefresh(void) #ifdef USE_SONOFF_IFAN } #endif // USE_SONOFF_IFAN + WSContentSend_P(PSTR("")); } +#ifdef USE_TUYA_MCU + if (IsModuleTuya()) { + uint32_t fanspeed = TuyaFanState(); + uint32_t modeset = TuyaModeSet(); + if (IsTuyaFanCtrl() && !AsModuleTuyaMS()) { + WSContentSend_P(PSTR("
" D_JSON_IRHVAC_FANSPEED ": %d
"), fanspeed); + } else if (!IsTuyaFanCtrl() && AsModuleTuyaMS()) { + WSContentSend_P(PSTR("
" D_JSON_IRHVAC_MODE ": %d
"), modeset); + } else if (IsTuyaFanCtrl() && AsModuleTuyaMS()) { + WSContentSend_P(PSTR("
" D_JSON_IRHVAC_MODE ": %d - " D_JSON_IRHVAC_FANSPEED ": %d
"), modeset, fanspeed); + } + } +#endif // USE_TUYA_MCU WSContentEnd(); return true; @@ -3208,22 +3277,18 @@ bool JsonWebColor(const char* dataBuf) // Default pre v7 (Light theme) // {"WebColor":["#000","#fff","#f2f2f2","#000","#fff","#000","#fff","#f00","#008000","#fff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#fff","#999","#000"]} // {"WebColor":["#000000","#ffffff","#f2f2f2","#000000","#ffffff","#000000","#ffffff","#ff0000","#008000","#ffffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999","#000000"]} - char dataBufLc[strlen(dataBuf) +1]; - LowerCase(dataBufLc, dataBuf); - RemoveSpace(dataBufLc); - if (strlen(dataBufLc) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks - - StaticJsonBuffer<450> jb; // 421 from https://arduinojson.org/v5/assistant/ - JsonObject& obj = jb.parseObject(dataBufLc); - if (!obj.success()) { return false; } - - char parm_lc[10]; - if (obj[LowerCase(parm_lc, D_CMND_WEBCOLOR)].success()) { - for (uint32_t i = 0; i < COL_LAST; i++) { - const char* color = obj[parm_lc][i]; - if (color != nullptr) { - WebHexCode(i, color); + JsonParser parser((char*) dataBuf); + JsonParserObject root = parser.getRootObject(); + JsonParserArray arr = root[PSTR(D_CMND_WEBCOLOR)].getArray(); + if (arr) { // if arr is valid, i.e. json is valid, the key D_CMND_WEBCOLOR was found and the token is an arra + uint32_t i = 0; + for (auto color : arr) { + if (i < COL_LAST) { + WebHexCode(i, color.getStr()); + } else { + break; } + i++; } } return true; diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 58fd04c2c..cc0ed8e62 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -494,7 +494,7 @@ void MqttConnected(void) Mqtt.connect_count++; GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); - Response_P(PSTR(D_ONLINE)); + Response_P(PSTR(MQTT_LWT_ONLINE)); MqttPublish(stopic, true); if (!Settings.flag4.only_json_message) { // SetOption90 - Disable non-json MQTT response @@ -581,7 +581,7 @@ void MqttReconnect(void) #if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) // don't enable MQTT for AWS IoT if Private Key or Certificate are not set if (Mqtt.mqtt_tls) { - if (0 == strlen(SettingsText(SET_MQTT_PWD))) { // we anticipate that an empty password does not make sense with TLS. This avoids failed connections + if (0 == strlen(SettingsText(SET_MQTT_PWD))) { // we anticipate that an empty password does not make sense with TLS. This avoids failed connections Mqtt.allowed = false; } } @@ -612,7 +612,7 @@ void MqttReconnect(void) } GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); - Response_P(S_OFFLINE); + Response_P(S_LWT_OFFLINE); if (MqttClient.connected()) { MqttClient.disconnect(); } #ifdef USE_MQTT_TLS @@ -895,8 +895,8 @@ void CmndFullTopic(void) char stemp1[TOPSZ]; strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_FULLTOPIC : XdrvMailbox.data, sizeof(stemp1)); if (strcmp(stemp1, SettingsText(SET_MQTT_FULLTOPIC))) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format - MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic + Response_P((Settings.flag.mqtt_offline) ? S_LWT_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format + MqttPublishPrefixTopic_P(TELE, S_LWT, true); // Offline or remove previous retained topic SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp1); restart_flag = 2; } @@ -999,8 +999,8 @@ void CmndTopic(void) char stemp1[TOPSZ]; strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_TOPIC : XdrvMailbox.data, sizeof(stemp1)); if (strcmp(stemp1, SettingsText(SET_MQTT_TOPIC))) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format - MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic + Response_P((Settings.flag.mqtt_offline) ? S_LWT_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format + MqttPublishPrefixTopic_P(TELE, S_LWT, true); // Offline or remove previous retained topic SettingsUpdateText(SET_MQTT_TOPIC, stemp1); restart_flag = 2; } @@ -1342,7 +1342,7 @@ void MqttSaveSettings(void) strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2)); MakeValidMqtt(1, stemp2); if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format + Response_P((Settings.flag.mqtt_offline) ? S_LWT_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format MqttPublishPrefixTopic_P(TELE, S_LWT, true); // Offline or remove previous retained topic } SettingsUpdateText(SET_MQTT_TOPIC, stemp); diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 941659d62..b90d882c8 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -263,8 +263,6 @@ struct LIGHT { uint32_t strip_timer_counter = 0; // Bars and Gradient power_t power = 0; // Power for each channel if SetOption68, or boolean if single light - uint16_t wakeup_counter = 0; - uint8_t entry_color[LST_MAX]; uint8_t current_color[LST_MAX]; uint8_t new_color[LST_MAX]; @@ -276,11 +274,12 @@ struct LIGHT { uint8_t subtype = 0; // LST_ subtype uint8_t device = 0; uint8_t old_power = 1; - uint8_t wakeup_active = 0; - uint8_t wakeup_dimmer = 0; + uint8_t wakeup_active = 0; // 0=inctive, 1=on-going, 2=about to start, 3=will be triggered next cycle uint8_t fixed_color_index = 1; uint8_t pwm_offset = 0; // Offset in color buffer uint8_t max_scheme = LS_MAX -1; + + uint32_t wakeup_start_time = 0; bool update = true; bool pwm_multi_channels = false; // SetOption68, treat each PWM channel as an independant dimmer @@ -1866,25 +1865,27 @@ void LightAnimate(void) light_controller.calcLevels(Light.new_color); break; case LS_WAKEUP: - if (2 == Light.wakeup_active) { - Light.wakeup_active = 1; - for (uint32_t i = 0; i < Light.subtype; i++) { - Light.new_color[i] = 0; + { + if (2 == Light.wakeup_active) { + Light.wakeup_active = 1; + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = 0; + } + Light.wakeup_start_time = millis(); } - Light.wakeup_counter = 0; - Light.wakeup_dimmer = 0; - } - Light.wakeup_counter++; - if (Light.wakeup_counter > ((Settings.light_wakeup * STATES) / Settings.light_dimmer)) { - Light.wakeup_counter = 0; - Light.wakeup_dimmer++; - if (Light.wakeup_dimmer <= Settings.light_dimmer) { - light_state.setDimmer(Light.wakeup_dimmer); + // which step are we in a range 0..1023 + uint32_t step_10 = ((millis() - Light.wakeup_start_time) * 1023) / (Settings.light_wakeup * 1000); + if (step_10 > 1023) { step_10 = 1023; } // sanity check + uint8_t wakeup_bri = changeUIntScale(step_10, 0, 1023, 0, LightStateClass::DimmerToBri(Settings.light_dimmer)); + + if (wakeup_bri != light_state.getBri()) { + light_state.setBri(wakeup_bri); light_controller.calcLevels(); for (uint32_t i = 0; i < Light.subtype; i++) { Light.new_color[i] = Light.current_color[i]; } - } else { + } + if (1023 == step_10) { Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\"")); ResponseLightState(1); ResponseJsonEnd(); diff --git a/tasmota/xdrv_05_irremote.ino b/tasmota/xdrv_05_irremote.ino index bb5ce0c3c..690dc0c61 100644 --- a/tasmota/xdrv_05_irremote.ino +++ b/tasmota/xdrv_05_irremote.ino @@ -176,30 +176,21 @@ void IrReceiveCheck(void) uint32_t IrRemoteCmndIrSendJson(void) { - // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 // IRsend { "protocol": "RC5", "bits": 12, "data":"0xC86" } // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { - return IE_INVALID_JSON; - } - - StaticJsonBuffer<140> jsonBuf; - JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (!root.success()) { - return IE_INVALID_JSON; - } + RemoveSpace(XdrvMailbox.data); // TODO is this really needed? + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { return IE_INVALID_JSON; } // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } // IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 } - char parm_uc[10]; - const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; - uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; - uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0); - uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))]; + const char *protocol = root.getStr(PSTR(D_JSON_IR_PROTOCOL), ""); + uint16_t bits = root.getUInt(PSTR(D_JSON_IR_BITS), 0); + uint64_t data = root.getULong(PSTR(D_JSON_IR_DATA), 0); + uint16_t repeat = root.getUInt(PSTR(D_JSON_IR_REPEAT), 0); + // check if the IRSend is great than repeat if (XdrvMailbox.index > repeat + 1) { repeat = XdrvMailbox.index - 1; @@ -243,7 +234,6 @@ void CmndIrSend(void) uint8_t error = IE_SYNTAX_IRSEND; if (XdrvMailbox.data_len) { -// error = (strstr(XdrvMailbox.data, "{") == nullptr) ? IrRemoteCmndIrSendRaw() : IrRemoteCmndIrSendJson(); if (strstr(XdrvMailbox.data, "{") == nullptr) { error = IE_INVALID_JSON; } else { diff --git a/tasmota/xdrv_05_irremote_full.ino b/tasmota/xdrv_05_irremote_full.ino index 9649e87b4..0b5f09ca3 100644 --- a/tasmota/xdrv_05_irremote_full.ino +++ b/tasmota/xdrv_05_irremote_full.ino @@ -111,38 +111,39 @@ void IrReceiveInit(void) } String sendACJsonState(const stdAc::state_t &state) { - DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); - json[D_JSON_IRHVAC_VENDOR] = typeToString(state.protocol); - json[D_JSON_IRHVAC_MODEL] = state.model; - json[D_JSON_IRHVAC_POWER] = IRac::boolToString(state.power); - json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(state.mode); + JsonGeneratorObject json; + json.add(PSTR(D_JSON_IRHVAC_VENDOR), typeToString(state.protocol)); + json.add(PSTR(D_JSON_IRHVAC_MODEL), state.model); + // Home Assistant wants mode to be off if power is also off & vice-versa. if (state.mode == stdAc::opmode_t::kOff || !state.power) { - json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff); - json[D_JSON_IRHVAC_POWER] = IRac::boolToString(false); - } - json[D_JSON_IRHVAC_CELSIUS] = IRac::boolToString(state.celsius); - if (floorf(state.degrees) == state.degrees) { - json[D_JSON_IRHVAC_TEMP] = floorf(state.degrees); // integer + json.add(PSTR(D_JSON_IRHVAC_MODE), IRac::opmodeToString(stdAc::opmode_t::kOff)); + json.add(PSTR(D_JSON_IRHVAC_POWER), IRac::boolToString(false)); } else { - json[D_JSON_IRHVAC_TEMP] = RawJson(String(state.degrees, 1)); // non-integer, limit to only 1 sub-digit + json.add(PSTR(D_JSON_IRHVAC_MODE), IRac::opmodeToString(state.mode)); + json.add(PSTR(D_JSON_IRHVAC_POWER), IRac::boolToString(state.power)); + } + json.add(PSTR(D_JSON_IRHVAC_CELSIUS), IRac::boolToString(state.celsius)); + if (floorf(state.degrees) == state.degrees) { + json.add(PSTR(D_JSON_IRHVAC_TEMP), (int32_t) floorf(state.degrees)); // integer + } else { + // TODO can do better here + json.addStrRaw(PSTR(D_JSON_IRHVAC_TEMP), String(state.degrees, 1).c_str()); // non-integer, limit to only 1 sub-digit } - json[D_JSON_IRHVAC_FANSPEED] = IRac::fanspeedToString(state.fanspeed); - json[D_JSON_IRHVAC_SWINGV] = IRac::swingvToString(state.swingv); - json[D_JSON_IRHVAC_SWINGH] = IRac::swinghToString(state.swingh); - json[D_JSON_IRHVAC_QUIET] = IRac::boolToString(state.quiet); - json[D_JSON_IRHVAC_TURBO] = IRac::boolToString(state.turbo); - json[D_JSON_IRHVAC_ECONO] = IRac::boolToString(state.econo); - json[D_JSON_IRHVAC_LIGHT] = IRac::boolToString(state.light); - json[D_JSON_IRHVAC_FILTER] = IRac::boolToString(state.filter); - json[D_JSON_IRHVAC_CLEAN] = IRac::boolToString(state.clean); - json[D_JSON_IRHVAC_BEEP] = IRac::boolToString(state.beep); - json[D_JSON_IRHVAC_SLEEP] = state.sleep; - String payload = ""; - payload.reserve(200); - json.printTo(payload); + json.add(PSTR(D_JSON_IRHVAC_FANSPEED), IRac::fanspeedToString(state.fanspeed)); + json.add(PSTR(D_JSON_IRHVAC_SWINGV), IRac::swingvToString(state.swingv)); + json.add(PSTR(D_JSON_IRHVAC_SWINGH), IRac::swinghToString(state.swingh)); + json.add(PSTR(D_JSON_IRHVAC_QUIET), IRac::boolToString(state.quiet)); + json.add(PSTR(D_JSON_IRHVAC_TURBO), IRac::boolToString(state.turbo)); + json.add(PSTR(D_JSON_IRHVAC_ECONO), IRac::boolToString(state.econo)); + json.add(PSTR(D_JSON_IRHVAC_LIGHT), IRac::boolToString(state.light)); + json.add(PSTR(D_JSON_IRHVAC_FILTER), IRac::boolToString(state.filter)); + json.add(PSTR(D_JSON_IRHVAC_CLEAN), IRac::boolToString(state.clean)); + json.add(PSTR(D_JSON_IRHVAC_BEEP), IRac::boolToString(state.beep)); + json.add(PSTR(D_JSON_IRHVAC_SLEEP), state.sleep); + + String payload = json.toString(); // copy string before returning, the original is on the stack return payload; } @@ -267,6 +268,16 @@ String listSupportedProtocols(bool hvac) { return l; } +bool strToBool(class JsonParserToken token, bool def) { + if (token.isBool() || token.isNum()) { + return token.getBool(); + } else if (token.isStr()) { + return IRac::strToBool(token.getStr()); + } else { + return def; + } +} + // used to convert values 0-5 to fanspeed_t const stdAc::fanspeed_t IrHvacFanSpeed[] PROGMEM = { stdAc::fanspeed_t::kAuto, stdAc::fanspeed_t::kMin, stdAc::fanspeed_t::kLow,stdAc::fanspeed_t::kMedium, @@ -275,17 +286,11 @@ const stdAc::fanspeed_t IrHvacFanSpeed[] PROGMEM = { stdAc::fanspeed_t::kAuto, uint32_t IrRemoteCmndIrHvacJson(void) { stdAc::state_t state, prev; - char parm_uc[12]; //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received %s"), XdrvMailbox.data); - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } - - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(dataBufUc); - if (!json.success()) { return IE_INVALID_JSON; } + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { return IE_INVALID_JSON; } // from: https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/CommonAcControl/CommonAcControl.ino state.protocol = decode_type_t::UNKNOWN; @@ -307,60 +312,44 @@ uint32_t IrRemoteCmndIrHvacJson(void) state.clean = false; // Turn off any Cleaning options if we can. state.clock = -1; // Don't set any current time if we can avoid it. - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); - if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); - if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } // also support 'protocol' + JsonParserToken val; + if (val = root[PSTR(D_JSON_IRHVAC_VENDOR)]) { state.protocol = strToDecodeType(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_PROTOCOL)]) { state.protocol = strToDecodeType(val.getStr()); } if (decode_type_t::UNKNOWN == state.protocol) { return IE_UNSUPPORTED_HVAC; } if (!IRac::isProtocolSupported(state.protocol)) { return IE_UNSUPPORTED_HVAC; } // for fan speed, we also support 1-5 values - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FANSPEED)); - if (json.containsKey(parm_uc)) { - uint32_t fan_speed = json[parm_uc]; + JsonParserToken tok_fan_speed = root[PSTR(D_JSON_IRHVAC_FANSPEED)]; + if (tok_fan_speed) { + uint32_t fan_speed = tok_fan_speed.getUInt(); if ((fan_speed >= 1) && (fan_speed <= 5)) { state.fanspeed = (stdAc::fanspeed_t) pgm_read_byte(&IrHvacFanSpeed[fan_speed]); } else { - state.fanspeed = IRac::strToFanspeed(json[parm_uc]); + state.fanspeed = IRac::strToFanspeed(tok_fan_speed.getStr()); } } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODEL)); - if (json.containsKey(parm_uc)) { state.model = IRac::strToModel(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODE)); - if (json.containsKey(parm_uc)) { state.mode = IRac::strToOpmode(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGV)); - if (json.containsKey(parm_uc)) { state.swingv = IRac::strToSwingV(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGH)); - if (json.containsKey(parm_uc)) { state.swingh = IRac::strToSwingH(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TEMP)); - if (json.containsKey(parm_uc)) { state.degrees = json[parm_uc]; } + if (val = root[PSTR(D_JSON_IRHVAC_MODEL)]) { state.model = IRac::strToModel(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_MODE)]) { state.mode = IRac::strToOpmode(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_SWINGV)]) { state.swingv = IRac::strToSwingV(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_SWINGH)]) { state.swingh = IRac::strToSwingH(val.getStr()); } + state.degrees = root.getFloat(PSTR(D_JSON_IRHVAC_TEMP), state.degrees); // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("model %d, mode %d, fanspeed %d, swingv %d, swingh %d"), // state.model, state.mode, state.fanspeed, state.swingv, state.swingh); // decode booleans - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_POWER)); - if (json.containsKey(parm_uc)) { state.power = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CELSIUS)); - if (json.containsKey(parm_uc)) { state.celsius = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_LIGHT)); - if (json.containsKey(parm_uc)) { state.light = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_BEEP)); - if (json.containsKey(parm_uc)) { state.beep = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_ECONO)); - if (json.containsKey(parm_uc)) { state.econo = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FILTER)); - if (json.containsKey(parm_uc)) { state.filter = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TURBO)); - if (json.containsKey(parm_uc)) { state.turbo = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_QUIET)); - if (json.containsKey(parm_uc)) { state.quiet = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CLEAN)); - if (json.containsKey(parm_uc)) { state.clean = IRac::strToBool(json[parm_uc]); } + state.power = strToBool(root[PSTR(D_JSON_IRHVAC_POWER)], state.power); + state.celsius = strToBool(root[PSTR(D_JSON_IRHVAC_CELSIUS)], state.celsius); + state.light = strToBool(root[PSTR(D_JSON_IRHVAC_LIGHT)], state.light); + state.beep = strToBool(root[PSTR(D_JSON_IRHVAC_BEEP)], state.beep); + state.econo = strToBool(root[PSTR(D_JSON_IRHVAC_ECONO)], state.econo); + state.filter = strToBool(root[PSTR(D_JSON_IRHVAC_FILTER)], state.filter); + state.turbo = strToBool(root[PSTR(D_JSON_IRHVAC_TURBO)], state.turbo); + state.quiet = strToBool(root[PSTR(D_JSON_IRHVAC_QUIET)], state.quiet); + state.clean = strToBool(root[PSTR(D_JSON_IRHVAC_CLEAN)], state.clean); // optional timer and clock - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SLEEP)); - if (json[parm_uc]) { state.sleep = json[parm_uc]; } + state.sleep = root.getInt(PSTR(D_JSON_IRHVAC_SLEEP), state.sleep); //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)); @@ -387,40 +376,32 @@ void CmndIrHvac(void) uint32_t IrRemoteCmndIrSendJson(void) { - char parm_uc[12]; // used to convert JSON keys to uppercase - // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 // IRsend { "protocol": "RC5", "bits": 12, "data":"0xC86" } // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } - - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(dataBufUc); - if (!json.success()) { return IE_INVALID_JSON; } + RemoveSpace(XdrvMailbox.data); // TODO is this really needed? + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { return IE_INVALID_JSON; } // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } // IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 } - decode_type_t protocol = decode_type_t::UNKNOWN; - uint16_t bits = 0; - uint64_t data; - uint8_t repeat = 0; + JsonParserToken value; - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); - if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); - if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } // also support 'protocol' + decode_type_t protocol = decode_type_t::UNKNOWN; + value = root[PSTR(D_JSON_IRHVAC_VENDOR)]; + if (root) { protocol = strToDecodeType(value.getStr()); } + value = root[PSTR(D_JSON_IRHVAC_PROTOCOL)]; + if (root) { protocol = strToDecodeType(value.getStr()); } if (decode_type_t::UNKNOWN == protocol) { return IE_UNSUPPORTED_PROTOCOL; } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS)); - if (json.containsKey(parm_uc)) { bits = json[parm_uc]; } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT)); - if (json.containsKey(parm_uc)) { repeat = json[parm_uc]; } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATALSB)); // accept LSB values - if (json.containsKey(parm_uc)) { data = reverseBitsInBytes64(strtoull(json[parm_uc], nullptr, 0)); } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA)); // or classical MSB (takes priority) - if (json.containsKey(parm_uc)) { data = strtoull(json[parm_uc], nullptr, 0); } + uint16_t bits = root.getUInt(PSTR(D_JSON_IR_BITS), 0); + uint16_t repeat = root.getUInt(PSTR(D_JSON_IR_REPEAT), 0); + + uint64_t data; + value = root[PSTR(D_JSON_IR_DATALSB)]; + if (root) { data = reverseBitsInBytes64(value.getULong()); } // accept LSB values + value = root[PSTR(D_JSON_IR_DATA)]; + if (value) { data = value.getULong(); } // or classical MSB (takes priority) if (0 == bits) { return IE_SYNTAX_IRSEND; } // check if the IRSend is greater than repeat, but can be overriden with JSON diff --git a/tasmota/xdrv_06_snfbridge.ino b/tasmota/xdrv_06_snfbridge.ino index af6f928bd..228564a45 100644 --- a/tasmota/xdrv_06_snfbridge.ino +++ b/tasmota/xdrv_06_snfbridge.ino @@ -127,7 +127,7 @@ ssize_t rf_decode_and_write(uint8_t *record, size_t size) uint16_t address = h->address_high * 0x100 + h->address_low; do { - err = c2_programming_init(); + err = c2_programming_init(C2_DEVID_EFM8BB1); err = c2_block_write(address, h->data, h->len); } while (err != C2_SUCCESS && retries--); } else if (h->record_type == IHX_RT_END_OF_FILE) { @@ -179,7 +179,7 @@ uint8_t rf_erase_flash(void) uint8_t err; for (uint32_t i = 0; i < 4; i++) { // HACK: Try multiple times as the command sometimes fails (unclear why) - err = c2_programming_init(); + err = c2_programming_init(C2_DEVID_EFM8BB1); if (err != C2_SUCCESS) { return 10; // Failed to init RF chip } diff --git a/tasmota/xdrv_07_domoticz.ino b/tasmota/xdrv_07_domoticz.ino index 2ea842491..0f4dafeb9 100644 --- a/tasmota/xdrv_07_domoticz.ino +++ b/tasmota/xdrv_07_domoticz.ino @@ -189,33 +189,13 @@ void DomoticzMqttSubscribe(void) { } } -/* - * ArduinoJSON Domoticz Switch entry used to calculate jsonBuf: JSON_OBJECT_SIZE(11) + 129 = 313 -{ - "Battery" : 255, - "RSSI" : 12, - "dtype" : "Light/Switch", - "id" : "000140E7", - "idx" : 159, - "name" : "Sonoff1", - "nvalue" : 1, - "stype" : "Switch", - "svalue1" : "0", - "switchType" : "Dimmer", - "unit" : 1 -} - * Fail on this one -{ - "LastUpdate" : "2018-10-02 20:39:45", - "Name" : "Sfeerverlichting", - "Status" : "Off", - "Timers" : "true", - "Type" : "Group", - "idx" : "2" -} -*/ - bool DomoticzMqttData(void) { +/* + XdrvMailbox.topic = topic; + XdrvMailbox.index = strlen(topic); + XdrvMailbox.data = (char*)data; + XdrvMailbox.data_len = data_len; +*/ domoticz_update_flag = true; if (strncasecmp_P(XdrvMailbox.topic, PSTR(DOMOTICZ_OUT_TOPIC), strlen(DOMOTICZ_OUT_TOPIC)) != 0) { @@ -226,19 +206,16 @@ bool DomoticzMqttData(void) { if (XdrvMailbox.data_len < 20) { return true; // No valid data } - StaticJsonBuffer<400> jsonBuf; - JsonObject& domoticz = jsonBuf.parseObject(XdrvMailbox.data); - if (!domoticz.success()) { + JsonParser parser(XdrvMailbox.data); + JsonParserObject domoticz = parser.getRootObject(); + if (!domoticz) { return true; // To much or invalid data } // if (strcmp_P(domoticz["dtype"],PSTR("Light/Switch"))) { // return true; // } - uint32_t idx = domoticz["idx"]; - int16_t nvalue = -1; - if (domoticz.containsKey("nvalue")) { - nvalue = domoticz["nvalue"]; - } + uint32_t idx = domoticz.getUInt(PSTR("idx"), 0); + int16_t nvalue = domoticz.getInt(PSTR("nvalue"), -1); AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue); @@ -247,20 +224,19 @@ bool DomoticzMqttData(void) { uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; for (uint32_t i = 0; i < maxdev; i++) { if (idx == Settings.domoticz_relay_idx[i]) { - bool iscolordimmer = strcmp_P(domoticz["dtype"],PSTR("Color Switch")) == 0; - bool isShutter = strcmp_P(domoticz["dtype"],PSTR("Light/Switch")) == 0 & strncmp_P(domoticz["switchType"],PSTR("Blinds"), 6) == 0; + bool iscolordimmer = strcmp_P(domoticz.getStr(PSTR("dtype")), PSTR("Color Switch")) == 0; + bool isShutter = strcmp_P(domoticz.getStr(PSTR("dtype")), PSTR("Light/Switch")) == 0 & strncmp_P(domoticz.getStr(PSTR("switchType")),PSTR("Blinds"), 6) == 0; char stemp1[10]; snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); #ifdef USE_SONOFF_IFAN if (IsModuleIfan() && (1 == i)) { // Idx 2 is fanspeed - uint8_t svalue = 0; - if (domoticz.containsKey("svalue1")) { - svalue = domoticz["svalue1"]; - } else { - return true; // Invalid data + JsonParserToken svalue_tok = domoticz[PSTR("svalue1")]; + if (!svalue_tok) { + return true; } - svalue = (nvalue == 2) ? svalue / 10 : 0; + uint8_t svalue = svalue_tok.getUInt(); + svalue = (2 == nvalue) ? svalue / 10 : 0; if (GetFanspeed() == svalue) { return true; // Stop loop as already set } @@ -273,18 +249,10 @@ bool DomoticzMqttData(void) { } else #endif // USE_SONOFF_IFAN #ifdef USE_SHUTTER - if (isShutter) - { - if (domoticz.containsKey("nvalue")) { - nvalue = domoticz["nvalue"]; - } - - uint8_t position = 0; - if (domoticz.containsKey("svalue1")) { - position = domoticz["svalue1"]; - } + if (isShutter) { + uint8_t position = domoticz.getUInt(PSTR("svalue1"), 0); if (nvalue != 2) { - position = nvalue == 0 ? 0 : 100; + position = (0 == nvalue) ? 0 : 100; } snprintf_P(XdrvMailbox.topic, TOPSZ, PSTR("/" D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION)); @@ -297,19 +265,16 @@ bool DomoticzMqttData(void) { #ifdef USE_LIGHT if (iscolordimmer && 10 == nvalue) { // Color_SetColor // https://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Set_a_light_to_a_certain_color_or_color_temperature - JsonObject& color = domoticz["Color"]; - uint16_t level = nvalue = domoticz["svalue1"]; - uint16_t r = color["r"]; r = r * level / 100; - uint16_t g = color["g"]; g = g * level / 100; - uint16_t b = color["b"]; b = b * level / 100; - uint16_t cw = color["cw"]; cw = cw * level / 100; - uint16_t ww = color["ww"]; ww = ww * level / 100; - uint16_t m = 0; - uint16_t t = 0; - if (color.containsKey("m")) { - m = color["m"]; - t = color["t"]; - } + JsonParserObject color = domoticz[PSTR("Color")].getObject(); + // JsonObject& color = domoticz["Color"]; + uint16_t level = nvalue = domoticz.getUInt(PSTR("svalue1"), 0); + uint16_t r = color.getUInt(PSTR("r"), 0) * level / 100; + uint16_t g = color.getUInt(PSTR("g"), 0) * level / 100; + uint16_t b = color.getUInt(PSTR("b"), 0) * level / 100; + uint16_t cw = color.getUInt(PSTR("cw"), 0) * level / 100; + uint16_t ww = color.getUInt(PSTR("ww"), 0) * level / 100; + uint16_t m = color.getUInt(PSTR("m"), 0); + uint16_t t = color.getUInt(PSTR("t"), 0); if (2 == m) { // White with color temperature. Valid fields: t snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_BACKLOG)); snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR(D_CMND_COLORTEMPERATURE " %d;" D_CMND_DIMMER " %d"), changeUIntScale(t, 0, 255, CT_MIN, CT_MAX), level); @@ -321,8 +286,8 @@ bool DomoticzMqttData(void) { } else if ((!iscolordimmer && 2 == nvalue) || // gswitch_sSetLevel (iscolordimmer && 15 == nvalue)) { // Color_SetBrightnessLevel - if (domoticz.containsKey("svalue1")) { - nvalue = domoticz["svalue1"]; + if (domoticz[PSTR("svalue1")]) { + nvalue = domoticz.getUInt(PSTR("svalue1"), 0); } else { return true; // Invalid data } diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/xdrv_09_timers.ino index 1ecfb0b67..35503836f 100644 --- a/tasmota/xdrv_09_timers.ino +++ b/tasmota/xdrv_09_timers.ino @@ -341,32 +341,33 @@ void CmndTimer(void) #if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 if (devices_present) { #endif - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - StaticJsonBuffer<256> jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(dataBufUc); - if (!root.success()) { + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); // JSON decode failed error = 1; } else { char parm_uc[10]; index--; - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { - Settings.timer[index].arm = (root[parm_uc] != 0); + JsonParserToken val = root[PSTR(D_JSON_TIMER_ARM)]; + if (val) { + Settings.timer[index].arm = (val.getInt() != 0); } #ifdef USE_SUNRISE - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_MODE))].success()) { - Settings.timer[index].mode = (uint8_t)root[parm_uc] & 0x03; + val = root[PSTR(D_JSON_TIMER_MODE)]; + if (val) { + Settings.timer[index].mode = val.getUInt() & 0x03; } #endif - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) { + val = root[PSTR(D_JSON_TIMER_TIME)]; + if (val) { uint16_t itime = 0; int8_t value = 0; uint8_t sign = 0; char time_str[10]; - strlcpy(time_str, root[parm_uc], sizeof(time_str)); + strlcpy(time_str, val.getStr(), sizeof(time_str)); const char *substr = strtok(time_str, ":"); if (substr != nullptr) { if (strchr(substr, '-')) { @@ -387,14 +388,16 @@ void CmndTimer(void) } Settings.timer[index].time = itime; } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_WINDOW))].success()) { - Settings.timer[index].window = (uint8_t)root[parm_uc] & 0x0F; + val = root[PSTR(D_JSON_TIMER_WINDOW)]; + if (val) { + Settings.timer[index].window = val.getUInt() & 0x0F; TimerSetRandomWindow(index); } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DAYS))].success()) { + val = root[PSTR(D_JSON_TIMER_DAYS)]; + if (val) { // SMTWTFS = 1234567 = 0011001 = 00TW00S = --TW--S Settings.timer[index].days = 0; - const char *tday = root[parm_uc]; + const char *tday = val.getStr(); uint8_t i = 0; char ch = *tday++; while ((ch != '\0') && (i < 7)) { @@ -404,15 +407,18 @@ void CmndTimer(void) ch = *tday++; } } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_REPEAT))].success()) { - Settings.timer[index].repeat = (root[parm_uc] != 0); + val = root[PSTR(D_JSON_TIMER_REPEAT)]; + if (val) { + Settings.timer[index].repeat = (val.getUInt() != 0); } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_OUTPUT))].success()) { - uint8_t device = ((uint8_t)root[parm_uc] -1) & 0x0F; + val = root[PSTR(D_JSON_TIMER_OUTPUT)]; + if (val) { + uint8_t device = (val.getUInt() -1) & 0x0F; Settings.timer[index].device = (device < devices_present) ? device : 0; } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ACTION))].success()) { - uint8_t action = (uint8_t)root[parm_uc] & 0x03; + val = root[PSTR(D_JSON_TIMER_ACTION)]; + if (val) { + uint8_t action = val.getUInt() & 0x03; Settings.timer[index].power = (devices_present) ? action : 3; // If no devices than only allow rules } diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index c377c4a91..2938e2d41 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -497,22 +497,19 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) rule_name = rule_name.substring(0, pos); // "SUBTYPE1#CURRENT" } -// StaticJsonBuffer<1280> jsonBuf; // Was 1024 until 20200811 - DynamicJsonBuffer jsonBuf; // Was static until 20200812 - JsonObject &root = jsonBuf.parseObject(event); - if (!root.success()) { + String buf = event; // copy the string into a new buffer that will be modified + JsonParser parser((char*)buf.c_str()); + JsonParserObject obj = parser.getRootObject(); + if (!obj) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event too long (%d)"), event.length()); - return false; - } // No valid JSON data - JsonObject *obj = &root; + return false; // No valid JSON data + } String subtype; uint32_t i = 0; while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT" subtype = rule_name.substring(0, pos); - const JsonVariant & val = GetCaseInsensitive(*obj, subtype.c_str()); - if (nullptr == &val) { return false; } // not found - obj = &(val.as()); - if (!obj->success()) { return false; } // not a JsonObject + obj = obj[subtype.c_str()].getObject(); + if (!obj) { return false; } // not found rule_name = rule_name.substring(pos +1); if (i++ > 10) { return false; } // Abandon possible loop @@ -520,13 +517,17 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) yield(); } - const JsonVariant & val = GetCaseInsensitive(*obj, rule_name.c_str()); - if (nullptr == &val) { return false; } // last level not found + JsonParserToken val = obj[rule_name.c_str()]; + if (!val) { return false; } // last level not found const char* str_value; if (rule_name_idx) { - str_value = (*obj)[rule_name][rule_name_idx -1]; // "CURRENT[1]" + if (val.isArray()) { + str_value = (val.getArray())[rule_name_idx -1].getStr(); + } else { + str_value = val.getStr(); + } } else { - str_value = (*obj)[rule_name]; // "CURRENT" + str_value = val.getStr(); // "CURRENT" } //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"), @@ -719,9 +720,11 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime())); RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL)); RulesVarReplace(commands, F("%TOPIC%"), mqtt_topic); - char unique_id[7]; - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X"), ESP_getChipId()); - RulesVarReplace(commands, F("%DEVICEID%"), unique_id); + snprintf_P(stemp, sizeof(stemp), PSTR("%06X"), ESP_getChipId()); + RulesVarReplace(commands, F("%DEVICEID%"), stemp); + String mac_address = WiFi.macAddress(); + mac_address.replace(":", ""); + RulesVarReplace(commands, F("%MACADDR%"), mac_address); #if defined(USE_TIMERS) && defined(USE_SUNRISE) RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0))); RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1))); @@ -1030,20 +1033,27 @@ bool RulesMqttData(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<500> jsonBuf; - JsonObject& jsonData = jsonBuf.parseObject(sData); + JsonParser parser((char*)sData.c_str()); + JsonParserObject jsonData = parser.getRootObject(); + String key1 = event_item.Key; String key2; - if (!jsonData.success()) break; //Failed to parse JSON data, ignore this message. + if (!jsonData) break; //Failed to parse JSON data, ignore this message. int dot; if ((dot = key1.indexOf('.')) > 0) { key2 = key1.substring(dot+1); key1 = key1.substring(0, dot); - if (!jsonData[key1][key2].success()) break; //Failed to get the key/value, ignore this message. - value = (const char *)jsonData[key1][key2]; + JsonParserToken value_tok = jsonData[key1.c_str()].getObject()[key2.c_str()]; + if (!value_tok) break; //Failed to get the key/value, ignore this message. + value = value_tok.getStr(); + // if (!jsonData[key1][key2].success()) break; //Failed to get the key/value, ignore this message. + // value = (const char *)jsonData[key1][key2]; } else { - if (!jsonData[key1].success()) break; - value = (const char *)jsonData[key1]; + JsonParserToken value_tok = jsonData[key1.c_str()]; + if (!value_tok) break; //Failed to get the key/value, ignore this message. + value = value_tok.getStr(); + // if (!jsonData[key1].success()) break; + // value = (const char *)jsonData[key1]; } } value.trim(); diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index ea8223233..8f5181914 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -65,6 +65,7 @@ keywords if then else endif, or, and are better readable for beginners (others m #define SCRIPT_MAXPERM (PMEM_SIZE)-4/sizeof(float) #define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS +#define MAX_SARRAY_NUM 32 uint32_t EncodeLightId(uint8_t relay_id); uint32_t DecodeLightId(uint32_t hue_id); @@ -79,6 +80,7 @@ uint32_t DecodeLightId(uint32_t hue_id); #undef LITTLEFS_SCRIPT_SIZE #undef EEP_SCRIPT_SIZE #undef USE_SCRIPT_COMPRESSION + #if USE_SCRIPT_FATFS==-1 #ifdef ESP32 @@ -250,9 +252,9 @@ SDClass *fsp; #define FAT_SCRIPT_NAME "script.txt" #endif -#if USE_STANDARD_SPI_LIBRARY==0 -#warning ("FATFS standard spi should be used"); -#endif +//#if USE_STANDARD_SPI_LIBRARY==0 +//#warning ("FATFS standard spi should be used"); +//#endif #endif // USE_SCRIPT_FATFS @@ -377,7 +379,11 @@ struct SCRIPT_MEM { struct T_INDEX *type; // type and index pointer struct M_FILT *mfilt; char *glob_vnp; // var name pointer +#ifdef SCRIPT_LARGE_VNBUFF + uint16_t *vnp_offset; +#else uint8_t *vnp_offset; +#endif char *glob_snp; // string vars pointer char *scriptptr; char *section_ptr; @@ -390,14 +396,16 @@ struct SCRIPT_MEM { void *script_mem; uint16_t script_mem_size; uint8_t script_dprec; + uint8_t script_lzero; uint8_t var_not_found; uint8_t glob_error; uint8_t max_ssize; uint8_t script_loglevel; uint8_t flags; - uint8_t si_num; - uint8_t siro_num; - char *last_index_string; + uint8_t si_num[3]; + uint8_t siro_num[3]; + uint8_t sind_num; + char *last_index_string[3]; #ifdef USE_SCRIPT_FATFS File files[SFS_MAX]; @@ -433,13 +441,37 @@ uint8_t fast_script=0; uint8_t glob_script=0; uint32_t script_lastmillis; +void flt2char(float num, char *nbuff) { + dtostrfd(num, glob_script_mem.script_dprec, nbuff); +} +// convert float to char with leading zeros +void f2char(float num, uint32_t dprec, uint32_t lzeros, char *nbuff) { + dtostrfd(num, dprec, nbuff); + if (lzeros>1) { + // check leading zeros + uint32_t nd = num; + nd/=10; + nd+=1; + if (lzeros>nd) { + // insert zeros + char cpbuf[24]; + char *cp = cpbuf; + for (uint32_t cnt = 0; cnt < lzeros - nd; cnt++) { + *cp++='0'; + } + *cp=0; + strcat(cpbuf,nbuff); + strcpy(nbuff,cpbuf); + } + } +} #ifdef USE_BUTTON_EVENT int8_t script_button[MAX_KEYS]; #endif //USE_BUTTON_EVENT -char *GetNumericArgument(char *lp,uint8_t lastop,float *fp,JsonObject *jo); -char *GetStringArgument(char *lp,uint8_t lastop,char *cp,JsonObject *jo); +char *GetNumericArgument(char *lp,uint8_t lastop,float *fp, JsonParserObject *jo); +char *GetStringArgument(char *lp,uint8_t lastop,char *cp, JsonParserObject *jo); char *ForceStringVar(char *lp,char *dstr); void send_download(void); uint8_t reject(char *name); @@ -511,7 +543,7 @@ char *script; char **snp_p = snp; uint8_t numperm = 0; uint8_t numflt = 0; - uint8_t count; + uint16_t count; glob_script_mem.max_ssize = SCRIPT_SVARSIZE; glob_script_mem.scriptptr = 0; @@ -592,7 +624,7 @@ char *script; } else { vtypes[vars].bits.is_filter = 0; } - *vnp_p ++= vnames_p; + *vnp_p++ = vnames_p; while (lp255) { + if (index > MAXVNSIZ) { free(glob_script_mem.script_mem); return -5; } @@ -760,7 +812,7 @@ char *script; // copy string variables char *cp1 = glob_script_mem.glob_snp; char *sp = strings; - for (count = 0; count=0 + if (sel == 0) { + result = SD.totalBytes()/1000; + } else if (sel == 1) { + result = (SD.totalBytes() - SD.usedBytes())/1000; + } +#else + if (sel == 0) { + result = FFat.totalBytes()/1000; + } else if (sel == 1) { + result = FFat.freeBytes()/1000; + } +#endif // USE_SCRIPT_FATFS>=0 +#else + // ESP8266 + FSInfo64 fsinfo; + fsp->info64(fsinfo); + if (sel == 0) { + result = fsinfo.totalBytes/1000; + } else if (sel == 1) { + result = (fsinfo.totalBytes - fsinfo.usedBytes)/1000; + } +#endif // ESP32 + return result; +} + +// format number with thousand marker +void form1000(uint32_t number, char *dp, char sc) { + char str[32]; + sprintf(str, "%d", number); + char *sp = str; + uint32_t inum = strlen(sp)/3; + uint32_t fnum = strlen(sp)%3; + if (!fnum) inum--; + for (uint32_t count=0; count<=inum; count++) { + if (fnum){ + memcpy(dp,sp,fnum); + dp+=fnum; + sp+=fnum; + fnum=0; + } else { + memcpy(dp,sp,3); + dp+=3; + sp+=3; + } + if (count!=inum) { + *dp++=sc; + } + } + *dp=0; +} + +#endif //USE_SCRIPT_FATFS + #ifdef USE_SCRIPT_GLOBVARS #define SCRIPT_UDP_BUFFER_SIZE 128 #define SCRIPT_UDP_PORT 1999 IPAddress script_udp_remote_ip; void Script_Stop_UDP(void) { - Script_PortUdp.flush(); - Script_PortUdp.stop(); - glob_script_mem.udp_flags.udp_connected = 0; + if (!glob_script_mem.udp_flags.udp_used) return; + if (glob_script_mem.udp_flags.udp_connected) { + Script_PortUdp.flush(); + Script_PortUdp.stop(); + glob_script_mem.udp_flags.udp_connected = 0; + } } void Script_Init_UDP() { if (global_state.network_down) return; + if (!glob_script_mem.udp_flags.udp_used) return; if (glob_script_mem.udp_flags.udp_connected) return; if (Script_PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), SCRIPT_UDP_PORT)) { @@ -898,6 +1013,7 @@ void Script_Init_UDP() { glob_script_mem.udp_flags.udp_connected = 0; } } + void Script_PollUdp(void) { if (global_state.network_down) return; if (!glob_script_mem.udp_flags.udp_used) return; @@ -1039,6 +1155,23 @@ float *Get_MFAddr(uint8_t index, uint16_t *len, uint16_t *ipos) { return 0; } +char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, JsonParserObject *jo); + + +float *get_array_by_name(char *name, uint16_t *alen) { + struct T_INDEX ind; + uint8_t vtype; + isvar(name, &vtype, &ind, 0, 0, 0); + if (vtype==VAR_NV) return 0; + if (vtype&STYPE) return 0; + uint16_t index = glob_script_mem.type[ind.index].index; + + if (glob_script_mem.type[ind.index].bits.is_filter) { + float *fa = Get_MFAddr(index, alen, 0); + return fa; + } + return 0; +} float Get_MFVal(uint8_t index, int16_t bind) { uint8_t *mp = (uint8_t*)glob_script_mem.mfilt; @@ -1304,10 +1437,85 @@ uint32_t match_vars(char *dvnam, float **fp, char **sp, uint32_t *ind) { } #endif //USE_SCRIPT_GLOBVARS +char *isargs(char *lp, uint32_t isind) { + float fvar; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + SCRIPT_SKIP_SPACES + if (*lp!='"') { + return lp; + } + lp++; + + if (glob_script_mem.si_num[isind]>0 && glob_script_mem.last_index_string[isind]) { + free(glob_script_mem.last_index_string[isind]); + } + char *sstart = lp; + uint8_t slen = 0; + for (uint32_t cnt = 0; cnt<256; cnt++) { + if (*lp=='\n' || *lp=='"' || *lp==0) { + lp++; + if (cnt>0 && !slen) { + slen++; + } + glob_script_mem.siro_num[isind] = slen; + break; + } + if (*lp=='|') { + slen++; + } + lp++; + } + + glob_script_mem.si_num[isind] = fvar; + if (glob_script_mem.si_num[isind]>0) { + if (glob_script_mem.si_num[isind]>MAX_SARRAY_NUM) { + glob_script_mem.si_num[isind] = MAX_SARRAY_NUM; + } + + glob_script_mem.last_index_string[isind] = (char*)calloc(glob_script_mem.max_ssize*glob_script_mem.si_num[isind], 1); + for (uint32_t cnt = 0; cntglob_script_mem.si_num[isind]) { + index = glob_script_mem.si_num[isind]; + } + strlcpy(str,glob_script_mem.last_index_string[isind] + (index * glob_script_mem.max_ssize), glob_script_mem.max_ssize); + } + } + lp++; + if (sp) strlcpy(sp, str, glob_script_mem.max_ssize); + return lp; +} // vtype => ff=nothing found, fe=constant number,fd = constant string else bit 7 => 80 = string, 0 = number // no flash strings here for performance reasons!!! -char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, JsonObject *jo) { +char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, JsonParserObject *jo) { uint16_t count,len = 0; uint8_t nres = 0; char vname[32]; @@ -1456,7 +1664,7 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, if (aindex<1 || aindex>6) aindex = 1; aindex--; } - if (jo->success()) { + if (jo->isValid()) { char *subtype = strchr(jvname, '#'); char *subtype2; if (subtype) { @@ -1469,23 +1677,23 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, } } vn = jvname; - str_value = (*jo)[vn]; - if ((*jo)[vn].success()) { + str_value = (*jo)[vn].getStr(); + if ((*jo)[vn].isValid()) { if (subtype) { - JsonObject &jobj1 = (*jo)[vn]; - if (jobj1.success()) { + JsonParserObject jobj1 = (*jo)[vn]; + if (jobj1.isValid()) { vn = subtype; jo = &jobj1; - str_value = (*jo)[vn]; - if ((*jo)[vn].success()) { + str_value = (*jo)[vn].getStr(); + if ((*jo)[vn].isValid()) { // 2. stage if (subtype2) { - JsonObject &jobj2 = (*jo)[vn]; - if ((*jo)[vn].success()) { + JsonParserObject jobj2 = (*jo)[vn]; + if ((*jo)[vn].isValid()) { vn = subtype2; jo = &jobj2; - str_value = (*jo)[vn]; - if ((*jo)[vn].success()) { + str_value = (*jo)[vn].getStr(); + if ((*jo)[vn].isValid()) { goto skip; } else { goto chknext; @@ -1504,10 +1712,10 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, skip: if (ja) { // json array - str_value = (*jo)[vn][aindex]; + str_value = (*jo)[vn].getArray()[aindex].getStr(); } if (str_value && *str_value) { - if ((*jo).is(vn)) { + if ((*jo)[vn].isStr()) { if (!strncmp(str_value, "ON", 2)) { if (fp) *fp = 1; goto nexit; @@ -1546,11 +1754,18 @@ chknext: case 'a': #ifdef USE_ANGLE_FUNC if (!strncmp(vname, "acos(", 5)) { - lp=GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); - fvar = acosf(fvar); - lp++; - len = 0; - goto exit; + lp=GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + fvar = acosf(fvar); + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "abs(", 4)) { + lp=GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + fvar = fabs(fvar); + lp++; + len = 0; + goto exit; } #endif if (!strncmp(vname, "asc(", 4)) { @@ -1982,6 +2197,101 @@ chknext: len = 0; goto exit; } + + if (!strncmp(vname, "fsi(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + fvar = get_fsinfo(fvar); + lp++; + len = 0; + goto exit; + } + + if (!strncmp(vname, "fwa(", 4)) { + struct T_INDEX ind; + uint8_t vtype; + lp = isvar(lp + 4, &vtype, &ind, 0, 0, 0); + if (vtype!=VAR_NV && (vtype&STYPE)==0 && glob_script_mem.type[ind.index].bits.is_filter) { + // found array as result + + } else { + // error + fvar = 0; + goto exit; + } + + while (*lp==' ') lp++; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + uint8_t index = fvar; + if (index>=SFS_MAX) index = SFS_MAX - 1; + if (glob_script_mem.file_flags[index].is_open) { + uint16_t len = 0; + float *fa = Get_MFAddr(glob_script_mem.type[ind.index].index, &len, 0); + char dstr[24]; + for (uint32_t cnt = 0; cnt=SFS_MAX) find = SFS_MAX - 1; + char str[glob_script_mem.max_ssize + 1]; + if (glob_script_mem.file_flags[find].is_open) { + uint16_t len = 0; + float *fa = Get_MFAddr(glob_script_mem.type[ind.index].index, &len, 0); + char dstr[24]; + for (uint32_t cnt = 0; cnt=glob_script_mem.max_ssize - 1) break; + } + } + *cp = 0; + *fa++=CharToFloat(str); + } + } else { + fvar = 0; + } + lp++; + len = 0; + goto exit; + } + #endif // USE_SCRIPT_FATFS_EXT if (!strncmp(vname, "fl1(", 4) || !strncmp(vname, "fl2(", 4) ) { uint8_t lknum = *(lp+2)&3; @@ -2099,7 +2409,6 @@ chknext: } #endif //USE_LIGHT break; -#define MAX_SARRAY_NUM 32 case 'i': if (!strncmp(vname, "int(", 4)) { lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); @@ -2109,77 +2418,35 @@ chknext: goto exit; } if (!strncmp(vname, "is(", 3)) { - lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); - SCRIPT_SKIP_SPACES - if (*lp!='"') { - break; - } - lp++; - - if (glob_script_mem.si_num>0 && glob_script_mem.last_index_string) { - free(glob_script_mem.last_index_string); - } - char *sstart = lp; - uint8_t slen = 0; - for (uint32_t cnt = 0; cnt<256; cnt++) { - if (*lp=='\n' || *lp=='"' || *lp==0) { - lp++; - if (cnt>0 && !slen) { - slen++; - } - glob_script_mem.siro_num = slen; - break; - } - if (*lp=='|') { - slen++; - } - lp++; - } - - glob_script_mem.si_num = fvar; - if (glob_script_mem.si_num>0) { - if (glob_script_mem.si_num>MAX_SARRAY_NUM) { - glob_script_mem.si_num = MAX_SARRAY_NUM; - } - - glob_script_mem.last_index_string = (char*)calloc(glob_script_mem.max_ssize*glob_script_mem.si_num, 1); - for (uint32_t cnt = 0; cntglob_script_mem.si_num) { - index = glob_script_mem.si_num; - } - strlcpy(str,glob_script_mem.last_index_string + (index * glob_script_mem.max_ssize), glob_script_mem.max_ssize); - } - } - lp++; - if (sp) strlcpy(sp, str, glob_script_mem.max_ssize); + lp = isget(lp + 3, sp, 0); + len = 0; + goto strexit; + } + if (!strncmp(vname, "is1[", 4)) { + lp = isget(lp + 4, sp, 1); + len = 0; + goto strexit; + } + if (!strncmp(vname, "is2[", 4)) { + lp = isget(lp + 4, sp, 2); len = 0; goto strexit; } @@ -2511,7 +2778,7 @@ chknext: if (!strncmp(vname, "s(", 2)) { lp = GetNumericArgument(lp + 2, OPER_EQU, &fvar, 0); char str[glob_script_mem.max_ssize + 1]; - dtostrfd(fvar, glob_script_mem.script_dprec, str); + f2char(fvar, glob_script_mem.script_dprec, glob_script_mem.script_lzero, str); if (sp) strlcpy(sp, str, glob_script_mem.max_ssize); lp++; len = 0; @@ -3014,7 +3281,7 @@ uint16_t GetStack(void) { } #endif //ESP8266 -char *GetStringArgument(char *lp, uint8_t lastop, char *cp, JsonObject *jo) { +char *GetStringArgument(char *lp, uint8_t lastop, char *cp, JsonParserObject *jo) { uint8_t operand = 0; uint8_t vtype; char *slp; @@ -3060,7 +3327,7 @@ char *GetStringArgument(char *lp, uint8_t lastop, char *cp, JsonObject *jo) { return lp; } -char *GetNumericArgument(char *lp, uint8_t lastop, float *fp, JsonObject *jo) { +char *GetNumericArgument(char *lp, uint8_t lastop, float *fp, JsonParserObject *jo) { uint8_t operand = 0; float fvar1,fvar; char *slp; @@ -3157,6 +3424,7 @@ void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dst uint16_t count; uint8_t vtype; uint8_t dprec = glob_script_mem.script_dprec; + uint8_t lzero = glob_script_mem.script_lzero; float fvar; cp = srcbuf; struct T_INDEX ind; @@ -3170,16 +3438,21 @@ void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dst dstbuf[count] = *cp++; } else { if (isdigit(*cp)) { + if (*(cp+1)=='.') { + lzero = *cp & 0xf; + cp+=2; + } dprec = *cp & 0xf; cp++; } else { dprec = glob_script_mem.script_dprec; + lzero = glob_script_mem.script_lzero; } if (*cp=='(') { // math expression cp++; cp = GetNumericArgument(cp, OPER_EQU, &fvar, 0); - dtostrfd(fvar, dprec, string); + f2char(fvar, dprec, lzero, string); uint8_t slen = strlen(string); if (count + slen0) return 0; - JsonObject *jo = 0; - DynamicJsonBuffer jsonBuffer; // on heap - JsonObject &jobj = jsonBuffer.parseObject(js); + JsonParserObject jo; if (js) { - jo = &jobj; - } else { - jo = 0; + String jss = js; // copy the string to a new buffer, not sure we can change the original buffer + JsonParser parser((char*)jss.c_str()); + jo = parser.getRootObject(); } - return Run_script_sub(type, tlen, jo); + return Run_script_sub(type, tlen, &jo); } -int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { +int16_t Run_script_sub(const char *type, int8_t tlen, JsonParserObject *jo) { uint8_t vtype=0,sindex,xflg,floop=0,globvindex,fromscriptcmd=0; char *lp_next; int16_t globaindex,saindex; @@ -3703,6 +3974,10 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { } else if (!strncmp(lp, "dp", 2) && isdigit(*(lp + 2))) { lp += 2; // number precision + if (*(lp + 1)== '.') { + glob_script_mem.script_lzero = atoi(lp); + lp+=2; + } glob_script_mem.script_dprec = atoi(lp); goto next_line; } @@ -4050,9 +4325,9 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { #endif //USE_SCRIPT_GLOBVARS if (saindex>=0) { if (lastop==OPER_EQU) { - strlcpy(glob_script_mem.last_index_string + (saindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); + strlcpy(glob_script_mem.last_index_string[glob_script_mem.sind_num] + (saindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); } else if (lastop==OPER_PLSEQU) { - strncat(glob_script_mem.last_index_string + (saindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); + strncat(glob_script_mem.last_index_string[glob_script_mem.sind_num] + (saindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); } last_sindex = -1; } else { @@ -4358,8 +4633,8 @@ const char HTTP_FORM_FILE_UPLOAD[] PROGMEM = "
" "
 %s" " "; const char HTTP_FORM_FILE_UPG[] PROGMEM = -"
" -"

" +"" +"

" "
"; const char HTTP_FORM_FILE_UPGb[] PROGMEM = @@ -4367,6 +4642,9 @@ const char HTTP_FORM_FILE_UPGb[] PROGMEM = "
" ""; +const char HTTP_FORM_FILE_UPGc[] PROGMEM = +"
total size: %s kB - free: %s kB
"; + const char HTTP_FORM_SDC_DIRa[] PROGMEM = "
"; const char HTTP_FORM_SDC_DIRb[] PROGMEM = @@ -4573,6 +4851,11 @@ void Script_FileUploadConfiguration(void) { WSContentSend_P(HTTP_FORM_FILE_UPLOAD,D_SDCARD_DIR); WSContentSend_P(HTTP_FORM_FILE_UPG, D_SCRIPT_UPLOAD); #ifdef SDCARD_DIR + char ts[16]; + char fs[16]; + form1000(get_fsinfo(0), ts, '.'); + form1000(get_fsinfo(1), fs, '.'); + WSContentSend_P(HTTP_FORM_FILE_UPGc, ts, fs); WSContentSend_P(HTTP_FORM_SDC_DIRa); if (glob_script_mem.script_sd_found) { ListDir(path, depth); @@ -4837,6 +5120,11 @@ void ScriptSaveSettings(void) { } void SaveScriptEnd(void) { + +#ifdef USE_SCRIPT_GLOBVARS + Script_Stop_UDP(); +#endif //USE_SCRIPT_GLOBVARS + if (glob_script_mem.script_mem) { Scripter_save_pvars(); free(glob_script_mem.script_mem); @@ -4856,6 +5144,9 @@ void SaveScriptEnd(void) { #endif // USE_SCRIPT_COMPRESSION if (bitRead(Settings.rule_enabled, 0)) { + + + int16_t res = Init_Scripter(); if (res) { AddLog_P2(LOG_LEVEL_INFO, PSTR("script init error: %d"), res); @@ -5234,15 +5525,16 @@ void Script_Handle_Hue(String *path) { if (Webserver->args()) { response = "["; - StaticJsonBuffer<400> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args()) - 1)); - if (hue_json.containsKey("on")) { + JsonParser parser((char*) Webserver->arg((Webserver->args())-1).c_str()); + JsonParserObject root = parser.getRootObject(); + JsonParserToken hue_on = root[PSTR("on")]; + if (hue_on) { response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); response.replace("{id", String(EncodeLightId(device))); response.replace("{cm", "on"); - bool on = hue_json["on"]; + bool on = hue_on.getBool(); if (on==false) { glob_script_mem.fvars[hue_script[index].index[0] - 1] = 0; response.replace("{re", "false"); @@ -5253,8 +5545,11 @@ void Script_Handle_Hue(String *path) { glob_script_mem.type[hue_script[index].vindex[0]].bits.changed = 1; resp = true; } - if (hue_json.containsKey("bri")) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. - tmp = hue_json["bri"]; + + parser.setCurrent(); + JsonParserToken hue_bri = root[PSTR("bri")]; + if (hue_bri) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. + tmp = hue_bri.getUInt(); bri = tmp; if (254 <= bri) { bri = 255; } if (resp) { response += ","; } @@ -5266,12 +5561,17 @@ void Script_Handle_Hue(String *path) { glob_script_mem.type[hue_script[index].vindex[1]].bits.changed = 1; resp = true; } - if (hue_json.containsKey("xy")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). + + JsonParserToken hue_xy = root[PSTR("xy")]; + if (hue_xy) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). float x, y; - x = hue_json["xy"][0]; - y = hue_json["xy"][1]; - const String &x_str = hue_json["xy"][0]; - const String &y_str = hue_json["xy"][1]; + JsonParserArray arr_xy = JsonParserArray(hue_xy); + JsonParserToken tok_x = arr_xy[0]; + JsonParserToken tok_y = arr_xy[1]; + x = tok_x.getFloat(); + y = tok_y.getFloat(); + String x_str = tok_x.getStr(); + String y_str = tok_y.getStr(); uint8_t rr,gg,bb; LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); @@ -5287,8 +5587,9 @@ void Script_Handle_Hue(String *path) { resp = true; } - if (hue_json.containsKey("hue")) { // The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue. - tmp = hue_json["hue"]; + JsonParserToken hue_hue = root[PSTR("hue")]; + if (hue_hue) { // The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue. + tmp = hue_hue.getUInt(); //hue = changeUIntScale(tmp, 0, 65535, 0, 359); //tmp = changeUIntScale(hue, 0, 359, 0, 65535); hue = tmp; @@ -5301,8 +5602,10 @@ void Script_Handle_Hue(String *path) { glob_script_mem.type[hue_script[index].vindex[2]].bits.changed = 1; resp = true; } - if (hue_json.containsKey("sat")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). - tmp = hue_json["sat"]; + + JsonParserToken hue_sat = root[PSTR("sat")]; + if (hue_sat) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). + tmp = hue_sat.getUInt(); sat = tmp; if (254 <= sat) { sat = 255; } if (resp) { response += ","; } @@ -5314,8 +5617,10 @@ void Script_Handle_Hue(String *path) { glob_script_mem.type[hue_script[index].vindex[3]].bits.changed = 1; resp = true; } - if (hue_json.containsKey("ct")) { // Color temperature 153 (Cold) to 500 (Warm) - ct = hue_json["ct"]; + + JsonParserToken hue_ct = root[PSTR("ct")]; + if (hue_ct) { // Color temperature 153 (Cold) to 500 (Warm) + ct = hue_ct.getUInt(); if (resp) { response += ","; } response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); response.replace("{id", String(EncodeLightId(device))); @@ -5533,21 +5838,23 @@ 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 jsonBuf; - JsonObject& jsonData = jsonBuf.parseObject(sData); + JsonParser parser((char*)sData.c_str()); + JsonParserObject jsonData = parser.getRootObject(); String key1 = event_item.Key; String key2; - if (!jsonData.success()) break; //Failed to parse JSON data, ignore this message. + if (!jsonData) break; //Failed to parse JSON data, ignore this message. int dot; if ((dot = key1.indexOf('.')) > 0) { key2 = key1.substring(dot+1); key1 = key1.substring(0, dot); lkey = key2; - if (!jsonData[key1][key2].success()) break; //Failed to get the key/value, ignore this message. - value = (const char *)jsonData[key1][key2]; + JsonParserToken val = jsonData[key1.c_str()].getObject()[key2.c_str()]; + if (!val) break; //Failed to get the key/value, ignore this message. + value = val.getStr(); } else { - if (!jsonData[key1].success()) break; - value = (const char *)jsonData[key1]; + JsonParserToken val = jsonData[key1.c_str()]; + if (!val) break; + value = val.getStr(); lkey = key1; } } @@ -6063,6 +6370,7 @@ const char SCRIPT_MSG_GTE1[] PROGMEM = "'%s'"; #define MAX_GARRAY 4 + char *gc_get_arrays(char *lp, float **arrays, uint8_t *ranum, uint16_t *rentries, uint16_t *ipos) { struct T_INDEX ind; uint8_t vtype; @@ -6540,7 +6848,7 @@ exgc: for (uint32_t ind = 0; ind < anum; ind++) { char acbuff[32]; float *fp = arrays[ind]; - dtostrfd(fp[aind], glob_script_mem.script_dprec, acbuff); + f2char(fp[aind], glob_script_mem.script_dprec, glob_script_mem.script_lzero, acbuff); WSContentSend_PD("%s", acbuff); if (indon("/exs", HTTP_GET, ScriptExecuteUploadSuccess); #ifdef USE_SCRIPT_FATFS - Webserver->on("/u3", HTTP_POST,[]() { Webserver->sendHeader("Location","/u3");Webserver->send(303);}, script_upload); - Webserver->on("/u3", HTTP_GET, ScriptFileUploadSuccess); + Webserver->on("/u13", HTTP_POST,[]() { Webserver->sendHeader("Location","/u13");Webserver->send(303);}, script_upload); + Webserver->on("/u13", HTTP_GET, ScriptFileUploadSuccess); Webserver->on("/upl", HTTP_GET, Script_FileUploadConfiguration); #endif //USE_SCRIPT_FATFS break; diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index ef9d25be9..1c2cf7fad 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -29,6 +29,7 @@ const char kHAssJsonSensorTypes[] PROGMEM = 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 "|" D_COLOR_RED "|" D_COLOR_GREEN "|" D_COLOR_BLUE"|" D_CCT "|" D_PROXIMITY "|Ambient|"; + const char kHAssJsonSensorUnits[] PROGMEM = "||||" "VA|%|A|Cm|Hz|%|LX|" @@ -44,7 +45,7 @@ const char kHAssJsonSensorDevCla[] PROGMEM = "dev_cla\":\"power|dev_cla\":\"power|dev_cla\":\"power|ic\":\"mdi:alpha-v-circle-outline|ic\":\"mdi:scale|dev_cla\":\"power|" "ic\":\"mdi:molecule-co2|ic\":\"mdi:molecule-co2|ic\":\"mdi:air-filter|" "ic\":\"mdi:palette|ic\":\"mdi:palette|ic\":\"mdi:palette|ic\":\"mdi:temperature-kelvin|ic\":\"mdi:ruler|dev_cla\":\"illuminance|"; - //"ic\":\"mdi:weather-windy|ic\":\"mdi:weather-windy|ic\":\"mdi:weather-windy|ic\":\"mdi:weather-windy|" + // List of sensors ready for discovery const char HASS_DISCOVER_BASE[] PROGMEM = @@ -58,8 +59,8 @@ const char HASS_DISCOVER_SENSOR[] PROGMEM = const char HASS_DISCOVER_SENSOR_LWT[] PROGMEM = ",\"avty_t\":\"%s\"," // tele/dualr2/LWT - "\"pl_avail\":\"" D_ONLINE "\"," // Online - "\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline + "\"pl_avail\":\"" MQTT_LWT_ONLINE "\"," // Online + "\"pl_not_avail\":\"" MQTT_LWT_OFFLINE "\""; // Offline const char HASS_DISCOVER_RELAY[] PROGMEM = ",\"cmd_t\":\"%s\"," // cmnd/dualr2/POWER2 @@ -174,6 +175,100 @@ uint8_t hass_init_step = 0; uint8_t hass_mode = 0; int hass_tele_period = 0; +// NEW DISCOVERY + +const char HASS_DISCOVER_DEVICE[] PROGMEM = // Basic parameters for Discovery + "{\"ip\":\"%s\"," // IP Address + "\"dn\":\"%s\"," // Device Name + "\"fn\":[%s]," // Friendly Names + "\"hn\":\"%s\"," // Host Name + "\"mac\":\"%s\"," // Full MAC as Device id + "\"md\":\"%s\"," // Module Name + "\"ofln\":\"" MQTT_LWT_OFFLINE "\"," // Payload Offline + "\"onln\":\"" MQTT_LWT_ONLINE "\"," // Payload Online + "\"state\":[\"%s\",\"%s\",\"%s\",\"%s\"]," // State text for "OFF","ON","TOGGLE","HOLD" + "\"sw\":\"%s\"," // Software Version + "\"t\":\"%s\"," // Topic + "\"ft\":\"%s\"," // Ful Topic + "\"tp\":[\"%s\",\"%s\",\"%s\"]," // Topics for command, stat and tele + "\"rl\":[%s],\"swc\":[%s],\"btn\":[%s]," // Inputs / Outputs + "\"so\":{\"11\":%d,\"13\":%d,\"17\":%d,\"20\":%d," // SetOptions + "\"30\":%d,\"37\":%d,\"68\":%d,\"73\":%d,\"80\":%d}," + "\"lt_st\":%d,\"ver\":1}"; // Light SubType, and Discovery version + +void NewHAssDiscovery(void) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[200]; + char stemp3[TOPSZ]; + char stemp4[TOPSZ]; + char stemp5[TOPSZ]; + char unique_id[30]; + char *state_topic = stemp1; + + stemp2[0] = '\0'; + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; + for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) { + char fname[TOPSZ]; + snprintf_P(fname, sizeof(fname), PSTR("\"%s\""), EscapeJSONString(SettingsText(SET_FRIENDLYNAME1 +i)).c_str()); + snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%s"), stemp2, (i > 0 ? "," : ""), (i < maxfn) ? fname : "null"); + } + + stemp3[0] = '\0'; + uint32_t maxrl = (devices_present > MAX_RELAYS) ? MAX_RELAYS : (!devices_present) ? 1 : devices_present; + for (uint32_t i = 0; i < MAX_RELAYS; i++) { + snprintf_P(stemp3, sizeof(stemp3), PSTR("%s%s%d"), stemp3, (i > 0 ? "," : ""), (i < maxrl) ? (Settings.flag.hass_light ? 2 : 1) : 0); + } + + stemp4[0] = '\0'; + // Enable Discovery for Switches only if SwitchTopic is set to a custom name + auto discover_switches = (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)); + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + snprintf_P(stemp4, sizeof(stemp4), PSTR("%s%s%d"), stemp4, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & discover_switches) ? Settings.switchmode[i] : -1); + } + + stemp5[0] = '\0'; + // Enable Discovery for Buttons only if SetOption73 is enabled + for (uint32_t i = 0; i < MAX_KEYS; i++) { + snprintf_P(stemp5, sizeof(stemp5), PSTR("%s%s%d"), stemp5, (i > 0 ? "," : ""), (PinUsed(GPIO_KEY1, i) & Settings.flag3.mqtt_buttons)); + } + + mqtt_data[0] = '\0'; // Clear retained message + + // Full 12 chars MAC address as ID + String mac_address = WiFi.macAddress(); + mac_address.replace(":", ""); + //String mac_part = mac_address.substring(0); + snprintf_P(unique_id, sizeof(unique_id), PSTR("%s"), mac_address.c_str()); + + //snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/discovery/%s/config"), unique_id); + snprintf_P(stopic, sizeof(stopic), PSTR("tasmota/discovery/%s/config"), unique_id); + GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); + + // Send empty message if new discovery is disabled + masterlog_level = 4; // Hide topic on clean and remove use weblog 4 to see it + if (!Settings.flag.hass_discovery) { + Response_P(HASS_DISCOVER_DEVICE, WiFi.localIP().toString().c_str(), SettingsText(SET_DEVICENAME), + stemp2, my_hostname, unique_id, ModuleName().c_str(), GetStateText(0), GetStateText(1), GetStateText(2), GetStateText(3), + my_version, mqtt_topic, MQTT_FULLTOPIC, SUB_PREFIX, PUB_PREFIX, PUB_PREFIX2, stemp3, stemp4, stemp5, Settings.flag.button_swap, + Settings.flag.button_single, Settings.flag.decimal_text, Settings.flag.not_power_linked, Settings.flag.hass_light, + light_controller.isCTRGBLinked(), Settings.flag3.pwm_multi_channels, Settings.flag3.mqtt_buttons, Settings.flag3.shutter_mode, Light.subtype); + } + MqttPublish(stopic, true); + + if (!Settings.flag.hass_discovery) { + snprintf_P(stopic, sizeof(stopic), PSTR("tasmota/discovery/%s/sensors"), unique_id); + Response_P(PSTR("{")); + MqttShowSensor(); + ResponseAppend_P(PSTR(",\"ver\":1}")); + MqttPublish(stopic, true); + } + masterlog_level = 0; // Restore WebLog state +} + +// NEW DISCOVERY + void TryResponseAppend_P(const char *format, ...) { va_list args; @@ -216,6 +311,7 @@ void HAssAnnounceRelayLight(void) bool TuyaMod = false; // Controls Tuya MCU modules bool PwmMod = false; // Controls PWM_DIMMER module bool FanMod = false; // Controls SONOFF_IFAN0X modules + uint8_t ShowTopic; // Used to hide/unhide a topic during Discovery to spare some cpu load uint8_t dimmer = 1; uint8_t valid_relay = 0; @@ -261,6 +357,8 @@ void HAssAnnounceRelayLight(void) TuyaDim = TuyaGetDpId((TUYA_MCU_FUNC_DIMMER) + active_device - 1); #endif //USE_TUYA_MCU + masterlog_level = ShowTopic = 4; // Hide topic on clean and remove use weblog 4 to see it + bool RelayX = PinUsed(GPIO_REL1, i-1) || (valid_relay >= i) || (TuyaRel > 0 && TuyaMod) || (TuyaRelInv > 0 && TuyaMod); // Check if the gpio is configured as Relay or force it for Sonoff DUAL R1 with MCU and Tuya MCU is_topic_light = Settings.flag.hass_light && RelayX || light_type && !RelayX || PwmMod || (TuyaDim > 0 && TuyaMod); // SetOption30 - Enforce HAss autodiscovery as light mqtt_data[0] = '\0'; // Clear retained message @@ -289,7 +387,9 @@ void HAssAnnounceRelayLight(void) char *command_topic = stemp1; char *state_topic = stemp2; char *availability_topic = stemp3; - masterlog_level = 0; + + ShowTopic = 0; + if (i > MAX_FRIENDLYNAMES) { snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i-1); } else { @@ -363,8 +463,8 @@ void HAssAnnounceRelayLight(void) TryResponseAppend_P(PSTR("}")); } } + masterlog_level = ShowTopic; MqttPublish(stopic, true); - masterlog_level = 4; } } @@ -377,7 +477,7 @@ void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t char stemp2[TOPSZ]; char unique_id[30]; char trigger2[8]; - + uint8_t ShowTopic; // Used to hide/unhide a topic during Discovery to spare some cpu load mqtt_data[0] = '\0'; // Clear retained message for (uint8_t i = trg_start; i <= trg_end; i++) { @@ -385,6 +485,8 @@ void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t 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); + masterlog_level = ShowTopic = 4; // Hide topic on clean and remove use weblog 4 to see it + if (Settings.flag.hass_discovery && present) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) char name[TOPSZ]; // friendlyname(33) + " " + "BTN" + " " + index char value_template[33]; @@ -392,7 +494,7 @@ void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t char *state_topic = stemp1; char *availability_topic = stemp2; char jsoname[8]; - masterlog_level = 0; + ShowTopic = 0; // Show the new generated topic GetPowerDevice(value_template, device + 1, sizeof(value_template), key + Settings.flag.device_index_enable); // Force index for Switch 1, Index on Button1 is controlled by SetOption26 - Switch between POWER or POWER1 snprintf_P(jsoname, sizeof(jsoname), PSTR("%s%d"), key ? "SWITCH" : "BUTTON", device + 1); @@ -420,8 +522,8 @@ void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t } } } + masterlog_level = ShowTopic; MqttPublish(stopic, true); - masterlog_level = 4; } } @@ -431,14 +533,18 @@ void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint char stemp1[TOPSZ]; char stemp2[TOPSZ]; char unique_id[30]; + uint8_t ShowTopic; // Used to hide/unhide a topic during Discovery to spare some cpu load + mqtt_data[0] = '\0'; // Clear retained message + masterlog_level = 4; // Hide topic on clean and remove use weblog 4 to see it + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_SW_%d"), ESP_getChipId(), device + 1); snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/binary_sensor/%s/config"), unique_id); + masterlog_level = ShowTopic = 4; // Hide topic on clean and remove use weblog 4 to see it if (Settings.flag.hass_discovery && present ) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) - masterlog_level = 0; if (!toggle || dual) { char name[TOPSZ]; // friendlyname(33) + " " + "BTN" + " " + index char value_template[33]; @@ -447,6 +553,8 @@ void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint char *availability_topic = stemp2; char jsoname[8]; + ShowTopic = 0; + GetPowerDevice(value_template, device + 1, sizeof(value_template), 1 + Settings.flag.device_index_enable); // Force index for Switch 1, Index on Button1 is controlled by SetOption26 - Switch between POWER or POWER1 snprintf_P(jsoname, sizeof(jsoname), PSTR("SWITCH%d"), device + 1); GetTopic_P(state_topic, STAT, mqtt_topic, jsoname); @@ -471,8 +579,9 @@ void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint TryResponseAppend_P(PSTR("}")); } } + masterlog_level = ShowTopic; MqttPublish(stopic, true); - masterlog_level = 4; + } void HAssAnnounceSwitches(void) @@ -594,7 +703,7 @@ void HAssAnnounceButtons(void) } } -void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, uint8_t subidx, uint8_t nested, const char* SubKey) +void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, bool nested, const char* SubKey) { char stopic[TOPSZ]; char stemp1[TOPSZ]; @@ -608,21 +717,20 @@ void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const NoAlNumToUnderscore(subname, MultiSubName); //Replace all non alphaumeric characters to '_' to avoid topic name issues snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%s"), ESP_getChipId(), sensorname, subname); snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); - if (Settings.flag.hass_discovery) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) char name[TOPSZ]; // friendlyname(33) + " " + sensorname(20?) + " " + sensortype(20?) char prefix[TOPSZ]; char *state_topic = stemp1; char *availability_topic = stemp2; - //bool LwtSensor = MQTT_LWT_DISCOVERY; + masterlog_level = 0; // Show the new generated topic GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_DEVICENAME), sensorname, MultiSubName); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); Response_P(HASS_DISCOVER_BASE, name, state_topic); - #ifdef DEEPSLEEP_LWT_HA_DISCOVERY +#ifdef DEEPSLEEP_LWT_HA_DISCOVERY TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); #else if (Settings.deepsleep == 0) @@ -648,30 +756,19 @@ void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const case 3: snprintf_P(param1, sizeof(param1), PSTR("%s"), PressureUnit().c_str()); break; - // case 4: // Speed. Default to km/h if not set to have a graph representation under HAss - // case 5: - // case 6: - // case 7: - // if (Settings.flag2.speed_conversion == 0) { - // snprintf_P(param1, sizeof(param1), PSTR("km/h")); - // } else { - // snprintf_P(param1, sizeof(param1), PSTR("%s"), SpeedUnit().c_str()); - // } - // break; } char param2[50]; GetTextIndexed(param2, sizeof(param2), sensor_index, kHAssJsonSensorDevCla); TryResponseAppend_P(HASS_DISCOVER_SENSOR, param1, param2, sensorname, subsensortype); - if (subidx) { - TryResponseAppend_P(PSTR("[%d]"), subqty -1); - } } else { TryResponseAppend_P(HASS_DISCOVER_SENSOR, " ", "ic\":\"mdi:eye", sensorname, subsensortype); } - if (nested) { - TryResponseAppend_P(PSTR("['%s']"), SubKey); - } + + if (nested) { TryResponseAppend_P(PSTR("['%s']"), SubKey); } + + if (subqty != 0) { TryResponseAppend_P(PSTR("[%d]"), subqty -1); } + TryResponseAppend_P(PSTR("}}\"}")); } MqttPublish(stopic, true); @@ -687,57 +784,66 @@ void HAssAnnounceSensors(void) tele_period = 2; // Do not allow HA updates during next function call XsnsNextCall(FUNC_JSON_APPEND, hass_xsns_index); // ,"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089} tele_period = tele_period_save; + size_t sensordata_len = strlen(mqtt_data); + char sensordata[sensordata_len+2]; // dynamically adjust the size + strcpy(sensordata, mqtt_data); // we can use strcpy since the buffer has the right size - char sensordata[512]; // Copy because we need to write to mqtt_data - strlcpy(sensordata, mqtt_data, sizeof(sensordata)); + // ******************* JSON TEST ******************* + // char sensordata[512]; + // snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"ENERGY\":{\"TotalStartTime\":\"2018-11-23T15:33:47\",\"ExportTariff\":[0.000,0.017],\"Speed\":{\"Act\":\"NE\"}}}")); + // size_t sensordata_len = strlen(sensordata); + // ******************* JSON TEST ******************* - if (strlen(sensordata)) + if (sensordata_len > 0) { + // // We replace the leader ',' with '{' sensordata[0] = '{'; - 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("{\"APDS9960\":{\"Red\":282,\"Green\":252,\"Blue\":196,\"Ambient\":169,\"CCT\":4217,\"Proximity\":9}}")); - //snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"ENERGY\":{\"TotalStartTime\":\"2018-11-23T15:33:47\",\"Total\":0.017,\"TotalTariff\":[0.000,0.017],\"Yesterday\":0.000,\"Today\":0.002,\"ExportActive\":0.000,\"ExportTariff\":[0.000,0.000],\"Period\":0.00,\"Power\":0.00,\"ApparentPower\":7.84,\"ReactivePower\":-7.21,\"Factor\":0.39,\"Frequency\":50.0,\"Voltage\":234.31,\"Current\":0.039,\"ImportActive\":12.580,\"ImportReactive\":0.002,\"ExportReactive\":39.131,\"PhaseAngle\":290.45}}")); + // // and we add a trailing '}' after the last '}' + sensordata[sensordata_len] = '}'; + sensordata[sensordata_len+1] = '\0'; - StaticJsonBuffer<500> jsonBuffer; - JsonObject &root = jsonBuffer.parseObject(sensordata); - if (!root.success()) + JsonParser parser(sensordata); + JsonParserObject root = parser.getRootObject(); + if (!root) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s '%s'"), kHAssError3, sensordata); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s '%s' (ERR1)"), kHAssError3, sensordata); continue; } - for (auto sensor : root) + for (auto sensor_key : root) { - const char *sensorname = sensor.key; - JsonObject &sensors = sensor.value.as(); - if (!sensors.success()) + // sensor is of type JsonParserKey + const char *sensorname = sensor_key.getStr(); + JsonParserObject sensors = sensor_key.getValue().getObject(); + + if (!sensors) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s '%s'"), kHAssError3, sensordata); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s '%s' (ERR2)"), kHAssError3, sensorname); continue; } - for (auto subsensor : sensors) + for (auto subsensor_key_token : sensors) { - if (subsensor.value.is()) { + const char * subsensor_key = subsensor_key_token.getStr(); + JsonParserToken subsensor = subsensor_key_token.getValue(); + if (subsensor.isObject()) { // If there is a nested json on sensor data, second level entitites will be created - char NestedName[20]; + JsonParserObject subsensors = subsensor.getObject(); char NewSensorName[20]; - snprintf_P(NestedName, sizeof(NestedName), PSTR("%s"), subsensor.key); - JsonObject& subsensors = subsensor.value.as(); - for (auto subsensor : subsensors) { - snprintf_P(NewSensorName, sizeof(NewSensorName), PSTR("%s %s"), NestedName, subsensor.key); - HAssAnnounceSensor(sensorname, NestedName, NewSensorName, 0, 0, 1, subsensor.key); + for (auto subsensor2_key : subsensors) { + snprintf_P(NewSensorName, sizeof(NewSensorName), PSTR("%s %s"), subsensor_key, subsensor2_key.getStr()); + HAssAnnounceSensor(sensorname, subsensor_key, NewSensorName, 0, 1, subsensor2_key.getStr()); } - } else if (subsensor.value.is()) { + } else if (subsensor.isArray()) { // If there is more than a value on sensor data, 'n' entitites will be created - JsonArray& subsensors = subsensor.value.as(); + JsonParserArray subsensors = subsensor.getArray(); uint8_t subqty = subsensors.size(); char MultiSubName[20]; for (int i = 1; i <= subqty; i++) { - snprintf_P(MultiSubName, sizeof(MultiSubName), PSTR("%s %d"), subsensor.key, i); - HAssAnnounceSensor(sensorname, subsensor.key, MultiSubName, i, 1, 0, subsensor.key); + snprintf_P(MultiSubName, sizeof(MultiSubName), PSTR("%s %d"), subsensor_key, i); + HAssAnnounceSensor(sensorname, subsensor_key, MultiSubName, i, 0, subsensor_key); } - } else { HAssAnnounceSensor(sensorname, subsensor.key, subsensor.key, 0, 0, 0, subsensor.key);} + } else { + HAssAnnounceSensor(sensorname, subsensor_key, subsensor_key, 0, 0, subsensor_key);} } } } @@ -752,15 +858,18 @@ void HAssAnnounceShutters(void) char stemp1[TOPSZ]; char stemp2[TOPSZ]; char unique_id[30]; + uint8_t ShowTopic; // Used to hide/unhide a topic during Discovery to spare some cpu load for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { mqtt_data[0] = '\0'; // Clear retained message + masterlog_level = ShowTopic = 4; // Hide topic on clean and remove use weblog 4 to see it + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_SHT_%d"), ESP_getChipId(), i + 1); snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/cover/%s/config"), unique_id); if (Settings.flag.hass_discovery && Settings.flag3.shutter_mode && Settings.shutter_startrelay[i] > 0 && Settings.shutter_startrelay[i] <= MAX_RELAYS) { - masterlog_level = 0; + ShowTopic = 0; // Show the new generated topic if (i > MAX_FRIENDLYNAMES) { snprintf_P(stemp1, sizeof(stemp1), PSTR("%s Shutter %d"), SettingsText(SET_DEVICENAME), i + 1); } else { @@ -783,8 +892,8 @@ void HAssAnnounceShutters(void) TryResponseAppend_P(PSTR("}")); } + masterlog_level = ShowTopic; MqttPublish(stopic, true); - masterlog_level = 4; } #endif } @@ -795,9 +904,11 @@ void HAssAnnounceDeviceInfoAndStatusSensor(void) char stemp1[TOPSZ]; char stemp2[TOPSZ]; char unique_id[30]; + uint8_t ShowTopic; // Used to hide/unhide a topic during Discovery to spare some cpu load + // Announce sensor mqtt_data[0] = '\0'; // Clear retained message - + masterlog_level = ShowTopic = 4; // Hide topic on clean and remove use weblog 4 to see it // Clear or Set topic snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_status"), ESP_getChipId()); snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); @@ -808,7 +919,7 @@ void HAssAnnounceDeviceInfoAndStatusSensor(void) char prefix[TOPSZ]; char *state_topic = stemp1; char *availability_topic = stemp2; - masterlog_level = 0; + ShowTopic = 0; // Show the new generated topic snprintf_P(name, sizeof(name), PSTR("%s status"), SettingsText(SET_DEVICENAME)); GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); @@ -820,10 +931,12 @@ void HAssAnnounceDeviceInfoAndStatusSensor(void) ModuleName().c_str(), my_version, my_image); TryResponseAppend_P(PSTR("}")); } + masterlog_level = ShowTopic; MqttPublish(stopic, true); + if (!Settings.flag.hass_discovery) { masterlog_level = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG "Home Assistant Discovery disabled. ")); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG "Home Assistant MQTT Discovery disabled.")); } } @@ -843,18 +956,18 @@ void HAssDiscovery(void) { // Configure Tasmota for default Home Assistant parameters to keep discovery message as short as possible if (Settings.flag.hass_discovery) - { // SetOption19 - Control Home Assistant automatic discovery (See SetOption59) - Settings.flag.mqtt_response = 0; // SetOption4 - Switch between MQTT RESULT or COMMAND - Response always as RESULT and not as uppercase command - Settings.flag.decimal_text = 1; // SetOption17 - Switch between decimal or hexadecimal output - Respond with decimal color values - Settings.flag3.hass_tele_on_power = 1; // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - send tele/STATE message as stat/RESULT - // the purpose of that is so that if HA is restarted, state in HA will be correct within one teleperiod otherwise state - // will not be correct until the device state is changed this is why in the patterns for switch and light, we tell HA to trigger on STATE, not RESULT. - //Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 (on hold due to new light configuration) + { // SetOption19 - Control Home Assistant automatic discovery (See SetOption59) + Settings.flag.mqtt_response = 0; // SetOption4 - Switch between MQTT RESULT or COMMAND - Response always as RESULT and not as uppercase command + Settings.flag.decimal_text = 1; // SetOption17 - Switch between decimal or hexadecimal output - Respond with decimal color values + Settings.flag3.hass_tele_on_power = 1; // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - send tele/STATE message as stat/RESULT + // the purpose of that is so that if HA is restarted, state in HA will be correct within one teleperiod otherwise state + // will not be correct until the device state is changed this is why in the patterns for switch and light, we tell HA to trigger on STATE, not RESULT. + //Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 (on hold due to new light configuration) } if (Settings.flag.hass_discovery || (1 == hass_mode)) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) - masterlog_level = 4; + hass_mode = 2; // Send info about buttons HAssAnnounceButtons(); @@ -872,8 +985,8 @@ void HAssDiscovery(void) // Send info about status sensor HAssAnnounceDeviceInfoAndStatusSensor(); - - masterlog_level = Settings.weblog_level; + masterlog_level = 0; // Restores weblog level + hass_mode = 3; } } @@ -885,10 +998,7 @@ void HAssDiscover(void) void HAssAnyKey(void) { - if (!Settings.flag.hass_discovery) - { - return; - } // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) + if (!Settings.flag.hass_discovery) { return; } // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) 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 @@ -933,14 +1043,14 @@ bool HAssMqttLWT(void) if (Settings.flag.hass_discovery && (strncasecmp_P(XdrvMailbox.data, PSTR("online"), strlen("online")) == 0) && (XdrvMailbox.data_len == 6)) { MqttPublishTeleState(); return true; - } + } else { return false; } } void HassLwtSubscribe(bool hasslwt) { char htopic[TOPSZ]; snprintf_P(htopic, sizeof(htopic), PSTR(HOME_ASSISTANT_LWT_TOPIC)); - if (hasslwt && Settings.flag.hass_discovery) { + if (hasslwt && (Settings.flag.hass_discovery)) { MqttSubscribe(htopic); } else { MqttUnsubscribe(htopic); } } @@ -983,6 +1093,9 @@ bool Xdrv12(uint8_t function) case FUNC_MQTT_INIT: hass_mode = 0; // Discovery only if Settings.flag.hass_discovery is set hass_init_step = 2; // Delayed discovery + if (!Settings.flag.hass_discovery) { + NewHAssDiscovery(); + } break; case FUNC_MQTT_SUBSCRIBE: diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 7b5b34054..1042cc463 100644 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -1165,50 +1165,45 @@ void DisplayAnalyzeJson(char *topic, char *json) // tele/wemos5/SENSOR {"Time":"2017-09-20T11:53:53","SHT1X":{"Temperature":20.1,"Humidity":58.9},"HTU21":{"Temperature":20.7,"Humidity":58.5},"BMP280":{"Temperature":21.6,"Pressure":1020.3},"TempUnit":"C"} // tele/th1/SENSOR {"Time":"2017-09-20T11:54:48","DS18B20":{"Temperature":49.7},"TempUnit":"C"} - -// char jsonStr[MESSZ]; -// strlcpy(jsonStr, json, sizeof(jsonStr)); // Save original before destruction by JsonObject String jsonStr = json; // Move from stack to heap to fix watchdogs (20180626) - StaticJsonBuffer<1024> jsonBuf; - JsonObject &root = jsonBuf.parseObject(jsonStr); - if (root.success()) { + JsonParser parser((char*)jsonStr.c_str()); + JsonParserObject root = parser.getRootObject(); + if (root) { // did JSON parsing went ok? - const char *unit; - unit = root[D_JSON_TEMPERATURE_UNIT]; + const char *unit = root.getStr(PSTR(D_JSON_TEMPERATURE_UNIT), nullptr); // nullptr if not found if (unit) { snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%s"), unit); // C or F } - unit = root[D_JSON_PRESSURE_UNIT]; + unit = root.getStr(PSTR(D_JSON_PRESSURE_UNIT), nullptr); // nullptr if not found if (unit) { snprintf_P(disp_pres, sizeof(disp_pres), PSTR("%s"), unit); // hPa or mmHg } - - for (JsonObject::iterator it = root.begin(); it != root.end(); ++it) { - JsonVariant value = it->value; - if (value.is()) { - JsonObject& Object2 = value; - for (JsonObject::iterator it2 = Object2.begin(); it2 != Object2.end(); ++it2) { - JsonVariant value2 = it2->value; - if (value2.is()) { - JsonObject& Object3 = value2; - for (JsonObject::iterator it3 = Object3.begin(); it3 != Object3.end(); ++it3) { - const char* value = it3->value; - if (value != nullptr) { // "DHT11":{"Temperature":null,"Humidity":null} - ignore null as it will raise exception 28 - DisplayJsonValue(topic, it->key, it3->key, value); // Sensor 56% + for (auto key1 : root) { + JsonParserToken value1 = key1.getValue(); + if (value1.isObject()) { + JsonParserObject Object2 = value1.getObject(); + for (auto key2 : Object2) { + JsonParserToken value2 = key2.getValue(); + if (value2.isObject()) { + JsonParserObject Object3 = value2.getObject(); + for (auto key3 : Object3) { + const char* value3 = key3.getValue().getStr(nullptr); + if (value3 != nullptr) { // "DHT11":{"Temperature":null,"Humidity":null} - ignore null as it will raise exception 28 + DisplayJsonValue(topic, key1.getStr(), key3.getStr(), value3); // Sensor 56% } } } else { - const char* value = it2->value; + const char* value = value2.getStr(nullptr); if (value != nullptr) { - DisplayJsonValue(topic, it->key, it2->key, value); // Sensor 56% + DisplayJsonValue(topic, key1.getStr(), key2.getStr(), value); // Sensor 56% } } } } else { - const char* value = it->value; + const char* value = value1.getStr(nullptr); if (value != nullptr) { - DisplayJsonValue(topic, it->key, it->key, value); // Topic 56% + DisplayJsonValue(topic, key1.getStr(), key1.getStr(), value); // Topic 56% } } } diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino index b4b9a5ff4..c58dc2130 100644 --- a/tasmota/xdrv_16_tuyamcu.ino +++ b/tasmota/xdrv_16_tuyamcu.ino @@ -1,7 +1,7 @@ /* xdrv_16_tuyamcu.ino - Tuya MCU support for Tasmota - Copyright (C) 2020 digiblur, Joel Stein and Theo Arends + Copyright (C) 2020 Federico Leoni, digiblur, Joel Stein 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 @@ -55,56 +55,32 @@ TasmotaSerial *TuyaSerial = nullptr; struct TUYA { - uint16_t new_dim = 0; // Tuya dimmer value temp - bool ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction - uint8_t cmd_status = 0; // Current status of serial-read - uint8_t cmd_checksum = 0; // Checksum of tuya command - uint8_t data_len = 0; // Data lenght of command + uint16_t Levels[5] = {0,0,0,0,0}; // Array to store the values of TuyaMCU channels + uint16_t Snapshot[5] = {0,0,0,0,0}; // Array to store a snapshot of Tasmota actual values for channels + char HSBColor[13]; // Stores HSB Color string in Hex format + uint16_t CTMin = 153; // Minimum CT level allowed - When SetOption82 is enabled will default to 200 + uint16_t CTMax = 500; // Maximum CT level allowed - When SetOption82 is enabled will default to 380 + bool ModeSet = false; // Controls 0 - Single Tone light, 1 - RGB Light + uint8_t FanState = 0; // Stores the current fan speed + bool SuspendTopic = false; // Used to reduce the load at init time or when polling the configuraton on demand + uint32_t ignore_topic_timeout = 0; // Suppress the /STAT topic (if enabled) to avoid data overflow until the configuration is over + bool ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction + uint8_t cmd_status = 0; // Current status of serial-read + uint8_t cmd_checksum = 0; // Checksum of tuya command + uint8_t data_len = 0; // Data lenght of command uint8_t wifi_state = -2; // Keep MCU wifi-status in sync with WifiState() - uint8_t heartbeat_timer = 0; // 10 second heartbeat timer for tuya module + uint8_t heartbeat_timer = 0; // 10 second heartbeat timer for tuya module #ifdef USE_ENERGY_SENSOR - uint32_t lastPowerCheckTime = 0; // Time when last power was checked + uint32_t lastPowerCheckTime = 0; // Time when last power was checked #endif // USE_ENERGY_SENSOR - char *buffer = nullptr; // Serial receive buffer - int byte_counter = 0; // Index in serial receive buffer - bool low_power_mode = false; // Normal or Low power mode protocol - bool send_success_next_second = false; // Second command success in low power mode - uint32_t ignore_dimmer_cmd_timeout = 0;// Time until which received dimmer commands should be ignored + char *buffer = nullptr; // Serial receive buffer + int byte_counter = 0; // Index in serial receive buffer + bool low_power_mode = false; // Normal or Low power mode protocol + bool send_success_next_second = false; // Second command success in low power mode + uint32_t ignore_dimmer_cmd_timeout = 0; // Time until which received dimmer commands should be ignored + bool ignore_tuyareceived = false; // When a modeset changes ignore stat } Tuya; - -// enum TuyaSupportedFunctions { -// TUYA_MCU_FUNC_NONE, -// TUYA_MCU_FUNC_SWT1 = 1, // Buttons -// TUYA_MCU_FUNC_SWT2, -// TUYA_MCU_FUNC_SWT3, -// TUYA_MCU_FUNC_SWT4, -// TUYA_MCU_FUNC_REL1 = 11, // Relays -// TUYA_MCU_FUNC_REL2, -// TUYA_MCU_FUNC_REL3, -// TUYA_MCU_FUNC_REL4, -// TUYA_MCU_FUNC_REL5, -// TUYA_MCU_FUNC_REL6, -// TUYA_MCU_FUNC_REL7, -// TUYA_MCU_FUNC_REL8, -// TUYA_MCU_FUNC_DIMMER = 21, -// TUYA_MCU_FUNC_POWER = 31, -// TUYA_MCU_FUNC_CURRENT, -// TUYA_MCU_FUNC_VOLTAGE, -// TUYA_MCU_FUNC_BATTERY_STATE, -// TUYA_MCU_FUNC_BATTERY_PERCENTAGE, -// TUYA_MCU_FUNC_REL1_INV = 41, // Inverted Relays -// TUYA_MCU_FUNC_REL2_INV, -// TUYA_MCU_FUNC_REL3_INV, -// TUYA_MCU_FUNC_REL4_INV, -// TUYA_MCU_FUNC_REL5_INV, -// TUYA_MCU_FUNC_REL6_INV, -// TUYA_MCU_FUNC_REL7_INV, -// TUYA_MCU_FUNC_REL8_INV, -// TUYA_MCU_FUNC_LOWPOWER_MODE = 51, -// TUYA_MCU_FUNC_LAST = 255 -// }; - const char kTuyaCommand[] PROGMEM = "|" // No prefix D_CMND_TUYA_MCU "|" D_CMND_TUYA_MCU_SEND_STATE; @@ -112,8 +88,48 @@ void (* const TuyaCommand[])(void) PROGMEM = { &CmndTuyaMcu, &CmndTuyaSend }; -/* +/*********************************************************************************************\ + * Web Interface +\*********************************************************************************************/ +bool IsModuleTuya(void) +{ + return ((TUYA_DIMMER == my_module_type) || (SK03_TUYA == my_module_type)); +} +bool AsModuleTuyaMS(void) // ModeSet Layout +{ + return ((light_type > LT_RGB) && TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0); +} + +bool IsTuyaFanCtrl(void) // Fan Speed Controller Layout +{ + return ((TuyaGetDpId(TUYA_MCU_FUNC_FAN3) != 0) || (TuyaGetDpId(TUYA_MCU_FUNC_FAN4) != 0) || + (TuyaGetDpId(TUYA_MCU_FUNC_FAN5) != 0) || (TuyaGetDpId(TUYA_MCU_FUNC_FAN6) != 0)); +} + +bool TuyaModeSet(void) // ModeSet Status +{ + return Tuya.ModeSet; +} + +uint8_t TuyaFanSpeeds(void) // Number of Fan Speeds for WebUI +{ + uint8_t FanSpeeds = 0; + for (uint32_t i = 0; i <= 3; i++) { + if (TuyaGetDpId(TUYA_MCU_FUNC_FAN3 + i) != 0) { + FanSpeeds = i + 2; + } + } + return FanSpeeds; +} + +uint8_t TuyaFanState(void) // Fan Speed Status +{ + return Tuya.FanState; +} +// Web Interface + +/* TuyaSend dpId,data TuyaSend0 -> Sends TUYA_CMD_QUERY_STATE @@ -122,16 +138,20 @@ TuyaSend2 11,100 -> Sends integer (Type 2) data 100 to dpId 11 (Max data length TuyaSend2 11,0xAABBCCDD -> Sends 4 bytes (Type 2) data to dpId 11 (Max data length 4 bytes) TuyaSend3 11,ThisIsTheData -> Sends the supplied string (Type 3) to dpId 11 ( Max data length not-known) TuyaSend4 11,1 -> Sends enum (Type 4) data 0/1/2/3/4/5 to dpId 11 (Max data length 1 bytes) - */ - void CmndTuyaSend(void) { - if (XdrvMailbox.index > 4) { + if (XdrvMailbox.index > 4 && XdrvMailbox.index < 8) { return; } if (XdrvMailbox.index == 0) { - TuyaRequestState(); + TuyaRequestState(0); + } else if (XdrvMailbox.index == 8) { + TuyaRequestState(8); + } else if (XdrvMailbox.index == 9) { // TuyaSend Topic Toggle + if (Settings.tuyamcu_topic) { Settings.tuyamcu_topic = 0; } else { Settings.tuyamcu_topic = 1; } + AddLog_P2(LOG_LEVEL_INFO, PSTR("TYA: TuyaMCU Stat Topic %s"), (Settings.tuyamcu_topic ? PSTR("enabled") : PSTR("disabled"))); + } else { if (XdrvMailbox.data_len > 0) { char *p; @@ -161,11 +181,7 @@ void CmndTuyaSend(void) { ResponseCmndDone(); } -/* - -TuyaMcu fnid,dpid - -*/ +// TuyaMcu fnid,dpid void CmndTuyaMcu(void) { if (XdrvMailbox.data_len > 0) { @@ -178,12 +194,28 @@ void CmndTuyaMcu(void) { } if (TuyaFuncIdValid(parm[0])) { + // TuyaAddMcuFunc(parm[0], parm[1]); + // restart_flag = 2; + // } else { + // AddLog_P2(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]); + // } + bool DualDim; + if (TUYA_MCU_FUNC_DIMMER2 == parm[0] && parm[1] != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) != 0) { DualDim = true; } + } else if (TUYA_MCU_FUNC_DIMMER == parm[0] && parm[1] != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2) != 0) { DualDim = true; } + } else if ((TUYA_MCU_FUNC_DIMMER == parm[0] && parm[1] == 0) || (TUYA_MCU_FUNC_DIMMER2 == parm[0] && parm[1] == 0)) { DualDim = false; }; + if (DualDim) { + if (TuyaGetDpId(TUYA_MCU_FUNC_CT) != 0) { TuyaAddMcuFunc(TUYA_MCU_FUNC_CT, 0); } // If the second dimmer is enabled CT, RGB or WHITE function must be removed + if (TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) { TuyaAddMcuFunc(TUYA_MCU_FUNC_RGB, 0); } + if (TuyaGetDpId(TUYA_MCU_FUNC_WHITE) != 0) { TuyaAddMcuFunc(TUYA_MCU_FUNC_WHITE, 0); } + Settings.flag3.pwm_multi_channels = 1; + } else { Settings.flag3.pwm_multi_channels = 0; } TuyaAddMcuFunc(parm[0], parm[1]); restart_flag = 2; } else { AddLog_P2(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]); } - } Response_P(PSTR("{\"" D_CMND_TUYA_MCU "\":[")); @@ -249,11 +281,13 @@ void UpdateDevices() { inline bool TuyaFuncIdValid(uint8_t fnId) { return (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) || - (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) || - fnId == TUYA_MCU_FUNC_DIMMER || - (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_VOLTAGE) || - (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) || - (fnId == TUYA_MCU_FUNC_LOWPOWER_MODE); + (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) || + (fnId >= TUYA_MCU_FUNC_DIMMER && fnId <= TUYA_MCU_FUNC_REPORT2) || + (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_BATTERY_PERCENTAGE) || + (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) || + (fnId >= TUYA_MCU_FUNC_FAN3 && fnId <= TUYA_MCU_FUNC_FAN6) || + (fnId >= TUYA_MCU_FUNC_MOTOR_DIR && fnId <= TUYA_MCU_FUNC_DUMMY) || + (fnId == TUYA_MCU_FUNC_LOWPOWER_MODE); } uint8_t TuyaGetFuncId(uint8_t dpid) { @@ -376,38 +410,137 @@ bool TuyaSetPower(void) bool TuyaSetChannels(void) { - LightSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); - //delay(20); // Hack when power is off and dimmer is set then both commands go too soon to Serial out. + uint16_t hue, TuyaData; + uint8_t sat, bri; + uint8_t TuyaIdx = 0; + char hex_char[13]; + bool noupd = false; + bool LightMode = TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0; + uint8_t idx = 0; + snprintf_P(hex_char, sizeof(hex_char), PSTR("000000000000")); + + if (LT_SERIAL1 == light_type) { + Tuya.Snapshot[0] = light_state.getDimmer(); + } + if (LT_SERIAL2 == light_type || LT_RGBWC == light_type) { + idx = 1; + if (LT_SERIAL2 == light_type && Settings.flag3.pwm_multi_channels && (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2) != 0)) { + // Special setup for dual dimmer (like the MOES 2 Way Dimmer) emulating 2 PWM channels + Tuya.Snapshot[0] = changeUIntScale(Light.current_color[0], 0, 255, 0, 100); + Tuya.Snapshot[1] = changeUIntScale(Light.current_color[1], 0, 255, 0, 100); + } else { // CT Light or RGBWC + light_state.getCTRange(&Tuya.CTMin, &Tuya.CTMax); // SetOption82 - Reduce the CT range from 153..500 to 200..380 to accomodate with Alexa range + Tuya.Snapshot[0] = light_state.getDimmer(); + Tuya.Snapshot[1] = light_state.getCT(); + } + } + if (LT_RGBW == light_type) { + idx = 1; + Tuya.Snapshot[0] = light_state.getDimmer(1); + Tuya.Snapshot[1] = light_state.getDimmer(2); + } + + if (light_type > LT_BASIC) { + + if (LT_RGB != light_type) { + for (uint8_t i = 0; i <= idx; i++) { + if (Tuya.Snapshot[i] != Tuya.Levels[i]) { + if (i == 0 && LightMode && Tuya.ModeSet ) { noupd = true;} + if (!noupd) { + LightSerialDuty(Tuya.Snapshot[i], &hex_char[0], i+1); + Tuya.Levels[i] = Tuya.Snapshot[i]; + } + noupd = false; + } + } + } + + if (light_type >= LT_RGB) { + + light_state.getHSB(&hue, &sat, &bri); + sat = changeUIntScale(sat, 0, 255, 0, 100); + bri = changeUIntScale(bri, 0, 255, 0, 100); + if (hue != Tuya.Snapshot[2] || sat != Tuya.Snapshot[3] || bri != Tuya.Snapshot[4]) { + if ((LightMode && Tuya.ModeSet) || LT_RGB == light_type) { + snprintf_P(hex_char, sizeof(hex_char), PSTR("%04X%04X%04X"), hue, sat * 10, bri * 10); // Create a TuyaMCU readable RGB payload + LightSerialDuty(0, &hex_char[0], 3); + memcpy_P(Tuya.HSBColor, hex_char, strlen(hex_char)); + Tuya.Snapshot[2] = hue; + Tuya.Snapshot[3] = sat; + Tuya.Snapshot[4] = bri; + } + } + } + } return true; } -void LightSerialDuty(uint16_t duty) +void LightSerialDuty(uint16_t duty, char *hex_char, uint8_t TuyaIdx) { uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER); - if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) { - duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); - if (duty < Settings.dimmer_hw_min) { duty = Settings.dimmer_hw_min; } // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself - if (Tuya.new_dim != duty) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim value=%d (id=%d)"), duty, dpid); - Tuya.ignore_dimmer_cmd_timeout = millis() + 250; // Ignore serial received dim commands for the next 250ms - TuyaSendValue(dpid, duty); + bool CTLight = false; + + if (TuyaIdx > 0 && TuyaIdx <= 2) { + + if (TuyaIdx == 2) { + if (!Settings.flag3.pwm_multi_channels) { + CTLight = true; + dpid = TuyaGetDpId(TUYA_MCU_FUNC_CT); + } else { dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2); } } - } else if (dpid > 0) { - Tuya.ignore_dim = false; // reset flag - duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value=%d"), duty); // due to 0 or already set - } else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); // + + if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) { + if (TuyaIdx == 2 && CTLight) { + duty = changeUIntScale(duty, Tuya.CTMin, Tuya.CTMax, Settings.dimmer_hw_max, 0); + } else { duty = changeUIntScale(duty, 0, 100, 0, Settings.dimmer_hw_max); } + + if (duty < Settings.dimmer_hw_min) { duty = Settings.dimmer_hw_min; } // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself + Tuya.ignore_dimmer_cmd_timeout = millis() + 250; // Ignore serial received dim commands for the next 250ms + if (Tuya.ModeSet && (TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0) && light_type > LT_RGB) { + TuyaSendEnum(TuyaGetDpId(TUYA_MCU_FUNC_MODESET), 0); + } + TuyaSendValue(dpid, duty); + + } else if (dpid > 0 && TuyaIdx <= 2) { + + Tuya.ignore_dim = false; // reset flag + + if (TuyaIdx == 2 && CTLight) { + duty = changeUIntScale(duty, Tuya.CTMin, Tuya.CTMax, Settings.dimmer_hw_max, 0); + } else { + duty = changeUIntScale(duty, 0, 100, 0, Settings.dimmer_hw_max); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value %d for dpid %d"), duty, dpid); // due to 0 or already set + } else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); // + } + } + + if (TuyaIdx == 3) { + dpid = TuyaGetDpId(TUYA_MCU_FUNC_RGB); + if (!Tuya.ModeSet && (TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0) && light_type > LT_RGB) { + TuyaSendEnum(TuyaGetDpId(TUYA_MCU_FUNC_MODESET), 1); + } + TuyaSendString(dpid, hex_char); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: TX RGB hex %s to dpId %d"), hex_char, dpid); } } -void TuyaRequestState(void) +void TuyaRequestState(uint8_t state_type) { if (TuyaSerial) { // Get current status of MCU AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state")); - - TuyaSendCmd(TUYA_CMD_QUERY_STATE); + Tuya.SuspendTopic = true; + Tuya.ignore_topic_timeout = millis() + 1000; // suppress /STAT topic for 1000ms to limit data + switch (state_type) { + case 0: + TuyaSendCmd(TUYA_CMD_QUERY_STATE); + break; + case 8: + TuyaSendCmd(TUYA_CMD_QUERY_PRODUCT); + break; + } } } @@ -452,25 +585,64 @@ void TuyaProcessStatePacket(void) { SwitchHandler(1); } } - } else if (Tuya.buffer[dpidStart + 1] == 2) { // Data Type 2 bool tuya_energy_enabled = (XNRG_32 == energy_flg); uint16_t packetValue = Tuya.buffer[dpidStart + 6] << 8 | Tuya.buffer[dpidStart + 7]; - if (fnId == TUYA_MCU_FUNC_DIMMER) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Dim State=%d"), packetValue); - Tuya.new_dim = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100); - if (Tuya.ignore_dimmer_cmd_timeout < millis()) { - if ((power || Settings.flag3.tuya_apply_o20) && // SetOption54 - Apply SetOption20 settings to Tuya device - (Tuya.new_dim > 0) && (abs(Tuya.new_dim - Settings.light_dimmer) > 1)) { - Tuya.ignore_dim = true; + uint8_t dimIndex; + if ((fnId == TUYA_MCU_FUNC_FAN3) || (fnId == TUYA_MCU_FUNC_FAN4) || + (fnId == TUYA_MCU_FUNC_FAN5) || (fnId == TUYA_MCU_FUNC_FAN6)) { + Tuya.FanState = packetValue; + } - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER "3 %d"), Tuya.new_dim ); + if ((fnId == TUYA_MCU_FUNC_DIMMER) || (fnId == TUYA_MCU_FUNC_REPORT1)) { + dimIndex = 0; + } + + if (fnId == TUYA_MCU_FUNC_DIMMER2 || fnId == TUYA_MCU_FUNC_REPORT2 || fnId == TUYA_MCU_FUNC_CT) { + dimIndex = 1; + if (Settings.flag3.pwm_multi_channels) { + Tuya.Levels[dimIndex] = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100); + } else { + Tuya.Levels[dimIndex] = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, Tuya.CTMax, Tuya.CTMin); + } + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX value %d from dpId %d "), packetValue, Tuya.buffer[dpidStart]); + + if (Tuya.ignore_dimmer_cmd_timeout < millis()) { + + if ((power || Settings.flag3.tuya_apply_o20) && ((Tuya.Levels[dimIndex] > 0) && (Tuya.Levels[dimIndex] != Tuya.Snapshot[dimIndex]))) { // SetOption54 - Apply SetOption20 settings to Tuya device + + Tuya.ignore_dim = true; + skip_light_fade = true; + + if ((fnId == TUYA_MCU_FUNC_DIMMER) || (fnId == TUYA_MCU_FUNC_REPORT1)) { + if (Settings.flag3.pwm_multi_channels && (abs(Tuya.Levels[0] - changeUIntScale(Light.current_color[0], 0, 255, 0, 100))) > 1) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "1 %d"), Tuya.Levels[0]); + } else { + if ((abs(Tuya.Levels[0] - light_state.getDimmer())) > 1) { snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER "3 %d"), Tuya.Levels[0]); } + } + ExecuteCommand(scmnd, SRC_SWITCH); + } + + if (((fnId == TUYA_MCU_FUNC_DIMMER2) || (fnId == TUYA_MCU_FUNC_REPORT2)) && + Settings.flag3.pwm_multi_channels && (abs(Tuya.Levels[1] - changeUIntScale(Light.current_color[1], 0, 255, 0, 100))) > 1) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "2 %d"), Tuya.Levels[1]); + ExecuteCommand(scmnd, SRC_SWITCH); + } + + if ((fnId == TUYA_MCU_FUNC_CT) && (abs(Tuya.Levels[1] - light_state.getCT())) > 1) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_COLORTEMPERATURE " %d"), Tuya.Levels[1]); + ExecuteCommand(scmnd, SRC_SWITCH); + } + + if ((fnId == TUYA_MCU_FUNC_WHITE) && (abs(Tuya.Levels[1] - light_state.getDimmer(2))) > 1) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WHITE " %d"), Tuya.Levels[1]); ExecuteCommand(scmnd, SRC_SWITCH); } } } - #ifdef USE_ENERGY_SENSOR else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) { Energy.voltage[0] = (float)packetValue / 10; @@ -489,14 +661,40 @@ void TuyaProcessStatePacket(void) { Tuya.lastPowerCheckTime = Rtc.utc_time; } #endif // USE_ENERGY_SENSOR - } - // } else { - // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Unknown FnId=%s for dpId=%s"), fnId, Tuya.buffer[6]); - dpidStart += dpDataLen + 4; + else if (Tuya.buffer[dpidStart + 1] == 3) { // Data Type 3 + const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4]; + if ((TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) && dpDataLen == 12) { //Decode the RGB hex and transmit HSBCOLOR command + + //Tuya.ignore_dim = true; + char RgbData[13]; + snprintf_P(RgbData, sizeof(RgbData), PSTR("%.*s"), dpDataLen, dpData); + char HSB1[5], HSB2[5], HSB3[5]; + snprintf_P(HSB1, sizeof(HSB1), PSTR("%.4s\n"), &RgbData[0]); + snprintf_P(HSB2, sizeof(HSB2), PSTR("%.4s\n"), &RgbData[4]); + snprintf_P(HSB3, sizeof(HSB3), PSTR("%.4s\n"), &RgbData[8]); + + if ((Tuya.Snapshot[2] != ((int)strtol(HSB1, NULL, 16)) || + Tuya.Snapshot[3] != ((int)strtol(HSB2, NULL, 16)) / 10 || Tuya.Snapshot[4] !=((int)strtol(HSB3, NULL, 16)) / 10)) { + + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_HSBCOLOR " %d,%d,%d"), ((int)strtol(HSB1, NULL, 16)), + ((int)strtol(HSB2, NULL, 16)) / 10, ((int)strtol(HSB3, NULL, 16)) / 10); + ExecuteCommand(scmnd, SRC_SWITCH); + + memcpy_P(Tuya.HSBColor, RgbData, strlen(RgbData)); + } + } + } + else if (Tuya.buffer[dpidStart + 1] == 4) { // Data Type 4 + const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4]; + if (fnId == TUYA_MCU_FUNC_MODESET) { // toggle light type + Tuya.ModeSet = dpData[0]; + Tuya.Levels[3] = dpData[0]; + } + } + dpidStart += dpDataLen + 4; } } - void TuyaLowPowerModePacketProcess(void) { switch (Tuya.buffer[3]) { case TUYA_CMD_QUERY_PRODUCT: @@ -579,7 +777,7 @@ void TuyaNormalPowerModePacketProcess(void) restart_flag = 2; } } - TuyaRequestState(); + TuyaRequestState(0); break; default: @@ -615,14 +813,33 @@ bool TuyaModuleSelected(void) } } - if (!relaySet) { + if (!relaySet && TuyaGetDpId(TUYA_MCU_FUNC_DUMMY) == 0) { //by default the first relay is created automatically the dummy let remove it if not needed TuyaAddMcuFunc(TUYA_MCU_FUNC_REL1, 1); devices_present++; SettingsSaveAll(); } + // Possible combinations for Lights: + // 0: NONE = LT_BASIC + // 1: DIMMER = LT_SERIAL1 - Common one channel dimmer + // 2: DIMMER, DIMMER2 = LT_SERIAL2 - Two channels dimmer (special setup used with SetOption68) + // 3: DIMMER, CT = LT_SERIAL2 - Dimmable light and White Color Temperature + // 4: DIMMER, RGB = LT_RGB - RGB Light + // 5: DIMMER, RGB, CT = LT_RGBWC - RGB LIght and White Color Temperature + // 6: DIMMER, RGB, WHITE = LT_RGBW - RGB LIght and White + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) != 0) { - light_type = LT_SERIAL1; + if (TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_CT) != 0) { + light_type = LT_RGBWC; + } else if (TuyaGetDpId(TUYA_MCU_FUNC_WHITE) != 0) { + light_type = LT_RGBW; + } else { light_type = LT_RGB; } + } else if (TuyaGetDpId(TUYA_MCU_FUNC_CT) != 0 || TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) { + light_type = LT_RGBWC; + } else { light_type = LT_SERIAL2; } + } else { light_type = LT_SERIAL1; } } else { light_type = LT_BASIC; } @@ -647,9 +864,11 @@ void TuyaInit(void) if (TuyaSerial->begin(baudrate)) { if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } // Get MCU Configuration - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration at %d baud rate")); - + Tuya.SuspendTopic = true; + Tuya.ignore_topic_timeout = millis() + 1000; // suppress /STAT topic for 1000ms to avoid data overflow + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration at %d bps"), baudrate); TuyaSendCmd(TUYA_CMD_QUERY_PRODUCT); + } } Tuya.heartbeat_timer = 0; // init heartbeat timer when dimmer init is done @@ -687,17 +906,24 @@ void TuyaSerialInput(void) char hex_char[(Tuya.byte_counter * 2) + 2]; uint16_t len = Tuya.buffer[4] << 8 | Tuya.buffer[5]; + Response_P(PSTR("{\"" D_JSON_TUYA_MCU_RECEIVED "\":{\"Data\":\"%s\",\"Cmnd\":%d"), ToHex_P((unsigned char*)Tuya.buffer, Tuya.byte_counter, hex_char, sizeof(hex_char)), Tuya.buffer[3]); + uint16_t DataVal = 0; + uint8_t dpId = 0; + uint8_t dpDataType = 0; + char DataStr[13]; + if (len > 0) { ResponseAppend_P(PSTR(",\"CmndData\":\"%s\""), ToHex_P((unsigned char*)&Tuya.buffer[6], len, hex_char, sizeof(hex_char))); if (TUYA_CMD_STATE == Tuya.buffer[3]) { //55 AA 03 07 00 0D 01 04 00 01 02 02 02 00 04 00 00 00 1A 40 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 uint8_t dpidStart = 6; + //snprintf_P(DataStr, sizeof(DataStr), PSTR("000000000000")); while (dpidStart + 4 < Tuya.byte_counter) { - uint8_t dpId = Tuya.buffer[dpidStart]; - uint8_t dpDataType = Tuya.buffer[dpidStart + 1]; + dpId = Tuya.buffer[dpidStart]; + dpDataType = Tuya.buffer[dpidStart + 1]; uint16_t dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4]; const char *dpHexData = ToHex_P(dpData, dpDataLen, hex_char, sizeof(hex_char)); @@ -706,15 +932,20 @@ void TuyaSerialInput(void) ResponseAppend_P(PSTR(",\"DpType%uId%u\":"), dpDataType, dpId); if (TUYA_TYPE_BOOL == dpDataType && dpDataLen == 1) { ResponseAppend_P(PSTR("%u"), dpData[0]); + DataVal = dpData[0]; } else if (TUYA_TYPE_VALUE == dpDataType && dpDataLen == 4) { uint32_t dpValue = (uint32_t)dpData[0] << 24 | (uint32_t)dpData[1] << 16 | (uint32_t)dpData[2] << 8 | (uint32_t)dpData[3] << 0; ResponseAppend_P(PSTR("%u"), dpValue); + DataVal = dpValue; } else if (TUYA_TYPE_STRING == dpDataType) { ResponseAppend_P(PSTR("\"%.*s\""), dpDataLen, dpData); + snprintf_P(DataStr, sizeof(DataStr), PSTR("%.*s"), dpDataLen, dpData); } else if (TUYA_TYPE_ENUM == dpDataType && dpDataLen == 1) { ResponseAppend_P(PSTR("%u"), dpData[0]); + DataVal = dpData[0]; } else { ResponseAppend_P(PSTR("\"0x%s\""), dpHexData); + snprintf_P(DataStr, sizeof(DataStr), PSTR("%s"), dpHexData); } } @@ -727,7 +958,6 @@ void TuyaSerialInput(void) } } } - ResponseAppend_P(PSTR("}}")); if (Settings.flag3.tuya_serial_mqtt_publish) { // SetOption66 - Enable TuyaMcuReceived messages over Mqtt @@ -737,6 +967,17 @@ void TuyaSerialInput(void) } XdrvRulesProcess(); + if (dpId != 0 && Settings.tuyamcu_topic) { // Publish a /STAT Topic ready to use for any home automation system + if (!Tuya.SuspendTopic) { + char scommand[10]; + snprintf_P(scommand, sizeof(scommand), PSTR("TuyaSend%d"), dpDataType); + + if (dpDataType != 3 && dpDataType != 5) { Response_P(PSTR("%d,%u"), dpId, DataVal); } + else { Response_P(PSTR("%d,%s"), dpId, DataStr); } + MqttPublishPrefixTopic_P(STAT, (PSTR("%s"), scommand)); + } + } + if (!Tuya.low_power_mode) { TuyaNormalPowerModePacketProcess(); } else { @@ -747,7 +988,7 @@ void TuyaSerialInput(void) Tuya.cmd_status = 0; Tuya.cmd_checksum = 0; Tuya.data_len = 0; - } // read additional packets from TUYA + } // read additional packets from TUYA else if (Tuya.byte_counter < TUYA_BUFFER_SIZE -1) { // add char to string if it still fits Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; Tuya.cmd_checksum += serial_in_byte; @@ -821,6 +1062,7 @@ void TuyaSetTime(void) { #endif //USE_TUYA_TIME #ifdef USE_ENERGY_SENSOR + /*********************************************************************************************\ * Energy Interface \*********************************************************************************************/ @@ -872,6 +1114,7 @@ bool Xdrv16(uint8_t function) result = TuyaButtonPressed(); break; case FUNC_EVERY_SECOND: + TuyaSetChannels(); if (TuyaSerial && Tuya.wifi_state != TuyaGetTuyaWifiState()) { TuyaSetWifiLed(); } if (!Tuya.low_power_mode) { Tuya.heartbeat_timer++; @@ -887,10 +1130,11 @@ bool Xdrv16(uint8_t function) } else { TuyaSendLowPowerSuccessIfNeeded(); } + if (Tuya.ignore_topic_timeout < millis()) { Tuya.SuspendTopic = false; } break; - case FUNC_SET_CHANNELS: - result = TuyaSetChannels(); - break; + // case FUNC_SET_CHANNELS: + // result = TuyaSetChannels(); + // break; case FUNC_COMMAND: result = DecodeCommand(kTuyaCommand, TuyaCommand); break; diff --git a/tasmota/xdrv_17_rcswitch.ino b/tasmota/xdrv_17_rcswitch.ino index ac4111e83..af3dc1bba 100644 --- a/tasmota/xdrv_17_rcswitch.ino +++ b/tasmota/xdrv_17_rcswitch.ino @@ -104,18 +104,16 @@ void CmndRfSend(void) int repeat = 10; int pulse = 350; - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - StaticJsonBuffer<150> jsonBuf; // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(5) + 40 = 134 - JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (root.success()) { + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (root) { // RFsend {"data":0x501014,"bits":24,"protocol":1,"repeat":10,"pulse":350} char parm_uc[10]; - data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_DATA))], nullptr, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input - bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_BITS))]; - protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PROTOCOL))]; - repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_REPEAT))]; - pulse = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PULSE))]; + data = root.getUInt(PSTR(D_JSON_RF_DATA), data); + bits = root.getUInt(PSTR(D_JSON_RF_BITS), bits); + protocol = root.getInt(PSTR(D_JSON_RF_PROTOCOL), protocol); + repeat = root.getInt(PSTR(D_JSON_RF_REPEAT), repeat); + pulse = root.getInt(PSTR(D_JSON_RF_PULSE), pulse); } else { // RFsend data, bits, protocol, repeat, pulse char *p; diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index 974ebbb78..7c2e29395 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -569,10 +569,12 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { if (Webserver->args()) { response = "["; - StaticJsonBuffer<300> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args())-1)); - if (hue_json.containsKey("on")) { - on = hue_json["on"]; + JsonParser parser((char*) Webserver->arg((Webserver->args())-1).c_str()); + JsonParserObject root = parser.getRootObject(); + + JsonParserToken hue_on = root[PSTR("on")]; + if (hue_on) { + on = hue_on.getBool(); snprintf_P(buf, buf_size, PSTR("{\"success\":{\"/lights/%d/state/on\":%s}}"), device_id, on ? "true" : "false"); @@ -587,15 +589,6 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { } } else { #endif -/* - switch(on) - { - case false : ExecuteCommandPower(device, POWER_OFF, SRC_HUE); - break; - case true : ExecuteCommandPower(device, POWER_ON, SRC_HUE); - break; - } -*/ ExecuteCommandPower(device, (on) ? POWER_ON : POWER_OFF, SRC_HUE); response += buf; resp = true; @@ -619,8 +612,10 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { } prev_x_str[0] = prev_y_str[0] = 0; // reset xy string - if (hue_json.containsKey("bri")) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. - bri = hue_json["bri"]; + parser.setCurrent(); + JsonParserToken hue_bri = root[PSTR("bri")]; + if (hue_bri) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. + bri = hue_bri.getUInt(); prev_bri = bri; // store command value if (resp) { response += ","; } snprintf_P(buf, buf_size, @@ -634,15 +629,19 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { } resp = true; } + // handle xy before Hue/Sat // If the request contains both XY and HS, we wan't to give priority to HS - if (hue_json.containsKey("xy")) { - float x = hue_json["xy"][0]; - float y = hue_json["xy"][1]; - const String &x_str = hue_json["xy"][0]; - const String &y_str = hue_json["xy"][1]; - x_str.toCharArray(prev_x_str, sizeof(prev_x_str)); - y_str.toCharArray(prev_y_str, sizeof(prev_y_str)); + parser.setCurrent(); + JsonParserToken hue_xy = root[PSTR("xy")]; + if (hue_xy) { + JsonParserArray arr_xy = JsonParserArray(hue_xy); + JsonParserToken tok_x = arr_xy[0]; + JsonParserToken tok_y = arr_xy[1]; + float x = tok_x.getFloat(); + float y = tok_y.getFloat(); + strlcpy(prev_x_str, tok_x.getStr(), sizeof(prev_x_str)); + strlcpy(prev_y_str, tok_y.getStr(), sizeof(prev_y_str)); uint8_t rr,gg,bb; LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); @@ -658,8 +657,11 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { resp = true; change = true; } - if (hue_json.containsKey("hue")) { // The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue. - hue = hue_json["hue"]; + + parser.setCurrent(); + JsonParserToken hue_hue = root[PSTR("hue")]; + if (hue_hue) { // The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue. + hue = hue_hue.getUInt(); prev_hue = hue; if (resp) { response += ","; } snprintf_P(buf, buf_size, @@ -674,8 +676,11 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { } resp = true; } - if (hue_json.containsKey("sat")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). - sat = hue_json["sat"]; + + parser.setCurrent(); + JsonParserToken hue_sat = root[PSTR("sat")]; + if (hue_sat) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). + sat = hue_sat.getUInt(); prev_sat = sat; // store command value if (resp) { response += ","; } snprintf_P(buf, buf_size, @@ -690,8 +695,11 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { } resp = true; } - if (hue_json.containsKey("ct")) { // Color temperature 153 (Cold) to 500 (Warm) - ct = hue_json["ct"]; + + parser.setCurrent(); + JsonParserToken hue_ct = root[PSTR("ct")]; + if (hue_ct) { // Color temperature 153 (Cold) to 500 (Warm) + ct = hue_ct.getUInt(); prev_ct = ct; // store commande value if (resp) { response += ","; } snprintf_P(buf, buf_size, @@ -704,6 +712,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { } resp = true; } + if (change) { #ifdef USE_SHUTTER if (ShutterState(device)) { diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino index c7e3093e2..3ccb54672 100644 --- a/tasmota/xdrv_23_zigbee_1_headers.ino +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -39,42 +39,6 @@ public: void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl); bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok = false); -// get the result as a string (const char*) and nullptr if there is no field or the string is empty -const char * getCaseInsensitiveConstCharNull(const JsonObject &json, const char *needle) { - const JsonVariant &val = GetCaseInsensitive(json, needle); - if (&val) { - const char *val_cs = val.as(); - if (strlen(val_cs)) { - return val_cs; - } - } - return nullptr; -} - -// Get an JSON attribute, with case insensitive key search starting with *needle -JsonVariant &startsWithCaseInsensitive(const JsonObject &json, const char *needle) { - // key can be in PROGMEM - if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { - return *(JsonVariant*)nullptr; - } - - String needle_s((const __FlashStringHelper *)needle); - needle_s.toLowerCase(); - - for (auto kv : json) { - String key_s(kv.key); - key_s.toLowerCase(); - JsonVariant &value = kv.value; - - if (key_s.startsWith(needle_s)) { - return value; - } - } - // if not found - return *(JsonVariant*)nullptr; -} - - uint32_t parseHex(const char **data, size_t max_len = 8) { uint32_t ret = 0; for (uint32_t i = 0; i < max_len; i++) { diff --git a/tasmota/xdrv_23_zigbee_1z_libs.ino b/tasmota/xdrv_23_zigbee_1z_libs.ino index 1f9012152..8b64baa5b 100644 --- a/tasmota/xdrv_23_zigbee_1z_libs.ino +++ b/tasmota/xdrv_23_zigbee_1z_libs.ino @@ -64,6 +64,46 @@ uint16_t Z_GetLastGroup(void) { return gZbLastMessage.groupaddr; } uint16_t Z_GetLastCluster(void) { return gZbLastMessage.cluster; } uint8_t Z_GetLastEndpoint(void) { return gZbLastMessage.endpoint; } +/*********************************************************************************************\ + * + * Class for attribute array of values + * This is a helper function to generate a clean list of unsigned ints + * +\*********************************************************************************************/ + +class Z_json_array { +public: + + Z_json_array(): val("[]") {} // start with empty array + void add(uint32_t uval32) { + // remove trailing ']' + val.remove(val.length()-1); + if (val.length() > 1) { // if not empty, prefix with comma + val += ','; + } + val += uval32; + val += ']'; + } + void addStrRaw(const char * sval) { + // remove trailing ']' + val.remove(val.length()-1); + if (val.length() > 1) { // if not empty, prefix with comma + val += ','; + } + val += sval; + val += ']'; + } + void addStr(const char * sval) { + addStrRaw(EscapeJSONString(sval).c_str()); + } + String &toString(void) { + return val; + } + +private : + String val; +}; + /*********************************************************************************************\ * * Class for single attribute @@ -79,7 +119,10 @@ enum class Za_type : uint8_t { Za_float, // float 32, uses fval // non-nummericals Za_raw, // bytes buffer, uses bval - Za_str // string, uses sval + Za_str, // string, uses sval + // sub_objects + Za_obj, // json sub-object + Za_arr, // array sub-object (string add-only) }; class Z_attribute { @@ -90,16 +133,18 @@ public: struct { uint16_t cluster; uint16_t attr_id; - } id; - char * key; + } id; + char * key; } key; // attribute value union { - uint32_t uval32; - int32_t ival32; - float fval; - SBuffer* bval; - char* sval; + uint32_t uval32; + int32_t ival32; + float fval; + SBuffer* bval; + char* sval; + class Z_attribute_list * objval; + class Z_json_array * arrval; } val; Za_type type; // uint8_t in size, type of attribute, see above bool key_is_str; // is the key a string? @@ -139,389 +184,67 @@ public: } // free any allocated memoruy for values - void freeVal(void) { - switch (type) { - case Za_type::Za_raw: - if (val.bval) { delete val.bval; val.bval = nullptr; } - break; - case Za_type::Za_str: - if (val.sval) { delete[] val.sval; val.sval = nullptr; } - break; - } - } + void freeVal(void); + // free any allocated memoruy for keys - void freeKey(void) { - if (key_is_str && key.key && !key_is_pmem) { delete[] key.key; } - key.key = nullptr; - } + void freeKey(void); // set key name - void setKeyName(const char * _key, bool pmem = false) { - freeKey(); - key_is_str = true; - key_is_pmem = pmem; - if (pmem) { - key.key = (char*) _key; - } else { - setKeyName(_key, nullptr); - } - } + void setKeyName(const char * _key, bool pmem = false); // provide two entries and concat - void setKeyName(const char * _key, const char * _key2) { - freeKey(); - key_is_str = true; - key_is_pmem = false; - if (_key) { - size_t key_len = strlen_P(_key); - if (_key2) { - key_len += strlen_P(_key2); - } - key.key = new char[key_len+1]; - strcpy_P(key.key, _key); - if (_key2) { - strcat_P(key.key, _key2); - } - } - } + void setKeyName(const char * _key, const char * _key2); - void setKeyId(uint16_t cluster, uint16_t attr_id) { - freeKey(); - key_is_str = false; - key.id.cluster = cluster; - key.id.attr_id = attr_id; - } + void setKeyId(uint16_t cluster, uint16_t attr_id); // Setters - void setNone(void) { - freeVal(); // free any previously allocated memory - val.uval32 = 0; - type = Za_type::Za_none; - } - void setUInt(uint32_t _val) { - freeVal(); // free any previously allocated memory - val.uval32 = _val; - type = Za_type::Za_uint; - } - void setBool(bool _val) { - freeVal(); // free any previously allocated memory - val.uval32 = _val; - type = Za_type::Za_bool; - } - void setInt(int32_t _val) { - freeVal(); // free any previously allocated memory - val.ival32 = _val; - type = Za_type::Za_int; - } - void setFloat(float _val) { - freeVal(); // free any previously allocated memory - val.fval = _val; - type = Za_type::Za_float; - } + void setNone(void); + void setUInt(uint32_t _val); + void setBool(bool _val); + void setInt(int32_t _val); + void setFloat(float _val); - void setBuf(const SBuffer &buf, size_t index, size_t len) { - freeVal(); - if (len) { - val.bval = new SBuffer(len); - val.bval->addBuffer(buf.buf(index), len); - } - type = Za_type::Za_raw; - } + void setBuf(const SBuffer &buf, size_t index, size_t len); // set the string value // PMEM argument is allowed // string will be copied, so it can be changed later // nullptr is allowed and considered as empty string // Note: memory is allocated only if string is non-empty - void setStr(const char * _val) { - freeVal(); // free any previously allocated memory - val_str_raw = false; - // val.sval is always nullptr after freeVal() - if (_val) { - size_t len = strlen_P(_val); - if (len) { - val.sval = new char[len+1]; - strcpy_P(val.sval, _val); - } - } - type = Za_type::Za_str; - } + void setStr(const char * _val); inline void setStrRaw(const char * _val) { setStr(_val); val_str_raw = true; } + Z_attribute_list & newAttrList(void); + Z_json_array & newJsonArray(void); + inline bool isNum(void) const { return (type >= Za_type::Za_bool) && (type <= Za_type::Za_float); } inline bool isNone(void) const { return (type == Za_type::Za_none);} // get num values - float getFloat(void) const { - switch (type) { - case Za_type::Za_bool: - case Za_type::Za_uint: return (float) val.uval32; - case Za_type::Za_int: return (float) val.ival32; - case Za_type::Za_float: return val.fval; - default: return 0.0f; - } - } - - int32_t getInt(void) const { - switch (type) { - case Za_type::Za_bool: - case Za_type::Za_uint: return (int32_t) val.uval32; - case Za_type::Za_int: return val.ival32; - case Za_type::Za_float: return (int32_t) val.fval; - default: return 0; - } - } - - uint32_t getUInt(void) const { - switch (type) { - case Za_type::Za_bool: - case Za_type::Za_uint: return val.uval32; - case Za_type::Za_int: return (uint32_t) val.ival32; - case Za_type::Za_float: return (uint32_t) val.fval; - default: return 0; - } - } - - bool getBool(void) const { - switch (type) { - case Za_type::Za_bool: - case Za_type::Za_uint: return val.uval32 ? true : false; - case Za_type::Za_int: return val.ival32 ? true : false; - case Za_type::Za_float: return val.fval ? true : false; - default: return false; - } - } - - const SBuffer * getRaw(void) const { - if (Za_type::Za_raw == type) { return val.bval; } - return nullptr; - } + float getFloat(void) const; + int32_t getInt(void) const; + uint32_t getUInt(void) const; + bool getBool(void) const; + const SBuffer * getRaw(void) const; // always return a point to a string, if not defined then empty string. // Never returns nullptr - const char * getStr(void) const { - if (Za_type::Za_str == type) { return val.sval; } - return ""; - } + const char * getStr(void) const; - bool equalsKey(const Z_attribute & attr2, bool ignore_key_suffix = false) const { - // check if keys are equal - if (key_is_str != attr2.key_is_str) { return false; } - if (key_is_str) { - if (strcmp_PP(key.key, attr2.key.key)) { return false; } - } else { - if ((key.id.cluster != attr2.key.id.cluster) || - (key.id.attr_id != attr2.key.id.attr_id)) { return false; } - } - if (!ignore_key_suffix) { - if (key_suffix != attr2.key_suffix) { return false; } - } - return true; - } + bool equalsKey(const Z_attribute & attr2, bool ignore_key_suffix = false) const; + bool equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const; + bool equalsKey(const char * name, uint8_t suffix = 0) const; + bool equalsVal(const Z_attribute & attr2) const; + bool equals(const Z_attribute & attr2) const; - bool equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const { - if (!key_is_str) { - if ((key.id.cluster == cluster) && (key.id.attr_id == attr_id)) { - if (suffix) { - if (key_suffix == suffix) { return true; } - } else { - return true; - } - } - } - return false; - } - - bool equalsKey(const char * name, uint8_t suffix = 0) const { - if (key_is_str) { - if (0 == strcmp_PP(key.key, name)) { - if (suffix) { - if (key_suffix == suffix) { return true; } - } else { - return true; - } - } - } - return false; - } - - bool equalsVal(const Z_attribute & attr2) const { - if (type != attr2.type) { return false; } - if ((type >= Za_type::Za_bool) && (type <= Za_type::Za_float)) { - // numerical value - if (val.uval32 != attr2.val.uval32) { return false; } - } else if (type == Za_type::Za_raw) { - // compare 2 Static buffers - return equalsSBuffer(val.bval, attr2.val.bval); - } else if (type == Za_type::Za_str) { - // if (val_str_raw != attr2.val_str_raw) { return false; } - if (strcmp_PP(val.sval, attr2.val.sval)) { return false; } - } - return true; - } - - bool equals(const Z_attribute & attr2) const { - return equalsKey(attr2) && equalsVal(attr2); - } - - String toString(bool prefix_comma = false) const { - String res(""); - if (prefix_comma) { res += ','; } - res += '"'; - // compute the attribute name - if (key_is_str) { - if (key.key) { res += EscapeJSONString(key.key); } - else { res += F("null"); } // shouldn't happen - if (key_suffix > 1) { - res += key_suffix; - } - } else { - char attr_name[12]; - snprintf_P(attr_name, sizeof(attr_name), PSTR("%04X/%04X"), key.id.cluster, key.id.attr_id); - res += attr_name; - if (key_suffix > 1) { - res += '+'; - res += key_suffix; - } - } - res += F("\":"); - // value part - switch (type) { - case Za_type::Za_none: - res += "null"; - break; - case Za_type::Za_bool: - res += val.uval32 ? F("true") : F("false"); - break; - case Za_type::Za_uint: - res += val.uval32; - break; - case Za_type::Za_int: - res += val.ival32; - break; - case Za_type::Za_float: - { - String fstr(val.fval, 2); - size_t last = fstr.length() - 1; - // remove trailing zeros - while (fstr[last] == '0') { - fstr.remove(last--); - } - // remove trailing dot - if (fstr[last] == '.') { - fstr.remove(last); - } - res += fstr; - } - break; - case Za_type::Za_raw: - res += '"'; - if (val.bval) { - size_t blen = val.bval->len(); - // print as HEX - char hex[2*blen+1]; - ToHex_P(val.bval->getBuffer(), blen, hex, sizeof(hex)); - res += hex; - } - res += '"'; - break; - case Za_type::Za_str: - if (val_str_raw) { - if (val.sval) { res += val.sval; } - } else { - res += '"'; - if (val.sval) { - res += EscapeJSONString(val.sval); // escape JSON chars - } - res += '"'; - } - break; - } - - return res; - } + String toString(bool prefix_comma = false) const; // copy value from one attribute to another, without changing its type - void copyVal(const Z_attribute & rhs) { - freeVal(); - // copy value - val.uval32 = 0x00000000; - type = rhs.type; - if (rhs.isNum()) { - val.uval32 = rhs.val.uval32; - } else if (rhs.type == Za_type::Za_raw) { - if (rhs.val.bval) { - val.bval = new SBuffer(rhs.val.bval->len()); - val.bval->addBuffer(*(rhs.val.bval)); - } - } else if (rhs.type == Za_type::Za_str) { - if (rhs.val.sval) { - size_t s_len = strlen_P(rhs.val.sval); - val.sval = new char[s_len+1]; - strcpy_P(val.sval, rhs.val.sval); - } - } - val_str_raw = rhs.val_str_raw; - } + void copyVal(const Z_attribute & rhs); protected: - void deepCopy(const Z_attribute & rhs) { - // copy key - if (!rhs.key_is_str) { - key.id.cluster = rhs.key.id.cluster; - key.id.attr_id = rhs.key.id.attr_id; - } else { - if (rhs.key_is_pmem) { - key.key = rhs.key.key; // PMEM, don't copy - } else { - key.key = nullptr; - if (rhs.key.key) { - size_t key_len = strlen_P(rhs.key.key); - if (key_len) { - key.key = new char[key_len+1]; - strcpy_P(key.key, rhs.key.key); - } - } - } - } - key_is_str = rhs.key_is_str; - key_is_pmem = rhs.key_is_pmem; - key_suffix = rhs.key_suffix; - attr_type = rhs.attr_type; - attr_multiplier = rhs.attr_multiplier; - // copy value - copyVal(rhs); - // don't touch next pointer - } -}; - -/*********************************************************************************************\ - * - * Class for attribute array of values - * This is a helper function to generate a clean list of unsigned ints - * -\*********************************************************************************************/ - -class Z_json_array { -public: - - Z_json_array(): val("[]") {} // start with empty array - void add(uint32_t uval32) { - // remove trailing ']' - val.remove(val.length()-1); - if (val.length() > 1) { // if not empty, prefix with comma - val += ','; - } - val += uval32; - val += ']'; - } - String &toString(void) { - return val; - } - -private : - String val; + void deepCopy(const Z_attribute & rhs); }; /*********************************************************************************************\ @@ -613,6 +336,406 @@ public: bool mergeList(const Z_attribute_list &list2); }; +/*********************************************************************************************\ + * + * Implementation for Z_attribute + * +\*********************************************************************************************/ + +// free any allocated memoruy for keys +void Z_attribute::freeKey(void) { + if (key_is_str && key.key && !key_is_pmem) { delete[] key.key; } + key.key = nullptr; +} + +// set key name +void Z_attribute::setKeyName(const char * _key, bool pmem) { + freeKey(); + key_is_str = true; + key_is_pmem = pmem; + if (pmem) { + key.key = (char*) _key; + } else { + setKeyName(_key, nullptr); + } +} +// provide two entries and concat +void Z_attribute::setKeyName(const char * _key, const char * _key2) { + freeKey(); + key_is_str = true; + key_is_pmem = false; + if (_key) { + size_t key_len = strlen_P(_key); + if (_key2) { + key_len += strlen_P(_key2); + } + key.key = new char[key_len+1]; + strcpy_P(key.key, _key); + if (_key2) { + strcat_P(key.key, _key2); + } + } +} + +void Z_attribute::setKeyId(uint16_t cluster, uint16_t attr_id) { + freeKey(); + key_is_str = false; + key.id.cluster = cluster; + key.id.attr_id = attr_id; +} + +// Setters +void Z_attribute::setNone(void) { + freeVal(); // free any previously allocated memory + val.uval32 = 0; + type = Za_type::Za_none; +} +void Z_attribute::setUInt(uint32_t _val) { + freeVal(); // free any previously allocated memory + val.uval32 = _val; + type = Za_type::Za_uint; +} +void Z_attribute::setBool(bool _val) { + freeVal(); // free any previously allocated memory + val.uval32 = _val; + type = Za_type::Za_bool; +} +void Z_attribute::setInt(int32_t _val) { + freeVal(); // free any previously allocated memory + val.ival32 = _val; + type = Za_type::Za_int; +} +void Z_attribute::setFloat(float _val) { + freeVal(); // free any previously allocated memory + val.fval = _val; + type = Za_type::Za_float; +} + +void Z_attribute::setBuf(const SBuffer &buf, size_t index, size_t len) { + freeVal(); + if (len) { + val.bval = new SBuffer(len); + val.bval->addBuffer(buf.buf(index), len); + } + type = Za_type::Za_raw; +} + +// set the string value +// PMEM argument is allowed +// string will be copied, so it can be changed later +// nullptr is allowed and considered as empty string +// Note: memory is allocated only if string is non-empty +void Z_attribute::setStr(const char * _val) { + freeVal(); // free any previously allocated memory + val_str_raw = false; + // val.sval is always nullptr after freeVal() + if (_val) { + size_t len = strlen_P(_val); + if (len) { + val.sval = new char[len+1]; + strcpy_P(val.sval, _val); + } + } + type = Za_type::Za_str; +} + +Z_attribute_list & Z_attribute::newAttrList(void) { + freeVal(); + val.objval = new Z_attribute_list(); + type = Za_type::Za_obj; + return *val.objval; +} + +Z_json_array & Z_attribute::newJsonArray(void) { + freeVal(); + val.arrval = new Z_json_array(); + type = Za_type::Za_arr; + return *val.arrval; +} + +// get num values +float Z_attribute::getFloat(void) const { + switch (type) { + case Za_type::Za_bool: + case Za_type::Za_uint: return (float) val.uval32; + case Za_type::Za_int: return (float) val.ival32; + case Za_type::Za_float: return val.fval; + default: return 0.0f; + } +} + +int32_t Z_attribute::getInt(void) const { + switch (type) { + case Za_type::Za_bool: + case Za_type::Za_uint: return (int32_t) val.uval32; + case Za_type::Za_int: return val.ival32; + case Za_type::Za_float: return (int32_t) val.fval; + default: return 0; + } +} + +uint32_t Z_attribute::getUInt(void) const { + switch (type) { + case Za_type::Za_bool: + case Za_type::Za_uint: return val.uval32; + case Za_type::Za_int: return (uint32_t) val.ival32; + case Za_type::Za_float: return (uint32_t) val.fval; + default: return 0; + } +} + +bool Z_attribute::getBool(void) const { + switch (type) { + case Za_type::Za_bool: + case Za_type::Za_uint: return val.uval32 ? true : false; + case Za_type::Za_int: return val.ival32 ? true : false; + case Za_type::Za_float: return val.fval ? true : false; + default: return false; + } +} + +const SBuffer * Z_attribute::getRaw(void) const { + if (Za_type::Za_raw == type) { return val.bval; } + return nullptr; +} + +// always return a point to a string, if not defined then empty string. +// Never returns nullptr +const char * Z_attribute::getStr(void) const { + if (Za_type::Za_str == type) { return val.sval; } + return ""; +} + +bool Z_attribute::equalsKey(const Z_attribute & attr2, bool ignore_key_suffix) const { + // check if keys are equal + if (key_is_str != attr2.key_is_str) { return false; } + if (key_is_str) { + if (strcmp_PP(key.key, attr2.key.key)) { return false; } + } else { + if ((key.id.cluster != attr2.key.id.cluster) || + (key.id.attr_id != attr2.key.id.attr_id)) { return false; } + } + if (!ignore_key_suffix) { + if (key_suffix != attr2.key_suffix) { return false; } + } + return true; +} + +bool Z_attribute::equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix) const { + if (!key_is_str) { + if ((key.id.cluster == cluster) && (key.id.attr_id == attr_id)) { + if (suffix) { + if (key_suffix == suffix) { return true; } + } else { + return true; + } + } + } + return false; +} + +bool Z_attribute::equalsKey(const char * name, uint8_t suffix) const { + if (key_is_str) { + if (0 == strcmp_PP(key.key, name)) { + if (suffix) { + if (key_suffix == suffix) { return true; } + } else { + return true; + } + } + } + return false; +} + +bool Z_attribute::equalsVal(const Z_attribute & attr2) const { + if (type != attr2.type) { return false; } + if ((type >= Za_type::Za_bool) && (type <= Za_type::Za_float)) { + // numerical value + if (val.uval32 != attr2.val.uval32) { return false; } + } else if (type == Za_type::Za_raw) { + // compare 2 Static buffers + return equalsSBuffer(val.bval, attr2.val.bval); + } else if (type == Za_type::Za_str) { + // if (val_str_raw != attr2.val_str_raw) { return false; } + if (strcmp_PP(val.sval, attr2.val.sval)) { return false; } + } else if (type == Za_type::Za_obj) { + return false; // TODO for now we'll assume sub-objects are always different + } else if (type == Za_type::Za_arr) { + return false; // TODO for now we'll assume sub-objects are always different + } + return true; +} + +bool Z_attribute::equals(const Z_attribute & attr2) const { + return equalsKey(attr2) && equalsVal(attr2); +} + +String Z_attribute::toString(bool prefix_comma) const { + String res(""); + if (prefix_comma) { res += ','; } + res += '"'; + // compute the attribute name + if (key_is_str) { + if (key.key) { res += EscapeJSONString(key.key); } + else { res += F("null"); } // shouldn't happen + if (key_suffix > 1) { + res += key_suffix; + } + } else { + char attr_name[12]; + snprintf_P(attr_name, sizeof(attr_name), PSTR("%04X/%04X"), key.id.cluster, key.id.attr_id); + res += attr_name; + if (key_suffix > 1) { + res += '+'; + res += key_suffix; + } + } + res += F("\":"); + // value part + switch (type) { + case Za_type::Za_none: + res += "null"; + break; + case Za_type::Za_bool: + res += val.uval32 ? F("true") : F("false"); + break; + case Za_type::Za_uint: + res += val.uval32; + break; + case Za_type::Za_int: + res += val.ival32; + break; + case Za_type::Za_float: + { + String fstr(val.fval, 2); + size_t last = fstr.length() - 1; + // remove trailing zeros + while (fstr[last] == '0') { + fstr.remove(last--); + } + // remove trailing dot + if (fstr[last] == '.') { + fstr.remove(last); + } + res += fstr; + } + break; + case Za_type::Za_raw: + res += '"'; + if (val.bval) { + size_t blen = val.bval->len(); + // print as HEX + char hex[2*blen+1]; + ToHex_P(val.bval->getBuffer(), blen, hex, sizeof(hex)); + res += hex; + } + res += '"'; + break; + case Za_type::Za_str: + if (val_str_raw) { + if (val.sval) { res += val.sval; } + } else { + res += '"'; + if (val.sval) { + res += EscapeJSONString(val.sval); // escape JSON chars + } + res += '"'; + } + break; + case Za_type::Za_obj: + res += '{'; + if (val.objval) { + res += val.objval->toString(); + } + res += '}'; + break; + case Za_type::Za_arr: + if (val.arrval) { + res += val.arrval->toString(); + } else { + res += "[]"; + } + break; + } + + return res; +} + +// copy value from one attribute to another, without changing its type +void Z_attribute::copyVal(const Z_attribute & rhs) { + freeVal(); + // copy value + val.uval32 = 0x00000000; + type = rhs.type; + if (rhs.isNum()) { + val.uval32 = rhs.val.uval32; + } else if (rhs.type == Za_type::Za_raw) { + if (rhs.val.bval) { + val.bval = new SBuffer(rhs.val.bval->len()); + val.bval->addBuffer(*(rhs.val.bval)); + } + } else if (rhs.type == Za_type::Za_str) { + if (rhs.val.sval) { + size_t s_len = strlen_P(rhs.val.sval); + val.sval = new char[s_len+1]; + strcpy_P(val.sval, rhs.val.sval); + } + } + val_str_raw = rhs.val_str_raw; +} + +// free any allocated memoruy for values +void Z_attribute::freeVal(void) { + switch (type) { + case Za_type::Za_raw: + if (val.bval) { delete val.bval; val.bval = nullptr; } + break; + case Za_type::Za_str: + if (val.sval) { delete[] val.sval; val.sval = nullptr; } + break; + case Za_type::Za_obj: + if (val.objval) { delete val.objval; val.objval = nullptr; } + break; + case Za_type::Za_arr: + if (val.arrval) { delete val.arrval; val.arrval = nullptr; } + break; + } +} + +void Z_attribute::deepCopy(const Z_attribute & rhs) { + // copy key + if (!rhs.key_is_str) { + key.id.cluster = rhs.key.id.cluster; + key.id.attr_id = rhs.key.id.attr_id; + } else { + if (rhs.key_is_pmem) { + key.key = rhs.key.key; // PMEM, don't copy + } else { + key.key = nullptr; + if (rhs.key.key) { + size_t key_len = strlen_P(rhs.key.key); + if (key_len) { + key.key = new char[key_len+1]; + strcpy_P(key.key, rhs.key.key); + } + } + } + } + key_is_str = rhs.key_is_str; + key_is_pmem = rhs.key_is_pmem; + key_suffix = rhs.key_suffix; + attr_type = rhs.attr_type; + attr_multiplier = rhs.attr_multiplier; + // copy value + copyVal(rhs); + // don't touch next pointer +} + +/*********************************************************************************************\ + * + * Implementation for Z_attribute_list + * +\*********************************************************************************************/ // add a cluster/attr_id attribute at the end of the list Z_attribute & Z_attribute_list::addAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix) { Z_attribute & attr = addToLast(); diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index b84db3e2e..506962f3a 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -72,9 +72,13 @@ public: int16_t temperature; // temperature in 1/10th of Celsius, 0x8000 if unknown uint16_t pressure; // air pressure in hPa, 0xFFFF if unknown uint8_t humidity; // humidity in percent, 0..100, 0xFF if unknown - // powe plug data + // power plug data uint16_t mains_voltage; // AC voltage int16_t mains_power; // Active power + uint32_t last_seen; // Last seen time (epoch) + // thermostat + int16_t temperature_target; // settings for the temparature + uint8_t th_setpoint; // percentage of heat/cool in percent // Constructor with all defaults Z_Device(uint16_t _shortaddr = BAD_SHORTADDR, uint64_t _longaddr = 0x00): @@ -103,7 +107,10 @@ public: pressure(0xFFFF), humidity(0xFF), mains_voltage(0xFFFF), - mains_power(-0x8000) + mains_power(-0x8000), + last_seen(0), + temperature_target(-0x8000), + th_setpoint(0xFF) { }; inline bool valid(void) const { return BAD_SHORTADDR != shortaddr; } // is the device known, valid and found? @@ -128,6 +135,10 @@ public: inline bool validTemperature(void) const { return -0x8000 != temperature; } inline bool validPressure(void) const { return 0xFFFF != pressure; } inline bool validHumidity(void) const { return 0xFF != humidity; } + inline bool validLastSeen(void) const { return 0x0 != last_seen; } + + inline bool validTemperatureTarget(void) const { return -0x8000 != temperature_target; } + inline bool validThSetpoint(void) const { return 0xFF != th_setpoint; } inline bool validMainsVoltage(void) const { return 0xFFFF != mains_voltage; } inline bool validMainsPower(void) const { return -0x8000 != mains_power; } @@ -160,6 +171,7 @@ typedef enum Z_Def_Category { Z_CAT_READ_ATTR, // Attribute reporting, either READ_ATTRIBUTE or REPORT_ATTRIBUTE, we coalesce all attributes reported if we can Z_CAT_VIRTUAL_OCCUPANCY, // Creation of a virtual attribute, typically after a time-out. Ex: Aqara presence sensor Z_CAT_REACHABILITY, // timer set to measure reachability of device, i.e. if we don't get an answer after 1s, it is marked as unreachable (for Alexa) + Z_CAT_PERMIT_JOIN, // timer to signal the end of the PermitJoin period // Below will clear based on device + cluster pair. Z_CLEAR_DEVICE_CLUSTER, Z_CAT_READ_CLUSTER, @@ -247,6 +259,7 @@ public: void setReachable(uint16_t shortaddr, bool reachable); void setLQI(uint16_t shortaddr, uint8_t lqi); + void setLastSeenNow(uint16_t shortaddr); // uint8_t getLQI(uint16_t shortaddr) const; void setBatteryPercent(uint16_t shortaddr, uint8_t bp); uint8_t getBatteryPercent(uint16_t shortaddr) const; @@ -257,7 +270,7 @@ public: // Dump json String dumpLightState(uint16_t shortaddr) const; String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const; - int32_t deviceRestore(const JsonObject &json); + int32_t deviceRestore(JsonParserObject json); // General Zigbee device profile support void setZbProfile(uint16_t shortaddr, uint8_t zb_profile); @@ -630,6 +643,17 @@ void Z_Devices::setLQI(uint16_t shortaddr, uint8_t lqi) { getShortAddr(shortaddr).lqi = lqi; } +void Z_Devices::setLastSeenNow(uint16_t shortaddr) { + if (shortaddr == localShortAddr) { return; } + // Only update time if after 2020-01-01 0000. + // Fixes issue where zigbee device pings before WiFi/NTP has set utc_time + // to the correct time, and "last seen" calculations are based on the + // pre-corrected last_seen time and the since-corrected utc_time. + if (Rtc.utc_time < 1577836800) { return; } + getShortAddr(shortaddr).last_seen = Rtc.utc_time; +} + + void Z_Devices::setBatteryPercent(uint16_t shortaddr, uint8_t bp) { getShortAddr(shortaddr).batterypercent = bp; } @@ -886,9 +910,20 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) { attr_list.reset(); // clear the attributes if (Settings.flag4.zigbee_distinct_topics) { - char subtopic[16]; - snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr); - MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain); + if (Settings.flag4.zb_topic_fname && fname) { + //Clean special characters and check size of friendly name + char stemp[TOPSZ]; + strlcpy(stemp, (!strlen(fname)) ? MQTT_TOPIC : fname, sizeof(stemp)); + MakeValidMqtt(0, stemp); + //Create topic with Prefix3 and cleaned up friendly name + char frtopic[TOPSZ]; + snprintf_P(frtopic, sizeof(frtopic), PSTR("%s/%s/" D_RSLT_SENSOR), SettingsText(SET_MQTTPREFIX3), stemp); + MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain); + } else { + char subtopic[16]; + snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr); + MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain); + } } else { MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); } @@ -915,7 +950,7 @@ void Z_Devices::clean(void) { // - a number 0..99, the index number in ZigbeeStatus // - a friendly name, between quotes, example: "Room_Temp" uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const { - if (nullptr == param) { return 0; } + if (nullptr == param) { return BAD_SHORTADDR; } size_t param_len = strlen(param); char dataBuf[param_len + 1]; strcpy(dataBuf, param); @@ -992,12 +1027,9 @@ String Z_Devices::dumpLightState(uint16_t shortaddr) const { // Dump the internal memory of Zigbee devices // Mode = 1: simple dump of devices addresses -// Mode = 2: simple dump of devices addresses and names -// Mode = 3: Mode 2 + also dump the endpoints, profiles and clusters +// Mode = 2: simple dump of devices addresses and names, endpoints, light String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { - DynamicJsonBuffer jsonBuffer; - JsonArray& json = jsonBuffer.createArray(); - JsonArray& devices = json; + Z_json_array json_arr; for (const auto & device : _devices) { uint16_t shortaddr = device.shortaddr; @@ -1006,44 +1038,41 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { // ignore non-current device, if device specified if ((BAD_SHORTADDR != status_shortaddr) && (status_shortaddr != shortaddr)) { continue; } - JsonObject& dev = devices.createNestedObject(); + Z_attribute_list attr_list; snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); - dev[F(D_JSON_ZIGBEE_DEVICE)] = hex; + attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex); if (device.friendlyName > 0) { - dev[F(D_JSON_ZIGBEE_NAME)] = (char*) device.friendlyName; + attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(device.friendlyName); } if (2 <= dump_mode) { hex[0] = '0'; // prefix with '0x' hex[1] = 'x'; Uint64toHex(device.longaddr, &hex[2], 64); - dev[F("IEEEAddr")] = hex; + attr_list.addAttribute(F("IEEEAddr")).setStr(hex); if (device.modelId) { - dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId; + attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(device.modelId); } int8_t bulbtype = getHueBulbtype(shortaddr); if (bulbtype >= 0) { - dev[F(D_JSON_ZIGBEE_LIGHT)] = bulbtype; // sign extend, 0xFF changed as -1 + attr_list.addAttribute(F(D_JSON_ZIGBEE_LIGHT)).setInt(bulbtype); // sign extend, 0xFF changed as -1 } if (device.manufacturerId) { - dev[F("Manufacturer")] = device.manufacturerId; + attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId); } - JsonArray& dev_endpoints = dev.createNestedArray(F("Endpoints")); + Z_json_array arr_ep; for (uint32_t i = 0; i < endpoints_max; i++) { uint8_t endpoint = device.endpoints[i]; if (0x00 == endpoint) { break; } - - snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); - dev_endpoints.add(hex); + arr_ep.add(endpoint); } + attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str()); } + json_arr.addStrRaw(attr_list.toString(true).c_str()); } - String payload = ""; - payload.reserve(200); - json.printTo(payload); - return payload; + return json_arr.toString(); } // Restore a single device configuration based on json export @@ -1055,7 +1084,7 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { // <0 : Error // // Ex: {"Device":"0x5ADF","Name":"IKEA_Light","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]} -int32_t Z_Devices::deviceRestore(const JsonObject &json) { +int32_t Z_Devices::deviceRestore(JsonParserObject json) { // params uint16_t device = 0x0000; // 0x0000 is coordinator so considered invalid @@ -1063,56 +1092,38 @@ int32_t Z_Devices::deviceRestore(const JsonObject &json) { const char * modelid = nullptr; const char * manufid = nullptr; const char * friendlyname = nullptr; - int8_t bulbtype = 0xFF; + int8_t bulbtype = -1; size_t endpoints_len = 0; // read mandatory "Device" - const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device")); - if (nullptr != &val_device) { - device = strToUInt(val_device); + JsonParserToken val_device = json[PSTR("Device")]; + if (val_device) { + device = (uint32_t) val_device.getUInt(device); } else { return -1; // missing "Device" attribute } - // read "IEEEAddr" 64 bits in format "0x0000000000000000" - const JsonVariant &val_ieeeaddr = GetCaseInsensitive(json, PSTR("IEEEAddr")); - if (nullptr != &val_ieeeaddr) { - ieeeaddr = strtoull(val_ieeeaddr.as(), nullptr, 0); - } - - // read "Name" - friendlyname = getCaseInsensitiveConstCharNull(json, PSTR("Name")); - - // read "ModelId" - modelid = getCaseInsensitiveConstCharNull(json, PSTR("ModelId")); - - // read "Manufacturer" - manufid = getCaseInsensitiveConstCharNull(json, PSTR("Manufacturer")); - - // read "Light" - const JsonVariant &val_bulbtype = GetCaseInsensitive(json, PSTR(D_JSON_ZIGBEE_LIGHT)); - if (nullptr != &val_bulbtype) { bulbtype = strToUInt(val_bulbtype);; } + ieeeaddr = json.getULong(PSTR("IEEEAddr"), ieeeaddr); // read "IEEEAddr" 64 bits in format "0x0000000000000000" + friendlyname = json.getStr(PSTR("Name"), nullptr); // read "Name" + modelid = json.getStr(PSTR("ModelId"), nullptr); + manufid = json.getStr(PSTR("Manufacturer"), nullptr); + JsonParserToken tok_bulbtype = json[PSTR(D_JSON_ZIGBEE_LIGHT)]; // update internal device information updateDevice(device, ieeeaddr); if (modelid) { setModelId(device, modelid); } if (manufid) { setManufId(device, manufid); } if (friendlyname) { setFriendlyName(device, friendlyname); } - if (&val_bulbtype) { setHueBulbtype(device, bulbtype); } + if (tok_bulbtype) { setHueBulbtype(device, tok_bulbtype.getInt()); } // read "Endpoints" - const JsonVariant &val_endpoints = GetCaseInsensitive(json, PSTR("Endpoints")); - if ((nullptr != &val_endpoints) && (val_endpoints.is())) { - const JsonArray &arr_ep = val_endpoints.as(); - endpoints_len = arr_ep.size(); + JsonParserToken val_endpoints = json[PSTR("Endpoints")]; + if (val_endpoints.isArray()) { + JsonParserArray arr_ep = JsonParserArray(val_endpoints); clearEndpoints(device); // clear even if array is empty - if (endpoints_len) { - for (auto ep_elt : arr_ep) { - uint8_t ep = strToUInt(ep_elt); - if (ep) { - addEndpoint(device, ep); - } - } + for (auto ep_elt : arr_ep) { + uint8_t ep = ep_elt.getUInt(); + if (ep) { addEndpoint(device, ep); } } } diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index 2accf849c..1f4b18e19 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -218,10 +218,12 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { if (Webserver->args()) { response = "["; - StaticJsonBuffer<300> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args())-1)); - if (hue_json.containsKey("on")) { - on = hue_json["on"]; + JsonParser parser((char*) Webserver->arg((Webserver->args())-1).c_str()); + JsonParserObject root = parser.getRootObject(); + + JsonParserToken hue_on = root[PSTR("on")]; + if (hue_on) { + on = hue_on.getBool(); snprintf_P(buf, buf_size, PSTR("{\"success\":{\"/lights/%d/state/on\":%s}}"), device_id, on ? "true" : "false"); @@ -235,8 +237,10 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { resp = true; } - if (hue_json.containsKey("bri")) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. - bri = hue_json["bri"]; + parser.setCurrent(); + JsonParserToken hue_bri = root[PSTR("bri")]; + if (hue_bri) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. + bri = hue_bri.getUInt(); prev_bri = bri; // store command value if (resp) { response += ","; } snprintf_P(buf, buf_size, @@ -252,13 +256,16 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { } // handle xy before Hue/Sat // If the request contains both XY and HS, we wan't to give priority to HS - if (hue_json.containsKey("xy")) { - float x = hue_json["xy"][0]; - float y = hue_json["xy"][1]; - const String &x_str = hue_json["xy"][0]; - const String &y_str = hue_json["xy"][1]; - x_str.toCharArray(prev_x_str, sizeof(prev_x_str)); - y_str.toCharArray(prev_y_str, sizeof(prev_y_str)); + parser.setCurrent(); + JsonParserToken hue_xy = root[PSTR("xy")]; + if (hue_xy) { + JsonParserArray arr_xy = JsonParserArray(hue_xy); + JsonParserToken tok_x = arr_xy[0]; + JsonParserToken tok_y = arr_xy[1]; + float x = tok_x.getFloat(); + float y = tok_y.getFloat(); + strlcpy(prev_x_str, tok_x.getStr(), sizeof(prev_x_str)); + strlcpy(prev_y_str, tok_y.getStr(), sizeof(prev_y_str)); if (resp) { response += ","; } snprintf_P(buf, buf_size, PSTR("{\"success\":{\"/lights/%d/state/xy\":[%s,%s]}}"), @@ -270,8 +277,11 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { ZigbeeHueXY(shortaddr, xi, yi); } bool huesat_changed = false; - if (hue_json.containsKey("hue")) { // The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue. - hue = hue_json["hue"]; + + parser.setCurrent(); + JsonParserToken hue_hue = root[PSTR("hue")]; + if (hue_hue) { // The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue. + hue = hue_hue.getUInt(); prev_hue = hue; if (resp) { response += ","; } snprintf_P(buf, buf_size, @@ -285,8 +295,11 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { } resp = true; } - if (hue_json.containsKey("sat")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). - sat = hue_json["sat"]; + + parser.setCurrent(); + JsonParserToken hue_sat = root[PSTR("sat")]; + if (hue_sat) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). + sat = hue_sat.getUInt(); prev_sat = sat; // store command value if (resp) { response += ","; } snprintf_P(buf, buf_size, @@ -303,8 +316,11 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { } resp = true; } - if (hue_json.containsKey("ct")) { // Color temperature 153 (Cold) to 500 (Warm) - ct = hue_json["ct"]; + + parser.setCurrent(); + JsonParserToken hue_ct = root[PSTR("ct")]; + if (hue_ct) { // Color temperature 153 (Cold) to 500 (Warm) + ct = hue_ct.getUInt(); prev_ct = ct; // store commande value if (resp) { response += ","; } snprintf_P(buf, buf_size, diff --git a/tasmota/xdrv_23_zigbee_5__constants.ino b/tasmota/xdrv_23_zigbee_5__constants.ino index 12a299a5f..7d35d7862 100644 --- a/tasmota/xdrv_23_zigbee_5__constants.ino +++ b/tasmota/xdrv_23_zigbee_5__constants.ino @@ -259,6 +259,22 @@ const char Z_strings[] PROGMEM = "DecelerationTimeLift" "\x00" "IntermediateSetpointsLift" "\x00" "IntermediateSetpointsTilt" "\x00" + "LocalTemperature" "\x00" + "OutdoorTemperature" "\x00" + "PICoolingDemand" "\x00" + "PIHeatingDemand" "\x00" + "LocalTemperatureCalibration" "\x00" + "OccupiedCoolingSetpoint" "\x00" + "OccupiedHeatingSetpoint" "\x00" + "UnoccupiedCoolingSetpoint" "\x00" + "UnoccupiedHeatingSetpoint" "\x00" + "RemoteSensing" "\x00" + "ControlSequenceOfOperation" "\x00" + "SystemMode" "\x00" + "TRVMode" "\x00" + "SetValvePosition" "\x00" + "EurotronicErrors" "\x00" + "CurrentTemperatureSetPoint" "\x00" "Hue" "\x00" "Sat" "\x00" "RemainingTime" "\x00" @@ -300,7 +316,6 @@ const char Z_strings[] PROGMEM = "TemperatureMinMeasuredValue" "\x00" "TemperatureMaxMeasuredValue" "\x00" "TemperatureTolerance" "\x00" - "PressureUnit" "\x00" "Pressure" "\x00" "PressureMinMeasuredValue" "\x00" "PressureMaxMeasuredValue" "\x00" @@ -310,6 +325,7 @@ const char Z_strings[] PROGMEM = "PressureMaxScaledValue" "\x00" "PressureScaledTolerance" "\x00" "PressureScale" "\x00" + "SeaPressure" "\x00" "FlowRate" "\x00" "FlowMinMeasuredValue" "\x00" "FlowMaxMeasuredValue" "\x00" @@ -620,179 +636,195 @@ enum Z_offsets { Zo_DecelerationTimeLift = 3144, Zo_IntermediateSetpointsLift = 3165, Zo_IntermediateSetpointsTilt = 3191, - Zo_Hue = 3217, - Zo_Sat = 3221, - Zo_RemainingTime = 3225, - Zo_X = 3239, - Zo_Y = 3241, - Zo_DriftCompensation = 3243, - Zo_CompensationText = 3261, - Zo_CT = 3278, - Zo_ColorMode = 3281, - Zo_NumberOfPrimaries = 3291, - Zo_Primary1X = 3309, - Zo_Primary1Y = 3319, - Zo_Primary1Intensity = 3329, - Zo_Primary2X = 3347, - Zo_Primary2Y = 3357, - Zo_Primary2Intensity = 3367, - Zo_Primary3X = 3385, - Zo_Primary3Y = 3395, - Zo_Primary3Intensity = 3405, - Zo_WhitePointX = 3423, - Zo_WhitePointY = 3435, - Zo_ColorPointRX = 3447, - Zo_ColorPointRY = 3460, - Zo_ColorPointRIntensity = 3473, - Zo_ColorPointGX = 3494, - Zo_ColorPointGY = 3507, - Zo_ColorPointGIntensity = 3520, - Zo_ColorPointBX = 3541, - Zo_ColorPointBY = 3554, - Zo_ColorPointBIntensity = 3567, - Zo_Illuminance = 3588, - Zo_IlluminanceMinMeasuredValue = 3600, - Zo_IlluminanceMaxMeasuredValue = 3628, - Zo_IlluminanceTolerance = 3656, - Zo_IlluminanceLightSensorType = 3677, - Zo_IlluminanceLevelStatus = 3704, - Zo_IlluminanceTargetLevel = 3727, - Zo_Temperature = 3750, - Zo_TemperatureMinMeasuredValue = 3762, - Zo_TemperatureMaxMeasuredValue = 3790, - Zo_TemperatureTolerance = 3818, - Zo_PressureUnit = 3839, - Zo_Pressure = 3852, - Zo_PressureMinMeasuredValue = 3861, - Zo_PressureMaxMeasuredValue = 3886, - Zo_PressureTolerance = 3911, - Zo_PressureScaledValue = 3929, - Zo_PressureMinScaledValue = 3949, - Zo_PressureMaxScaledValue = 3972, - Zo_PressureScaledTolerance = 3995, - Zo_PressureScale = 4019, - Zo_FlowRate = 4033, - Zo_FlowMinMeasuredValue = 4042, - Zo_FlowMaxMeasuredValue = 4063, - Zo_FlowTolerance = 4084, - Zo_Humidity = 4098, - Zo_HumidityMinMeasuredValue = 4107, - Zo_HumidityMaxMeasuredValue = 4132, - Zo_HumidityTolerance = 4157, - Zo_Occupancy = 4175, - Zo_OccupancySensorType = 4185, - Zo_ZoneState = 4205, - Zo_ZoneType = 4215, - Zo_ZoneStatus = 4224, - Zo_CurrentSummDelivered = 4235, - Zo_CompanyName = 4256, - Zo_MeterTypeID = 4268, - Zo_DataQualityID = 4280, - Zo_CustomerName = 4294, - Zo_Model = 4307, - Zo_PartNumber = 4313, - Zo_ProductRevision = 4324, - Zo_SoftwareRevision = 4340, - Zo_UtilityName = 4357, - Zo_POD = 4369, - Zo_AvailablePower = 4373, - Zo_PowerThreshold = 4388, - Zo_RMSVoltage = 4403, - Zo_RMSCurrent = 4414, - Zo_ActivePower = 4425, - Zo_NumberOfResets = 4437, - Zo_PersistentMemoryWrites = 4452, - Zo_LastMessageLQI = 4475, - Zo_LastMessageRSSI = 4490, - Zo_Identify = 4506, - Zo_xxxx = 4515, - Zo_IdentifyQuery = 4520, - Zo_AddGroup = 4534, - Zo_xxxx00 = 4543, - Zo_ViewGroup = 4550, - Zo_GetGroup = 4560, - Zo_01xxxx = 4569, - Zo_GetAllGroups = 4576, - Zo_00 = 4589, - Zo_RemoveGroup = 4592, - Zo_RemoveAllGroups = 4604, - Zo_ViewScene = 4620, - Zo_xxxxyy = 4630, - Zo_RemoveScene = 4637, - Zo_RemoveAllScenes = 4649, - Zo_RecallScene = 4665, - Zo_GetSceneMembership = 4677, - Zo_PowerOffEffect = 4696, - Zo_xxyy = 4711, - Zo_PowerOnRecall = 4716, - Zo_PowerOnTimer = 4730, - Zo_xxyyyyzzzz = 4743, - Zo_xx0A00 = 4754, - Zo_DimmerUp = 4761, - Zo_00190200 = 4770, - Zo_DimmerDown = 4779, - Zo_01190200 = 4790, - Zo_DimmerStop = 4799, - Zo_ResetAlarm = 4810, - Zo_xxyyyy = 4821, - Zo_ResetAllAlarms = 4828, - Zo_xx000A00 = 4843, - Zo_HueSat = 4852, - Zo_xxyy0A00 = 4859, - Zo_Color = 4868, - Zo_xxxxyyyy0A00 = 4874, - Zo_xxxx0A00 = 4887, - Zo_ShutterOpen = 4896, - Zo_ShutterClose = 4908, - Zo_ShutterStop = 4921, - Zo_ShutterLift = 4933, - Zo_xx = 4945, - Zo_ShutterTilt = 4948, - Zo_Shutter = 4960, - Zo_DimmerMove = 4968, - Zo_xx0A = 4979, - Zo_DimmerStepUp = 4984, - Zo_00xx0A00 = 4997, - Zo_DimmerStepDown = 5006, - Zo_01xx0A00 = 5021, - Zo_DimmerStep = 5030, - Zo_xx190A00 = 5041, - Zo_01 = 5050, - Zo_HueMove = 5053, - Zo_xx19 = 5061, - Zo_HueStepUp = 5066, - Zo_HueStepDown = 5076, - Zo_03xx0A00 = 5088, - Zo_HueStep = 5097, - Zo_SatMove = 5105, - Zo_SatStep = 5113, - Zo_xx190A = 5121, - Zo_ColorMove = 5128, - Zo_xxxxyyyy = 5138, - Zo_ColorStep = 5147, - Zo_ColorTempMoveUp = 5157, - Zo_01xxxx000000000000 = 5173, - Zo_ColorTempMoveDown = 5192, - Zo_03xxxx000000000000 = 5210, - Zo_ColorTempMoveStop = 5229, - Zo_00xxxx000000000000 = 5247, - Zo_ColorTempMove = 5266, - Zo_xxyyyy000000000000 = 5280, - Zo_ColorTempStepUp = 5299, - Zo_01xxxx0A0000000000 = 5315, - Zo_ColorTempStepDown = 5334, - Zo_03xxxx0A0000000000 = 5352, - Zo_ColorTempStep = 5371, - Zo_xxyyyy0A0000000000 = 5385, - Zo_ArrowClick = 5404, - Zo_ArrowHold = 5415, - Zo_ArrowRelease = 5425, - Zo_ZoneStatusChange = 5438, - Zo_xxxxyyzz = 5455, - Zo_xxyyzzzz = 5464, - Zo_AddScene = 5473, - Zo_xxyyyyzz = 5482, - Zo_StoreScene = 5491, + Zo_LocalTemperature = 3217, + Zo_OutdoorTemperature = 3234, + Zo_PICoolingDemand = 3253, + Zo_PIHeatingDemand = 3269, + Zo_LocalTemperatureCalibration = 3285, + Zo_OccupiedCoolingSetpoint = 3313, + Zo_OccupiedHeatingSetpoint = 3337, + Zo_UnoccupiedCoolingSetpoint = 3361, + Zo_UnoccupiedHeatingSetpoint = 3387, + Zo_RemoteSensing = 3413, + Zo_ControlSequenceOfOperation = 3427, + Zo_SystemMode = 3454, + Zo_TRVMode = 3465, + Zo_SetValvePosition = 3473, + Zo_EurotronicErrors = 3490, + Zo_CurrentTemperatureSetPoint = 3507, + Zo_Hue = 3534, + Zo_Sat = 3538, + Zo_RemainingTime = 3542, + Zo_X = 3556, + Zo_Y = 3558, + Zo_DriftCompensation = 3560, + Zo_CompensationText = 3578, + Zo_CT = 3595, + Zo_ColorMode = 3598, + Zo_NumberOfPrimaries = 3608, + Zo_Primary1X = 3626, + Zo_Primary1Y = 3636, + Zo_Primary1Intensity = 3646, + Zo_Primary2X = 3664, + Zo_Primary2Y = 3674, + Zo_Primary2Intensity = 3684, + Zo_Primary3X = 3702, + Zo_Primary3Y = 3712, + Zo_Primary3Intensity = 3722, + Zo_WhitePointX = 3740, + Zo_WhitePointY = 3752, + Zo_ColorPointRX = 3764, + Zo_ColorPointRY = 3777, + Zo_ColorPointRIntensity = 3790, + Zo_ColorPointGX = 3811, + Zo_ColorPointGY = 3824, + Zo_ColorPointGIntensity = 3837, + Zo_ColorPointBX = 3858, + Zo_ColorPointBY = 3871, + Zo_ColorPointBIntensity = 3884, + Zo_Illuminance = 3905, + Zo_IlluminanceMinMeasuredValue = 3917, + Zo_IlluminanceMaxMeasuredValue = 3945, + Zo_IlluminanceTolerance = 3973, + Zo_IlluminanceLightSensorType = 3994, + Zo_IlluminanceLevelStatus = 4021, + Zo_IlluminanceTargetLevel = 4044, + Zo_Temperature = 4067, + Zo_TemperatureMinMeasuredValue = 4079, + Zo_TemperatureMaxMeasuredValue = 4107, + Zo_TemperatureTolerance = 4135, + Zo_Pressure = 4156, + Zo_PressureMinMeasuredValue = 4165, + Zo_PressureMaxMeasuredValue = 4190, + Zo_PressureTolerance = 4215, + Zo_PressureScaledValue = 4233, + Zo_PressureMinScaledValue = 4253, + Zo_PressureMaxScaledValue = 4276, + Zo_PressureScaledTolerance = 4299, + Zo_PressureScale = 4323, + Zo_SeaPressure = 4337, + Zo_FlowRate = 4349, + Zo_FlowMinMeasuredValue = 4358, + Zo_FlowMaxMeasuredValue = 4379, + Zo_FlowTolerance = 4400, + Zo_Humidity = 4414, + Zo_HumidityMinMeasuredValue = 4423, + Zo_HumidityMaxMeasuredValue = 4448, + Zo_HumidityTolerance = 4473, + Zo_Occupancy = 4491, + Zo_OccupancySensorType = 4501, + Zo_ZoneState = 4521, + Zo_ZoneType = 4531, + Zo_ZoneStatus = 4540, + Zo_CurrentSummDelivered = 4551, + Zo_CompanyName = 4572, + Zo_MeterTypeID = 4584, + Zo_DataQualityID = 4596, + Zo_CustomerName = 4610, + Zo_Model = 4623, + Zo_PartNumber = 4629, + Zo_ProductRevision = 4640, + Zo_SoftwareRevision = 4656, + Zo_UtilityName = 4673, + Zo_POD = 4685, + Zo_AvailablePower = 4689, + Zo_PowerThreshold = 4704, + Zo_RMSVoltage = 4719, + Zo_RMSCurrent = 4730, + Zo_ActivePower = 4741, + Zo_NumberOfResets = 4753, + Zo_PersistentMemoryWrites = 4768, + Zo_LastMessageLQI = 4791, + Zo_LastMessageRSSI = 4806, + Zo_Identify = 4822, + Zo_xxxx = 4831, + Zo_IdentifyQuery = 4836, + Zo_AddGroup = 4850, + Zo_xxxx00 = 4859, + Zo_ViewGroup = 4866, + Zo_GetGroup = 4876, + Zo_01xxxx = 4885, + Zo_GetAllGroups = 4892, + Zo_00 = 4905, + Zo_RemoveGroup = 4908, + Zo_RemoveAllGroups = 4920, + Zo_ViewScene = 4936, + Zo_xxxxyy = 4946, + Zo_RemoveScene = 4953, + Zo_RemoveAllScenes = 4965, + Zo_RecallScene = 4981, + Zo_GetSceneMembership = 4993, + Zo_PowerOffEffect = 5012, + Zo_xxyy = 5027, + Zo_PowerOnRecall = 5032, + Zo_PowerOnTimer = 5046, + Zo_xxyyyyzzzz = 5059, + Zo_xx0A00 = 5070, + Zo_DimmerUp = 5077, + Zo_00190200 = 5086, + Zo_DimmerDown = 5095, + Zo_01190200 = 5106, + Zo_DimmerStop = 5115, + Zo_ResetAlarm = 5126, + Zo_xxyyyy = 5137, + Zo_ResetAllAlarms = 5144, + Zo_xx000A00 = 5159, + Zo_HueSat = 5168, + Zo_xxyy0A00 = 5175, + Zo_Color = 5184, + Zo_xxxxyyyy0A00 = 5190, + Zo_xxxx0A00 = 5203, + Zo_ShutterOpen = 5212, + Zo_ShutterClose = 5224, + Zo_ShutterStop = 5237, + Zo_ShutterLift = 5249, + Zo_xx = 5261, + Zo_ShutterTilt = 5264, + Zo_Shutter = 5276, + Zo_DimmerMove = 5284, + Zo_xx0A = 5295, + Zo_DimmerStepUp = 5300, + Zo_00xx0A00 = 5313, + Zo_DimmerStepDown = 5322, + Zo_01xx0A00 = 5337, + Zo_DimmerStep = 5346, + Zo_xx190A00 = 5357, + Zo_01 = 5366, + Zo_HueMove = 5369, + Zo_xx19 = 5377, + Zo_HueStepUp = 5382, + Zo_HueStepDown = 5392, + Zo_03xx0A00 = 5404, + Zo_HueStep = 5413, + Zo_SatMove = 5421, + Zo_SatStep = 5429, + Zo_xx190A = 5437, + Zo_ColorMove = 5444, + Zo_xxxxyyyy = 5454, + Zo_ColorStep = 5463, + Zo_ColorTempMoveUp = 5473, + Zo_01xxxx000000000000 = 5489, + Zo_ColorTempMoveDown = 5508, + Zo_03xxxx000000000000 = 5526, + Zo_ColorTempMoveStop = 5545, + Zo_00xxxx000000000000 = 5563, + Zo_ColorTempMove = 5582, + Zo_xxyyyy000000000000 = 5596, + Zo_ColorTempStepUp = 5615, + Zo_01xxxx0A0000000000 = 5631, + Zo_ColorTempStepDown = 5650, + Zo_03xxxx0A0000000000 = 5668, + Zo_ColorTempStep = 5687, + Zo_xxyyyy0A0000000000 = 5701, + Zo_ArrowClick = 5720, + Zo_ArrowHold = 5731, + Zo_ArrowRelease = 5741, + Zo_ZoneStatusChange = 5754, + Zo_xxxxyyzz = 5771, + Zo_xxyyzzzz = 5780, + Zo_AddScene = 5789, + Zo_xxyyyyzz = 5798, + Zo_StoreScene = 5807, }; diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index f29c514d5..217a003c4 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -106,16 +106,16 @@ enum Cx_cluster_short { Cx0000, Cx0001, Cx0002, Cx0003, Cx0004, Cx0005, Cx0006, Cx0007, 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, Cx0500, Cx0702, Cx0B01, Cx0B04, Cx0B05, + Cx0101, Cx0102, Cx0201, Cx0300, Cx0400, Cx0401, Cx0402, Cx0403, + Cx0404, Cx0405, Cx0406, Cx0500, Cx0702, Cx0B01, Cx0B04, Cx0B05, }; const uint16_t Cx_cluster[] PROGMEM = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 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, 0x0500, 0x0702, 0x0B01, 0x0B04, 0x0B05, + 0x0101, 0x0102, 0x0201, 0x0300, 0x0400, 0x0401, 0x0402, 0x0403, + 0x0404, 0x0405, 0x0406, 0x0500, 0x0702, 0x0B01, 0x0B04, 0x0B05, }; uint16_t CxToCluster(uint8_t cx) { @@ -403,6 +403,25 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { { Zoctstr, Cx0102, 0x0018, Z_(IntermediateSetpointsLift),1 }, { Zoctstr, Cx0102, 0x0019, Z_(IntermediateSetpointsTilt),1 }, + // Thermostat + { Zint16, Cx0201, 0x0000, Z_(LocalTemperature), -100 }, + { Zint16, Cx0201, 0x0001, Z_(OutdoorTemperature),-100 }, + { Zuint8, Cx0201, 0x0007, Z_(PICoolingDemand), 1 }, + { Zuint8, Cx0201, 0x0008, Z_(PIHeatingDemand), 1 }, + { Zint8, Cx0201, 0x0010, Z_(LocalTemperatureCalibration), -10 }, + { Zint16, Cx0201, 0x0011, Z_(OccupiedCoolingSetpoint), -100 }, + { Zint16, Cx0201, 0x0012, Z_(OccupiedHeatingSetpoint), -100 }, + { Zint16, Cx0201, 0x0013, Z_(UnoccupiedCoolingSetpoint), -100 }, + { Zint16, Cx0201, 0x0014, Z_(UnoccupiedHeatingSetpoint), -100 }, + { Zmap8, Cx0201, 0x001A, Z_(RemoteSensing), 1 }, + { Zenum8, Cx0201, 0x001B, Z_(ControlSequenceOfOperation), 1 }, + { Zenum8, Cx0201, 0x001C, Z_(SystemMode), 1 }, + // below is Eurotronic specific + { Zenum8, Cx0201, 0x4000, Z_(TRVMode), 1 }, + { Zuint8, Cx0201, 0x4001, Z_(SetValvePosition), 1 }, + { Zuint8, Cx0201, 0x4002, Z_(EurotronicErrors), 1 }, + { Zint16, Cx0201, 0x4003, Z_(CurrentTemperatureSetPoint), -100 }, + // Color Control cluster { Zuint8, Cx0300, 0x0000, Z_(Hue), 1 }, { Zuint8, Cx0300, 0x0001, Z_(Sat), 1 }, @@ -466,6 +485,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { { Zint16, Cx0403, 0x0012, Z_(PressureMaxScaledValue), 1 }, // { Zuint16, Cx0403, 0x0013, Z_(PressureScaledTolerance), 1 }, // { Zint8, Cx0403, 0x0014, Z_(PressureScale), 1 }, // + { Zint16, Cx0403, 0xFF00, Z_(SeaPressure), 1 }, // Pressure at Sea Level, Tasmota specific { Zunk, Cx0403, 0xFFFF, Z_(), 0 }, // Remove all other Pressure values // Flow Measurement cluster @@ -642,6 +662,7 @@ public: void parseReportAttributes(Z_attribute_list& attr_list); void generateSyntheticAttributes(Z_attribute_list& attr_list); + void computeSyntheticAttributes(Z_attribute_list& attr_list); void generateCallBacks(Z_attribute_list& attr_list); void parseReadAttributes(Z_attribute_list& attr_list); void parseReadAttributesResponse(Z_attribute_list& attr_list); @@ -1073,6 +1094,9 @@ void ZCLFrame::parseReportAttributes(Z_attribute_list& attr_list) { } } +// +// Extract attributes hidden in other compound attributes +// void ZCLFrame::generateSyntheticAttributes(Z_attribute_list& attr_list) { // scan through attributes and apply specific converters for (auto &attr : attr_list) { @@ -1080,11 +1104,6 @@ void ZCLFrame::generateSyntheticAttributes(Z_attribute_list& attr_list) { uint32_t ccccaaaa = (attr.key.id.cluster << 16) | attr.key.id.attr_id; switch (ccccaaaa) { // 0xccccaaaa . c=cluster, a=attribute - case 0x00010020: // BatteryVoltage - if (attr_list.countAttribute(0x0001,0x0021) == 0) { // if it does not already contain BatteryPercentage - uint32_t mv = attr.getUInt()*100; - attr_list.addAttribute(0x0001, 0x0021).setUInt(toPercentageCR2032(mv) * 2); - } case 0x0000FF01: syntheticAqaraSensor(attr_list, attr); break; @@ -1102,6 +1121,48 @@ void ZCLFrame::generateSyntheticAttributes(Z_attribute_list& attr_list) { } } +// +// Compute new attributes based on the standard set +// Note: both function are now split to compute on extracted attributes +// +void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) { + // scan through attributes and apply specific converters + for (auto &attr : attr_list) { + if (attr.key_is_str) { continue; } // pass if key is a name + uint32_t ccccaaaa = (attr.key.id.cluster << 16) | attr.key.id.attr_id; + + switch (ccccaaaa) { // 0xccccaaaa . c=cluster, a=attribute + case 0x00010020: // BatteryVoltage + if (attr_list.countAttribute(0x0001,0x0021) == 0) { // if it does not already contain BatteryPercentage + uint32_t mv = attr.getUInt()*100; + attr_list.addAttribute(0x0001, 0x0021).setUInt(toPercentageCR2032(mv) * 2); + } + break; + case 0x02010008: // Pi Heating Demand - solve Eutotronic bug + { + const char * manufacturer_c = zigbee_devices.getManufacturerId(_srcaddr); // null if unknown + String manufacturerId((char*) manufacturer_c); + if (manufacturerId.equals(F("Eurotronic"))) { + // Eurotronic does not report 0..100 but 0..255, including 255 which is normally an ivalid value + uint8_t valve = attr.getUInt(); + if (attr.isNone()) { valve = 255; } + uint8_t valve_100 = changeUIntScale(valve, 0, 255, 0, 100); + attr.setUInt(valve_100); + } + } + break; + case 0x04030000: // Pressure + { + int16_t pressure = attr.getInt(); + int16_t pressure_sealevel = (pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0f), 5.255f)) - 21.6f; + attr_list.addAttribute(0x0403, 0xFF00).setInt(pressure_sealevel); + // We create a synthetic attribute 0403/FF00 to indicate sea level + } + break; + } + } +} + // Set deferred callbacks for Occupancy // TODO make delay a parameter void ZCLFrame::generateCallBacks(Z_attribute_list& attr_list) { @@ -1641,6 +1702,11 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_ case 0x00060000: case 0x00068000: device.setPower(attr.getBool()); break; case 0x00080000: device.dimmer = uval16; break; + case 0x02010000: device.temperature = fval * 10 + 0.5f; break; + case 0x02010008: device.th_setpoint = uval16; break; + case 0x02010012: device.temperature_target = fval * 10 + 0.5f; break; + case 0x02010007: device.th_setpoint = uval16; break; + case 0x02010011: device.temperature_target = fval * 10 + 0.5f; break; case 0x03000000: device.hue = changeUIntScale(uval16, 0, 254, 0, 360); break; case 0x03000001: device.sat = uval16; break; case 0x03000003: device.x = uval16; break; @@ -1648,6 +1714,7 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_ case 0x03000007: device.ct = uval16; break; case 0x03000008: device.colormode = uval16; break; case 0x04020000: device.temperature = fval * 10 + 0.5f; break; + case 0x0403FF00: device.pressure = fval + 0.5f; break; // give priority to sea level case 0x04030000: device.pressure = fval + 0.5f; break; case 0x04050000: device.humidity = fval + 0.5f; break; case 0x0B040505: device.mains_voltage = uval16; break; diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index ee9bfd19a..a529edd0e 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -28,6 +28,7 @@ const uint8_t ZIGBEE_STATUS_STARTING = 3; // Starting CC2530 as co const uint8_t ZIGBEE_STATUS_PERMITJOIN_CLOSE = 20; // Disable PermitJoin const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_60 = 21; // Enable PermitJoin for 60 seconds const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_XX = 22; // Enable PermitJoin until next boot +const uint8_t ZIGBEE_STATUS_PERMITJOIN_ERROR = 23; // Enable PermitJoin until next boot const uint8_t ZIGBEE_STATUS_DEVICE_ANNOUNCE = 30; // Device announces its address const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; // Node descriptor const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; // Endpoints descriptor @@ -648,7 +649,9 @@ ZBM(ZBS_SET_NETWORKS, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG ZBM(ZBS_SET_PACKET_BUF, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_PACKET_BUFFER_COUNT, 0xFF, 0x00) // 530001FF00 ZBM(ZBR_SET_OK, EZSP_setConfigurationValue, 0x00 /*high*/, 0x00 /*ok*/) // 530000 -ZBM(ZBR_SET_OK2, 0x00, 0x00 /*high*/, 0x00 /*ok*/) // 000000 - TODO why does setting EZSP_CONFIG_PACKET_BUFFER_COUNT has a different response? +// There is a bug in v6.7 where the response if 000000 instead of 530000 +// If we detect the version to be v6.7, the first byte is changed to 00 +ZBR(ZBR_SET_OK2, EZSP_setConfigurationValue, 0x00 /*high*/, 0x00 /*ok*/) // Read some configuration values // ZBM(ZBS_GET_APS_UNI, EZSP_getConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_APS_UNICAST_MESSAGE_COUNT) // 520003 diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index c761cabb6..2ce98cd01 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -166,21 +166,22 @@ int32_t EZ_RouteError(int32_t res, const class SBuffer &buf) { int32_t EZ_PermitJoinRsp(int32_t res, const class SBuffer &buf) { uint8_t status = buf.get8(2); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"Message\":\"%s"), - (0 == status) ? ZIGBEE_STATUS_PERMITJOIN_OPEN_60 : ZIGBEE_STATUS_PERMITJOIN_CLOSE, - (0 == status) ? PSTR("Pairing mode enabled") : PSTR("Pairing mode error") - ); - if (status) { - ResponseAppend_P("0x%02X", status); + if (status) { // only report if there is an error + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":23,\"Message\":\"Pairing mode error 0x%02X\"}}"), status); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); } - ResponseAppend_P(PSTR("\"}}")); - - MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - return -1; } +// +// Special case: EZSP does not send an event for PermitJoin end, so we generate a synthetic one +// +void Z_PermitJoinDisable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":20,\"Message\":\"Pairing mode disabled\"}}")); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); +} + + // // Received MessageSentHandler // @@ -395,6 +396,10 @@ int32_t EZ_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); if (0x08 == protocol_version) { + if ((stack_version & 0xFF00) == 0x6700) { + // If v6.7 there is a bug so we need to change the response + ZBW(ZBR_SET_OK2, 0x00, 0x00 /*high*/, 0x00 /*ok*/) + } return 0; // protocol v8 is ok } else { return ZIGBEE_LABEL_UNSUPPORTED_VERSION; // abort @@ -1348,7 +1353,8 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) { // log the packet details zcl_received.log(); - zigbee_devices.setLQI(srcaddr, linkquality); // EFR32 has a different scale for LQI + zigbee_devices.setLQI(srcaddr, linkquality != 0xFF ? linkquality : 0xFE); // EFR32 has a different scale for LQI + zigbee_devices.setLastSeenNow(srcaddr); char shortaddr[8]; snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); @@ -1390,6 +1396,7 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) { } zcl_received.generateSyntheticAttributes(attr_list); + zcl_received.computeSyntheticAttributes(attr_list); zcl_received.generateCallBacks(attr_list); // set deferred callbacks, ex: Occupancy zcl_received.postProcessAttributes(srcaddr, attr_list); @@ -1492,6 +1499,7 @@ int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) { // ZDO request // Report LQI zigbee_devices.setLQI(srcaddr, linkquality); + zigbee_devices.setLastSeenNow(srcaddr); // Since ZDO messages start with a sequence number, we skip it // but we add the source address in the last 2 bytes SBuffer zdo_buf(buf.get8(20) - 1 + 2); diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 5ca3b555e..7bacb43d9 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -97,19 +97,6 @@ void ZigbeeInit(void) * Commands \*********************************************************************************************/ -uint32_t strToUInt(const JsonVariant &val) { - // if the string starts with 0x, it is considered Hex, otherwise it is an int - if (val.is()) { - return val.as(); - } else { - if (val.is()) { - String sval = val.as(); - return strtoull(sval.c_str(), nullptr, 0); - } - } - return 0; // couldn't parse anything -} - #ifdef USE_ZIGBEE_ZNP // Do a factory reset of the CC2530 const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM = @@ -242,7 +229,7 @@ bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_stat // Parse "Report", "Write", "Response" or "Condig" attribute // Operation is one of: ZCL_REPORT_ATTRIBUTES (0x0A), ZCL_WRITE_ATTRIBUTES (0x02) or ZCL_READ_ATTRIBUTES_RESPONSE (0x01) -void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) { +void ZbSendReportWrite(class JsonParserToken val_pubwrite, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) { SBuffer buf(200); // buffer to store the binary output of attibutes if (nullptr == XdrvMailbox.command) { @@ -250,12 +237,11 @@ void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t } // iterate on keys - for (JsonObject::const_iterator it=val_pubwrite.begin(); it!=val_pubwrite.end(); ++it) { - const char *key = it->key; - const JsonVariant &value = it->value; + for (auto key : val_pubwrite.getObject()) { + JsonParserToken value = key.getValue(); Z_attribute attr; - attr.setKeyName(key); + attr.setKeyName(key.getStr()); if (Z_parseAttributeKey(attr)) { // Buffer ready, do some sanity checks if (0xFFFF == cluster) { @@ -276,10 +262,10 @@ void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t } } - if (value.is()) { - attr.setStr(value.as()); - } else if (value.is()) { - attr.setFloat(value.as()); + if (value.isStr()) { + attr.setStr(value.getStr()); + } else if (value.isNum()) { + attr.setFloat(value.getFloat()); } double val_d = 0; // I try to avoid `double` but this type capture both float and (u)int32_t without prevision loss @@ -293,41 +279,30 @@ void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t } else { // //////////////////////////////////////////////////////////////////////////////// // ZCL_CONFIGURE_REPORTING - if (!value.is()) { + if (!value.isObject()) { ResponseCmndChar_P(PSTR("Config requires JSON objects")); return; } - JsonObject &attr_config = value.as(); + JsonParserObject attr_config = value.getObject(); bool attr_direction = false; - const JsonVariant &val_attr_direction = GetCaseInsensitive(attr_config, PSTR("DirectionReceived")); - if (nullptr != &val_attr_direction) { - uint32_t dir = strToUInt(val_attr_direction); - if (dir) { - attr_direction = true; - } - } + uint32_t dir = attr_config.getUInt(PSTR("DirectionReceived"), 0); + if (dir) { attr_direction = true; } // read MinInterval and MaxInterval, default to 0xFFFF if not specified - uint16_t attr_min_interval = 0xFFFF; - uint16_t attr_max_interval = 0xFFFF; - const JsonVariant &val_attr_min = GetCaseInsensitive(attr_config, PSTR("MinInterval")); - if (nullptr != &val_attr_min) { attr_min_interval = strToUInt(val_attr_min); } - const JsonVariant &val_attr_max = GetCaseInsensitive(attr_config, PSTR("MaxInterval")); - if (nullptr != &val_attr_max) { attr_max_interval = strToUInt(val_attr_max); } + uint16_t attr_min_interval = attr_config.getUInt(PSTR("MinInterval"), 0xFFFF); + uint16_t attr_max_interval = attr_config.getUInt(PSTR("MaxInterval"), 0xFFFF); // read ReportableChange - const JsonVariant &val_attr_rc = GetCaseInsensitive(attr_config, PSTR("ReportableChange")); - if (nullptr != &val_attr_rc) { - val_d = val_attr_rc.as(); - val_str = val_attr_rc.as(); + JsonParserToken val_attr_rc = attr_config[PSTR("ReportableChange")]; + if (val_attr_rc) { + val_d = val_attr_rc.getFloat(); + val_str = val_attr_rc.getStr(); ZbApplyMultiplier(val_d, attr.attr_multiplier); } // read TimeoutPeriod - uint16_t attr_timeout = 0x0000; - const JsonVariant &val_attr_timeout = GetCaseInsensitive(attr_config, PSTR("TimeoutPeriod")); - if (nullptr != &val_attr_timeout) { attr_timeout = strToUInt(val_attr_timeout); } + uint16_t attr_timeout = attr_config.getUInt(PSTR("TimeoutPeriod"), 0x0000); bool attr_discrete = Z_isDiscreteDataType(attr.attr_type); @@ -376,37 +351,36 @@ void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t } // Parse the "Send" attribute and send the command -void ZbSendSend(const JsonVariant &val_cmd, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf) { +void ZbSendSend(class JsonParserToken val_cmd, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf) { uint8_t cmd = 0; String cmd_str = ""; // the actual low-level command, either specified or computed - const char *cmd_s; // pointer to payload string + const char *cmd_s = ""; // pointer to payload string bool clusterSpecific = true; static char delim[] = ", "; // delimiters for parameters // probe the type of the argument // If JSON object, it's high level commands // If String, it's a low level command - if (val_cmd.is()) { + if (val_cmd.isObject()) { // we have a high-level command - const JsonObject &cmd_obj = val_cmd.as(); + JsonParserObject cmd_obj = val_cmd.getObject(); int32_t cmd_size = cmd_obj.size(); if (cmd_size > 1) { Response_P(PSTR("Only 1 command allowed (%d)"), cmd_size); return; } else if (1 == cmd_size) { // We have exactly 1 command, parse it - JsonObject::const_iterator it = cmd_obj.begin(); // just get the first key/value - String key = it->key; - const JsonVariant& value = it->value; + JsonParserKey key = cmd_obj.getFirstElement(); + JsonParserToken value = key.getValue(); uint32_t x = 0, y = 0, z = 0; uint16_t cmd_var; uint16_t local_cluster_id; - const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.c_str(), &local_cluster_id, &cmd_var); + const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.getStr(), &local_cluster_id, &cmd_var); if (tasmota_cmd) { cmd_str = tasmota_cmd; } else { - Response_P(PSTR("Unrecognized zigbee command: %s"), key.c_str()); + Response_P(PSTR("Unrecognized zigbee command: %s"), key.getStr()); return; } // check cluster @@ -418,13 +392,17 @@ void ZbSendSend(const JsonVariant &val_cmd, uint16_t device, uint16_t groupaddr, } // parse the JSON value, depending on its type fill in x,y,z - if (value.is()) { - x = value.as() ? 1 : 0; - } else if (value.is()) { - x = value.as(); + if (value.isNum()) { + x = value.getUInt(); // automatic conversion to 0/1 + // if (value.is()) { + // // x = value.as() ? 1 : 0; + // } else if + // } else if (value.is()) { + // x = value.as(); } else { // if non-bool or non-int, trying char* - const char *s_const = value.as(); + const char *s_const = value.getStr(nullptr); + // const char *s_const = value.as(); if (s_const != nullptr) { char s[strlen(s_const)+1]; strcpy(s, s_const); @@ -459,14 +437,13 @@ void ZbSendSend(const JsonVariant &val_cmd, uint16_t device, uint16_t groupaddr, } else { // we have zero command, pass through until last error for missing command } - } else if (val_cmd.is()) { + } else if (val_cmd.isStr()) { // low-level command - cmd_str = val_cmd.as(); // Now parse the string to extract cluster, command, and payload // Parse 'cmd' in the form "AAAA_BB/CCCCCCCC" or "AAAA!BB/CCCCCCCC" // where AA is the cluster number, BBBB the command number, CCCC... the payload // First delimiter is '_' for a global command, or '!' for a cluster specific command - const char * data = cmd_str.c_str(); + const char * data = val_cmd.getStr(); uint16_t local_cluster_id = parseHex(&data, 4); // check cluster @@ -505,7 +482,7 @@ void ZbSendSend(const JsonVariant &val_cmd, uint16_t device, uint16_t groupaddr, // Parse the "Send" attribute and send the command -void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) { +void ZbSendRead(JsonParserToken val_attr, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) { // ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":5} // ZbSend {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Read":"0x0005"} // ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":[5,6,7,4]} @@ -525,32 +502,30 @@ void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr attr_item_offset = 1; } - uint16_t val = strToUInt(val_attr); - if (val_attr.is()) { + if (val_attr.isArray()) { // value is an array [] - const JsonArray& attr_arr = val_attr.as(); + JsonParserArray attr_arr = val_attr.getArray(); attrs_len = attr_arr.size() * attr_item_len; attrs = (uint8_t*) calloc(attrs_len, 1); uint32_t i = 0; for (auto value : attr_arr) { - uint16_t val = strToUInt(value); + uint16_t val = value.getUInt(); i += attr_item_offset; attrs[i++] = val & 0xFF; attrs[i++] = val >> 8; i += attr_item_len - 2 - attr_item_offset; // normally 0 } - } else if (val_attr.is()) { + } else if (val_attr.isObject()) { // value is an object {} - const JsonObject& attr_obj = val_attr.as(); + JsonParserObject attr_obj = val_attr.getObject(); attrs_len = attr_obj.size() * attr_item_len; attrs = (uint8_t*) calloc(attrs_len, 1); uint32_t actual_attr_len = 0; // iterate on keys - for (JsonObject::const_iterator it=attr_obj.begin(); it!=attr_obj.end(); ++it) { - const char *key = it->key; - const JsonVariant &value = it->value; // we don't need the value here, only keys are relevant + for (auto key : attr_obj) { + JsonParserToken value = key.getValue(); bool found = false; // scan attributes to find by name, and retrieve type @@ -561,11 +536,11 @@ void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr uint16_t local_cluster_id = CxToCluster(pgm_read_byte(&converter->cluster_short)); // uint8_t local_type_id = pgm_read_byte(&converter->type); - if ((pgm_read_word(&converter->name_offset)) && (0 == strcasecmp_P(key, Z_strings + pgm_read_word(&converter->name_offset)))) { + if ((pgm_read_word(&converter->name_offset)) && (0 == strcasecmp_P(key.getStr(), Z_strings + pgm_read_word(&converter->name_offset)))) { // match name // check if there is a conflict with cluster // TODO - if (!value && attr_item_offset) { + if (!(value.getBool()) && attr_item_offset) { // If value is false (non-default) then set direction to 1 (for ReadConfig) attrs[actual_attr_len] = 0x01; } @@ -594,6 +569,7 @@ void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr } else { // value is a literal if (0xFFFF != cluster) { + uint16_t val = val_attr.getUInt(); attrs_len = attr_item_len; attrs = (uint8_t*) calloc(attrs_len, 1); attrs[0 + attr_item_offset] = val & 0xFF; // little endian @@ -648,9 +624,9 @@ void CmndZbSend(void) { // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"1,2"} } // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"0x1122,0xFFEE"} } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - DynamicJsonBuffer jsonBuf; - const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); - if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // params uint16_t device = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid @@ -661,15 +637,15 @@ void CmndZbSend(void) { // parse "Device" and "Group" - const JsonVariant &val_device = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_DEVICE)); - if (nullptr != &val_device) { - device = zigbee_devices.parseDeviceParam(val_device.as()); + JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_DEVICE)]; + if (val_device) { + device = zigbee_devices.parseDeviceParam(val_device.getStr()); if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } } if (BAD_SHORTADDR == device) { // if not found, check if we have a group - const JsonVariant &val_group = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_GROUP)); - if (nullptr != &val_group) { - groupaddr = strToUInt(val_group); + JsonParserToken val_group = root[PSTR(D_CMND_ZIGBEE_GROUP)]; + if (val_group) { + groupaddr = val_group.getUInt(); } else { // no device nor group ResponseCmndChar_P(PSTR("Unknown device")); return; @@ -679,12 +655,9 @@ void CmndZbSend(void) { // Note: groupaddr == 0 is valid // read other parameters - const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_CLUSTER)); - if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } - const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_ENDPOINT)); - if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } - const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_MANUF)); - if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); } + cluster = root.getUInt(PSTR(D_CMND_ZIGBEE_CLUSTER), cluster); + endpoint = root.getUInt(PSTR(D_CMND_ZIGBEE_ENDPOINT), endpoint); + manuf = root.getUInt(PSTR(D_CMND_ZIGBEE_MANUF), manuf); // infer endpoint if (BAD_SHORTADDR == device) { @@ -700,61 +673,61 @@ void CmndZbSend(void) { // from here endpoint is valid and non-zero // cluster may be already specified or 0xFFFF - const JsonVariant &val_cmd = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_SEND)); - const JsonVariant &val_read = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_READ)); - const JsonVariant &val_write = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_WRITE)); - const JsonVariant &val_publish = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_REPORT)); - const JsonVariant &val_response = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_RESPONSE)); - const JsonVariant &val_read_config = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_READ_CONFIG)); - const JsonVariant &val_config = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_CONFIG)); - uint32_t multi_cmd = (nullptr != &val_cmd) + (nullptr != &val_read) + (nullptr != &val_write) + (nullptr != &val_publish) - + (nullptr != &val_response) + (nullptr != &val_read_config) + (nullptr != &val_config); + JsonParserToken val_cmd = root[PSTR(D_CMND_ZIGBEE_SEND)]; + JsonParserToken val_read = root[PSTR(D_CMND_ZIGBEE_READ)]; + JsonParserToken val_write = root[PSTR(D_CMND_ZIGBEE_WRITE)]; + JsonParserToken val_publish = root[PSTR(D_CMND_ZIGBEE_REPORT)]; + JsonParserToken val_response = root[PSTR(D_CMND_ZIGBEE_RESPONSE)]; + JsonParserToken val_read_config = root[PSTR(D_CMND_ZIGBEE_READ_CONFIG)]; + JsonParserToken val_config = root[PSTR(D_CMND_ZIGBEE_CONFIG)]; + uint32_t multi_cmd = ((bool)val_cmd) + ((bool)val_read) + ((bool)val_write) + ((bool)val_publish) + + ((bool)val_response) + ((bool)val_read_config) + ((bool)val_config); if (multi_cmd > 1) { ResponseCmndChar_P(PSTR("Can only have one of: 'Send', 'Read', 'Write', 'Report', 'Reponse', 'ReadConfig' or 'Config'")); return; } // from here we have one and only one command - if (nullptr != &val_cmd) { + if (val_cmd) { // "Send":{...commands...} // we accept either a string or a JSON object ZbSendSend(val_cmd, device, groupaddr, cluster, endpoint, manuf); - } else if (nullptr != &val_read) { + } else if (val_read) { // "Read":{...attributes...}, "Read":attribute or "Read":[...attributes...] // we accept eitehr a number, a string, an array of numbers/strings, or a JSON object ZbSendRead(val_read, device, groupaddr, cluster, endpoint, manuf, ZCL_READ_ATTRIBUTES); - } else if (nullptr != &val_write) { + } else if (val_write) { // only KSON object - if (!val_write.is()) { + if (!val_write.isObject()) { ResponseCmndChar_P(PSTR("Missing parameters")); return; } // "Write":{...attributes...} ZbSendReportWrite(val_write, device, groupaddr, cluster, endpoint, manuf, ZCL_WRITE_ATTRIBUTES); - } else if (nullptr != &val_publish) { + } else if (val_publish) { // "Publish":{...attributes...} // only KSON object - if (!val_publish.is()) { + if (!val_publish.isObject()) { ResponseCmndChar_P(PSTR("Missing parameters")); return; } ZbSendReportWrite(val_publish, device, groupaddr, cluster, endpoint, manuf, ZCL_REPORT_ATTRIBUTES); - } else if (nullptr != &val_response) { + } else if (val_response) { // "Report":{...attributes...} // only KSON object - if (!val_response.is()) { + if (!val_response.isObject()) { ResponseCmndChar_P(PSTR("Missing parameters")); return; } ZbSendReportWrite(val_response, device, groupaddr, cluster, endpoint, manuf, ZCL_READ_ATTRIBUTES_RESPONSE); - } else if (nullptr != &val_read_config) { + } else if (val_read_config) { // "ReadConfg":{...attributes...}, "ReadConfg":attribute or "ReadConfg":[...attributes...] // we accept eitehr a number, a string, an array of numbers/strings, or a JSON object ZbSendRead(val_read_config, device, groupaddr, cluster, endpoint, manuf, ZCL_READ_REPORTING_CONFIGURATION); - } else if (nullptr != &val_config) { + } else if (val_config) { // "Config":{...attributes...} // only JSON object - if (!val_config.is()) { + if (!val_config.isObject()) { ResponseCmndChar_P(PSTR("Missing parameters")); return; } @@ -774,61 +747,57 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind // local endpoint is always 1, IEEE addresses are calculated if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - DynamicJsonBuffer jsonBuf; - const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); - if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // params - uint16_t srcDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid + uint16_t srcDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid uint16_t dstDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid uint64_t dstLongAddr = 0; uint8_t endpoint = 0x00; // 0x00 is invalid for the src endpoint - uint8_t toendpoint = 0x00; // 0x00 is invalid for the dst endpoint + uint8_t toendpoint = 0x01; // default dest endpoint to 0x01 uint16_t toGroup = 0x0000; // group address uint16_t cluster = 0; // 0xFFFF is invalid uint32_t group = 0xFFFFFFFF; // 16 bits values, otherwise 0xFFFFFFFF is unspecified // Information about source device: "Device", "Endpoint", "Cluster" // - the source endpoint must have a known IEEE address - const JsonVariant &val_device = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_DEVICE)); - if (nullptr != &val_device) { - srcDevice = zigbee_devices.parseDeviceParam(val_device.as()); - } - if ((nullptr == &val_device) || (BAD_SHORTADDR == srcDevice)) { ResponseCmndChar_P(PSTR("Unknown source device")); return; } + srcDevice = zigbee_devices.parseDeviceParam(root.getStr(PSTR(D_CMND_ZIGBEE_DEVICE), nullptr)); + if (BAD_SHORTADDR == srcDevice) { ResponseCmndChar_P(PSTR("Unknown source device")); return; } // check if IEEE address is known uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice); if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; } // look for source endpoint - const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_ENDPOINT)); - if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } - else { endpoint = zigbee_devices.findFirstEndpoint(srcDevice); } + endpoint = root.getUInt(PSTR(D_CMND_ZIGBEE_ENDPOINT), endpoint); + if (0 == endpoint) { endpoint = zigbee_devices.findFirstEndpoint(srcDevice); } // look for source cluster - const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_CLUSTER)); - if (nullptr != &val_cluster) { - cluster = strToUInt(val_cluster); // first convert as number + JsonParserToken val_cluster = root[PSTR(D_CMND_ZIGBEE_CLUSTER)]; + if (val_cluster) { + cluster = val_cluster.getUInt(cluster); // first convert as number if (0 == cluster) { - zigbeeFindAttributeByName(val_cluster.as(), &cluster, nullptr, nullptr); + zigbeeFindAttributeByName(val_cluster.getStr(), &cluster, nullptr, nullptr); } } // Or Group Address - we don't need a dstEndpoint in this case - const JsonVariant &to_group = GetCaseInsensitive(json, PSTR("ToGroup")); - if (nullptr != &to_group) { toGroup = strToUInt(to_group); } + JsonParserToken to_group = root[PSTR("ToGroup")]; + if (to_group) { toGroup = to_group.getUInt(toGroup); } // Either Device address // In this case the following parameters are mandatory // - "ToDevice" and the device must have a known IEEE address // - "ToEndpoint" - const JsonVariant &dst_device = GetCaseInsensitive(json, PSTR("ToDevice")); + JsonParserToken dst_device = root[PSTR("ToDevice")]; // If no target is specified, we default to coordinator 0x0000 - if ((nullptr == &to_group) && (nullptr == &dst_device)) { + if ((!to_group) && (!dst_device)) { dstDevice = 0x0000; } - if ((nullptr != &dst_device) || (BAD_SHORTADDR != dstDevice)) { + if ((dst_device) || (BAD_SHORTADDR != dstDevice)) { if (BAD_SHORTADDR == dstDevice) { - dstDevice = zigbee_devices.parseDeviceParam(dst_device.as()); + dstDevice = zigbee_devices.parseDeviceParam(dst_device.getStr(nullptr)); if (BAD_SHORTADDR == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } } if (0x0000 == dstDevice) { @@ -838,14 +807,12 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind } if (0 == dstLongAddr) { ResponseCmndChar_P(PSTR("Unknown dest IEEE address")); return; } - const JsonVariant &val_toendpoint = GetCaseInsensitive(json, PSTR("ToEndpoint")); - if (nullptr != &val_toendpoint) { toendpoint = strToUInt(val_toendpoint); } - else { toendpoint = 0x01; } // default to endpoint 1 + toendpoint = root.getUInt(PSTR("ToEndpoint"), toendpoint); } // make sure we don't have conflicting parameters - if (&to_group && dstLongAddr) { ResponseCmndChar_P(PSTR("Cannot have both \"ToDevice\" and \"ToGroup\"")); return; } - if (!&to_group && !dstLongAddr) { ResponseCmndChar_P(PSTR("Missing \"ToDevice\" or \"ToGroup\"")); return; } + if (to_group && dstLongAddr) { ResponseCmndChar_P(PSTR("Cannot have both \"ToDevice\" and \"ToGroup\"")); return; } + if (!to_group && !dstLongAddr) { ResponseCmndChar_P(PSTR("Missing \"ToDevice\" or \"ToGroup\"")); return; } #ifdef USE_ZIGBEE_ZNP SBuffer buf(34); @@ -1097,38 +1064,32 @@ void CmndZbSave(void) { // ZbRestore {"Device":"0x5ADF","Name":"Petite_Lampe","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]} void CmndZbRestore(void) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - DynamicJsonBuffer jsonBuf; - const JsonVariant json_parsed = jsonBuf.parse((const char*) XdrvMailbox.data); // const to force a copy of parameter - const JsonVariant * json = &json_parsed; // root of restore, to be changed if needed - bool success = false; + JsonParser parser(XdrvMailbox.data); + JsonParserToken root = parser.getRoot(); - // check if parsing succeeded - if (json_parsed.is()) { - success = json_parsed.as().success(); - } else if (json_parsed.is()) { - success = json_parsed.as().success(); - } - if (!success) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + if (!parser || !(root.isObject() || root.isArray())) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // Check is root contains `ZbStatus` key, if so change the root - const JsonVariant * zbstatus = &startsWithCaseInsensitive(*json, PSTR("ZbStatus")); - if (nullptr != zbstatus) { - json = zbstatus; + JsonParserToken zbstatus = root.getObject().findStartsWith(PSTR("ZbStatus")); + if (zbstatus) { + root = zbstatus; } // check if the root is an array - if (json->is()) { - const JsonArray& arr = json->as(); - for (auto elt : arr) { + if (root.isArray()) { + JsonParserArray arr = JsonParserArray(root); + for (const auto elt : arr) { // call restore on each item - int32_t res = zigbee_devices.deviceRestore(elt); - if (res < 0) { - ResponseCmndChar_P(PSTR("Restore failed")); - return; + if (elt.isObject()) { + int32_t res = zigbee_devices.deviceRestore(JsonParserObject(elt)); + if (res < 0) { + ResponseCmndChar_P(PSTR("Restore failed")); + return; + } } } - } else if (json->is()) { - int32_t res = zigbee_devices.deviceRestore(*json); + } else if (root.isObject()) { + int32_t res = zigbee_devices.deviceRestore(JsonParserObject(root)); if (res < 0) { ResponseCmndChar_P(PSTR("Restore failed")); return; @@ -1191,6 +1152,15 @@ void CmndZbPermitJoin(void) { buf.add8(duration); buf.add8(0x01); // TC_Significance - This field shall always have a value of 1, indicating a request to change the Trust Center policy. If a frame is received with a value of 0, it shall be treated as having a value of 1. EZ_SendZDO(0xFFFC, ZDO_Mgmt_Permit_Joining_req, buf.buf(), buf.len()); + + // Set Timer after the end of the period, and reset a non-expired previous timer + if (duration > 0) { + // Log pairing mode enabled + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":21,\"Message\":\"Pairing mode enabled\"}}")); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + } + // always register timer for disable, might happen at next tick + zigbee_devices.setTimer(0x0000 /* coordinator */, 0 /* group addr*/, duration * 1000, 0, 0 /* endpoint */, Z_CAT_PERMIT_JOIN, 0 /* value */, &Z_PermitJoinDisable); #endif // USE_ZIGBEE_EZSP ResponseCmndDone(); @@ -1258,31 +1228,20 @@ void CmndZbConfig(void) { // if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } RemoveAllSpaces(XdrvMailbox.data); if (strlen(XdrvMailbox.data) > 0) { - DynamicJsonBuffer jsonBuf; - const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); - if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } - + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // Channel - const JsonVariant &val_channel = GetCaseInsensitive(json, PSTR("Channel")); - if (nullptr != &val_channel) { zb_channel = strToUInt(val_channel); } + + zb_channel = root.getUInt(PSTR("Channel"), zb_channel); + zb_pan_id = root.getUInt(PSTR("PanID"), zb_pan_id); + zb_ext_panid = root.getULong(PSTR("ExtPanID"), zb_ext_panid); + zb_precfgkey_l = root.getULong(PSTR("KeyL"), zb_precfgkey_l); + zb_precfgkey_h = root.getULong(PSTR("KeyH"), zb_precfgkey_h); + zb_txradio_dbm = root.getUInt(PSTR("TxRadio"), zb_txradio_dbm); + if (zb_channel < 11) { zb_channel = 11; } if (zb_channel > 26) { zb_channel = 26; } - // PanID - const JsonVariant &val_pan_id = GetCaseInsensitive(json, PSTR("PanID")); - if (nullptr != &val_pan_id) { zb_pan_id = strToUInt(val_pan_id); } - // ExtPanID - const JsonVariant &val_ext_pan_id = GetCaseInsensitive(json, PSTR("ExtPanID")); - if (nullptr != &val_ext_pan_id) { zb_ext_panid = strtoull(val_ext_pan_id.as(), nullptr, 0); } - // KeyL - const JsonVariant &val_key_l = GetCaseInsensitive(json, PSTR("KeyL")); - if (nullptr != &val_key_l) { zb_precfgkey_l = strtoull(val_key_l.as(), nullptr, 0); } - // KeyH - const JsonVariant &val_key_h = GetCaseInsensitive(json, PSTR("KeyH")); - if (nullptr != &val_key_h) { zb_precfgkey_h = strtoull(val_key_h.as(), nullptr, 0); } - // TxRadio dBm - const JsonVariant &val_txradio = GetCaseInsensitive(json, PSTR("TxRadio")); - if (nullptr != &val_txradio) { zb_txradio_dbm = strToUInt(val_txradio); } - // if network key is zero, we generate a truly random key with a hardware generator from ESP if ((0 == zb_precfgkey_l) && (0 == zb_precfgkey_h)) { AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "generating random Zigbee network key")); @@ -1350,8 +1309,37 @@ extern "C" { return 1; } } -} + +// Convert seconds to a string representing days, hours or minutes present in the n-value. +// The string will contain the most coarse time only, rounded down (61m == 01h, 01h37m == 01h). +// Inputs: +// - n: uint32_t representing some number of seconds +// - result: a buffer of suitable size (7 bytes would represent the entire solution space +// for UINT32_MAX including the trailing null-byte, or "49710d") +// - result_len: A numeric value representing the total length of the result buffer +// Returns: +// - The number of characters that would have been written were result sufficiently large +// - negatve number on encoding error from snprintf +// + int convert_seconds_to_dhm(uint32_t n, char *result, size_t result_len){ + char fmtstr[] = "%02dmhd"; // Don't want this in progmem, because we mutate it. + uint32_t conversions[3] = {24 * 3600, 3600, 60}; + uint32_t value; + for(int i = 0; i < 3; ++i) { + value = n / conversions[i]; + if(value > 0) { + fmtstr[4] = fmtstr[6-i]; + break; + } + n = n % conversions[i]; + } + + // Null-terminate the string at the last "valid" index, removing any excess zero values. + fmtstr[5] = '\0'; + return snprintf(result, result_len, fmtstr, value); + } +} void ZigbeeShow(bool json) { if (json) { @@ -1362,12 +1350,22 @@ void ZigbeeShow(bool json) if (!zigbee_num) { return; } if (zigbee_num > 255) { zigbee_num = 255; } - // Calculate fixed column width for best visual result (Theos opinion) - const uint8_t px_batt = 30; // Battery icon is 20px, add 10px as separator - const uint8_t px_lqi = (strlen(D_LQI) + 4) * 10; // LQI 254 = 70px - WSContentSend_P(PSTR("{t}")); // Terminate current two column table and open new table - WSContentSend_P(PSTR("")); + WSContentSend_P(PSTR( + "" + )); // sort elements by name, then by id uint8_t sorted_idx[zigbee_num]; @@ -1376,44 +1374,69 @@ void ZigbeeShow(bool json) } qsort(sorted_idx, zigbee_num, sizeof(sorted_idx[0]), device_cmp); + uint32_t now = Rtc.utc_time; + for (uint32_t i = 0; i < zigbee_num; i++) { const Z_Device &device = zigbee_devices.devicesAt(sorted_idx[i]); uint16_t shortaddr = device.shortaddr; - { // exxplicit scope to free up stack allocated strings - char *name = (char*) device.friendlyName; - char sdevice[33]; - if (nullptr == name) { - snprintf_P(sdevice, sizeof(sdevice), PSTR(D_DEVICE " 0x%04X"), shortaddr); - name = sdevice; - } + char *name = (char*) device.friendlyName; - char slqi[8]; - snprintf_P(slqi, sizeof(slqi), PSTR("-")); - if (device.validLqi()) { - snprintf_P(slqi, sizeof(slqi), PSTR("%d"), device.lqi); - } - - char sbatt[64]; - snprintf_P(sbatt, sizeof(sbatt), PSTR(" ")); - if (device.validBatteryPercent()) { - snprintf_P(sbatt, sizeof(sbatt), PSTR(""), device.batterypercent, changeUIntScale(device.batterypercent, 0, 100, 0, 14)); - } - - if (!i) { // First row needs style info - WSContentSend_PD(PSTR("%s%s" D_LQI " %s{e}"), - name, px_batt, sbatt, px_lqi, slqi); - } else { // Following rows don't need style info so reducing ajax package - WSContentSend_PD(PSTR("%s%s" D_LQI " %s{e}"), name, sbatt, slqi); - } + char sdevice[33]; + if (nullptr == name) { + snprintf_P(sdevice, sizeof(sdevice), PSTR(D_DEVICE " 0x%04X"), shortaddr); + name = sdevice; } - // Sensor + char sbatt[64]; + snprintf_P(sbatt, sizeof(sbatt), PSTR(" ")); + if (device.validBatteryPercent()) { + snprintf_P(sbatt, sizeof(sbatt), + PSTR(""), + device.batterypercent, changeUIntScale(device.batterypercent, 0, 100, 0, 14) + ); + } + + uint32_t num_bars = 0; + + char slqi[4]; + slqi[0] = '-'; + slqi[1] = '\0'; + if (device.validLqi()){ + num_bars = changeUIntScale(device.lqi, 0, 254, 0, 4); + snprintf_P(slqi, sizeof(slqi), PSTR("%d"), device.lqi); + } + + WSContentSend_PD(PSTR( + "" + "%s" // name + "%s" // sbatt (Battery Indicator) + "
" // slqi + ), name, sbatt, slqi); + + if(device.validLqi()) { + for(uint32_t j = 0; j < 4; ++j) { + WSContentSend_PD(PSTR(""), j, (num_bars < j) ? PSTR(" o30") : PSTR("")); + } + } + char dhm[16]; // len("🕗" + "49710d" + '\0') == 16 + snprintf_P(dhm, sizeof(dhm), PSTR(" ")); + if(device.validLastSeen()){ + snprintf_P(dhm, sizeof(dhm), PSTR("🕗")); + convert_seconds_to_dhm(now - device.last_seen, &dhm[9], 7); + } + + WSContentSend_PD(PSTR( + "
" // Close LQI + "%s{e}" // dhm (Last Seen) + ), dhm ); + + // Sensors bool temperature_ok = device.validTemperature(); bool humidity_ok = device.validHumidity(); bool pressure_ok = device.validPressure(); if (temperature_ok || humidity_ok || pressure_ok) { - WSContentSend_P(PSTR("┆")); + WSContentSend_P(PSTR("┆")); if (temperature_ok) { char buf[12]; dtostrf(device.temperature / 10.0f, 3, 1, buf); @@ -1425,6 +1448,7 @@ void ZigbeeShow(bool json) if (pressure_ok) { WSContentSend_P(PSTR(" ⛅ %d hPa"), device.pressure); } + WSContentSend_P(PSTR("{e}")); } @@ -1433,7 +1457,7 @@ void ZigbeeShow(bool json) if (power_ok) { uint8_t channels = device.getLightChannels(); if (0xFF == channels) { channels = 5; } // if number of channel is unknown, display all known attributes - WSContentSend_P(PSTR("┆ %s"), device.getPower() ? PSTR(D_ON) : PSTR(D_OFF)); + WSContentSend_P(PSTR("┆ %s"), device.getPower() ? PSTR(D_ON) : PSTR(D_OFF)); if (device.validDimmer() && (channels >= 1)) { WSContentSend_P(PSTR(" 🔅 %d%%"), changeUIntScale(device.dimmer,0,254,0,100)); } @@ -1460,6 +1484,7 @@ void ZigbeeShow(bool json) WSContentSend_P(PSTR(" %dW"), device.mains_power); } } + WSContentSend_P(PSTR("{e}")); } } diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 19e89f113..44bbb5429 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -321,7 +321,6 @@ void ShutterInit(void) } ShutterLimitRealAndTargetPositions(i); Settings.shutter_accuracy = 1; - } } @@ -350,9 +349,7 @@ void ShutterReportPosition(bool always, uint32_t index) if (always || (rules_flag.shutter_moving)) { MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER)); // RulesProcess() now re-entry protected } - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: rules_flag.shutter_moving: %d, moved %d"), rules_flag.shutter_moving, rules_flag.shutter_moved); - } void ShutterLimitRealAndTargetPositions(uint32_t i) { @@ -398,7 +395,7 @@ void ShutterDecellerateForStop(uint8_t i) case SHT_PWM_VALUE: case SHT_COUNTER: int16_t missing_steps; - Shutter[i].accelerator = -(ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1) *11/10); + Shutter[i].accelerator = -(ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>4 ? (Shutter[i].motordelay*11)/10 : 4) ); while (Shutter[i].pwm_velocity > -2*Shutter[i].accelerator ) { AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter[i].pwm_velocity, Shutter[i].accelerator ); //Shutter[i].pwm_velocity = tmax(Shutter[i].pwm_velocity-Shutter[i].accelerator , 0); @@ -430,7 +427,7 @@ void ShutterDecellerateForStop(uint8_t i) } void ShutterPowerOff(uint8_t i) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Shutter %d .."), i); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Shutter %d. Switchmode %d"), i,Shutter[i].switch_mode); ShutterDecellerateForStop(i); if (Shutter[i].direction !=0) { Shutter[i].direction = 0; @@ -474,21 +471,15 @@ void ShutterUpdatePosition(void) char scommand[CMDSZ]; char stopic[TOPSZ]; - for (uint32_t i = 0; i < shutters_present; i++) { if (Shutter[i].direction != 0) { - - // Calculate position with counter. Much more accurate and no need for motordelay workaround - // adding some steps to stop early - //Shutter[i].real_position = ShutterCalculatePosition(i); if (!ShutterGlobal.start_reported) { ShutterReportPosition(true, i); XdrvRulesProcess(); ShutterGlobal.start_reported = 1; } - //ShutterCalculateAccelerator(i); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, current_stop_way %d,vel_vur %d, vel_max %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, velocity_change_per_step_max %d"),Shutter[i].time,toBeAcc,current_stop_way, - Shutter[i].pwm_velocity,velocity_max, Shutter[i].accelerator,min_runtime_ms,Shutter[i].real_position, next_possible_stop_position,Shutter[i].target_position,velocity_change_per_step_max); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, current_stop_way %d,vel_cur %d, vel_max %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, max_vel_change %d, dir: %d"),Shutter[i].time,toBeAcc,current_stop_way, + Shutter[i].pwm_velocity,velocity_max, Shutter[i].accelerator,min_runtime_ms,Shutter[i].real_position, next_possible_stop_position,Shutter[i].target_position,velocity_change_per_step_max,Shutter[i].direction); if ( Shutter[i].real_position * Shutter[i].direction >= Shutter[i].target_position * Shutter[i].direction || Shutter[i].pwm_velocity 0 ? 100 : -Shutter[i].close_velocity)); + int32_t temp_realpos = ShutterCalculatePosition(i); XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, i); - //XdrvMailbox.payload = Settings.shuttercoeff[2][i] * 5 > temp_realpos ? temp_realpos / Settings.shuttercoeff[2][i] : (temp_realpos-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i]; last_source = SRC_WEBGUI; CmndShutterPosition(); } else { diff --git a/tasmota/xdrv_29_deepsleep.ino b/tasmota/xdrv_29_deepsleep.ino index 84b30f740..bb1a7744c 100644 --- a/tasmota/xdrv_29_deepsleep.ino +++ b/tasmota/xdrv_29_deepsleep.ino @@ -130,7 +130,7 @@ void DeepSleepPrepare(void) Response_P(PSTR("{\"" D_PRFX_DEEPSLEEP "\":{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%d}}"), (char*)dt.c_str(), RtcSettings.nextwakeup); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_STATUS)); -// Response_P(S_OFFLINE); +// Response_P(S_LWT_OFFLINE); // MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic } diff --git a/tasmota/xdrv_39_thermostat.ino b/tasmota/xdrv_39_thermostat.ino index 8e863822d..4a29ad36b 100644 --- a/tasmota/xdrv_39_thermostat.ino +++ b/tasmota/xdrv_39_thermostat.ino @@ -1328,17 +1328,14 @@ void ThermostatDebug(uint8_t ctr_output) #endif // DEBUG_THERMOSTAT void ThermostatGetLocalSensor(uint8_t ctr_output) { - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject((const char*)mqtt_data); - if (root.success()) { - const char* value_c = root[THERMOSTAT_SENSOR_NAME]["Temperature"]; - if (value_c != NULL && strlen(value_c) > 0 && (isdigit(value_c[0]) || (value_c[0] == '-' && isdigit(value_c[1])) ) ) { - int16_t value; + JsonParser parser(mqtt_data); + JsonParserObject root = parser.getRootObject(); + if (root) { + JsonParserToken value_token = root[PSTR(THERMOSTAT_SENSOR_NAME)].getObject()[PSTR("Temperature")]; + if (value_token.isNum()) { + int16_t value = value_token.getFloat() * 10; if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { - value = (int16_t)ThermostatFahrenheitToCelsius((int32_t)(CharToFloat(value_c) * 10), TEMP_CONV_ABSOLUTE); - } - else { - value = (int16_t)(CharToFloat(value_c) * 10); + value = ThermostatFahrenheitToCelsius(value, TEMP_CONV_ABSOLUTE); } if ( (value >= -1000) && (value <= 1000) diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/xdrv_40_telegram.ino index 92fca7c90..9834d3b30 100644 --- a/tasmota/xdrv_40_telegram.ino +++ b/tasmota/xdrv_40_telegram.ino @@ -76,7 +76,7 @@ bool TelegramInit(void) { if (!telegramClient) { telegramClient = new BearSSL::WiFiClientSecure_light(tls_rx_size, tls_tx_size); #ifdef USE_MQTT_TLS_CA_CERT - telegramClient->setTrustAnchor(&GoDaddyCAG2_TA); + telegramClient->setTrustAnchor(&GoDaddyCAG2_TA, 1); #else telegramClient->setPubKeyFingerprint(Telegram_Fingerprint, Telegram_Fingerprint, false); // check server fingerprint #endif @@ -225,15 +225,22 @@ void TelegramAnalizeMessage(void) { for (uint32_t i = 1; i < Telegram.message[0][0].toInt() +1; i++) { Telegram.message[i][5] = ""; - DynamicJsonBuffer jsonBuffer; - JsonObject &root = jsonBuffer.parseObject(Telegram.message[i][0]); - if (root.success()) { - Telegram.message[i][0] = root["update_id"].as(); - Telegram.message[i][1] = root["message"]["from"]["id"].as(); - Telegram.message[i][2] = root["message"]["from"]["first_name"].as(); - Telegram.message[i][3] = root["message"]["from"]["last_name"].as(); - Telegram.message[i][4] = root["message"]["chat"]["id"].as(); - Telegram.message[i][5] = root["message"]["text"].as(); + String buf = Telegram.message[i][0]; // we need to keep a copy of the buffer + JsonParser parser((char*)buf.c_str()); + JsonParserObject root = parser.getRootObject(); + if (root) { + Telegram.message[i][0] = root["update_id"].getStr(); + Telegram.message[i][1] = root["message"].getObject()["from"].getObject()["id"].getStr(); + Telegram.message[i][2] = root["message"].getObject()["from"].getObject()["first_name"].getStr(); + Telegram.message[i][3] = root["message"].getObject()["from"].getObject()["last_name"].getStr(); + Telegram.message[i][4] = root["message"].getObject()["chat"].getObject()["id"].getStr(); + Telegram.message[i][5] = root["message"].getObject()["text"].getStr(); + // Telegram.message[i][0] = root["update_id"].as(); + // Telegram.message[i][1] = root["message"]["from"]["id"].as(); + // Telegram.message[i][2] = root["message"]["from"]["first_name"].as(); + // Telegram.message[i][3] = root["message"]["from"]["last_name"].as(); + // Telegram.message[i][4] = root["message"]["chat"]["id"].as(); + // Telegram.message[i][5] = root["message"]["text"].as(); } int id = Telegram.message[Telegram.message[0][0].toInt()][0].toInt() +1; diff --git a/tasmota/xdrv_42_i2s_audio.ino b/tasmota/xdrv_42_i2s_audio.ino index 5a31d832b..4a3bd91b0 100644 --- a/tasmota/xdrv_42_i2s_audio.ino +++ b/tasmota/xdrv_42_i2s_audio.ino @@ -54,11 +54,6 @@ AudioFileSourceID3 *id3; AudioGeneratorMP3 *decoder = NULL; void *mp3ram = NULL; -#ifdef USE_WEBRADIO -AudioFileSourceICYStream *ifile = NULL; -AudioFileSourceBuffer *buff = NULL; -char wr_title[64]; -//char status[64]; #ifdef ESP8266 const int preallocateBufferSize = 5*1024; @@ -69,6 +64,12 @@ const int preallocateCodecSize = 29192; // MP3 codec max mem needed //const int preallocateCodecSize = 85332; // AAC+SBR codec max mem needed #endif +#ifdef USE_WEBRADIO +AudioFileSourceICYStream *ifile = NULL; +AudioFileSourceBuffer *buff = NULL; +char wr_title[64]; +//char status[64]; + void *preallocateBuffer = NULL; void *preallocateCodec = NULL; uint32_t retryms = 0; diff --git a/tasmota/xdrv_83_esp32watch.ino b/tasmota/xdrv_83_esp32watch.ino index 2f759aa9a..6a58974c0 100644 --- a/tasmota/xdrv_83_esp32watch.ino +++ b/tasmota/xdrv_83_esp32watch.ino @@ -64,6 +64,7 @@ struct TTGO_globs { bool bma_double_click = false; bool bma_click = false; bool bma_button = false; + bool power_ok = false; } ttgo_globs; @@ -115,15 +116,19 @@ void TTGO_Init(void) { ttgo_globs.bma->enableWakeupInterrupt(true); ttgo_globs.bma->enableAnyNoMotionInterrupt(true); ttgo_globs.bma->enableAccel(); -#endif +#endif // USE_BMA423 } void initPower(void) { int ret = ttgo_globs.ttgo_power->begin(axpReadBytes, axpWriteBytes); if (ret == AXP_FAIL) { //DBGX("AXP Power begin failed"); + // Serial.printf("AXP202 failed\n" ); } else { I2cSetActiveFound(AXP202_SLAVE_ADDRESS, "AXP202"); + ttgo_globs.power_ok = true; + // Serial.printf("AXP202 OK\n" ); + //Change the button boot time to 4 seconds ttgo_globs.ttgo_power->setShutdownTime(AXP_POWER_OFF_TIME_4S); // Turn off the charging instructions, there should be no @@ -162,6 +167,7 @@ void initPower(void) { portYIELD_FROM_ISR (); } }, FALLING); + } } @@ -205,6 +211,8 @@ const char HTTP_TTGO_BMA[] PROGMEM = void TTGO_WebShow(uint32_t json) { + if (ttgo_globs.power_ok == false) return; + TTGO_GetADC(); char vstring[32]; @@ -276,8 +284,9 @@ int32_t ttgo_sleeptime; ttgo_sleeptime = stime; +#ifdef USE_DISPLAY DisplayOnOff(0); - +#endif if (ttgo_sleeptime>=0) { // ligh sleep mode WifiShutdown(); @@ -311,7 +320,9 @@ int32_t ttgo_sleeptime; if (ttgo_sleeptime) { ttgo_globs.lenergy = false; rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M); +#ifdef USE_DISPLAY DisplayOnOff(1); +#endif } else { while (ttgo_globs.lenergy == true) { TTGO_loop(0); @@ -332,7 +343,9 @@ uint8_t data; if (ttgo_globs.lenergy) { ttgo_globs.lenergy = false; rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M); +#ifdef USE_DISPLAY DisplayOnOff(1); +#endif } #ifdef USE_BMA423 diff --git a/tasmota/xnrg_07_ade7953.ino b/tasmota/xnrg_07_ade7953.ino index 3d7b405d5..beaea9635 100644 --- a/tasmota/xnrg_07_ade7953.ino +++ b/tasmota/xnrg_07_ade7953.ino @@ -199,7 +199,8 @@ void Ade7953EnergyEverySecond(void) void Ade7953DrvInit(void) { - if (PinUsed(GPIO_ADE7953_IRQ)) { // Irq on GPIO16 is not supported... + if (PinUsed(GPIO_ADE7953_IRQ)) { // Irq on GPIO16 is not supported... + pinMode(Pin(GPIO_ADE7953_IRQ), INPUT); // Related to resetPins() - Must be set to input delay(100); // Need 100mS to init ADE7953 if (I2cSetDevice(ADE7953_ADDR)) { if (HLW_PREF_PULSE == Settings.energy_power_calibration) { diff --git a/tasmota/xsns_05_ds18x20.ino b/tasmota/xsns_05_ds18x20.ino index 361847932..e828c55d6 100644 --- a/tasmota/xsns_05_ds18x20.ino +++ b/tasmota/xsns_05_ds18x20.ino @@ -17,6 +17,7 @@ along with this program. If not, see . */ +#ifdef ESP8266 #ifdef USE_DS18x20 /*********************************************************************************************\ * DS18B20 - Temperature - Multiple sensors @@ -547,3 +548,4 @@ bool Xsns05(uint8_t function) } #endif // USE_DS18x20 +#endif // ESP8266 diff --git a/tasmota/xsns_05_ds18x20_esp32.ino b/tasmota/xsns_05_ds18x20_esp32.ino new file mode 100644 index 000000000..1d622448c --- /dev/null +++ b/tasmota/xsns_05_ds18x20_esp32.ino @@ -0,0 +1,254 @@ +/* + xsns_05_ds18x20_esp32.ino - DS18x20 temperature sensor support for Tasmota + + Copyright (C) 2020 Heiko Krupp 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_DS18x20 +/*********************************************************************************************\ + * DS18B20 - Temperature - Multiple sensors +\*********************************************************************************************/ + +#define XSNS_05 5 + +#define DS18S20_CHIPID 0x10 // +/-0.5C 9-bit +#define DS1822_CHIPID 0x22 // +/-2C 12-bit +#define DS18B20_CHIPID 0x28 // +/-0.5C 12-bit +#define MAX31850_CHIPID 0x3B // +/-0.25C 14-bit + +#define W1_SKIP_ROM 0xCC +#define W1_CONVERT_TEMP 0x44 +#define W1_READ_SCRATCHPAD 0xBE + +#define DS18X20_MAX_SENSORS 8 + +const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850"; + +uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID }; + +uint8_t ds18x20_address[DS18X20_MAX_SENSORS][8]; +uint8_t ds18x20_index[DS18X20_MAX_SENSORS]; +uint8_t ds18x20_valid[DS18X20_MAX_SENSORS]; +uint8_t ds18x20_sensors = 0; +char ds18x20_types[12]; + +/********************************************************************************************/ + +#include + +OneWire *ds = nullptr; + +void Ds18x20Init(void) { + ds = new OneWire(Pin(GPIO_DSB)); + + Ds18x20Search(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); +} + +void Ds18x20Search(void) { + uint8_t num_sensors=0; + uint8_t sensor = 0; + + ds->reset_search(); + for (num_sensors = 0; num_sensors < DS18X20_MAX_SENSORS; num_sensors) { + if (!ds->search(ds18x20_address[num_sensors])) { + ds->reset_search(); + break; + } + // If CRC Ok and Type DS18S20, DS1822, DS18B20 or MAX31850 + if ((OneWire::crc8(ds18x20_address[num_sensors], 7) == ds18x20_address[num_sensors][7]) && + ((ds18x20_address[num_sensors][0]==DS18S20_CHIPID) || + (ds18x20_address[num_sensors][0]==DS1822_CHIPID) || + (ds18x20_address[num_sensors][0]==DS18B20_CHIPID) || + (ds18x20_address[num_sensors][0]==MAX31850_CHIPID))) { + num_sensors++; + } + } + for (uint32_t i = 0; i < num_sensors; i++) { + ds18x20_index[i] = i; + } + for (uint32_t i = 0; i < num_sensors; i++) { + for (uint32_t j = i + 1; j < num_sensors; j++) { + if (uint32_t(ds18x20_address[ds18x20_index[i]]) > uint32_t(ds18x20_address[ds18x20_index[j]])) { + std::swap(ds18x20_index[i], ds18x20_index[j]); + } + } + } + ds18x20_sensors = num_sensors; +} + +void Ds18x20Convert(void) { + ds->reset(); + ds->write(W1_SKIP_ROM); // Address all Sensors on Bus + ds->write(W1_CONVERT_TEMP); // start conversion, no parasite power on at the end +// delay(750); // 750ms should be enough for 12bit conv +} + +bool Ds18x20Read(uint8_t sensor, float &t) +{ + uint8_t data[12]; + int8_t sign = 1; + + t = NAN; + + uint8_t index = ds18x20_index[sensor]; + if (ds18x20_valid[index]) { ds18x20_valid[index]--; } + + ds->reset(); + ds->select(ds18x20_address[index]); + ds->write(W1_READ_SCRATCHPAD); // Read Scratchpad + + for (uint32_t i = 0; i < 9; i++) { + data[i] = ds->read(); + } + if (OneWire::crc8(data, 8) == data[8]) { + switch(ds18x20_address[index][0]) { + case DS18S20_CHIPID: { + int16_t tempS = (((data[1] << 8) | (data[0] & 0xFE)) << 3) | ((0x10 - data[6]) & 0x0F); + t = ConvertTemp(tempS * 0.0625 - 0.250); + ds18x20_valid[index] = SENSOR_MAX_MISS; + return true; + } + case DS1822_CHIPID: + case DS18B20_CHIPID: { + uint16_t temp12 = (data[1] << 8) + data[0]; + if (temp12 > 2047) { + temp12 = (~temp12) +1; + sign = -1; + } + t = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16 + ds18x20_valid[index] = SENSOR_MAX_MISS; + return true; + } + case MAX31850_CHIPID: { + int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); + t = ConvertTemp(temp14 * 0.0625); // Divide by 16 + ds18x20_valid[index] = SENSOR_MAX_MISS; + return true; + } + } + } + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); + return false; +} + +void Ds18x20Name(uint8_t sensor) +{ + uint8_t index = sizeof(ds18x20_chipids); + while (index) { + if (ds18x20_address[ds18x20_index[sensor]][0] == ds18x20_chipids[index]) { + break; + } + index--; + } + GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types); + if (ds18x20_sensors > 1) { + snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s%c%d"), ds18x20_types, IndexSeparator(), sensor +1); + } +} + +/********************************************************************************************/ + +void Ds18x20EverySecond(void) +{ + if (!ds18x20_sensors) { return; } + + if (uptime & 1) { + // 2mS +// Ds18x20Search(); // Check for changes in sensors number + Ds18x20Convert(); // Start Conversion, takes up to one second + } else { + float t; + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + // 12mS per device + if (!Ds18x20Read(i, t)) { // Read temperature + Ds18x20Name(i); + AddLogMissed(ds18x20_types, ds18x20_valid[ds18x20_index[i]]); + } + } + } +} + +void Ds18x20Show(bool json) +{ + float t; + + uint8_t dsxflg = 0; + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + if (Ds18x20Read(i, t)) { // Check if read failed + char temperature[33]; + dtostrfd(t, Settings.flag2.temperature_resolution, temperature); + + Ds18x20Name(i); + + if (json) { + char address[17]; + for (uint32_t j = 0; j < 6; j++) { + sprintf(address+2*j, "%02X", ds18x20_address[ds18x20_index[i]][6-j]); // Skip sensor type and crc + } + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, address, temperature); + dsxflg++; +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (1 == dsxflg)) { + DomoticzSensor(DZ_TEMP, temperature); + } +#endif // USE_DOMOTICZ +#ifdef USE_KNX + if ((0 == tele_period) && (1 == dsxflg)) { + KnxSensor(KNX_TEMPERATURE, t); + } +#endif // USE_KNX +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, ds18x20_types, temperature, TempUnit()); +#endif // USE_WEBSERVER + } + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns05(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_DSB)) { + switch (function) { + case FUNC_INIT: + Ds18x20Init(); + break; + case FUNC_EVERY_SECOND: + Ds18x20EverySecond(); + break; + case FUNC_JSON_APPEND: + Ds18x20Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ds18x20Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_DS18x20 +#endif // ESP32 diff --git a/tasmota/xsns_16_tsl2561.ino b/tasmota/xsns_16_tsl2561.ino index 99c243329..573fc3b93 100644 --- a/tasmota/xsns_16_tsl2561.ino +++ b/tasmota/xsns_16_tsl2561.ino @@ -37,6 +37,8 @@ Tsl2561 Tsl(Wire); uint8_t tsl2561_type = 0; uint8_t tsl2561_valid = 0; uint32_t tsl2561_milliLux = 0; +uint32_t tsl2561_full = 0; +uint32_t tsl2561_ir = 0; char tsl2561_types[] = "TSL2561"; bool Tsl2561Read(void) @@ -47,15 +49,18 @@ bool Tsl2561Read(void) bool gain; Tsl2561::exposure_t exposure; uint16_t scaledFull, scaledIr; - uint32_t full, ir; if (Tsl.on()) { if (Tsl.id(id) && Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr) - && Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr) - && Tsl2561Util::milliLux(full, ir, tsl2561_milliLux, Tsl2561::packageCS(id))) { + && Tsl2561Util::normalizedLuminosity(gain, exposure, tsl2561_full = scaledFull, tsl2561_ir = scaledIr)) { + if (! Tsl2561Util::milliLux(tsl2561_full, tsl2561_ir, tsl2561_milliLux, Tsl2561::packageCS(id))) { + tsl2561_milliLux = 0; + } } else{ tsl2561_milliLux = 0; + tsl2561_full = 0; + tsl2561_ir = 0; } } tsl2561_valid = SENSOR_MAX_MISS; @@ -94,8 +99,8 @@ void Tsl2561Show(bool json) { if (tsl2561_valid) { if (json) { - ResponseAppend_P(PSTR(",\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"), - tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); + ResponseAppend_P(PSTR(",\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u,\"IR\":%u,\"Broadband\":%u}"), + tsl2561_milliLux / 1000, tsl2561_milliLux % 1000, tsl2561_ir, tsl2561_full); #ifdef USE_DOMOTICZ if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, (tsl2561_milliLux + 500) / 1000); } #endif // USE_DOMOTICZ diff --git a/tasmota/xsns_40_pn532.ino b/tasmota/xsns_40_pn532.ino index 3c29d4c82..8fd4e450d 100644 --- a/tasmota/xsns_40_pn532.ino +++ b/tasmota/xsns_40_pn532.ino @@ -495,7 +495,7 @@ void PN532_ScanForTag(void) #endif // USE_PN532_DATA_FUNCTION #ifdef USE_PN532_DATA_FUNCTION - ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), uids, card_datas); + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"" D_JSON_DATA "\":\"%s\"}}"), uids, card_datas); #else ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), uids); #endif // USE_PN532_DATA_FUNCTION diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino index 44456a6a7..e9d2841ea 100644 --- a/tasmota/xsns_61_MI_NRF24.ino +++ b/tasmota/xsns_61_MI_NRF24.ino @@ -20,7 +20,8 @@ -------------------------------------------------------------------------------------------- Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- - + 0.9.8.1 20200918 integrate - add MHOC303, ATC-custom FW, allow lower case commands + --- 0.9.8.0 20200705 integrate - add YEE-RC, NLIGHT and MJYD2S, add NRFUSE --- 0.9.7.0 20200624 integrate - fix BEARSSL-decryption, remove MBEDTLS, prepare night light sensors @@ -84,8 +85,10 @@ #define MJYD2S 8 #define YEERC 9 #define MHOC401 10 +#define MHOC303 11 +#define ATC 12 -#define MI_TYPES 10 //count this manually +#define MI_TYPES 12 //count this manually #define D_CMND_NRF "NRF" @@ -121,7 +124,9 @@ const uint16_t kMINRFDeviceID[MI_TYPES]={ 0x0098, // Flora 0x03dd, // NLIGHT 0x07f6, // MJYD2S 0x0153, // yee-rc - 0x0387 // MHO-C401 + 0x0387, // MHO-C401 + 0x06d3, // MHO-C303 + 0x0a1c // ATC -> this is a fake ID }; const char kMINRFDeviceType1[] PROGMEM = "Flora"; @@ -134,7 +139,9 @@ const char kMINRFDeviceType7[] PROGMEM = "NLIGHT"; const char kMINRFDeviceType8[] PROGMEM = "MJYD2S"; const char kMINRFDeviceType9[] PROGMEM = "YEERC"; const char kMINRFDeviceType10[] PROGMEM = "MHOC401"; -const char * kMINRFDeviceType[] PROGMEM = {kMINRFDeviceType1,kMINRFDeviceType2,kMINRFDeviceType3,kMINRFDeviceType4,kMINRFDeviceType5,kMINRFDeviceType6,kMINRFDeviceType7,kMINRFDeviceType8,kMINRFDeviceType9,kMINRFDeviceType10}; +const char kMINRFDeviceType11[] PROGMEM = "MHOC303"; +const char kMINRFDeviceType12[] PROGMEM = "ATC"; +const char * kMINRFDeviceType[] PROGMEM = {kMINRFDeviceType1,kMINRFDeviceType2,kMINRFDeviceType3,kMINRFDeviceType4,kMINRFDeviceType5,kMINRFDeviceType6,kMINRFDeviceType7,kMINRFDeviceType8,kMINRFDeviceType9,kMINRFDeviceType10,kMINRFDeviceType11,kMINRFDeviceType12}; // PDU's or different channels 37-39 const uint32_t kMINRFFloPDU[3] = {0x3eaa857d,0xef3b8730,0x71da7b46}; @@ -146,11 +153,13 @@ const uint32_t kMINRFCGGPDU[3] = {0x4760cd6e,0xdbcc0cdb,0x33048dfd}; const uint32_t kMINRFCGDPDU[3] = {0x5da0d752,0xc10c16e7,0x29c497c1}; // const uint32_t kMINRFNLIPDU[3] = {0x4760C56E,0xDBCC04DB,0x0330485FD}; //NLIGHT const uint32_t kMINRFYRCPDU[3] = {0x216D63E2,0x5C3DD47E,0x0A5D0E96}; //yee-rc - 50 30 +const uint32_t kMINRFATCPDU[3] = {0xA6E4D00A,0xD0CDAD5A,0x8B03FB3A}; //ATC // start-LSFR for different channels 37-39 const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; // Flora, LYWSD02 const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; // MJ_HT_V1, LYWSD03, CGx const uint8_t kMINRFlsfrList_C[3] = {0x38,0x25,0x2e}; // yee-rc +const uint8_t kMINRFlsfrList_D[3] = {0x26,0x23,0x20}; // ATC #pragma pack(1) // important!! @@ -224,6 +233,15 @@ struct mjysd02_Packet_t{ uint8_t data[18]; }; +struct ATCPacket_t{ + uint8_t MAC[6]; + int16_t temp; //sadly this is in wrong endianess + uint8_t hum; + uint8_t batPer; + uint16_t batMV; + uint8_t frameCnt; +}; + union mi_bindKey_t{ struct{ uint8_t key[16]; @@ -363,7 +381,7 @@ bool MINRFinitBLE(uint8_t _mode) MINRFchangePacketModeTo(_mode); return true; } - // DEBUG_SENSOR_LOG(PSTR("MINRF chip NOT !!!! connected")); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF chip NOT !!!! connected")); return false; } @@ -406,35 +424,38 @@ bool MINRFreceivePacket(void) MINRFswapbuf((uint8_t*)&MINRF.buffer, sizeof(MINRF.buffer) ); // MINRF_LOG_BUFFER(); - // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: _lsfrlist: %x, chan: %u, mode: %u"),_lsfrlist[MINRF.currentChan],MINRF.currentChan, MINRF.packetMode); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: _lsfrlist: %x, chan: %u, mode: %u"),_lsfrlist[MINRF.currentChan],MINRF.currentChan, MINRF.packetMode); switch (MINRF.packetMode) { - case 0: case 7: case 8: + case 0: case NLIGHT: case MJYD2S: MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); // "BEACON" mode, "NLIGHT" mode, "MJYD2S" mode break; - case 1: case 3: + case FLORA: case LYWSD02: case MHOC303: MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); // "flora" mode, "LYWSD02" mode break; - case 2: case 4: case 5: case 6: case MHOC401: + case MJ_HT_V1: case LYWSD03: case CGG1: case CGD1: case MHOC401: MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); // "MJ_HT_V1" mode, LYWSD03" mode, "CGG1" mode, "CGD1" mode break; - case 9: + case YEERC: MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_C[MINRF.currentChan]); // "YEE-RC" mode break; + case ATC: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_D[MINRF.currentChan]); // ATC + break; } - // DEBUG_SENSOR_LOG(PSTR("MINRF: LSFR:%x"),_lsfr); + // DEBUG_SENSOR_LOG(PSTR("NRF: LSFR:%x"),_lsfr); // if (_lsfr>254) _lsfr=0; } - // DEBUG_SENSOR_LOG(PSTR("MINRF: did read FIFO")); + // DEBUG_SENSOR_LOG(PSTR("NRF: did read FIFO")); return true; } // #ifdef DEBUG_TASMOTA_SENSOR void MINRFshowBuffer(uint8_t (&buf)[32]){ // we use this only for the 32-byte-FIFO-buffer, so 32 is hardcoded - // DEBUG_SENSOR_LOG(PSTR("MINRF: Buffer: %c %c %c %c %c %c %c %c" + // DEBUG_SENSOR_LOG(PSTR("NRF: Buffer: %c %c %c %c %c %c %c %c" // " %c %c %c %c %c %c %c %c" // " %c %c %c %c %c %c %c %c" // " %c %c %c %c %c %c %c %c") - DEBUG_SENSOR_LOG(PSTR("MINRF: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x " + DEBUG_SENSOR_LOG(PSTR("NRF: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x ") @@ -511,7 +532,7 @@ void MINRFhandleScan(void){ 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); + if(e.showedUp>2) AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: 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)); }), @@ -524,7 +545,7 @@ void MINRFhandleScan(void){ for(uint32_t i=0; i30) break; uint32_t ADtype = _buf[i+1]; - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Size: %u AD: %x i:%u"), size, ADtype,i); + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Size: %u AD: %x i:%u"), size, ADtype,i); if (size+i>32+offset) size=32-i+offset-2; if (size>30) break; char _stemp[(size*2)]; uint32_t backupSize; switch(ADtype){ case 0x01: - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Flags: %02x"), _buf[i+2]); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Flags: %02x"), _buf[i+2]); break; case 0x02: case 0x03: entry->uuid = _buf[i+3]*256 + _buf[i+2]; - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: UUID: %04x"), entry->uuid); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: UUID: %04x"), entry->uuid); success = true; break; case 0x08: case 0x09: backupSize = _buf[i+size+1]; _buf[i+size+1] = 0; - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Name: %s"), (char*)&_buf[i+2]); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Name: %s"), (char*)&_buf[i+2]); success = true; _buf[i+size+1] = backupSize; break; case 0x0a: - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: TxPow: %02u"), _buf[i+2]); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: TxPow: %02u"), _buf[i+2]); break; case 0xff: entry->cid = _buf[i+3]*256 + _buf[i+2]; - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Cid: %04x"), entry->cid); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Cid: %04x"), entry->cid); ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); success = true; break; case 0x16: entry->svc = _buf[i+3]*256 + _buf[i+2]; - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Svc: %04x"), entry->svc); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Svc: %04x"), entry->svc); ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); success = true; @@ -712,7 +733,7 @@ int MINRFdecryptPacket(char *_buf){ br_ccm_run(&ctx, 0, output, sizeof(packet->payload.cipher)); ret = br_ccm_check_tag(&ctx, packet->payload.tag); - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("BEARSSL: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, output[0],output[1],output[2],output[3],output[4]); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, output[0],output[1],output[2],output[3],output[4]); memcpy((uint8_t*)(packet->payload.cipher)+1,output,sizeof(packet->payload.cipher)); return ret; } @@ -833,6 +854,7 @@ void MINRFAddKey(char* payload){ */ void MINRFKeyMACStringToBytes(char* _string,uint8_t _keyMAC[]) { //uppercase uint32_t index = 0; + UpperCase(_string,_string); while (index < 44) { char c = _string[index]; uint8_t value = 0; @@ -843,9 +865,9 @@ void MINRFKeyMACStringToBytes(char* _string,uint8_t _keyMAC[]) { //uppercase _keyMAC[(index/2)] += value << (((index + 1) % 2) * 4); index++; } - DEBUG_SENSOR_LOG(PSTR("MINRF: %s to:"),_string); - DEBUG_SENSOR_LOG(PSTR("MINRF: key-array: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"),_keyMAC[0],_keyMAC[1],_keyMAC[2],_keyMAC[3],_keyMAC[4],_keyMAC[5],_keyMAC[6],_keyMAC[7],_keyMAC[8],_keyMAC[9],_keyMAC[10],_keyMAC[11],_keyMAC[12],_keyMAC[13],_keyMAC[14],_keyMAC[15]); - DEBUG_SENSOR_LOG(PSTR("MINRF: MAC-array: %02X%02X%02X%02X%02X%02X"),_keyMAC[16],_keyMAC[17],_keyMAC[18],_keyMAC[19],_keyMAC[20],_keyMAC[21]); + DEBUG_SENSOR_LOG(PSTR("NRF: %s to:"),_string); + DEBUG_SENSOR_LOG(PSTR("NRF: key-array: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"),_keyMAC[0],_keyMAC[1],_keyMAC[2],_keyMAC[3],_keyMAC[4],_keyMAC[5],_keyMAC[6],_keyMAC[7],_keyMAC[8],_keyMAC[9],_keyMAC[10],_keyMAC[11],_keyMAC[12],_keyMAC[13],_keyMAC[14],_keyMAC[15]); + DEBUG_SENSOR_LOG(PSTR("NRF: MAC-array: %02X%02X%02X%02X%02X%02X"),_keyMAC[16],_keyMAC[17],_keyMAC[18],_keyMAC[19],_keyMAC[20],_keyMAC[21]); } #endif //USE_MI_DECRYPTION /** @@ -856,6 +878,7 @@ void MINRFKeyMACStringToBytes(char* _string,uint8_t _keyMAC[]) { //uppercase */ void MINRFMACStringToBytes(char* _string, uint8_t _MAC[]) { //uppercase uint32_t index = 0; + UpperCase(_string,_string); while (index < 12) { char c = _string[index]; uint8_t value = 0; @@ -866,7 +889,7 @@ void MINRFMACStringToBytes(char* _string, uint8_t _MAC[]) { //uppercase _MAC[(index/2)] += value << (((index + 1) % 2) * 4); index++; } - // DEBUG_SENSOR_LOG(PSTR("MINRF: %s to MAC-array: %02X%02X%02X%02X%02X%02X"),_string,_MAC[0],_MAC[1],_MAC[2],_MAC[3],_MAC[4],_MAC[5]); + // DEBUG_SENSOR_LOG(PSTR("NRF: %s to MAC-array: %02X%02X%02X%02X%02X%02X"),_string,_MAC[0],_MAC[1],_MAC[2],_MAC[3],_MAC[4],_MAC[5]); } /** @@ -876,7 +899,7 @@ void MINRFMACStringToBytes(char* _string, uint8_t _MAC[]) { //uppercase void MINRFcomputefirstUsedPacketMode(void){ for (uint32_t i = 0; iMI_TYPES) MINRF.firstUsedPacketMode=0; break; @@ -913,34 +936,37 @@ void MINRFchangePacketModeTo(uint8_t _mode) { case 0: // normal BLE advertisement NRF24radio.openReadingPipe(0,0x6B7D9171); // advertisement address: 0x8E89BED6 (bit-reversed -> 0x6B7D9171) break; - case 1: // special flora packet + case FLORA: // special flora packet NRF24radio.openReadingPipe(0,kMINRFFloPDU[_nextchannel]); // 95 fe 71 20 -> flora break; - case 2: // special MJ_HT_V1 packet + case MJ_HT_V1: // special MJ_HT_V1 packet NRF24radio.openReadingPipe(0,kMINRFMJPDU[_nextchannel]); // 95 fe 50 20 -> MJ_HT_V1 break; - case 3: // special LYWSD02 packet + case LYWSD02: case MHOC303: // special LYWSD02 packet NRF24radio.openReadingPipe(0,kMINRFL2PDU[_nextchannel]);// 95 fe 70 20 -> LYWSD02 break; - case 4: case MHOC401: // special LYWSD03 packet, MHOC401 has the same + case LYWSD03: case MHOC401: // special LYWSD03 packet, MHOC401 has the same NRF24radio.openReadingPipe(0,kMINRFL3PDU[_nextchannel]);// 95 fe 58 58 -> LYWSD03 (= encrypted data message) break; - case 5: // special CGG1 packet + case CGG1: // special CGG1 packet NRF24radio.openReadingPipe(0,kMINRFCGGPDU[_nextchannel]); // 95 fe 50 30 -> CGG1 break; - case 6: // special CGD1 packet + case CGD1: // special CGD1 packet NRF24radio.openReadingPipe(0,kMINRFCGDPDU[_nextchannel]); // cd fd 08 0c -> CGD1 break; - case 7: case 8:// MAC based LIGHT packet + case NLIGHT: case MJYD2S:// MAC based LIGHT packet if (MIBLElights.size()==0) break; NRF24radio.openReadingPipe(0,MIBLElights[MINRF.activeLight].PDU[_nextchannel]); // computed from MAC -> NLIGHT and MJYSD2S MINRF.activeLight++; break; - case 9: // YEE-RC packet + case YEERC: // YEE-RC packet NRF24radio.openReadingPipe(0,kMINRFYRCPDU[_nextchannel]);// 95 fe 50 30 -> YEE-RC break; + case ATC: + NRF24radio.openReadingPipe(0,kMINRFATCPDU[_nextchannel]);// 10 16 1a 18 -> ATC + break; } - // DEBUG_SENSOR_LOG(PSTR("MINRF: Change Mode to %u"),_mode); + // DEBUG_SENSOR_LOG(PSTR("NRF: Change Mode to %u"),_mode); MINRF.packetMode = _mode; } @@ -953,27 +979,27 @@ void MINRFchangePacketModeTo(uint8_t _mode) { */ uint32_t MINRFgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type){ - DEBUG_SENSOR_LOG(PSTR("MINRF: will test ID-type: %x"), _type); + DEBUG_SENSOR_LOG(PSTR("NRF: will test ID-type: %x"), _type); bool _success = false; for (uint32_t i=0;itype,MINRF.buffer.miBeacon.type); + DEBUG_SENSOR_LOG(PSTR("NRF: %u %u %u"),_slot,_sensorVec->type,MINRF.buffer.miBeacon.type); float _tempFloat; int decryptRet; @@ -1082,7 +1108,7 @@ void MINRFhandleMiBeaconPacket(void){ switch(MINRF.buffer.miBeacon.type){ case 0x1: if(MINRF.buffer.miBeacon.counter==_sensorVec->lastCnt) break; - // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: YEE-RC button: %u Long: %u"), MINRF.buffer.miBeacon.Btn.num, MINRF.buffer.miBeacon.Btn.longPress); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: YEE-RC button: %u Long: %u"), MINRF.buffer.miBeacon.Btn.num, MINRF.buffer.miBeacon.Btn.longPress); _sensorVec->lastCnt=MINRF.buffer.miBeacon.counter; _sensorVec->btn=MINRF.buffer.miBeacon.Btn.num + (MINRF.buffer.miBeacon.Btn.longPress/2)*6; _sensorVec->shallSendMQTT = 1; @@ -1154,7 +1180,7 @@ void MINRFhandleMiBeaconPacket(void){ void MINRFhandleCGD1Packet(void){ // no MiBeacon MINRFreverseMAC(MINRF.buffer.CGDPacket.MAC); uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.CGDPacket.MAC, 0x0576); // This must be hard-coded, no object-id in Cleargrass-packet - DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); + DEBUG_SENSOR_LOG(PSTR("NRF: Sensor slot: %u"), _slot); if(_slot==0xff) return; switch (MINRF.buffer.CGDPacket.mode){ @@ -1179,7 +1205,7 @@ void MINRFhandleCGD1Packet(void){ // no MiBeacon } break; default: - DEBUG_SENSOR_LOG(PSTR("MINRF: unexpected CGD1-packet")); + DEBUG_SENSOR_LOG(PSTR("NRF: unexpected CGD1-packet")); MINRF_LOG_BUFFER(MINRF.buffer.raw); } } @@ -1188,10 +1214,10 @@ void MINRFhandleNlightPacket(void){ // no MiBeacon uint32_t offset = 6; uint8_t _buf[32+offset]; MINRFrecalcBuffer((uint8_t*)&_buf,offset); - // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6],_buf[7],_buf[8],_buf[9],_buf[10],_buf[11],_buf[12],_buf[13],_buf[14],_buf[15],_buf[16],_buf[17],_buf[18]); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6],_buf[7],_buf[8],_buf[9],_buf[10],_buf[11],_buf[12],_buf[13],_buf[14],_buf[15],_buf[16],_buf[17],_buf[18]); uint32_t _frame_PID = _buf[15]<<24 | _buf[16]<<16 | _buf[17]<<8 | _buf[18]; if(_frame_PID!=0x4030dd03) return; // invalid packet - // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT:%x"),_frame_PID); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT:%x"),_frame_PID); uint32_t _idx = MINRF.activeLight-1; if((millis() - MIBLElights[_idx].lastTime)<1500) return; if(_buf[19]!=MIBLElights[_idx].lastCnt){ @@ -1199,7 +1225,7 @@ void MINRFhandleNlightPacket(void){ // no MiBeacon MIBLElights[_idx].events++; MIBLElights[_idx].shallSendMQTT = 1; MIBLElights[_idx].lastTime = millis(); - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: NLIGHT %u: events: %u, Cnt:%u"), _idx,MIBLElights[_idx].events, MIBLElights[_idx].lastCnt); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: NLIGHT %u: events: %u, Cnt:%u"), _idx,MIBLElights[_idx].events, MIBLElights[_idx].lastCnt); } } @@ -1209,20 +1235,20 @@ void MINRFhandleMJYD2SPacket(void){ // no MiBeacon MINRFrecalcBuffer((uint8_t*)&_buf,offset); mjysd02_Packet_t *_packet = (mjysd02_Packet_t*)&_buf; if(_packet->PID!=0x07f6) return; // invalid packet - // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: MJYD2S: %02u %04x %04x %04x %02x"),_packet->payloadSize,_packet->UUID,_packet->frameCtrl,_packet->PID,_packet->frameCnt); - // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: PAYLOAD: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_packet->data[0],_packet->data[1],_packet->data[2],_packet->data[3],_packet->data[4],_packet->data[5],_packet->data[6],_packet->data[7],_packet->data[8],_packet->data[9],_packet->data[10],_packet->data[11],_packet->data[12],_packet->data[13],_packet->data[14],_packet->data[15],_packet->data[16],_packet->data[17]); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S: %02u %04x %04x %04x %02x"),_packet->payloadSize,_packet->UUID,_packet->frameCtrl,_packet->PID,_packet->frameCnt); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: PAYLOAD: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_packet->data[0],_packet->data[1],_packet->data[2],_packet->data[3],_packet->data[4],_packet->data[5],_packet->data[6],_packet->data[7],_packet->data[8],_packet->data[9],_packet->data[10],_packet->data[11],_packet->data[12],_packet->data[13],_packet->data[14],_packet->data[15],_packet->data[16],_packet->data[17]); uint32_t _idx = MINRF.activeLight-1; switch(_packet->frameCtrl){ case 0x5910: if(_packet->frameCnt!=MIBLElights[_idx].lastCnt){ - // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: MJYD2S after motion:%x"),_packet->frameCnt); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S after motion:%x"),_packet->frameCnt); MIBLElights[_idx].lastCnt = _packet->frameCnt; if(millis()-MIBLElights[_idx].lastTime>120000){ MIBLElights[_idx].eventType = 1; MIBLElights[_idx].events++; MIBLElights[_idx].shallSendMQTT = 1; MIBLElights[_idx].lastTime = millis(); - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: MJYD2S secondary PIR")); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S secondary PIR")); } } break; @@ -1238,7 +1264,7 @@ void MINRFhandleMJYD2SPacket(void){ // no MiBeacon if(millis()-MIBLElights[_idx].lastTime>1000){ MIBLElights[_idx].eventType = 1; //PIR MIBLElights[_idx].shallSendMQTT = 1; - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: MJYD2S primary PIR")); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S primary PIR")); MIBLElights[_idx].events++; } MIBLElights[_idx].lastTime = millis(); @@ -1259,23 +1285,23 @@ void MINRFhandleMJYD2SPacket(void){ // no MiBeacon MIBLElights[_idx].NMT = output[6]<<24 | output[5]<<16 | output[4]<<8 | output[3]; MIBLElights[_idx].eventType = 3; // NMT 0, 120, 300, 600, 1800, ... seconds MIBLElights[_idx].shallSendMQTT = 1; - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: MJYD2S NMT: %u"), MIBLElights[_idx].NMT ); + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S NMT: %u"), MIBLElights[_idx].NMT ); break; } } } - // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT:%x"),_frame_PID); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT:%x"),_frame_PID); } void MINRFhandleLightPacket(void){ switch(MIBLElights[MINRF.activeLight-1].type){ case NLIGHT: - // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT!!")); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT!!")); MINRFhandleNlightPacket(); break; case MJYD2S: - // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: MJYD2S !!")); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S !!")); MINRFhandleMJYD2SPacket(); break; } @@ -1285,7 +1311,7 @@ void MINRFhandleLightPacket(void){ void MINRFaddLight(uint8_t _MAC[], uint8_t _type){ // no MiBeacon for(uint32_t i=0; iMAC, 0x0a1c); // This must be a hard-coded fake ID + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("known %s at slot %u"), kMINRFDeviceType[MIBLEsensors[_slot].type-1],_slot); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: ATC: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),MINRF.buffer.raw[0],MINRF.buffer.raw[1],MINRF.buffer.raw[2],MINRF.buffer.raw[3],MINRF.buffer.raw[4],MINRF.buffer.raw[5],MINRF.buffer.raw[6],MINRF.buffer.raw[7],MINRF.buffer.raw[8],MINRF.buffer.raw[9],MINRF.buffer.raw[10],MINRF.buffer.raw[11]); + if(_slot==0xff) return; + + MIBLEsensors.at(_slot).temp = (float)(__builtin_bswap16(_packet->temp))/10.0f; + MIBLEsensors.at(_slot).hum = (float)_packet->hum; + MIBLEsensors.at(_slot).bat = _packet->batPer; + + MIBLEsensors[_slot].shallSendMQTT = 1; } /*********************************************************************************************\ @@ -1309,14 +1349,15 @@ void MINRFaddLight(uint8_t _MAC[], uint8_t _type){ // 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")); + DEBUG_SENSOR_LOG(PSTR("NRF: check for FAKE sensors")); MINRFpurgeFakeSensors(); MINRF.timer=0; } MINRF.timer++; if (!MINRFreceivePacket()){ - // DEBUG_SENSOR_LOG(PSTR("MINRF: nothing received")); + // DEBUG_SENSOR_LOG(PSTR("NRF: nothing received")); + // if (MINRF.packetMode==ATC) AddLog_P2(LOG_LEVEL_INFO,PSTR("no ATC..")); } else { @@ -1327,7 +1368,7 @@ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds } else MINRFhandleScan(); break; - case FLORA: case MJ_HT_V1: case LYWSD02: case CGG1: case LYWSD03: case YEERC: case MHOC401: + case FLORA: case MJ_HT_V1: case LYWSD02: case CGG1: case LYWSD03: case YEERC: case MHOC401: case MHOC303: MINRFhandleMiBeaconPacket(); break; case CGD1: @@ -1336,6 +1377,9 @@ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds case NLIGHT: //case MJYD2S: MINRFhandleLightPacket(); break; + case ATC: + MINRFhandleATCPacket(); + break; default: break; } @@ -1534,7 +1578,7 @@ void MINRFShow(bool json) if (json) { for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { if(MIBLEsensors[i].showedUp < 3){ - DEBUG_SENSOR_LOG(PSTR("MINRF: sensor not fully registered yet")); + DEBUG_SENSOR_LOG(PSTR("NRF: sensor not fully registered yet")); if(MIBLEsensors[i].type != YEERC) break; // send every RC code, even if there is a potentially false MAC } switch(MIBLEsensors[i].type){ @@ -1647,7 +1691,7 @@ void MINRFShow(bool json) WSContentSend_PD(HTTP_NRF24NEW, NRF24type, NRF24.chipType, i+1,stemp,MINRF.confirmedSensors); for (i ; igetServiceData(0).data(),advertisedDevice->getServiceData(0).length(), addr, rssi); } + else if(uuid==0x181a) { //ATC + MI32ParseATCPacket((char*)advertisedDevice->getServiceData(0).data(),advertisedDevice->getServiceData(0).length(), addr, rssi); + } else { + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %x: %s Buffer: %u"), uuid, advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); MI32Scan->erase(advertisedDevice->getAddress()); - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); } }; }; @@ -404,6 +423,7 @@ void MI32_ReverseMAC(uint8_t _mac[]){ void MI32AddKey(char* payload){ mi_bindKey_t keyMAC; memset(keyMAC.buf,0,sizeof(keyMAC)); + UpperCase(payload,payload); MI32KeyMACStringToBytes(payload,keyMAC.buf); bool unknownKey = true; for(uint32_t i=0; iMAC, 0x0a1c, _packet->frameCnt); // This must be a hard-coded fake ID + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + if(_slot==0xff) return; + + MIBLEsensors[_slot].rssi=rssi; + + MIBLEsensors.at(_slot).temp = (float)(__builtin_bswap16(_packet->temp))/10.0f; + MIBLEsensors.at(_slot).hum = (float)_packet->hum; + MIBLEsensors[_slot].eventType.tempHum = 1; + MIBLEsensors.at(_slot).bat = _packet->batPer; + MIBLEsensors[_slot].eventType.bat = 1; + + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; + +} + void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int rssi){ // no MiBeacon uint8_t _addr[6]; memcpy(_addr,addr,6); @@ -1568,7 +1607,7 @@ bool MI32Cmd(void) { \*********************************************************************************************/ 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_MI32_MAC[] PROGMEM = "{s}%s %s{m}%s{e}"; const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}"; const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u %%{e}"; const char HTTP_LASTBUTTON[] PROGMEM = "{s}%s Last Button{m}%u {e}"; @@ -1580,6 +1619,15 @@ const char HTTP_MI32_HL[] PROGMEM = "{s}
{m}
{e}"; void MI32Show(bool json) { if (json) { +#ifdef USE_HOME_ASSISTANT + bool _noSummarySave = MI32.option.noSummary; + bool _minimalSummarySave = MI32.option.minimalSummary; + if(hass_mode==2){ + MI32.option.noSummary = false; + MI32.option.minimalSummary = false; + } +#endif //USE_HOME_ASSISTANT + if(!MI32.mode.triggeredTele){ MI32.mode.shallClearResults=1; if(MI32.option.noSummary) return; // no message at TELEPERIOD @@ -1599,7 +1647,11 @@ void MI32Show(bool json) bool tempHumSended = false; if(MIBLEsensors[i].feature.tempHum){ if(MIBLEsensors[i].eventType.tempHum || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ - if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)) { + if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp) +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif //USE_HOME_ASSISTANT + ) { ResponseAppend_P(PSTR(",")); ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum); tempHumSended = true; @@ -1608,7 +1660,11 @@ void MI32Show(bool json) } if(MIBLEsensors[i].feature.temp && !tempHumSended){ if(MIBLEsensors[i].eventType.temp || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate) { - if (!isnan(MIBLEsensors[i].temp)) { + if (!isnan(MIBLEsensors[i].temp) +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif //USE_HOME_ASSISTANT + ) { char temperature[FLOATSZ]; dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"), temperature); @@ -1617,7 +1673,11 @@ void MI32Show(bool json) } if(MIBLEsensors[i].feature.hum && !tempHumSended){ if(MIBLEsensors[i].eventType.hum || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate) { - if (!isnan(MIBLEsensors[i].hum)) { + if (!isnan(MIBLEsensors[i].hum) +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif //USE_HOME_ASSISTANT + ) { char hum[FLOATSZ]; dtostrfd(MIBLEsensors[i].hum, Settings.flag2.humidity_resolution, hum); ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), hum); @@ -1626,27 +1686,43 @@ void MI32Show(bool json) } if (MIBLEsensors[i].feature.lux){ if(MIBLEsensors[i].eventType.lux || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ - if (MIBLEsensors[i].lux!=0x0ffffff) { // this is the error code -> no lux + if (MIBLEsensors[i].lux!=0x0ffffff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif //USE_HOME_ASSISTANT + ) { // this is the error code -> no lux ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); } } } if (MIBLEsensors[i].feature.moist){ if(MIBLEsensors[i].eventType.moist || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ - if (MIBLEsensors[i].moisture!=0xff) { + if (MIBLEsensors[i].moisture!=0xff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif //USE_HOME_ASSISTANT + ) { ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%u"), MIBLEsensors[i].moisture); } } } if (MIBLEsensors[i].feature.fert){ if(MIBLEsensors[i].eventType.fert || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ - if (MIBLEsensors[i].fertility!=0xffff) { + if (MIBLEsensors[i].fertility!=0xffff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif //USE_HOME_ASSISTANT + ) { ResponseAppend_P(PSTR(",\"Fertility\":%u"), MIBLEsensors[i].fertility); } } } if (MIBLEsensors[i].feature.Btn){ - if(MIBLEsensors[i].eventType.Btn){ + if(MIBLEsensors[i].eventType.Btn +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif //USE_HOME_ASSISTANT + ){ ResponseAppend_P(PSTR(",\"Btn\":%u"),MIBLEsensors[i].Btn); } } @@ -1674,7 +1750,11 @@ void MI32Show(bool json) } if (MIBLEsensors[i].feature.bat){ if(MIBLEsensors[i].eventType.bat || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ - if (MIBLEsensors[i].bat != 0x00) { // this is the error code -> no battery + if (MIBLEsensors[i].bat != 0x00 +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif //USE_HOME_ASSISTANT + ) { // this is the error code -> no battery ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); } } @@ -1688,11 +1768,16 @@ void MI32Show(bool json) MIBLEsensors[i].eventType.raw = 0; if(MIBLEsensors[i].shallSendMQTT==1){ MIBLEsensors[i].shallSendMQTT = 0; - break; + continue; } } MI32.mode.triggeredTele = 0; - // ResponseAppend_P(PSTR("}")); +#ifdef USE_HOME_ASSISTANT + if(hass_mode==2){ + MI32.option.noSummary = _noSummarySave; + MI32.option.minimalSummary = _minimalSummarySave; + } +#endif //USE_HOME_ASSISTANT #ifdef USE_WEBSERVER } else { static uint16_t _page = 0; @@ -1711,7 +1796,9 @@ void MI32Show(bool json) WSContentSend_PD(HTTP_MI32, i+1,stemp,MIBLEsensors.size()); for (i; iMAC,MIBLEsensors.at(_slot).MAC,6)!=0) return; // data corruption + MIBLEsensors.at(_slot).temp = (float)(__builtin_bswap16(_packet->temp))/10.0f; + MIBLEsensors.at(_slot).hum = (float)_packet->hum; + MIBLEsensors.at(_slot).bat = _packet->batPer; + MIBLEsensors[_slot].shallSendMQTT = 1; +} + +char* HM10ParseResponse(char *buf, uint16_t bufsize) { if (!strncmp(buf,"HMSoft",6)) { //8 const char* _fw = "000"; memcpy((void *)_fw,(void *)(buf+8),3); HM10.firmware = atoi(_fw); DEBUG_SENSOR_LOG(PSTR("%s: Firmware: %d"),D_CMND_HM10, HM10.firmware); - return; + return buf; } - char * _pos = strstr(buf, "ISA:"); + char * _pos = nullptr; + uint32_t _idx = 0; + char _subStr[] = "SA:"; + while(_pos = (char*) memchr(buf+_idx, 'I', 60)){ //strstr() does miss too much + _idx=_pos-buf; + if(memcmp(&_pos+1,_subStr,3)){ + break; + } + } if(_pos) { uint8_t _newMacArray[6] = {0}; memcpy((void *)_newMacArray,(void *)(_pos+4),6); uint32_t _rssi = 255- (uint8_t)(_pos[11]); - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("rssi: %u"),(255- (uint8_t)(_pos[11]))); HM10_ReverseMAC(_newMacArray); DEBUG_SENSOR_LOG(PSTR("%s: MAC-array: %02x%02x%02x%02x%02x%02x"),D_CMND_HM10,_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]); uint16_t _type=0xffff; - for (uint32_t idx =10;idx64) return _pos+12; + else return nullptr; } else if (strstr(buf, "LOST")){ + HM10.current_task_delay = 0; HM10.mode.connected = false; } + else if (strstr(buf, "CONNF")){ + HM10.mode.connected = false; + HM10.current_task_delay = 0; + } else if (strstr(buf, "CONN")){ HM10.current_task_delay = 0; } else { DEBUG_SENSOR_LOG(PSTR("%s: empty response"),D_CMND_HM10); + return buf; } + return _pos; } void HM10readHT_LY(char *_buf){ - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + // AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)_buf,7); if(_buf[0]==0x4f && _buf[1]==0x4b) return; // "OK" 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"),D_CMND_HM10,LYWSD0x_HT.temp,LYWSD0x_HT.hum); + LYWSD0x_HT_t *packet = (LYWSD0x_HT_t*)_buf; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u"),D_CMND_HM10,packet->temp,packet->hum); uint32_t _slot = HM10.state.sensor; DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); static float _tempFloat; - _tempFloat=(float)(LYWSD0x_HT.temp)/100.0f; + _tempFloat=(float)(packet->temp)/100.0f; if(_tempFloat<60){ MIBLEsensors[_slot].temp=_tempFloat; HM10.mode.awaiting = none; HM10.current_task_delay = 0; MIBLEsensors[_slot].showedUp=255; // this sensor is real } - _tempFloat=(float)LYWSD0x_HT.hum; + _tempFloat=(float)packet->hum; if(_tempFloat<100){ MIBLEsensors[_slot].hum = _tempFloat; DEBUG_SENSOR_LOG(PSTR("LYWSD0x: hum updated")); } MIBLEsensors[_slot].eventType.tempHum = 1; if (MIBLEsensors[_slot].type == LYWSD03MMC || MIBLEsensors[_slot].type == MHOC401){ - MIBLEsensors[_slot].bat = ((float)LYWSD0x_HT.volt-2100.0f)/12.0f; + MIBLEsensors[_slot].bat = ((float)packet->volt-2100.0f)/12.0f; MIBLEsensors[_slot].eventType.bat = 1; } MIBLEsensors[_slot].shallSendMQTT = 1; @@ -730,20 +777,20 @@ void HM10readHT_CGD1(char *_buf){ if(_buf[0]==0x4f && _buf[1]==0x4b) return; // "OK" if(_buf[0] == 0){ if(_buf[1]==0 && _buf[2]==0 && _buf[3]==0 && _buf[4]==0) return; - memcpy(&CGD1_HT,(void *)_buf,5); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H * 100: %u"),D_CMND_HM10,CGD1_HT.temp,CGD1_HT.hum); + CGD1_HT_t *_packet = (CGD1_HT_t*)_buf; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H * 100: %u"),D_CMND_HM10,_packet->temp,_packet->hum); uint32_t _slot = HM10.state.sensor; DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); static float _tempFloat; - _tempFloat=(float)(CGD1_HT.temp)/100.0f; + _tempFloat=(float)(_packet->temp)/100.0f; if(_tempFloat<60){ MIBLEsensors[_slot].temp=_tempFloat; HM10.mode.awaiting = none; HM10.current_task_delay = 0; MIBLEsensors[_slot].showedUp=255; // this sensor is real } - _tempFloat=(float)CGD1_HT.hum/100.0f; + _tempFloat=(float)_packet->hum/100.0f; if(_tempFloat<100){ MIBLEsensors[_slot].hum = _tempFloat; DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated")); @@ -784,23 +831,21 @@ void HM10readHT_MJ_HT_V1(char *_buf){ } void HM10readTLMF(char *_buf){ - DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); - if(_buf[0]==0x4f && _buf[1]==0x4b) return; // "OK" - if(_buf[0] != 0 || _buf[1] != 0){ // this will lose 0.0 degree, but it is not possible to measure a successful reading - memcpy(&Flora_TLMF,(void *)_buf,10); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, L: %u, M: %u, F: %u"),D_CMND_HM10,Flora_TLMF.temp,Flora_TLMF.lux,Flora_TLMF.moist,Flora_TLMF.fert); + AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)_buf,16); + Flora_TLMF_t *_packet = (Flora_TLMF_t*)_buf; + if(_packet->ID==0xFB003C02){ // this is a magic word ... hopefully independent of FW version + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, L: %u, M: %u, F: %u"),D_CMND_HM10,_packet->temp,_packet->lux,_packet->moist,_packet->fert); uint32_t _slot = HM10.state.sensor; - DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + MIBLEsensors[_slot].showedUp=255; // this sensor is real + static float _tempFloat; - _tempFloat=(float)(Flora_TLMF.temp)/10.0f; - if(_tempFloat<60){ - MIBLEsensors[_slot].temp=_tempFloat; - MIBLEsensors[_slot].showedUp=255; // this sensor is real - } - MIBLEsensors[_slot].lux = Flora_TLMF.lux; - MIBLEsensors[_slot].moisture = Flora_TLMF.moist; - MIBLEsensors[_slot].fertility = Flora_TLMF.fert; + _tempFloat=(float)(_packet->temp)/10.0f; + MIBLEsensors[_slot].temp=_tempFloat; + + MIBLEsensors[_slot].lux = _packet->lux; + MIBLEsensors[_slot].moisture = _packet->moist; + MIBLEsensors[_slot].fertility = _packet->fert; MIBLEsensors[_slot].eventType.temp = 1; MIBLEsensors[_slot].eventType.lux = 1; MIBLEsensors[_slot].eventType.moist = 1; @@ -842,12 +887,10 @@ bool HM10readBat(char *_buf){ 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(iread(); + HM10.rxBuffer[i] = HM10Serial->read(); } i++; success = true; @@ -864,45 +907,48 @@ bool HM10SerialHandleFeedback(){ // every 50 milliseconds HM10triggerTele(); } return success; - } + } switch (HM10.mode.awaiting){ case bat: if (HM10.mode.connected) { - if (HM10readBat(ret)){ + if (HM10readBat(HM10.rxBuffer)){ HM10.mode.awaiting = none; HM10.current_task_delay = 0; } } break; case tempHumLY: - if (HM10.mode.connected) HM10readHT_LY(ret); + if (HM10.mode.connected) HM10readHT_LY(HM10.rxBuffer); break; case tempHumCGD1: - if (HM10.mode.connected) HM10readHT_CGD1(ret); + if (HM10.mode.connected) HM10readHT_CGD1(HM10.rxBuffer); break; case TLMF: - if (HM10.mode.connected) HM10readTLMF(ret); + if (HM10.mode.connected) HM10readTLMF(HM10.rxBuffer); break; case discScan: if(success) { - HM10ParseResponse(ret,i); + char *_src = HM10ParseResponse(HM10.rxBuffer,i); + if(_src){ + HM10ParseResponse(_src,i-(_src-HM10.rxBuffer)); // try a second parse + } } break; case tempHumMJ: - if (HM10.mode.connected) HM10readHT_MJ_HT_V1(ret); + if (HM10.mode.connected) HM10readHT_MJ_HT_V1(HM10.rxBuffer); break; case none: if(success) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: response: %s"),D_CMND_HM10, (char *)ret); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: response: %s"),D_CMND_HM10, (char *)HM10.rxBuffer); // for(uint32_t j = 0; j. +*/ + +#ifdef USE_I2C +#ifdef USE_VL53L1X +/*********************************************************************************************\ + * VL53L1X + * + * Source: + * + * I2C Address: 0x29 +\*********************************************************************************************/ + +#define XSNS_77 77 +#define XI2C_54 54 // See I2CDEVICES.md + +#include "VL53L1X.h" +VL53L1X vl53l1x = VL53L1X(); // create object copy + +#define VL53L1X_ADDRESS 0x29 + +struct { + bool ready = false; + uint16_t distance = 0; +} vl53l1x_sensors; + +/********************************************************************************************/ + +void Vl53l1Detect(void) { + if (!I2cSetDevice(VL53L1X_ADDRESS)) { return; } + if (!vl53l1x.init()) { return; } + + I2cSetActiveFound(vl53l1x.getAddress(), "VL53L1X"); + vl53l1x.setTimeout(500); + vl53l1x.setDistanceMode(VL53L1X::Long); // could be Short, Medium, Long + vl53l1x.setMeasurementTimingBudget(140000); + vl53l1x.startContinuous(50); + vl53l1x_sensors.ready = true; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_VL53L1X[] PROGMEM = + "{s}VL53L1X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; // {s} = , {m} = , {e} = +#endif // USE_WEBSERVER + +void Vl53l1Every_250MSecond(void) { + // every 250 ms + uint16_t dist = vl53l1x.read(); + if (!dist || dist > 4000) { + dist = 9999; + } + vl53l1x_sensors.distance = dist; +} + +#ifdef USE_DOMOTICZ +void Vl53l1Every_Second(void) { + char distance[FLOATSZ]; + dtostrfd((float)vl53l1x_sensors.distance / 10, 1, distance); + DomoticzSensor(DZ_ILLUMINANCE, distance); +} +#endif // USE_DOMOTICZ + +void Vl53l1Show(bool json) { + if (json) { +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + Vl53l1Every_Second(); + } +#endif // USE_DOMOTICZ + ResponseAppend_P(PSTR(",\"VL53L1X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l1x_sensors.distance); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_VL53L1X, vl53l1x_sensors.distance); +#endif + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns77(uint8_t function) +{ + if (!I2cEnabled(XI2C_54)) { return false; } + bool result = false; + + if (FUNC_INIT == function) { + Vl53l1Detect(); + } + else if (vl53l1x_sensors.ready) { + switch (function) { + case FUNC_EVERY_250_MSECOND: + Vl53l1Every_250MSecond(); + break; +#ifdef USE_DOMOTICZ + case FUNC_EVERY_SECOND: + Vl53l1Every_Second(); + break; +#endif // USE_DOMOTICZ + case FUNC_JSON_APPEND: + Vl53l1Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Vl53l1Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_VL53L1X +#endif // USE_I2C diff --git a/tools/decode-status.py b/tools/decode-status.py index 3a664b28c..f1c2c8302 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -229,7 +229,7 @@ a_features = [[ "USE_VEML7700","USE_MCP9808","USE_BL0940","USE_TELEGRAM", "USE_HP303B","USE_TCP_BRIDGE","USE_TELEINFO","USE_LMT01", "USE_PROMETHEUS","USE_IEM3000","USE_DYP","USE_I2S_AUDIO", - "USE_MLX90640","","","", + "USE_MLX90640","USE_VL53L1X","","", "","USE_TTGO_WATCH","USE_ETHERNET","USE_WEBCAM" ],[ "","","","", diff --git a/tools/fw_zbbridge/ncp-uart-sw-6.8.0.1_115200.ota b/tools/fw_zbbridge/ncp-uart-sw-6.8.0.1_115200.ota new file mode 100644 index 000000000..cac52942e Binary files /dev/null and b/tools/fw_zbbridge/ncp-uart-sw-6.8.0.1_115200.ota differ diff --git a/tools/fw_zbbridge/readme.txt b/tools/fw_zbbridge/readme.txt index 38049cf62..345da663e 100644 --- a/tools/fw_zbbridge/readme.txt +++ b/tools/fw_zbbridge/readme.txt @@ -1,2 +1,15 @@ -The ncp-uart-sw_6.7.6_115200.ota is for EZSP v8 compatible hosts -The ncp-uart-sw_6.5.5_115200.ota its for older like current ZHA +# EmberZNet NCP UART EZSP firmware + +## EmberZNet NCP UART EZSP firmware signed for Sonoff ZBBridge + +The ncp-uart-sw-6.8.0.1_115200.ota is the latest cutting-edge version, still untested and only for experimental testing with EZSP v8 compatible hosts. +The ncp-uart-sw_6.7.6_115200.ota is the recommended stable version for EZSP v6, EZSP v7, and EZSP v8 compatible hosts. +The ncp-uart-sw_6.5.5_115200.ota it the legacy version for EZSP v4, EZSP v5, EZSP v6, or EZSP v7 compatible hosts. + +## EmberZNet and EZSP Protocol Versions + +Silicon Labs do not currently have a consolidated list of changes by EmberZNet SDK or EZSP protocol version. The EZSP additions, changes and deletions have only ever been listed in the "Zigbee EmberZNet Release Notes" (EmberZNet SDK) under the "New items" section as well as the matching UG100 EZSP Reference Guide included with each EmberZNet SDK release. + +The largest change was between EZSP v4 (first added in EmberZNet 4.7.2 SDK) and EZSP v5 that was added in EmberZNet 5.9.0 SDK which requires the Legacy Frame ID 0xFF. The change from EZSP v5 to EZSP v6 was done in EmberZNet 6.0.0 SDK. The change from EZSP v6 to EZSP v7 was in EmberZNet 6.4.0 SDK. EmberZNet 6.7.0 SDK added EZSP v8 (and Secure EZSP Protocol Version 2). + +Perhaps more important to know today is that EZSP v5, v6 and v7 (EmberZNet 6.6.x.x) use the same framing format, but EmberZNet 6.7.x.x/EZSP v8 introduced new framing format and expanded command id field from 8 bits to 16 bits. diff --git a/uncrustify.cfg b/uncrustify.cfg new file mode 100644 index 000000000..fbd5cab07 --- /dev/null +++ b/uncrustify.cfg @@ -0,0 +1,2807 @@ +# Uncrustify-0.69.0-1-72de5fdb + +# +# General options +# + +# The type of line endings. +# +# Default: auto +newlines = auto # lf/crlf/cr/auto + +# The original size of tabs in the input. +# +# Default: 8 +input_tab_size = 8 # unsigned number + +# The size of tabs in the output (only used if align_with_tabs=true). +# +# Default: 8 +output_tab_size = 8 # unsigned number + +# The ASCII value of the string escape char, usually 92 (\) or (Pawn) 94 (^). +# +# Default: 92 +string_escape_char = 92 # unsigned number + +# Alternate string escape char (usually only used for Pawn). +# Only works right before the quote char. +string_escape_char2 = 0 # unsigned number + +# Replace tab characters found in string literals with the escape sequence \t +# instead. +string_replace_tab_chars = false # true/false + +# Allow interpreting '>=' and '>>=' as part of a template in code like +# 'void f(list>=val);'. If true, 'assert(x<0 && y>=3)' will be broken. +# Improvements to template detection may make this option obsolete. +tok_split_gte = false # true/false + +# Specify the marker used in comments to disable processing of part of the +# file. +# +# Default: *INDENT-OFF* +disable_processing_cmt = " *INDENT-OFF*" # string + +# Specify the marker used in comments to (re)enable processing in a file. +# +# Default: *INDENT-ON* +enable_processing_cmt = " *INDENT-ON*" # string + +# Enable parsing of digraphs. +enable_digraphs = false # true/false + +# Add or remove the UTF-8 BOM (recommend 'remove'). +utf8_bom = ignore # ignore/add/remove/force + +# If the file contains bytes with values between 128 and 255, but is not +# UTF-8, then output as UTF-8. +utf8_byte = false # true/false + +# Force the output encoding to UTF-8. +utf8_force = false # true/false + +# +# Spacing options +# + +# Add or remove space around non-assignment symbolic operators ('+', '/', '%', +# '<<', and so forth). +sp_arith = add # ignore/add/remove/force + +# Add or remove space around arithmetic operators '+' and '-'. +# +# Overrides sp_arith. +sp_arith_additive = ignore # ignore/add/remove/force + +# Add or remove space around assignment operator '=', '+=', etc. +sp_assign = add # ignore/add/remove/force + +# Add or remove space around '=' in C++11 lambda capture specifications. +# +# Overrides sp_assign. +sp_cpp_lambda_assign = ignore # ignore/add/remove/force + +# Add or remove space after the capture specification in C++11 lambda. +sp_cpp_lambda_paren = ignore # ignore/add/remove/force + +# Add or remove space around assignment operator '=' in a prototype. +# +# If set to ignore, use sp_assign. +sp_assign_default = ignore # ignore/add/remove/force + +# Add or remove space before assignment operator '=', '+=', etc. +# +# Overrides sp_assign. +sp_before_assign = ignore # ignore/add/remove/force + +# Add or remove space after assignment operator '=', '+=', etc. +# +# Overrides sp_assign. +sp_after_assign = ignore # ignore/add/remove/force + +# Add or remove space in 'NS_ENUM ('. +sp_enum_paren = ignore # ignore/add/remove/force + +# Add or remove space around assignment '=' in enum. +sp_enum_assign = add # ignore/add/remove/force + +# Add or remove space before assignment '=' in enum. +# +# Overrides sp_enum_assign. +sp_enum_before_assign = ignore # ignore/add/remove/force + +# Add or remove space after assignment '=' in enum. +# +# Overrides sp_enum_assign. +sp_enum_after_assign = ignore # ignore/add/remove/force + +# Add or remove space around assignment ':' in enum. +sp_enum_colon = ignore # ignore/add/remove/force + +# Add or remove space around preprocessor '##' concatenation operator. +# +# Default: add +sp_pp_concat = add # ignore/add/remove/force + +# Add or remove space after preprocessor '#' stringify operator. +# Also affects the '#@' charizing operator. +sp_pp_stringify = add # ignore/add/remove/force + +# Add or remove space before preprocessor '#' stringify operator +# as in '#define x(y) L#y'. +sp_before_pp_stringify = ignore # ignore/add/remove/force + +# Add or remove space around boolean operators '&&' and '||'. +sp_bool = add # ignore/add/remove/force + +# Add or remove space around compare operator '<', '>', '==', etc. +sp_compare = add # ignore/add/remove/force + +# Add or remove space inside '(' and ')'. +sp_inside_paren = remove # ignore/add/remove/force + +# Add or remove space between nested parentheses, i.e. '((' vs. ') )'. +sp_paren_paren = remove # ignore/add/remove/force + +# Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('. +sp_cparen_oparen = ignore # ignore/add/remove/force + +# Whether to balance spaces inside nested parentheses. +sp_balance_nested_parens = false # true/false + +# Add or remove space between ')' and '{'. +sp_paren_brace = add # ignore/add/remove/force + +# Add or remove space between nested braces, i.e. '{{' vs '{ {'. +sp_brace_brace = ignore # ignore/add/remove/force + +# Add or remove space before pointer star '*'. +sp_before_ptr_star = add # ignore/add/remove/force + +# Add or remove space before pointer star '*' that isn't followed by a +# variable name. If set to 'ignore', sp_before_ptr_star is used instead. +sp_before_unnamed_ptr_star = add # ignore/add/remove/force + +# Add or remove space between pointer stars '*'. +sp_between_ptr_star = remove # ignore/add/remove/force + +# Add or remove space after pointer star '*', if followed by a word. +sp_after_ptr_star = remove # ignore/add/remove/force + +# Add or remove space after pointer caret '^', if followed by a word. +sp_after_ptr_block_caret = ignore # ignore/add/remove/force + +# Add or remove space after pointer star '*', if followed by a qualifier. +sp_after_ptr_star_qualifier = ignore # ignore/add/remove/force + +# Add or remove space after a pointer star '*', if followed by a function +# prototype or function definition. +sp_after_ptr_star_func = add # ignore/add/remove/force + +# Add or remove space after a pointer star '*', if followed by an open +# parenthesis, as in 'void* (*)(). +sp_ptr_star_paren = ignore # ignore/add/remove/force + +# Add or remove space before a pointer star '*', if followed by a function +# prototype or function definition. +sp_before_ptr_star_func = remove # ignore/add/remove/force + +# Add or remove space before a reference sign '&'. +sp_before_byref = remove # ignore/add/remove/force + +# Add or remove space before a reference sign '&' that isn't followed by a +# variable name. If set to 'ignore', sp_before_byref is used instead. +sp_before_unnamed_byref = remove # ignore/add/remove/force + +# Add or remove space after reference sign '&', if followed by a word. +sp_after_byref = add # ignore/add/remove/force + +# Add or remove space after a reference sign '&', if followed by a function +# prototype or function definition. +sp_after_byref_func = add # ignore/add/remove/force + +# Add or remove space before a reference sign '&', if followed by a function +# prototype or function definition. +sp_before_byref_func = remove # ignore/add/remove/force + +# Add or remove space between type and word. +# +# Default: force +sp_after_type = add # ignore/add/remove/force + +# Add or remove space between 'decltype(...)' and word. +sp_after_decltype = ignore # ignore/add/remove/force + +# (D) Add or remove space before the parenthesis in the D constructs +# 'template Foo(' and 'class Foo('. +sp_before_template_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'template' and '<'. +# If set to ignore, sp_before_angle is used. +sp_template_angle = ignore # ignore/add/remove/force + +# Add or remove space before '<'. +sp_before_angle = remove # ignore/add/remove/force + +# Add or remove space inside '<' and '>'. +sp_inside_angle = remove # ignore/add/remove/force + +# Add or remove space inside '<>'. +sp_inside_angle_empty = ignore # ignore/add/remove/force + +# Add or remove space between '>' and ':'. +sp_angle_colon = ignore # ignore/add/remove/force + +# Add or remove space after '<>'. +sp_after_angle = remove # ignore/add/remove/force + +# Add or remove space between '>' and '(' as found in 'new List(foo);'. +sp_angle_paren = remove # ignore/add/remove/force + +# Add or remove space between '>' and '()' as found in 'new List();'. +sp_angle_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space between '>' and a word as in 'List m;' or +# 'template static ...'. +sp_angle_word = remove # ignore/add/remove/force + +# Add or remove space between '>' and '>' in '>>' (template stuff). +# +# Default: add +sp_angle_shift = add # ignore/add/remove/force + +# (C++11) Permit removal of the space between '>>' in 'foo >'. Note +# that sp_angle_shift cannot remove the space without this option. +sp_permit_cpp11_shift = false # true/false + +# Add or remove space before '(' of control statements ('if', 'for', 'switch', +# 'while', etc.). +sp_before_sparen = add # ignore/add/remove/force + +# Add or remove space inside '(' and ')' of control statements. +sp_inside_sparen = remove # ignore/add/remove/force + +# Add or remove space after '(' of control statements. +# +# Overrides sp_inside_sparen. +sp_inside_sparen_open = ignore # ignore/add/remove/force + +# Add or remove space before ')' of control statements. +# +# Overrides sp_inside_sparen. +sp_inside_sparen_close = ignore # ignore/add/remove/force + +# Add or remove space after ')' of control statements. +sp_after_sparen = ignore # ignore/add/remove/force + +# Add or remove space between ')' and '{' of of control statements. +sp_sparen_brace = add # ignore/add/remove/force + +# (D) Add or remove space between 'invariant' and '('. +sp_invariant_paren = ignore # ignore/add/remove/force + +# (D) Add or remove space after the ')' in 'invariant (C) c'. +sp_after_invariant_paren = ignore # ignore/add/remove/force + +# Add or remove space before empty statement ';' on 'if', 'for' and 'while'. +sp_special_semi = remove # ignore/add/remove/force + +# Add or remove space before ';'. +# +# Default: remove +sp_before_semi = remove # ignore/add/remove/force + +# Add or remove space before ';' in non-empty 'for' statements. +sp_before_semi_for = remove # ignore/add/remove/force + +# Add or remove space before a semicolon of an empty part of a for statement. +sp_before_semi_for_empty = remove # ignore/add/remove/force + +# Add or remove space after ';', except when followed by a comment. +# +# Default: add +sp_after_semi = add # ignore/add/remove/force + +# Add or remove space after ';' in non-empty 'for' statements. +# +# Default: force +sp_after_semi_for = force # ignore/add/remove/force + +# Add or remove space after the final semicolon of an empty part of a for +# statement, as in 'for ( ; ; )'. +sp_after_semi_for_empty = remove # ignore/add/remove/force + +# Add or remove space before '[' (except '[]'). +sp_before_square = remove # ignore/add/remove/force + +# Add or remove space before '[]'. +sp_before_squares = remove # ignore/add/remove/force + +# Add or remove space before C++17 structured bindings. +sp_cpp_before_struct_binding = ignore # ignore/add/remove/force + +# Add or remove space inside a non-empty '[' and ']'. +sp_inside_square = remove # ignore/add/remove/force + +# (OC) Add or remove space inside a non-empty Objective-C boxed array '@[' and +# ']'. If set to ignore, sp_inside_square is used. +sp_inside_square_oc_array = ignore # ignore/add/remove/force + +# Add or remove space after ',', i.e. 'a,b' vs. 'a, b'. +sp_after_comma = add # ignore/add/remove/force + +# Add or remove space before ','. +# +# Default: remove +sp_before_comma = remove # ignore/add/remove/force + +# (C#) Add or remove space between ',' and ']' in multidimensional array type +# like 'int[,,]'. +sp_after_mdatype_commas = ignore # ignore/add/remove/force + +# (C#) Add or remove space between '[' and ',' in multidimensional array type +# like 'int[,,]'. +sp_before_mdatype_commas = ignore # ignore/add/remove/force + +# (C#) Add or remove space between ',' in multidimensional array type +# like 'int[,,]'. +sp_between_mdatype_commas = ignore # ignore/add/remove/force + +# Add or remove space between an open parenthesis and comma, +# i.e. '(,' vs. '( ,'. +# +# Default: force +sp_paren_comma = force # ignore/add/remove/force + +# Add or remove space before the variadic '...' when preceded by a +# non-punctuator. +sp_before_ellipsis = ignore # ignore/add/remove/force + +# Add or remove space between a type and '...'. +sp_type_ellipsis = ignore # ignore/add/remove/force + +# (D) Add or remove space between a type and '?'. +sp_type_question = ignore # ignore/add/remove/force + +# Add or remove space between ')' and '...'. +sp_paren_ellipsis = ignore # ignore/add/remove/force + +# Add or remove space between ')' and a qualifier such as 'const'. +sp_paren_qualifier = ignore # ignore/add/remove/force + +# Add or remove space between ')' and 'noexcept'. +sp_paren_noexcept = ignore # ignore/add/remove/force + +# Add or remove space after class ':'. +sp_after_class_colon = add # ignore/add/remove/force + +# Add or remove space before class ':'. +sp_before_class_colon = add # ignore/add/remove/force + +# Add or remove space after class constructor ':'. +sp_after_constr_colon = ignore # ignore/add/remove/force + +# Add or remove space before class constructor ':'. +sp_before_constr_colon = ignore # ignore/add/remove/force + +# Add or remove space before case ':'. +# +# Default: remove +sp_before_case_colon = remove # ignore/add/remove/force + +# Add or remove space between 'operator' and operator sign. +sp_after_operator = remove # ignore/add/remove/force + +# Add or remove space between the operator symbol and the open parenthesis, as +# in 'operator ++('. +sp_after_operator_sym = remove # ignore/add/remove/force + +# Overrides sp_after_operator_sym when the operator has no arguments, as in +# 'operator *()'. +sp_after_operator_sym_empty = ignore # ignore/add/remove/force + +# Add or remove space after C/D cast, i.e. 'cast(int)a' vs. 'cast(int) a' or +# '(int)a' vs. '(int) a'. +sp_after_cast = remove # ignore/add/remove/force + +# Add or remove spaces inside cast parentheses. +sp_inside_paren_cast = remove # ignore/add/remove/force + +# Add or remove space between the type and open parenthesis in a C++ cast, +# i.e. 'int(exp)' vs. 'int (exp)'. +sp_cpp_cast_paren = remove # ignore/add/remove/force + +# Add or remove space between 'sizeof' and '('. +sp_sizeof_paren = remove # ignore/add/remove/force + +# Add or remove space between 'sizeof' and '...'. +sp_sizeof_ellipsis = ignore # ignore/add/remove/force + +# Add or remove space between 'sizeof...' and '('. +sp_sizeof_ellipsis_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'decltype' and '('. +sp_decltype_paren = ignore # ignore/add/remove/force + +# (Pawn) Add or remove space after the tag keyword. +sp_after_tag = ignore # ignore/add/remove/force + +# Add or remove space inside enum '{' and '}'. +sp_inside_braces_enum = add # ignore/add/remove/force + +# Add or remove space inside struct/union '{' and '}'. +sp_inside_braces_struct = add # ignore/add/remove/force + +# (OC) Add or remove space inside Objective-C boxed dictionary '{' and '}' +sp_inside_braces_oc_dict = ignore # ignore/add/remove/force + +# Add or remove space after open brace in an unnamed temporary +# direct-list-initialization. +sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force + +# Add or remove space before close brace in an unnamed temporary +# direct-list-initialization. +sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force + +# Add or remove space inside an unnamed temporary direct-list-initialization. +sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force + +# Add or remove space inside '{' and '}'. +sp_inside_braces = add # ignore/add/remove/force + +# Add or remove space inside '{}'. +sp_inside_braces_empty = remove # ignore/add/remove/force + +# Add or remove space between return type and function name. A minimum of 1 +# is forced except for pointer return types. +sp_type_func = add # ignore/add/remove/force + +# Add or remove space between type and open brace of an unnamed temporary +# direct-list-initialization. +sp_type_brace_init_lst = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' on function declaration. +sp_func_proto_paren = remove # ignore/add/remove/force + +# Add or remove space between function name and '()' on function declaration +# without parameters. +sp_func_proto_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' on function definition. +sp_func_def_paren = remove # ignore/add/remove/force + +# Add or remove space between function name and '()' on function definition +# without parameters. +sp_func_def_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space inside empty function '()'. +sp_inside_fparens = remove # ignore/add/remove/force + +# Add or remove space inside function '(' and ')'. +sp_inside_fparen = remove # ignore/add/remove/force + +# Add or remove space inside the first parentheses in a function type, as in +# 'void (*x)(...)'. +sp_inside_tparen = ignore # ignore/add/remove/force + +# Add or remove space between the ')' and '(' in a function type, as in +# 'void (*x)(...)'. +sp_after_tparen_close = ignore # ignore/add/remove/force + +# Add or remove space between ']' and '(' when part of a function call. +sp_square_fparen = remove # ignore/add/remove/force + +# Add or remove space between ')' and '{' of function. +sp_fparen_brace = add # ignore/add/remove/force + +# Add or remove space between ')' and '{' of s function call in object +# initialization. +# +# Overrides sp_fparen_brace. +sp_fparen_brace_initializer = ignore # ignore/add/remove/force + +# (Java) Add or remove space between ')' and '{{' of double brace initializer. +sp_fparen_dbrace = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' on function calls. +sp_func_call_paren = remove # ignore/add/remove/force + +# Add or remove space between function name and '()' on function calls without +# parameters. If set to 'ignore' (the default), sp_func_call_paren is used. +sp_func_call_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space between the user function name and '(' on function +# calls. You need to set a keyword to be a user function in the config file, +# like: +# set func_call_user tr _ i18n +sp_func_call_user_paren = remove # ignore/add/remove/force + +# Add or remove space inside user function '(' and ')'. +sp_func_call_user_inside_fparen = ignore # ignore/add/remove/force + +# Add or remove space between nested parentheses with user functions, +# i.e. '((' vs. '( ('. +sp_func_call_user_paren_paren = ignore # ignore/add/remove/force + +# Add or remove space between a constructor/destructor and the open +# parenthesis. +sp_func_class_paren = remove # ignore/add/remove/force + +# Add or remove space between a constructor without parameters or destructor +# and '()'. +sp_func_class_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space between 'return' and '('. +sp_return_paren = add # ignore/add/remove/force + +# Add or remove space between 'return' and '{'. +sp_return_brace = ignore # ignore/add/remove/force + +# Add or remove space between '__attribute__' and '('. +sp_attribute_paren = remove # ignore/add/remove/force + +# Add or remove space between 'defined' and '(' in '#if defined (FOO)'. +sp_defined_paren = remove # ignore/add/remove/force + +# Add or remove space between 'throw' and '(' in 'throw (something)'. +sp_throw_paren = remove # ignore/add/remove/force + +# Add or remove space between 'throw' and anything other than '(' as in +# '@throw [...];'. +sp_after_throw = ignore # ignore/add/remove/force + +# Add or remove space between 'catch' and '(' in 'catch (something) { }'. +# If set to ignore, sp_before_sparen is used. +sp_catch_paren = ignore # ignore/add/remove/force + +# (OC) Add or remove space between '@catch' and '(' +# in '@catch (something) { }'. If set to ignore, sp_catch_paren is used. +sp_oc_catch_paren = ignore # ignore/add/remove/force + +# (OC) Add or remove space between class name and '(' +# in '@interface className(categoryName):BaseClass' +sp_oc_classname_paren = ignore # ignore/add/remove/force + +# (D) Add or remove space between 'version' and '(' +# in 'version (something) { }'. If set to ignore, sp_before_sparen is used. +sp_version_paren = ignore # ignore/add/remove/force + +# (D) Add or remove space between 'scope' and '(' +# in 'scope (something) { }'. If set to ignore, sp_before_sparen is used. +sp_scope_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'super' and '(' in 'super (something)'. +# +# Default: remove +sp_super_paren = remove # ignore/add/remove/force + +# Add or remove space between 'this' and '(' in 'this (something)'. +# +# Default: remove +sp_this_paren = remove # ignore/add/remove/force + +# Add or remove space between a macro name and its definition. +sp_macro = add # ignore/add/remove/force + +# Add or remove space between a macro function ')' and its definition. +sp_macro_func = remove # ignore/add/remove/force + +# Add or remove space between 'else' and '{' if on the same line. +sp_else_brace = add # ignore/add/remove/force + +# Add or remove space between '}' and 'else' if on the same line. +sp_brace_else = add # ignore/add/remove/force + +# Add or remove space between '}' and the name of a typedef on the same line. +sp_brace_typedef = add # ignore/add/remove/force + +# Add or remove space before the '{' of a 'catch' statement, if the '{' and +# 'catch' are on the same line, as in 'catch (decl) {'. +sp_catch_brace = add # ignore/add/remove/force + +# (OC) Add or remove space before the '{' of a '@catch' statement, if the '{' +# and '@catch' are on the same line, as in '@catch (decl) {'. +# If set to ignore, sp_catch_brace is used. +sp_oc_catch_brace = ignore # ignore/add/remove/force + +# Add or remove space between '}' and 'catch' if on the same line. +sp_brace_catch = add # ignore/add/remove/force + +# (OC) Add or remove space between '}' and '@catch' if on the same line. +# If set to ignore, sp_brace_catch is used. +sp_oc_brace_catch = ignore # ignore/add/remove/force + +# Add or remove space between 'finally' and '{' if on the same line. +sp_finally_brace = add # ignore/add/remove/force + +# Add or remove space between '}' and 'finally' if on the same line. +sp_brace_finally = add # ignore/add/remove/force + +# Add or remove space between 'try' and '{' if on the same line. +sp_try_brace = add # ignore/add/remove/force + +# Add or remove space between get/set and '{' if on the same line. +sp_getset_brace = add # ignore/add/remove/force + +# Add or remove space between a variable and '{' for C++ uniform +# initialization. +# +# Default: add +sp_word_brace = add # ignore/add/remove/force + +# Add or remove space between a variable and '{' for a namespace. +# +# Default: add +sp_word_brace_ns = add # ignore/add/remove/force + +# Add or remove space before the '::' operator. +sp_before_dc = remove # ignore/add/remove/force + +# Add or remove space after the '::' operator. +sp_after_dc = remove # ignore/add/remove/force + +# (D) Add or remove around the D named array initializer ':' operator. +sp_d_array_colon = ignore # ignore/add/remove/force + +# Add or remove space after the '!' (not) unary operator. +# +# Default: remove +sp_not = remove # ignore/add/remove/force + +# Add or remove space after the '~' (invert) unary operator. +# +# Default: remove +sp_inv = remove # ignore/add/remove/force + +# Add or remove space after the '&' (address-of) unary operator. This does not +# affect the spacing after a '&' that is part of a type. +# +# Default: remove +sp_addr = remove # ignore/add/remove/force + +# Add or remove space around the '.' or '->' operators. +# +# Default: remove +sp_member = remove # ignore/add/remove/force + +# Add or remove space after the '*' (dereference) unary operator. This does +# not affect the spacing after a '*' that is part of a type. +# +# Default: remove +sp_deref = remove # ignore/add/remove/force + +# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. +# +# Default: remove +sp_sign = remove # ignore/add/remove/force + +# Add or remove space between '++' and '--' the word to which it is being +# applied, as in '(--x)' or 'y++;'. +# +# Default: remove +sp_incdec = remove # ignore/add/remove/force + +# Add or remove space before a backslash-newline at the end of a line. +# +# Default: add +sp_before_nl_cont = remove # ignore/add/remove/force + +# (OC) Add or remove space after the scope '+' or '-', as in '-(void) foo;' +# or '+(int) bar;'. +sp_after_oc_scope = remove # ignore/add/remove/force + +# (OC) Add or remove space after the colon in message specs, +# i.e. '-(int) f:(int) x;' vs. '-(int) f: (int) x;'. +sp_after_oc_colon = remove # ignore/add/remove/force + +# (OC) Add or remove space before the colon in message specs, +# i.e. '-(int) f: (int) x;' vs. '-(int) f : (int) x;'. +sp_before_oc_colon = remove # ignore/add/remove/force + +# (OC) Add or remove space after the colon in immutable dictionary expression +# 'NSDictionary *test = @{@"foo" :@"bar"};'. +sp_after_oc_dict_colon = ignore # ignore/add/remove/force + +# (OC) Add or remove space before the colon in immutable dictionary expression +# 'NSDictionary *test = @{@"foo" :@"bar"};'. +sp_before_oc_dict_colon = ignore # ignore/add/remove/force + +# (OC) Add or remove space after the colon in message specs, +# i.e. '[object setValue:1];' vs. '[object setValue: 1];'. +sp_after_send_oc_colon = add # ignore/add/remove/force + +# (OC) Add or remove space before the colon in message specs, +# i.e. '[object setValue:1];' vs. '[object setValue :1];'. +sp_before_send_oc_colon = remove # ignore/add/remove/force + +# (OC) Add or remove space after the (type) in message specs, +# i.e. '-(int)f: (int) x;' vs. '-(int)f: (int)x;'. +sp_after_oc_type = remove # ignore/add/remove/force + +# (OC) Add or remove space after the first (type) in message specs, +# i.e. '-(int) f:(int)x;' vs. '-(int)f:(int)x;'. +sp_after_oc_return_type = ignore # ignore/add/remove/force + +# (OC) Add or remove space between '@selector' and '(', +# i.e. '@selector(msgName)' vs. '@selector (msgName)'. +# Also applies to '@protocol()' constructs. +sp_after_oc_at_sel = ignore # ignore/add/remove/force + +# (OC) Add or remove space between '@selector(x)' and the following word, +# i.e. '@selector(foo) a:' vs. '@selector(foo)a:'. +sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force + +# (OC) Add or remove space inside '@selector' parentheses, +# i.e. '@selector(foo)' vs. '@selector( foo )'. +# Also applies to '@protocol()' constructs. +sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force + +# (OC) Add or remove space before a block pointer caret, +# i.e. '^int (int arg){...}' vs. ' ^int (int arg){...}'. +sp_before_oc_block_caret = ignore # ignore/add/remove/force + +# (OC) Add or remove space after a block pointer caret, +# i.e. '^int (int arg){...}' vs. '^ int (int arg){...}'. +sp_after_oc_block_caret = ignore # ignore/add/remove/force + +# (OC) Add or remove space between the receiver and selector in a message, +# as in '[receiver selector ...]'. +sp_after_oc_msg_receiver = ignore # ignore/add/remove/force + +# (OC) Add or remove space after '@property'. +sp_after_oc_property = ignore # ignore/add/remove/force + +# (OC) Add or remove space between '@synchronized' and the open parenthesis, +# i.e. '@synchronized(foo)' vs. '@synchronized (foo)'. +sp_after_oc_synchronized = ignore # ignore/add/remove/force + +# Add or remove space around the ':' in 'b ? t : f'. +sp_cond_colon = add # ignore/add/remove/force + +# Add or remove space before the ':' in 'b ? t : f'. +# +# Overrides sp_cond_colon. +sp_cond_colon_before = ignore # ignore/add/remove/force + +# Add or remove space after the ':' in 'b ? t : f'. +# +# Overrides sp_cond_colon. +sp_cond_colon_after = ignore # ignore/add/remove/force + +# Add or remove space around the '?' in 'b ? t : f'. +sp_cond_question = add # ignore/add/remove/force + +# Add or remove space before the '?' in 'b ? t : f'. +# +# Overrides sp_cond_question. +sp_cond_question_before = ignore # ignore/add/remove/force + +# Add or remove space after the '?' in 'b ? t : f'. +# +# Overrides sp_cond_question. +sp_cond_question_after = ignore # ignore/add/remove/force + +# In the abbreviated ternary form '(a ?: b)', add or remove space between '?' +# and ':'. +# +# Overrides all other sp_cond_* options. +sp_cond_ternary_short = ignore # ignore/add/remove/force + +# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make +# sense here. +sp_case_label = ignore # ignore/add/remove/force + +# (D) Add or remove space around the D '..' operator. +sp_range = ignore # ignore/add/remove/force + +# Add or remove space after ':' in a Java/C++11 range-based 'for', +# as in 'for (Type var : expr)'. +sp_after_for_colon = ignore # ignore/add/remove/force + +# Add or remove space before ':' in a Java/C++11 range-based 'for', +# as in 'for (Type var : expr)'. +sp_before_for_colon = ignore # ignore/add/remove/force + +# (D) Add or remove space between 'extern' and '(' as in 'extern (C)'. +sp_extern_paren = ignore # ignore/add/remove/force + +# Add or remove space after the opening of a C++ comment, +# i.e. '// A' vs. '//A'. +sp_cmt_cpp_start = add # ignore/add/remove/force + +# If true, space is added with sp_cmt_cpp_start will be added after doxygen +# sequences like '///', '///<', '//!' and '//!<'. +sp_cmt_cpp_doxygen = false # true/false + +# If true, space is added with sp_cmt_cpp_start will be added after Qt +# translator or meta-data comments like '//:', '//=', and '//~'. +sp_cmt_cpp_qttr = false # true/false + +# Add or remove space between #else or #endif and a trailing comment. +sp_endif_cmt = ignore # ignore/add/remove/force + +# Add or remove space after 'new', 'delete' and 'delete[]'. +sp_after_new = ignore # ignore/add/remove/force + +# Add or remove space between 'new' and '(' in 'new()'. +sp_between_new_paren = ignore # ignore/add/remove/force + +# Add or remove space between ')' and type in 'new(foo) BAR'. +sp_after_newop_paren = ignore # ignore/add/remove/force + +# Add or remove space inside parenthesis of the new operator +# as in 'new(foo) BAR'. +sp_inside_newop_paren = ignore # ignore/add/remove/force + +# Add or remove space after the open parenthesis of the new operator, +# as in 'new(foo) BAR'. +# +# Overrides sp_inside_newop_paren. +sp_inside_newop_paren_open = ignore # ignore/add/remove/force + +# Add or remove space before the close parenthesis of the new operator, +# as in 'new(foo) BAR'. +# +# Overrides sp_inside_newop_paren. +sp_inside_newop_paren_close = ignore # ignore/add/remove/force + +# Add or remove space before a trailing or embedded comment. +sp_before_tr_emb_cmt = ignore # ignore/add/remove/force + +# Number of spaces before a trailing or embedded comment. +sp_num_before_tr_emb_cmt = 0 # unsigned number + +# (Java) Add or remove space between an annotation and the open parenthesis. +sp_annotation_paren = ignore # ignore/add/remove/force + +# If true, vbrace tokens are dropped to the previous token and skipped. +sp_skip_vbrace_tokens = false # true/false + +# Add or remove space after 'noexcept'. +sp_after_noexcept = ignore # ignore/add/remove/force + +# Add or remove space after '_'. +sp_vala_after_translation = ignore # ignore/add/remove/force + +# If true, a is inserted after #define. +force_tab_after_define = false # true/false + +# +# Indenting options +# + +# The number of columns to indent per level. Usually 2, 3, 4, or 8. +# +# Default: 8 +indent_columns = 2 # unsigned number + +# The continuation indent. If non-zero, this overrides the indent of '(', '[' +# and '=' continuation indents. Negative values are OK; negative value is +# absolute and not increased for each '(' or '[' level. +# +# For FreeBSD, this is set to 4. +indent_continue = 0 # number + +# The continuation indent, only for class header line(s). If non-zero, this +# overrides the indent of 'class' continuation indents. +indent_continue_class_head = 0 # unsigned number + +# Whether to indent empty lines (i.e. lines which contain only spaces before +# the newline character). +indent_single_newlines = false # true/false + +# The continuation indent for func_*_param if they are true. If non-zero, this +# overrides the indent. +indent_param = 0 # unsigned number + +# How to use tabs when indenting code. +# +# 0: Spaces only +# 1: Indent with tabs to brace level, align with spaces (default) +# 2: Indent and align with tabs, using spaces when not on a tabstop +# +# Default: 1 +indent_with_tabs = 0 # unsigned number + +# Whether to indent comments that are not at a brace level with tabs on a +# tabstop. Requires indent_with_tabs=2. If false, will use spaces. +indent_cmt_with_tabs = false # true/false + +# Whether to indent strings broken by '\' so that they line up. +indent_align_string = true # true/false + +# The number of spaces to indent multi-line XML strings. +# Requires indent_align_string=true. +indent_xml_string = 0 # unsigned number + +# Spaces to indent '{' from level. +indent_brace = 0 # unsigned number + +# Whether braces are indented to the body level. +indent_braces = false # true/false + +# Whether to disable indenting function braces if indent_braces=true. +indent_braces_no_func = false # true/false + +# Whether to disable indenting class braces if indent_braces=true. +indent_braces_no_class = false # true/false + +# Whether to disable indenting struct braces if indent_braces=true. +indent_braces_no_struct = false # true/false + +# Whether to indent based on the size of the brace parent, +# i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. +indent_brace_parent = false # true/false + +# Whether to indent based on the open parenthesis instead of the open brace +# in '({\n'. +indent_paren_open_brace = false # true/false + +# (C#) Whether to indent the brace of a C# delegate by another level. +indent_cs_delegate_brace = false # true/false + +# (C#) Whether to indent a C# delegate (to handle delegates with no brace) by +# another level. +indent_cs_delegate_body = false # true/false + +# Whether to indent the body of a 'namespace'. +indent_namespace = false # true/false + +# Whether to indent only the first namespace, and not any nested namespaces. +# Requires indent_namespace=true. +indent_namespace_single_indent = false # true/false + +# The number of spaces to indent a namespace block. +# If set to zero, use the value indent_columns +indent_namespace_level = 0 # unsigned number + +# If the body of the namespace is longer than this number, it won't be +# indented. Requires indent_namespace=true. 0 means no limit. +indent_namespace_limit = 0 # unsigned number + +# Whether the 'extern "C"' body is indented. +indent_extern = false # true/false + +# Whether the 'class' body is indented. +indent_class = true # true/false + +# Whether to indent the stuff after a leading base class colon. +indent_class_colon = true # true/false + +# Whether to indent based on a class colon instead of the stuff after the +# colon. Requires indent_class_colon=true. +indent_class_on_colon = false # true/false + +# Whether to indent the stuff after a leading class initializer colon. +indent_constr_colon = false # true/false + +# Virtual indent from the ':' for member initializers. +# +# Default: 2 +indent_ctor_init_leading = 2 # unsigned number + +# Additional indent for constructor initializer list. +# Negative values decrease indent down to the first column. +indent_ctor_init = 0 # number + +# Whether to indent 'if' following 'else' as a new block under the 'else'. +# If false, 'else\nif' is treated as 'else if' for indenting purposes. +indent_else_if = false # true/false + +# Amount to indent variable declarations after a open brace. +# +# <0: Relative +# >=0: Absolute +indent_var_def_blk = 0 # number + +# Whether to indent continued variable declarations instead of aligning. +indent_var_def_cont = false # true/false + +# Whether to indent continued shift expressions ('<<' and '>>') instead of +# aligning. Set align_left_shift=false when enabling this. +indent_shift = false # true/false + +# Whether to force indentation of function definitions to start in column 1. +indent_func_def_force_col1 = false # true/false + +# Whether to indent continued function call parameters one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_call_param = false # true/false + +# Same as indent_func_call_param, but for function definitions. +indent_func_def_param = false # true/false + +# Same as indent_func_call_param, but for function prototypes. +indent_func_proto_param = false # true/false + +# Same as indent_func_call_param, but for class declarations. +indent_func_class_param = false # true/false + +# Same as indent_func_call_param, but for class variable constructors. +indent_func_ctor_var_param = false # true/false + +# Same as indent_func_call_param, but for template parameter lists. +indent_template_param = false # true/false + +# Double the indent for indent_func_xxx_param options. +# Use both values of the options indent_columns and indent_param. +indent_func_param_double = false # true/false + +# Indentation column for standalone 'const' qualifier on a function +# prototype. +indent_func_const = 0 # unsigned number + +# Indentation column for standalone 'throw' qualifier on a function +# prototype. +indent_func_throw = 0 # unsigned number + +# The number of spaces to indent a continued '->' or '.'. +# Usually set to 0, 1, or indent_columns. +indent_member = 0 # unsigned number + +# Whether lines broken at '.' or '->' should be indented by a single indent. +# The indent_member option will not be effective if this is set to true. +indent_member_single = false # true/false + +# Spaces to indent single line ('//') comments on lines before code. +indent_sing_line_comments = 0 # unsigned number + +# Whether to indent trailing single line ('//') comments relative to the code +# instead of trying to keep the same absolute column. +indent_relative_single_line_comments = false # true/false + +# Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns. +indent_switch_case = 2 # unsigned number + +# Whether to indent preprocessor statements inside of switch statements. +# +# Default: true +indent_switch_pp = true # true/false + +# Spaces to shift the 'case' line, without affecting any other lines. +# Usually 0. +indent_case_shift = 0 # unsigned number + +# Spaces to indent '{' from 'case'. By default, the brace will appear under +# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK. +indent_case_brace = 0 # number + +# Whether to indent comments found in first column. +indent_col1_comment = true # true/false + +# Whether to indent multi string literal in first column. +indent_col1_multi_string_literal = false # true/false + +# How to indent goto labels. +# +# >0: Absolute column where 1 is the leftmost column +# <=0: Subtract from brace indent +# +# Default: 1 +indent_label = 1 # number + +# Same as indent_label, but for access specifiers that are followed by a +# colon. +# +# Default: 1 +indent_access_spec = 1 # number + +# Whether to indent the code after an access specifier by one level. +# If true, this option forces 'indent_access_spec=0'. +indent_access_spec_body = false # true/false + +# If an open parenthesis is followed by a newline, whether to indent the next +# line so that it lines up after the open parenthesis (not recommended). +indent_paren_nl = false # true/false + +# How to indent a close parenthesis after a newline. +# +# 0: Indent to body level (default) +# 1: Align under the open parenthesis +# 2: Indent to the brace level +indent_paren_close = 0 # unsigned number + +# Whether to indent the open parenthesis of a function definition, +# if the parenthesis is on its own line. +indent_paren_after_func_def = false # true/false + +# Whether to indent the open parenthesis of a function declaration, +# if the parenthesis is on its own line. +indent_paren_after_func_decl = false # true/false + +# Whether to indent the open parenthesis of a function call, +# if the parenthesis is on its own line. +indent_paren_after_func_call = false # true/false + +# Whether to indent a comma when inside a parenthesis. +# If true, aligns under the open parenthesis. +indent_comma_paren = false # true/false + +# Whether to indent a Boolean operator when inside a parenthesis. +# If true, aligns under the open parenthesis. +indent_bool_paren = false # true/false + +# Whether to indent a semicolon when inside a for parenthesis. +# If true, aligns under the open for parenthesis. +indent_semicolon_for_paren = false # true/false + +# Whether to align the first expression to following ones +# if indent_bool_paren=true. +indent_first_bool_expr = false # true/false + +# Whether to align the first expression to following ones +# if indent_semicolon_for_paren=true. +indent_first_for_expr = false # true/false + +# If an open square is followed by a newline, whether to indent the next line +# so that it lines up after the open square (not recommended). +indent_square_nl = false # true/false + +# (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies. +indent_preserve_sql = false # true/false + +# Whether to align continued statements at the '='. If false or if the '=' is +# followed by a newline, the next line is indent one tab. +# +# Default: true +indent_align_assign = true # true/false + +# Whether to align continued statements at the '('. If false or the '(' is +# followed by a newline, the next line indent is one tab. +# +# Default: true +indent_align_paren = true # true/false + +# (OC) Whether to indent Objective-C blocks at brace level instead of usual +# rules. +indent_oc_block = false # true/false + +# (OC) Indent for Objective-C blocks in a message relative to the parameter +# name. +# +# =0: Use indent_oc_block rules +# >0: Use specified number of spaces to indent +indent_oc_block_msg = 0 # unsigned number + +# (OC) Minimum indent for subsequent parameters +indent_oc_msg_colon = 0 # unsigned number + +# (OC) Whether to prioritize aligning with initial colon (and stripping spaces +# from lines, if necessary). +# +# Default: true +indent_oc_msg_prioritize_first_colon = true # true/false + +# (OC) Whether to indent blocks the way that Xcode does by default +# (from the keyword if the parameter is on its own line; otherwise, from the +# previous indentation level). Requires indent_oc_block_msg=true. +indent_oc_block_msg_xcode_style = false # true/false + +# (OC) Whether to indent blocks from where the brace is, relative to a +# message keyword. Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_keyword = false # true/false + +# (OC) Whether to indent blocks from where the brace is, relative to a message +# colon. Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_colon = false # true/false + +# (OC) Whether to indent blocks from where the block caret is. +# Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_caret = false # true/false + +# (OC) Whether to indent blocks from where the brace caret is. +# Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_brace = false # true/false + +# When indenting after virtual brace open and newline add further spaces to +# reach this minimum indent. +indent_min_vbrace_open = 0 # unsigned number + +# Whether to add further spaces after regular indent to reach next tabstop +# when identing after virtual brace open and newline. +indent_vbrace_open_on_tabstop = false # true/false + +# How to indent after a brace followed by another token (not a newline). +# true: indent all contained lines to match the token +# false: indent all contained lines to match the brace +# +# Default: true +indent_token_after_brace = true # true/false + +# Whether to indent the body of a C++11 lambda. +indent_cpp_lambda_body = false # true/false + +# (C#) Whether to indent a 'using' block if no braces are used. +# +# Default: true +indent_using_block = true # true/false + +# How to indent the continuation of ternary operator. +# +# 0: Off (default) +# 1: When the `if_false` is a continuation, indent it under `if_false` +# 2: When the `:` is a continuation, indent it under `?` +indent_ternary_operator = 0 # unsigned number + +# If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column. +indent_off_after_return_new = false # true/false + +# If true, the tokens after return are indented with regular single indentation. By default (false) the indentation is after the return token. +indent_single_after_return = false # true/false + +# Whether to ignore indent and alignment for 'asm' blocks (i.e. assume they +# have their own indentation). +indent_ignore_asm_block = false # true/false + +# +# Newline adding and removing options +# + +# Whether to collapse empty blocks between '{' and '}'. +nl_collapse_empty_body = true # true/false + +# Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'. +nl_assign_leave_one_liners = false # true/false + +# Don't split one-line braced statements inside a 'class xx { }' body. +nl_class_leave_one_liners = false # true/false + +# Don't split one-line enums, as in 'enum foo { BAR = 15 };' +nl_enum_leave_one_liners = false # true/false + +# Don't split one-line get or set functions. +nl_getset_leave_one_liners = false # true/false + +# (C#) Don't split one-line property get or set functions. +nl_cs_property_leave_one_liners = false # true/false + +# Don't split one-line function definitions, as in 'int foo() { return 0; }'. +nl_func_leave_one_liners = false # true/false + +# Don't split one-line C++11 lambdas, as in '[]() { return 0; }'. +nl_cpp_lambda_leave_one_liners = false # true/false + +# Don't split one-line if/else statements, as in 'if(...) b++;'. +nl_if_leave_one_liners = false # true/false + +# Don't split one-line while statements, as in 'while(...) b++;'. +nl_while_leave_one_liners = false # true/false + +# Don't split one-line for statements, as in 'for(...) b++;'. +nl_for_leave_one_liners = false # true/false + +# (OC) Don't split one-line Objective-C messages. +nl_oc_msg_leave_one_liner = false # true/false + +# (OC) Add or remove newline between method declaration and '{'. +nl_oc_mdef_brace = ignore # ignore/add/remove/force + +# (OC) Add or remove newline between Objective-C block signature and '{'. +nl_oc_block_brace = ignore # ignore/add/remove/force + +# (OC) Add or remove newline between '@interface' and '{'. +nl_oc_interface_brace = ignore # ignore/add/remove/force + +# (OC) Add or remove newline between '@implementation' and '{'. +nl_oc_implementation_brace = ignore # ignore/add/remove/force + +# Add or remove newlines at the start of the file. +nl_start_of_file = ignore # ignore/add/remove/force + +# The minimum number of newlines at the start of the file (only used if +# nl_start_of_file is 'add' or 'force'). +nl_start_of_file_min = 0 # unsigned number + +# Add or remove newline at the end of the file. +nl_end_of_file = add # ignore/add/remove/force + +# The minimum number of newlines at the end of the file (only used if +# nl_end_of_file is 'add' or 'force'). +nl_end_of_file_min = 1 # unsigned number + +# Add or remove newline between '=' and '{'. +nl_assign_brace = ignore # ignore/add/remove/force + +# (D) Add or remove newline between '=' and '['. +nl_assign_square = ignore # ignore/add/remove/force + +# Add or remove newline between '[]' and '{'. +nl_tsquare_brace = ignore # ignore/add/remove/force + +# (D) Add or remove newline after '= ['. Will also affect the newline before +# the ']'. +nl_after_square_assign = ignore # ignore/add/remove/force + +# Add or remove newline between a function call's ')' and '{', as in +# 'list_for_each(item, &list) { }'. +nl_fcall_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum' and '{'. +nl_enum_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum' and 'class'. +nl_enum_class = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum class' and the identifier. +nl_enum_class_identifier = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum class' type and ':'. +nl_enum_identifier_colon = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum class identifier :' and type. +nl_enum_colon_type = ignore # ignore/add/remove/force + +# Add or remove newline between 'struct and '{'. +nl_struct_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'union' and '{'. +nl_union_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'if' and '{'. +nl_if_brace = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and 'else'. +nl_brace_else = ignore # ignore/add/remove/force + +# Add or remove newline between 'else if' and '{'. If set to ignore, +# nl_if_brace is used instead. +nl_elseif_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'else' and '{'. +nl_else_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'else' and 'if'. +nl_else_if = ignore # ignore/add/remove/force + +# Add or remove newline before 'if'/'else if' closing parenthesis. +nl_before_if_closing_paren = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and 'finally'. +nl_brace_finally = ignore # ignore/add/remove/force + +# Add or remove newline between 'finally' and '{'. +nl_finally_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'try' and '{'. +nl_try_brace = ignore # ignore/add/remove/force + +# Add or remove newline between get/set and '{'. +nl_getset_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'for' and '{'. +nl_for_brace = ignore # ignore/add/remove/force + +# Add or remove newline before the '{' of a 'catch' statement, as in +# 'catch (decl) {'. +nl_catch_brace = ignore # ignore/add/remove/force + +# (OC) Add or remove newline before the '{' of a '@catch' statement, as in +# '@catch (decl) {'. If set to ignore, nl_catch_brace is used. +nl_oc_catch_brace = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and 'catch'. +nl_brace_catch = ignore # ignore/add/remove/force + +# (OC) Add or remove newline between '}' and '@catch'. If set to ignore, +# nl_brace_catch is used. +nl_oc_brace_catch = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and ']'. +nl_brace_square = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and ')' in a function invocation. +nl_brace_fparen = ignore # ignore/add/remove/force + +# Add or remove newline between 'while' and '{'. +nl_while_brace = ignore # ignore/add/remove/force + +# (D) Add or remove newline between 'scope (x)' and '{'. +nl_scope_brace = ignore # ignore/add/remove/force + +# (D) Add or remove newline between 'unittest' and '{'. +nl_unittest_brace = ignore # ignore/add/remove/force + +# (D) Add or remove newline between 'version (x)' and '{'. +nl_version_brace = ignore # ignore/add/remove/force + +# (C#) Add or remove newline between 'using' and '{'. +nl_using_brace = ignore # ignore/add/remove/force + +# Add or remove newline between two open or close braces. Due to general +# newline/brace handling, REMOVE may not work. +nl_brace_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'do' and '{'. +nl_do_brace = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and 'while' of 'do' statement. +nl_brace_while = ignore # ignore/add/remove/force + +# Add or remove newline between 'switch' and '{'. +nl_switch_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'synchronized' and '{'. +nl_synchronized_brace = ignore # ignore/add/remove/force + +# Add a newline between ')' and '{' if the ')' is on a different line than the +# if/for/etc. +# +# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and +# nl_catch_brace. +nl_multi_line_cond = false # true/false + +# Force a newline in a define after the macro name for multi-line defines. +nl_multi_line_define = false # true/false + +# Whether to add a newline before 'case', and a blank line before a 'case' +# statement that follows a ';' or '}'. +nl_before_case = false # true/false + +# Whether to add a newline after a 'case' statement. +nl_after_case = false # true/false + +# Add or remove newline between a case ':' and '{'. +# +# Overrides nl_after_case. +nl_case_colon_brace = ignore # ignore/add/remove/force + +# Add or remove newline between ')' and 'throw'. +nl_before_throw = ignore # ignore/add/remove/force + +# Add or remove newline between 'namespace' and '{'. +nl_namespace_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'template<>' and whatever follows. +nl_template_class = ignore # ignore/add/remove/force + +# Add or remove newline between 'class' and '{'. +nl_class_brace = remove # ignore/add/remove/force + +# Add or remove newline before or after (depending on pos_class_comma, +# may not be IGNORE) each',' in the base class list. +nl_class_init_args = add # ignore/add/remove/force + +# Add or remove newline after each ',' in the constructor member +# initialization. Related to nl_constr_colon, pos_constr_colon and +# pos_constr_comma. +nl_constr_init_args = ignore # ignore/add/remove/force + +# Add or remove newline before first element, after comma, and after last +# element, in 'enum'. +nl_enum_own_lines = ignore # ignore/add/remove/force + +# Add or remove newline between return type and function name in a function +# definition. +nl_func_type_name = ignore # ignore/add/remove/force + +# Add or remove newline between return type and function name inside a class +# definition. If set to ignore, nl_func_type_name or nl_func_proto_type_name +# is used instead. +nl_func_type_name_class = ignore # ignore/add/remove/force + +# Add or remove newline between class specification and '::' +# in 'void A::f() { }'. Only appears in separate member implementation (does +# not appear with in-line implementation). +nl_func_class_scope = ignore # ignore/add/remove/force + +# Add or remove newline between function scope and name, as in +# 'void A :: f() { }'. +nl_func_scope_name = ignore # ignore/add/remove/force + +# Add or remove newline between return type and function name in a prototype. +nl_func_proto_type_name = ignore # ignore/add/remove/force + +# Add or remove newline between a function name and the opening '(' in the +# declaration. +nl_func_paren = ignore # ignore/add/remove/force + +# Overrides nl_func_paren for functions with no parameters. +nl_func_paren_empty = ignore # ignore/add/remove/force + +# Add or remove newline between a function name and the opening '(' in the +# definition. +nl_func_def_paren = ignore # ignore/add/remove/force + +# Overrides nl_func_def_paren for functions with no parameters. +nl_func_def_paren_empty = ignore # ignore/add/remove/force + +# Add or remove newline between a function name and the opening '(' in the +# call. +nl_func_call_paren = ignore # ignore/add/remove/force + +# Overrides nl_func_call_paren for functions with no parameters. +nl_func_call_paren_empty = ignore # ignore/add/remove/force + +# Add or remove newline after '(' in a function declaration. +nl_func_decl_start = ignore # ignore/add/remove/force + +# Add or remove newline after '(' in a function definition. +nl_func_def_start = ignore # ignore/add/remove/force + +# Overrides nl_func_decl_start when there is only one parameter. +nl_func_decl_start_single = ignore # ignore/add/remove/force + +# Overrides nl_func_def_start when there is only one parameter. +nl_func_def_start_single = ignore # ignore/add/remove/force + +# Whether to add a newline after '(' in a function declaration if '(' and ')' +# are in different lines. If false, nl_func_decl_start is used instead. +nl_func_decl_start_multi_line = false # true/false + +# Whether to add a newline after '(' in a function definition if '(' and ')' +# are in different lines. If false, nl_func_def_start is used instead. +nl_func_def_start_multi_line = false # true/false + +# Add or remove newline after each ',' in a function declaration. +nl_func_decl_args = add # ignore/add/remove/force + +# Add or remove newline after each ',' in a function definition. +nl_func_def_args = ignore # ignore/add/remove/force + +# Whether to add a newline after each ',' in a function declaration if '(' +# and ')' are in different lines. If false, nl_func_decl_args is used instead. +nl_func_decl_args_multi_line = false # true/false + +# Whether to add a newline after each ',' in a function definition if '(' +# and ')' are in different lines. If false, nl_func_def_args is used instead. +nl_func_def_args_multi_line = false # true/false + +# Add or remove newline before the ')' in a function declaration. +nl_func_decl_end = ignore # ignore/add/remove/force + +# Add or remove newline before the ')' in a function definition. +nl_func_def_end = ignore # ignore/add/remove/force + +# Overrides nl_func_decl_end when there is only one parameter. +nl_func_decl_end_single = ignore # ignore/add/remove/force + +# Overrides nl_func_def_end when there is only one parameter. +nl_func_def_end_single = ignore # ignore/add/remove/force + +# Whether to add a newline before ')' in a function declaration if '(' and ')' +# are in different lines. If false, nl_func_decl_end is used instead. +nl_func_decl_end_multi_line = false # true/false + +# Whether to add a newline before ')' in a function definition if '(' and ')' +# are in different lines. If false, nl_func_def_end is used instead. +nl_func_def_end_multi_line = false # true/false + +# Add or remove newline between '()' in a function declaration. +nl_func_decl_empty = ignore # ignore/add/remove/force + +# Add or remove newline between '()' in a function definition. +nl_func_def_empty = ignore # ignore/add/remove/force + +# Add or remove newline between '()' in a function call. +nl_func_call_empty = ignore # ignore/add/remove/force + +# Whether to add a newline after '(' in a function call, +# has preference over nl_func_call_start_multi_line. +nl_func_call_start = ignore # ignore/add/remove/force + +# Whether to add a newline after '(' in a function call if '(' and ')' are in +# different lines. +nl_func_call_start_multi_line = false # true/false + +# Whether to add a newline after each ',' in a function call if '(' and ')' +# are in different lines. +nl_func_call_args_multi_line = false # true/false + +# Whether to add a newline before ')' in a function call if '(' and ')' are in +# different lines. +nl_func_call_end_multi_line = false # true/false + +# (OC) Whether to put each Objective-C message parameter on a separate line. +# See nl_oc_msg_leave_one_liner. +nl_oc_msg_args = false # true/false + +# Add or remove newline between function signature and '{'. +nl_fdef_brace = ignore # ignore/add/remove/force + +# Add or remove newline between function signature and '{', +# if signature ends with ')'. Overrides nl_fdef_brace. +nl_fdef_brace_cond = ignore # ignore/add/remove/force + +# Add or remove newline between C++11 lambda signature and '{'. +nl_cpp_ldef_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'return' and the return expression. +nl_return_expr = ignore # ignore/add/remove/force + +# Whether to add a newline after semicolons, except in 'for' statements. +nl_after_semicolon = false # true/false + +# (Java) Add or remove newline between the ')' and '{{' of the double brace +# initializer. +nl_paren_dbrace_open = ignore # ignore/add/remove/force + +# Whether to add a newline after the type in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst = ignore # ignore/add/remove/force + +# Whether to add a newline after the open brace in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst_open = ignore # ignore/add/remove/force + +# Whether to add a newline before the close brace in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst_close = ignore # ignore/add/remove/force + +# Whether to add a newline after '{'. This also adds a newline before the +# matching '}'. +nl_after_brace_open = false # true/false + +# Whether to add a newline between the open brace and a trailing single-line +# comment. Requires nl_after_brace_open=true. +nl_after_brace_open_cmt = false # true/false + +# Whether to add a newline after a virtual brace open with a non-empty body. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open = false # true/false + +# Whether to add a newline after a virtual brace open with an empty body. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open_empty = false # true/false + +# Whether to add a newline after '}'. Does not apply if followed by a +# necessary ';'. +nl_after_brace_close = false # true/false + +# Whether to add a newline after a virtual brace close, +# as in 'if (foo) a++; return;'. +nl_after_vbrace_close = false # true/false + +# Add or remove newline between the close brace and identifier, +# as in 'struct { int a; } b;'. Affects enumerations, unions and +# structures. If set to ignore, uses nl_after_brace_close. +nl_brace_struct_var = ignore # ignore/add/remove/force + +# Whether to alter newlines in '#define' macros. +nl_define_macro = true # true/false + +# Whether to alter newlines between consecutive parenthesis closes. The number +# of closing parentheses in a line will depend on respective open parenthesis +# lines. +nl_squeeze_paren_close = false # true/false + +# Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and +# '#endif'. Does not affect top-level #ifdefs. +nl_squeeze_ifdef = false # true/false + +# Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well. +nl_squeeze_ifdef_top_level = false # true/false + +# Add or remove blank line before 'if'. +nl_before_if = add # ignore/add/remove/force + +# Add or remove blank line after 'if' statement. Add/Force work only if the +# next token is not a closing brace. +nl_after_if = ignore # ignore/add/remove/force + +# Add or remove blank line before 'for'. +nl_before_for = add # ignore/add/remove/force + +# Add or remove blank line after 'for' statement. +nl_after_for = ignore # ignore/add/remove/force + +# Add or remove blank line before 'while'. +nl_before_while = add # ignore/add/remove/force + +# Add or remove blank line after 'while' statement. +nl_after_while = ignore # ignore/add/remove/force + +# Add or remove blank line before 'switch'. +nl_before_switch = add # ignore/add/remove/force + +# Add or remove blank line after 'switch' statement. +nl_after_switch = ignore # ignore/add/remove/force + +# Add or remove blank line before 'synchronized'. +nl_before_synchronized = ignore # ignore/add/remove/force + +# Add or remove blank line after 'synchronized' statement. +nl_after_synchronized = ignore # ignore/add/remove/force + +# Add or remove blank line before 'do'. +nl_before_do = add # ignore/add/remove/force + +# Add or remove blank line after 'do/while' statement. +nl_after_do = ignore # ignore/add/remove/force + +# Whether to put a blank line before 'return' statements, unless after an open +# brace. +nl_before_return = false # true/false + +# Whether to put a blank line after 'return' statements, unless followed by a +# close brace. +nl_after_return = false # true/false + +# Whether to double-space commented-entries in 'struct'/'union'/'enum'. +nl_ds_struct_enum_cmt = true # true/false + +# Whether to force a newline before '}' of a 'struct'/'union'/'enum'. +# (Lower priority than eat_blanks_before_close_brace.) +nl_ds_struct_enum_close_brace = true # true/false + +# Add or remove newline before or after (depending on pos_class_colon) a class +# colon, as in 'class Foo : public Bar'. +nl_class_colon = ignore # ignore/add/remove/force + +# Add or remove newline around a class constructor colon. The exact position +# depends on nl_constr_init_args, pos_constr_colon and pos_constr_comma. +nl_constr_colon = ignore # ignore/add/remove/force + +# Whether to collapse a two-line namespace, like 'namespace foo\n{ decl; }' +# into a single line. If true, prevents other brace newline rules from turning +# such code into four lines. +nl_namespace_two_to_one_liner = false # true/false + +# Whether to remove a newline in simple unbraced if statements, turning them +# into one-liners, as in 'if(b)\n i++;' => 'if(b) i++;'. +nl_create_if_one_liner = true # true/false + +# Whether to remove a newline in simple unbraced for statements, turning them +# into one-liners, as in 'for (...)\n stmt;' => 'for (...) stmt;'. +nl_create_for_one_liner = true # true/false + +# Whether to remove a newline in simple unbraced while statements, turning +# them into one-liners, as in 'while (expr)\n stmt;' => 'while (expr) stmt;'. +nl_create_while_one_liner = true # true/false + +# Whether to collapse a function definition whose body (not counting braces) +# is only one line so that the entire definition (prototype, braces, body) is +# a single line. +nl_create_func_def_one_liner = false # true/false + +# Whether to split one-line simple unbraced if statements into two lines by +# adding a newline, as in 'if(b) i++;'. +nl_split_if_one_liner = false # true/false + +# Whether to split one-line simple unbraced for statements into two lines by +# adding a newline, as in 'for (...) stmt;'. +nl_split_for_one_liner = false # true/false + +# Whether to split one-line simple unbraced while statements into two lines by +# adding a newline, as in 'while (expr) stmt;'. +nl_split_while_one_liner = false # true/false + +# +# Blank line options +# + +# The maximum number of consecutive newlines (3 = 2 blank lines). +nl_max = 3 # unsigned number + +# The maximum number of consecutive newlines in a function. +nl_max_blank_in_func = 0 # unsigned number + +# The number of newlines before a function prototype. +nl_before_func_body_proto = 0 # unsigned number + +# The number of newlines before a multi-line function definition. +nl_before_func_body_def = 0 # unsigned number + +# The number of newlines before a class constructor/destructor prototype. +nl_before_func_class_proto = 0 # unsigned number + +# The number of newlines before a class constructor/destructor definition. +nl_before_func_class_def = 0 # unsigned number + +# The number of newlines after a function prototype. +nl_after_func_proto = 0 # unsigned number + +# The number of newlines after a function prototype, if not followed by +# another function prototype. +nl_after_func_proto_group = 0 # unsigned number + +# The number of newlines after a class constructor/destructor prototype. +nl_after_func_class_proto = 0 # unsigned number + +# The number of newlines after a class constructor/destructor prototype, +# if not followed by another constructor/destructor prototype. +nl_after_func_class_proto_group = 0 # unsigned number + +# Whether one-line method definitions inside a class body should be treated +# as if they were prototypes for the purposes of adding newlines. +# +# Requires nl_class_leave_one_liners=true. Overrides nl_before_func_body_def +# and nl_before_func_class_def for one-liners. +nl_class_leave_one_liner_groups = false # true/false + +# The number of newlines after '}' of a multi-line function body. +nl_after_func_body = 2 # unsigned number + +# The number of newlines after '}' of a multi-line function body in a class +# declaration. Also affects class constructors/destructors. +# +# Overrides nl_after_func_body. +nl_after_func_body_class = 0 # unsigned number + +# The number of newlines after '}' of a single line function body. Also +# affects class constructors/destructors. +# +# Overrides nl_after_func_body and nl_after_func_body_class. +nl_after_func_body_one_liner = 2 # unsigned number + +# The number of blank lines after a block of variable definitions at the top +# of a function body. +# +# 0 = No change (default). +nl_func_var_def_blk = 1 # unsigned number + +# The number of newlines before a block of typedefs. If nl_after_access_spec +# is non-zero, that option takes precedence. +# +# 0 = No change (default). +nl_typedef_blk_start = 0 # unsigned number + +# The number of newlines after a block of typedefs. +# +# 0 = No change (default). +nl_typedef_blk_end = 0 # unsigned number + +# The maximum number of consecutive newlines within a block of typedefs. +# +# 0 = No change (default). +nl_typedef_blk_in = 0 # unsigned number + +# The number of newlines before a block of variable definitions not at the top +# of a function body. If nl_after_access_spec is non-zero, that option takes +# precedence. +# +# 0 = No change (default). +nl_var_def_blk_start = 0 # unsigned number + +# The number of newlines after a block of variable definitions not at the top +# of a function body. +# +# 0 = No change (default). +nl_var_def_blk_end = 0 # unsigned number + +# The maximum number of consecutive newlines within a block of variable +# definitions. +# +# 0 = No change (default). +nl_var_def_blk_in = 0 # unsigned number + +# The minimum number of newlines before a multi-line comment. +# Doesn't apply if after a brace open or another multi-line comment. +nl_before_block_comment = 2 # unsigned number + +# The minimum number of newlines before a single-line C comment. +# Doesn't apply if after a brace open or other single-line C comments. +nl_before_c_comment = 2 # unsigned number + +# The minimum number of newlines before a CPP comment. +# Doesn't apply if after a brace open or other CPP comments. +nl_before_cpp_comment = 2 # unsigned number + +# Whether to force a newline after a multi-line comment. +nl_after_multiline_comment = true # true/false + +# Whether to force a newline after a label's colon. +nl_after_label_colon = false # true/false + +# The number of newlines after '}' or ';' of a struct/enum/union definition. +nl_after_struct = 0 # unsigned number + +# The number of newlines before a class definition. +nl_before_class = 0 # unsigned number + +# The number of newlines after '}' or ';' of a class definition. +nl_after_class = 0 # unsigned number + +# The number of newlines before an access specifier label. This also includes +# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count +# if after a brace open. +# +# 0 = No change (default). +nl_before_access_spec = 2 # unsigned number + +# The number of newlines after an access specifier label. This also includes +# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count +# if after a brace open. +# +# 0 = No change (default). +# +# Overrides nl_typedef_blk_start and nl_var_def_blk_start. +nl_after_access_spec = 2 # unsigned number + +# The number of newlines between a function definition and the function +# comment, as in '// comment\n void foo() {...}'. +# +# 0 = No change (default). +nl_comment_func_def = 1 # unsigned number + +# The number of newlines after a try-catch-finally block that isn't followed +# by a brace close. +# +# 0 = No change (default). +nl_after_try_catch_finally = 1 # unsigned number + +# (C#) The number of newlines before and after a property, indexer or event +# declaration. +# +# 0 = No change (default). +nl_around_cs_property = 0 # unsigned number + +# (C#) The number of newlines between the get/set/add/remove handlers. +# +# 0 = No change (default). +nl_between_get_set = 0 # unsigned number + +# (C#) Add or remove newline between property and the '{'. +nl_property_brace = ignore # ignore/add/remove/force + +# The number of newlines after '{' of a namespace. This also adds newlines +# before the matching '}'. +# +# 0 = Apply eat_blanks_after_open_brace or eat_blanks_before_close_brace if +# applicable, otherwise no change. +# +# Overrides eat_blanks_after_open_brace and eat_blanks_before_close_brace. +nl_inside_namespace = 0 # unsigned number + +# Whether to remove blank lines after '{'. +eat_blanks_after_open_brace = true # true/false + +# Whether to remove blank lines before '}'. +eat_blanks_before_close_brace = true # true/false + +# How aggressively to remove extra newlines not in preprocessor. +# +# 0: No change (default) +# 1: Remove most newlines not handled by other config +# 2: Remove all newlines and reformat completely by config +nl_remove_extra_newlines = 0 # unsigned number + +# (Java) Add or remove newline after an annotation statement. Only affects +# annotations that are after a newline. +nl_after_annotation = ignore # ignore/add/remove/force + +# (Java) Add or remove newline between two annotations. +nl_between_annotation = ignore # ignore/add/remove/force + +# +# Positioning options +# + +# The position of arithmetic operators in wrapped expressions. +pos_arith = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of assignment in wrapped expressions. Do not affect '=' +# followed by '{'. +pos_assign = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of Boolean operators in wrapped expressions. +pos_bool = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of comparison operators in wrapped expressions. +pos_compare = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of conditional operators, as in the '?' and ':' of +# 'expr ? stmt : stmt', in wrapped expressions. +pos_conditional = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in wrapped expressions. +pos_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in enum entries. +pos_enum_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in the base class list if there is more than one +# line. Affects nl_class_init_args. +pos_class_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in the constructor initialization list. +# Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon. +pos_constr_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of trailing/leading class colon, between class and base class +# list. Affects nl_class_colon. +pos_class_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of colons between constructor and member initialization. +# Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma. +pos_constr_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# +# Line splitting options +# + +# Try to limit code width to N columns. +code_width = 142 # unsigned number + +# Whether to fully split long 'for' statements at semi-colons. +ls_for_split_full = false # true/false + +# Whether to fully split long function prototypes/calls at commas. +# The option ls_code_width has priority over the option ls_func_split_full. +ls_func_split_full = true # true/false + +# Whether to split lines as close to code_width as possible and ignore some +# groupings. +# The option ls_code_width has priority over the option ls_func_split_full. +ls_code_width = false # true/false + +# +# Code alignment options (not left column spaces/tabs) +# + +# Whether to keep non-indenting tabs. +align_keep_tabs = false # true/false + +# Whether to use tabs for aligning. +align_with_tabs = false # true/false + +# Whether to bump out to the next tab when aligning. +align_on_tabstop = false # true/false + +# Whether to right-align numbers. +align_number_right = false # true/false + +# Whether to keep whitespace not required for alignment. +align_keep_extra_space = false # true/false + +# Whether to align variable definitions in prototypes and functions. +align_func_params = true # true/false + +# The span for aligning parameter definitions in function on parameter name. +# +# 0 = Don't align (default). +align_func_params_span = 0 # unsigned number + +# The threshold for aligning function parameter definitions. +# Use a negative number for absolute thresholds. +# +# 0 = No limit (default). +align_func_params_thresh = 0 # number + +# The gap for aligning function parameter definitions. +align_func_params_gap = 0 # unsigned number + +# The span for aligning constructor value. +# +# 0 = Don't align (default). +align_constr_value_span = 0 # unsigned number + +# The threshold for aligning constructor value. +# Use a negative number for absolute thresholds. +# +# 0 = No limit (default). +align_constr_value_thresh = 0 # number + +# The gap for aligning constructor value. +align_constr_value_gap = 0 # unsigned number + +# Whether to align parameters in single-line functions that have the same +# name. The function names must already be aligned with each other. +align_same_func_call_params = true # true/false + +# The span for aligning function-call parameters for single line functions. +# +# 0 = Don't align (default). +align_same_func_call_params_span = 0 # unsigned number + +# The threshold for aligning function-call parameters for single line +# functions. +# Use a negative number for absolute thresholds. +# +# 0 = No limit (default). +align_same_func_call_params_thresh = 0 # number + +# The span for aligning variable definitions. +# +# 0 = Don't align (default). +align_var_def_span = 1 # unsigned number + +# How to consider (or treat) the '*' in the alignment of variable definitions. +# +# 0: Part of the type 'void * foo;' (default) +# 1: Part of the variable 'void *foo;' +# 2: Dangling 'void *foo;' +# Dangling: the '*' will not be taken into account when aligning. +align_var_def_star_style = 2 # unsigned number + +# How to consider (or treat) the '&' in the alignment of variable definitions. +# +# 0: Part of the type 'long & foo;' (default) +# 1: Part of the variable 'long &foo;' +# 2: Dangling 'long &foo;' +# Dangling: the '&' will not be taken into account when aligning. +align_var_def_amp_style = 2 # unsigned number + +# The threshold for aligning variable definitions. +# Use a negative number for absolute thresholds. +# +# 0 = No limit (default). +align_var_def_thresh = 3 # number + +# The gap for aligning variable definitions. +align_var_def_gap = 1 # unsigned number + +# Whether to align the colon in struct bit fields. +align_var_def_colon = true # true/false + +# The gap for aligning the colon in struct bit fields. +align_var_def_colon_gap = 0 # unsigned number + +# Whether to align any attribute after the variable name. +align_var_def_attribute = true # true/false + +# Whether to align inline struct/enum/union variable definitions. +align_var_def_inline = true # true/false + +# The span for aligning on '=' in assignments. +# +# 0 = Don't align (default). +align_assign_span = 1 # unsigned number + +# The span for aligning on '=' in function prototype modifier. +# +# 0 = Don't align (default). +align_assign_func_proto_span = 0 # unsigned number + +# The threshold for aligning on '=' in assignments. +# Use a negative number for absolute thresholds. +# +# 0 = No limit (default). +align_assign_thresh = 0 # number + +# How to apply align_assign_span to function declaration "assignments", i.e. +# 'virtual void foo() = 0' or '~foo() = {default|delete}'. +# +# 0: Align with other assignments (default) +# 1: Align with each other, ignoring regular assignments +# 2: Don't align +align_assign_decl_func = 0 # unsigned number + +# The span for aligning on '=' in enums. +# +# 0 = Don't align (default). +align_enum_equ_span = 1 # unsigned number + +# The threshold for aligning on '=' in enums. +# Use a negative number for absolute thresholds. +# +# 0 = no limit (default). +align_enum_equ_thresh = 0 # number + +# The span for aligning class member definitions. +# +# 0 = Don't align (default). +align_var_class_span = 0 # unsigned number + +# The threshold for aligning class member definitions. +# Use a negative number for absolute thresholds. +# +# 0 = No limit (default). +align_var_class_thresh = 0 # number + +# The gap for aligning class member definitions. +align_var_class_gap = 0 # unsigned number + +# The span for aligning struct/union member definitions. +# +# 0 = Don't align (default). +align_var_struct_span = 1 # unsigned number + +# The threshold for aligning struct/union member definitions. +# Use a negative number for absolute thresholds. +# +# 0 = No limit (default). +align_var_struct_thresh = 0 # number + +# The gap for aligning struct/union member definitions. +align_var_struct_gap = 0 # unsigned number + +# The span for aligning struct initializer values. +# +# 0 = Don't align (default). +align_struct_init_span = 1 # unsigned number + +# The span for aligning single-line typedefs. +# +# 0 = Don't align (default). +align_typedef_span = 1 # unsigned number + +# The minimum space between the type and the synonym of a typedef. +align_typedef_gap = 0 # unsigned number + +# How to align typedef'd functions with other typedefs. +# +# 0: Don't mix them at all (default) +# 1: Align the open parenthesis with the types +# 2: Align the function type name with the other type names +align_typedef_func = 0 # unsigned number + +# How to consider (or treat) the '*' in the alignment of typedefs. +# +# 0: Part of the typedef type, 'typedef int * pint;' (default) +# 1: Part of type name: 'typedef int *pint;' +# 2: Dangling: 'typedef int *pint;' +# Dangling: the '*' will not be taken into account when aligning. +align_typedef_star_style = 2 # unsigned number + +# How to consider (or treat) the '&' in the alignment of typedefs. +# +# 0: Part of the typedef type, 'typedef int & intref;' (default) +# 1: Part of type name: 'typedef int &intref;' +# 2: Dangling: 'typedef int &intref;' +# Dangling: the '&' will not be taken into account when aligning. +align_typedef_amp_style = 2 # unsigned number + +# The span for aligning comments that end lines. +# +# 0 = Don't align (default). +align_right_cmt_span = 4 # unsigned number + +# Minimum number of columns between preceding text and a trailing comment in +# order for the comment to qualify for being aligned. Must be non-zero to have +# an effect. +align_right_cmt_gap = 0 # unsigned number + +# If aligning comments, whether to mix with comments after '}' and #endif with +# less than three spaces before the comment. +align_right_cmt_mix = false # true/false + +# Whether to only align trailing comments that are at the same brace level. +align_right_cmt_same_level = false # true/false + +# Minimum column at which to align trailing comments. Comments which are +# aligned beyond this column, but which can be aligned in a lesser column, +# may be "pulled in". +# +# 0 = Ignore (default). +align_right_cmt_at_col = 1 # unsigned number + +# The span for aligning function prototypes. +# +# 0 = Don't align (default). +align_func_proto_span = 3 # unsigned number + +# The threshold for aligning function prototypes. +# Use a negative number for absolute thresholds. +# +# 0 = No limit (default). +align_func_proto_thresh = 0 # number + +# Minimum gap between the return type and the function name. +align_func_proto_gap = 0 # unsigned number + +# Whether to align function prototypes on the 'operator' keyword instead of +# what follows. +align_on_operator = true # true/false + +# Whether to mix aligning prototype and variable declarations. If true, +# align_var_def_XXX options are used instead of align_func_proto_XXX options. +align_mix_var_proto = false # true/false + +# Whether to align single-line functions with function prototypes. +# Uses align_func_proto_span. +align_single_line_func = true # true/false + +# Whether to align the open brace of single-line functions. +# Requires align_single_line_func=true. Uses align_func_proto_span. +align_single_line_brace = true # true/false + +# Gap for align_single_line_brace. +align_single_line_brace_gap = 0 # unsigned number + +# (OC) The span for aligning Objective-C message specifications. +# +# 0 = Don't align (default). +align_oc_msg_spec_span = 0 # unsigned number + +# Whether to align macros wrapped with a backslash and a newline. This will +# not work right if the macro contains a multi-line comment. +align_nl_cont = true # true/false + +# Whether to align macro functions and variables together. +align_pp_define_together = false # true/false + +# The span for aligning on '#define' bodies. +# +# =0: Don't align (default) +# >0: Number of lines (including comments) between blocks +align_pp_define_span = 0 # unsigned number + +# The minimum space between label and value of a preprocessor define. +align_pp_define_gap = 0 # unsigned number + +# Whether to align lines that start with '<<' with previous '<<'. +# +# Default: true +align_left_shift = true # true/false + +# Whether to align text after 'asm volatile ()' colons. +align_asm_colon = false # true/false + +# (OC) Span for aligning parameters in an Objective-C message call +# on the ':'. +# +# 0 = Don't align. +align_oc_msg_colon_span = 0 # unsigned number + +# (OC) Whether to always align with the first parameter, even if it is too +# short. +align_oc_msg_colon_first = false # true/false + +# (OC) Whether to align parameters in an Objective-C '+' or '-' declaration +# on the ':'. +align_oc_decl_colon = false # true/false + +# +# Comment modification options +# + +# Try to wrap comments at N columns. +cmt_width = 140 # unsigned number + +# How to reflow comments. +# +# 0: No reflowing (apart from the line wrapping due to cmt_width) (default) +# 1: No touching at all +# 2: Full reflow +cmt_reflow_mode = 0 # unsigned number + +# Whether to convert all tabs to spaces in comments. If false, tabs in +# comments are left alone, unless used for indenting. +cmt_convert_tab_to_spaces = false # true/false + +# Whether to apply changes to multi-line comments, including cmt_width, +# keyword substitution and leading chars. +# +# Default: true +cmt_indent_multi = true # true/false + +# Whether to group c-comments that look like they are in a block. +cmt_c_group = false # true/false + +# Whether to put an empty '/*' on the first line of the combined c-comment. +cmt_c_nl_start = false # true/false + +# Whether to add a newline before the closing '*/' of the combined c-comment. +cmt_c_nl_end = false # true/false + +# Whether to change cpp-comments into c-comments. +cmt_cpp_to_c = false # true/false + +# Whether to group cpp-comments that look like they are in a block. Only +# meaningful if cmt_cpp_to_c=true. +cmt_cpp_group = false # true/false + +# Whether to put an empty '/*' on the first line of the combined cpp-comment +# when converting to a c-comment. +# +# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. +cmt_cpp_nl_start = false # true/false + +# Whether to add a newline before the closing '*/' of the combined cpp-comment +# when converting to a c-comment. +# +# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. +cmt_cpp_nl_end = false # true/false + +# Whether to put a star on subsequent comment lines. +cmt_star_cont = false # true/false + +# The number of spaces to insert at the start of subsequent comment lines. +cmt_sp_before_star_cont = 0 # unsigned number + +# The number of spaces to insert after the star on subsequent comment lines. +cmt_sp_after_star_cont = 0 # unsigned number + +# For multi-line comments with a '*' lead, remove leading spaces if the first +# and last lines of the comment are the same length. +# +# Default: true +cmt_multi_check_last = true # true/false + +# For multi-line comments with a '*' lead, remove leading spaces if the first +# and last lines of the comment are the same length AND if the length is +# bigger as the first_len minimum. +# +# Default: 4 +cmt_multi_first_len_minimum = 4 # unsigned number + +# Path to a file that contains text to insert at the beginning of a file if +# the file doesn't start with a C/C++ comment. If the inserted text contains +# '$(filename)', that will be replaced with the current file's name. +cmt_insert_file_header = "" # string + +# Path to a file that contains text to insert at the end of a file if the +# file doesn't end with a C/C++ comment. If the inserted text contains +# '$(filename)', that will be replaced with the current file's name. +cmt_insert_file_footer = "" # string + +# Path to a file that contains text to insert before a function definition if +# the function isn't preceded by a C/C++ comment. If the inserted text +# contains '$(function)', '$(javaparam)' or '$(fclass)', these will be +# replaced with, respectively, the name of the function, the javadoc '@param' +# and '@return' stuff, or the name of the class to which the member function +# belongs. +cmt_insert_func_header = "" # string + +# Path to a file that contains text to insert before a class if the class +# isn't preceded by a C/C++ comment. If the inserted text contains '$(class)', +# that will be replaced with the class name. +cmt_insert_class_header = "" # string + +# Path to a file that contains text to insert before an Objective-C message +# specification, if the method isn't preceded by a C/C++ comment. If the +# inserted text contains '$(message)' or '$(javaparam)', these will be +# replaced with, respectively, the name of the function, or the javadoc +# '@param' and '@return' stuff. +cmt_insert_oc_msg_header = "" # string + +# Whether a comment should be inserted if a preprocessor is encountered when +# stepping backwards from a function name. +# +# Applies to cmt_insert_oc_msg_header, cmt_insert_func_header and +# cmt_insert_class_header. +cmt_insert_before_preproc = false # true/false + +# Whether a comment should be inserted if a function is declared inline to a +# class definition. +# +# Applies to cmt_insert_func_header. +# +# Default: true +cmt_insert_before_inlines = true # true/false + +# Whether a comment should be inserted if the function is a class constructor +# or destructor. +# +# Applies to cmt_insert_func_header. +cmt_insert_before_ctor_dtor = false # true/false + +# +# Code modifying options (non-whitespace) +# + +# Add or remove braces on a single-line 'do' statement. +mod_full_brace_do = add # ignore/add/remove/force + +# Add or remove braces on a single-line 'for' statement. +mod_full_brace_for = add # ignore/add/remove/force + +# (Pawn) Add or remove braces on a single-line function definition. +mod_full_brace_function = ignore # ignore/add/remove/force + +# Add or remove braces on a single-line 'if' statement. Braces will not be +# removed if the braced statement contains an 'else'. +mod_full_brace_if = add # ignore/add/remove/force + +# Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either +# have, or do not have, braces. If true, braces will be added if any block +# needs braces, and will only be removed if they can be removed from all +# blocks. +# +# Overrides mod_full_brace_if. +mod_full_brace_if_chain = false # true/false + +# Whether to add braces to all blocks of an 'if'/'else if'/'else' chain. +# If true, mod_full_brace_if_chain will only remove braces from an 'if' that +# does not have an 'else if' or 'else'. +mod_full_brace_if_chain_only = false # true/false + +# Add or remove braces on single-line 'while' statement. +mod_full_brace_while = add # ignore/add/remove/force + +# Add or remove braces on single-line 'using ()' statement. +mod_full_brace_using = ignore # ignore/add/remove/force + +# Don't remove braces around statements that span N newlines +mod_full_brace_nl = 1 # unsigned number + +# Whether to prevent removal of braces from 'if'/'for'/'while'/etc. blocks +# which span multiple lines. +# +# Affects: +# mod_full_brace_for +# mod_full_brace_if +# mod_full_brace_if_chain +# mod_full_brace_if_chain_only +# mod_full_brace_while +# mod_full_brace_using +# +# Does not affect: +# mod_full_brace_do +# mod_full_brace_function +mod_full_brace_nl_block_rem_mlcond = false # true/false + +# Add or remove unnecessary parenthesis on 'return' statement. +mod_paren_on_return = remove # ignore/add/remove/force + +# (Pawn) Whether to change optional semicolons to real semicolons. +mod_pawn_semicolon = false # true/false + +# Whether to fully parenthesize Boolean expressions in 'while' and 'if' +# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'. +mod_full_paren_if_bool = true # true/false + +# Whether to remove superfluous semicolons. +mod_remove_extra_semicolon = true # true/false + +# If a function body exceeds the specified number of newlines and doesn't have +# a comment after the close brace, a comment will be added. +mod_add_long_function_closebrace_comment = 0 # unsigned number + +# If a namespace body exceeds the specified number of newlines and doesn't +# have a comment after the close brace, a comment will be added. +mod_add_long_namespace_closebrace_comment = 0 # unsigned number + +# If a class body exceeds the specified number of newlines and doesn't have a +# comment after the close brace, a comment will be added. +mod_add_long_class_closebrace_comment = 0 # unsigned number + +# If a switch body exceeds the specified number of newlines and doesn't have a +# comment after the close brace, a comment will be added. +mod_add_long_switch_closebrace_comment = 0 # unsigned number + +# If an #ifdef body exceeds the specified number of newlines and doesn't have +# a comment after the #endif, a comment will be added. +mod_add_long_ifdef_endif_comment = 1 # unsigned number + +# If an #ifdef or #else body exceeds the specified number of newlines and +# doesn't have a comment after the #else, a comment will be added. +mod_add_long_ifdef_else_comment = 1 # unsigned number + +# Whether to sort consecutive single-line 'import' statements. +mod_sort_import = false # true/false + +# (C#) Whether to sort consecutive single-line 'using' statements. +mod_sort_using = false # true/false + +# Whether to sort consecutive single-line '#include' statements (C/C++) and +# '#import' statements (Objective-C). Be aware that this has the potential to +# break your code if your includes/imports have ordering dependencies. +mod_sort_include = false # true/false + +# Whether to move a 'break' that appears after a fully braced 'case' before +# the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'. +mod_move_case_break = true # true/false + +# Add or remove braces around a fully braced case statement. Will only remove +# braces if there are no variable declarations in the block. +mod_case_brace = ignore # ignore/add/remove/force + +# Whether to remove a void 'return;' that appears as the last statement in a +# function. +mod_remove_empty_return = true # true/false + +# Add or remove the comma after the last value of an enumeration. +mod_enum_last_comma = ignore # ignore/add/remove/force + +# (OC) Whether to organize the properties. If true, properties will be +# rearranged according to the mod_sort_oc_property_*_weight factors. +mod_sort_oc_properties = false # true/false + +# (OC) Weight of a class property modifier. +mod_sort_oc_property_class_weight = 0 # number + +# (OC) Weight of 'atomic' and 'nonatomic'. +mod_sort_oc_property_thread_safe_weight = 0 # number + +# (OC) Weight of 'readwrite' when organizing properties. +mod_sort_oc_property_readwrite_weight = 0 # number + +# (OC) Weight of a reference type specifier ('retain', 'copy', 'assign', +# 'weak', 'strong') when organizing properties. +mod_sort_oc_property_reference_weight = 0 # number + +# (OC) Weight of getter type ('getter=') when organizing properties. +mod_sort_oc_property_getter_weight = 0 # number + +# (OC) Weight of setter type ('setter=') when organizing properties. +mod_sort_oc_property_setter_weight = 0 # number + +# (OC) Weight of nullability type ('nullable', 'nonnull', 'null_unspecified', +# 'null_resettable') when organizing properties. +mod_sort_oc_property_nullability_weight = 0 # number + +# +# Preprocessor options +# + +# Add or remove indentation of preprocessor directives inside #if blocks +# at brace level 0 (file-level). +pp_indent = ignore # ignore/add/remove/force + +# Whether to indent #if/#else/#endif at the brace level. If false, these are +# indented from column 1. +pp_indent_at_level = false # true/false + +# Specifies the number of columns to indent preprocessors per level +# at brace level 0 (file-level). If pp_indent_at_level=false, also specifies +# the number of columns to indent preprocessors per level +# at brace level > 0 (function-level). +# +# Default: 1 +pp_indent_count = 1 # unsigned number + +# Add or remove space after # based on pp_level of #if blocks. +pp_space = add # ignore/add/remove/force + +# Sets the number of spaces per level added with pp_space. +pp_space_count = 0 # unsigned number + +# The indent for '#region' and '#endregion' in C# and '#pragma region' in +# C/C++. Negative values decrease indent down to the first column. +pp_indent_region = 0 # number + +# Whether to indent the code between #region and #endregion. +pp_region_indent_code = false # true/false + +# If pp_indent_at_level=true, sets the indent for #if, #else and #endif when +# not at file-level. Negative values decrease indent down to the first column. +# +# =0: Indent preprocessors using output_tab_size +# >0: Column at which all preprocessors will be indented +pp_indent_if = 0 # number + +# Whether to indent the code between #if, #else and #endif. +pp_if_indent_code = false # true/false + +# Whether to indent '#define' at the brace level. If false, these are +# indented from column 1. +pp_define_at_level = false # true/false + +# Whether to ignore the '#define' body while formatting. +pp_ignore_define_body = false # true/false + +# Whether to indent case statements between #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the case statements +# directly inside of. +# +# Default: true +pp_indent_case = true # true/false + +# Whether to indent whole function definitions between #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the function definition +# is directly inside of. +# +# Default: true +pp_indent_func_def = true # true/false + +# Whether to indent extern C blocks between #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the extern block is +# directly inside of. +# +# Default: true +pp_indent_extern = true # true/false + +# Whether to indent braces directly inside #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the braces are directly +# inside of. +# +# Default: true +pp_indent_brace = true # true/false + +# +# Sort includes options +# + +# The regex for include category with priority 0. +include_category_0 = "" # string + +# The regex for include category with priority 1. +include_category_1 = "" # string + +# The regex for include category with priority 2. +include_category_2 = "" # string + +# +# Use or Do not Use options +# + +# true: indent_func_call_param will be used (default) +# false: indent_func_call_param will NOT be used +# +# Default: true +use_indent_func_call_param = true # true/false + +# The value of the indentation for a continuation line is calculated +# differently if the statement is: +# - a declaration: your case with QString fileName ... +# - an assignment: your case with pSettings = new QSettings( ... +# +# At the second case the indentation value might be used twice: +# - at the assignment +# - at the function call (if present) +# +# To prevent the double use of the indentation value, use this option with the +# value 'true'. +# +# true: indent_continue will be used only once +# false: indent_continue will be used every time (default) +use_indent_continue_only_once = false # true/false + +# The value might be used twice: +# - at the assignment +# - at the opening brace +# +# To prevent the double use of the indentation value, use this option with the +# value 'true'. +# +# true: indentation will be used only once +# false: indentation will be used every time (default) +indent_cpp_lambda_only_once = false # true/false + +# Whether to apply special formatting for Qt SIGNAL/SLOT macros. Essentially, +# this tries to format these so that they match Qt's normalized form (i.e. the +# result of QMetaObject::normalizedSignature), which can slightly improve the +# performance of the QObject::connect call, rather than how they would +# otherwise be formatted. +# +# See options_for_QT.cpp for details. +# +# Default: true +use_options_overriding_for_qt_macros = true # true/false + +# +# Warn levels - 1: error, 2: warning (default), 3: note +# + +# (C#) Warning is given if doing tab-to-\t replacement and we have found one +# in a C# verbatim string literal. +# +# Default: 2 +warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number + +# Meaning of the settings: +# Ignore - do not do any changes +# Add - makes sure there is 1 or more space/brace/newline/etc +# Force - makes sure there is exactly 1 space/brace/newline/etc, +# behaves like Add in some contexts +# Remove - removes space/brace/newline/etc +# +# +# - Token(s) can be treated as specific type(s) with the 'set' option: +# `set tokenType tokenString [tokenString...]` +# +# Example: +# `set BOOL __AND__ __OR__` +# +# tokenTypes are defined in src/token_enum.h, use them without the +# 'CT_' prefix: 'CT_BOOL' => 'BOOL' +# +# +# - Token(s) can be treated as type(s) with the 'type' option. +# `type tokenString [tokenString...]` +# +# Example: +# `type int c_uint_8 Rectangle` +# +# This can also be achieved with `set TYPE int c_uint_8 Rectangle` +# +# +# To embed whitespace in tokenStrings use the '\' escape character, or quote +# the tokenStrings. These quotes are supported: "'` +# +# +# - Support for the auto detection of languages through the file ending can be +# added using the 'file_ext' command. +# `file_ext langType langString [langString..]` +# +# Example: +# `file_ext CPP .ch .cxx .cpp.in` +# +# langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use +# them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP' +# +# +# - Custom macro-based indentation can be set up using 'macro-open', +# 'macro-else' and 'macro-close'. +# `(macro-open | macro-else | macro-close) tokenString` +# +# Example: +# `macro-open BEGIN_TEMPLATE_MESSAGE_MAP` +# `macro-open BEGIN_MESSAGE_MAP` +# `macro-close END_MESSAGE_MAP` +# +# +# option(s) with 'not default' value: 164 +#