Initial commit.
Some checks failed
Sync remain PRs to Jira / Sync PRs to Jira (push) Has been cancelled
Some checks failed
Sync remain PRs to Jira / Sync PRs to Jira (push) Has been cancelled
This commit is contained in:
101
.github/ISSUE_TEMPLATE/01_compile_bug.yml
vendored
Normal file
101
.github/ISSUE_TEMPLATE/01_compile_bug.yml
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
name: Compilation error report
|
||||||
|
description: Report build issues
|
||||||
|
labels: ['Type: Bug']
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Answers checklist.
|
||||||
|
description: Before submitting a new issue, please follow the checklist and try to find the answer.
|
||||||
|
options:
|
||||||
|
- label: I have read the [Rainmaker documentation](https://rainmaker.espressif.com/docs/get-started.html) and the issue is not addressed there.
|
||||||
|
required: true
|
||||||
|
- label: I have updated my IDF branch (release/vX.Y) to the latest version and checked that the issue is present there. This is not applicable if you are using Rainmaker with Arduino.
|
||||||
|
required: true
|
||||||
|
- label: I have searched the [Rainmaker forum](https://www.esp32.com/viewforum.php?f=41) and issue tracker for a similar issue and not found a similar issue.
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: idf_version
|
||||||
|
attributes:
|
||||||
|
label: IDF / ESP32-Arduino version.
|
||||||
|
description: On which IDF version does this issue occur on? Run `git describe --tags` or `idf.py --version` to find it. For Arduino users, mention the version of ESP32-Arduino (from Boards manager).
|
||||||
|
placeholder: ex. v3.2-dev-1148-g96cd3b75c / ESP32-Arduino 2.0.6
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: operating_system
|
||||||
|
attributes:
|
||||||
|
label: Operating System used.
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- Windows
|
||||||
|
- Linux
|
||||||
|
- macOS
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: build
|
||||||
|
attributes:
|
||||||
|
label: How did you build your project?
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- Command line with Make
|
||||||
|
- Command line with CMake
|
||||||
|
- Command line with idf.py
|
||||||
|
- Arduino IDE
|
||||||
|
- Other (please specify in More Information)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: devkit
|
||||||
|
attributes:
|
||||||
|
label: Development Kit.
|
||||||
|
description: On which Development Kit does this issue occur on?
|
||||||
|
placeholder: ex. ESP32-Wrover-Kit v2 | Custom Board
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: What is the expected behavior?
|
||||||
|
description: Please provide a clear and concise description of the expected behavior.
|
||||||
|
placeholder: I expected it to...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: actual
|
||||||
|
attributes:
|
||||||
|
label: What is the actual behavior?
|
||||||
|
description: Please describe actual behavior.
|
||||||
|
placeholder: Instead it...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce.
|
||||||
|
description: 'How do you trigger this bug? Please walk us through it step by step. Please attach sdkconfig file (from your project folder). Please attach your code here or the name of the rainmaker example.'
|
||||||
|
value: |
|
||||||
|
1. Step
|
||||||
|
2. Step
|
||||||
|
3. Step
|
||||||
|
...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: debug_logs
|
||||||
|
attributes:
|
||||||
|
label: Build Logs.
|
||||||
|
description: Compilation log goes here, should contain the backtrace, as well as the reset source if it is a crash.
|
||||||
|
placeholder: Your log goes here.
|
||||||
|
render: plain
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: more-info
|
||||||
|
attributes:
|
||||||
|
label: More Information.
|
||||||
|
description: Do you have any other information from investigating this?
|
||||||
|
placeholder: ex. I tried on my friend's Windows 10 PC and the command works there.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
101
.github/ISSUE_TEMPLATE/02_runtime_bug.yml
vendored
Normal file
101
.github/ISSUE_TEMPLATE/02_runtime_bug.yml
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
name: Runtime bug report
|
||||||
|
description: Report runtime bugs/crashes
|
||||||
|
labels: ['Type: Bug']
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Answers checklist.
|
||||||
|
description: Before submitting a new issue, please follow the checklist and try to find the answer.
|
||||||
|
options:
|
||||||
|
- label: I have read the [Rainmaker documentation](https://rainmaker.espressif.com/docs/get-started.html) and the issue is not addressed there.
|
||||||
|
required: true
|
||||||
|
- label: I have updated my IDF branch (release/vX.Y) to the latest version and checked that the issue is present there. This is not applicable if you are using Rainmaker with Arduino.
|
||||||
|
required: true
|
||||||
|
- label: I have searched the [Rainmaker forum](https://www.esp32.com/viewforum.php?f=41) and issue tracker for a similar issue and not found a similar issue.
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: idf_version
|
||||||
|
attributes:
|
||||||
|
label: IDF / ESP32-Arduino version.
|
||||||
|
description: On which IDF version does this issue occur on? Run `git describe --tags` or `idf.py --version` to find it. For Arduino users, mention the version of ESP32-Arduino (from Boards manager).
|
||||||
|
placeholder: ex. v3.2-dev-1148-g96cd3b75c / ESP32-Arduino 2.0.6
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: operating_system
|
||||||
|
attributes:
|
||||||
|
label: Operating System used.
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- Windows
|
||||||
|
- Linux
|
||||||
|
- macOS
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: build
|
||||||
|
attributes:
|
||||||
|
label: How did you build your project?
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- Command line with Make
|
||||||
|
- Command line with CMake
|
||||||
|
- Command line with idf.py
|
||||||
|
- Arduino IDE
|
||||||
|
- Other (please specify in More Information)
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: devkit
|
||||||
|
attributes:
|
||||||
|
label: Development Kit.
|
||||||
|
description: On which Development Kit does this issue occur on?
|
||||||
|
placeholder: ex. ESP32-Wrover-Kit v2 | Custom Board
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: What is the expected behavior?
|
||||||
|
description: Please provide a clear and concise description of the expected behavior.
|
||||||
|
placeholder: I expected it to...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: actual
|
||||||
|
attributes:
|
||||||
|
label: What is the actual behavior?
|
||||||
|
description: Please describe actual behavior.
|
||||||
|
placeholder: Instead it...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce.
|
||||||
|
description: 'How do you trigger this bug? Please walk us through it step by step. Please attach your code here or the name of the rainmaker example.'
|
||||||
|
value: |
|
||||||
|
1. Step
|
||||||
|
2. Step
|
||||||
|
3. Step
|
||||||
|
...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: debug_logs
|
||||||
|
attributes:
|
||||||
|
label: Debug Logs.
|
||||||
|
description: Debug log goes here, should contain the backtrace, as well as the reset source if it is a crash.
|
||||||
|
placeholder: Your log goes here.
|
||||||
|
render: plain
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: more-info
|
||||||
|
attributes:
|
||||||
|
label: More Information.
|
||||||
|
description: Do you have any other information from investigating this?
|
||||||
|
placeholder: ex. I tried on my friend's Windows 10 PC and the command works there.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
31
.github/ISSUE_TEMPLATE/03_feature_request.yml
vendored
Normal file
31
.github/ISSUE_TEMPLATE/03_feature_request.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: Feature request
|
||||||
|
description: Suggest an idea for this project.
|
||||||
|
labels: ['Type: Feature Request']
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: We welcome any ideas or feature requests! It’s helpful if you can explain exactly why the feature would be useful.
|
||||||
|
- type: textarea
|
||||||
|
id: problem-related
|
||||||
|
attributes:
|
||||||
|
label: Is your feature request related to a problem?
|
||||||
|
description: Please provide a clear and concise description of what the problem is.
|
||||||
|
placeholder: ex. I'm always frustrated when ...
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: Describe the solution you'd like.
|
||||||
|
description: Please provide a clear and concise description of what you want to happen.
|
||||||
|
placeholder: ex. When using the Rainmaker app ...
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Describe alternatives you've considered.
|
||||||
|
description: Please provide a clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
placeholder: ex. Choosing other approach wouldn't work, because ...
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional context.
|
||||||
|
description: Please add any other context or screenshots about the feature request here.
|
||||||
|
placeholder: ex. This would work only when ...
|
||||||
23
.github/ISSUE_TEMPLATE/04_other_issue.yml
vendored
Normal file
23
.github/ISSUE_TEMPLATE/04_other_issue.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: General issue / query report
|
||||||
|
description: File an issue report
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Answers checklist.
|
||||||
|
description: Before submitting a new issue, please follow the checklist and try to find the answer.
|
||||||
|
options:
|
||||||
|
- label: I have read the [Rainmaker documentation](https://rainmaker.espressif.com/docs/get-started.html) and the issue is not addressed there.
|
||||||
|
required: true
|
||||||
|
- label: I have updated my IDF branch (release/vX.Y) to the latest version and checked that the issue is present there. This is not applicable if you are using Rainmaker with Arduino.
|
||||||
|
required: true
|
||||||
|
- label: I have searched the [Rainmaker forum](https://www.esp32.com/viewforum.php?f=41) and issue tracker for a similar issue and not found a similar issue.
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: issue
|
||||||
|
attributes:
|
||||||
|
label: General issue report
|
||||||
|
description: Your issue report goes here.
|
||||||
|
placeholder: ex. How do I run...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Getting Started - ESP Rainmaker
|
||||||
|
url: https://rainmaker.espressif.com/docs/get-started.html
|
||||||
|
about: Guide for getting started with Rainmaker
|
||||||
|
- name: ESP Rainmaker API Guide
|
||||||
|
url: https://rainmaker.espressif.com/docs/api.html
|
||||||
|
about: Documentation for using Rainmaker APIs
|
||||||
|
- name: Espressif Rainmaker Forum
|
||||||
|
url: https://www.esp32.com/viewforum.php?f=41
|
||||||
|
about: For asking questions to the community about using ESP Rainmaker, create a discussion topic here.
|
||||||
20
.github/workflows/issue_comment.yml
vendored
Normal file
20
.github/workflows/issue_comment.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Sync issue comments to JIRA
|
||||||
|
|
||||||
|
# This workflow will be triggered when new issue comment is created (including PR comments)
|
||||||
|
on: issue_comment
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync_issue_comments_to_jira:
|
||||||
|
name: Sync Issue Comments to Jira
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Sync issue comments to JIRA
|
||||||
|
uses: espressif/github-actions/sync_issues_to_jira@master
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
JIRA_PASS: ${{ secrets.JIRA_PASS }}
|
||||||
|
JIRA_PROJECT: MEGH
|
||||||
|
JIRA_COMPONENT: GitHub
|
||||||
|
JIRA_URL: ${{ secrets.JIRA_URL }}
|
||||||
|
JIRA_USER: ${{ secrets.JIRA_USER }}
|
||||||
138
.github/workflows/launchpad.yml
vendored
Normal file
138
.github/workflows/launchpad.yml
vendored
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# This workflow build examples, store the artifacts and deploy them to github pages.
|
||||||
|
# Generates the launchpad configuration file that can be used with the url.
|
||||||
|
|
||||||
|
name: Build Examples
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Runs on pushes targeting the default branch
|
||||||
|
push:
|
||||||
|
branches: ["master"]
|
||||||
|
|
||||||
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
# Allow one concurrent deployment
|
||||||
|
concurrency:
|
||||||
|
group: "pages"
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Build:
|
||||||
|
# Disable the job in forks
|
||||||
|
if: ${{ github.repository_owner == 'espressif' }}
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: espressif/idf:v5.3.1
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
example: [fan, led_light, multi_device, switch, temperature_sensor]
|
||||||
|
target: [esp32, esp32c3, esp32c6, esp32s3]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
|
||||||
|
- run: mkdir -p images
|
||||||
|
|
||||||
|
- name: build application
|
||||||
|
run: |
|
||||||
|
. $IDF_PATH/export.sh
|
||||||
|
export ESP_RMAKER_PATH=$PWD
|
||||||
|
cd examples/${{ matrix.example }}
|
||||||
|
|
||||||
|
# Lets use the assisted claim by default for launchpad examples
|
||||||
|
echo "CONFIG_ESP_RMAKER_ASSISTED_CLAIM=y" >> sdkconfig.defaults
|
||||||
|
|
||||||
|
idf.py set-target ${{ matrix.target }} build
|
||||||
|
|
||||||
|
cd build
|
||||||
|
TARGET_CHIP=`cat project_description.json | python3 -c 'import sys,json; print(json.load(sys.stdin)["target"])'`
|
||||||
|
APP_BIN=`cat project_description.json | python3 -c 'import sys,json; print(json.load(sys.stdin)["app_bin"])'`
|
||||||
|
|
||||||
|
OUT_BIN=$ESP_RMAKER_PATH/images/"$TARGET_CHIP"_RainMaker_"$APP_BIN"
|
||||||
|
esptool.py --chip $TARGET_CHIP merge_bin -o $OUT_BIN @flash_args
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: build-images-${{ matrix.target }}-${{ matrix.example }}
|
||||||
|
path: images
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
# Disable the job in forks
|
||||||
|
if: ${{ github.repository_owner == 'espressif' }}
|
||||||
|
|
||||||
|
needs: Build
|
||||||
|
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: espressif/esp-matter:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
id: checkout
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
|
||||||
|
- name: Download builds
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
pattern: build-images-*
|
||||||
|
path: images/
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: generate launchpad config
|
||||||
|
run: |
|
||||||
|
export ESP_RMAKER_PATH=$PWD
|
||||||
|
|
||||||
|
cd images
|
||||||
|
$ESP_RMAKER_PATH/tools/launchpad/generate_launchpad_config.sh ${{ github.repository_owner }} `basename ${{ github.repository }}`
|
||||||
|
|
||||||
|
echo "#### Build Config" >> build_cfg.md
|
||||||
|
echo "" >> build_cfg.md
|
||||||
|
echo "" >> build_cfg.md
|
||||||
|
echo "- ESP-IDF: [`git -C $IDF_PATH rev-parse HEAD`](https://github.com/espressif/esp-idf/tree/`git -C $IDF_PATH rev-parse HEAD`)" >> build_cfg.md
|
||||||
|
echo "- ESP-RainMaker: [${{steps.checkout.outputs.commit}}](https://github.com/espressif/esp-rainmaker/tree/${{steps.checkout.outputs.commit}})" >> build_cfg.md
|
||||||
|
echo "" >> build_cfg.md
|
||||||
|
echo "" >> build_cfg.md
|
||||||
|
|
||||||
|
cat $ESP_RMAKER_PATH/tools/launchpad/app_link_guide.md >> build_cfg.md
|
||||||
|
|
||||||
|
tree -H '.' -L 1 --noreport -T 'ESP RainMaker Launchpad Artifacts' -shi --charset utf-8 -I "index.html" -o index.html
|
||||||
|
|
||||||
|
- name: Setup Pages
|
||||||
|
uses: actions/configure-pages@v4
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
with:
|
||||||
|
path: images/
|
||||||
|
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
||||||
20
.github/workflows/new_issues.yml
vendored
Normal file
20
.github/workflows/new_issues.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Sync issues to Jira
|
||||||
|
|
||||||
|
# This workflow will be triggered when a new issue is opened
|
||||||
|
on: issues
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync_issues_to_jira:
|
||||||
|
name: Sync issues to Jira
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Sync GitHub issues to Jira project
|
||||||
|
uses: espressif/github-actions/sync_issues_to_jira@master
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
JIRA_PASS: ${{ secrets.JIRA_PASS }}
|
||||||
|
JIRA_PROJECT: MEGH
|
||||||
|
JIRA_COMPONENT: GitHub
|
||||||
|
JIRA_URL: ${{ secrets.JIRA_URL }}
|
||||||
|
JIRA_USER: ${{ secrets.JIRA_USER }}
|
||||||
25
.github/workflows/new_prs.yml
vendored
Normal file
25
.github/workflows/new_prs.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: Sync remain PRs to Jira
|
||||||
|
|
||||||
|
# This workflow will be triggered every hour, to sync remaining PRs (i.e. PRs with zero comment) to Jira project
|
||||||
|
# Note that, PRs can also get synced when new PR comment is created
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 * * * *"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync_prs_to_jira:
|
||||||
|
name: Sync PRs to Jira
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Sync PRs to Jira project
|
||||||
|
uses: espressif/github-actions/sync_issues_to_jira@master
|
||||||
|
with:
|
||||||
|
cron_job: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
JIRA_PASS: ${{ secrets.JIRA_PASS }}
|
||||||
|
JIRA_PROJECT: MEGH
|
||||||
|
JIRA_COMPONENT: GitHub
|
||||||
|
JIRA_URL: ${{ secrets.JIRA_URL }}
|
||||||
|
JIRA_USER: ${{ secrets.JIRA_USER }}
|
||||||
20
.github/workflows/upload_components.yml
vendored
Normal file
20
.github/workflows/upload_components.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Push components to Espressif Component Service
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
upload_components:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Upload ESP RainMaker components to Component Registry
|
||||||
|
uses: espressif/upload-components-ci-action@v1
|
||||||
|
with:
|
||||||
|
directories: >
|
||||||
|
components/esp_schedule;
|
||||||
|
components/esp_rainmaker;
|
||||||
|
namespace: "espressif"
|
||||||
|
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||||
94
.gitignore
vendored
Normal file
94
.gitignore
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Linker output
|
||||||
|
*.ilk
|
||||||
|
*.map
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Kernel Module Compile Results
|
||||||
|
*.mod*
|
||||||
|
*.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
modules.order
|
||||||
|
Module.symvers
|
||||||
|
Mkfile.old
|
||||||
|
dkms.conf
|
||||||
|
|
||||||
|
# emacs
|
||||||
|
.dir-locals.el
|
||||||
|
|
||||||
|
# emacs temp file suffixes
|
||||||
|
*~
|
||||||
|
.#*
|
||||||
|
\#*#
|
||||||
|
|
||||||
|
# eclipse setting
|
||||||
|
.settings
|
||||||
|
|
||||||
|
# MacOS directory files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Application project files
|
||||||
|
**/sdkconfig
|
||||||
|
**/sdkconfig.old
|
||||||
|
**/build
|
||||||
|
**/cloud_cfg/*.key
|
||||||
|
**/cloud_cfg/*.crt
|
||||||
|
**/cloud_cfg/*.info
|
||||||
|
**/.cache
|
||||||
|
|
||||||
|
# node_modules
|
||||||
|
docs/docusaurus/website/node_modules
|
||||||
|
|
||||||
|
# cli logs
|
||||||
|
**/logs
|
||||||
|
|
||||||
|
# IDF package manager
|
||||||
|
**/managed_components/
|
||||||
|
*.lock
|
||||||
|
|
||||||
|
# Patch files
|
||||||
|
*.patch
|
||||||
|
|
||||||
|
# Failed patch files
|
||||||
|
*.rej
|
||||||
|
*.orig
|
||||||
|
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "components/rmaker_common"]
|
||||||
|
path = components/rmaker_common
|
||||||
|
url = ../esp-rainmaker-common.git
|
||||||
353
CHANGES.md
Normal file
353
CHANGES.md
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
# Changes
|
||||||
|
|
||||||
|
## 27-Sep-2024: Modified esp-rainmaker examples to use partitions_4mb_optimised.csv
|
||||||
|
- RainMaker examples now use `partitions_4mb_optimised.csv` by default instead of `partitions.csv` and it is recommended to use the same in any new RainMaker projects.
|
||||||
|
- The fctry partition address is different for this partition table. So, if the host claiming is used, please pass the new address using:
|
||||||
|
```bash
|
||||||
|
esp-rainmaker-cli claim $ESPPORT --address 0x3fa000
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7-Aug-2024: Enabled ESP-Insights command response feature
|
||||||
|
- With ESP-Insights updated to the newer component, the command response feature from rainmaker can be used.
|
||||||
|
- To use the same, in addition to enabling esp-insights, please set below option from menuconfig:
|
||||||
|
```bash
|
||||||
|
CONFIG_ESP_INSIGHTS_CMD_RESP_ENABLED=y
|
||||||
|
```
|
||||||
|
- More info on this can be found [here](https://github.com/espressif/esp-insights/blob/main/FEATURES.md#command-response).
|
||||||
|
|
||||||
|
## 18-Jul-2024: Use network_provsioning for ESP-IDF v5.1 or later to support RainMaker over Thread
|
||||||
|
- The network_provisioning component can be used for provisioning both Wi-Fi or Thread devices. It also stays backward capabitable with wifi_provisioning component.
|
||||||
|
|
||||||
|
## 27-Feb-2024: Add support for closing provisioning window after PoP mismatch
|
||||||
|
- For ESP IDF v5.1.3 and later, provisioning will be stopped if there are 5 attempts to establish secure session with wrong PoP. This count can be set to any value between 0 and 20. 0 means that provisioning will not be stopped (which will be same as the earlier behaviour before this change).
|
||||||
|
|
||||||
|
## 21-Nov-2022 (esp_rmaker_mqtt: Add MQTT budgeting to control the number of messages sent)
|
||||||
|
|
||||||
|
- Due to some poor, non-optimised coding or bugs, it is possible that the node keeps bombarding the MQTT
|
||||||
|
broker with publish messages. To prevent this, a concept of MQTT Budgeting has been added.
|
||||||
|
- By default, a node will be given a budget of 100 (`CONFIG_ESP_RMAKER_MQTT_DEFAULT_BUDGET`), which will
|
||||||
|
go on incrementing by 1 (`CONFIG_ESP_RMAKER_MQTT_BUDGET_REVIVE_COUNT` every 5 seconds (`CONFIG_ESP_RMAKER_MQTT_BUDGET_REVIVE_PERIOD`),
|
||||||
|
limited to a max value of 1024 (`CONFIG_ESP_RMAKER_MQTT_MAX_BUDGET`).
|
||||||
|
- Budget will be decremented by 1 for every MQTT publish and messages will be dropped if budget is 0.
|
||||||
|
- This behaviour is enabled by default and can be disabled by disabling `CONFIG_ESP_RMAKER_MQTT_ENABLE_BUDGETING`.
|
||||||
|
|
||||||
|
## 16-Nov-2022 (mqtt_topics: Added support for AWS basic ingest topics.)
|
||||||
|
|
||||||
|
- AWS Basic Ingest Topics optimize data flow by removing the publish/subscribe message broker from the ingestion path, making it more cost effective. You can refer the official docs [here](https://docs.aws.amazon.com/iot/latest/developerguide/iot-basic-ingest.html).
|
||||||
|
- This setting is turned on by default and can be turned off by running `idf.py menuconfig` and disabling `CONFIG_ESP_RMAKER_MQTT_USE_BASIC_INGEST_TOPICS` option.
|
||||||
|
|
||||||
|
## 2-Nov-2022 (Added MQTT disconnect and user node mapping reset calls on WiFi/Factory Reset.)
|
||||||
|
|
||||||
|
- On a Wi-Fi reset triggered via esp_rmaker_wifi_reset(), the rmaker core will first disconnect from MQTT so that its offline state reflects immediately.
|
||||||
|
- On a Factory reset triggered via esp_rmaker_factory_reset(), the rmaker core will trigger a user mapping reset (if mqtt connection is active) so that the node gets removed from the user's account immediately.
|
||||||
|
- The reset delay time for the push button based reset has been changed from 0 to 2 seconds to give some time for the above mentioned operations.
|
||||||
|
|
||||||
|
Note: This config is enabled by default. You can disable it using `idf.py menuconfig`
|
||||||
|
## 28-Jun-2022 (examples: Enable CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE in all examples)
|
||||||
|
|
||||||
|
`CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE` has been enabled in all examples by default,
|
||||||
|
as a safety measure to prevent devices getting bricked after a faulty firmware upgrade.
|
||||||
|
The OTA firmware upgrade will be marked as successful only if the firmware can connect to
|
||||||
|
MQTT within 90 seconds of calling `esp_rmaker_ota_enable_default()` (or other esp_emaker_enable APIs).
|
||||||
|
The time out is configurable using `CONFIG_ESP_RMAKER_OTA_ROLLBACK_WAIT_PERIOD`.
|
||||||
|
|
||||||
|
Note that this is a bootloader feature and so, just enabling this feature and pushing out updated
|
||||||
|
firmware to existing devices won't be of any use. Please flash new bootloader on the devices to
|
||||||
|
make this work.
|
||||||
|
|
||||||
|
## 26-May-2022 (claiming and ota)
|
||||||
|
|
||||||
|
- claiming: Make self claiming as the default for esp32s3 and esp32c3
|
||||||
|
- ota: Make "OTA using Topics" as default and provide a simplified API for that
|
||||||
|
|
||||||
|
Self claiming is much more convenient and fast since the node directly gets the
|
||||||
|
credentials from the claiming service over HTTPS, instead of using the slower BLE based
|
||||||
|
Assisted claiming, wherein the phone app acts as a proxy between the node and the
|
||||||
|
claiming service. However, with self claiming, there was no concept of
|
||||||
|
[Admin Role](https://rainmaker.espressif.com/docs/user-roles.html#admin-users) and so, it was
|
||||||
|
not possible to access the node via the RainMaker or Insights dashboards. This was one
|
||||||
|
reason why Assisted Claiming was kept as a default for esp32c3 and esp32s3 even though
|
||||||
|
they support self claiming.
|
||||||
|
|
||||||
|
With recent changes in the Public RainMaker backend, the primary user (the user who performs the [user-node
|
||||||
|
mapping](https://rainmaker.espressif.com/docs/user-node-mapping.html)) for a self claimed
|
||||||
|
node is now made as the admin. This gives the primary user the access to the node for OTA and Insights.
|
||||||
|
So, self claiming has now been made as the default for all chips (except esp32) and the OTA Using Topics
|
||||||
|
has also been made as the default, since it is convenient and also the correct option for
|
||||||
|
production devices. A simpler API `esp_rmaker_ota_enable_default()` as also been added in esp_rmaker_core.h.
|
||||||
|
|
||||||
|
Note: Nodes that are already claimed via Assisted/Host Claiming will not have any effect, even if the
|
||||||
|
new firmware is enabled with self claiming. The self claiming will take effect only if the flash is
|
||||||
|
erased. **This will result in a change of node_id, since mac address is the node_id for self claimed nodes.**
|
||||||
|
If you want to contine using Assisted Claiming (probably because there is quite some data associated
|
||||||
|
with the node_id), please set is explicitly in your sdkconfig.
|
||||||
|
|
||||||
|
## 25-Jan-2022 (app_wifi: Minor feature additions to provisioning workflow)
|
||||||
|
|
||||||
|
Added a 30 minute timeout for Wi-Fi provisioning as a security measure. A device reboot will be
|
||||||
|
required to restart provisioning after it times out. The value can changed using the
|
||||||
|
`CONFIG_APP_WIFI_PROV_TIMEOUT_PERIOD` config option. A value of 0 will disable the timeout logic.
|
||||||
|
`APP_WIFI_EVENT_PROV_TIMEOUT` event will be triggerd to indicate that the provisioning has timed out.
|
||||||
|
|
||||||
|
## 25-Jan-2022 (examples: Enable some security features and change order of component dirs)
|
||||||
|
|
||||||
|
A couple of security features were added some time back, viz.
|
||||||
|
|
||||||
|
1. esp_rmaker_local_ctrl: Added support for sec1
|
||||||
|
2. esp_rmaker_user_mapping: Add checks for user id for better security
|
||||||
|
|
||||||
|
These are kept disabled by default at component level to maintain backward compatibility and not
|
||||||
|
change any existing projects. However, since enabling them is recommended, these are added in
|
||||||
|
the sdkconfig.defaults of all examples.
|
||||||
|
|
||||||
|
A minor change in CMakeLists.txt has also been done for all examples so that the rmaker_common
|
||||||
|
component from esp-rainmaker gets used, rather than the one from esp-insights.
|
||||||
|
|
||||||
|
## 12-Jan-2022 (esp_rmaker_local_ctrl: Added support for sec1)
|
||||||
|
|
||||||
|
This commit adds support for security1 for local control. This can be enabled by setting
|
||||||
|
`CONFIG_ESP_RMAKER_LOCAL_CTRL_SECURITY_1` when using local control feature (this is the
|
||||||
|
default security level when enabling local control). This would also require the latest
|
||||||
|
phone apps which have the support for security1.
|
||||||
|
|
||||||
|
You can check the docs [here](https://rainmaker.espressif.com/docs/local-control.html) for more details.
|
||||||
|
|
||||||
|
## 24-Aug-2021 (esp_rmaker_user_mapping: Add checks for user id for better security)
|
||||||
|
|
||||||
|
This commit adds some logic to detect a reset to factory or a user change during the
|
||||||
|
user node association workflow so that the RainMaker cloud can reset any earlier
|
||||||
|
mappings if this reset state is reported by the device during user node association.
|
||||||
|
|
||||||
|
If an existing, provisioned node is upgraded with a new firmware with this logic enabled,
|
||||||
|
it will send a request to cloud to reset the user mapping and so a re-provisioning would be
|
||||||
|
required. Moreover, a simple Wi-Fi reset will be treated as a factory reset in the context of
|
||||||
|
user node association. A side effect of this would be that the cloud can remove the secondary
|
||||||
|
users associated with that node and so, those would have to be added back again.
|
||||||
|
All subsequent Wi-Fi/Factory resets and provisioning + user node association would work fine.
|
||||||
|
For all new nodes, enabling this logic would have no issues.
|
||||||
|
|
||||||
|
Since this change in behavior is a breaking change, the feature has been kept disabled by default.
|
||||||
|
However, it is strongly recommended to enable this using CONFIG_ESP_RMAKER_USER_ID_CHECK
|
||||||
|
|
||||||
|
## 02-Jul-2021 (esp_insights: Add facility to enable esp_insights in the examples)
|
||||||
|
|
||||||
|
This commit introduces a breaking change in compilation, not due to any API change,
|
||||||
|
but introduction of new components under components/esp-insights/components.
|
||||||
|
You can either choose to include these components in your projects CMakeLists.txt
|
||||||
|
as per the standard examples as given here:
|
||||||
|
|
||||||
|
```
|
||||||
|
set(EXTRA_COMPONENT_DIRS ${RMAKER_PATH}/components ${RMAKER_PATH}/examples/common ${RMAKER_PATH}/components/esp-insights/components)
|
||||||
|
```
|
||||||
|
|
||||||
|
You will also have to pull in the new esp-insights submodule by executing this command:
|
||||||
|
|
||||||
|
```
|
||||||
|
git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
|
||||||
|
Another option is to exclude the common example component (app_insights) that adds these
|
||||||
|
components to the dependencies by adding this to your project's CMakeLists.txt:
|
||||||
|
|
||||||
|
```
|
||||||
|
set(EXCLUDE_COMPONENTS app_insights)
|
||||||
|
```
|
||||||
|
|
||||||
|
Check out the [esp-insights](https://github.com/espressif/esp-insights) project to understand more about this.
|
||||||
|
You can also check the docs [here](https://rainmaker.espressif.com/docs/esp-insights.html) to get started with enabling Insights in ESP RainMaker.
|
||||||
|
|
||||||
|
## 28-May-2021 (esp_rmaker_core: Add a system service for reboot/reset)
|
||||||
|
|
||||||
|
The reboot/reset API prototypes have changed from
|
||||||
|
|
||||||
|
```
|
||||||
|
esp_err_t esp_rmaker_reboot(uint8_t seconds);
|
||||||
|
esp_err_t esp_rmaker_wifi_reset(uint8_t seconds);
|
||||||
|
esp_err_t esp_rmaker_factory_reset(uint8_t seconds);
|
||||||
|
```
|
||||||
|
To
|
||||||
|
|
||||||
|
```
|
||||||
|
esp_err_t esp_rmaker_reboot(int8_t seconds);
|
||||||
|
esp_err_t esp_rmaker_wifi_reset(int8_t reset_seconds, int8_t reboot_seconds);
|
||||||
|
esp_err_t esp_rmaker_factory_reset(int8_t reset_seconds, int8_t reboot_seconds);
|
||||||
|
```
|
||||||
|
|
||||||
|
- The behavior of `esp_rmaker_reboot()` has changed such that passing a value of 0 would trigger
|
||||||
|
an immediate reboot without starting any timer.
|
||||||
|
- The `esp_rmaker_wifi_reset()` and `esp_rmaker_factory_reset()` APIs have been modified such that
|
||||||
|
they now accept 2 time values. The `reset_seconds` specify the time after which the reset should trigger
|
||||||
|
and the `reboot_seconds` specify the time after which the reboot should trigger, after the reset
|
||||||
|
was done.
|
||||||
|
- `reboot_seconds` is similar to the earlier `seconds` argument, but it allows for 0 and negative values.
|
||||||
|
0 indicates that the reboot should happen immediately after reset and negative value indicates that the
|
||||||
|
reboot should be skipped.
|
||||||
|
|
||||||
|
Please refer the [API documentation](https://docs.espressif.com/projects/esp-rainmaker/en/latest/c-api-reference/rainmaker_common.html#utilities) for additional details.
|
||||||
|
|
||||||
|
## 1-Feb-2021 (esp_rmaker: Moved out some generic modules from esp_rainmaker component)
|
||||||
|
|
||||||
|
Some generic code has been moved out of the esp_rainmaker repo and included as submodules at
|
||||||
|
components/rmaker_common and cli/.
|
||||||
|
|
||||||
|
To get these submodules, you will now have to execute `git submodule update --init --recursive` once.
|
||||||
|
|
||||||
|
For new clones, use `git clone --recursive https://github.com/espressif/esp-rainmaker.git`
|
||||||
|
|
||||||
|
### RainMaker Events
|
||||||
|
|
||||||
|
As part of the above changes, the following events have changed
|
||||||
|
|
||||||
|
- RMAKER_EVENT_MQTT_CONNECTED -> RMAKER_MQTT_EVENT_CONNECTED
|
||||||
|
- RMAKER_EVENT_MQTT_DISCONNECTED -> RMAKER_MQTT_EVENT_DISCONNECTED
|
||||||
|
- RMAKER_EVENT_MQTT_PUBLISHED -> RMAKER_MQTT_EVENT_PUBLISHED
|
||||||
|
|
||||||
|
Moreover, the event base for the MQTT events has changed from `RMAKER_EVENT` to `RMAKER_COMMON_EVENT`. The base has similarly changed even for the following:
|
||||||
|
|
||||||
|
- RMAKER_EVENT_REBOOT
|
||||||
|
- RMAKER_EVENT_WIFI_RESET
|
||||||
|
- RMAKER_EVENT_FACTORY_RESET
|
||||||
|
|
||||||
|
## 16-Oct-2020 (json: Use upstream json_generator and json_parser as submodules)
|
||||||
|
|
||||||
|
To get these submodules, you will now have to execute `git submodule update --init --recursive` once.
|
||||||
|
|
||||||
|
For new clones, use `git clone --recursive https://github.com/espressif/esp-rainmaker.git`
|
||||||
|
|
||||||
|
## 16-Oct-2020 (app_wifi: Changes in SSID and PoP generation for Provisioning)
|
||||||
|
|
||||||
|
The PoP for Wi-Fi provisioning was being fetched from a random 8 character hex string stored in the fctry partition.
|
||||||
|
In this commit, the random 8 character hex string has been replaced by 64 byte random number, which can be used for other purposes as well.
|
||||||
|
PoP is now generated by reading the first 4 bytes of this and converting to 8 character hex string.
|
||||||
|
Even the SSID now uses the last 3 bytes of this random number as the suffix, instead of last 3 bytes of MAC address.
|
||||||
|
With this change, it will now be possible to generate the complete Provisioning QR code payload outside the device,
|
||||||
|
without having to know its MAC address.
|
||||||
|
|
||||||
|
## 29-Sep-2020 (esp_rmaker_standard_types: Start default names of all standard params with capital letter)
|
||||||
|
|
||||||
|
Default parameter names like name, power, etc. have been changed to Name, Power, etc. respectively, so that they look better in the phone app UIs.
|
||||||
|
|
||||||
|
With this change, any user configured device name (the name set from phone apps), or any other persistent parameter for which a
|
||||||
|
default name was used (Eg. power) will be affected, as the values will no more be found in the NVS storage.
|
||||||
|
Please edit your application code accordingly if you want to stick with the old names.
|
||||||
|
|
||||||
|
Eg. If you were using the standard lightbulb device API which internally creates power and name parameters
|
||||||
|
```
|
||||||
|
light_device = esp_rmaker_lightbulb_device_create("Light", NULL, DEFAULT_POWER);
|
||||||
|
```
|
||||||
|
|
||||||
|
Please change to below, if you want to stick with old parameter names "name" and "power".
|
||||||
|
|
||||||
|
```
|
||||||
|
light_device = esp_rmaker_device_create("Light", ESP_RMAKER_DEVICE_LIGHTBULB, NULL);
|
||||||
|
esp_rmaker_device_add_param(light_device, esp_rmaker_name_param_create("name", "Light"));
|
||||||
|
esp_rmaker_param_t *power_param = esp_rmaker_power_param_create("power", DEFAULT_POWER);
|
||||||
|
esp_rmaker_device_add_param(light_device, power_param);
|
||||||
|
esp_rmaker_device_assign_primary_param(light_device, power_param);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5-Aug-2020 (wifi_provisioning: Use a random pop instead of creating it from MAC address)
|
||||||
|
|
||||||
|
Till date, the last 4 bytes of the MAC address were being used to generate the 8 character Proof of Possession (PoP) PIN for Wi-Fi provisioning. This is not secure enough because MAC address is a public information and can also be sniffed easily by devices in vicinity. A minor risk in this is that somebody else in the vicinity can provision your device, but a major risk is a man in the middle attack, wherein someone in vicinity can read the data being exchanged between a phone and the device and get the Wi-Fi credentials.
|
||||||
|
|
||||||
|
To prevent this, it is best to use a randomly generated PoP which cannot be guessed. So now, a random stream of bytes is generated and flashed in the fctry partition during claiming and then used as PoP. If your device is already claimed, it is recommended to erase the flash and perform the claiming again. If you erase the flash again, the PoP will change. However, if you just do a reset to factory, it will not.
|
||||||
|
|
||||||
|
If for some reason, you want to continue using the earlier mac address based method, please pass `POP_TYPE_MAC` to the `esp_err_t app_wifi_start(app_wifi_pop_type_t pop_type)` function.
|
||||||
|
|
||||||
|
## 31-July-2020 (esp\_rainmaker\_core: Code restructure and API changes)
|
||||||
|
|
||||||
|
Recently we made some significant changes to most ESP RainMaker APIs to make them even more modular and object oriented. You can check the examples to see what has changed, but here is a guide to help you understand some major changes.
|
||||||
|
|
||||||
|
### RainMaker Initialisation
|
||||||
|
|
||||||
|
#### Old
|
||||||
|
```
|
||||||
|
typedef struct {
|
||||||
|
char *name;
|
||||||
|
char *type;
|
||||||
|
char *fw_version;
|
||||||
|
char *model;
|
||||||
|
} esp_rmaker_node_info_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_rmaker_node_info_t info;
|
||||||
|
bool enable_time_sync;
|
||||||
|
} esp_rmaker_config_t;
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_init(esp_rmaker_config_t *config);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### New
|
||||||
|
```
|
||||||
|
typedef struct {
|
||||||
|
bool enable_time_sync;
|
||||||
|
} esp_rmaker_config_t;
|
||||||
|
|
||||||
|
esp_rmaker_node_t *esp_rmaker_node_init(const esp_rmaker_config_t *config, const char *name, const char *type);
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
|
||||||
|
```
|
||||||
|
esp_err_t esp_rmaker_node_add_fw_version(const esp_rmaker_node_t *node, const char *fw_version);
|
||||||
|
esp_err_t esp_rmaker_node_add_model(const esp_rmaker_node_t *node, const char *model);
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- `esp_rmaker_init()` changed to `esp_rmaker_node_init()`.
|
||||||
|
- Init function now returns a node handle instead of error code.
|
||||||
|
- Node Info is no more part of the config structure. The mandatory fields, "name" and "type" are directly passed as strings during initialization.
|
||||||
|
- Model and fw version are set internally using the project name and version build variables. They can be overriden using `esp_rmaker_node_add_fw_version/model`.
|
||||||
|
|
||||||
|
### Devices
|
||||||
|
|
||||||
|
#### Old
|
||||||
|
```
|
||||||
|
typedef esp_err_t (*esp_rmaker_param_callback_t)(const char *name, const char *dev_name, esp_rmaker_param_val_t val, void *priv_data);
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_create_device(const char *dev_name, const char *type, esp_rmaker_param_callback_t cb, void *priv_data);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### New
|
||||||
|
```
|
||||||
|
typedef esp_err_t (*esp_rmaker_device_write_cb_t)(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||||
|
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx);
|
||||||
|
typedef esp_err_t (*esp_rmaker_device_read_cb_t)(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||||
|
void *priv_data, esp_rmaker_read_ctx_t *ctx);
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_device_create(const char *dev_name, const char *type, void *priv_data);
|
||||||
|
esp_err_t esp_rmaker_device_add_cb(const esp_rmaker_device_t *device, esp_rmaker_device_write_cb_t write_cb, esp_rmaker_device_read_cb_t read_cb);
|
||||||
|
esp_err_t esp_rmaker_node_add_device(const esp_rmaker_node_t *node, const esp_rmaker_device_t *device);
|
||||||
|
esp_err_t esp_rmaker_node_remove_device(const esp_rmaker_node_t *node, const esp_rmaker_device_t *device);
|
||||||
|
```
|
||||||
|
|
||||||
|
- `esp_rmaker_create_device()` changed to `esp_rmaker_device_create()`. It returns a device handle which has to be used for all further operations.
|
||||||
|
- The callback has changed such that it now gets the device and param handle, rather than the names. A read callback has also been introduced for future use. It can be kept NULL.
|
||||||
|
- The callbacks have a new "context" which can have additional information related to that callback, populated by the rainmaker core.
|
||||||
|
- After creating the device, it has to be added to the node explicitly using `esp_rmaker_node_add_device()`.
|
||||||
|
- Device can be removed using `esp_rmaker_node_remove_device()`. This may be required for bridges.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
#### Old
|
||||||
|
```
|
||||||
|
esp_err_t esp_rmaker_device_add_param(const char *dev_name, const char *param_name,
|
||||||
|
esp_rmaker_param_val_t val, uint8_t properties);
|
||||||
|
esp_rmaker_param_add_type(const char *dev_name, const char *param_name, const char* type);
|
||||||
|
esp_err_t esp_rmaker_update_param(const char *dev_name, const char *param_name, esp_rmaker_param_val_t val);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### New
|
||||||
|
```
|
||||||
|
esp_rmaker_param_t *esp_rmaker_param_create(const char *param_name, const char *type,
|
||||||
|
esp_rmaker_param_val_t val, uint8_t properties);
|
||||||
|
esp_err_t esp_rmaker_device_add_param(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param);
|
||||||
|
esp_err_t esp_rmaker_param_update_and_report(const esp_rmaker_param_t *param, esp_rmaker_param_val_t val);
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- New API `esp_rmaker_param_create()` introduced to create a parameter. It returns a param handle which has to be used for all further operations.
|
||||||
|
- `esp_rmaker_device_add_param()` modified to accept the device and param handles.
|
||||||
|
- `esp_rmaker_param_add_type()` removed because the type is now included in `esp_rmaker_param_create()`
|
||||||
|
- `esp_rmaker_update_param()` changed to `esp_rmaker_param_update_and_report()`. It now accepts param handle, instead of device and parameter names.
|
||||||
201
LICENSE
Normal file
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
65
README.md
Normal file
65
README.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# ESP RainMaker
|
||||||
|
|
||||||
|
> This branch will not support esp-idf v4.x releases anymore as they have reached their end of life. Please check out `idf_4_x_compat` branch if your project requires it.
|
||||||
|
|
||||||
|
> Note: For major changes, please refer [this file](CHANGES.md).
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
ESP RainMaker is an end-to-end solution offered by Espressif to enable remote control and monitoring for products based on ESP32 series of SoCs (e.g., ESP32, ESP32-S2, ESP32-C3, ESP32-C6, ESP32-C2, etc.) without any configuration required in the Cloud. <br>
|
||||||
|
|
||||||
|
The primary components of this solution are:
|
||||||
|
|
||||||
|
- Claiming Service (to get the Cloud connectivity credentials)
|
||||||
|
- RainMaker Agent (i.e. this repo, to develop the firmware)
|
||||||
|
- RainMaker Cloud (backend, offering remote connectivity)
|
||||||
|
- RainMaker Phone App/CLI (Client utilities for remote access)
|
||||||
|
|
||||||
|
|
||||||
|
The key features of ESP RainMaker are:
|
||||||
|
|
||||||
|
1. Ability to define own devices and parameters, of any type, in the firmware.
|
||||||
|
2. Zero configuration required on the Cloud.
|
||||||
|
3. Phone apps that dynamically render the UI as per the device information.
|
||||||
|
|
||||||
|
## Get ESP RainMaker
|
||||||
|
|
||||||
|
Please clone this repository using the below command:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone --recursive https://github.com/espressif/esp-rainmaker.git
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note the --recursive option. This is required to pull in the various dependencies into esp-rainmaker. In case you have already cloned the repository without this option, execute this to pull in the submodules: `git submodule update --init --recursive`
|
||||||
|
|
||||||
|
Please check the ESP RainMaker documentation [here](https://rainmaker.espressif.com/docs/get-started.html) to get started.
|
||||||
|
|
||||||
|
Each example has its own README with additional information about using the example.
|
||||||
|
|
||||||
|
## Supported ESP-IDF versions
|
||||||
|
|
||||||
|
ESP RainMaker can work with ESP IDF 4.1 and above.
|
||||||
|
|
||||||
|
## Phone Apps
|
||||||
|
|
||||||
|
### Android
|
||||||
|
|
||||||
|
- [Google PlayStore](https://play.google.com/store/apps/details?id=com.espressif.rainmaker)
|
||||||
|
- [Direct APK](https://github.com/espressif/esp-rainmaker/wiki)
|
||||||
|
- [Source Code](https://github.com/espressif/esp-rainmaker-android)
|
||||||
|
|
||||||
|
### iOS
|
||||||
|
- [Apple App Store](https://apps.apple.com/app/esp-rainmaker/id1497491540)
|
||||||
|
- [Source Code](https://github.com/espressif/esp-rainmaker-ios)
|
||||||
|
|
||||||
|
## Discussions
|
||||||
|
|
||||||
|
[ESP32 Forum](https://www.esp32.com/viewforum.php?f=41)
|
||||||
|
|
||||||
|
[](https://gitter.im/esp-rainmaker/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a href="https://espressif.github.io/esp-launchpad/?solution=rainmaker">
|
||||||
|
<img alt="Try it with ESP Launchpad" src="https://espressif.github.io/esp-launchpad/assets/try_with_launchpad.png" width="250" height="70">
|
||||||
|
</a>
|
||||||
94
components/esp_rainmaker/CMakeLists.txt
Normal file
94
components/esp_rainmaker/CMakeLists.txt
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# CORE
|
||||||
|
set(core_srcs "src/core/esp_rmaker_core.c"
|
||||||
|
"src/core/esp_rmaker_node.c"
|
||||||
|
"src/core/esp_rmaker_device.c"
|
||||||
|
"src/core/esp_rmaker_param.c"
|
||||||
|
"src/core/esp_rmaker_node_config.c"
|
||||||
|
"src/core/esp_rmaker_client_data.c"
|
||||||
|
"src/core/esp_rmaker_time_service.c"
|
||||||
|
"src/core/esp_rmaker_system_service.c"
|
||||||
|
"src/core/esp_rmaker_user_mapping.pb-c.c"
|
||||||
|
"src/core/esp_rmaker_user_mapping.c"
|
||||||
|
"src/core/esp_rmaker_node_auth.c"
|
||||||
|
"src/core/esp_rmaker_schedule.c"
|
||||||
|
"src/core/esp_rmaker_scenes.c"
|
||||||
|
"src/core/esp_rmaker_cmd_resp_manager.c"
|
||||||
|
"src/core/esp_rmaker_secure_boot_digest.c"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(priv_req protobuf-c json_parser json_generator
|
||||||
|
nvs_flash esp_http_client app_update esp-tls mbedtls esp_https_ota
|
||||||
|
console esp_local_ctrl esp_https_server mdns esp_schedule efuse driver rmaker_common wifi_provisioning)
|
||||||
|
|
||||||
|
if ("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||||
|
list(APPEND priv_req esp_app_format)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if ("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.1")
|
||||||
|
# NAT64 and DNS64 features were introduced for openthread component in IDF v5.1
|
||||||
|
# Network Provisioning component is supported for IDF v5.1+
|
||||||
|
list(APPEND priv_req openthread network_provisioning)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CONFIG_ESP_RMAKER_ASSISTED_CLAIM)
|
||||||
|
list(APPEND core_srcs
|
||||||
|
"src/core/esp_rmaker_claim.c"
|
||||||
|
"src/core/esp_rmaker_claim.pb-c.c")
|
||||||
|
endif()
|
||||||
|
if(CONFIG_ESP_RMAKER_SELF_CLAIM)
|
||||||
|
list(APPEND core_srcs
|
||||||
|
"src/core/esp_rmaker_claim.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE)
|
||||||
|
list(APPEND core_srcs
|
||||||
|
"src/core/esp_rmaker_local_ctrl.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(core_priv_includes "src/core")
|
||||||
|
|
||||||
|
# MQTT
|
||||||
|
set(mqtt_srcs "src/mqtt/esp_rmaker_mqtt.c"
|
||||||
|
"src/mqtt/esp_rmaker_mqtt_budget.c")
|
||||||
|
set(mqtt_priv_includes "src/mqtt")
|
||||||
|
|
||||||
|
# OTA
|
||||||
|
set(ota_srcs "src/ota/esp_rmaker_ota.c"
|
||||||
|
"src/ota/esp_rmaker_ota_using_params.c"
|
||||||
|
"src/ota/esp_rmaker_ota_using_topics.c")
|
||||||
|
set(ota_priv_includes "src/ota")
|
||||||
|
|
||||||
|
# Thread BR
|
||||||
|
set(thread_br_srcs )
|
||||||
|
set(thread_br_priv_includes )
|
||||||
|
if (CONFIG_OPENTHREAD_BORDER_ROUTER)
|
||||||
|
list(APPEND thread_br_srcs "src/thread_br/esp_rmaker_thread_br.c"
|
||||||
|
"src/thread_br/esp_rmaker_thread_br_service.c"
|
||||||
|
"src/thread_br/esp_rmaker_thread_br_internal.c"
|
||||||
|
"src/thread_br/esp_rmaker_thread_br_launcher.c")
|
||||||
|
list(APPEND thread_br_priv_includes "src/thread_br")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# CONSOLE
|
||||||
|
set(console_srcs "src/console/esp_rmaker_console.c"
|
||||||
|
"src/console/esp_rmaker_commands.c")
|
||||||
|
set(console_priv_includes "src/console")
|
||||||
|
|
||||||
|
# STANDARD TYPES
|
||||||
|
set(standard_types_srcs "src/standard_types/esp_rmaker_standard_params.c"
|
||||||
|
"src/standard_types/esp_rmaker_standard_devices.c"
|
||||||
|
"src/standard_types/esp_rmaker_standard_services.c")
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${core_srcs} ${mqtt_srcs} ${ota_srcs} ${standard_types_srcs} ${console_srcs} ${thread_br_srcs}
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
PRIV_INCLUDE_DIRS ${core_priv_includes} ${ota_priv_includes} ${console_priv_includes} ${mqtt_priv_includes} ${thread_br_priv_includes}
|
||||||
|
REQUIRES rmaker_common
|
||||||
|
PRIV_REQUIRES ${priv_req})
|
||||||
|
|
||||||
|
target_add_binary_data(${COMPONENT_TARGET} "server_certs/rmaker_mqtt_server.crt" TEXT)
|
||||||
|
target_add_binary_data(${COMPONENT_TARGET} "server_certs/rmaker_claim_service_server.crt" TEXT)
|
||||||
|
target_add_binary_data(${COMPONENT_TARGET} "server_certs/rmaker_ota_server.crt" TEXT)
|
||||||
|
|
||||||
|
# Added just to automatically trigger re-runs of CMake
|
||||||
|
git_describe(RMAKER_VERSION ${COMPONENT_DIR})
|
||||||
|
message("ESP RainMaker Project commit: " ${RMAKER_VERSION})
|
||||||
396
components/esp_rainmaker/Kconfig.projbuild
Normal file
396
components/esp_rainmaker/Kconfig.projbuild
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
menu "ESP RainMaker Config"
|
||||||
|
|
||||||
|
choice ESP_RMAKER_CLAIM_TYPE
|
||||||
|
bool "Claiming Type"
|
||||||
|
default ESP_RMAKER_SELF_CLAIM
|
||||||
|
default ESP_RMAKER_ASSISTED_CLAIM if IDF_TARGET_ESP32
|
||||||
|
help
|
||||||
|
Claiming type to be used.
|
||||||
|
|
||||||
|
config ESP_RMAKER_NO_CLAIM
|
||||||
|
bool "Do not use Claiming"
|
||||||
|
help
|
||||||
|
Do not use any claiming. The MQTT credentials need to
|
||||||
|
be pre-programmed for this to work. This should be used
|
||||||
|
for all private RainMaker deployments.
|
||||||
|
|
||||||
|
config ESP_RMAKER_SELF_CLAIM
|
||||||
|
bool "Use Self Claiming"
|
||||||
|
depends on !IDF_TARGET_ESP32 && !IDF_TARGET_ESP32C2
|
||||||
|
help
|
||||||
|
Use Self Claiming i.e. get the MQTT credentials
|
||||||
|
directly from the claiming service.
|
||||||
|
|
||||||
|
config ESP_RMAKER_ASSISTED_CLAIM
|
||||||
|
bool "Use Assisted Claiming"
|
||||||
|
depends on BT_ENABLED && !IDF_TARGET_ESP32S2
|
||||||
|
help
|
||||||
|
Use Assisted Claiming i.e. get the MQTT credentials
|
||||||
|
from the claiming service via assistance from clients,
|
||||||
|
like the phone apps.
|
||||||
|
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
choice ESP_RMAKER_CHOOSE_PKI_ACCESS_METHOD
|
||||||
|
prompt "Choose PKI credentials access method"
|
||||||
|
default ESP_RMAKER_USE_NVS
|
||||||
|
help
|
||||||
|
ESP devices support multiple ways to secure store the PKI credentials.
|
||||||
|
Currently, NVS and ESP Secure Cert Manager are supported.
|
||||||
|
The default behaviour is to access the PKI credentials from the NVS.
|
||||||
|
Consult the ESP-TLS documentation in ESP-IDF Programming guide for more details.
|
||||||
|
|
||||||
|
config ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||||
|
bool "Use ESP Secure Certificate Manager"
|
||||||
|
depends on ESP_RMAKER_NO_CLAIM
|
||||||
|
help
|
||||||
|
Enable the use of ESP Secure Certificate Manager APIs for the example.
|
||||||
|
Please refer to ESP Secure Certificate Manager documentation for more details.
|
||||||
|
|
||||||
|
config ESP_RMAKER_USE_NVS
|
||||||
|
bool "Use NVS (default)"
|
||||||
|
help
|
||||||
|
This option expects the Private key and Device certificate to be in the NVS.
|
||||||
|
This is the default behaviour.
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
|
||||||
|
config ESP_RMAKER_CLAIM_TYPE
|
||||||
|
int
|
||||||
|
default 0 if ESP_RMAKER_NO_CLAIM
|
||||||
|
default 1 if ESP_RMAKER_SELF_CLAIM
|
||||||
|
default 2 if ESP_RMAKER_ASSISTED_CLAIM
|
||||||
|
|
||||||
|
config ESP_RMAKER_CLAIM_SERVICE_BASE_URL
|
||||||
|
string "ESP RainMaker Claiming Service Base URL"
|
||||||
|
default "https://esp-claiming.rainmaker.espressif.com"
|
||||||
|
depends on ESP_RMAKER_SELF_CLAIM
|
||||||
|
help
|
||||||
|
ESP RainMaker Claiming Service Base URL.
|
||||||
|
|
||||||
|
config ESP_RMAKER_READ_MQTT_HOST_FROM_CONFIG
|
||||||
|
bool "Read MQTT Host from ESP_RMAKER_MQTT_HOST (Read Docs)"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Normally, if self claiming or assisted claiming is used, the MQTT Host is anyways read from
|
||||||
|
ESP_RMAKER_MQTT_HOST, independent of this config option. However, if this is set, even if
|
||||||
|
an MQTT host value is found in NVS, it will be overriden with ESP_RMAKER_MQTT_HOST.
|
||||||
|
|
||||||
|
config ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN
|
||||||
|
bool "Read Node ID from Device Certificate"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
If enabled, the device will get its node id from the device certificate's CN field. If not enabled,
|
||||||
|
it will read the node id either from nvs factory partition or mac address, depending on the configuration.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_HOST
|
||||||
|
string "ESP RainMaker MQTT Host"
|
||||||
|
depends on ESP_RMAKER_SELF_CLAIM || ESP_RMAKER_ASSISTED_CLAIM || ESP_RMAKER_READ_MQTT_HOST_FROM_CONFIG
|
||||||
|
default "a1p72mufdu6064-ats.iot.us-east-1.amazonaws.com"
|
||||||
|
help
|
||||||
|
ESP RainMaker MQTT Host name.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_USE_BASIC_INGEST_TOPICS
|
||||||
|
bool "Use Basic Ingest Topics"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
This config enables the use of AWS Basic Ingest Topics for Node to Cloud communication,
|
||||||
|
which eliminates the MQTT Broker and thus reduces messaging cost.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||||
|
bool "Enable MQTT budgeting"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Enable MQTT budgeting, which will control the number of MQTT messages sent by the node.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_DEFAULT_BUDGET
|
||||||
|
int "Default MQTT Budget"
|
||||||
|
depends on ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||||
|
default 100
|
||||||
|
range 64 ESP_RMAKER_MQTT_MAX_BUDGET
|
||||||
|
help
|
||||||
|
Default MQTT budget. Budget will reduce on sending an MQTT message and increase based on
|
||||||
|
ESP_RMAKER_MQTT_BUDGET_REVIVE_PERIOD. If no budget is available, MQTT message will be dropped.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_MAX_BUDGET
|
||||||
|
int "Max MQTT Budget"
|
||||||
|
depends on ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||||
|
default 1024
|
||||||
|
range 64 2048
|
||||||
|
help
|
||||||
|
Maximum budget that the node can have. No additional budget will be allocated if this count is reached.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_BUDGET_REVIVE_PERIOD
|
||||||
|
int "MQTT Budget revive period"
|
||||||
|
depends on ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||||
|
default 5
|
||||||
|
range 5 600
|
||||||
|
help
|
||||||
|
Period in seconds after which the MQTT budget should revive (by ESP_RMAKER_MQTT_BUDGET_REVIVE_COUNT).
|
||||||
|
This is used to limit the messages being sent by the node.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_BUDGET_REVIVE_COUNT
|
||||||
|
int "MQTT Budget revive count"
|
||||||
|
depends on ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||||
|
default 1
|
||||||
|
range 1 16
|
||||||
|
help
|
||||||
|
The count by which the budget will be increased periodically based on ESP_RMAKER_MQTT_BUDGET_REVIVE_PERIOD.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MAX_PARAM_DATA_SIZE
|
||||||
|
int "Maximum Parameters' data size"
|
||||||
|
default 1024
|
||||||
|
range 64 8192
|
||||||
|
help
|
||||||
|
Maximum size of the payload for reporting parameter values.
|
||||||
|
|
||||||
|
config ESP_RMAKER_DISABLE_USER_MAPPING_PROV
|
||||||
|
bool "Disable User Mapping during Provisioning"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
The handlers for User Node Mapping are now registered internally by ESP RainMaker core,
|
||||||
|
by registering to appropriate Wi-Fi Provisioning events. If your application code also
|
||||||
|
has the calls to create and register the user mapping handlers, enable this config
|
||||||
|
option to prevent duplication.
|
||||||
|
|
||||||
|
config ESP_RMAKER_USER_ID_CHECK
|
||||||
|
bool "User id check for User Node mapping"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This enables the additional user id checks during user node mapping. Whenever a new user
|
||||||
|
id is received, it is checked against the existing user id in NVS. If there is a mismatch,
|
||||||
|
or if no user id exists in NVS, this is considered as a reset state and the same is reported
|
||||||
|
to the ESP RainMaker Cloud during the User Node association MQTT Publish so that the cloud
|
||||||
|
can take appropriate action w.r.t user permissions. It is recommended to enable this option
|
||||||
|
for security reasons.
|
||||||
|
|
||||||
|
config RMAKER_NAME_PARAM_CB
|
||||||
|
bool "Call device callback for Name param"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
By default, the "Name" parameter (esp.param.name) changes are handled internally. If Applications
|
||||||
|
want to handle this themselves, this config option can be enabled. Please ensure that you update
|
||||||
|
and report the name parameter in your callback so that it reflects correctly everywhere.
|
||||||
|
If no device callback is registered, the name paramater will be handled internally.
|
||||||
|
|
||||||
|
config ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE
|
||||||
|
bool "ESP RainMaker Local Control Feature"
|
||||||
|
default n
|
||||||
|
select ESP_HTTPS_SERVER_ENABLE
|
||||||
|
help
|
||||||
|
Enabling this allows to discover and control the node over local Wi-Fi network.
|
||||||
|
Note that this uses only Wi-Fi level security and so, any client on the same
|
||||||
|
Wi-Fi network can potentially control the node. The communication is not encrypted
|
||||||
|
and uses plain HTTP. Please Check the RainMaker documentation for additional details.
|
||||||
|
Note that enabling this just means that the APIs to enable/disable local
|
||||||
|
control will be compiled in and can be used in application code. If CONFIG_ESP_RMAKER_LOCAL_CTRL_AUTO_ENABLE
|
||||||
|
is also enabled, then no additional APIs are required for actually enabling local control.
|
||||||
|
|
||||||
|
config ESP_RMAKER_LOCAL_CTRL_AUTO_ENABLE
|
||||||
|
bool "Auto ESP RainMaker Local Control"
|
||||||
|
default n
|
||||||
|
select ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE
|
||||||
|
help
|
||||||
|
Automatically enabled local control when RainMaker starts.
|
||||||
|
|
||||||
|
config ESP_RMAKER_LOCAL_CTRL_HTTP_PORT
|
||||||
|
int "Local Control HTTP Port"
|
||||||
|
depends on ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE
|
||||||
|
default 8080
|
||||||
|
help
|
||||||
|
The port number to be used for http for local control.
|
||||||
|
|
||||||
|
config ESP_RMAKER_LOCAL_CTRL_STACK_SIZE
|
||||||
|
int "Local Control HTTP Server task stack size"
|
||||||
|
depends on ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE
|
||||||
|
default 6144
|
||||||
|
help
|
||||||
|
The task stack size to be used for http server for local control.
|
||||||
|
|
||||||
|
choice ESP_RMAKER_LOCAL_CTRL_SECURITY
|
||||||
|
prompt "Local Control Security Type"
|
||||||
|
depends on ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE
|
||||||
|
default ESP_RMAKER_LOCAL_CTRL_SECURITY_1
|
||||||
|
help
|
||||||
|
Security type to be selected for local control.
|
||||||
|
|
||||||
|
config ESP_RMAKER_LOCAL_CTRL_SECURITY_0
|
||||||
|
bool "sec0"
|
||||||
|
config ESP_RMAKER_LOCAL_CTRL_SECURITY_1
|
||||||
|
bool "sec1"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config ESP_RMAKER_LOCAL_CTRL_SECURITY
|
||||||
|
int
|
||||||
|
default 0 if ESP_RMAKER_LOCAL_CTRL_SECURITY_0
|
||||||
|
default 1 if ESP_RMAKER_LOCAL_CTRL_SECURITY_1
|
||||||
|
|
||||||
|
choice ESP_RMAKER_CONSOLE_UART_NUM
|
||||||
|
prompt "UART for console input"
|
||||||
|
default ESP_RMAKER_CONSOLE_UART_NUM_0
|
||||||
|
help
|
||||||
|
UART to be selected for serial console.
|
||||||
|
|
||||||
|
config ESP_RMAKER_CONSOLE_UART_NUM_0
|
||||||
|
bool "UART0"
|
||||||
|
config ESP_RMAKER_CONSOLE_UART_NUM_1
|
||||||
|
bool "UART1"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config ESP_RMAKER_CONSOLE_UART_NUM
|
||||||
|
int
|
||||||
|
default 0 if ESP_RMAKER_CONSOLE_UART_NUM_0
|
||||||
|
default 1 if ESP_RMAKER_CONSOLE_UART_NUM_1
|
||||||
|
|
||||||
|
config ESP_RMAKER_USE_CERT_BUNDLE
|
||||||
|
bool "Use Certificate Bundle"
|
||||||
|
default y
|
||||||
|
select ESP_RMAKER_MQTT_USE_CERT_BUNDLE
|
||||||
|
help
|
||||||
|
Use Certificate Bundle for server authentication. Enabling this is recommended to safeguard
|
||||||
|
against any changes in the server certificates in future. This has an impact on the binary
|
||||||
|
size as well as heap requirement.
|
||||||
|
|
||||||
|
menu "ESP RainMaker OTA Config"
|
||||||
|
|
||||||
|
config ESP_RMAKER_OTA_AUTOFETCH
|
||||||
|
bool "Auto Fetch OTA"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Applicable only for OTA using Topics.
|
||||||
|
Fetch the OTA (i.e. get the URL and other details) by actively sending an
|
||||||
|
OTA fetch request to ESP RainMaker Cloud. If this is disabled, the node
|
||||||
|
will stay subscribed to the OTA Topics, but will get the information only
|
||||||
|
if someone explicitly triggers it.
|
||||||
|
|
||||||
|
config ESP_RMAKER_OTA_AUTOFETCH_PERIOD
|
||||||
|
int "OTA Auto Fetch Period"
|
||||||
|
default 0
|
||||||
|
range 0 168
|
||||||
|
depends on ESP_RMAKER_OTA_AUTOFETCH
|
||||||
|
help
|
||||||
|
Periodically send an OTA fetch request. If set to 0, the request will be sent only once,
|
||||||
|
when the node connects to the ESP RainMaker Cloud first time after a boot.
|
||||||
|
Else, this defines the period (in hours) for the periodic fetch request.
|
||||||
|
|
||||||
|
config ESP_RMAKER_SKIP_COMMON_NAME_CHECK
|
||||||
|
bool "Skip server certificate CN field check"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This allows you to skip the validation of OTA server certificate CN field.
|
||||||
|
|
||||||
|
config ESP_RMAKER_SKIP_VERSION_CHECK
|
||||||
|
bool "Skip firmware version check"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This allows you to skip the firmware version check. Useful during development,
|
||||||
|
but not for production.
|
||||||
|
|
||||||
|
config ESP_RMAKER_SKIP_SECURE_VERSION_CHECK
|
||||||
|
bool "Skip secure version check"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This allows you to skip the secure version check. Useful during development,
|
||||||
|
but not for production. Check out ESP IDF's Anti-rollback feature for more details.
|
||||||
|
|
||||||
|
config ESP_RMAKER_SKIP_PROJECT_NAME_CHECK
|
||||||
|
bool "Skip project name check"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This allows you to skip the project name check.
|
||||||
|
|
||||||
|
config ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE
|
||||||
|
int "OTA HTTP receive buffer size"
|
||||||
|
default 1024
|
||||||
|
range 512 LWIP_TCP_WND_DEFAULT
|
||||||
|
help
|
||||||
|
Increasing this value beyond the default would speed up the OTA download process.
|
||||||
|
However, please ensure that your application has enough memory headroom to allow this,
|
||||||
|
else, the OTA may fail.
|
||||||
|
|
||||||
|
config ESP_RMAKER_OTA_ROLLBACK_WAIT_PERIOD
|
||||||
|
int "OTA Rollback Wait Period (Seconds)"
|
||||||
|
default 90
|
||||||
|
range 30 600
|
||||||
|
help
|
||||||
|
After an OTA Update, if CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is set, then the firmware will wait for MQTT
|
||||||
|
connection to mark the new firmware as valid. However, if it is not able to do so within
|
||||||
|
this wait period (in seconds), the firmware will be marked as invalid and the older
|
||||||
|
firmware will be booted into.
|
||||||
|
|
||||||
|
config ESP_RMAKER_OTA_DISABLE_AUTO_REBOOT
|
||||||
|
bool "Disable auto reboot"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
After OTA image is flashed and active partition changed, the device automatically reboots. To disable this
|
||||||
|
behaviour and handle reboot on your own, based on RMAKER_OTA event, enable this option.
|
||||||
|
|
||||||
|
config ESP_RMAKER_OTA_TIME_SUPPORT
|
||||||
|
bool "Enable OTA Time Support"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
OTA Jobs can include additional metadata for time to indicate a range of valid date and the time within
|
||||||
|
those dates. Eg. Perform OTA between 1 Dec 2022 and 10 Dec 2022 that too only between 2:00am and 5:00am.
|
||||||
|
If you want to ignore this, disable this option.
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
menu "ESP RainMaker Scheduling"
|
||||||
|
|
||||||
|
config ESP_RMAKER_SCHEDULING_MAX_SCHEDULES
|
||||||
|
int "Maximum schedules"
|
||||||
|
default 10
|
||||||
|
range 1 50
|
||||||
|
help
|
||||||
|
Maximum Number of schedules allowed. The json size for report params increases as the number of schedules increases.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
menu "ESP RainMaker Scenes"
|
||||||
|
|
||||||
|
config ESP_RMAKER_SCENES_MAX_SCENES
|
||||||
|
int "Maximum scenes"
|
||||||
|
default 10
|
||||||
|
range 1 50
|
||||||
|
help
|
||||||
|
Maximum Number of scenes allowed. The json size for report params increases as the number of scenes increases.
|
||||||
|
|
||||||
|
config ESP_RMAKER_SCENES_DEACTIVATE_SUPPORT
|
||||||
|
bool "Enable Deactivate support"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This enables the deactivate callback support. The application callback will be invoked with the source
|
||||||
|
set to ESP_RMAKER_REQ_SRC_SCENE_DEACTIVATE when the deactivate operation is received. However, the
|
||||||
|
param values would be the same as those for activate, since the RainMaker core does not know what the
|
||||||
|
expected values are for scene deactivation.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
menu "ESP RainMaker Command-Response"
|
||||||
|
|
||||||
|
config ESP_RMAKER_CMD_RESP_ENABLE
|
||||||
|
bool "Enable Command-Response Module"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Enable the ESP RainMaker Command-Response module for semi-synchronous communication. Please refer the RainMaker documents
|
||||||
|
for additional information.
|
||||||
|
|
||||||
|
config ESP_RMAKER_CMD_RESP_TEST_ENABLE
|
||||||
|
bool "Enable Command-Response Testing"
|
||||||
|
default n
|
||||||
|
depends on ESP_RMAKER_CMD_RESP_ENABLE
|
||||||
|
help
|
||||||
|
Enable testing for Command-Response module. This enables triggering commands and parsing response from the node itself,
|
||||||
|
rather than receiving the commands from cloud. C API or the serial console can be used to trigger the commands.
|
||||||
|
This should be enabled only while testing commands, but should always be disabled in production firmware.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
config ESP_RMAKER_USING_NETWORK_PROV
|
||||||
|
bool "Using Network Provisioning"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
RainMaker will use network_provisioning component to provision a device to a Wi-Fi/Thread network if enabling this option.
|
||||||
|
If the option is not enabled, it will use wifi_provisioning instead. This option only works when IDF verson is later than
|
||||||
|
v5.1.
|
||||||
|
|
||||||
|
endmenu
|
||||||
201
components/esp_rainmaker/LICENSE
Normal file
201
components/esp_rainmaker/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
5
components/esp_rainmaker/README.md
Normal file
5
components/esp_rainmaker/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# ESP RainMaker Agent Component
|
||||||
|
|
||||||
|
[](https://components.espressif.com/components/espressif/esp_rainmaker)
|
||||||
|
|
||||||
|
This is the main firmware agent for ESP RainMaker, which will then pull in other required components. Please check the [ESP RainMaker documentation](https://rainmaker.espressif.com/) for details.
|
||||||
12
components/esp_rainmaker/component.mk
Normal file
12
components/esp_rainmaker/component.mk
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
COMPONENT_SRCDIRS := src/core src/mqtt src/ota src/standard_types src/console
|
||||||
|
COMPONENT_ADD_INCLUDEDIRS := include
|
||||||
|
COMPONENT_PRIV_INCLUDEDIRS := src/core src/ota src/console
|
||||||
|
|
||||||
|
ifndef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||||
|
COMPONENT_OBJEXCLUDE += src/core/esp_rmaker_claim.pb-c.o
|
||||||
|
ifndef CONFIG_ESP_RMAKER_SELF_CLAIM
|
||||||
|
COMPONENT_OBJEXCLUDE += src/core/esp_rmaker_claim.o
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
COMPONENT_EMBED_TXTFILES := server_certs/rmaker_mqtt_server.crt server_certs/rmaker_claim_service_server.crt server_certs/rmaker_ota_server.crt
|
||||||
33
components/esp_rainmaker/idf_component.yml
Normal file
33
components/esp_rainmaker/idf_component.yml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
## IDF Component Manager Manifest File
|
||||||
|
version: "1.5.4"
|
||||||
|
description: ESP RainMaker firmware agent
|
||||||
|
url: https://github.com/espressif/esp-rainmaker/tree/master/components/esp_rainmaker
|
||||||
|
repository: https://github.com/espressif/esp-rainmaker.git
|
||||||
|
issues: https://github.com/espressif/esp-rainmaker/issues
|
||||||
|
documentation: https://rainmaker.espressif.com/
|
||||||
|
discussion: https://www.esp32.com/viewforum.php?f=41
|
||||||
|
dependencies:
|
||||||
|
espressif/mdns:
|
||||||
|
version: "^1.2.0"
|
||||||
|
rules:
|
||||||
|
- if: "idf_version >=5.0"
|
||||||
|
espressif/esp_secure_cert_mgr:
|
||||||
|
version: "^2.2.1"
|
||||||
|
rules:
|
||||||
|
- if: "idf_version >=4.3"
|
||||||
|
espressif/rmaker_common:
|
||||||
|
version: "~1.4.6"
|
||||||
|
espressif/json_parser:
|
||||||
|
version: "~1.0.3"
|
||||||
|
espressif/json_generator:
|
||||||
|
version: "~1.1.1"
|
||||||
|
espressif/esp_schedule:
|
||||||
|
version: "~1.2.0"
|
||||||
|
espressif/network_provisioning:
|
||||||
|
version: "~1.0.0"
|
||||||
|
rules:
|
||||||
|
- if: "idf_version >= 5.1"
|
||||||
|
espressif/esp_rcp_update:
|
||||||
|
version: "~1.2.0"
|
||||||
|
rules:
|
||||||
|
- if: "idf_version >= 5.1"
|
||||||
52
components/esp_rainmaker/include/esp_rmaker_console.h
Normal file
52
components/esp_rainmaker/include/esp_rmaker_console.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Initialize console
|
||||||
|
*
|
||||||
|
* Initializes serial console and adds basic commands.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of failures.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_console_init(void);
|
||||||
|
|
||||||
|
/* Reference for adding custom console commands:
|
||||||
|
#include <esp_console.h>
|
||||||
|
|
||||||
|
static int command_console_handler(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
// Command code here
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_console_command()
|
||||||
|
{
|
||||||
|
const esp_console_cmd_t cmd = {
|
||||||
|
.command = "<command_name>",
|
||||||
|
.help = "<help_details>",
|
||||||
|
.func = &command_console_handler,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
1068
components/esp_rainmaker/include/esp_rmaker_core.h
Normal file
1068
components/esp_rainmaker/include/esp_rmaker_core.h
Normal file
File diff suppressed because it is too large
Load Diff
125
components/esp_rainmaker/include/esp_rmaker_mqtt.h
Normal file
125
components/esp_rainmaker/include/esp_rmaker_mqtt.h
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_rmaker_mqtt_glue.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_rmaker_mqtt_conn_params_t *esp_rmaker_mqtt_get_conn_params(void);
|
||||||
|
|
||||||
|
/** Initialize ESP RainMaker MQTT
|
||||||
|
*
|
||||||
|
* @param[in] conn_params The MQTT configuration data
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of any error.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_mqtt_init(esp_rmaker_mqtt_conn_params_t *conn_params);
|
||||||
|
|
||||||
|
/* Deinitialize ESP RainMaker MQTT
|
||||||
|
*
|
||||||
|
* Call this function after MQTT has disconnected.
|
||||||
|
*/
|
||||||
|
void esp_rmaker_mqtt_deinit(void);
|
||||||
|
|
||||||
|
/** MQTT Connect
|
||||||
|
*
|
||||||
|
* Starts the connection attempts to the MQTT broker as per the configuration
|
||||||
|
* provided during initializing.
|
||||||
|
* This should ideally be called after successful network connection.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of any error.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_mqtt_connect(void);
|
||||||
|
|
||||||
|
/** MQTT Disconnect
|
||||||
|
*
|
||||||
|
* Disconnects from the MQTT broker.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of any error.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_mqtt_disconnect(void);
|
||||||
|
|
||||||
|
/** Publish MQTT Message
|
||||||
|
*
|
||||||
|
* @param[in] topic The MQTT topic on which the message should be published.
|
||||||
|
* @param[in] data Data to be published
|
||||||
|
* @param[in] data_len Length of the data
|
||||||
|
* @param[in] qos Quality of Service for the Publish. Can be 0, 1 or 2. Also depends on what the MQTT broker supports.
|
||||||
|
* @param[out] msg_id msg_id for tracking if message is queued
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of any error.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_mqtt_publish(const char *topic, void *data, size_t data_len, uint8_t qos, int *msg_id);
|
||||||
|
|
||||||
|
/** Subscribe to MQTT topic
|
||||||
|
*
|
||||||
|
* @param[in] topic The topic to be subscribed to.
|
||||||
|
* @param[in] cb The callback to be invoked when a message is received on the given topic.
|
||||||
|
* @param[in] priv_data Optional private data to be passed to the callback
|
||||||
|
* @param[in] qos Quality of Service for the Subscription. Can be 0, 1 or 2. Also depends on what the MQTT broker supports.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of any error.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_mqtt_subscribe(const char *topic, esp_rmaker_mqtt_subscribe_cb_t cb, uint8_t qos, void *priv_data);
|
||||||
|
|
||||||
|
/** Unsubscribe from MQTT topic
|
||||||
|
*
|
||||||
|
* @param[in] topic Topic from which to unsubscribe.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of any error.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_mqtt_unsubscribe(const char *topic);
|
||||||
|
esp_err_t esp_rmaker_mqtt_setup(esp_rmaker_mqtt_config_t mqtt_config);
|
||||||
|
|
||||||
|
/** Creates appropriate MQTT Topic String based on CONFIG_ESP_RMAKER_MQTT_USE_BASIC_INGEST_TOPICS
|
||||||
|
* @param[out] buf Buffer to hold topic string
|
||||||
|
* @param[in] buf_size Size of buffer
|
||||||
|
* @param[in] topic_suffix MQTT Topic suffix
|
||||||
|
* @param[in] rule Basic Ingests Rule Name
|
||||||
|
*/
|
||||||
|
void esp_rmaker_create_mqtt_topic(char *buf, size_t buf_size, const char *topic_suffix, const char *rule);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if budget is available to publish an mqtt message
|
||||||
|
*
|
||||||
|
* @return true if budget is available
|
||||||
|
* @return false if budget is exhausted
|
||||||
|
*
|
||||||
|
* @note `esp_rmaker_mqtt_publish` API already does this check. In addition to that,
|
||||||
|
* some use-cases might still need to check for this.
|
||||||
|
*/
|
||||||
|
bool esp_rmaker_mqtt_is_budget_available(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if device is connected to MQTT Server
|
||||||
|
*
|
||||||
|
* @return true if device is connected
|
||||||
|
* @return false if device is not connected
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool esp_rmaker_is_mqtt_connected();
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
272
components/esp_rainmaker/include/esp_rmaker_ota.h
Normal file
272
components/esp_rainmaker/include/esp_rmaker_ota.h
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @cond **/
|
||||||
|
/** ESP RainMaker Event Base */
|
||||||
|
ESP_EVENT_DECLARE_BASE(RMAKER_OTA_EVENT);
|
||||||
|
/** @endcond **/
|
||||||
|
|
||||||
|
/** ESP RainMaker Events */
|
||||||
|
typedef enum {
|
||||||
|
/* Invalid event. Used for internal handling only */
|
||||||
|
RMAKER_OTA_EVENT_INVALID = 0,
|
||||||
|
/** RainMaker OTA is Starting */
|
||||||
|
RMAKER_OTA_EVENT_STARTING,
|
||||||
|
/** RainMaker OTA has Started */
|
||||||
|
RMAKER_OTA_EVENT_IN_PROGRESS,
|
||||||
|
/** RainMaker OTA Successful */
|
||||||
|
RMAKER_OTA_EVENT_SUCCESSFUL,
|
||||||
|
/** RainMaker OTA Failed */
|
||||||
|
RMAKER_OTA_EVENT_FAILED,
|
||||||
|
/** RainMaker OTA Rejected */
|
||||||
|
RMAKER_OTA_EVENT_REJECTED,
|
||||||
|
/** RainMaker OTA Delayed */
|
||||||
|
RMAKER_OTA_EVENT_DELAYED,
|
||||||
|
/** OTA Image has been flashed and active partition changed. Reboot is requested. Applicable only if Auto reboot is disabled **/
|
||||||
|
RMAKER_OTA_EVENT_REQ_FOR_REBOOT,
|
||||||
|
} esp_rmaker_ota_event_t;
|
||||||
|
|
||||||
|
/** Default ESP RainMaker OTA Server Certificate */
|
||||||
|
extern const char *ESP_RMAKER_OTA_DEFAULT_SERVER_CERT;
|
||||||
|
|
||||||
|
/** OTA Status to be reported to ESP RainMaker Cloud */
|
||||||
|
typedef enum {
|
||||||
|
/** OTA is in Progress. This can be reported multiple times as the OTA progresses. */
|
||||||
|
OTA_STATUS_IN_PROGRESS = 1,
|
||||||
|
/** OTA Succeeded. This should be reported only once, at the end of OTA. */
|
||||||
|
OTA_STATUS_SUCCESS,
|
||||||
|
/** OTA Failed. This should be reported only once, at the end of OTA. */
|
||||||
|
OTA_STATUS_FAILED,
|
||||||
|
/** OTA was delayed by the application */
|
||||||
|
OTA_STATUS_DELAYED,
|
||||||
|
/** OTA rejected due to some reason (wrong project, version, etc.) */
|
||||||
|
OTA_STATUS_REJECTED,
|
||||||
|
} ota_status_t;
|
||||||
|
|
||||||
|
/** OTA Workflow type */
|
||||||
|
typedef enum {
|
||||||
|
/** OTA will be performed using services and parameters. */
|
||||||
|
OTA_USING_PARAMS = 1,
|
||||||
|
/** OTA will be performed using pre-defined MQTT topics. */
|
||||||
|
OTA_USING_TOPICS
|
||||||
|
} esp_rmaker_ota_type_t;
|
||||||
|
|
||||||
|
/** The OTA Handle to be used by the OTA callback */
|
||||||
|
typedef void *esp_rmaker_ota_handle_t;
|
||||||
|
|
||||||
|
/** OTA Data */
|
||||||
|
typedef struct {
|
||||||
|
/** The OTA URL received from ESP RainMaker Cloud */
|
||||||
|
char *url;
|
||||||
|
/** Size of the OTA File. Can be 0 if the file size isn't received from
|
||||||
|
* the ESP RainMaker Cloud */
|
||||||
|
int filesize;
|
||||||
|
/** The firmware version of the OTA image **/
|
||||||
|
char *fw_version;
|
||||||
|
/** The OTA Job ID received from cloud **/
|
||||||
|
char *ota_job_id;
|
||||||
|
/** The server certificate passed in esp_rmaker_enable_ota() */
|
||||||
|
const char *server_cert;
|
||||||
|
/** The private data passed in esp_rmaker_enable_ota() */
|
||||||
|
char *priv;
|
||||||
|
/** OTA Metadata. Applicable only for OTA using Topics. Will be received (if applicable) from the backend, along with the OTA URL */
|
||||||
|
char *metadata;
|
||||||
|
} esp_rmaker_ota_data_t;
|
||||||
|
|
||||||
|
/** Function prototype for OTA Callback
|
||||||
|
*
|
||||||
|
* This function will be invoked by the ESP RainMaker core whenever an OTA is available.
|
||||||
|
* The esp_rmaker_report_ota_status() API should be used to indicate the progress and
|
||||||
|
* success/fail status.
|
||||||
|
*
|
||||||
|
* @param[in] handle An OTA handle assigned by the ESP RainMaker Core
|
||||||
|
* @param[in] ota_data The data to be used for the OTA
|
||||||
|
*
|
||||||
|
* @return ESP_OK if the OTA was successful
|
||||||
|
* @return ESP_FAIL if the OTA failed.
|
||||||
|
*/
|
||||||
|
typedef esp_err_t (*esp_rmaker_ota_cb_t) (esp_rmaker_ota_handle_t handle,
|
||||||
|
esp_rmaker_ota_data_t *ota_data);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/** OTA Diagnostics Failed. Rollback the firmware. */
|
||||||
|
OTA_DIAG_STATUS_FAIL,
|
||||||
|
/** OTA Diagnostics Pending. Additional validations will be done later. */
|
||||||
|
OTA_DIAG_STATUS_PENDING,
|
||||||
|
/** OTA Diagnostics Succeeded. Firmware can be considered valid. */
|
||||||
|
OTA_DIAG_STATUS_SUCCESS
|
||||||
|
} esp_rmaker_ota_diag_status_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/** OTA State: Initialised. */
|
||||||
|
OTA_DIAG_STATE_INIT,
|
||||||
|
/** OTA state: MQTT has connected. */
|
||||||
|
OTA_DIAG_STATE_POST_MQTT
|
||||||
|
} esp_rmaker_ota_diag_state_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/** OTA diagnostic state */
|
||||||
|
esp_rmaker_ota_diag_state_t state;
|
||||||
|
/** Flag to indicate whether the OTA which has triggered the Diagnostics checks for rollback
|
||||||
|
* was triggered via RainMaker or not. This would be useful only when your application has some
|
||||||
|
* other mechanism for OTA too.
|
||||||
|
*/
|
||||||
|
bool rmaker_ota;
|
||||||
|
} esp_rmaker_ota_diag_priv_t;
|
||||||
|
|
||||||
|
/** Function Prototype for Post OTA Diagnostics
|
||||||
|
*
|
||||||
|
* If the Application rollback feature is enabled, this callback will be invoked
|
||||||
|
* as soon as you call esp_rmaker_ota_enable(), if it is the first
|
||||||
|
* boot after an OTA. You may perform some application specific diagnostics and
|
||||||
|
* report the status which will decide whether to roll back or not.
|
||||||
|
*
|
||||||
|
* This will be invoked once again after MQTT has connected, in case some additional validations
|
||||||
|
* are to be done later.
|
||||||
|
*
|
||||||
|
* If OTA state == OTA_DIAG_STATE_INIT, then
|
||||||
|
* return OTA_DIAG_STATUS_FAIL to indicate failure and rollback.
|
||||||
|
* return OTA_DIAG_STATUS_SUCCESS or OTA_DIAG_STATUS_PENDING to tell internal OTA logic to continue further.
|
||||||
|
*
|
||||||
|
* If OTA state == OTA_DIAG_STATE_POST_MQTT, then
|
||||||
|
* return OTA_DIAG_STATUS_FAIL to indicate failure and rollback.
|
||||||
|
* return OTA_DIAG_STATUS_SUCCESS to indicate validation was successful and mark OTA as valid
|
||||||
|
* return OTA_DIAG_STATUS_PENDING to indicate that some additional validations will be done later
|
||||||
|
* and the OTA will eventually be marked valid/invalid using esp_rmaker_ota_mark_valid() or
|
||||||
|
* esp_rmaker_ota_mark_invalid() respectively.
|
||||||
|
*
|
||||||
|
* @return esp_rmaker_ota_diag_status_t as applicable
|
||||||
|
*/
|
||||||
|
typedef esp_rmaker_ota_diag_status_t (*esp_rmaker_post_ota_diag_t)(esp_rmaker_ota_diag_priv_t *ota_diag_priv, void *priv);
|
||||||
|
|
||||||
|
/** ESP RainMaker OTA Configuration */
|
||||||
|
typedef struct {
|
||||||
|
/** OTA Callback.
|
||||||
|
* The callback to be invoked when an OTA Job is available.
|
||||||
|
* If kept NULL, the internal default callback will be used (Recommended).
|
||||||
|
*/
|
||||||
|
esp_rmaker_ota_cb_t ota_cb;
|
||||||
|
/** OTA Diagnostics Callback.
|
||||||
|
* A post OTA diagnostic handler to be invoked if app rollback feature is enabled.
|
||||||
|
* If kept NULL, the new firmware will be assumed to be fine,
|
||||||
|
* and no rollback will be performed.
|
||||||
|
*/
|
||||||
|
esp_rmaker_post_ota_diag_t ota_diag;
|
||||||
|
/** Server Certificate.
|
||||||
|
* The certificate to be passed to the OTA callback for server authentication.
|
||||||
|
* This is mandatory, unless you have disabled it in ESP HTTPS OTA config option.
|
||||||
|
* If you are using the ESP RainMaker OTA Service, you can just set this to
|
||||||
|
* `ESP_RMAKER_OTA_DEFAULT_SERVER_CERT`.
|
||||||
|
*/
|
||||||
|
const char *server_cert;
|
||||||
|
/** Private Data.
|
||||||
|
* Optional private data to be passed to the OTA callback.
|
||||||
|
*/
|
||||||
|
void *priv;
|
||||||
|
} esp_rmaker_ota_config_t;
|
||||||
|
|
||||||
|
/** Enable OTA
|
||||||
|
*
|
||||||
|
* Calling this API enables OTA as per the ESP RainMaker specification.
|
||||||
|
* Please check the various ESP RainMaker configuration options to
|
||||||
|
* use the different variants of OTA. Refer the documentation for
|
||||||
|
* additional details.
|
||||||
|
*
|
||||||
|
* @param[in] ota_config Pointer to an OTA configuration structure
|
||||||
|
* @param[in] type The OTA workflow type
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return error on failure
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_ota_enable(esp_rmaker_ota_config_t *ota_config, esp_rmaker_ota_type_t type);
|
||||||
|
|
||||||
|
/** Report OTA Status
|
||||||
|
*
|
||||||
|
* This API must be called from the OTA Callback to indicate the status of the OTA. The OTA_STATUS_IN_PROGRESS
|
||||||
|
* can be reported multiple times with appropriate additional information. The final success/failure should
|
||||||
|
* be reported only once, at the end.
|
||||||
|
*
|
||||||
|
* This can be ignored if you are using the default internal OTA callback.
|
||||||
|
*
|
||||||
|
* @param[in] ota_handle The OTA handle received by the callback
|
||||||
|
* @param[in] status Status to be reported
|
||||||
|
* @param[in] additional_info NULL terminated string indicating additional information for the status
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return error on failure
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_ota_report_status(esp_rmaker_ota_handle_t ota_handle, ota_status_t status, char *additional_info);
|
||||||
|
|
||||||
|
/** Default OTA callback
|
||||||
|
*
|
||||||
|
* This is the default OTA callback which will get used if you do not pass your own callback. You can call this
|
||||||
|
* even from your callback, in case you want better control on when the OTA can proceed and yet let the actual
|
||||||
|
* OTA process be managed by the RainMaker Core.
|
||||||
|
*
|
||||||
|
* @param[in] handle An OTA handle assigned by the ESP RainMaker Core
|
||||||
|
* @param[in] ota_data The data to be used for the OTA
|
||||||
|
*
|
||||||
|
* @return ESP_OK if the OTA was successful
|
||||||
|
* @return ESP_FAIL if the OTA failed.
|
||||||
|
* */
|
||||||
|
esp_err_t esp_rmaker_ota_default_cb(esp_rmaker_ota_handle_t handle, esp_rmaker_ota_data_t *ota_data);
|
||||||
|
|
||||||
|
/** Fetch OTA Info
|
||||||
|
*
|
||||||
|
* For OTA using Topics, this API can be used to explicitly ask the backend if an OTA is available.
|
||||||
|
* If it is, then the OTA callback would get invoked.
|
||||||
|
*
|
||||||
|
* @return ESP_OK if the OTA fetch publish message was successful.
|
||||||
|
* @return error on failure
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_ota_fetch(void);
|
||||||
|
|
||||||
|
/** Fetch OTA Info with a delay
|
||||||
|
*
|
||||||
|
* For OTA using Topics, this API can be used to explicitly ask the backend if an OTA is available
|
||||||
|
* after a delay (in seconds) passed as an argument.
|
||||||
|
*
|
||||||
|
* @param[in] time Delay (in seconds)
|
||||||
|
*
|
||||||
|
* @return ESP_OK if the OTA fetch timer was created.
|
||||||
|
* @return error on failure
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_ota_fetch_with_delay(int time);
|
||||||
|
|
||||||
|
/** Mark OTA as valid
|
||||||
|
*
|
||||||
|
* This should be called if the OTA validation has been kept pending by returning OTA_DIAG_STATUS_PENDING
|
||||||
|
* in the ota_diag callback and then, the validation was eventually successful. This can also be used to mark
|
||||||
|
* the OTA valid even before RainMaker core does its own validations (primarily MQTT connection).
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return error on failure
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_ota_mark_valid(void);
|
||||||
|
|
||||||
|
/** Mark OTA as invalid
|
||||||
|
*
|
||||||
|
* This should be called if the OTA validation has been kept pending by returning OTA_DIAG_STATUS_PENDING
|
||||||
|
* in the ota_diag callback and then, the validation eventually failed. This can even be used to rollback
|
||||||
|
* at any point of time before RainMaker core's internal logic and the application's logic mark the OTA
|
||||||
|
* as valid.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return error on failure
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_ota_mark_invalid(void);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
38
components/esp_rainmaker/include/esp_rmaker_scenes.h
Normal file
38
components/esp_rainmaker/include/esp_rmaker_scenes.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <esp_err.h>
|
||||||
|
|
||||||
|
/** Enable Scenes
|
||||||
|
*
|
||||||
|
* This API enables the scenes service for the node. For more information,
|
||||||
|
* check [here](https://rainmaker.espressif.com/docs/scenes.html)
|
||||||
|
*
|
||||||
|
* @note This API should be called after esp_rmaker_node_init() but before esp_rmaker_start().
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_scenes_enable(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
38
components/esp_rainmaker/include/esp_rmaker_schedule.h
Normal file
38
components/esp_rainmaker/include/esp_rmaker_schedule.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Enable Schedules
|
||||||
|
*
|
||||||
|
* This API enables the scheduling service for the node. For more information,
|
||||||
|
* check [here](https://rainmaker.espressif.com/docs/scheduling.html)
|
||||||
|
*
|
||||||
|
* It is recommended to set the timezone while using schedules. Check [here](https://rainmaker.espressif.com/docs/time-service.html#time-zone) for more information on timezones
|
||||||
|
*
|
||||||
|
* @note This API should be called after esp_rmaker_node_init() but before esp_rmaker_start().
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_schedule_enable(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Create a standard Switch device
|
||||||
|
*
|
||||||
|
* This creates a Switch device with the mandatory parameters and also assigns
|
||||||
|
* the primary parameter. The default parameter names will be used.
|
||||||
|
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||||
|
*
|
||||||
|
* @param[in] dev_name The unique device name
|
||||||
|
* @param[in] priv_data (Optional) Private data associated with the device. This should stay
|
||||||
|
* allocated throughout the lifetime of the device
|
||||||
|
* #@param[in] power Default value of the mandatory parameter "power"
|
||||||
|
*
|
||||||
|
* @return Device handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_device_t *esp_rmaker_switch_device_create(const char *dev_name,
|
||||||
|
void *priv_data, bool power);
|
||||||
|
|
||||||
|
/** Create a standard Lightbulb device
|
||||||
|
*
|
||||||
|
* This creates a Lightbulb device with the mandatory parameters and also assigns
|
||||||
|
* the primary parameter. The default parameter names will be used.
|
||||||
|
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||||
|
*
|
||||||
|
* @param[in] dev_name The unique device name
|
||||||
|
* @param[in] priv_data (Optional) Private data associated with the device. This should stay
|
||||||
|
* allocated throughout the lifetime of the device
|
||||||
|
* @param[in] power Default value of the mandatory parameter "power"
|
||||||
|
*
|
||||||
|
* @return Device handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_device_t *esp_rmaker_lightbulb_device_create(const char *dev_name,
|
||||||
|
void *priv_data, bool power);
|
||||||
|
|
||||||
|
/** Create a standard Fan device
|
||||||
|
*
|
||||||
|
* This creates a Fan device with the mandatory parameters and also assigns
|
||||||
|
* the primary parameter. The default parameter names will be used.
|
||||||
|
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||||
|
*
|
||||||
|
* @param[in] dev_name The unique device name
|
||||||
|
* @param[in] priv_data (Optional) Private data associated with the device. This should stay
|
||||||
|
* allocated throughout the lifetime of the device
|
||||||
|
* @param[in] power Default value of the mandatory parameter "power"
|
||||||
|
*
|
||||||
|
* @return Device handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_device_t *esp_rmaker_fan_device_create(const char *dev_name,
|
||||||
|
void *priv_data, bool power);
|
||||||
|
|
||||||
|
/** Create a standard Temperature Sensor device
|
||||||
|
*
|
||||||
|
* This creates a Temperature Sensor device with the mandatory parameters and also assigns
|
||||||
|
* the primary parameter. The default parameter names will be used.
|
||||||
|
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||||
|
*
|
||||||
|
* @param[in] dev_name The unique device name
|
||||||
|
* @param[in] priv_data (Optional) Private data associated with the device. This should stay
|
||||||
|
* allocated throughout the lifetime of the device
|
||||||
|
* @param[in] temperature Default value of the mandatory parameter "temperature"
|
||||||
|
*
|
||||||
|
* @return Device handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_device_t *esp_rmaker_temp_sensor_device_create(const char *dev_name,
|
||||||
|
void *priv_data, float temperature);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
352
components/esp_rainmaker/include/esp_rmaker_standard_params.h
Normal file
352
components/esp_rainmaker/include/esp_rmaker_standard_params.h
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Suggested default names for the parameters.
|
||||||
|
* These will also be used by default if you use any standard device helper APIs.
|
||||||
|
*
|
||||||
|
* @note These names are not mandatory. You can use the ESP RainMaker Core APIs
|
||||||
|
* to create your own parameters with custom names, if required.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ESP_RMAKER_DEF_NAME_PARAM "Name"
|
||||||
|
#define ESP_RMAKER_DEF_POWER_NAME "Power"
|
||||||
|
#define ESP_RMAKER_DEF_BRIGHTNESS_NAME "Brightness"
|
||||||
|
#define ESP_RMAKER_DEF_HUE_NAME "Hue"
|
||||||
|
#define ESP_RMAKER_DEF_SATURATION_NAME "Saturation"
|
||||||
|
#define ESP_RMAKER_DEF_INTENSITY_NAME "Intensity"
|
||||||
|
#define ESP_RMAKER_DEF_CCT_NAME "CCT"
|
||||||
|
#define ESP_RMAKER_DEF_DIRECTION_NAME "Direction"
|
||||||
|
#define ESP_RMAKER_DEF_SPEED_NAME "Speed"
|
||||||
|
#define ESP_RMAKER_DEF_TEMPERATURE_NAME "Temperature"
|
||||||
|
#define ESP_RMAKER_DEF_OTA_STATUS_NAME "Status"
|
||||||
|
#define ESP_RMAKER_DEF_OTA_INFO_NAME "Info"
|
||||||
|
#define ESP_RMAKER_DEF_OTA_URL_NAME "URL"
|
||||||
|
#define ESP_RMAKER_DEF_TIMEZONE_NAME "TZ"
|
||||||
|
#define ESP_RMAKER_DEF_TIMEZONE_POSIX_NAME "TZ-POSIX"
|
||||||
|
#define ESP_RMAKER_DEF_SCHEDULE_NAME "Schedules"
|
||||||
|
#define ESP_RMAKER_DEF_SCENES_NAME "Scenes"
|
||||||
|
#define ESP_RMAKER_DEF_REBOOT_NAME "Reboot"
|
||||||
|
#define ESP_RMAKER_DEF_FACTORY_RESET_NAME "Factory-Reset"
|
||||||
|
#define ESP_RMAKER_DEF_WIFI_RESET_NAME "Wi-Fi-Reset"
|
||||||
|
#define ESP_RMAKER_DEF_LOCAL_CONTROL_POP "POP"
|
||||||
|
#define ESP_RMAKER_DEF_LOCAL_CONTROL_TYPE "Type"
|
||||||
|
#define ESP_RMAKER_DEF_ADD_ZIGBEE_DEVICE "Add_zigbee_device"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard name param
|
||||||
|
*
|
||||||
|
* This will create the standard name parameter.
|
||||||
|
* This should be added to all devices for which you want a user customisable name.
|
||||||
|
* The value should be same as the device name.
|
||||||
|
*
|
||||||
|
* All standard device creation APIs will add this internally.
|
||||||
|
* No application registered callback will be called for this parameter,
|
||||||
|
* and changes will be managed internally.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val The device name
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_name_param_create(const char *param_name, const char *val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Power param
|
||||||
|
*
|
||||||
|
* This will create the standard power parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_power_param_create(const char *param_name, bool val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Brightness param
|
||||||
|
*
|
||||||
|
* This will create the standard brightness parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_brightness_param_create(const char *param_name, int val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Hue param
|
||||||
|
*
|
||||||
|
* This will create the standard hue parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_hue_param_create(const char *param_name, int val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Saturation param
|
||||||
|
*
|
||||||
|
* This will create the standard saturation parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_saturation_param_create(const char *param_name, int val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Intensity param
|
||||||
|
*
|
||||||
|
* This will create the standard intensity parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_intensity_param_create(const char *param_name, int val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard CCT param
|
||||||
|
*
|
||||||
|
* This will create the standard cct parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_cct_param_create(const char *param_name, int val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Direction param
|
||||||
|
*
|
||||||
|
* This will create the standard direction parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_direction_param_create(const char *param_name, int val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Speed param
|
||||||
|
*
|
||||||
|
* This will create the standard speed parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_speed_param_create(const char *param_name, int val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Temperature param
|
||||||
|
*
|
||||||
|
* This will create the standard temperature parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_temperature_param_create(const char *param_name, float val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard OTA Status param
|
||||||
|
*
|
||||||
|
* This will create the standard ota status parameter. Default value
|
||||||
|
* is set internally.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_ota_status_param_create(const char *param_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard OTA Info param
|
||||||
|
*
|
||||||
|
* This will create the standard ota info parameter. Default value
|
||||||
|
* is set internally.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_ota_info_param_create(const char *param_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard OTA URL param
|
||||||
|
*
|
||||||
|
* This will create the standard ota url parameter. Default value
|
||||||
|
* is set internally.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_ota_url_param_create(const char *param_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Timezone param
|
||||||
|
*
|
||||||
|
* This will create the standard timezone parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter (Eg. "Asia/Shanghai"). Can be kept NULL.
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_timezone_param_create(const char *param_name, const char *val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard POSIX Timezone param
|
||||||
|
*
|
||||||
|
* This will create the standard posix timezone parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter (Eg. "CST-8"). Can be kept NULL.
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_timezone_posix_param_create(const char *param_name, const char *val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Schedules param
|
||||||
|
*
|
||||||
|
* This will create the standard schedules parameter. Default value
|
||||||
|
* is set internally.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] max_schedules Maximum number of schedules allowed
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_schedules_param_create(const char *param_name, int max_schedules);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Scenes param
|
||||||
|
*
|
||||||
|
* This will create the standard scenes parameter. Default value
|
||||||
|
* is set internally.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] max_scenes Maximum number of scenes allowed
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_scenes_param_create(const char *param_name, int max_scenes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Reboot param
|
||||||
|
*
|
||||||
|
* This will create the standard reboot parameter.
|
||||||
|
* Set value to true (via write param) for the action to trigger.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_reboot_param_create(const char *param_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Factory Reset param
|
||||||
|
*
|
||||||
|
* This will create the standard factory reset parameter.
|
||||||
|
* Set value to true (via write param) for the action to trigger.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_factory_reset_param_create(const char *param_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Wi-Fi Reset param
|
||||||
|
*
|
||||||
|
* This will create the standard Wi-Fi Reset parameter.
|
||||||
|
* Set value to true (via write param) for the action to trigger.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_wifi_reset_param_create(const char *param_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Local Control POP param
|
||||||
|
*
|
||||||
|
* This will create the standard Local Control POP parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter (Eg. "abcd1234"). Can be kept NULL.
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_local_control_pop_param_create(const char *param_name, const char *val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create standard Local Control Type param
|
||||||
|
*
|
||||||
|
* This will create the standard Local Control security type parameter.
|
||||||
|
*
|
||||||
|
* @param[in] param_name Name of the parameter
|
||||||
|
* @param[in] val Default Value of the parameter
|
||||||
|
*
|
||||||
|
* @return Parameter handle on success.
|
||||||
|
* @return NULL in case of failures.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_t *esp_rmaker_local_control_type_param_create(const char *param_name, int val);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
124
components/esp_rainmaker/include/esp_rmaker_standard_services.h
Normal file
124
components/esp_rainmaker/include/esp_rmaker_standard_services.h
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Create a standard OTA service
|
||||||
|
*
|
||||||
|
* This creates an OTA service with the mandatory parameters. The default parameter names will be used.
|
||||||
|
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||||
|
*
|
||||||
|
* @param[in] serv_name The unique service name
|
||||||
|
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||||
|
* allocated throughout the lifetime of the service.
|
||||||
|
*
|
||||||
|
* @return service_handle on success.
|
||||||
|
* @return NULL in case of any error.
|
||||||
|
*/
|
||||||
|
esp_rmaker_device_t *esp_rmaker_ota_service_create(const char *serv_name, void *priv_data);
|
||||||
|
|
||||||
|
/** Create a standard Time service
|
||||||
|
*
|
||||||
|
* This creates a Time service with the mandatory parameters. The default parameter names will be used.
|
||||||
|
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||||
|
*
|
||||||
|
* @param[in] serv_name The unique service name
|
||||||
|
* @param[in] timezone Default value of timezone string (Eg. "Asia/Shanghai"). Can be kept NULL.
|
||||||
|
* @param[in] timezone_posix Default value of posix timezone string (Eg. "CST-8"). Can be kept NULL.
|
||||||
|
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||||
|
* allocated throughout the lifetime of the service.
|
||||||
|
*
|
||||||
|
* @return service_handle on success.
|
||||||
|
* @return NULL in case of any error.
|
||||||
|
*/
|
||||||
|
esp_rmaker_device_t *esp_rmaker_time_service_create(const char *serv_name, const char *timezone,
|
||||||
|
const char *timezone_posix, void *priv_data);
|
||||||
|
|
||||||
|
/** Create a standard Schedule service
|
||||||
|
*
|
||||||
|
* This creates a Schedule service with the mandatory parameters. The default parameter names will be used.
|
||||||
|
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||||
|
*
|
||||||
|
* @param[in] serv_name The unique service name
|
||||||
|
* @param[in] write_cb Write callback.
|
||||||
|
* @param[in] read_cb Read callback.
|
||||||
|
* @param[in] max_schedules Maximum number of schedules supported.
|
||||||
|
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||||
|
* allocated throughout the lifetime of the service.
|
||||||
|
*
|
||||||
|
* @return service_handle on success.
|
||||||
|
* @return NULL in case of any error.
|
||||||
|
*/
|
||||||
|
esp_rmaker_device_t *esp_rmaker_create_schedule_service(const char *serv_name, esp_rmaker_device_write_cb_t write_cb, esp_rmaker_device_read_cb_t read_cb, int max_schedules, void *priv_data);
|
||||||
|
|
||||||
|
/** Create a standard Scenes service
|
||||||
|
*
|
||||||
|
* This creates a Scenes service with the mandatory parameters. The default parameter names will be used.
|
||||||
|
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||||
|
*
|
||||||
|
* @param[in] serv_name The unique service name
|
||||||
|
* @param[in] write_cb Write callback.
|
||||||
|
* @param[in] read_cb Read callback.
|
||||||
|
* @param[in] max_scenes Maximum number of scenes supported.
|
||||||
|
* @param[in] deactivation_support Deactivation callback support.
|
||||||
|
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||||
|
* allocated throughout the lifetime of the service.
|
||||||
|
*
|
||||||
|
* @return service_handle on success.
|
||||||
|
* @return NULL in case of any error.
|
||||||
|
*/
|
||||||
|
esp_rmaker_device_t *esp_rmaker_create_scenes_service(const char *serv_name, esp_rmaker_device_write_cb_t write_cb, esp_rmaker_device_read_cb_t read_cb, int max_scenes, bool deactivation_support, void *priv_data);
|
||||||
|
|
||||||
|
/** Create a standard System service
|
||||||
|
*
|
||||||
|
* This creates an empty System service. Appropriate parameters should be added by the caller.
|
||||||
|
*
|
||||||
|
* @param[in] serv_name The unique service name
|
||||||
|
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||||
|
* allocated throughout the lifetime of the service.
|
||||||
|
*
|
||||||
|
* @return service_handle on success.
|
||||||
|
* @return NULL in case of any error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_create_system_service(const char *serv_name, void *priv_data);
|
||||||
|
|
||||||
|
/** Create a standard Local Control service
|
||||||
|
*
|
||||||
|
* This creates a Local Control service with the mandatory parameters. The default parameter names will be used.
|
||||||
|
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||||
|
*
|
||||||
|
* @param[in] serv_name The unique service name
|
||||||
|
* @param[in] pop Proof of possession
|
||||||
|
* @param[in] sec_type Security type
|
||||||
|
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||||
|
* allocated throughout the lifetime of the service.
|
||||||
|
*
|
||||||
|
* @return service_handle on success.
|
||||||
|
* @return NULL in case of any error.
|
||||||
|
*/
|
||||||
|
esp_rmaker_device_t *esp_rmaker_create_local_control_service(const char *serv_name, const char *pop, int sec_type, void *priv_data);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
102
components/esp_rainmaker/include/esp_rmaker_standard_types.h
Normal file
102
components/esp_rainmaker/include/esp_rmaker_standard_types.h
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/********** STANDARD UI TYPES **********/
|
||||||
|
|
||||||
|
#define ESP_RMAKER_UI_TOGGLE "esp.ui.toggle"
|
||||||
|
#define ESP_RMAKER_UI_SLIDER "esp.ui.slider"
|
||||||
|
#define ESP_RMAKER_UI_DROPDOWN "esp.ui.dropdown"
|
||||||
|
#define ESP_RMAKER_UI_TEXT "esp.ui.text"
|
||||||
|
#define ESP_RMAKER_UI_HUE_SLIDER "esp.ui.hue-slider"
|
||||||
|
#define ESP_RMAKER_UI_HUE_CIRCLE "esp.ui.hue-circle"
|
||||||
|
#define ESP_RMAKER_UI_PUSHBUTTON "esp.ui.push-btn-big"
|
||||||
|
#define ESP_RMAKER_UI_TRIGGER "esp.ui.trigger"
|
||||||
|
#define ESP_RMAKER_UI_HIDDEN "esp.ui.hidden"
|
||||||
|
#define ESP_RMAKER_UI_QR_SCAN "esp.ui.qr-scan"
|
||||||
|
|
||||||
|
/********** STANDARD PARAM TYPES **********/
|
||||||
|
|
||||||
|
#define ESP_RMAKER_PARAM_NAME "esp.param.name"
|
||||||
|
#define ESP_RMAKER_PARAM_POWER "esp.param.power"
|
||||||
|
#define ESP_RMAKER_PARAM_BRIGHTNESS "esp.param.brightness"
|
||||||
|
#define ESP_RMAKER_PARAM_HUE "esp.param.hue"
|
||||||
|
#define ESP_RMAKER_PARAM_SATURATION "esp.param.saturation"
|
||||||
|
#define ESP_RMAKER_PARAM_INTENSITY "esp.param.intensity"
|
||||||
|
#define ESP_RMAKER_PARAM_CCT "esp.param.cct"
|
||||||
|
#define ESP_RMAKER_PARAM_SPEED "esp.param.speed"
|
||||||
|
#define ESP_RMAKER_PARAM_DIRECTION "esp.param.direction"
|
||||||
|
#define ESP_RMAKER_PARAM_TEMPERATURE "esp.param.temperature"
|
||||||
|
#define ESP_RMAKER_PARAM_OTA_STATUS "esp.param.ota_status"
|
||||||
|
#define ESP_RMAKER_PARAM_OTA_INFO "esp.param.ota_info"
|
||||||
|
#define ESP_RMAKER_PARAM_OTA_URL "esp.param.ota_url"
|
||||||
|
#define ESP_RMAKER_PARAM_TIMEZONE "esp.param.tz"
|
||||||
|
#define ESP_RMAKER_PARAM_TIMEZONE_POSIX "esp.param.tz_posix"
|
||||||
|
#define ESP_RMAKER_PARAM_SCHEDULES "esp.param.schedules"
|
||||||
|
#define ESP_RMAKER_PARAM_SCENES "esp.param.scenes"
|
||||||
|
#define ESP_RMAKER_PARAM_REBOOT "esp.param.reboot"
|
||||||
|
#define ESP_RMAKER_PARAM_FACTORY_RESET "esp.param.factory-reset"
|
||||||
|
#define ESP_RMAKER_PARAM_WIFI_RESET "esp.param.wifi-reset"
|
||||||
|
#define ESP_RMAKER_PARAM_LOCAL_CONTROL_POP "esp.param.local_control_pop"
|
||||||
|
#define ESP_RMAKER_PARAM_LOCAL_CONTROL_TYPE "esp.param.local_control_type"
|
||||||
|
#define ESP_RMAKER_PARAM_TOGGLE "esp.param.toggle"
|
||||||
|
#define ESP_RMAKER_PARAM_RANGE "esp.param.range"
|
||||||
|
#define ESP_RMAKER_PARAM_MODE "esp.param.mode"
|
||||||
|
#define ESP_RMAKER_PARAM_BLINDS_POSITION "esp.param.blinds-position"
|
||||||
|
#define ESP_RMAKER_PARAM_GARAGE_POSITION "esp.param.garage-position"
|
||||||
|
#define ESP_RMAKER_PARAM_LIGHT_MODE "esp.param.light-mode"
|
||||||
|
#define ESP_RMAKER_PARAM_AC_MODE "esp.param.ac-mode"
|
||||||
|
#define ESP_RMAKER_PARAM_ADD_ZIGBEE_DEVICE "esp.param.add_zigbee_device"
|
||||||
|
|
||||||
|
|
||||||
|
/********** STANDARD DEVICE TYPES **********/
|
||||||
|
|
||||||
|
#define ESP_RMAKER_DEVICE_SWITCH "esp.device.switch"
|
||||||
|
#define ESP_RMAKER_DEVICE_LIGHTBULB "esp.device.lightbulb"
|
||||||
|
#define ESP_RMAKER_DEVICE_FAN "esp.device.fan"
|
||||||
|
#define ESP_RMAKER_DEVICE_TEMP_SENSOR "esp.device.temperature-sensor"
|
||||||
|
#define ESP_RMAKER_DEVICE_LIGHT "esp.device.light"
|
||||||
|
#define ESP_RMAKER_DEVICE_OUTLET "esp.device.outlet"
|
||||||
|
#define ESP_RMAKER_DEVICE_PLUG "esp.device.plug"
|
||||||
|
#define ESP_RMAKER_DEVICE_SOCKET "esp.device.socket"
|
||||||
|
#define ESP_RMAKER_DEVICE_LOCK "esp.device.lock"
|
||||||
|
#define ESP_RMAKER_DEVICE_BLINDS_INTERNAL "esp.device.blinds-internal"
|
||||||
|
#define ESP_RMAKER_DEVICE_BLINDS_EXTERNAL "esp.device.blinds-external"
|
||||||
|
#define ESP_RMAKER_DEVICE_GARAGE_DOOR "esp.device.garage-door"
|
||||||
|
#define ESP_RMAKER_DEVICE_GARAGE_LOCK "esp.device.garage-door-lock"
|
||||||
|
#define ESP_RMAKER_DEVICE_SPEAKER "esp.device.speaker"
|
||||||
|
#define ESP_RMAKER_DEVICE_AIR_CONDITIONER "esp.device.air-conditioner"
|
||||||
|
#define ESP_RMAKER_DEVICE_THERMOSTAT "esp.device.thermostat"
|
||||||
|
#define ESP_RMAKER_DEVICE_TV "esp.device.tv"
|
||||||
|
#define ESP_RMAKER_DEVICE_WASHER "esp.device.washer"
|
||||||
|
#define ESP_RMAKER_DEVICE_OTHER "esp.device.other"
|
||||||
|
#define ESP_RMAKER_DEVICE_ZIGBEE_GATEWAY "esp.device.zigbee_gateway"
|
||||||
|
#define ESP_RMAKER_DEVICE_THREAD_BR "esp.device.thread-br"
|
||||||
|
|
||||||
|
/********** STANDARD SERVICE TYPES **********/
|
||||||
|
#define ESP_RMAKER_SERVICE_OTA "esp.service.ota"
|
||||||
|
#define ESP_RMAKER_SERVICE_TIME "esp.service.time"
|
||||||
|
#define ESP_RMAKER_SERVICE_SCHEDULE "esp.service.schedule"
|
||||||
|
#define ESP_RMAKER_SERVICE_SCENES "esp.service.scenes"
|
||||||
|
#define ESP_RMAKER_SERVICE_SYSTEM "esp.service.system"
|
||||||
|
#define ESP_RMAKER_SERVICE_LOCAL_CONTROL "esp.service.local_control"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
44
components/esp_rainmaker/include/esp_rmaker_thread_br.h
Normal file
44
components/esp_rainmaker/include/esp_rmaker_thread_br.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_openthread.h>
|
||||||
|
#include <esp_rcp_update.h>
|
||||||
|
|
||||||
|
/** Enable Thread Border Router
|
||||||
|
*
|
||||||
|
* This API enables the Thread Border Router service for the node. For more information,
|
||||||
|
* check [here](https://openthread.io/guides/border-router/espressif-esp32)
|
||||||
|
*
|
||||||
|
* @note This API should be called after esp_rmaker_node_init() but before esp_rmaker_start().
|
||||||
|
*
|
||||||
|
* @param[in] platform_config Platform config for OpenThread
|
||||||
|
* @param[in] rcp_update_config RCP update config
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_thread_br_enable(const esp_openthread_platform_config_t *platform_config,
|
||||||
|
const esp_rcp_update_config_t *rcp_update_config);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
84
components/esp_rainmaker/include/esp_rmaker_user_mapping.h
Normal file
84
components/esp_rainmaker/include/esp_rmaker_user_mapping.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
#include <esp_err.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** User-Node Mapping states */
|
||||||
|
typedef enum {
|
||||||
|
/** Mapping does not exist or is not initialized */
|
||||||
|
ESP_RMAKER_USER_MAPPING_RESET = 0,
|
||||||
|
/** Mapping has started */
|
||||||
|
ESP_RMAKER_USER_MAPPING_STARTED,
|
||||||
|
/** Mapping request sent to cloud */
|
||||||
|
ESP_RMAKER_USER_MAPPING_REQ_SENT,
|
||||||
|
/** Mapping is done */
|
||||||
|
ESP_RMAKER_USER_MAPPING_DONE,
|
||||||
|
} esp_rmaker_user_mapping_state_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get User-Node mapping state
|
||||||
|
*
|
||||||
|
* This returns the current user-node mapping state.
|
||||||
|
*
|
||||||
|
* @return user mapping state
|
||||||
|
*/
|
||||||
|
esp_rmaker_user_mapping_state_t esp_rmaker_user_node_mapping_get_state(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create User Mapping Endpoint
|
||||||
|
*
|
||||||
|
* This will create a custom provisioning endpoint for user-node mapping.
|
||||||
|
* This should be called after network_prov_mgr_init()/wifi_prov_mgr_init() but before
|
||||||
|
* network_prov_mgr_start_provisioning()/wifi_prov_mgr_start_provisioning()
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return error on failure
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_user_mapping_endpoint_create(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register User Mapping Endpoint
|
||||||
|
*
|
||||||
|
* This will register the callback for the custom provisioning endpoint
|
||||||
|
* for user-node mapping which was created with esp_rmaker_user_mapping_endpoint_create().
|
||||||
|
* This should be called immediately after network_prov_mgr_start_provisioning()/wifi_prov_mgr_start_provisioning().
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return error on failure
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_user_mapping_endpoint_register(void);
|
||||||
|
|
||||||
|
/** Add User-Node mapping
|
||||||
|
*
|
||||||
|
* This call will start the user-node mapping workflow on the node.
|
||||||
|
* This is automatically called if you have used esp_rmaker_user_mapping_endpoint_register().
|
||||||
|
* Use this API only if you want to trigger the user-node mapping after the Wi-Fi provisioning
|
||||||
|
* has already been done.
|
||||||
|
*
|
||||||
|
* @param[in] user_id The User identifier received from the client (Phone app/CLI)
|
||||||
|
* @param[in] secret_key The Secret key received from the client (Phone app/CLI)
|
||||||
|
*
|
||||||
|
* @return ESP_OK if the workflow was successfully triggered. This does not guarantee success
|
||||||
|
* of the actual mapping. The mapping status needs to be checked separately by the clients.
|
||||||
|
* @return error on failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_start_user_node_mapping(char *user_id, char *secret_key);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
4
components/esp_rainmaker/sdkconfig.rename
Normal file
4
components/esp_rainmaker/sdkconfig.rename
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# sdkconfig replacement configurations for deprecated options formatted as
|
||||||
|
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
|
||||||
|
|
||||||
|
CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE CONFIG_ESP_RMAKER_LOCAL_CTRL_AUTO_ENABLE
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
|
||||||
|
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
|
||||||
|
b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
|
||||||
|
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
|
||||||
|
b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
|
||||||
|
ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
|
||||||
|
9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
|
||||||
|
IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
|
||||||
|
VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
|
||||||
|
93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
|
||||||
|
jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
|
||||||
|
AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
|
||||||
|
A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
|
||||||
|
U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
|
||||||
|
N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
|
||||||
|
o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
|
||||||
|
5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
|
||||||
|
rqXRfboQnoZsG4q5WTP468SQvvG5
|
||||||
|
-----END CERTIFICATE-----
|
||||||
20
components/esp_rainmaker/server_certs/rmaker_mqtt_server.crt
Normal file
20
components/esp_rainmaker/server_certs/rmaker_mqtt_server.crt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
|
||||||
|
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
|
||||||
|
b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
|
||||||
|
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
|
||||||
|
b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
|
||||||
|
ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
|
||||||
|
9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
|
||||||
|
IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
|
||||||
|
VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
|
||||||
|
93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
|
||||||
|
jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
|
||||||
|
AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
|
||||||
|
A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
|
||||||
|
U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
|
||||||
|
N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
|
||||||
|
o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
|
||||||
|
5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
|
||||||
|
rqXRfboQnoZsG4q5WTP468SQvvG5
|
||||||
|
-----END CERTIFICATE-----
|
||||||
54
components/esp_rainmaker/server_certs/rmaker_ota_server.crt
Normal file
54
components/esp_rainmaker/server_certs/rmaker_ota_server.crt
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
ESP RainMaker OTA Upgrade Server Certificate.
|
||||||
|
Replace this if you choose to use any other Server.
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEYzCCA0ugAwIBAgIQAYL4CY6i5ia5GjsnhB+5rzANBgkqhkiG9w0BAQsFADBa
|
||||||
|
MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl
|
||||||
|
clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE1
|
||||||
|
MTIwODEyMDUwN1oXDTI1MDUxMDEyMDAwMFowZDELMAkGA1UEBhMCVVMxFTATBgNV
|
||||||
|
BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEjMCEG
|
||||||
|
A1UEAxMaRGlnaUNlcnQgQmFsdGltb3JlIENBLTIgRzIwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQC75wD+AAFz75uI8FwIdfBccHMf/7V6H40II/3HwRM/
|
||||||
|
sSEGvU3M2y24hxkx3tprDcFd0lHVsF5y1PBm1ITykRhBtQkmsgOWBGmVU/oHTz6+
|
||||||
|
hjpDK7JZtavRuvRZQHJaZ7bN5lX8CSukmLK/zKkf1L+Hj4Il/UWAqeydjPl0kM8c
|
||||||
|
+GVQr834RavIL42ONh3e6onNslLZ5QnNNnEr2sbQm8b2pFtbObYfAB8ZpPvTvgzm
|
||||||
|
+4/dDoDmpOdaxMAvcu6R84Nnyc3KzkqwIIH95HKvCRjnT0LsTSdCTQeg3dUNdfc2
|
||||||
|
YMwmVJihiDfwg/etKVkgz7sl4dWe5vOuwQHrtQaJ4gqPAgMBAAGjggEZMIIBFTAd
|
||||||
|
BgNVHQ4EFgQUwBKyKHRoRmfpcCV0GgBFWwZ9XEQwHwYDVR0jBBgwFoAU5Z1ZMIJH
|
||||||
|
WMys+ghUNoZ7OrUETfAwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMC
|
||||||
|
AYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
|
||||||
|
Y2VydC5jb20wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybDMuZGlnaWNlcnQu
|
||||||
|
Y29tL09tbmlyb290MjAyNS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYB
|
||||||
|
BQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDQYJKoZIhvcNAQEL
|
||||||
|
BQADggEBAC/iN2bDGs+RVe4pFPpQEL6ZjeIo8XQWB2k7RDA99blJ9Wg2/rcwjang
|
||||||
|
B0lCY0ZStWnGm0nyGg9Xxva3vqt1jQ2iqzPkYoVDVKtjlAyjU6DqHeSmpqyVDmV4
|
||||||
|
7DOMvpQ+2HCr6sfheM4zlbv7LFjgikCmbUHY2Nmz+S8CxRtwa+I6hXsdGLDRS5rB
|
||||||
|
bxcQKegOw+FUllSlkZUIII1pLJ4vP1C0LuVXH6+kc9KhJLsNkP5FEx2noSnYZgvD
|
||||||
|
0WyzT7QrhExHkOyL4kGJE7YHRndC/bseF/r/JUuOUFfrjsxOFT+xJd1BDKCcYm1v
|
||||||
|
upcHi9nzBhDFKdT3uhaQqNBU4UtJx5g=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEdTCCA12gAwIBAgIJAKcOSkw0grd/MA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV
|
||||||
|
BAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIw
|
||||||
|
MAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
|
||||||
|
eTAeFw0wOTA5MDIwMDAwMDBaFw0zNDA2MjgxNzM5MTZaMIGYMQswCQYDVQQGEwJV
|
||||||
|
UzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UE
|
||||||
|
ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjE7MDkGA1UEAxMyU3RhcmZp
|
||||||
|
ZWxkIFNlcnZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi
|
||||||
|
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVDDrEKvlO4vW+GZdfjohTsR8/
|
||||||
|
y8+fIBNtKTrID30892t2OGPZNmCom15cAICyL1l/9of5JUOG52kbUpqQ4XHj2C0N
|
||||||
|
Tm/2yEnZtvMaVq4rtnQU68/7JuMauh2WLmo7WJSJR1b/JaCTcFOD2oR0FMNnngRo
|
||||||
|
Ot+OQFodSk7PQ5E751bWAHDLUu57fa4657wx+UX2wmDPE1kCK4DMNEffud6QZW0C
|
||||||
|
zyyRpqbn3oUYSXxmTqM6bam17jQuug0DuDPfR+uxa40l2ZvOgdFFRjKWcIfeAg5J
|
||||||
|
Q4W2bHO7ZOphQazJ1FTfhy/HIrImzJ9ZVGif/L4qL8RVHHVAYBeFAlU5i38FAgMB
|
||||||
|
AAGjgfAwge0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0O
|
||||||
|
BBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMB8GA1UdIwQYMBaAFL9ft9HO3R+G9FtV
|
||||||
|
rNzXEMIOqYjnME8GCCsGAQUFBwEBBEMwQTAcBggrBgEFBQcwAYYQaHR0cDovL28u
|
||||||
|
c3MyLnVzLzAhBggrBgEFBQcwAoYVaHR0cDovL3guc3MyLnVzL3guY2VyMCYGA1Ud
|
||||||
|
HwQfMB0wG6AZoBeGFWh0dHA6Ly9zLnNzMi51cy9yLmNybDARBgNVHSAECjAIMAYG
|
||||||
|
BFUdIAAwDQYJKoZIhvcNAQELBQADggEBACMd44pXyn3pF3lM8R5V/cxTbj5HD9/G
|
||||||
|
VfKyBDbtgB9TxF00KGu+x1X8Z+rLP3+QsjPNG1gQggL4+C/1E2DUBc7xgQjB3ad1
|
||||||
|
l08YuW3e95ORCLp+QCztweq7dp4zBncdDQh/U90bZKuCJ/Fp1U1ervShw3WnWEQt
|
||||||
|
8jxwmKy6abaVd38PMV4s/KCHOkdp8Hlf9BRUpJVeEXgSYCfOn8J3/yNTd126/+pZ
|
||||||
|
59vPr5KW7ySaNRB6nJHGDn2Z9j8Z3/VyVOEVqQdZe4O/Ui5GjLIAZHYcSNPYeehu
|
||||||
|
VsyuLAOQ1xk4meTKCRlb/weWsKh/NEnfVqn3sF/tM+2MR7cwA130A4w=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
206
components/esp_rainmaker/src/console/esp_rmaker_commands.c
Normal file
206
components/esp_rainmaker/src/console/esp_rmaker_commands.c
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_wifi.h>
|
||||||
|
#include <esp_console.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_user_mapping.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include <esp_rmaker_cmd_resp.h>
|
||||||
|
|
||||||
|
#include <esp_rmaker_internal.h>
|
||||||
|
#include <esp_rmaker_console_internal.h>
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
#include <network_provisioning/manager.h>
|
||||||
|
#else
|
||||||
|
#include <wifi_provisioning/manager.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_commands";
|
||||||
|
|
||||||
|
static int user_node_mapping_handler(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc == 3) {
|
||||||
|
printf("%s: Starting user-node mapping\n", TAG);
|
||||||
|
return esp_rmaker_start_user_node_mapping(argv[1], argv[2]);
|
||||||
|
} else {
|
||||||
|
printf("%s: Invalid Usage.\n", TAG);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_user_node_mapping()
|
||||||
|
{
|
||||||
|
const esp_console_cmd_t cmd = {
|
||||||
|
.command = "add-user",
|
||||||
|
.help = "Initiate the User-Node mapping from the node. Usage: add-user <user_id> <secret_key>",
|
||||||
|
.func = &user_node_mapping_handler,
|
||||||
|
};
|
||||||
|
ESP_LOGI(TAG, "Registering command: %s", cmd.command);
|
||||||
|
esp_console_cmd_register(&cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_node_id_handler(int argc, char** argv)
|
||||||
|
{
|
||||||
|
printf("%s: Node ID: %s\n", TAG, esp_rmaker_get_node_id());
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_get_node_id()
|
||||||
|
{
|
||||||
|
const esp_console_cmd_t cmd = {
|
||||||
|
.command = "get-node-id",
|
||||||
|
.help = "Get the Node ID for this board",
|
||||||
|
.func = &get_node_id_handler,
|
||||||
|
};
|
||||||
|
ESP_LOGI(TAG, "Registering command: %s", cmd.command);
|
||||||
|
esp_console_cmd_register(&cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wifi_prov_handler(int argc, char** argv)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||||
|
if (argc < 2) {
|
||||||
|
printf("%s: Invalid Usage.\n", TAG);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
wifi_config_t wifi_config;
|
||||||
|
memset(&wifi_config, 0, sizeof(wifi_config));
|
||||||
|
memcpy(wifi_config.sta.ssid, argv[1], strlen(argv[1]));
|
||||||
|
if (argc == 3) {
|
||||||
|
memcpy(wifi_config.sta.password, argv[2], strlen(argv[2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If device is still provisioning, use network_prov_mgr_configure_wifi_sta/wifi_prov_mgr_configure_sta */
|
||||||
|
bool provisioned = false;
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
network_prov_mgr_is_wifi_provisioned(&provisioned);
|
||||||
|
#else
|
||||||
|
wifi_prov_mgr_is_provisioned(&provisioned);
|
||||||
|
#endif
|
||||||
|
if (!provisioned) { // provisioning in progress
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
network_prov_mgr_configure_wifi_sta(&wifi_config);
|
||||||
|
#else
|
||||||
|
wifi_prov_mgr_configure_sta(&wifi_config);
|
||||||
|
#endif
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If already provisioned, just set the new credentials */
|
||||||
|
/* Stop the Wi-Fi */
|
||||||
|
if (esp_wifi_stop() != ESP_OK) {
|
||||||
|
printf("%s: Failed to stop wifi\n", TAG);
|
||||||
|
}
|
||||||
|
/* Configure Wi-Fi station with provided host credentials */
|
||||||
|
if (esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) != ESP_OK) {
|
||||||
|
printf("%s: Failed to set WiFi configuration\n", TAG);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
/* (Re)Start Wi-Fi */
|
||||||
|
if (esp_wifi_start() != ESP_OK) {
|
||||||
|
printf("%s: Failed to start WiFi\n", TAG);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
/* Connect to AP */
|
||||||
|
if (esp_wifi_connect() != ESP_OK) {
|
||||||
|
printf("%s: Failed to connect WiFi\n", TAG);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
#else
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_wifi_prov()
|
||||||
|
{
|
||||||
|
const esp_console_cmd_t cmd = {
|
||||||
|
.command = "wifi-prov",
|
||||||
|
.help = "Wi-Fi Provision the node. Usage: wifi-prov <ssid> [<passphrase>]",
|
||||||
|
.func = &wifi_prov_handler,
|
||||||
|
};
|
||||||
|
ESP_LOGI(TAG, "Registering command: %s", cmd.command);
|
||||||
|
esp_console_cmd_register(&cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_resp_cli_handler(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc != 5) {
|
||||||
|
printf("Usage: cmd <req_id> <user_role> <cmd> <data>\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
char *req_id = argv[1];
|
||||||
|
uint8_t user_role = atoi(argv[2]);
|
||||||
|
uint16_t cmd = atoi(argv[3]);
|
||||||
|
esp_rmaker_cmd_resp_test_send(req_id, user_role, cmd, (void *)argv[4], strlen(argv[4]), esp_rmaker_test_cmd_resp, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_cmd_resp_command()
|
||||||
|
{
|
||||||
|
const esp_console_cmd_t cmd_resp_cmd = {
|
||||||
|
.command = "cmd",
|
||||||
|
.help = "Send command to command-response module. Usage cmd <req_id> <cmd> <user_role> <data>",
|
||||||
|
.func = &cmd_resp_cli_handler,
|
||||||
|
};
|
||||||
|
ESP_LOGI(TAG, "Registering command: %s", cmd_resp_cmd.command);
|
||||||
|
esp_console_cmd_register(&cmd_resp_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sign_data_command(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc != 2) {
|
||||||
|
printf("Usage: sign-data <data>\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
char *data = (char *)argv[1];
|
||||||
|
size_t outlen = 0;
|
||||||
|
char *response = NULL;
|
||||||
|
esp_err_t err = esp_rmaker_node_auth_sign_msg((const void *)data, strlen(data), (void **)&response, &outlen);
|
||||||
|
if(err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to sign message");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "hex signature(len %d): %s", outlen, response);
|
||||||
|
free(response);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_sign_data_command()
|
||||||
|
{
|
||||||
|
const esp_console_cmd_t cmd = {
|
||||||
|
.command = "sign-data",
|
||||||
|
.help = "sends some data and expects a rsa signed response",
|
||||||
|
.func = &sign_data_command,
|
||||||
|
};
|
||||||
|
ESP_LOGI(TAG, "Registering command: %s", cmd.command);
|
||||||
|
esp_console_cmd_register(&cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_commands()
|
||||||
|
{
|
||||||
|
register_user_node_mapping();
|
||||||
|
register_get_node_id();
|
||||||
|
register_wifi_prov();
|
||||||
|
register_cmd_resp_command();
|
||||||
|
register_sign_data_command();
|
||||||
|
}
|
||||||
24
components/esp_rainmaker/src/console/esp_rmaker_console.c
Normal file
24
components/esp_rainmaker/src/console/esp_rmaker_console.c
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_rmaker_common_console.h>
|
||||||
|
#include <esp_rmaker_console_internal.h>
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_console_init()
|
||||||
|
{
|
||||||
|
esp_rmaker_common_console_init();
|
||||||
|
register_commands();
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void register_commands(void);
|
||||||
1041
components/esp_rainmaker/src/core/esp_rmaker_claim.c
Normal file
1041
components/esp_rainmaker/src/core/esp_rmaker_claim.c
Normal file
File diff suppressed because it is too large
Load Diff
49
components/esp_rainmaker/src/core/esp_rmaker_claim.h
Normal file
49
components/esp_rainmaker/src/core/esp_rmaker_claim.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <mbedtls/pk.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#define MAX_CSR_SIZE 1024
|
||||||
|
#define MAX_PAYLOAD_SIZE 3072
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RMAKER_CLAIM_STATE_PK_GENERATED = 1,
|
||||||
|
RMAKER_CLAIM_STATE_INIT,
|
||||||
|
RMAKER_CLAIM_STATE_INIT_DONE,
|
||||||
|
RMAKER_CLAIM_STATE_CSR_GENERATED,
|
||||||
|
RMAKER_CLAIM_STATE_VERIFY,
|
||||||
|
RMAKER_CLAIM_STATE_VERIFY_DONE,
|
||||||
|
} esp_rmaker_claim_state_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_rmaker_claim_state_t state;
|
||||||
|
unsigned char csr[MAX_CSR_SIZE];
|
||||||
|
char payload[MAX_PAYLOAD_SIZE];
|
||||||
|
size_t payload_offset;
|
||||||
|
size_t payload_len;
|
||||||
|
mbedtls_pk_context key;
|
||||||
|
} esp_rmaker_claim_data_t;
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_SELF_CLAIM
|
||||||
|
esp_rmaker_claim_data_t * esp_rmaker_self_claim_init(void);
|
||||||
|
esp_err_t esp_rmaker_self_claim_perform(esp_rmaker_claim_data_t *claim_data);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||||
|
esp_rmaker_claim_data_t * esp_rmaker_assisted_claim_init(void);
|
||||||
|
esp_err_t esp_rmaker_assisted_claim_perform(esp_rmaker_claim_data_t *claim_data);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void esp_rmaker_claim_data_free(esp_rmaker_claim_data_t *claim_data);
|
||||||
|
|
||||||
398
components/esp_rainmaker/src/core/esp_rmaker_claim.pb-c.c
Normal file
398
components/esp_rainmaker/src/core/esp_rmaker_claim.pb-c.c
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
|
||||||
|
/* Generated from: esp_rmaker_claim.proto */
|
||||||
|
|
||||||
|
/* Do not generate deprecated warnings for self */
|
||||||
|
#ifndef PROTOBUF_C__NO_DEPRECATED
|
||||||
|
#define PROTOBUF_C__NO_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "esp_rmaker_claim.pb-c.h"
|
||||||
|
void rmaker_claim__payload_buf__init
|
||||||
|
(RmakerClaim__PayloadBuf *message)
|
||||||
|
{
|
||||||
|
static const RmakerClaim__PayloadBuf init_value = RMAKER_CLAIM__PAYLOAD_BUF__INIT;
|
||||||
|
*message = init_value;
|
||||||
|
}
|
||||||
|
size_t rmaker_claim__payload_buf__get_packed_size
|
||||||
|
(const RmakerClaim__PayloadBuf *message)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__payload_buf__descriptor);
|
||||||
|
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||||
|
}
|
||||||
|
size_t rmaker_claim__payload_buf__pack
|
||||||
|
(const RmakerClaim__PayloadBuf *message,
|
||||||
|
uint8_t *out)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__payload_buf__descriptor);
|
||||||
|
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||||
|
}
|
||||||
|
size_t rmaker_claim__payload_buf__pack_to_buffer
|
||||||
|
(const RmakerClaim__PayloadBuf *message,
|
||||||
|
ProtobufCBuffer *buffer)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__payload_buf__descriptor);
|
||||||
|
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||||
|
}
|
||||||
|
RmakerClaim__PayloadBuf *
|
||||||
|
rmaker_claim__payload_buf__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
return (RmakerClaim__PayloadBuf *)
|
||||||
|
protobuf_c_message_unpack (&rmaker_claim__payload_buf__descriptor,
|
||||||
|
allocator, len, data);
|
||||||
|
}
|
||||||
|
void rmaker_claim__payload_buf__free_unpacked
|
||||||
|
(RmakerClaim__PayloadBuf *message,
|
||||||
|
ProtobufCAllocator *allocator)
|
||||||
|
{
|
||||||
|
if(!message)
|
||||||
|
return;
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__payload_buf__descriptor);
|
||||||
|
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||||
|
}
|
||||||
|
void rmaker_claim__resp_payload__init
|
||||||
|
(RmakerClaim__RespPayload *message)
|
||||||
|
{
|
||||||
|
static const RmakerClaim__RespPayload init_value = RMAKER_CLAIM__RESP_PAYLOAD__INIT;
|
||||||
|
*message = init_value;
|
||||||
|
}
|
||||||
|
size_t rmaker_claim__resp_payload__get_packed_size
|
||||||
|
(const RmakerClaim__RespPayload *message)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__resp_payload__descriptor);
|
||||||
|
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||||
|
}
|
||||||
|
size_t rmaker_claim__resp_payload__pack
|
||||||
|
(const RmakerClaim__RespPayload *message,
|
||||||
|
uint8_t *out)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__resp_payload__descriptor);
|
||||||
|
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||||
|
}
|
||||||
|
size_t rmaker_claim__resp_payload__pack_to_buffer
|
||||||
|
(const RmakerClaim__RespPayload *message,
|
||||||
|
ProtobufCBuffer *buffer)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__resp_payload__descriptor);
|
||||||
|
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||||
|
}
|
||||||
|
RmakerClaim__RespPayload *
|
||||||
|
rmaker_claim__resp_payload__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
return (RmakerClaim__RespPayload *)
|
||||||
|
protobuf_c_message_unpack (&rmaker_claim__resp_payload__descriptor,
|
||||||
|
allocator, len, data);
|
||||||
|
}
|
||||||
|
void rmaker_claim__resp_payload__free_unpacked
|
||||||
|
(RmakerClaim__RespPayload *message,
|
||||||
|
ProtobufCAllocator *allocator)
|
||||||
|
{
|
||||||
|
if(!message)
|
||||||
|
return;
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__resp_payload__descriptor);
|
||||||
|
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||||
|
}
|
||||||
|
void rmaker_claim__rmaker_claim_payload__init
|
||||||
|
(RmakerClaim__RMakerClaimPayload *message)
|
||||||
|
{
|
||||||
|
static const RmakerClaim__RMakerClaimPayload init_value = RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__INIT;
|
||||||
|
*message = init_value;
|
||||||
|
}
|
||||||
|
size_t rmaker_claim__rmaker_claim_payload__get_packed_size
|
||||||
|
(const RmakerClaim__RMakerClaimPayload *message)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__rmaker_claim_payload__descriptor);
|
||||||
|
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||||
|
}
|
||||||
|
size_t rmaker_claim__rmaker_claim_payload__pack
|
||||||
|
(const RmakerClaim__RMakerClaimPayload *message,
|
||||||
|
uint8_t *out)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__rmaker_claim_payload__descriptor);
|
||||||
|
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||||
|
}
|
||||||
|
size_t rmaker_claim__rmaker_claim_payload__pack_to_buffer
|
||||||
|
(const RmakerClaim__RMakerClaimPayload *message,
|
||||||
|
ProtobufCBuffer *buffer)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__rmaker_claim_payload__descriptor);
|
||||||
|
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||||
|
}
|
||||||
|
RmakerClaim__RMakerClaimPayload *
|
||||||
|
rmaker_claim__rmaker_claim_payload__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
return (RmakerClaim__RMakerClaimPayload *)
|
||||||
|
protobuf_c_message_unpack (&rmaker_claim__rmaker_claim_payload__descriptor,
|
||||||
|
allocator, len, data);
|
||||||
|
}
|
||||||
|
void rmaker_claim__rmaker_claim_payload__free_unpacked
|
||||||
|
(RmakerClaim__RMakerClaimPayload *message,
|
||||||
|
ProtobufCAllocator *allocator)
|
||||||
|
{
|
||||||
|
if(!message)
|
||||||
|
return;
|
||||||
|
assert(message->base.descriptor == &rmaker_claim__rmaker_claim_payload__descriptor);
|
||||||
|
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||||
|
}
|
||||||
|
static const ProtobufCFieldDescriptor rmaker_claim__payload_buf__field_descriptors[3] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"Offset",
|
||||||
|
1,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_UINT32,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(RmakerClaim__PayloadBuf, offset),
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Payload",
|
||||||
|
2,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_BYTES,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(RmakerClaim__PayloadBuf, payload),
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"TotalLen",
|
||||||
|
3,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_UINT32,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(RmakerClaim__PayloadBuf, totallen),
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static const unsigned rmaker_claim__payload_buf__field_indices_by_name[] = {
|
||||||
|
0, /* field[0] = Offset */
|
||||||
|
1, /* field[1] = Payload */
|
||||||
|
2, /* field[2] = TotalLen */
|
||||||
|
};
|
||||||
|
static const ProtobufCIntRange rmaker_claim__payload_buf__number_ranges[1 + 1] =
|
||||||
|
{
|
||||||
|
{ 1, 0 },
|
||||||
|
{ 0, 3 }
|
||||||
|
};
|
||||||
|
const ProtobufCMessageDescriptor rmaker_claim__payload_buf__descriptor =
|
||||||
|
{
|
||||||
|
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||||
|
"rmaker_claim.PayloadBuf",
|
||||||
|
"PayloadBuf",
|
||||||
|
"RmakerClaim__PayloadBuf",
|
||||||
|
"rmaker_claim",
|
||||||
|
sizeof(RmakerClaim__PayloadBuf),
|
||||||
|
3,
|
||||||
|
rmaker_claim__payload_buf__field_descriptors,
|
||||||
|
rmaker_claim__payload_buf__field_indices_by_name,
|
||||||
|
1, rmaker_claim__payload_buf__number_ranges,
|
||||||
|
(ProtobufCMessageInit) rmaker_claim__payload_buf__init,
|
||||||
|
NULL,NULL,NULL /* reserved[123] */
|
||||||
|
};
|
||||||
|
static const ProtobufCFieldDescriptor rmaker_claim__resp_payload__field_descriptors[2] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"Status",
|
||||||
|
1,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_ENUM,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(RmakerClaim__RespPayload, status),
|
||||||
|
&rmaker_claim__rmaker_claim_status__descriptor,
|
||||||
|
NULL,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Buf",
|
||||||
|
2,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_MESSAGE,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(RmakerClaim__RespPayload, buf),
|
||||||
|
&rmaker_claim__payload_buf__descriptor,
|
||||||
|
NULL,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static const unsigned rmaker_claim__resp_payload__field_indices_by_name[] = {
|
||||||
|
1, /* field[1] = Buf */
|
||||||
|
0, /* field[0] = Status */
|
||||||
|
};
|
||||||
|
static const ProtobufCIntRange rmaker_claim__resp_payload__number_ranges[1 + 1] =
|
||||||
|
{
|
||||||
|
{ 1, 0 },
|
||||||
|
{ 0, 2 }
|
||||||
|
};
|
||||||
|
const ProtobufCMessageDescriptor rmaker_claim__resp_payload__descriptor =
|
||||||
|
{
|
||||||
|
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||||
|
"rmaker_claim.RespPayload",
|
||||||
|
"RespPayload",
|
||||||
|
"RmakerClaim__RespPayload",
|
||||||
|
"rmaker_claim",
|
||||||
|
sizeof(RmakerClaim__RespPayload),
|
||||||
|
2,
|
||||||
|
rmaker_claim__resp_payload__field_descriptors,
|
||||||
|
rmaker_claim__resp_payload__field_indices_by_name,
|
||||||
|
1, rmaker_claim__resp_payload__number_ranges,
|
||||||
|
(ProtobufCMessageInit) rmaker_claim__resp_payload__init,
|
||||||
|
NULL,NULL,NULL /* reserved[123] */
|
||||||
|
};
|
||||||
|
static const ProtobufCFieldDescriptor rmaker_claim__rmaker_claim_payload__field_descriptors[3] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"msg",
|
||||||
|
1,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_ENUM,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(RmakerClaim__RMakerClaimPayload, msg),
|
||||||
|
&rmaker_claim__rmaker_claim_msg_type__descriptor,
|
||||||
|
NULL,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdPayload",
|
||||||
|
10,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_MESSAGE,
|
||||||
|
offsetof(RmakerClaim__RMakerClaimPayload, payload_case),
|
||||||
|
offsetof(RmakerClaim__RMakerClaimPayload, cmdpayload),
|
||||||
|
&rmaker_claim__payload_buf__descriptor,
|
||||||
|
NULL,
|
||||||
|
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"respPayload",
|
||||||
|
11,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_MESSAGE,
|
||||||
|
offsetof(RmakerClaim__RMakerClaimPayload, payload_case),
|
||||||
|
offsetof(RmakerClaim__RMakerClaimPayload, resppayload),
|
||||||
|
&rmaker_claim__resp_payload__descriptor,
|
||||||
|
NULL,
|
||||||
|
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static const unsigned rmaker_claim__rmaker_claim_payload__field_indices_by_name[] = {
|
||||||
|
1, /* field[1] = cmdPayload */
|
||||||
|
0, /* field[0] = msg */
|
||||||
|
2, /* field[2] = respPayload */
|
||||||
|
};
|
||||||
|
static const ProtobufCIntRange rmaker_claim__rmaker_claim_payload__number_ranges[2 + 1] =
|
||||||
|
{
|
||||||
|
{ 1, 0 },
|
||||||
|
{ 10, 1 },
|
||||||
|
{ 0, 3 }
|
||||||
|
};
|
||||||
|
const ProtobufCMessageDescriptor rmaker_claim__rmaker_claim_payload__descriptor =
|
||||||
|
{
|
||||||
|
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||||
|
"rmaker_claim.RMakerClaimPayload",
|
||||||
|
"RMakerClaimPayload",
|
||||||
|
"RmakerClaim__RMakerClaimPayload",
|
||||||
|
"rmaker_claim",
|
||||||
|
sizeof(RmakerClaim__RMakerClaimPayload),
|
||||||
|
3,
|
||||||
|
rmaker_claim__rmaker_claim_payload__field_descriptors,
|
||||||
|
rmaker_claim__rmaker_claim_payload__field_indices_by_name,
|
||||||
|
2, rmaker_claim__rmaker_claim_payload__number_ranges,
|
||||||
|
(ProtobufCMessageInit) rmaker_claim__rmaker_claim_payload__init,
|
||||||
|
NULL,NULL,NULL /* reserved[123] */
|
||||||
|
};
|
||||||
|
static const ProtobufCEnumValue rmaker_claim__rmaker_claim_status__enum_values_by_number[5] =
|
||||||
|
{
|
||||||
|
{ "Success", "RMAKER_CLAIM__RMAKER_CLAIM_STATUS__Success", 0 },
|
||||||
|
{ "Fail", "RMAKER_CLAIM__RMAKER_CLAIM_STATUS__Fail", 1 },
|
||||||
|
{ "InvalidParam", "RMAKER_CLAIM__RMAKER_CLAIM_STATUS__InvalidParam", 2 },
|
||||||
|
{ "InvalidState", "RMAKER_CLAIM__RMAKER_CLAIM_STATUS__InvalidState", 3 },
|
||||||
|
{ "NoMemory", "RMAKER_CLAIM__RMAKER_CLAIM_STATUS__NoMemory", 4 },
|
||||||
|
};
|
||||||
|
static const ProtobufCIntRange rmaker_claim__rmaker_claim_status__value_ranges[] = {
|
||||||
|
{0, 0},{0, 5}
|
||||||
|
};
|
||||||
|
static const ProtobufCEnumValueIndex rmaker_claim__rmaker_claim_status__enum_values_by_name[5] =
|
||||||
|
{
|
||||||
|
{ "Fail", 1 },
|
||||||
|
{ "InvalidParam", 2 },
|
||||||
|
{ "InvalidState", 3 },
|
||||||
|
{ "NoMemory", 4 },
|
||||||
|
{ "Success", 0 },
|
||||||
|
};
|
||||||
|
const ProtobufCEnumDescriptor rmaker_claim__rmaker_claim_status__descriptor =
|
||||||
|
{
|
||||||
|
PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
|
||||||
|
"rmaker_claim.RMakerClaimStatus",
|
||||||
|
"RMakerClaimStatus",
|
||||||
|
"RmakerClaim__RMakerClaimStatus",
|
||||||
|
"rmaker_claim",
|
||||||
|
5,
|
||||||
|
rmaker_claim__rmaker_claim_status__enum_values_by_number,
|
||||||
|
5,
|
||||||
|
rmaker_claim__rmaker_claim_status__enum_values_by_name,
|
||||||
|
1,
|
||||||
|
rmaker_claim__rmaker_claim_status__value_ranges,
|
||||||
|
NULL,NULL,NULL,NULL /* reserved[1234] */
|
||||||
|
};
|
||||||
|
static const ProtobufCEnumValue rmaker_claim__rmaker_claim_msg_type__enum_values_by_number[8] =
|
||||||
|
{
|
||||||
|
{ "TypeCmdClaimStart", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimStart", 0 },
|
||||||
|
{ "TypeRespClaimStart", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimStart", 1 },
|
||||||
|
{ "TypeCmdClaimInit", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimInit", 2 },
|
||||||
|
{ "TypeRespClaimInit", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimInit", 3 },
|
||||||
|
{ "TypeCmdClaimVerify", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimVerify", 4 },
|
||||||
|
{ "TypeRespClaimVerify", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimVerify", 5 },
|
||||||
|
{ "TypeCmdClaimAbort", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimAbort", 6 },
|
||||||
|
{ "TypeRespClaimAbort", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimAbort", 7 },
|
||||||
|
};
|
||||||
|
static const ProtobufCIntRange rmaker_claim__rmaker_claim_msg_type__value_ranges[] = {
|
||||||
|
{0, 0},{0, 8}
|
||||||
|
};
|
||||||
|
static const ProtobufCEnumValueIndex rmaker_claim__rmaker_claim_msg_type__enum_values_by_name[8] =
|
||||||
|
{
|
||||||
|
{ "TypeCmdClaimAbort", 6 },
|
||||||
|
{ "TypeCmdClaimInit", 2 },
|
||||||
|
{ "TypeCmdClaimStart", 0 },
|
||||||
|
{ "TypeCmdClaimVerify", 4 },
|
||||||
|
{ "TypeRespClaimAbort", 7 },
|
||||||
|
{ "TypeRespClaimInit", 3 },
|
||||||
|
{ "TypeRespClaimStart", 1 },
|
||||||
|
{ "TypeRespClaimVerify", 5 },
|
||||||
|
};
|
||||||
|
const ProtobufCEnumDescriptor rmaker_claim__rmaker_claim_msg_type__descriptor =
|
||||||
|
{
|
||||||
|
PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
|
||||||
|
"rmaker_claim.RMakerClaimMsgType",
|
||||||
|
"RMakerClaimMsgType",
|
||||||
|
"RmakerClaim__RMakerClaimMsgType",
|
||||||
|
"rmaker_claim",
|
||||||
|
8,
|
||||||
|
rmaker_claim__rmaker_claim_msg_type__enum_values_by_number,
|
||||||
|
8,
|
||||||
|
rmaker_claim__rmaker_claim_msg_type__enum_values_by_name,
|
||||||
|
1,
|
||||||
|
rmaker_claim__rmaker_claim_msg_type__value_ranges,
|
||||||
|
NULL,NULL,NULL,NULL /* reserved[1234] */
|
||||||
|
};
|
||||||
175
components/esp_rainmaker/src/core/esp_rmaker_claim.pb-c.h
Normal file
175
components/esp_rainmaker/src/core/esp_rmaker_claim.pb-c.h
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
|
||||||
|
/* Generated from: esp_rmaker_claim.proto */
|
||||||
|
|
||||||
|
#ifndef PROTOBUF_C_esp_5frmaker_5fclaim_2eproto__INCLUDED
|
||||||
|
#define PROTOBUF_C_esp_5frmaker_5fclaim_2eproto__INCLUDED
|
||||||
|
|
||||||
|
#include <protobuf-c/protobuf-c.h>
|
||||||
|
|
||||||
|
PROTOBUF_C__BEGIN_DECLS
|
||||||
|
|
||||||
|
#if PROTOBUF_C_VERSION_NUMBER < 1003000
|
||||||
|
# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
|
||||||
|
#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
|
||||||
|
# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _RmakerClaim__PayloadBuf RmakerClaim__PayloadBuf;
|
||||||
|
typedef struct _RmakerClaim__RespPayload RmakerClaim__RespPayload;
|
||||||
|
typedef struct _RmakerClaim__RMakerClaimPayload RmakerClaim__RMakerClaimPayload;
|
||||||
|
|
||||||
|
|
||||||
|
/* --- enums --- */
|
||||||
|
|
||||||
|
typedef enum _RmakerClaim__RMakerClaimStatus {
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_STATUS__Success = 0,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_STATUS__Fail = 1,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_STATUS__InvalidParam = 2,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_STATUS__InvalidState = 3,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_STATUS__NoMemory = 4
|
||||||
|
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RMAKER_CLAIM__RMAKER_CLAIM_STATUS)
|
||||||
|
} RmakerClaim__RMakerClaimStatus;
|
||||||
|
typedef enum _RmakerClaim__RMakerClaimMsgType {
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimStart = 0,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimStart = 1,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimInit = 2,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimInit = 3,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimVerify = 4,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimVerify = 5,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimAbort = 6,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimAbort = 7
|
||||||
|
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE)
|
||||||
|
} RmakerClaim__RMakerClaimMsgType;
|
||||||
|
|
||||||
|
/* --- messages --- */
|
||||||
|
|
||||||
|
struct _RmakerClaim__PayloadBuf
|
||||||
|
{
|
||||||
|
ProtobufCMessage base;
|
||||||
|
uint32_t offset;
|
||||||
|
ProtobufCBinaryData payload;
|
||||||
|
uint32_t totallen;
|
||||||
|
};
|
||||||
|
#define RMAKER_CLAIM__PAYLOAD_BUF__INIT \
|
||||||
|
{ PROTOBUF_C_MESSAGE_INIT (&rmaker_claim__payload_buf__descriptor) \
|
||||||
|
, 0, {0,NULL}, 0 }
|
||||||
|
|
||||||
|
|
||||||
|
struct _RmakerClaim__RespPayload
|
||||||
|
{
|
||||||
|
ProtobufCMessage base;
|
||||||
|
RmakerClaim__RMakerClaimStatus status;
|
||||||
|
RmakerClaim__PayloadBuf *buf;
|
||||||
|
};
|
||||||
|
#define RMAKER_CLAIM__RESP_PAYLOAD__INIT \
|
||||||
|
{ PROTOBUF_C_MESSAGE_INIT (&rmaker_claim__resp_payload__descriptor) \
|
||||||
|
, RMAKER_CLAIM__RMAKER_CLAIM_STATUS__Success, NULL }
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__PAYLOAD__NOT_SET = 0,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__PAYLOAD_CMD_PAYLOAD = 10,
|
||||||
|
RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__PAYLOAD_RESP_PAYLOAD = 11
|
||||||
|
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__PAYLOAD)
|
||||||
|
} RmakerClaim__RMakerClaimPayload__PayloadCase;
|
||||||
|
|
||||||
|
struct _RmakerClaim__RMakerClaimPayload
|
||||||
|
{
|
||||||
|
ProtobufCMessage base;
|
||||||
|
RmakerClaim__RMakerClaimMsgType msg;
|
||||||
|
RmakerClaim__RMakerClaimPayload__PayloadCase payload_case;
|
||||||
|
union {
|
||||||
|
RmakerClaim__PayloadBuf *cmdpayload;
|
||||||
|
RmakerClaim__RespPayload *resppayload;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
#define RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__INIT \
|
||||||
|
{ PROTOBUF_C_MESSAGE_INIT (&rmaker_claim__rmaker_claim_payload__descriptor) \
|
||||||
|
, RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimStart, RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__PAYLOAD__NOT_SET, {0} }
|
||||||
|
|
||||||
|
|
||||||
|
/* RmakerClaim__PayloadBuf methods */
|
||||||
|
void rmaker_claim__payload_buf__init
|
||||||
|
(RmakerClaim__PayloadBuf *message);
|
||||||
|
size_t rmaker_claim__payload_buf__get_packed_size
|
||||||
|
(const RmakerClaim__PayloadBuf *message);
|
||||||
|
size_t rmaker_claim__payload_buf__pack
|
||||||
|
(const RmakerClaim__PayloadBuf *message,
|
||||||
|
uint8_t *out);
|
||||||
|
size_t rmaker_claim__payload_buf__pack_to_buffer
|
||||||
|
(const RmakerClaim__PayloadBuf *message,
|
||||||
|
ProtobufCBuffer *buffer);
|
||||||
|
RmakerClaim__PayloadBuf *
|
||||||
|
rmaker_claim__payload_buf__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data);
|
||||||
|
void rmaker_claim__payload_buf__free_unpacked
|
||||||
|
(RmakerClaim__PayloadBuf *message,
|
||||||
|
ProtobufCAllocator *allocator);
|
||||||
|
/* RmakerClaim__RespPayload methods */
|
||||||
|
void rmaker_claim__resp_payload__init
|
||||||
|
(RmakerClaim__RespPayload *message);
|
||||||
|
size_t rmaker_claim__resp_payload__get_packed_size
|
||||||
|
(const RmakerClaim__RespPayload *message);
|
||||||
|
size_t rmaker_claim__resp_payload__pack
|
||||||
|
(const RmakerClaim__RespPayload *message,
|
||||||
|
uint8_t *out);
|
||||||
|
size_t rmaker_claim__resp_payload__pack_to_buffer
|
||||||
|
(const RmakerClaim__RespPayload *message,
|
||||||
|
ProtobufCBuffer *buffer);
|
||||||
|
RmakerClaim__RespPayload *
|
||||||
|
rmaker_claim__resp_payload__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data);
|
||||||
|
void rmaker_claim__resp_payload__free_unpacked
|
||||||
|
(RmakerClaim__RespPayload *message,
|
||||||
|
ProtobufCAllocator *allocator);
|
||||||
|
/* RmakerClaim__RMakerClaimPayload methods */
|
||||||
|
void rmaker_claim__rmaker_claim_payload__init
|
||||||
|
(RmakerClaim__RMakerClaimPayload *message);
|
||||||
|
size_t rmaker_claim__rmaker_claim_payload__get_packed_size
|
||||||
|
(const RmakerClaim__RMakerClaimPayload *message);
|
||||||
|
size_t rmaker_claim__rmaker_claim_payload__pack
|
||||||
|
(const RmakerClaim__RMakerClaimPayload *message,
|
||||||
|
uint8_t *out);
|
||||||
|
size_t rmaker_claim__rmaker_claim_payload__pack_to_buffer
|
||||||
|
(const RmakerClaim__RMakerClaimPayload *message,
|
||||||
|
ProtobufCBuffer *buffer);
|
||||||
|
RmakerClaim__RMakerClaimPayload *
|
||||||
|
rmaker_claim__rmaker_claim_payload__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data);
|
||||||
|
void rmaker_claim__rmaker_claim_payload__free_unpacked
|
||||||
|
(RmakerClaim__RMakerClaimPayload *message,
|
||||||
|
ProtobufCAllocator *allocator);
|
||||||
|
/* --- per-message closures --- */
|
||||||
|
|
||||||
|
typedef void (*RmakerClaim__PayloadBuf_Closure)
|
||||||
|
(const RmakerClaim__PayloadBuf *message,
|
||||||
|
void *closure_data);
|
||||||
|
typedef void (*RmakerClaim__RespPayload_Closure)
|
||||||
|
(const RmakerClaim__RespPayload *message,
|
||||||
|
void *closure_data);
|
||||||
|
typedef void (*RmakerClaim__RMakerClaimPayload_Closure)
|
||||||
|
(const RmakerClaim__RMakerClaimPayload *message,
|
||||||
|
void *closure_data);
|
||||||
|
|
||||||
|
/* --- services --- */
|
||||||
|
|
||||||
|
|
||||||
|
/* --- descriptors --- */
|
||||||
|
|
||||||
|
extern const ProtobufCEnumDescriptor rmaker_claim__rmaker_claim_status__descriptor;
|
||||||
|
extern const ProtobufCEnumDescriptor rmaker_claim__rmaker_claim_msg_type__descriptor;
|
||||||
|
extern const ProtobufCMessageDescriptor rmaker_claim__payload_buf__descriptor;
|
||||||
|
extern const ProtobufCMessageDescriptor rmaker_claim__resp_payload__descriptor;
|
||||||
|
extern const ProtobufCMessageDescriptor rmaker_claim__rmaker_claim_payload__descriptor;
|
||||||
|
|
||||||
|
PROTOBUF_C__END_DECLS
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* PROTOBUF_C_esp_5frmaker_5fclaim_2eproto__INCLUDED */
|
||||||
41
components/esp_rainmaker/src/core/esp_rmaker_claim.proto
Normal file
41
components/esp_rainmaker/src/core/esp_rmaker_claim.proto
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package rmaker_claim;
|
||||||
|
|
||||||
|
enum RMakerClaimStatus {
|
||||||
|
Success = 0;
|
||||||
|
Fail = 1;
|
||||||
|
InvalidParam = 2;
|
||||||
|
InvalidState = 3;
|
||||||
|
NoMemory = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PayloadBuf {
|
||||||
|
uint32 Offset = 1;
|
||||||
|
bytes Payload = 2;
|
||||||
|
uint32 TotalLen = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RespPayload {
|
||||||
|
RMakerClaimStatus Status = 1;
|
||||||
|
PayloadBuf Buf = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RMakerClaimMsgType {
|
||||||
|
TypeCmdClaimStart = 0;
|
||||||
|
TypeRespClaimStart = 1;
|
||||||
|
TypeCmdClaimInit = 2;
|
||||||
|
TypeRespClaimInit = 3;
|
||||||
|
TypeCmdClaimVerify = 4;
|
||||||
|
TypeRespClaimVerify = 5;
|
||||||
|
TypeCmdClaimAbort = 6;
|
||||||
|
TypeRespClaimAbort = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RMakerClaimPayload {
|
||||||
|
RMakerClaimMsgType msg = 1;
|
||||||
|
oneof payload {
|
||||||
|
PayloadBuf cmdPayload = 10;
|
||||||
|
RespPayload respPayload = 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
178
components/esp_rainmaker/src/core/esp_rmaker_client_data.c
Normal file
178
components/esp_rainmaker/src/core/esp_rmaker_client_data.c
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
#include <esp_rmaker_factory.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
|
||||||
|
#include "esp_rmaker_internal.h"
|
||||||
|
#include "esp_rmaker_client_data.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||||
|
#include "esp_secure_cert_read.h"
|
||||||
|
/*
|
||||||
|
* Since TAG is not used in any other place in this file at the moment,
|
||||||
|
* it has been placed inside this #ifdef to avoid -Werror=unused-variable.
|
||||||
|
*/
|
||||||
|
static const char *TAG = "esp_rmaker_client_data";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern uint8_t mqtt_server_root_ca_pem_start[] asm("_binary_rmaker_mqtt_server_crt_start");
|
||||||
|
extern uint8_t mqtt_server_root_ca_pem_end[] asm("_binary_rmaker_mqtt_server_crt_end");
|
||||||
|
|
||||||
|
char * esp_rmaker_get_mqtt_host()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_READ_MQTT_HOST_FROM_CONFIG
|
||||||
|
return strdup(CONFIG_ESP_RMAKER_MQTT_HOST);
|
||||||
|
#else
|
||||||
|
char *host = esp_rmaker_factory_get(ESP_RMAKER_MQTT_HOST_NVS_KEY);
|
||||||
|
#if defined(CONFIG_ESP_RMAKER_SELF_CLAIM) || defined(CONFIG_ESP_RMAKER_ASSISTED_CLAIM)
|
||||||
|
if (!host) {
|
||||||
|
return strdup(CONFIG_ESP_RMAKER_MQTT_HOST);
|
||||||
|
}
|
||||||
|
#endif /* defined(CONFIG_ESP_RMAKER_SELF_CLAIM) || defined(CONFIG_ESP_RMAKER_ASSISTED_CLAIM) */
|
||||||
|
return host;
|
||||||
|
#endif /* !CONFIG_ESP_RMAKER_READ_MQTT_HOST_FROM_CONFIG */
|
||||||
|
}
|
||||||
|
|
||||||
|
char * esp_rmaker_get_client_cert()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||||
|
uint32_t client_cert_len = 0;
|
||||||
|
char *client_cert_addr = NULL;
|
||||||
|
if (esp_secure_cert_get_device_cert(&client_cert_addr, &client_cert_len) == ESP_OK) {
|
||||||
|
return client_cert_addr;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to obtain flash address of device cert");
|
||||||
|
ESP_LOGI(TAG, "Attempting to fetch client certificate from NVS");
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||||
|
return esp_rmaker_factory_get(ESP_RMAKER_CLIENT_CERT_NVS_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t esp_rmaker_get_client_cert_len()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||||
|
uint32_t client_cert_len = 0;
|
||||||
|
char *client_cert_addr = NULL;
|
||||||
|
if (esp_secure_cert_get_device_cert(&client_cert_addr, &client_cert_len) == ESP_OK) {
|
||||||
|
return client_cert_len;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to obtain flash address of device cert");
|
||||||
|
ESP_LOGI(TAG, "Attempting to fetch client certificate from NVS");
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||||
|
return esp_rmaker_factory_get_size(ESP_RMAKER_CLIENT_CERT_NVS_KEY) + 1; /* +1 for NULL terminating byte */
|
||||||
|
}
|
||||||
|
|
||||||
|
char * esp_rmaker_get_client_key()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||||
|
uint32_t client_key_len = 0;
|
||||||
|
char *client_key_addr = NULL;
|
||||||
|
if (esp_secure_cert_get_priv_key(&client_key_addr, &client_key_len) == ESP_OK) {
|
||||||
|
return client_key_addr;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to obtain flash address of private_key");
|
||||||
|
ESP_LOGI(TAG, "Attempting to fetch key from NVS");
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||||
|
return esp_rmaker_factory_get(ESP_RMAKER_CLIENT_KEY_NVS_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t esp_rmaker_get_client_key_len()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||||
|
uint32_t client_key_len = 0;
|
||||||
|
char *client_key_addr = NULL;
|
||||||
|
if (esp_secure_cert_get_priv_key(&client_key_addr, &client_key_len) == ESP_OK) {
|
||||||
|
return client_key_len;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to obtain flash address of private_key");
|
||||||
|
ESP_LOGI(TAG, "Attempting to fetch key from NVS");
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||||
|
return esp_rmaker_factory_get_size(ESP_RMAKER_CLIENT_KEY_NVS_KEY) + 1; /* +1 for NULL terminating byte */
|
||||||
|
}
|
||||||
|
|
||||||
|
char * esp_rmaker_get_client_csr()
|
||||||
|
{
|
||||||
|
return esp_rmaker_factory_get(ESP_RMAKER_CLIENT_CSR_NVS_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_mqtt_conn_params_t *esp_rmaker_get_mqtt_conn_params()
|
||||||
|
{
|
||||||
|
esp_rmaker_mqtt_conn_params_t *mqtt_conn_params = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_mqtt_conn_params_t));
|
||||||
|
|
||||||
|
#if defined(CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR) && defined(CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL)
|
||||||
|
mqtt_conn_params->ds_data = esp_secure_cert_get_ds_ctx();
|
||||||
|
if (mqtt_conn_params->ds_data == NULL) /* Get client key only if ds_data is NULL */
|
||||||
|
#endif /* (defined(CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR) && defined(CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL)) */
|
||||||
|
{
|
||||||
|
if ((mqtt_conn_params->client_key = esp_rmaker_get_client_key()) == NULL) {
|
||||||
|
goto init_err;
|
||||||
|
}
|
||||||
|
mqtt_conn_params->client_key_len = esp_rmaker_get_client_key_len();
|
||||||
|
}
|
||||||
|
if ((mqtt_conn_params->client_cert = esp_rmaker_get_client_cert()) == NULL) {
|
||||||
|
goto init_err;
|
||||||
|
}
|
||||||
|
mqtt_conn_params->client_cert_len = esp_rmaker_get_client_cert_len();
|
||||||
|
if ((mqtt_conn_params->mqtt_host = esp_rmaker_get_mqtt_host()) == NULL) {
|
||||||
|
goto init_err;
|
||||||
|
}
|
||||||
|
mqtt_conn_params->server_cert = (char *)mqtt_server_root_ca_pem_start;
|
||||||
|
mqtt_conn_params->client_id = esp_rmaker_get_node_id();
|
||||||
|
return mqtt_conn_params;
|
||||||
|
init_err:
|
||||||
|
esp_rmaker_clean_mqtt_conn_params(mqtt_conn_params);
|
||||||
|
free(mqtt_conn_params);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_rmaker_clean_mqtt_conn_params(esp_rmaker_mqtt_conn_params_t *mqtt_conn_params)
|
||||||
|
{
|
||||||
|
if (mqtt_conn_params) {
|
||||||
|
if (mqtt_conn_params->mqtt_host) {
|
||||||
|
free(mqtt_conn_params->mqtt_host);
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||||
|
if (mqtt_conn_params->client_cert) {
|
||||||
|
esp_secure_cert_free_device_cert(mqtt_conn_params->client_cert);
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
|
||||||
|
if (mqtt_conn_params->ds_data) {
|
||||||
|
esp_secure_cert_free_ds_ctx(mqtt_conn_params->ds_data);
|
||||||
|
}
|
||||||
|
#else /* !CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */
|
||||||
|
if (mqtt_conn_params->client_key) {
|
||||||
|
esp_secure_cert_free_priv_key(mqtt_conn_params->client_key);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */
|
||||||
|
#else /* !CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||||
|
if (mqtt_conn_params->client_cert) {
|
||||||
|
free(mqtt_conn_params->client_cert);
|
||||||
|
}
|
||||||
|
if (mqtt_conn_params->client_key) {
|
||||||
|
free(mqtt_conn_params->client_key);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||||
|
}
|
||||||
|
}
|
||||||
31
components/esp_rainmaker/src/core/esp_rmaker_client_data.h
Normal file
31
components/esp_rainmaker/src/core/esp_rmaker_client_data.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <esp_rmaker_mqtt_glue.h>
|
||||||
|
|
||||||
|
#define ESP_RMAKER_CLIENT_CERT_NVS_KEY "client_cert"
|
||||||
|
#define ESP_RMAKER_CLIENT_KEY_NVS_KEY "client_key"
|
||||||
|
#define ESP_RMAKER_MQTT_HOST_NVS_KEY "mqtt_host"
|
||||||
|
#define ESP_RMAKER_CLIENT_CSR_NVS_KEY "csr"
|
||||||
|
#define ESP_RMAKER_CLIENT_RANDOM_NVS_KEY "random"
|
||||||
|
|
||||||
|
char *esp_rmaker_get_client_cert();
|
||||||
|
size_t esp_rmaker_get_client_cert_len();
|
||||||
|
char *esp_rmaker_get_client_key();
|
||||||
|
size_t esp_rmaker_get_client_key_len();
|
||||||
|
char *esp_rmaker_get_client_csr();
|
||||||
|
char *esp_rmaker_get_mqtt_host();
|
||||||
|
esp_rmaker_mqtt_conn_params_t *esp_rmaker_get_mqtt_conn_params();
|
||||||
|
void esp_rmaker_clean_mqtt_conn_params(esp_rmaker_mqtt_conn_params_t *mqtt_conn_params);
|
||||||
150
components/esp_rainmaker/src/core/esp_rmaker_cmd_resp_manager.c
Normal file
150
components/esp_rainmaker/src/core/esp_rmaker_cmd_resp_manager.c
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
#include <esp_rmaker_common_events.h>
|
||||||
|
#include <esp_rmaker_mqtt.h>
|
||||||
|
#include <esp_rmaker_cmd_resp.h>
|
||||||
|
#include "esp_rmaker_internal.h"
|
||||||
|
#include "esp_rmaker_mqtt_topics.h"
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_cmd_resp";
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE
|
||||||
|
|
||||||
|
/* These are for testing purpose only */
|
||||||
|
static void esp_rmaker_resp_callback(const char *topic, void *payload, size_t payload_len, void *priv_data)
|
||||||
|
{
|
||||||
|
esp_rmaker_cmd_resp_parse_response(payload, payload_len, priv_data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_test_cmd_resp(const void *cmd, size_t cmd_len, void *priv_data)
|
||||||
|
{
|
||||||
|
if (!cmd) {
|
||||||
|
ESP_LOGE(TAG, "No command data to send.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||||
|
snprintf(publish_topic, sizeof(publish_topic), "node/%s/%s", esp_rmaker_get_node_id(), TO_NODE_TOPIC_SUFFIX);
|
||||||
|
return esp_rmaker_mqtt_publish(publish_topic, (void *)cmd, cmd_len, RMAKER_MQTT_QOS1, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_cmd_resp_test_enable(void)
|
||||||
|
{
|
||||||
|
char subscribe_topic[100];
|
||||||
|
snprintf(subscribe_topic, sizeof(subscribe_topic), "node/%s/%s",
|
||||||
|
esp_rmaker_get_node_id(), CMD_RESP_TOPIC_SUFFIX);
|
||||||
|
esp_err_t err = esp_rmaker_mqtt_subscribe(subscribe_topic, esp_rmaker_resp_callback, RMAKER_MQTT_QOS1, NULL);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to subscribe to %s. Error %d", subscribe_topic, err);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Command-Response test support enabled.");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
esp_err_t esp_rmaker_test_cmd_resp(const void *cmd, size_t cmd_len, void *priv_data)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Please enable CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE to use this.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
#endif /* !CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE */
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_publish_response(void *output, size_t output_len)
|
||||||
|
{
|
||||||
|
if (output) {
|
||||||
|
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||||
|
esp_rmaker_create_mqtt_topic(publish_topic, sizeof(publish_topic), CMD_RESP_TOPIC_SUFFIX, CMD_RESP_TOPIC_RULE);
|
||||||
|
esp_err_t err = esp_rmaker_mqtt_publish(publish_topic, output, output_len, RMAKER_MQTT_QOS1, NULL);
|
||||||
|
free(output);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to publish reponse.");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "No output generated by command-response handler.");
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_cmd_callback(const char *topic, void *payload, size_t payload_len, void *priv_data)
|
||||||
|
{
|
||||||
|
void *output = NULL;
|
||||||
|
size_t output_len = 0;
|
||||||
|
/* Any command data received is directly sent to the command response framework and on success,
|
||||||
|
* the response (if any) is sent back to the MQTT Broker.
|
||||||
|
*/
|
||||||
|
if (esp_rmaker_cmd_response_handler(payload, payload_len, &output, &output_len) == ESP_OK) {
|
||||||
|
esp_rmaker_publish_response(output, output_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keeping the variable outside the function as there would subsequently also be a function
|
||||||
|
* to disable command response.
|
||||||
|
*/
|
||||||
|
static bool cmd_resp_topic_subscribed = false;
|
||||||
|
static esp_err_t esp_rmaker_cmd_resp_check_pending(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Checking for pending commands.");
|
||||||
|
char subscribe_topic[100];
|
||||||
|
snprintf(subscribe_topic, sizeof(subscribe_topic), "node/%s/%s",
|
||||||
|
esp_rmaker_get_node_id(), TO_NODE_TOPIC_SUFFIX);
|
||||||
|
if (!cmd_resp_topic_subscribed) {
|
||||||
|
/* Subscribing just once because any subsequent reconnect will automatically subscribe to the topic */
|
||||||
|
esp_err_t err = esp_rmaker_mqtt_subscribe(subscribe_topic, esp_rmaker_cmd_callback, RMAKER_MQTT_QOS1, NULL);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to subscribe to %s. Error %d", subscribe_topic, err);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
cmd_resp_topic_subscribed = true;
|
||||||
|
}
|
||||||
|
void *output = NULL;
|
||||||
|
size_t output_len = 0;
|
||||||
|
if (esp_rmaker_cmd_prepare_empty_response(&output, &output_len) == ESP_OK) {
|
||||||
|
return esp_rmaker_publish_response(output, output_len);
|
||||||
|
}
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void* event_data)
|
||||||
|
{
|
||||||
|
esp_rmaker_cmd_resp_check_pending();
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_cmd_response_enable(void)
|
||||||
|
{
|
||||||
|
static bool enabled = false;
|
||||||
|
if (enabled == true) {
|
||||||
|
ESP_LOGI(TAG, "Command-response Module already enabled.");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Enabling Command-Response Module.");
|
||||||
|
if (esp_rmaker_is_mqtt_connected()) {
|
||||||
|
esp_rmaker_cmd_resp_check_pending();
|
||||||
|
}
|
||||||
|
if (esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &event_handler, NULL) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "RMAKER_MQTT_EVENT_CONNECTED event subscription failed.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE
|
||||||
|
esp_rmaker_cmd_resp_test_enable();
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE */
|
||||||
|
enabled = true;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
714
components/esp_rainmaker/src/core/esp_rmaker_core.c
Normal file
714
components/esp_rainmaker/src/core/esp_rmaker_core.c
Normal file
@@ -0,0 +1,714 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <freertos/queue.h>
|
||||||
|
#include <freertos/event_groups.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_wifi.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
#include <esp_bit_defs.h>
|
||||||
|
|
||||||
|
#include <esp_rmaker_factory.h>
|
||||||
|
#include <esp_rmaker_work_queue.h>
|
||||||
|
#include <esp_rmaker_common_events.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_user_mapping.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include "esp_rmaker_internal.h"
|
||||||
|
#include "esp_rmaker_mqtt.h"
|
||||||
|
#include "esp_rmaker_claim.h"
|
||||||
|
#include "esp_rmaker_client_data.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_OPENTHREAD_ENABLED
|
||||||
|
#include <esp_openthread.h>
|
||||||
|
#include <esp_openthread_lock.h>
|
||||||
|
#include <esp_openthread_dns64.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||||
|
static const int WIFI_CONNECTED_EVENT = BIT0;
|
||||||
|
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD)
|
||||||
|
static const int THREAD_SET_DNS_SEVER_EVENT = BIT0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||||
|
#include "esp_mac.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const int MQTT_CONNECTED_EVENT = BIT1;
|
||||||
|
static EventGroupHandle_t rmaker_core_event_group;
|
||||||
|
|
||||||
|
ESP_EVENT_DEFINE_BASE(RMAKER_EVENT);
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_core";
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(CONFIG_ESP_RMAKER_SELF_CLAIM) || defined(CONFIG_ESP_RMAKER_ASSISTED_CLAIM)
|
||||||
|
#define ESP_RMAKER_CLAIM_ENABLED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ESP_RMAKER_CHECK_HANDLE(rval) \
|
||||||
|
{ \
|
||||||
|
if (!esp_rmaker_priv_data) {\
|
||||||
|
ESP_LOGE(TAG, "ESP RainMaker not initialised"); \
|
||||||
|
return rval; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ESP_CLAIM_NODE_ID_SIZE 12
|
||||||
|
|
||||||
|
/* Handle to maintain internal information (will move to an internal file) */
|
||||||
|
typedef struct {
|
||||||
|
char *node_id;
|
||||||
|
const esp_rmaker_node_t *node;
|
||||||
|
bool enable_time_sync;
|
||||||
|
esp_rmaker_state_t state;
|
||||||
|
bool mqtt_connected;
|
||||||
|
esp_rmaker_mqtt_conn_params_t *mqtt_conn_params;
|
||||||
|
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||||
|
bool need_claim;
|
||||||
|
esp_rmaker_claim_data_t *claim_data;
|
||||||
|
#endif /* ESP_RMAKER_CLAIM_ENABLED */
|
||||||
|
} esp_rmaker_priv_data_t;
|
||||||
|
|
||||||
|
static esp_rmaker_priv_data_t *esp_rmaker_priv_data;
|
||||||
|
|
||||||
|
bool esp_rmaker_is_mqtt_connected()
|
||||||
|
{
|
||||||
|
if (esp_rmaker_priv_data) {
|
||||||
|
return esp_rmaker_priv_data->mqtt_connected;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_state_t esp_rmaker_get_state(void)
|
||||||
|
{
|
||||||
|
if (esp_rmaker_priv_data) {
|
||||||
|
return esp_rmaker_priv_data->state;
|
||||||
|
}
|
||||||
|
return ESP_RMAKER_STATE_DEINIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_event_handler(void* arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void* event_data)
|
||||||
|
{
|
||||||
|
switch (event_id) {
|
||||||
|
case RMAKER_EVENT_WIFI_RESET:
|
||||||
|
esp_rmaker_mqtt_disconnect();
|
||||||
|
break;
|
||||||
|
case RMAKER_EVENT_FACTORY_RESET:
|
||||||
|
esp_rmaker_reset_user_node_mapping();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_mqtt_event_handler(void* arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void* event_data)
|
||||||
|
{
|
||||||
|
if (!esp_rmaker_priv_data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event_base == RMAKER_COMMON_EVENT && event_id == RMAKER_MQTT_EVENT_CONNECTED) {
|
||||||
|
esp_rmaker_priv_data->mqtt_connected = true;
|
||||||
|
} else if (event_base == RMAKER_COMMON_EVENT && event_id == RMAKER_MQTT_EVENT_DISCONNECTED) {
|
||||||
|
esp_rmaker_priv_data->mqtt_connected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN
|
||||||
|
|
||||||
|
#include "mbedtls/x509_crt.h"
|
||||||
|
#include "mbedtls/oid.h"
|
||||||
|
|
||||||
|
static char* esp_rmaker_populate_node_id_from_cert()
|
||||||
|
{
|
||||||
|
void *addr = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
addr = esp_rmaker_get_client_cert();
|
||||||
|
if (addr) {
|
||||||
|
len = esp_rmaker_get_client_cert_len();
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to get device certificate.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
mbedtls_x509_crt crt;
|
||||||
|
mbedtls_x509_crt_init(&crt);
|
||||||
|
char *node_id = NULL;
|
||||||
|
int ret = mbedtls_x509_crt_parse(&crt, addr, len);
|
||||||
|
if (ret != 0) {
|
||||||
|
ESP_LOGE(TAG, "Parsing of device certificate failed, returned %02X", ret);
|
||||||
|
} else {
|
||||||
|
mbedtls_asn1_named_data *cn_data;
|
||||||
|
cn_data = mbedtls_asn1_find_named_data(&crt.subject, MBEDTLS_OID_AT_CN,
|
||||||
|
MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN));
|
||||||
|
if (cn_data) {
|
||||||
|
node_id = MEM_CALLOC_EXTRAM(1, cn_data->val.len + 1);
|
||||||
|
memcpy(node_id, (const char *)cn_data->val.p, cn_data->val.len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mbedtls_x509_crt_free(&crt);
|
||||||
|
return node_id;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static char *esp_rmaker_populate_node_id(bool use_claiming)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN
|
||||||
|
char *node_id = esp_rmaker_populate_node_id_from_cert();
|
||||||
|
#else /* !CONFIG_ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN */
|
||||||
|
char *node_id = esp_rmaker_factory_get("node_id");
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN */
|
||||||
|
|
||||||
|
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||||
|
if (!node_id && use_claiming) {
|
||||||
|
uint8_t mac_addr[6];
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||||
|
/* ESP_MAC_BASE was introduced in ESP-IDF v5.1. It is the same as the Wi-Fi Station MAC address
|
||||||
|
* for chips supporting Wi-Fi. We can use base MAC address to generate claim init request for both
|
||||||
|
* Wi-Fi and Thread devices
|
||||||
|
*/
|
||||||
|
esp_err_t err = esp_read_mac(mac_addr, ESP_MAC_BASE);
|
||||||
|
#else
|
||||||
|
/* Thread was officially supported in ESP-IDF v5.1. Use Wi-Fi Station MAC address to generate claim
|
||||||
|
* init request.
|
||||||
|
*/
|
||||||
|
esp_err_t err = esp_wifi_get_mac(WIFI_IF_STA, mac_addr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Could not fetch MAC address.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
node_id = MEM_CALLOC_EXTRAM(1, ESP_CLAIM_NODE_ID_SIZE + 1); /* +1 for NULL terminatation */
|
||||||
|
snprintf(node_id, ESP_CLAIM_NODE_ID_SIZE + 1, "%02X%02X%02X%02X%02X%02X",
|
||||||
|
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||||
|
}
|
||||||
|
#endif /* ESP_RMAKER_CLAIM_ENABLED */
|
||||||
|
return node_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_change_node_id(char *node_id, size_t len)
|
||||||
|
{
|
||||||
|
if(esp_rmaker_priv_data) {
|
||||||
|
char *new_node_id = strndup(node_id, len);
|
||||||
|
if (!new_node_id) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate %lu bytes for new node_id.", (unsigned long) len);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
if (esp_rmaker_priv_data->node_id) {
|
||||||
|
free(esp_rmaker_priv_data->node_id);
|
||||||
|
}
|
||||||
|
esp_rmaker_priv_data->node_id = new_node_id;
|
||||||
|
_esp_rmaker_node_t *node = (_esp_rmaker_node_t *)esp_rmaker_get_node();
|
||||||
|
node->node_id = new_node_id;
|
||||||
|
ESP_LOGI(TAG, "New Node ID ----- %s", new_node_id);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Event handler for catching system events */
|
||||||
|
static void esp_rmaker_event_handler(void* arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void* event_data)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||||
|
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||||
|
if (esp_rmaker_priv_data->claim_data) {
|
||||||
|
ESP_LOGE(TAG, "Node connected to Wi-Fi without Assisted claiming. Cannot proceed to MQTT connection.");
|
||||||
|
ESP_LOGE(TAG, "Please update your phone apps and repeat Wi-Fi provisioning with BLE transport.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (rmaker_core_event_group) {
|
||||||
|
/* Signal rmaker thread to continue execution */
|
||||||
|
xEventGroupSetBits(rmaker_core_event_group, WIFI_CONNECTED_EVENT);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD) /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||||
|
if (event_base == OPENTHREAD_EVENT && event_id == OPENTHREAD_EVENT_SET_DNS_SERVER) {
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||||
|
if (esp_rmaker_priv_data->claim_data) {
|
||||||
|
ESP_LOGE(TAG, "Node connected to Thread network without Assisted claiming. Cannot proceed to MQTT connection.");
|
||||||
|
ESP_LOGE(TAG, "Please update your phone apps and repeat Wi-Fi provisioning with BLE transport.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (rmaker_core_event_group) {
|
||||||
|
/* Signal rmaker thread to continue execution */
|
||||||
|
xEventGroupSetBits(rmaker_core_event_group, THREAD_SET_DNS_SEVER_EVENT);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||||
|
if (event_base == RMAKER_EVENT &&
|
||||||
|
(event_id == RMAKER_EVENT_USER_NODE_MAPPING_DONE ||
|
||||||
|
event_id == RMAKER_EVENT_USER_NODE_MAPPING_RESET)) {
|
||||||
|
esp_event_handler_unregister(RMAKER_EVENT, event_id, &esp_rmaker_event_handler);
|
||||||
|
esp_rmaker_params_mqtt_init();
|
||||||
|
} else if (event_base == RMAKER_COMMON_EVENT && event_id == RMAKER_MQTT_EVENT_CONNECTED) {
|
||||||
|
if (rmaker_core_event_group) {
|
||||||
|
/* Signal rmaker thread to continue execution */
|
||||||
|
xEventGroupSetBits(rmaker_core_event_group, MQTT_CONNECTED_EVENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_deinit_priv_data(esp_rmaker_priv_data_t *rmaker_priv_data)
|
||||||
|
{
|
||||||
|
if (!rmaker_priv_data) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_rmaker_work_queue_deinit();
|
||||||
|
#ifndef CONFIG_ESP_RMAKER_DISABLE_USER_MAPPING_PROV
|
||||||
|
esp_rmaker_user_mapping_prov_deinit();
|
||||||
|
#endif
|
||||||
|
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||||
|
if (rmaker_priv_data->claim_data) {
|
||||||
|
esp_rmaker_claim_data_free(rmaker_priv_data->claim_data);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (rmaker_priv_data->mqtt_conn_params) {
|
||||||
|
esp_rmaker_clean_mqtt_conn_params(rmaker_priv_data->mqtt_conn_params);
|
||||||
|
free(rmaker_priv_data->mqtt_conn_params);
|
||||||
|
}
|
||||||
|
if (rmaker_priv_data->node_id) {
|
||||||
|
free(rmaker_priv_data->node_id);
|
||||||
|
}
|
||||||
|
free(rmaker_priv_data);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_node_deinit(const esp_rmaker_node_t *node)
|
||||||
|
{
|
||||||
|
if (!esp_rmaker_priv_data) {
|
||||||
|
ESP_LOGE(TAG, "ESP RainMaker already de-initialized.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_rmaker_priv_data->state != ESP_RMAKER_STATE_INIT_DONE) {
|
||||||
|
ESP_LOGE(TAG, "ESP RainMaker is still running. Please stop it first.");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
esp_rmaker_node_delete(node);
|
||||||
|
esp_rmaker_priv_data->node = NULL;
|
||||||
|
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||||
|
esp_rmaker_priv_data = NULL;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *esp_rmaker_get_node_id(void)
|
||||||
|
{
|
||||||
|
if (esp_rmaker_priv_data) {
|
||||||
|
return esp_rmaker_priv_data->node_id;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_report_node_config_and_state()
|
||||||
|
{
|
||||||
|
if (esp_rmaker_report_node_config() != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Report node config failed.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (esp_rmaker_user_node_mapping_get_state() == ESP_RMAKER_USER_MAPPING_DONE) {
|
||||||
|
if (esp_rmaker_report_node_state() != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Report node state failed.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __esp_rmaker_report_node_config_and_state(void *data)
|
||||||
|
{
|
||||||
|
esp_rmaker_report_node_config_and_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_report_node_details()
|
||||||
|
{
|
||||||
|
return esp_rmaker_work_queue_add_task(__esp_rmaker_report_node_config_and_state, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void esp_rmaker_task(void *data)
|
||||||
|
{
|
||||||
|
ESP_RMAKER_CHECK_HANDLE();
|
||||||
|
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_STARTING;
|
||||||
|
esp_err_t err = ESP_FAIL;
|
||||||
|
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||||
|
wifi_ap_record_t ap_info;
|
||||||
|
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD)
|
||||||
|
ip6_addr_t nat64_prefix;
|
||||||
|
#endif
|
||||||
|
esp_rmaker_priv_data->mqtt_connected = false;
|
||||||
|
esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &esp_rmaker_mqtt_event_handler, NULL);
|
||||||
|
esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_DISCONNECTED, &esp_rmaker_mqtt_event_handler, NULL);
|
||||||
|
rmaker_core_event_group = xEventGroupCreate();
|
||||||
|
if (!rmaker_core_event_group) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create event group. Aborting");
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||||
|
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &esp_rmaker_event_handler, esp_rmaker_priv_data);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to register event handler. Error: %d. Aborting", err);
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD)
|
||||||
|
err = esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_SET_DNS_SERVER, &esp_rmaker_event_handler, esp_rmaker_priv_data);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to register event handler. Error: %d. Aborting", err);
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
err = esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &esp_rmaker_event_handler, esp_rmaker_priv_data);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to register event handler. Error: %d. Aborting", err);
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
/* Assisted claiming needs to be done before Wi-Fi connection */
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||||
|
if (esp_rmaker_priv_data->need_claim) {
|
||||||
|
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||||
|
if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Node connected to Wi-Fi without Assisted claiming. Cannot proceed to MQTT connection.");
|
||||||
|
ESP_LOGE(TAG, "Please update your phone apps and repeat Wi-Fi provisioning with BLE transport.");
|
||||||
|
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &esp_rmaker_event_handler);
|
||||||
|
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD)
|
||||||
|
if (esp_openthread_get_nat64_prefix(&nat64_prefix) == ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Node connected to Thread without Assisted claiming. Cannot proceed to MQTT connection.");
|
||||||
|
ESP_LOGE(TAG, "Please update your phone apps and repeat Thread provisioning with BLE transport.");
|
||||||
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_SET_DNS_SERVER, &esp_rmaker_event_handler);
|
||||||
|
#endif
|
||||||
|
err = ESP_FAIL;
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_STARTED, NULL, 0);
|
||||||
|
err = esp_rmaker_assisted_claim_perform(esp_rmaker_priv_data->claim_data);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_FAILED, NULL, 0);
|
||||||
|
ESP_LOGE(TAG, "esp_rmaker_self_claim_perform() returned %d. Aborting", err);
|
||||||
|
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||||
|
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &esp_rmaker_event_handler);
|
||||||
|
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD)
|
||||||
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_SET_DNS_SERVER, &esp_rmaker_event_handler);
|
||||||
|
#endif
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
esp_rmaker_priv_data->claim_data = NULL;
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_SUCCESSFUL, NULL, 0);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_ASSISTED_CLAIM */
|
||||||
|
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||||
|
/* Check if already connected to Wi-Fi */
|
||||||
|
if (esp_wifi_sta_get_ap_info(&ap_info) != ESP_OK) {
|
||||||
|
/* Wait for Wi-Fi connection */
|
||||||
|
xEventGroupWaitBits(rmaker_core_event_group, WIFI_CONNECTED_EVENT, false, true, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &esp_rmaker_event_handler);
|
||||||
|
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD) /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
err = esp_openthread_get_nat64_prefix(&nat64_prefix);
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
/* Check if already get nat64 prefix */
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
/* Wait for Thread connection */
|
||||||
|
xEventGroupWaitBits(rmaker_core_event_group, THREAD_SET_DNS_SEVER_EVENT, false, true, portMAX_DELAY);
|
||||||
|
err = ESP_OK;
|
||||||
|
}
|
||||||
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_SET_DNS_SERVER, &esp_rmaker_event_handler);
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||||
|
|
||||||
|
if (esp_rmaker_priv_data->enable_time_sync) {
|
||||||
|
#ifdef CONFIG_MBEDTLS_HAVE_TIME_DATE
|
||||||
|
esp_rmaker_time_wait_for_sync(portMAX_DELAY);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/* Self claiming can be done only after Wi-Fi connection */
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_SELF_CLAIM
|
||||||
|
if (esp_rmaker_priv_data->need_claim) {
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_STARTED, NULL, 0);
|
||||||
|
err = esp_rmaker_self_claim_perform(esp_rmaker_priv_data->claim_data);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_FAILED, NULL, 0);
|
||||||
|
ESP_LOGE(TAG, "esp_rmaker_self_claim_perform() returned %d. Aborting", err);
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
esp_rmaker_priv_data->claim_data = NULL;
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_SUCCESSFUL, NULL, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||||
|
if (esp_rmaker_priv_data->need_claim) {
|
||||||
|
esp_rmaker_priv_data->mqtt_conn_params = esp_rmaker_get_mqtt_conn_params();
|
||||||
|
if (!esp_rmaker_priv_data->mqtt_conn_params) {
|
||||||
|
ESP_LOGE(TAG, "Failed to initialise MQTT Config after claiming. Aborting");
|
||||||
|
err = ESP_FAIL;
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
err = esp_rmaker_mqtt_init(esp_rmaker_priv_data->mqtt_conn_params);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "esp_rmaker_mqtt_init() returned %d. Aborting", err);
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
esp_rmaker_priv_data->need_claim = false;
|
||||||
|
}
|
||||||
|
#endif /* ESP_RMAKER_CLAIM_ENABLED */
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_CMD_RESP_ENABLE
|
||||||
|
err = esp_rmaker_cmd_response_enable();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to enable Command - Response module. Aborting!!!");
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
esp_rmaker_node_add_attribute(esp_rmaker_get_node(), "cmd-resp", "1");
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "Command-Response Module not enabled. Set CONFIG_ESP_RMAKER_CMD_RESP_ENABLE=y to use it.");
|
||||||
|
#endif /* !CONFIG_ESP_RMAKER_CMD_RESP_ENABLE */
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE
|
||||||
|
err = esp_rmaker_local_ctrl_enable();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to start local control service. Aborting!!!");
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE */
|
||||||
|
err = esp_rmaker_mqtt_connect();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "esp_rmaker_mqtt_connect() returned %d. Aborting", err);
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Waiting for MQTT connection");
|
||||||
|
xEventGroupWaitBits(rmaker_core_event_group, MQTT_CONNECTED_EVENT, false, true, portMAX_DELAY);
|
||||||
|
esp_event_handler_unregister(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &esp_rmaker_event_handler);
|
||||||
|
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_STARTED;
|
||||||
|
err = esp_rmaker_report_node_config();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Aborting!!!");
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
if (esp_rmaker_user_node_mapping_get_state() == ESP_RMAKER_USER_MAPPING_DONE) {
|
||||||
|
err = esp_rmaker_params_mqtt_init();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Aborting!!!");
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* If network is connected without even starting the user-node mapping workflow,
|
||||||
|
* it could mean that some incorrect app was used to provision the device. Even
|
||||||
|
* if the older user would not be able to update the params, it would be better
|
||||||
|
* to completely reset the user permissions by sending a dummy user node mapping
|
||||||
|
* request, so that the earlier user won't even see the connectivity and other
|
||||||
|
* status.
|
||||||
|
*/
|
||||||
|
if (esp_rmaker_user_node_mapping_get_state() != ESP_RMAKER_USER_MAPPING_STARTED) {
|
||||||
|
esp_rmaker_reset_user_node_mapping();
|
||||||
|
/* Wait for user reset to finish. */
|
||||||
|
err = esp_event_handler_register(RMAKER_EVENT, RMAKER_EVENT_USER_NODE_MAPPING_RESET,
|
||||||
|
&esp_rmaker_event_handler, NULL);
|
||||||
|
} else {
|
||||||
|
/* Wait for User Node mapping to finish. */
|
||||||
|
err = esp_event_handler_register(RMAKER_EVENT, RMAKER_EVENT_USER_NODE_MAPPING_DONE,
|
||||||
|
&esp_rmaker_event_handler, NULL);
|
||||||
|
}
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Aborting!!!");
|
||||||
|
goto rmaker_end;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Waiting for User Node Association.");
|
||||||
|
}
|
||||||
|
err = ESP_OK;
|
||||||
|
|
||||||
|
rmaker_end:
|
||||||
|
if (rmaker_core_event_group) {
|
||||||
|
vEventGroupDelete(rmaker_core_event_group);
|
||||||
|
}
|
||||||
|
rmaker_core_event_group = NULL;
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (esp_rmaker_priv_data->mqtt_connected) {
|
||||||
|
esp_rmaker_mqtt_disconnect();
|
||||||
|
}
|
||||||
|
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_INIT_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_mqtt_conn_params_init(esp_rmaker_priv_data_t *rmaker_priv_data, bool use_claiming)
|
||||||
|
{
|
||||||
|
rmaker_priv_data->mqtt_conn_params = esp_rmaker_get_mqtt_conn_params();
|
||||||
|
if (rmaker_priv_data->mqtt_conn_params) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||||
|
if (use_claiming) {
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_SELF_CLAIM
|
||||||
|
rmaker_priv_data->claim_data = esp_rmaker_self_claim_init();
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||||
|
rmaker_priv_data->claim_data = esp_rmaker_assisted_claim_init();
|
||||||
|
#endif
|
||||||
|
if (!rmaker_priv_data->claim_data) {
|
||||||
|
ESP_LOGE(TAG, "Failed to initialise Claiming.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
} else {
|
||||||
|
rmaker_priv_data->need_claim = true;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* ESP_RMAKER_CLAIM_ENABLED */
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
/* Initialize ESP RainMaker */
|
||||||
|
static esp_err_t esp_rmaker_init(const esp_rmaker_config_t *config, bool use_claiming)
|
||||||
|
{
|
||||||
|
if (esp_rmaker_priv_data) {
|
||||||
|
ESP_LOGE(TAG, "ESP RainMaker already initialised");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
if (!config) {
|
||||||
|
ESP_LOGE(TAG, "RainMaker config missing. Cannot initialise");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (esp_rmaker_factory_init() != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to initialise storage");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_rmaker_priv_data = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_priv_data_t));
|
||||||
|
if (!esp_rmaker_priv_data) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_priv_data->node_id = esp_rmaker_populate_node_id(use_claiming);
|
||||||
|
if (!esp_rmaker_priv_data->node_id) {
|
||||||
|
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||||
|
esp_rmaker_priv_data = NULL;
|
||||||
|
ESP_LOGE(TAG, "Failed to initialise Node Id. Please perform \"claiming\" using RainMaker CLI.");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_rmaker_work_queue_init() != ESP_OK) {
|
||||||
|
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||||
|
esp_rmaker_priv_data = NULL;
|
||||||
|
ESP_LOGE(TAG, "ESP RainMaker Queue Creation Failed");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
#ifndef CONFIG_ESP_RMAKER_DISABLE_USER_MAPPING_PROV
|
||||||
|
if (esp_rmaker_user_mapping_prov_init()) {
|
||||||
|
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||||
|
esp_rmaker_priv_data = NULL;
|
||||||
|
ESP_LOGE(TAG, "Could not initialise User-Node mapping.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
#endif /* !CONFIG_ESP_RMAKER_DISABLE_USER_MAPPING_PROV */
|
||||||
|
if (esp_rmaker_mqtt_conn_params_init(esp_rmaker_priv_data, use_claiming) != ESP_OK) {
|
||||||
|
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||||
|
esp_rmaker_priv_data = NULL;
|
||||||
|
ESP_LOGE(TAG, "Failed to initialise MQTT Params. Please perform \"claiming\" using RainMaker CLI.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
} else {
|
||||||
|
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||||
|
if (!esp_rmaker_priv_data->need_claim)
|
||||||
|
#endif /* ESP_RMAKER_CLAIM_ENABLED */
|
||||||
|
{
|
||||||
|
if (esp_rmaker_mqtt_init(esp_rmaker_priv_data->mqtt_conn_params) != ESP_OK) {
|
||||||
|
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||||
|
esp_rmaker_priv_data = NULL;
|
||||||
|
ESP_LOGE(TAG, "Failed to initialise MQTT");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esp_rmaker_user_node_mapping_init();
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE
|
||||||
|
esp_rmaker_init_local_ctrl_service();
|
||||||
|
#endif
|
||||||
|
esp_rmaker_priv_data->enable_time_sync = config->enable_time_sync;
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_INIT_DONE, NULL, 0);
|
||||||
|
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_INIT_DONE;
|
||||||
|
|
||||||
|
/* Adding the RainMaker Task to the queue so that it is will be the first function
|
||||||
|
* to be executed when the Work Queue task begins.
|
||||||
|
*/
|
||||||
|
esp_rmaker_work_queue_add_task(esp_rmaker_task, NULL);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_register_node(const esp_rmaker_node_t *node)
|
||||||
|
{
|
||||||
|
ESP_RMAKER_CHECK_HANDLE(ESP_ERR_INVALID_STATE);
|
||||||
|
if (esp_rmaker_priv_data->node) {
|
||||||
|
ESP_LOGE(TAG, "A node has already been registered. Cannot register another.");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
if (!node) {
|
||||||
|
ESP_LOGE(TAG, "Node handle cannot be NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_rmaker_priv_data->node = node;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_node_t *esp_rmaker_node_init(const esp_rmaker_config_t *config, const char *name, const char *type)
|
||||||
|
{
|
||||||
|
esp_err_t err = esp_rmaker_init(config, true);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
esp_rmaker_node_t *node = esp_rmaker_node_create(name, type);
|
||||||
|
if (!node) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create node");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
err = esp_rmaker_register_node(node);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
free(node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const esp_rmaker_node_t *esp_rmaker_get_node()
|
||||||
|
{
|
||||||
|
ESP_RMAKER_CHECK_HANDLE(NULL);
|
||||||
|
return esp_rmaker_priv_data->node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start the ESP RainMaker Core Task */
|
||||||
|
esp_err_t esp_rmaker_start(void)
|
||||||
|
{
|
||||||
|
ESP_RMAKER_CHECK_HANDLE(ESP_ERR_INVALID_STATE);
|
||||||
|
if (esp_rmaker_priv_data->enable_time_sync) {
|
||||||
|
esp_rmaker_time_sync_init(NULL);
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Starting RainMaker Work Queue task");
|
||||||
|
if (esp_rmaker_work_queue_start() != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Couldn't create RainMaker Work Queue task");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, &reset_event_handler, NULL));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_stop()
|
||||||
|
{
|
||||||
|
ESP_RMAKER_CHECK_HANDLE(ESP_ERR_INVALID_STATE);
|
||||||
|
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_STOP_REQUESTED;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
379
components/esp_rainmaker/src/core/esp_rmaker_device.c
Normal file
379
components/esp_rainmaker/src/core/esp_rmaker_device.c
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_standard_types.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
|
||||||
|
#include "esp_rmaker_internal.h"
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_device";
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_device_delete(const esp_rmaker_device_t *device)
|
||||||
|
{
|
||||||
|
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||||
|
if (_device) {
|
||||||
|
if (_device->parent) {
|
||||||
|
ESP_LOGE(TAG, "Cannot delete device as it is part of a node. Remove it from the node first.");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
esp_rmaker_attr_t *attr = _device->attributes;
|
||||||
|
while (attr) {
|
||||||
|
esp_rmaker_attr_t *next_attr = attr->next;
|
||||||
|
esp_rmaker_attribute_delete(attr);
|
||||||
|
attr = next_attr;
|
||||||
|
}
|
||||||
|
_esp_rmaker_param_t *param = _device->params;
|
||||||
|
while (param) {
|
||||||
|
_esp_rmaker_param_t *next_param = param->next;
|
||||||
|
esp_rmaker_param_delete((esp_rmaker_param_t *)param);
|
||||||
|
param = next_param;
|
||||||
|
}
|
||||||
|
if (_device->subtype) {
|
||||||
|
free(_device->subtype);
|
||||||
|
}
|
||||||
|
if (_device->model) {
|
||||||
|
free(_device->model);
|
||||||
|
}
|
||||||
|
if (_device->name) {
|
||||||
|
free(_device->name);
|
||||||
|
}
|
||||||
|
if (_device->type) {
|
||||||
|
free(_device->type);
|
||||||
|
}
|
||||||
|
free(_device);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_default_bulk_write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_write_req_t write_req[],
|
||||||
|
uint8_t count, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||||
|
_esp_rmaker_param_t *param;
|
||||||
|
if (_device) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
param = (_esp_rmaker_param_t *)(write_req[i].param);
|
||||||
|
if (param->type && (strcmp(param->type, ESP_RMAKER_PARAM_NAME) == 0)) {
|
||||||
|
#ifndef CONFIG_RMAKER_NAME_PARAM_CB
|
||||||
|
esp_rmaker_param_update(write_req[i].param, write_req[i].val);
|
||||||
|
continue;
|
||||||
|
#else
|
||||||
|
if (!_device->write_cb) {
|
||||||
|
esp_rmaker_param_update(write_req[i].param, write_req[i].val);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (_device->write_cb) {
|
||||||
|
if (_device->write_cb(device, write_req[i].param, write_req[i].val, priv_data, ctx) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Remote update to param %s - %s failed", _device->name, ((_esp_rmaker_param_t *)(write_req[i].param))->name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "No write callback for device %s", _device->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_rmaker_device_t *__esp_rmaker_device_create(const char *name, const char *type, void *priv, bool is_service)
|
||||||
|
{
|
||||||
|
if (!name) {
|
||||||
|
ESP_LOGE(TAG, "%s name is mandatory", is_service ? "Service":"Device");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
_esp_rmaker_device_t *_device = MEM_CALLOC_EXTRAM(1, sizeof(_esp_rmaker_device_t));
|
||||||
|
if (!_device) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for %s %s", is_service ? "Service":"Device", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
_device->name = strdup(name);
|
||||||
|
if (!_device->name) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for name for %s %s", is_service ? "Service":"Device", name);
|
||||||
|
goto device_create_err;
|
||||||
|
}
|
||||||
|
if (type) {
|
||||||
|
_device->type = strdup(type);
|
||||||
|
if (!_device->type) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for type for %s %s", is_service ? "Service":"Device", name);
|
||||||
|
goto device_create_err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_device->priv_data = priv;
|
||||||
|
_device->is_service = is_service;
|
||||||
|
/* Adding a default bulk write callback for backward compatibility with application code using single param write callback */
|
||||||
|
_device->bulk_write_cb = esp_rmaker_default_bulk_write_cb;
|
||||||
|
|
||||||
|
return (esp_rmaker_device_t *)_device;
|
||||||
|
|
||||||
|
device_create_err:
|
||||||
|
esp_rmaker_device_delete((esp_rmaker_device_t *)_device);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_device_create(const char *name, const char *type, void *priv)
|
||||||
|
{
|
||||||
|
return __esp_rmaker_device_create(name, type, priv, false);
|
||||||
|
}
|
||||||
|
esp_rmaker_device_t *esp_rmaker_service_create(const char *name, const char *type, void *priv)
|
||||||
|
{
|
||||||
|
return __esp_rmaker_device_create(name, type, priv, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_device_add_param(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param)
|
||||||
|
{
|
||||||
|
if (!device || !param) {
|
||||||
|
ESP_LOGE(TAG, "Device or Param handle cannot be NULL");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||||
|
_esp_rmaker_param_t *_new_param = (_esp_rmaker_param_t *)param;
|
||||||
|
|
||||||
|
_esp_rmaker_param_t *_param = _device->params;
|
||||||
|
while(_param) {
|
||||||
|
if (strcmp(_param->name, _new_param->name) == 0) {
|
||||||
|
ESP_LOGE(TAG, "Parameter with name %s already exists in Device %s", _new_param->name, _device->name);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (_param->next) {
|
||||||
|
_param = _param->next;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_new_param->parent = _device;
|
||||||
|
if (_param) {
|
||||||
|
_param->next = _new_param;
|
||||||
|
} else {
|
||||||
|
_device->params = _new_param;
|
||||||
|
}
|
||||||
|
_device->param_count++;
|
||||||
|
/* We check the stored value here, and not during param creation, because a parameter
|
||||||
|
* in itself isn't unique. However, it is unique within a given device and hence can
|
||||||
|
* be uniquely represented in storage only when added to a device.
|
||||||
|
*/
|
||||||
|
esp_rmaker_param_val_t stored_val;
|
||||||
|
stored_val.type = _new_param->val.type;
|
||||||
|
if (_new_param->prop_flags & PROP_FLAG_PERSIST) {
|
||||||
|
if (esp_rmaker_param_get_stored_value(_new_param, &stored_val) == ESP_OK) {
|
||||||
|
if ((_new_param->val.type == RMAKER_VAL_TYPE_STRING) || (_new_param->val.type == RMAKER_VAL_TYPE_OBJECT)
|
||||||
|
|| (_new_param->val.type == RMAKER_VAL_TYPE_ARRAY)) {
|
||||||
|
if (_new_param->val.val.s) {
|
||||||
|
free(_new_param->val.val.s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_new_param->val = stored_val;
|
||||||
|
/* The device callback should be invoked once with the stored value, so
|
||||||
|
* that applications can do initialisations as required.
|
||||||
|
*/
|
||||||
|
if (_device->bulk_write_cb) {
|
||||||
|
/* However, the callback should be invoked, only if the parameter is not
|
||||||
|
* of type ESP_RMAKER_PARAM_NAME, as it has special handling internally.
|
||||||
|
*/
|
||||||
|
if (!(_new_param->type && strcmp(_new_param->type, ESP_RMAKER_PARAM_NAME) == 0)) {
|
||||||
|
esp_rmaker_write_ctx_t ctx = {
|
||||||
|
.src = ESP_RMAKER_REQ_SRC_INIT,
|
||||||
|
};
|
||||||
|
esp_rmaker_param_write_req_t write_req = {
|
||||||
|
.param = (esp_rmaker_param_t *)param,
|
||||||
|
.val = stored_val,
|
||||||
|
};
|
||||||
|
_device->bulk_write_cb(device, &write_req, 1, _device->priv_data, &ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
esp_rmaker_param_store_value(_new_param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Param %s added in %s", _new_param->name, _device->name);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a new Device Attribute */
|
||||||
|
esp_err_t esp_rmaker_device_add_attribute(const esp_rmaker_device_t *device, const char *attr_name, const char *val)
|
||||||
|
{
|
||||||
|
if (!device || !attr_name || !val) {
|
||||||
|
ESP_LOGE(TAG, "Device handle, attribute name or value cannot be NULL");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
_esp_rmaker_device_t *_device = ( _esp_rmaker_device_t *)device;
|
||||||
|
esp_rmaker_attr_t *attr = _device->attributes;
|
||||||
|
while(attr) {
|
||||||
|
if (strcmp(attr_name, attr->name) == 0) {
|
||||||
|
ESP_LOGE(TAG, "Attribute with name %s already exists in Device %s", attr_name, _device->name);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (attr->next) {
|
||||||
|
attr = attr->next;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esp_rmaker_attr_t *new_attr = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_attr_t));
|
||||||
|
if (!new_attr) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for device attribute");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
new_attr->name = strdup(attr_name);
|
||||||
|
new_attr->value = strdup(val);
|
||||||
|
if (!new_attr->name || !new_attr->value) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for device attribute name or value");
|
||||||
|
esp_rmaker_attribute_delete(new_attr);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
if (attr) {
|
||||||
|
attr->next = new_attr;
|
||||||
|
} else {
|
||||||
|
_device->attributes = new_attr;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Device attribute %s.%s added", _device->name, attr_name);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a device subtype */
|
||||||
|
esp_err_t esp_rmaker_device_add_subtype(const esp_rmaker_device_t *device, const char *subtype)
|
||||||
|
{
|
||||||
|
if (!device || !subtype) {
|
||||||
|
ESP_LOGE(TAG, "Device handle or subtype cannot be NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||||
|
if (_device->subtype) {
|
||||||
|
free(_device->subtype);
|
||||||
|
}
|
||||||
|
if ((_device->subtype = strdup(subtype)) != NULL ){
|
||||||
|
return ESP_OK;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for device subtype");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a device model */
|
||||||
|
esp_err_t esp_rmaker_device_add_model(const esp_rmaker_device_t *device, const char *model)
|
||||||
|
{
|
||||||
|
if (!device || !model) {
|
||||||
|
ESP_LOGE(TAG, "Device handle or model cannot be NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||||
|
if (_device->model) {
|
||||||
|
free(_device->model);
|
||||||
|
}
|
||||||
|
if ((_device->model = strdup(model)) != NULL ){
|
||||||
|
return ESP_OK;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for device model");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_device_assign_primary_param(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param)
|
||||||
|
{
|
||||||
|
if (!device || !param) {
|
||||||
|
ESP_LOGE(TAG,"Device or Param handle cannot be NULL");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
((_esp_rmaker_device_t *)device)->primary = (_esp_rmaker_param_t *)param;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_device_add_cb(const esp_rmaker_device_t *device, esp_rmaker_device_write_cb_t write_cb, esp_rmaker_device_read_cb_t read_cb)
|
||||||
|
{
|
||||||
|
if (!device) {
|
||||||
|
ESP_LOGE(TAG, "Device handle cannot be NULL");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||||
|
_device->write_cb = write_cb;
|
||||||
|
_device->read_cb = read_cb;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_device_add_bulk_cb(const esp_rmaker_device_t *device, esp_rmaker_device_bulk_write_cb_t write_cb,
|
||||||
|
esp_rmaker_device_bulk_read_cb_t read_cb)
|
||||||
|
{
|
||||||
|
if (!device) {
|
||||||
|
ESP_LOGE(TAG, "Device handle cannot be NULL");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||||
|
_device->bulk_write_cb = write_cb;
|
||||||
|
_device->bulk_read_cb = read_cb;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *esp_rmaker_device_get_name(const esp_rmaker_device_t *device)
|
||||||
|
{
|
||||||
|
if (!device) {
|
||||||
|
ESP_LOGE(TAG, "Device handle cannot be NULL.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return ((_esp_rmaker_device_t *)device)->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *esp_rmaker_device_get_priv_data(const esp_rmaker_device_t *device)
|
||||||
|
{
|
||||||
|
if (!device) {
|
||||||
|
ESP_LOGE(TAG, "Device handle cannot be NULL.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return ((_esp_rmaker_device_t *)device)->priv_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *esp_rmaker_device_get_type(const esp_rmaker_device_t *device)
|
||||||
|
{
|
||||||
|
if (!device) {
|
||||||
|
ESP_LOGE(TAG, "Device handle cannot be NULL.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return ((_esp_rmaker_device_t *)device)->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_device_get_param_by_type(const esp_rmaker_device_t *device, const char *param_type)
|
||||||
|
{
|
||||||
|
if (!device || !param_type) {
|
||||||
|
ESP_LOGE(TAG, "Device handle or param type cannot be NULL");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
_esp_rmaker_param_t *param = ((_esp_rmaker_device_t *)device)->params;
|
||||||
|
while(param) {
|
||||||
|
if (strcmp(param->type, param_type) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
param = param->next;
|
||||||
|
}
|
||||||
|
return (esp_rmaker_param_t *)param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_device_get_param_by_name(const esp_rmaker_device_t *device, const char *param_name)
|
||||||
|
{
|
||||||
|
if (!device || !param_name) {
|
||||||
|
ESP_LOGE(TAG, "Device handle or param name cannot be NULL");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
_esp_rmaker_param_t *param = ((_esp_rmaker_device_t *)device)->params;
|
||||||
|
while(param) {
|
||||||
|
if (strcmp(param->name, param_name) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
param = param->next;
|
||||||
|
}
|
||||||
|
return (esp_rmaker_param_t *)param;
|
||||||
|
}
|
||||||
129
components/esp_rainmaker/src/core/esp_rmaker_internal.h
Normal file
129
components/esp_rainmaker/src/core/esp_rmaker_internal.h
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/queue.h>
|
||||||
|
#include <json_generator.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_idf_version.h>
|
||||||
|
|
||||||
|
#define RMAKER_PARAM_FLAG_VALUE_CHANGE (1 << 0)
|
||||||
|
#define RMAKER_PARAM_FLAG_VALUE_NOTIFY (1 << 1)
|
||||||
|
#define ESP_RMAKER_NVS_PART_NAME "nvs"
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) && defined(CONFIG_ESP_RMAKER_USING_NETWORK_PROV)
|
||||||
|
#define RMAKER_USING_NETWORK_PROV 1
|
||||||
|
#else
|
||||||
|
#define RMAKER_USING_NETWORK_PROV 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ESP_RMAKER_STATE_DEINIT = 0,
|
||||||
|
ESP_RMAKER_STATE_INIT_DONE,
|
||||||
|
ESP_RMAKER_STATE_STARTING,
|
||||||
|
ESP_RMAKER_STATE_STARTED,
|
||||||
|
ESP_RMAKER_STATE_STOP_REQUESTED,
|
||||||
|
} esp_rmaker_state_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_rmaker_param_val_t min;
|
||||||
|
esp_rmaker_param_val_t max;
|
||||||
|
esp_rmaker_param_val_t step;
|
||||||
|
} esp_rmaker_param_bounds_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t str_list_cnt;
|
||||||
|
const char **str_list;
|
||||||
|
} esp_rmaker_param_valid_str_list_t;
|
||||||
|
|
||||||
|
struct esp_rmaker_param {
|
||||||
|
char *name;
|
||||||
|
char *type;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t prop_flags;
|
||||||
|
char *ui_type;
|
||||||
|
esp_rmaker_param_val_t val;
|
||||||
|
esp_rmaker_param_bounds_t *bounds;
|
||||||
|
esp_rmaker_param_valid_str_list_t *valid_str_list;
|
||||||
|
struct esp_rmaker_device *parent;
|
||||||
|
struct esp_rmaker_param * next;
|
||||||
|
};
|
||||||
|
typedef struct esp_rmaker_param _esp_rmaker_param_t;
|
||||||
|
|
||||||
|
struct esp_rmaker_attr {
|
||||||
|
char *name;
|
||||||
|
char *value;
|
||||||
|
struct esp_rmaker_attr *next;
|
||||||
|
};
|
||||||
|
typedef struct esp_rmaker_attr esp_rmaker_attr_t;
|
||||||
|
|
||||||
|
|
||||||
|
struct esp_rmaker_device {
|
||||||
|
char *name;
|
||||||
|
char *type;
|
||||||
|
char *subtype;
|
||||||
|
char *model;
|
||||||
|
uint8_t param_count;
|
||||||
|
esp_rmaker_device_write_cb_t write_cb;
|
||||||
|
esp_rmaker_device_read_cb_t read_cb;
|
||||||
|
esp_rmaker_device_bulk_write_cb_t bulk_write_cb;
|
||||||
|
esp_rmaker_device_bulk_read_cb_t bulk_read_cb;
|
||||||
|
void *priv_data;
|
||||||
|
bool is_service;
|
||||||
|
esp_rmaker_attr_t *attributes;
|
||||||
|
_esp_rmaker_param_t *params;
|
||||||
|
_esp_rmaker_param_t *primary;
|
||||||
|
const esp_rmaker_node_t *parent;
|
||||||
|
struct esp_rmaker_device *next;
|
||||||
|
};
|
||||||
|
typedef struct esp_rmaker_device _esp_rmaker_device_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *node_id;
|
||||||
|
esp_rmaker_node_info_t *info;
|
||||||
|
esp_rmaker_attr_t *attributes;
|
||||||
|
_esp_rmaker_device_t *devices;
|
||||||
|
} _esp_rmaker_node_t;
|
||||||
|
|
||||||
|
esp_rmaker_node_t *esp_rmaker_node_create(const char *name, const char *type);
|
||||||
|
esp_err_t esp_rmaker_change_node_id(char *node_id, size_t len);
|
||||||
|
esp_err_t esp_rmaker_report_value(const esp_rmaker_param_val_t *val, char *key, json_gen_str_t *jptr);
|
||||||
|
esp_err_t esp_rmaker_report_data_type(esp_rmaker_val_type_t type, char *data_type_key, json_gen_str_t *jptr);
|
||||||
|
esp_err_t esp_rmaker_report_node_config(void);
|
||||||
|
esp_err_t esp_rmaker_report_node_state(void);
|
||||||
|
_esp_rmaker_device_t *esp_rmaker_node_get_first_device(const esp_rmaker_node_t *node);
|
||||||
|
esp_rmaker_attr_t *esp_rmaker_node_get_first_attribute(const esp_rmaker_node_t *node);
|
||||||
|
esp_err_t esp_rmaker_params_mqtt_init(void);
|
||||||
|
esp_err_t esp_rmaker_param_get_stored_value(_esp_rmaker_param_t *param, esp_rmaker_param_val_t *val);
|
||||||
|
esp_err_t esp_rmaker_param_store_value(_esp_rmaker_param_t *param);
|
||||||
|
esp_err_t esp_rmaker_node_delete(const esp_rmaker_node_t *node);
|
||||||
|
esp_err_t esp_rmaker_param_delete(const esp_rmaker_param_t *param);
|
||||||
|
esp_err_t esp_rmaker_attribute_delete(esp_rmaker_attr_t *attr);
|
||||||
|
char *esp_rmaker_get_node_config(void);
|
||||||
|
char *esp_rmaker_get_node_params(void);
|
||||||
|
esp_err_t esp_rmaker_handle_set_params(char *data, size_t data_len, esp_rmaker_req_src_t src);
|
||||||
|
esp_err_t esp_rmaker_user_mapping_prov_init(void);
|
||||||
|
esp_err_t esp_rmaker_user_mapping_prov_deinit(void);
|
||||||
|
esp_err_t esp_rmaker_user_node_mapping_init(void);
|
||||||
|
esp_err_t esp_rmaker_user_node_mapping_deinit(void);
|
||||||
|
esp_err_t esp_rmaker_reset_user_node_mapping(void);
|
||||||
|
esp_err_t esp_rmaker_init_local_ctrl_service(void);
|
||||||
|
esp_err_t esp_rmaker_start_local_ctrl_service(const char *serv_name);
|
||||||
|
static inline esp_err_t esp_rmaker_post_event(esp_rmaker_event_t event_id, void* data, size_t data_size)
|
||||||
|
{
|
||||||
|
return esp_event_post(RMAKER_EVENT, event_id, data, data_size, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
esp_rmaker_state_t esp_rmaker_get_state(void);
|
||||||
|
esp_err_t esp_rmaker_cmd_response_enable(void);
|
||||||
650
components/esp_rainmaker/src/core/esp_rmaker_local_ctrl.c
Normal file
650
components/esp_rainmaker/src/core/esp_rmaker_local_ctrl.c
Normal file
@@ -0,0 +1,650 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <nvs.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
#include <esp_local_ctrl.h>
|
||||||
|
#include <esp_rmaker_internal.h>
|
||||||
|
#include <esp_rmaker_standard_services.h>
|
||||||
|
#include <esp_https_server.h>
|
||||||
|
#include <esp_rmaker_work_queue.h>
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||||
|
#include <mdns.h>
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||||
|
#include <esp_openthread.h>
|
||||||
|
#include <esp_openthread_lock.h>
|
||||||
|
#include <openthread/srp_client.h>
|
||||||
|
#include <openthread/srp_client_buffers.h>
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
|
||||||
|
#include <esp_idf_version.h>
|
||||||
|
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
#include <network_provisioning/manager.h>
|
||||||
|
#else
|
||||||
|
#include <wifi_provisioning/manager.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||||
|
// Features supported in 4.2
|
||||||
|
|
||||||
|
#define ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE CONFIG_ESP_RMAKER_LOCAL_CTRL_SECURITY
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if CONFIG_ESP_RMAKER_LOCAL_CTRL_SECURITY != 0
|
||||||
|
#warning "Local control security type is not supported in idf versions below 4.2. Using sec0 by default."
|
||||||
|
#endif
|
||||||
|
#define ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE 0
|
||||||
|
|
||||||
|
#endif /* !IDF4.2 */
|
||||||
|
|
||||||
|
static const char * TAG = "esp_rmaker_local";
|
||||||
|
|
||||||
|
/* Random Port number that will be used by the local control http instance
|
||||||
|
* for internal control communication.
|
||||||
|
*/
|
||||||
|
#define ESP_RMAKER_LOCAL_CTRL_DEVICE_NAME "Local Control"
|
||||||
|
#define ESP_RMAKER_LOCAL_CTRL_HTTP_CTRL_PORT 12312
|
||||||
|
#define ESP_RMAKER_NVS_PART_NAME "nvs"
|
||||||
|
#define ESP_RMAKER_NVS_LOCAL_CTRL_NAMESPACE "local_ctrl"
|
||||||
|
#define ESP_RMAKER_NVS_LOCAL_CTRL_POP "pop"
|
||||||
|
#define ESP_RMAKER_POP_LEN 9
|
||||||
|
|
||||||
|
/* Custom allowed property types */
|
||||||
|
enum property_types {
|
||||||
|
PROP_TYPE_NODE_CONFIG = 1,
|
||||||
|
PROP_TYPE_NODE_PARAMS,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Custom flags that can be set for a property */
|
||||||
|
enum property_flags {
|
||||||
|
PROP_FLAG_READONLY = (1 << 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool g_local_ctrl_is_started = false;
|
||||||
|
|
||||||
|
static char *g_serv_name;
|
||||||
|
static bool wait_for_provisioning;
|
||||||
|
/********* Handler functions for responding to control requests / commands *********/
|
||||||
|
|
||||||
|
static esp_err_t get_property_values(size_t props_count,
|
||||||
|
const esp_local_ctrl_prop_t props[],
|
||||||
|
esp_local_ctrl_prop_val_t prop_values[],
|
||||||
|
void *usr_ctx)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < props_count && ret == ESP_OK ; i++) {
|
||||||
|
ESP_LOGD(TAG, "(%"PRIu32") Reading property : %s", i, props[i].name);
|
||||||
|
switch (props[i].type) {
|
||||||
|
case PROP_TYPE_NODE_CONFIG: {
|
||||||
|
char *node_config = esp_rmaker_get_node_config();
|
||||||
|
if (!node_config) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for %s", props[i].name);
|
||||||
|
ret = ESP_ERR_NO_MEM;
|
||||||
|
} else {
|
||||||
|
prop_values[i].size = strlen(node_config);
|
||||||
|
prop_values[i].data = node_config;
|
||||||
|
prop_values[i].free_fn = free;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PROP_TYPE_NODE_PARAMS: {
|
||||||
|
char *node_params = esp_rmaker_get_node_params();
|
||||||
|
if (!node_params) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for %s", props[i].name);
|
||||||
|
ret = ESP_ERR_NO_MEM;
|
||||||
|
} else {
|
||||||
|
prop_values[i].size = strlen(node_params);
|
||||||
|
prop_values[i].data = node_params;
|
||||||
|
prop_values[i].free_fn = free;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
for (uint32_t j = 0; j <= i; j++) {
|
||||||
|
if (prop_values[j].free_fn) {
|
||||||
|
ESP_LOGI(TAG, "Freeing memory for %s", props[j].name);
|
||||||
|
prop_values[j].free_fn(prop_values[j].data);
|
||||||
|
prop_values[j].free_fn = NULL;
|
||||||
|
prop_values[j].data = NULL;
|
||||||
|
prop_values[j].size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t set_property_values(size_t props_count,
|
||||||
|
const esp_local_ctrl_prop_t props[],
|
||||||
|
const esp_local_ctrl_prop_val_t prop_values[],
|
||||||
|
void *usr_ctx)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
uint32_t i;
|
||||||
|
/* First check if any of the properties are read-only properties. If found, just abort */
|
||||||
|
for (i = 0; i < props_count; i++) {
|
||||||
|
/* Cannot set the value of a read-only property */
|
||||||
|
if (props[i].flags & PROP_FLAG_READONLY) {
|
||||||
|
ESP_LOGE(TAG, "%s is read-only", props[i].name);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < props_count && ret == ESP_OK; i++) {
|
||||||
|
switch (props[i].type) {
|
||||||
|
case PROP_TYPE_NODE_PARAMS:
|
||||||
|
ret = esp_rmaker_handle_set_params((char *)prop_values[i].data,
|
||||||
|
prop_values[i].size, ESP_RMAKER_REQ_SRC_LOCAL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *__esp_rmaker_local_ctrl_get_nvs(const char *key)
|
||||||
|
{
|
||||||
|
char *val = NULL;
|
||||||
|
nvs_handle handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, ESP_RMAKER_NVS_LOCAL_CTRL_NAMESPACE, NVS_READONLY, &handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
size_t len = 0;
|
||||||
|
if ((err = nvs_get_blob(handle, key, NULL, &len)) == ESP_OK) {
|
||||||
|
val = MEM_CALLOC_EXTRAM(1, len + 1); /* +1 for NULL termination */
|
||||||
|
if (val) {
|
||||||
|
nvs_get_blob(handle, key, val, &len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nvs_close(handle);
|
||||||
|
return val;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t __esp_rmaker_local_ctrl_set_nvs(const char *key, const char *val)
|
||||||
|
{
|
||||||
|
nvs_handle handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, ESP_RMAKER_NVS_LOCAL_CTRL_NAMESPACE, NVS_READWRITE, &handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = nvs_set_blob(handle, key, val, strlen(val));
|
||||||
|
nvs_commit(handle);
|
||||||
|
nvs_close(handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *esp_rmaker_local_ctrl_get_pop()
|
||||||
|
{
|
||||||
|
char *pop = __esp_rmaker_local_ctrl_get_nvs(ESP_RMAKER_NVS_LOCAL_CTRL_POP);
|
||||||
|
if (pop) {
|
||||||
|
return pop;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Couldn't find POP in NVS. Generating a new one.");
|
||||||
|
pop = (char *)MEM_CALLOC_EXTRAM(1, ESP_RMAKER_POP_LEN);
|
||||||
|
if (!pop) {
|
||||||
|
ESP_LOGE(TAG, "Couldn't allocate POP");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
uint8_t random_bytes[ESP_RMAKER_POP_LEN] = {0};
|
||||||
|
esp_fill_random(&random_bytes, sizeof(random_bytes));
|
||||||
|
snprintf(pop, ESP_RMAKER_POP_LEN, "%02x%02x%02x%02x", random_bytes[0], random_bytes[1], random_bytes[2], random_bytes[3]);
|
||||||
|
|
||||||
|
__esp_rmaker_local_ctrl_set_nvs(ESP_RMAKER_NVS_LOCAL_CTRL_POP, pop);
|
||||||
|
return pop;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int esp_rmaker_local_ctrl_get_security_type()
|
||||||
|
{
|
||||||
|
return ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_local_ctrl_service_enable(void)
|
||||||
|
{
|
||||||
|
char *pop_str = esp_rmaker_local_ctrl_get_pop();
|
||||||
|
if (!pop_str) {
|
||||||
|
ESP_LOGE(TAG, "Get POP failed");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
int sec_ver = esp_rmaker_local_ctrl_get_security_type();
|
||||||
|
|
||||||
|
esp_rmaker_device_t *local_ctrl_service = esp_rmaker_create_local_control_service(ESP_RMAKER_LOCAL_CTRL_DEVICE_NAME, pop_str, sec_ver, NULL);;
|
||||||
|
if (!local_ctrl_service) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create Local Control Service.");
|
||||||
|
free(pop_str);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
free(pop_str);
|
||||||
|
|
||||||
|
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), local_ctrl_service);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to add Local Control Service to Node. err=0x%x", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#ifndef CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE
|
||||||
|
/* Report node details only if the local control is not enabled via config option,
|
||||||
|
* but instead via an API call.
|
||||||
|
*/
|
||||||
|
err = esp_rmaker_report_node_details();
|
||||||
|
#endif
|
||||||
|
ESP_LOGI(TAG, "Local Control Service Enabled");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_local_ctrl_service_disable(void)
|
||||||
|
{
|
||||||
|
const esp_rmaker_node_t *node = esp_rmaker_get_node();
|
||||||
|
esp_rmaker_device_t *local_ctrl_service = esp_rmaker_node_get_device_by_name(node, ESP_RMAKER_LOCAL_CTRL_DEVICE_NAME);
|
||||||
|
esp_err_t err = esp_rmaker_node_remove_device(node, local_ctrl_service);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to remove Local Control Service from Node. err=0x%x", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = esp_rmaker_device_delete(local_ctrl_service);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to delete Local Control Service. err=0x%x", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#ifndef CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE
|
||||||
|
/* Report node details only if the local control is not enabled via config option,
|
||||||
|
* but instead via an API call.
|
||||||
|
*/
|
||||||
|
err = esp_rmaker_report_node_details();
|
||||||
|
#endif
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||||
|
#define SRP_MAX_HOST_NAME_LEN 32
|
||||||
|
static char srp_host_name[SRP_MAX_HOST_NAME_LEN + 1];
|
||||||
|
|
||||||
|
static esp_err_t srp_client_set_host(const char *host_name)
|
||||||
|
{
|
||||||
|
if (!host_name || strlen(host_name) > 15) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
// Avoid adding the same host name multiple times
|
||||||
|
if (strcmp(srp_host_name, host_name) != 0) {
|
||||||
|
strncpy(srp_host_name, host_name, SRP_MAX_HOST_NAME_LEN);
|
||||||
|
srp_host_name[strnlen(host_name, SRP_MAX_HOST_NAME_LEN)] = 0;
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
otInstance *instance = esp_openthread_get_instance();
|
||||||
|
if (otSrpClientSetHostName(instance, srp_host_name) != OT_ERROR_NONE) {
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (otSrpClientEnableAutoHostAddress(instance) != OT_ERROR_NONE) {
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t srp_client_add_local_ctrl_service(const char *serv_name)
|
||||||
|
{
|
||||||
|
static uint8_t rainmaker_node_id_txt_value[30];
|
||||||
|
char *rmaker_node_id = esp_rmaker_get_node_id();
|
||||||
|
if (rmaker_node_id == NULL || strlen(rmaker_node_id) > sizeof(rainmaker_node_id_txt_value)) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
memcpy(rainmaker_node_id_txt_value, rmaker_node_id, strlen(rmaker_node_id));
|
||||||
|
const static uint8_t text_values[3][23] = {
|
||||||
|
{'/', 'e', 's', 'p', '_', 'l', 'o', 'c', 'a', 'l', '_', 'c', 't', 'r', 'l', '/', 'v', 'e', 'r', 's', 'i', 'o', 'n'},
|
||||||
|
{'/', 'e', 's', 'p', '_', 'l', 'o', 'c', 'a', 'l', '_', 'c', 't', 'r', 'l', '/', 's', 'e', 's', 's', 'i', 'o', 'n'},
|
||||||
|
{'/', 'e', 's', 'p', '_', 'l', 'o', 'c', 'a', 'l', '_', 'c', 't', 'r', 'l', '/', 'c', 'o', 'n', 't', 'r', 'o', 'l'}};
|
||||||
|
static otDnsTxtEntry txt_entries[4] = {
|
||||||
|
{
|
||||||
|
.mKey = "version_endpoint",
|
||||||
|
.mValue = text_values[0],
|
||||||
|
.mValueLength = 23,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mKey = "session_endpoint",
|
||||||
|
.mValue = text_values[1],
|
||||||
|
.mValueLength = 23,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mKey = "control_endpoint",
|
||||||
|
.mValue = text_values[2],
|
||||||
|
.mValueLength = 23,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mKey = "node_id",
|
||||||
|
.mValue = rainmaker_node_id_txt_value,
|
||||||
|
.mValueLength = sizeof(rainmaker_node_id_txt_value),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
txt_entries[3].mValueLength = (uint16_t)strlen(rmaker_node_id);
|
||||||
|
static char s_serv_name[30];
|
||||||
|
strncpy(s_serv_name, serv_name, strnlen(serv_name, sizeof(s_serv_name) - 1));
|
||||||
|
s_serv_name[strnlen(serv_name, sizeof(s_serv_name) - 1)] = 0;
|
||||||
|
static otSrpClientService srp_client_service = {
|
||||||
|
.mName = "_esp_local_ctrl._tcp",
|
||||||
|
.mInstanceName = (const char*)s_serv_name,
|
||||||
|
.mTxtEntries = txt_entries,
|
||||||
|
.mPort = CONFIG_ESP_RMAKER_LOCAL_CTRL_HTTP_PORT,
|
||||||
|
.mNumTxtEntries = 4,
|
||||||
|
.mNext = NULL,
|
||||||
|
.mLease = 0,
|
||||||
|
.mKeyLease = 0,
|
||||||
|
};
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
otInstance *instance = esp_openthread_get_instance();
|
||||||
|
// Try to remove the service registered before adding a new service. If the previous service is not removed,
|
||||||
|
// Adding service will fail with a duplicated instance error. This could happen when the device reboots, which
|
||||||
|
// might result in the wrong resolved IP addresss on the phone app side.
|
||||||
|
(void)otSrpClientRemoveService(instance, &srp_client_service);
|
||||||
|
if (otSrpClientAddService(instance, &srp_client_service) != OT_ERROR_NONE) {
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
otSrpClientEnableAutoStartMode(instance, NULL, NULL);
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t srp_client_clean_up()
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
otInstance *instance = esp_openthread_get_instance();
|
||||||
|
if (otSrpClientRemoveHostAndServices(instance, false, true) != OT_ERROR_NONE) {
|
||||||
|
ret = ESP_FAIL;
|
||||||
|
}
|
||||||
|
memset(srp_host_name, 0, sizeof(srp_host_name));
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||||
|
|
||||||
|
static esp_err_t __esp_rmaker_start_local_ctrl_service(const char *serv_name)
|
||||||
|
{
|
||||||
|
if (!serv_name) {
|
||||||
|
ESP_LOGE(TAG, "Service name cannot be empty.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Starting ESP Local control with HTTP Transport and security version: %d", esp_rmaker_local_ctrl_get_security_type());
|
||||||
|
/* Set the configuration */
|
||||||
|
static httpd_ssl_config_t https_conf = HTTPD_SSL_CONFIG_DEFAULT();
|
||||||
|
https_conf.transport_mode = HTTPD_SSL_TRANSPORT_INSECURE;
|
||||||
|
https_conf.port_insecure = CONFIG_ESP_RMAKER_LOCAL_CTRL_HTTP_PORT;
|
||||||
|
https_conf.httpd.ctrl_port = ESP_RMAKER_LOCAL_CTRL_HTTP_CTRL_PORT;
|
||||||
|
https_conf.httpd.stack_size = CONFIG_ESP_RMAKER_LOCAL_CTRL_STACK_SIZE;
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||||
|
mdns_init();
|
||||||
|
mdns_hostname_set(serv_name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||||
|
srp_client_set_host(serv_name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_local_ctrl_config_t config = {
|
||||||
|
.transport = ESP_LOCAL_CTRL_TRANSPORT_HTTPD,
|
||||||
|
.transport_config = {
|
||||||
|
.httpd = &https_conf
|
||||||
|
},
|
||||||
|
.handlers = {
|
||||||
|
/* User defined handler functions */
|
||||||
|
.get_prop_values = get_property_values,
|
||||||
|
.set_prop_values = set_property_values,
|
||||||
|
.usr_ctx = NULL,
|
||||||
|
.usr_ctx_free_fn = NULL
|
||||||
|
},
|
||||||
|
/* Maximum number of properties that may be set */
|
||||||
|
.max_properties = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
/* If sec1, add security type details to the config */
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
#define PROTOCOMM_SEC_DATA protocomm_security1_params_t
|
||||||
|
#else
|
||||||
|
#define PROTOCOMM_SEC_DATA protocomm_security_pop_t
|
||||||
|
#endif /* ESP_IDF_VERSION */
|
||||||
|
PROTOCOMM_SEC_DATA *pop = NULL;
|
||||||
|
#if ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE == 1
|
||||||
|
char *pop_str = esp_rmaker_local_ctrl_get_pop();
|
||||||
|
/* Note: pop_str shouldn't be freed. If it gets freed, the pointer which is internally copied in esp_local_ctrl_start() will become invalid which would cause corruption. */
|
||||||
|
|
||||||
|
int sec_ver = esp_rmaker_local_ctrl_get_security_type();
|
||||||
|
|
||||||
|
if (sec_ver != 0 && pop_str) {
|
||||||
|
pop = (PROTOCOMM_SEC_DATA *)MEM_CALLOC_EXTRAM(1, sizeof(PROTOCOMM_SEC_DATA));
|
||||||
|
if (!pop) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate pop");
|
||||||
|
free(pop_str);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
pop->data = (uint8_t *)pop_str;
|
||||||
|
pop->len = strlen(pop_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.proto_sec.version = sec_ver;
|
||||||
|
config.proto_sec.custom_handle = NULL;
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
config.proto_sec.sec_params = pop;
|
||||||
|
#else
|
||||||
|
config.proto_sec.pop = pop;
|
||||||
|
#endif /* ESP_IDF_VERSION */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Start esp_local_ctrl service */
|
||||||
|
ESP_ERROR_CHECK(esp_local_ctrl_start(&config));
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||||
|
/* The instance name of mdns service set by esp_local_ctrl_start is 'Local Control Service'.
|
||||||
|
* We should ensure that each end-device should have an unique instance name.
|
||||||
|
*/
|
||||||
|
mdns_service_instance_name_set("_esp_local_ctrl", "_tcp", serv_name);
|
||||||
|
/* Add node_id in mdns */
|
||||||
|
mdns_service_txt_item_set("_esp_local_ctrl", "_tcp", "node_id", esp_rmaker_get_node_id());
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||||
|
srp_client_add_local_ctrl_service(serv_name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (pop) {
|
||||||
|
free(pop);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "esp_local_ctrl service started with name : %s", serv_name);
|
||||||
|
|
||||||
|
/* Create the Node Config property */
|
||||||
|
esp_local_ctrl_prop_t node_config = {
|
||||||
|
.name = "config",
|
||||||
|
.type = PROP_TYPE_NODE_CONFIG,
|
||||||
|
.size = 0,
|
||||||
|
.flags = PROP_FLAG_READONLY,
|
||||||
|
.ctx = NULL,
|
||||||
|
.ctx_free_fn = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Create the Node Params property */
|
||||||
|
esp_local_ctrl_prop_t node_params = {
|
||||||
|
.name = "params",
|
||||||
|
.type = PROP_TYPE_NODE_PARAMS,
|
||||||
|
.size = 0,
|
||||||
|
.flags = 0,
|
||||||
|
.ctx = NULL,
|
||||||
|
.ctx_free_fn = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Now register the properties */
|
||||||
|
ESP_ERROR_CHECK(esp_local_ctrl_add_property(&node_config));
|
||||||
|
ESP_ERROR_CHECK(esp_local_ctrl_add_property(&node_params));
|
||||||
|
|
||||||
|
/* update the global status */
|
||||||
|
g_local_ctrl_is_started = true;
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_LOCAL_CTRL_STARTED, (void *)serv_name, strlen(serv_name) + 1);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_local_ctrl_prov_event_handler(void* arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void* event_data)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Event %"PRIu32, event_id);
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
if (event_base == NETWORK_PROV_EVENT) {
|
||||||
|
#else
|
||||||
|
if (event_base == WIFI_PROV_EVENT) {
|
||||||
|
#endif
|
||||||
|
switch (event_id) {
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
case NETWORK_PROV_START:
|
||||||
|
#else
|
||||||
|
case WIFI_PROV_START:
|
||||||
|
#endif
|
||||||
|
wait_for_provisioning = true;
|
||||||
|
break;
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
case NETWORK_PROV_DEINIT:
|
||||||
|
#else
|
||||||
|
case WIFI_PROV_DEINIT:
|
||||||
|
#endif
|
||||||
|
if (wait_for_provisioning == true) {
|
||||||
|
wait_for_provisioning = false;
|
||||||
|
if (g_serv_name) {
|
||||||
|
__esp_rmaker_start_local_ctrl_service(g_serv_name);
|
||||||
|
free(g_serv_name);
|
||||||
|
g_serv_name = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||||
|
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||||
|
#else
|
||||||
|
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||||
|
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool esp_rmaker_local_ctrl_service_started(void)
|
||||||
|
{
|
||||||
|
return g_local_ctrl_is_started;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_init_local_ctrl_service(void)
|
||||||
|
{
|
||||||
|
/* ESP Local Control uses protocomm_httpd, which is also used by SoftAP Provisioning.
|
||||||
|
* If local control is started before provisioning ends, it fails because only one protocomm_httpd
|
||||||
|
* instance is allowed at a time.
|
||||||
|
* So, we check for the NETWORK_PROV_START event, and if received, wait for the NETWORK_PROV_DEINIT
|
||||||
|
* event before starting local control.
|
||||||
|
* This would not be required in case of BLE Provisioning, but this code has no easy way of knowing
|
||||||
|
* what provisioning transport is being used and hence this logic will come into picture for both,
|
||||||
|
* SoftAP and BLE provisioning.
|
||||||
|
*/
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
esp_event_handler_register(NETWORK_PROV_EVENT, NETWORK_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler, NULL);
|
||||||
|
esp_event_handler_register(NETWORK_PROV_EVENT, NETWORK_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler, NULL);
|
||||||
|
#else
|
||||||
|
esp_event_handler_register(WIFI_PROV_EVENT, WIFI_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler, NULL);
|
||||||
|
esp_event_handler_register(WIFI_PROV_EVENT, WIFI_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler, NULL);
|
||||||
|
#endif
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_start_local_ctrl_service(const char *serv_name)
|
||||||
|
{
|
||||||
|
if (ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE == 1) {
|
||||||
|
esp_rmaker_local_ctrl_service_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wait_for_provisioning) {
|
||||||
|
return __esp_rmaker_start_local_ctrl_service(serv_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Waiting for Wi-Fi provisioning to finish.");
|
||||||
|
g_serv_name = strdup(serv_name);
|
||||||
|
if (g_serv_name) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_local_ctrl_enable(void)
|
||||||
|
{
|
||||||
|
if (g_local_ctrl_is_started) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Enabling Local Control");
|
||||||
|
esp_rmaker_init_local_ctrl_service();
|
||||||
|
esp_err_t err;
|
||||||
|
if ((err = esp_rmaker_start_local_ctrl_service(esp_rmaker_get_node_id())) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to start Local Control Service. err=0x%x", err);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_local_ctrl_disable(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Disabling Local Control");
|
||||||
|
if (g_serv_name) {
|
||||||
|
free(g_serv_name);
|
||||||
|
g_serv_name = NULL;
|
||||||
|
}
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||||
|
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||||
|
#else
|
||||||
|
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||||
|
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||||
|
#endif
|
||||||
|
if (!g_local_ctrl_is_started) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||||
|
mdns_free();
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||||
|
srp_client_clean_up();
|
||||||
|
#endif
|
||||||
|
esp_err_t err = esp_local_ctrl_stop();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE == 1) {
|
||||||
|
err = esp_rmaker_local_ctrl_service_disable();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* update the global status */
|
||||||
|
g_local_ctrl_is_started = false;
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_LOCAL_CTRL_STOPPED, NULL, 0);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
35
components/esp_rainmaker/src/core/esp_rmaker_mqtt_topics.h
Normal file
35
components/esp_rainmaker/src/core/esp_rmaker_mqtt_topics.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define NODE_CONFIG_TOPIC_RULE "esp_node_config"
|
||||||
|
#define NODE_PARAMS_LOCAL_TOPIC_RULE "esp_set_params"
|
||||||
|
#define NODE_PARAMS_LOCAL_INIT_RULE "esp_init_params"
|
||||||
|
#define NODE_PARAMS_ALERT_TOPIC_RULE "esp_node_alert"
|
||||||
|
#define USER_MAPPING_TOPIC_RULE "esp_user_node_mapping"
|
||||||
|
#define OTAFETCH_TOPIC_RULE "esp_node_otafetch"
|
||||||
|
#define OTASTATUS_TOPIC_RULE "esp_node_otastatus"
|
||||||
|
#define TIME_SERIES_DATA_TOPIC_RULE "esp_ts_ingest"
|
||||||
|
#define SIMPLE_TS_DATA_TOPIC_RULE "esp_simple_ts_ingest"
|
||||||
|
#define CMD_RESP_TOPIC_RULE "esp_cmd_resp"
|
||||||
|
|
||||||
|
|
||||||
|
#define USER_MAPPING_TOPIC_SUFFIX "user/mapping"
|
||||||
|
#define NODE_PARAMS_LOCAL_TOPIC_SUFFIX "params/local"
|
||||||
|
#define NODE_PARAMS_LOCAL_INIT_TOPIC_SUFFIX "params/local/init"
|
||||||
|
#define NODE_PARAMS_REMOTE_TOPIC_SUFFIX "params/remote"
|
||||||
|
#define TIME_SERIES_DATA_TOPIC_SUFFIX "tsdata"
|
||||||
|
#define SIMPLE_TS_DATA_TOPIC_SUFFIX "simple_tsdata"
|
||||||
|
#define NODE_PARAMS_ALERT_TOPIC_SUFFIX "alert"
|
||||||
|
#define NODE_CONFIG_TOPIC_SUFFIX "config"
|
||||||
|
#define OTAURL_TOPIC_SUFFIX "otaurl"
|
||||||
|
#define OTAFETCH_TOPIC_SUFFIX "otafetch"
|
||||||
|
#define OTASTATUS_TOPIC_SUFFIX "otastatus"
|
||||||
|
#define CMD_RESP_TOPIC_SUFFIX "from-node"
|
||||||
|
#define TO_NODE_TOPIC_SUFFIX "to-node"
|
||||||
|
#define INSIGHTS_TOPIC_SUFFIX "diagnostics/from-node"
|
||||||
|
|
||||||
|
#define MQTT_TOPIC_BUFFER_SIZE 150
|
||||||
375
components/esp_rainmaker/src/core/esp_rmaker_node.c
Normal file
375
components/esp_rainmaker/src/core/esp_rmaker_node.c
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "esp_idf_version.h"
|
||||||
|
#include <esp_log.h>
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
#include <esp_app_desc.h>
|
||||||
|
#else
|
||||||
|
#include <esp_ota_ops.h>
|
||||||
|
#endif
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_secure_boot_digest.h>
|
||||||
|
|
||||||
|
#include "esp_rmaker_internal.h"
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_node";
|
||||||
|
|
||||||
|
static void esp_rmaker_node_info_free(esp_rmaker_node_info_t *info)
|
||||||
|
{
|
||||||
|
if (info) {
|
||||||
|
if (info->name) {
|
||||||
|
free(info->name);
|
||||||
|
}
|
||||||
|
if (info->type) {
|
||||||
|
free(info->type);
|
||||||
|
}
|
||||||
|
if (info->model) {
|
||||||
|
free(info->model);
|
||||||
|
}
|
||||||
|
if (info->fw_version) {
|
||||||
|
free(info->fw_version);
|
||||||
|
}
|
||||||
|
if (info->subtype) {
|
||||||
|
free(info->subtype);
|
||||||
|
}
|
||||||
|
if (info->secure_boot_digest) {
|
||||||
|
esp_rmaker_secure_boot_digest_free(info->secure_boot_digest);
|
||||||
|
info->secure_boot_digest = NULL;
|
||||||
|
}
|
||||||
|
free(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_attribute_delete(esp_rmaker_attr_t *attr)
|
||||||
|
{
|
||||||
|
if (attr) {
|
||||||
|
if (attr->name) {
|
||||||
|
free(attr->name);
|
||||||
|
}
|
||||||
|
if (attr->value) {
|
||||||
|
free(attr->value);
|
||||||
|
}
|
||||||
|
free(attr);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_node_delete(const esp_rmaker_node_t *node)
|
||||||
|
{
|
||||||
|
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||||
|
if (_node) {
|
||||||
|
esp_rmaker_attr_t *attr = _node->attributes;
|
||||||
|
while (attr) {
|
||||||
|
esp_rmaker_attr_t *next_attr = attr->next;
|
||||||
|
esp_rmaker_attribute_delete(attr);
|
||||||
|
attr = next_attr;
|
||||||
|
}
|
||||||
|
_esp_rmaker_device_t *device = _node->devices;
|
||||||
|
while (device) {
|
||||||
|
_esp_rmaker_device_t *next_device = device->next;
|
||||||
|
device->parent = NULL;
|
||||||
|
esp_rmaker_device_delete((esp_rmaker_device_t *)device);
|
||||||
|
device = next_device;
|
||||||
|
}
|
||||||
|
/* Node ID is created in the context of esp_rmaker_init and just assigned
|
||||||
|
* here. So, we would not free it here.
|
||||||
|
*/
|
||||||
|
if (_node->node_id) {
|
||||||
|
_node->node_id = NULL;
|
||||||
|
}
|
||||||
|
if (_node->info) {
|
||||||
|
esp_rmaker_node_info_free(_node->info);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_node_t *esp_rmaker_node_create(const char *name, const char *type)
|
||||||
|
{
|
||||||
|
static bool node_created;
|
||||||
|
if (node_created) {
|
||||||
|
ESP_LOGE(TAG, "Node has already been created. Cannot create another");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!name || !type) {
|
||||||
|
ESP_LOGE(TAG, "Node Name and Type are mandatory.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
_esp_rmaker_node_t *node = MEM_CALLOC_EXTRAM(1, sizeof(_esp_rmaker_node_t));
|
||||||
|
if (!node) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for node.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
node->node_id = esp_rmaker_get_node_id();
|
||||||
|
if (!node->node_id) {
|
||||||
|
ESP_LOGE(TAG, "Failed to initialise Node Id. Please perform \"claiming\" using RainMaker CLI.");
|
||||||
|
goto node_create_err;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Node ID ----- %s", node->node_id);
|
||||||
|
|
||||||
|
node->info = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_node_info_t));
|
||||||
|
if (!node->info) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for node info.");
|
||||||
|
goto node_create_err;
|
||||||
|
}
|
||||||
|
node->info->name = strdup(name);
|
||||||
|
node->info->type = strdup(type);
|
||||||
|
const esp_app_desc_t *app_desc;
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
app_desc = esp_app_get_description();
|
||||||
|
#else
|
||||||
|
app_desc = esp_ota_get_app_description();
|
||||||
|
#endif
|
||||||
|
node->info->fw_version = strdup(app_desc->version);
|
||||||
|
node->info->model = strdup(app_desc->project_name);
|
||||||
|
if (esp_secure_boot_enabled()) {
|
||||||
|
node->info->secure_boot_digest = esp_rmaker_get_secure_boot_digest();
|
||||||
|
if (!node->info->secure_boot_digest) {
|
||||||
|
goto node_create_err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!node->info->name || !node->info->type
|
||||||
|
|| !node->info->fw_version || !node->info->model) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for node info.");
|
||||||
|
goto node_create_err;
|
||||||
|
}
|
||||||
|
node_created = true;
|
||||||
|
return (esp_rmaker_node_t *)node;
|
||||||
|
node_create_err:
|
||||||
|
esp_rmaker_node_delete((esp_rmaker_node_t *)node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_node_add_fw_version(const esp_rmaker_node_t *node, const char *fw_version)
|
||||||
|
{
|
||||||
|
if (!node || !fw_version) {
|
||||||
|
ESP_LOGE(TAG, "Node handle or fw version cannot be NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_rmaker_node_info_t *info = esp_rmaker_node_get_info(node);
|
||||||
|
if (!info) {
|
||||||
|
ESP_LOGE(TAG, "Failed to get Node Info.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (info->fw_version) {
|
||||||
|
free(info->fw_version);
|
||||||
|
}
|
||||||
|
info->fw_version = strdup(fw_version);
|
||||||
|
if (!info->fw_version) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for fw version.");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_node_add_model(const esp_rmaker_node_t *node, const char *model)
|
||||||
|
{
|
||||||
|
if (!node || !model) {
|
||||||
|
ESP_LOGE(TAG, "Node handle or model cannot be NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_rmaker_node_info_t *info = esp_rmaker_node_get_info(node);
|
||||||
|
if (!info) {
|
||||||
|
ESP_LOGE(TAG, "Failed to get Node Info.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (info->model) {
|
||||||
|
free(info->model);
|
||||||
|
}
|
||||||
|
info->model = strdup(model);
|
||||||
|
if (!info->model) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for node model.");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_node_add_subtype(const esp_rmaker_node_t *node, const char *subtype)
|
||||||
|
{
|
||||||
|
if (!node || !subtype) {
|
||||||
|
ESP_LOGE(TAG, "Node handle or subtype cannot be NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_rmaker_node_info_t *info = esp_rmaker_node_get_info(node);
|
||||||
|
if (!info) {
|
||||||
|
ESP_LOGE(TAG, "Failed to get Node Info.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (info->subtype) {
|
||||||
|
free(info->subtype);
|
||||||
|
}
|
||||||
|
info->subtype = strdup(subtype);
|
||||||
|
if (!info->subtype) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for node subtype.");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_node_add_attribute(const esp_rmaker_node_t *node, const char *attr_name, const char *value)
|
||||||
|
{
|
||||||
|
if (!node || !attr_name || !value) {
|
||||||
|
ESP_LOGE(TAG, "Node handle, attribute name or value cannot be NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_rmaker_attr_t *attr = ((_esp_rmaker_node_t *)node)->attributes;
|
||||||
|
while(attr && attr->next) {
|
||||||
|
if (strcmp(attr->name, attr_name) == 0) {
|
||||||
|
ESP_LOGE(TAG, "Node attribute with name %s already exists.", attr_name);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
attr = attr->next;
|
||||||
|
}
|
||||||
|
esp_rmaker_attr_t *new_attr = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_attr_t));
|
||||||
|
if (!new_attr) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create node attribute %s.", attr_name);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
new_attr->name = strdup(attr_name);
|
||||||
|
new_attr->value = strdup(value);
|
||||||
|
if (!new_attr->name || !new_attr->value) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for name/value for attribute %s.", attr_name);
|
||||||
|
esp_rmaker_attribute_delete(new_attr);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attr) {
|
||||||
|
attr->next = new_attr;
|
||||||
|
} else {
|
||||||
|
((_esp_rmaker_node_t *)node)->attributes = new_attr;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Node attribute %s created", attr_name);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_node_add_device(const esp_rmaker_node_t *node, const esp_rmaker_device_t *device)
|
||||||
|
{
|
||||||
|
if (!node || !device) {
|
||||||
|
ESP_LOGE(TAG, "Node or Device/Service handle cannot be NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||||
|
_esp_rmaker_device_t *_new_device = (_esp_rmaker_device_t *)device;
|
||||||
|
_esp_rmaker_device_t *_device = _node->devices;
|
||||||
|
while(_device) {
|
||||||
|
if (strcmp(_device->name, _new_device->name) == 0) {
|
||||||
|
ESP_LOGE(TAG, "%s with name %s already exists", _new_device->is_service ? "Service":"Device", _new_device->name);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (_device->next) {
|
||||||
|
_device = _device->next;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_device) {
|
||||||
|
_device->next = _new_device;
|
||||||
|
} else {
|
||||||
|
_node->devices = _new_device;
|
||||||
|
}
|
||||||
|
_new_device->parent = node;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_node_remove_device(const esp_rmaker_node_t *node, const esp_rmaker_device_t *device)
|
||||||
|
{
|
||||||
|
if (!node || !device) {
|
||||||
|
ESP_LOGE(TAG, "Node or Device/Service handle cannot be NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||||
|
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||||
|
|
||||||
|
_esp_rmaker_device_t *tmp_device = _node->devices;
|
||||||
|
_esp_rmaker_device_t *prev_device = NULL;
|
||||||
|
while(tmp_device) {
|
||||||
|
if (tmp_device == _device) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev_device = tmp_device;
|
||||||
|
tmp_device = tmp_device->next;
|
||||||
|
}
|
||||||
|
if (!tmp_device) {
|
||||||
|
ESP_LOGE(TAG, "Device %s not found in node %s", _device->name, _node->info->name);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (tmp_device == _node->devices) {
|
||||||
|
_node->devices = tmp_device->next;
|
||||||
|
} else {
|
||||||
|
prev_device->next = tmp_device->next;
|
||||||
|
}
|
||||||
|
tmp_device->parent = NULL;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_node_get_device_by_name(const esp_rmaker_node_t *node, const char *device_name)
|
||||||
|
{
|
||||||
|
if (!node || !device_name) {
|
||||||
|
ESP_LOGE(TAG, "Node handle or device name cannot be NULL");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
_esp_rmaker_device_t *device = ((_esp_rmaker_node_t *)node)->devices;
|
||||||
|
while(device) {
|
||||||
|
if (strcmp(device->name, device_name) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
device = device->next;
|
||||||
|
}
|
||||||
|
return (esp_rmaker_device_t *)device;
|
||||||
|
}
|
||||||
|
|
||||||
|
_esp_rmaker_device_t *esp_rmaker_node_get_first_device(const esp_rmaker_node_t *node)
|
||||||
|
{
|
||||||
|
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||||
|
if (!_node) {
|
||||||
|
ESP_LOGE(TAG, "Node handle cannot be NULL.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return _node->devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_node_info_t *esp_rmaker_node_get_info(const esp_rmaker_node_t *node)
|
||||||
|
{
|
||||||
|
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||||
|
if (!_node) {
|
||||||
|
ESP_LOGE(TAG, "Node handle cannot be NULL.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return _node->info;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_attr_t *esp_rmaker_node_get_first_attribute(const esp_rmaker_node_t *node)
|
||||||
|
{
|
||||||
|
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||||
|
if (!_node) {
|
||||||
|
ESP_LOGE(TAG, "Node handle cannot be NULL.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return _node->attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *esp_rmaker_node_get_id(const esp_rmaker_node_t *node)
|
||||||
|
{
|
||||||
|
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||||
|
if (!_node) {
|
||||||
|
ESP_LOGE(TAG, "Node handle cannot be NULL.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return _node->node_id;
|
||||||
|
}
|
||||||
191
components/esp_rainmaker/src/core/esp_rmaker_node_auth.c
Normal file
191
components/esp_rainmaker/src/core/esp_rmaker_node_auth.c
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <esp_idf_version.h>
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include "mbedtls/platform.h"
|
||||||
|
#include "mbedtls/pk.h"
|
||||||
|
#include "mbedtls/sha256.h"
|
||||||
|
#include "mbedtls/ecdsa.h"
|
||||||
|
#include "sha/sha_parallel_engine.h"
|
||||||
|
|
||||||
|
#include "esp_secure_cert_read.h"
|
||||||
|
#include "esp_rmaker_utils.h"
|
||||||
|
#include "esp_rmaker_client_data.h"
|
||||||
|
#include "mbedtls/x509_crt.h"
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_user_node_auth";
|
||||||
|
|
||||||
|
static int myrand(void *rng_state, unsigned char *output, size_t len)
|
||||||
|
{
|
||||||
|
esp_fill_random(output, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t to_hex_digit(unsigned val)
|
||||||
|
{
|
||||||
|
return (val < 10) ? ('0' + val) : ('a' + val - 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bytes_to_hex(uint8_t *src, uint8_t *dst, int in_len)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < in_len; i++) {
|
||||||
|
dst[2 * i] = to_hex_digit(src[i] >> 4);
|
||||||
|
dst[2 * i + 1] = to_hex_digit(src[i] & 0xf);
|
||||||
|
}
|
||||||
|
dst[2 * in_len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_node_auth_sign_msg(const void *challenge, size_t inlen, void **response, size_t *outlen)
|
||||||
|
{
|
||||||
|
if (!challenge || (inlen == 0)) {
|
||||||
|
ESP_LOGE(TAG, "function arguments challenge and inlen cannot be NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get private key */
|
||||||
|
char *priv_key = NULL;
|
||||||
|
size_t priv_key_len = 0;
|
||||||
|
#if CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||||
|
esp_secure_cert_key_type_t key_type;
|
||||||
|
esp_err_t err = esp_secure_cert_get_priv_key_type(&key_type);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to get the type of private key from secure cert partition, err:%d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (key_type == ESP_SECURE_CERT_INVALID_KEY) {
|
||||||
|
ESP_LOGE(TAG, "Private key type in secure cert partition is invalid");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
/* This flow is for devices supporting ECDSA peripheral */
|
||||||
|
if (key_type == ESP_SECURE_CERT_ECDSA_PERIPHERAL_KEY) {
|
||||||
|
#if CONFIG_USE_ESP32_ECDSA_PERIPHERAL
|
||||||
|
/* TODO: code for signing the challenge on devices that have a DS peripheral. */
|
||||||
|
return ESP_FAIL;
|
||||||
|
#else /* !CONFIG_USE_ESP32_ECDSA_PERIPHERAL */
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
#endif /* CONFIG_USE_ESP32_ECDSA_PERIPHERAL */
|
||||||
|
} else
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||||
|
{
|
||||||
|
/* This flow is for devices which do not support ECDSA peripheral */
|
||||||
|
#if !CONFIG_USE_ESP32_ECDSA_PERIPHERAL
|
||||||
|
priv_key = esp_rmaker_get_client_key();
|
||||||
|
priv_key_len = esp_rmaker_get_client_key_len();
|
||||||
|
#else /* CONFIG_USE_ESP32_ECDSA_PERIPHERAL */
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
#endif /* !CONFIG_USE_ESP32_ECDSA_PERIPHERAL */
|
||||||
|
}
|
||||||
|
if (!priv_key) {
|
||||||
|
ESP_LOGE(TAG, "Error getting private key");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
/* Calculate SHA of challenge */
|
||||||
|
uint8_t hash[32];
|
||||||
|
esp_sha(SHA2_256,(const unsigned char *)challenge, inlen, hash);
|
||||||
|
|
||||||
|
/* Sign the hash using RSA or ECDSA */
|
||||||
|
mbedtls_pk_context pk_ctx;
|
||||||
|
mbedtls_pk_init(&pk_ctx);
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
int ret = mbedtls_pk_parse_key(&pk_ctx, (uint8_t *)priv_key, priv_key_len, NULL, 0, NULL, 0);
|
||||||
|
#else /* !(ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) */
|
||||||
|
int ret = mbedtls_pk_parse_key(&pk_ctx, (uint8_t *)priv_key, priv_key_len, NULL, 0);
|
||||||
|
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) */
|
||||||
|
uint8_t *signature = NULL;
|
||||||
|
if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_RSA) {
|
||||||
|
ESP_LOGI(TAG, "RSA key found");
|
||||||
|
signature = (uint8_t *)MEM_CALLOC_EXTRAM(1, 256); // TODO: replace magic number 256
|
||||||
|
} else if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_ECKEY) {
|
||||||
|
ESP_LOGI(TAG, "ECDSA key found");
|
||||||
|
signature = (uint8_t *)MEM_CALLOC_EXTRAM(1, MBEDTLS_ECDSA_MAX_LEN);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "found different key: %d", mbedtls_pk_get_type(&pk_ctx));
|
||||||
|
}
|
||||||
|
if (!signature) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory to signature.");
|
||||||
|
mbedtls_pk_free(&pk_ctx);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
size_t slen = 0;
|
||||||
|
if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_ECKEY) {
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
ret = mbedtls_ecdsa_write_signature(mbedtls_pk_ec(pk_ctx), MBEDTLS_MD_SHA256, hash, sizeof(hash), signature, MBEDTLS_ECDSA_MAX_LEN, &slen, myrand, NULL);
|
||||||
|
#else /* !(ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) */
|
||||||
|
ret = mbedtls_ecdsa_write_signature(mbedtls_pk_ec(pk_ctx), MBEDTLS_MD_SHA256, hash, sizeof(hash), signature, &slen, myrand, NULL);
|
||||||
|
#endif /* (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) */
|
||||||
|
if (ret != 0) {
|
||||||
|
ESP_LOGE(TAG, "Error in writing signature. err = %d", ret);
|
||||||
|
free(signature);
|
||||||
|
mbedtls_pk_free(&pk_ctx);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
} else if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_RSA) {
|
||||||
|
mbedtls_rsa_context *rsa_ctx = mbedtls_pk_rsa(pk_ctx);
|
||||||
|
// rsa_ctx->MBEDTLS_PRIVATE(len) = 256;
|
||||||
|
mbedtls_rsa_set_padding(rsa_ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
|
||||||
|
ret = mbedtls_rsa_rsassa_pss_sign(rsa_ctx,
|
||||||
|
myrand,
|
||||||
|
NULL,
|
||||||
|
MBEDTLS_MD_SHA256,
|
||||||
|
sizeof(hash),
|
||||||
|
hash,
|
||||||
|
signature);
|
||||||
|
if (ret != 0) {
|
||||||
|
ESP_LOGE(TAG, "Error in writing signature. err = %d", ret);
|
||||||
|
free(signature);
|
||||||
|
mbedtls_pk_free(&pk_ctx);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
slen = mbedtls_rsa_get_len(rsa_ctx);
|
||||||
|
ESP_LOGI(TAG, "signature length %d", slen);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TEST_SIGNATURE_VERIFICATION
|
||||||
|
char *cert = esp_rmaker_get_client_cert();
|
||||||
|
mbedtls_x509_crt crt;
|
||||||
|
mbedtls_x509_crt_init(&crt);
|
||||||
|
mbedtls_x509_crt_parse(&crt, (const unsigned char *)cert, strlen(cert) + 1);
|
||||||
|
mbedtls_pk_context *pk = &crt.pk;
|
||||||
|
|
||||||
|
if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_ECKEY) {
|
||||||
|
ret = mbedtls_pk_verify(pk, MBEDTLS_MD_SHA256, hash, sizeof(hash), signature, sizeof(signature));
|
||||||
|
} else if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_RSA) {
|
||||||
|
mbedtls_pk_rsassa_pss_options opt = {
|
||||||
|
.mgf1_hash_id = MBEDTLS_MD_SHA256,
|
||||||
|
.expected_salt_len = MBEDTLS_RSA_SALT_LEN_ANY
|
||||||
|
};
|
||||||
|
ret = mbedtls_pk_verify_ext(MBEDTLS_PK_RSASSA_PSS, (const void *)&opt, pk, MBEDTLS_MD_SHA256, hash, sizeof(hash), signature, slen);
|
||||||
|
}
|
||||||
|
|
||||||
|
mbedtls_x509_crt_free(&crt);
|
||||||
|
free(cert);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGI(TAG, "Signature is valid");
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Signature verification failed %d", ret);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Convert hex stream to bytes */
|
||||||
|
#define BYTE_ENCODED_SIGNATURE_LEN ((2 * slen) + 1) /* +1 for null character */
|
||||||
|
char *char_signature = (char *)MEM_ALLOC_EXTRAM(BYTE_ENCODED_SIGNATURE_LEN);
|
||||||
|
if (!char_signature) {
|
||||||
|
ESP_LOGE(TAG, "Error in allocating memory for challenge response.");
|
||||||
|
free(signature);
|
||||||
|
mbedtls_pk_free(&pk_ctx);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
bytes_to_hex(signature, (uint8_t *)char_signature, slen);
|
||||||
|
mbedtls_pk_free(&pk_ctx);
|
||||||
|
free(signature);
|
||||||
|
/* Set output variables */
|
||||||
|
*(char **)response = char_signature;
|
||||||
|
*outlen = 2 * slen; /* hex encoding takes 2 bytes per input byte */
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) */
|
||||||
311
components/esp_rainmaker/src/core/esp_rmaker_node_config.c
Normal file
311
components/esp_rainmaker/src/core/esp_rmaker_node_config.c
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <json_generator.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include "esp_rmaker_internal.h"
|
||||||
|
#include "esp_rmaker_mqtt.h"
|
||||||
|
#include "esp_rmaker_mqtt_topics.h"
|
||||||
|
#include <esp_rmaker_secure_boot_digest.h>
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
#include <esp_app_desc.h>
|
||||||
|
#else
|
||||||
|
#include <esp_ota_ops.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NODE_CONFIG_TOPIC_SUFFIX "config"
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_node_config";
|
||||||
|
static esp_err_t esp_rmaker_report_info(json_gen_str_t *jptr)
|
||||||
|
{
|
||||||
|
/* TODO: Error handling */
|
||||||
|
esp_rmaker_node_info_t *info = esp_rmaker_node_get_info(esp_rmaker_get_node());
|
||||||
|
json_gen_obj_set_string(jptr, "node_id", esp_rmaker_get_node_id());
|
||||||
|
json_gen_obj_set_string(jptr, "config_version", ESP_RMAKER_CONFIG_VERSION);
|
||||||
|
json_gen_push_object(jptr, "info");
|
||||||
|
json_gen_obj_set_string(jptr, "name", info->name);
|
||||||
|
json_gen_obj_set_string(jptr, "fw_version", info->fw_version);
|
||||||
|
json_gen_obj_set_string(jptr, "type", info->type);
|
||||||
|
if (info->subtype) {
|
||||||
|
json_gen_obj_set_string(jptr, "subtype", info->subtype);
|
||||||
|
}
|
||||||
|
json_gen_obj_set_string(jptr, "model", info->model);
|
||||||
|
const esp_app_desc_t *app_desc;
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
app_desc = esp_app_get_description();
|
||||||
|
#else
|
||||||
|
app_desc = esp_ota_get_app_description();
|
||||||
|
#endif
|
||||||
|
json_gen_obj_set_string(jptr, "project_name", (char *)app_desc->project_name);
|
||||||
|
json_gen_obj_set_string(jptr, "platform", CONFIG_IDF_TARGET);
|
||||||
|
#ifdef CONFIG_SECURE_BOOT_V2_ENABLED
|
||||||
|
json_gen_push_object(jptr, "secure_boot_digest");
|
||||||
|
for (int i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||||
|
char key_name[3];
|
||||||
|
snprintf(key_name, sizeof(key_name), "k%d", i);
|
||||||
|
key_name[2] = '\0';
|
||||||
|
if (info->secure_boot_digest[i] == NULL) {
|
||||||
|
json_gen_obj_set_null(jptr, key_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
json_gen_obj_set_string(jptr, key_name, info->secure_boot_digest[i]);
|
||||||
|
}
|
||||||
|
json_gen_pop_object(jptr);
|
||||||
|
#endif
|
||||||
|
json_gen_pop_object(jptr);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_report_attribute(esp_rmaker_attr_t *attr, json_gen_str_t *jptr)
|
||||||
|
{
|
||||||
|
json_gen_start_object(jptr);
|
||||||
|
json_gen_obj_set_string(jptr, "name", attr->name);
|
||||||
|
json_gen_obj_set_string(jptr, "value", attr->value);
|
||||||
|
json_gen_end_object(jptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_report_node_attributes(json_gen_str_t *jptr)
|
||||||
|
{
|
||||||
|
esp_rmaker_attr_t *attr = esp_rmaker_node_get_first_attribute(esp_rmaker_get_node());
|
||||||
|
if (!attr) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
json_gen_push_array(jptr, "attributes");
|
||||||
|
while (attr) {
|
||||||
|
esp_rmaker_report_attribute(attr, jptr);
|
||||||
|
attr = attr->next;
|
||||||
|
}
|
||||||
|
json_gen_pop_array(jptr);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_report_value(const esp_rmaker_param_val_t *val, char *key, json_gen_str_t *jptr)
|
||||||
|
{
|
||||||
|
if (!key || !jptr) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (!val) {
|
||||||
|
json_gen_obj_set_null(jptr, key);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
switch (val->type) {
|
||||||
|
case RMAKER_VAL_TYPE_BOOLEAN:
|
||||||
|
json_gen_obj_set_bool(jptr, key, val->val.b);
|
||||||
|
break;
|
||||||
|
case RMAKER_VAL_TYPE_INTEGER:
|
||||||
|
json_gen_obj_set_int(jptr, key, val->val.i);
|
||||||
|
break;
|
||||||
|
case RMAKER_VAL_TYPE_FLOAT:
|
||||||
|
json_gen_obj_set_float(jptr, key, val->val.f);
|
||||||
|
break;
|
||||||
|
case RMAKER_VAL_TYPE_STRING:
|
||||||
|
json_gen_obj_set_string(jptr, key, val->val.s);
|
||||||
|
break;
|
||||||
|
case RMAKER_VAL_TYPE_OBJECT:
|
||||||
|
json_gen_push_object_str(jptr, key, val->val.s);
|
||||||
|
break;
|
||||||
|
case RMAKER_VAL_TYPE_ARRAY:
|
||||||
|
json_gen_push_array_str(jptr, key, val->val.s);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_report_data_type(esp_rmaker_val_type_t type, char *data_type_key, json_gen_str_t *jptr)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case RMAKER_VAL_TYPE_BOOLEAN:
|
||||||
|
json_gen_obj_set_string(jptr, data_type_key, "bool");
|
||||||
|
break;
|
||||||
|
case RMAKER_VAL_TYPE_INTEGER:
|
||||||
|
json_gen_obj_set_string(jptr, data_type_key, "int");
|
||||||
|
break;
|
||||||
|
case RMAKER_VAL_TYPE_FLOAT:
|
||||||
|
json_gen_obj_set_string(jptr, data_type_key, "float");
|
||||||
|
break;
|
||||||
|
case RMAKER_VAL_TYPE_STRING:
|
||||||
|
json_gen_obj_set_string(jptr, data_type_key, "string");
|
||||||
|
break;
|
||||||
|
case RMAKER_VAL_TYPE_OBJECT:
|
||||||
|
json_gen_obj_set_string(jptr, data_type_key, "object");
|
||||||
|
break;
|
||||||
|
case RMAKER_VAL_TYPE_ARRAY:
|
||||||
|
json_gen_obj_set_string(jptr, data_type_key, "array");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
json_gen_obj_set_string(jptr, data_type_key, "invalid");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_report_param_config(_esp_rmaker_param_t *param, json_gen_str_t *jptr)
|
||||||
|
{
|
||||||
|
json_gen_start_object(jptr);
|
||||||
|
if (param->name) {
|
||||||
|
json_gen_obj_set_string(jptr, "name", param->name);
|
||||||
|
}
|
||||||
|
if (param->type) {
|
||||||
|
json_gen_obj_set_string(jptr, "type", param->type);
|
||||||
|
}
|
||||||
|
esp_rmaker_report_data_type(param->val.type, "data_type", jptr);
|
||||||
|
json_gen_push_array(jptr, "properties");
|
||||||
|
if (param->prop_flags & PROP_FLAG_READ) {
|
||||||
|
json_gen_arr_set_string(jptr, "read");
|
||||||
|
}
|
||||||
|
if (param->prop_flags & PROP_FLAG_WRITE) {
|
||||||
|
json_gen_arr_set_string(jptr, "write");
|
||||||
|
}
|
||||||
|
/* A parameter cannot have both, PROP_FLAG_TIME_SERIES and PROP_FLAG_SIMPLE_TIME_SERIES */
|
||||||
|
if (param->prop_flags & PROP_FLAG_TIME_SERIES) {
|
||||||
|
json_gen_arr_set_string(jptr, "time_series");
|
||||||
|
} else if (param->prop_flags & PROP_FLAG_SIMPLE_TIME_SERIES) {
|
||||||
|
json_gen_arr_set_string(jptr, "simple_ts");
|
||||||
|
}
|
||||||
|
json_gen_pop_array(jptr);
|
||||||
|
if (param->bounds) {
|
||||||
|
json_gen_push_object(jptr, "bounds");
|
||||||
|
esp_rmaker_report_value(¶m->bounds->min, "min", jptr);
|
||||||
|
esp_rmaker_report_value(¶m->bounds->max, "max", jptr);
|
||||||
|
if (param->bounds->step.val.i) {
|
||||||
|
esp_rmaker_report_value(¶m->bounds->step, "step", jptr);
|
||||||
|
}
|
||||||
|
json_gen_pop_object(jptr);
|
||||||
|
}
|
||||||
|
if (param->valid_str_list) {
|
||||||
|
json_gen_push_array(jptr, "valid_strs");
|
||||||
|
for (int i = 0; i < param->valid_str_list->str_list_cnt; i++) {
|
||||||
|
json_gen_arr_set_string(jptr, (char *)param->valid_str_list->str_list[i]);
|
||||||
|
}
|
||||||
|
json_gen_pop_array(jptr);
|
||||||
|
}
|
||||||
|
if (param->ui_type) {
|
||||||
|
json_gen_obj_set_string(jptr, "ui_type", param->ui_type);
|
||||||
|
}
|
||||||
|
json_gen_end_object(jptr);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_report_devices_or_services(json_gen_str_t *jptr, char *key)
|
||||||
|
{
|
||||||
|
_esp_rmaker_device_t *device = esp_rmaker_node_get_first_device(esp_rmaker_get_node());
|
||||||
|
if (!device) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
bool is_service = false;
|
||||||
|
if (strcmp(key, "services") == 0) {
|
||||||
|
is_service = true;
|
||||||
|
}
|
||||||
|
json_gen_push_array(jptr, key);
|
||||||
|
while (device) {
|
||||||
|
if (device->is_service == is_service) {
|
||||||
|
json_gen_start_object(jptr);
|
||||||
|
json_gen_obj_set_string(jptr, "name", device->name);
|
||||||
|
if (device->type) {
|
||||||
|
json_gen_obj_set_string(jptr, "type", device->type);
|
||||||
|
}
|
||||||
|
if (device->subtype) {
|
||||||
|
json_gen_obj_set_string(jptr, "subtype", device->subtype);
|
||||||
|
}
|
||||||
|
if (device->model) {
|
||||||
|
json_gen_obj_set_string(jptr, "model", device->model);
|
||||||
|
}
|
||||||
|
if (device->attributes) {
|
||||||
|
json_gen_push_array(jptr, "attributes");
|
||||||
|
esp_rmaker_attr_t *attr = device->attributes;
|
||||||
|
while (attr) {
|
||||||
|
esp_rmaker_report_attribute(attr, jptr);
|
||||||
|
attr = attr->next;
|
||||||
|
}
|
||||||
|
json_gen_pop_array(jptr);
|
||||||
|
}
|
||||||
|
if (device->primary) {
|
||||||
|
json_gen_obj_set_string(jptr, "primary", device->primary->name);
|
||||||
|
}
|
||||||
|
if (device->params) {
|
||||||
|
json_gen_push_array(jptr, "params");
|
||||||
|
_esp_rmaker_param_t *param = device->params;
|
||||||
|
while (param) {
|
||||||
|
esp_rmaker_report_param_config(param, jptr);
|
||||||
|
param = param->next;
|
||||||
|
}
|
||||||
|
json_gen_pop_array(jptr);
|
||||||
|
}
|
||||||
|
json_gen_end_object(jptr);
|
||||||
|
}
|
||||||
|
device = device->next;
|
||||||
|
}
|
||||||
|
json_gen_pop_array(jptr);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __esp_rmaker_get_node_config(char *buf, size_t buf_size)
|
||||||
|
{
|
||||||
|
json_gen_str_t jstr;
|
||||||
|
json_gen_str_start(&jstr, buf, buf_size, NULL, NULL);
|
||||||
|
json_gen_start_object(&jstr);
|
||||||
|
esp_rmaker_report_info(&jstr);
|
||||||
|
esp_rmaker_report_node_attributes(&jstr);
|
||||||
|
esp_rmaker_report_devices_or_services(&jstr, "devices");
|
||||||
|
esp_rmaker_report_devices_or_services(&jstr, "services");
|
||||||
|
if (json_gen_end_object(&jstr) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return json_gen_str_end(&jstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *esp_rmaker_get_node_config(void)
|
||||||
|
{
|
||||||
|
/* Setting buffer to NULL and size to 0 just to get the required buffer size */
|
||||||
|
int req_size = __esp_rmaker_get_node_config(NULL, 0);
|
||||||
|
if (req_size < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to get required size for Node config JSON.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char *node_config = MEM_CALLOC_EXTRAM(1, req_size);
|
||||||
|
if (!node_config) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate %d bytes for node config", req_size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (__esp_rmaker_get_node_config(node_config, req_size) < 0) {
|
||||||
|
free(node_config);
|
||||||
|
ESP_LOGE(TAG, "Failed to generate Node config JSON.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Generated Node config of length %d", req_size);
|
||||||
|
return node_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_report_node_config()
|
||||||
|
{
|
||||||
|
char *publish_payload = esp_rmaker_get_node_config();
|
||||||
|
if (!publish_payload) {
|
||||||
|
ESP_LOGE(TAG, "Could not get node configuration for reporting to cloud");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||||
|
esp_rmaker_create_mqtt_topic(publish_topic, MQTT_TOPIC_BUFFER_SIZE, NODE_CONFIG_TOPIC_SUFFIX, NODE_CONFIG_TOPIC_RULE);
|
||||||
|
ESP_LOGD(TAG, "Reporting Node Configuration of length %lu bytes.", (unsigned long) strlen(publish_payload));
|
||||||
|
ESP_LOGD(TAG, "%s", publish_payload);
|
||||||
|
esp_err_t ret = esp_rmaker_mqtt_publish(publish_topic, publish_payload, strlen(publish_payload),
|
||||||
|
RMAKER_MQTT_QOS1, NULL);
|
||||||
|
free(publish_payload);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
1001
components/esp_rainmaker/src/core/esp_rmaker_param.c
Normal file
1001
components/esp_rainmaker/src/core/esp_rmaker_param.c
Normal file
File diff suppressed because it is too large
Load Diff
548
components/esp_rainmaker/src/core/esp_rmaker_scenes.c
Normal file
548
components/esp_rainmaker/src/core/esp_rmaker_scenes.c
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <json_parser.h>
|
||||||
|
#include <json_generator.h>
|
||||||
|
|
||||||
|
#include <esp_rmaker_internal.h>
|
||||||
|
#include <esp_rmaker_standard_services.h>
|
||||||
|
#include <esp_rmaker_standard_types.h>
|
||||||
|
#include <esp_rmaker_scenes.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
|
||||||
|
#define MAX_ID_LEN 8
|
||||||
|
#define MAX_NAME_LEN 32
|
||||||
|
#define MAX_INFO_LEN 100
|
||||||
|
#define MAX_OPERATION_LEN 10
|
||||||
|
#define MAX_SCENES CONFIG_ESP_RMAKER_SCENES_MAX_SCENES
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_scenes";
|
||||||
|
|
||||||
|
typedef struct esp_rmaker_scene_action {
|
||||||
|
void *data;
|
||||||
|
size_t data_len;
|
||||||
|
} esp_rmaker_scene_action_t;
|
||||||
|
|
||||||
|
typedef struct esp_rmaker_scene {
|
||||||
|
char name[MAX_NAME_LEN + 1]; /* +1 for NULL termination */
|
||||||
|
char id[MAX_ID_LEN + 1]; /* +1 for NULL termination */
|
||||||
|
/* Info is used to store additional information, it is limited to MAX_INFO_LEN bytes. */
|
||||||
|
char *info;
|
||||||
|
/* Flags can be used to identify the scene. */
|
||||||
|
uint32_t flags;
|
||||||
|
esp_rmaker_scene_action_t action;
|
||||||
|
struct esp_rmaker_scene *next;
|
||||||
|
} esp_rmaker_scene_t;
|
||||||
|
|
||||||
|
typedef enum scenes_operation {
|
||||||
|
OPERATION_INVALID,
|
||||||
|
OPERATION_ADD,
|
||||||
|
OPERATION_EDIT,
|
||||||
|
OPERATION_REMOVE,
|
||||||
|
OPERATION_ACTIVATE,
|
||||||
|
OPERATION_DEACTIVATE,
|
||||||
|
} scenes_operation_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_rmaker_scene_t *scenes_list;
|
||||||
|
int total_scenes;
|
||||||
|
bool deactivate_support;
|
||||||
|
esp_rmaker_device_t *scenes_service;
|
||||||
|
} esp_rmaker_scenes_priv_data_t;
|
||||||
|
|
||||||
|
static esp_rmaker_scenes_priv_data_t *scenes_priv_data;
|
||||||
|
|
||||||
|
static void esp_rmaker_scenes_free(esp_rmaker_scene_t *scene)
|
||||||
|
{
|
||||||
|
if (!scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (scene->action.data) {
|
||||||
|
free(scene->action.data);
|
||||||
|
}
|
||||||
|
if (scene->info) {
|
||||||
|
free(scene->info);
|
||||||
|
}
|
||||||
|
free(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_rmaker_scene_t *esp_rmaker_scenes_get_scene_from_id(const char *id)
|
||||||
|
{
|
||||||
|
if (!id) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
esp_rmaker_scene_t *scene = scenes_priv_data->scenes_list;
|
||||||
|
while(scene) {
|
||||||
|
if (strncmp(id, scene->id, sizeof(scene->id)) == 0) {
|
||||||
|
ESP_LOGD(TAG, "Scene with id %s found in list for get.", id);
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
scene = scene->next;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Scene with id %s not found in list for get.", id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_scenes_add_to_list(esp_rmaker_scene_t *scene)
|
||||||
|
{
|
||||||
|
if (!scene) {
|
||||||
|
ESP_LOGE(TAG, "Scene is NULL. Not adding to list.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_rmaker_scenes_get_scene_from_id(scene->id) != NULL) {
|
||||||
|
ESP_LOGI(TAG, "Scene with id %s already added to list. Not adding again.", scene->id);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
/* Parse list */
|
||||||
|
esp_rmaker_scene_t *prev_scene = scenes_priv_data->scenes_list;
|
||||||
|
while(prev_scene) {
|
||||||
|
if (prev_scene->next) {
|
||||||
|
prev_scene = prev_scene->next;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add to list */
|
||||||
|
if (prev_scene) {
|
||||||
|
prev_scene->next = scene;
|
||||||
|
} else {
|
||||||
|
scenes_priv_data->scenes_list = scene;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Scene with id %s added to list.", scene->id);
|
||||||
|
scenes_priv_data->total_scenes++;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_scenes_remove_from_list(esp_rmaker_scene_t *scene)
|
||||||
|
{
|
||||||
|
if (!scene) {
|
||||||
|
ESP_LOGE(TAG, "Scene is NULL. Not removing from list.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
/* Parse list */
|
||||||
|
esp_rmaker_scene_t *curr_scene = scenes_priv_data->scenes_list;
|
||||||
|
esp_rmaker_scene_t *prev_scene = curr_scene;
|
||||||
|
while(curr_scene) {
|
||||||
|
if (strncmp(scene->id, curr_scene->id, sizeof(scene->id)) == 0) {
|
||||||
|
ESP_LOGD(TAG, "Scene with id %s found in list for removing", scene->id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev_scene = curr_scene;
|
||||||
|
curr_scene = curr_scene->next;
|
||||||
|
}
|
||||||
|
if (!curr_scene) {
|
||||||
|
ESP_LOGE(TAG, "Scene with id %s not found in list. Not removing.", scene->id);
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove from list */
|
||||||
|
if (curr_scene == scenes_priv_data->scenes_list) {
|
||||||
|
scenes_priv_data->scenes_list = curr_scene->next;
|
||||||
|
} else {
|
||||||
|
prev_scene->next = curr_scene->next;
|
||||||
|
}
|
||||||
|
scenes_priv_data->total_scenes--;
|
||||||
|
ESP_LOGD(TAG, "Scene with id %s removed from list.", scene->id);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes_operation_t esp_rmaker_scenes_get_operation_from_str(char *operation)
|
||||||
|
{
|
||||||
|
if (!operation) {
|
||||||
|
return OPERATION_INVALID;
|
||||||
|
}
|
||||||
|
if (strncmp(operation, "add", strlen(operation)) == 0) {
|
||||||
|
return OPERATION_ADD;
|
||||||
|
} else if (strncmp(operation, "edit", strlen(operation)) == 0) {
|
||||||
|
return OPERATION_EDIT;
|
||||||
|
} else if (strncmp(operation, "remove", strlen(operation)) == 0) {
|
||||||
|
return OPERATION_REMOVE;
|
||||||
|
} else if (strncmp(operation, "activate", strlen(operation)) == 0) {
|
||||||
|
return OPERATION_ACTIVATE;
|
||||||
|
} else if (strncmp(operation, "deactivate", strlen(operation)) == 0) {
|
||||||
|
return OPERATION_DEACTIVATE;
|
||||||
|
}
|
||||||
|
return OPERATION_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static scenes_operation_t esp_rmaker_scenes_parse_operation(jparse_ctx_t *jctx, char *id)
|
||||||
|
{
|
||||||
|
char operation_str[MAX_OPERATION_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||||
|
scenes_operation_t operation = OPERATION_INVALID;
|
||||||
|
json_obj_get_string(jctx, "operation", operation_str, sizeof(operation_str));
|
||||||
|
if (strlen(operation_str) <= 0) {
|
||||||
|
ESP_LOGE(TAG, "Operation not found in scene with id: %s", id);
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
operation = esp_rmaker_scenes_get_operation_from_str(operation_str);
|
||||||
|
if (operation == OPERATION_EDIT) {
|
||||||
|
/* Get scene temporarily */
|
||||||
|
if (esp_rmaker_scenes_get_scene_from_id(id) == NULL) {
|
||||||
|
/* Operation is edit, but scene not present already. Consider this as add. */
|
||||||
|
ESP_LOGD(TAG, "Operation is edit, but scene with id %s not found. Changing the operation to add.", id);
|
||||||
|
operation = OPERATION_ADD;
|
||||||
|
}
|
||||||
|
} else if (operation == OPERATION_INVALID) {
|
||||||
|
ESP_LOGE(TAG, "Invalid scene operation found: %s", operation_str);
|
||||||
|
}
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_scenes_parse_info_and_flags(jparse_ctx_t *jctx, char **info, uint32_t *flags)
|
||||||
|
{
|
||||||
|
char _info[MAX_INFO_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||||
|
int _flags = 0;
|
||||||
|
|
||||||
|
int err_code = json_obj_get_string(jctx, "info", _info, sizeof(_info));
|
||||||
|
if (err_code == OS_SUCCESS) {
|
||||||
|
if (*info) {
|
||||||
|
free(*info);
|
||||||
|
*info = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = strlen(_info);
|
||||||
|
if (len > 0) {
|
||||||
|
/* +1 for NULL termination */
|
||||||
|
*info = (char *)MEM_CALLOC_EXTRAM(1, len + 1);
|
||||||
|
if (*info) {
|
||||||
|
memcpy(*info, _info, len + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err_code = json_obj_get_int(jctx, "flags", &_flags);
|
||||||
|
if (err_code == OS_SUCCESS) {
|
||||||
|
if (flags) {
|
||||||
|
*flags = _flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_scenes_parse_action(jparse_ctx_t *jctx, esp_rmaker_scene_action_t *action)
|
||||||
|
{
|
||||||
|
int data_len = 0;
|
||||||
|
json_obj_get_object_strlen(jctx, "action", &data_len);
|
||||||
|
if (data_len <= 0) {
|
||||||
|
ESP_LOGD(TAG, "Action not found in JSON");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
action->data_len = data_len + 1;
|
||||||
|
|
||||||
|
if (action->data) {
|
||||||
|
free(action->data);
|
||||||
|
}
|
||||||
|
action->data = (void *)MEM_CALLOC_EXTRAM(1, action->data_len);
|
||||||
|
if (!action->data) {
|
||||||
|
ESP_LOGE(TAG, "Could not allocate action");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
json_obj_get_object_str(jctx, "action", action->data, action->data_len);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_rmaker_scene_t *esp_rmaker_scenes_find_or_create(jparse_ctx_t *jctx, char *id, scenes_operation_t operation)
|
||||||
|
{
|
||||||
|
char name[MAX_NAME_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||||
|
esp_rmaker_scene_t *scene = NULL;
|
||||||
|
if (operation == OPERATION_ADD) {
|
||||||
|
/* Checking if scene with same id already exists. */
|
||||||
|
scene = esp_rmaker_scenes_get_scene_from_id(id);
|
||||||
|
if (scene) {
|
||||||
|
ESP_LOGE(TAG, "Scene with id %s already exists. Not adding it again.", id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get name */
|
||||||
|
json_obj_get_string(jctx, "name", name, sizeof(name));
|
||||||
|
if (strlen(name) <= 0) {
|
||||||
|
ESP_LOGE(TAG, "Name not found for scene with id: %s", id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is a new scene. Fill it. */
|
||||||
|
scene = (esp_rmaker_scene_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_scene_t));
|
||||||
|
if (!scene) {
|
||||||
|
ESP_LOGE(TAG, "Couldn't allocate scene with id: %s", id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strlcpy(scene->id, id, sizeof(scene->id));
|
||||||
|
strlcpy(scene->name, name, sizeof(scene->name));
|
||||||
|
} else {
|
||||||
|
/* This scene should already be present */
|
||||||
|
scene = esp_rmaker_scenes_get_scene_from_id(id);
|
||||||
|
if (!scene) {
|
||||||
|
ESP_LOGE(TAG, "Scene with id %s not found", id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get name */
|
||||||
|
if (operation == OPERATION_EDIT) {
|
||||||
|
json_obj_get_string(jctx, "name", name, sizeof(name));
|
||||||
|
if (strlen(name) > 0) {
|
||||||
|
/* If there is name in the request, replace the name in the scene with this new one */
|
||||||
|
memset(scene->name, 0, sizeof(scene->name));
|
||||||
|
strlcpy(scene->name, name, sizeof(scene->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_scenes_perform_operation(esp_rmaker_scene_t *scene, scenes_operation_t operation)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
switch (operation) {
|
||||||
|
case OPERATION_ADD:
|
||||||
|
if (scenes_priv_data->total_scenes < MAX_SCENES) {
|
||||||
|
err = esp_rmaker_scenes_add_to_list(scene);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Max sceness (%d) reached. Not adding this scene with id %s", MAX_SCENES,
|
||||||
|
scene->id);
|
||||||
|
err = ESP_FAIL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPERATION_EDIT:
|
||||||
|
/* Nothing to do here. name, info, action have already been handled */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPERATION_REMOVE:
|
||||||
|
err = esp_rmaker_scenes_remove_from_list(scene);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
esp_rmaker_scenes_free(scene);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPERATION_ACTIVATE:
|
||||||
|
err = esp_rmaker_handle_set_params(scene->action.data, scene->action.data_len, ESP_RMAKER_REQ_SRC_SCENE_ACTIVATE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPERATION_DEACTIVATE:
|
||||||
|
if (scenes_priv_data->deactivate_support) {
|
||||||
|
err = esp_rmaker_handle_set_params(scene->action.data, scene->action.data_len, ESP_RMAKER_REQ_SRC_SCENE_DEACTIVATE);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Deactivate operation not supported.");
|
||||||
|
err = ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "Invalid Operation: %d", operation);
|
||||||
|
err = ESP_FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_scenes_parse_json(void *data, size_t data_len, esp_rmaker_req_src_t src,
|
||||||
|
bool *report_params)
|
||||||
|
{
|
||||||
|
char id[MAX_ID_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||||
|
scenes_operation_t operation = OPERATION_INVALID;
|
||||||
|
int current_scene = 0;
|
||||||
|
esp_rmaker_scene_t *scene = NULL;
|
||||||
|
|
||||||
|
/* Get details from JSON */
|
||||||
|
jparse_ctx_t jctx;
|
||||||
|
if (json_parse_start(&jctx, (char *)data, data_len) != 0) {
|
||||||
|
ESP_LOGE(TAG, "Json parse start failed");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse all scenes */
|
||||||
|
while(json_arr_get_object(&jctx, current_scene) == 0) {
|
||||||
|
/* Get ID */
|
||||||
|
json_obj_get_string(&jctx, "id", id, sizeof(id));
|
||||||
|
if (strlen(id) <= 0) {
|
||||||
|
ESP_LOGE(TAG, "ID not found in scene JSON");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get operation */
|
||||||
|
if (src == ESP_RMAKER_REQ_SRC_INIT) {
|
||||||
|
/* Scene loaded from NVS. Add it */
|
||||||
|
operation = OPERATION_ADD;
|
||||||
|
} else {
|
||||||
|
operation = esp_rmaker_scenes_parse_operation(&jctx, id);
|
||||||
|
if (operation == OPERATION_INVALID) {
|
||||||
|
ESP_LOGE(TAG, "Error getting operation");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find/Create new scene */
|
||||||
|
scene = esp_rmaker_scenes_find_or_create(&jctx, id, operation);
|
||||||
|
if (!scene) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get other scene details */
|
||||||
|
if (operation == OPERATION_ADD || operation == OPERATION_EDIT) {
|
||||||
|
/* Get info and flags */
|
||||||
|
esp_rmaker_scenes_parse_info_and_flags(&jctx, &scene->info, &scene->flags);
|
||||||
|
|
||||||
|
/* Get action */
|
||||||
|
esp_rmaker_scenes_parse_action(&jctx, &scene->action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set report_params */
|
||||||
|
if (operation == OPERATION_ADD || operation == OPERATION_EDIT || operation == OPERATION_REMOVE) {
|
||||||
|
*report_params = true;
|
||||||
|
} else {
|
||||||
|
*report_params = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform operation */
|
||||||
|
esp_rmaker_scenes_perform_operation(scene, operation);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
json_arr_leave_object(&jctx);
|
||||||
|
current_scene++;
|
||||||
|
}
|
||||||
|
json_parse_end(&jctx);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t __esp_rmaker_scenes_get_params(char *buf, size_t *buf_size)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
esp_rmaker_scene_t *scene = scenes_priv_data->scenes_list;
|
||||||
|
json_gen_str_t jstr;
|
||||||
|
json_gen_str_start(&jstr, buf, *buf_size, NULL, NULL);
|
||||||
|
json_gen_start_array(&jstr);
|
||||||
|
|
||||||
|
while (scene) {
|
||||||
|
json_gen_start_object(&jstr);
|
||||||
|
|
||||||
|
/* Add details */
|
||||||
|
json_gen_obj_set_string(&jstr, "name", scene->name);
|
||||||
|
json_gen_obj_set_string(&jstr, "id", scene->id);
|
||||||
|
/* If info and flags is not zero, add it. */
|
||||||
|
if (scene->info != NULL) {
|
||||||
|
json_gen_obj_set_string(&jstr, "info", scene->info);
|
||||||
|
}
|
||||||
|
if (scene->flags != 0) {
|
||||||
|
json_gen_obj_set_int(&jstr, "flags", scene->flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add action */
|
||||||
|
json_gen_push_object_str(&jstr, "action", scene->action.data);
|
||||||
|
|
||||||
|
json_gen_end_object(&jstr);
|
||||||
|
|
||||||
|
/* Go to next scene */
|
||||||
|
scene = scene->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_gen_end_array(&jstr) < 0) {
|
||||||
|
ESP_LOGE(TAG, "Buffer size %lu not sufficient for reporting Scenes Params.", (unsigned long) *buf_size);
|
||||||
|
err = ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
*buf_size = json_gen_str_end(&jstr);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *esp_rmaker_scenes_get_params(void)
|
||||||
|
{
|
||||||
|
size_t req_size = 0;
|
||||||
|
esp_err_t err = __esp_rmaker_scenes_get_params(NULL, &req_size);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to get required size for scenes JSON.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char *data = MEM_CALLOC_EXTRAM(1, req_size);
|
||||||
|
if (!data) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate %lu bytes for scenes.", (unsigned long) req_size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
err = __esp_rmaker_scenes_get_params(data, &req_size);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Error occured while trying to populate sceness JSON.");
|
||||||
|
free(data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_scenes_report_params(void)
|
||||||
|
{
|
||||||
|
char *data = esp_rmaker_scenes_get_params();
|
||||||
|
esp_rmaker_param_val_t val = {
|
||||||
|
.type = RMAKER_VAL_TYPE_ARRAY,
|
||||||
|
.val.s = data,
|
||||||
|
};
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_device_get_param_by_type(scenes_priv_data->scenes_service, ESP_RMAKER_PARAM_SCENES);
|
||||||
|
esp_rmaker_param_update_and_report(param, val);
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||||
|
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_SCENES) != 0) {
|
||||||
|
ESP_LOGE(TAG, "Got callback for invalid param with name %s and type %s", esp_rmaker_param_get_name(param), esp_rmaker_param_get_type(param));
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (strlen(val.val.s) <= 0) {
|
||||||
|
ESP_LOGI(TAG, "Invalid length for params: %lu", (unsigned long) strlen(val.val.s));
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
bool report_params = false;
|
||||||
|
esp_rmaker_scenes_parse_json(val.val.s, strlen(val.val.s), ctx->src, &report_params);
|
||||||
|
if (ctx->src != ESP_RMAKER_REQ_SRC_INIT) {
|
||||||
|
/* Since this is a persisting param, we get a write_cb while booting up. We need not report the param when the source is 'init' as this will get reported when the device first reports all the params. */
|
||||||
|
if (report_params) {
|
||||||
|
/* report_params is only set for add, edit, remove operations. The scenes params are not changed for
|
||||||
|
activate, deactivate operations. So need to report the params in that case. */
|
||||||
|
esp_rmaker_scenes_report_params();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_scenes_enable(void)
|
||||||
|
{
|
||||||
|
scenes_priv_data = (esp_rmaker_scenes_priv_data_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_scenes_priv_data_t));
|
||||||
|
if (!scenes_priv_data) {
|
||||||
|
ESP_LOGE(TAG, "Couldn't allocate scenes_priv_data");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_SCENES_DEACTIVATE_SUPPORT
|
||||||
|
scenes_priv_data->deactivate_support = CONFIG_ESP_RMAKER_SCENES_DEACTIVATE_SUPPORT;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
scenes_priv_data->scenes_service = esp_rmaker_create_scenes_service("Scenes", write_cb, NULL, MAX_SCENES, scenes_priv_data->deactivate_support, NULL);
|
||||||
|
if (!scenes_priv_data->scenes_service) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create Scenes Service");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), scenes_priv_data->scenes_service);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to add Scenes Service");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Scenes Service Enabled");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
983
components/esp_rainmaker/src/core/esp_rmaker_schedule.c
Normal file
983
components/esp_rainmaker/src/core/esp_rmaker_schedule.c
Normal file
@@ -0,0 +1,983 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/timers.h>
|
||||||
|
#include <json_parser.h>
|
||||||
|
#include <esp_rmaker_work_queue.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include <esp_rmaker_internal.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include <esp_rmaker_standard_services.h>
|
||||||
|
#include <esp_rmaker_standard_types.h>
|
||||||
|
#include <esp_rmaker_schedule.h>
|
||||||
|
#include <esp_schedule.h>
|
||||||
|
|
||||||
|
#define MAX_ID_LEN 8
|
||||||
|
#define MAX_NAME_LEN 32
|
||||||
|
#define MAX_INFO_LEN 128
|
||||||
|
#define MAX_OPERATION_LEN 10
|
||||||
|
#define TIME_SYNC_DELAY 10 /* 10 seconds */
|
||||||
|
#define MAX_SCHEDULES CONFIG_ESP_RMAKER_SCHEDULING_MAX_SCHEDULES
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_schedule";
|
||||||
|
|
||||||
|
typedef enum trigger_type {
|
||||||
|
TRIGGER_TYPE_INVALID = 0,
|
||||||
|
TRIGGER_TYPE_DAYS_OF_WEEK,
|
||||||
|
TRIGGER_TYPE_DATE,
|
||||||
|
TRIGGER_TYPE_RELATIVE,
|
||||||
|
} trigger_type_t;
|
||||||
|
|
||||||
|
typedef struct esp_rmaker_schedule_trigger {
|
||||||
|
trigger_type_t type;
|
||||||
|
/* Relative Seconds */
|
||||||
|
int relative_seconds;
|
||||||
|
/* Minutes from 12am */
|
||||||
|
uint16_t minutes;
|
||||||
|
struct {
|
||||||
|
/* 'OR' list of days or the week. Eg. Monday = 0b1, Tuesday = 0b10 */
|
||||||
|
uint8_t repeat_days;
|
||||||
|
} day;
|
||||||
|
struct {
|
||||||
|
/* Day of the month */
|
||||||
|
uint8_t day;
|
||||||
|
/* 'OR' list of months of the year. Eg. January = 0b1, February = 0b10.
|
||||||
|
0 for next date (either this month or next). */
|
||||||
|
uint16_t repeat_months;
|
||||||
|
uint16_t year;
|
||||||
|
bool repeat_every_year;
|
||||||
|
} date;
|
||||||
|
/* Used for non repeating schedules */
|
||||||
|
int64_t next_timestamp;
|
||||||
|
} esp_rmaker_schedule_trigger_t;
|
||||||
|
|
||||||
|
typedef struct esp_rmaker_schedule_action {
|
||||||
|
void *data;
|
||||||
|
size_t data_len;
|
||||||
|
} esp_rmaker_schedule_action_t;
|
||||||
|
|
||||||
|
typedef struct esp_rmaker_schedule {
|
||||||
|
char name[MAX_NAME_LEN + 1]; /* +1 for NULL termination */
|
||||||
|
char id[MAX_ID_LEN + 1]; /* +1 for NULL termination */
|
||||||
|
/* Info is used to store additional information, it is limited to 128 bytes. */
|
||||||
|
char *info;
|
||||||
|
/* Index is used in the callback to get back the schedule. */
|
||||||
|
long index;
|
||||||
|
/* Flags are used to identify the schedule. Eg. timing, countdown */
|
||||||
|
uint32_t flags;
|
||||||
|
bool enabled;
|
||||||
|
esp_schedule_handle_t handle;
|
||||||
|
esp_rmaker_schedule_action_t action;
|
||||||
|
esp_rmaker_schedule_trigger_t trigger;
|
||||||
|
esp_schedule_validity_t validity;
|
||||||
|
struct esp_rmaker_schedule *next;
|
||||||
|
} esp_rmaker_schedule_t;
|
||||||
|
|
||||||
|
enum time_sync_state {
|
||||||
|
TIME_SYNC_NOT_STARTED,
|
||||||
|
TIME_SYNC_STARTED,
|
||||||
|
TIME_SYNC_DONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum schedule_operation {
|
||||||
|
OPERATION_INVALID,
|
||||||
|
OPERATION_ADD,
|
||||||
|
OPERATION_EDIT,
|
||||||
|
OPERATION_REMOVE,
|
||||||
|
OPERATION_ENABLE,
|
||||||
|
OPERATION_DISABLE,
|
||||||
|
} schedule_operation_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_rmaker_schedule_t *schedule_list;
|
||||||
|
int total_schedules;
|
||||||
|
/* This index just increases. This makes sure it is unique for the given schedules */
|
||||||
|
int32_t index;
|
||||||
|
esp_rmaker_device_t *schedule_service;
|
||||||
|
TimerHandle_t time_sync_timer;
|
||||||
|
enum time_sync_state time_sync_state;;
|
||||||
|
} esp_rmaker_schedule_priv_data_t;
|
||||||
|
|
||||||
|
static esp_rmaker_schedule_priv_data_t *schedule_priv_data;
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_operation_enable(esp_rmaker_schedule_t *schedule);
|
||||||
|
static esp_err_t esp_rmaker_schedule_operation_disable(esp_rmaker_schedule_t *schedule);
|
||||||
|
static esp_err_t esp_rmaker_schedule_report_params(void);
|
||||||
|
static esp_err_t esp_rmaker_schedule_timesync_timer_deinit(void);
|
||||||
|
static esp_err_t esp_rmaker_schedule_timesync_timer_start(void);
|
||||||
|
|
||||||
|
static void esp_rmaker_schedule_free(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
if (!schedule) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (schedule->action.data) {
|
||||||
|
free(schedule->action.data);
|
||||||
|
}
|
||||||
|
if (schedule->info) {
|
||||||
|
free(schedule->info);
|
||||||
|
}
|
||||||
|
free(schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_rmaker_schedule_t *esp_rmaker_schedule_get_schedule_from_id(const char *id)
|
||||||
|
{
|
||||||
|
if (!id) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
esp_rmaker_schedule_t *schedule = schedule_priv_data->schedule_list;
|
||||||
|
while(schedule) {
|
||||||
|
if (strncmp(id, schedule->id, sizeof(schedule->id)) == 0) {
|
||||||
|
ESP_LOGD(TAG, "Schedule with id %s found in list for get.", id);
|
||||||
|
return schedule;
|
||||||
|
}
|
||||||
|
schedule = schedule->next;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Schedule with id %s not found in list for get.", id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_rmaker_schedule_t *esp_rmaker_schedule_get_schedule_from_index(long index)
|
||||||
|
{
|
||||||
|
esp_rmaker_schedule_t *schedule = schedule_priv_data->schedule_list;
|
||||||
|
while(schedule) {
|
||||||
|
if (schedule->index == index) {
|
||||||
|
ESP_LOGD(TAG, "Schedule with index %ld found in list for get.", index);
|
||||||
|
return schedule;
|
||||||
|
}
|
||||||
|
schedule = schedule->next;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Schedule with index %ld not found in list for get.", index);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_add_to_list(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
if (!schedule) {
|
||||||
|
ESP_LOGE(TAG, "Schedule is NULL. Not adding to list.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_rmaker_schedule_get_schedule_from_id(schedule->id) != NULL) {
|
||||||
|
ESP_LOGI(TAG, "Schedule with id %s already added to list. Not adding again.", schedule->id);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
/* Parse list */
|
||||||
|
esp_rmaker_schedule_t *prev_schedule = schedule_priv_data->schedule_list;
|
||||||
|
while(prev_schedule) {
|
||||||
|
if (prev_schedule->next) {
|
||||||
|
prev_schedule = prev_schedule->next;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add to list */
|
||||||
|
if (prev_schedule) {
|
||||||
|
prev_schedule->next = schedule;
|
||||||
|
} else {
|
||||||
|
schedule_priv_data->schedule_list = schedule;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Schedule with id %s added to list.", schedule->id);
|
||||||
|
schedule_priv_data->total_schedules++;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_remove_from_list(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
if (!schedule) {
|
||||||
|
ESP_LOGE(TAG, "Schedule is NULL. Not removing from list.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
/* Parse list */
|
||||||
|
esp_rmaker_schedule_t *curr_schedule = schedule_priv_data->schedule_list;
|
||||||
|
esp_rmaker_schedule_t *prev_schedule = curr_schedule;
|
||||||
|
while(curr_schedule) {
|
||||||
|
if (strncmp(schedule->id, curr_schedule->id, sizeof(schedule->id)) == 0) {
|
||||||
|
ESP_LOGD(TAG, "Schedule with id %s found in list for removing", schedule->id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev_schedule = curr_schedule;
|
||||||
|
curr_schedule = curr_schedule->next;
|
||||||
|
}
|
||||||
|
if (!curr_schedule) {
|
||||||
|
ESP_LOGE(TAG, "Schedule with id %s not found in list. Not removing.", schedule->id);
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove from list */
|
||||||
|
if (curr_schedule == schedule_priv_data->schedule_list) {
|
||||||
|
schedule_priv_data->schedule_list = curr_schedule->next;
|
||||||
|
} else {
|
||||||
|
prev_schedule->next = curr_schedule->next;
|
||||||
|
}
|
||||||
|
schedule_priv_data->total_schedules--;
|
||||||
|
ESP_LOGD(TAG, "Schedule with id %s removed from list.", schedule->id);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool esp_rmaker_schedule_is_expired(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
time_t current_timestamp = 0;
|
||||||
|
struct tm current_time = {0};
|
||||||
|
time(¤t_timestamp);
|
||||||
|
localtime_r(¤t_timestamp, ¤t_time);
|
||||||
|
|
||||||
|
if (schedule->trigger.type == TRIGGER_TYPE_RELATIVE) {
|
||||||
|
if (schedule->trigger.next_timestamp > 0 && schedule->trigger.next_timestamp <= current_timestamp) {
|
||||||
|
/* Relative seconds based schedule has expired */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (schedule->trigger.type == TRIGGER_TYPE_DAYS_OF_WEEK) {
|
||||||
|
if (schedule->trigger.day.repeat_days == 0) {
|
||||||
|
if (schedule->trigger.next_timestamp > 0 && schedule->trigger.next_timestamp <= current_timestamp) {
|
||||||
|
/* One time schedule has expired */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (schedule->trigger.type == TRIGGER_TYPE_DATE) {
|
||||||
|
if (schedule->trigger.date.repeat_months == 0) {
|
||||||
|
if (schedule->trigger.next_timestamp > 0 && schedule->trigger.next_timestamp <= current_timestamp) {
|
||||||
|
/* One time schedule has expired */
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (schedule->trigger.date.repeat_every_year == true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tm schedule_time = {0};
|
||||||
|
localtime_r(¤t_timestamp, &schedule_time);
|
||||||
|
schedule_time.tm_sec = 0;
|
||||||
|
schedule_time.tm_min = schedule->trigger.minutes;
|
||||||
|
schedule_time.tm_hour = 0;
|
||||||
|
schedule_time.tm_mday = schedule->trigger.date.day;
|
||||||
|
/* For expiry, just check the last month of the repeat_months. */
|
||||||
|
/* '-1' because struct tm has months starting from 0 and we have months starting from 1. */
|
||||||
|
schedule_time.tm_mon = fls(schedule->trigger.date.repeat_months) - 1;
|
||||||
|
/* '-1900' because struct tm has number of years after 1900 */
|
||||||
|
schedule_time.tm_year = schedule->trigger.date.year - 1900;
|
||||||
|
time_t schedule_timestamp = mktime(&schedule_time);
|
||||||
|
|
||||||
|
if (schedule_timestamp < current_timestamp) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_process_action(esp_rmaker_schedule_action_t *action)
|
||||||
|
{
|
||||||
|
return esp_rmaker_handle_set_params(action->data, action->data_len, ESP_RMAKER_REQ_SRC_SCHEDULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_schedule_trigger_work_cb(void *priv_data)
|
||||||
|
{
|
||||||
|
long index = (long)priv_data;
|
||||||
|
esp_rmaker_schedule_t *schedule = esp_rmaker_schedule_get_schedule_from_index(index);
|
||||||
|
if (!schedule) {
|
||||||
|
ESP_LOGE(TAG, "Schedule with index %ld not found for trigger work callback", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_rmaker_schedule_process_action(&schedule->action);
|
||||||
|
if (esp_rmaker_schedule_is_expired(schedule)) {
|
||||||
|
/* This schedule does not repeat anymore. Disable it and report the params. */
|
||||||
|
esp_rmaker_schedule_operation_disable(schedule);
|
||||||
|
esp_rmaker_schedule_report_params();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_schedule_trigger_common_cb(esp_schedule_handle_t handle, void *priv_data)
|
||||||
|
{
|
||||||
|
/* Adding to work queue to change the context from timer's task. */
|
||||||
|
esp_rmaker_work_queue_add_task(esp_rmaker_schedule_trigger_work_cb, priv_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_schedule_timestamp_common_cb(esp_schedule_handle_t handle, uint32_t next_timestamp, void *priv_data)
|
||||||
|
{
|
||||||
|
long index = (long)priv_data;
|
||||||
|
esp_rmaker_schedule_t *schedule = esp_rmaker_schedule_get_schedule_from_index(index);
|
||||||
|
if (!schedule) {
|
||||||
|
ESP_LOGE(TAG, "Schedule with index %ld not found for timestamp callback", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
schedule->trigger.next_timestamp = next_timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_prepare_config(esp_rmaker_schedule_t *schedule, esp_schedule_config_t *schedule_config)
|
||||||
|
{
|
||||||
|
if (!schedule || !schedule_config) {
|
||||||
|
ESP_LOGE(TAG, "schedule or schedule_config is NULL.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schedule->trigger.type == TRIGGER_TYPE_RELATIVE) {
|
||||||
|
schedule_config->trigger.next_scheduled_time_utc = (time_t)schedule->trigger.next_timestamp;
|
||||||
|
schedule_config->trigger.relative_seconds = schedule->trigger.relative_seconds;
|
||||||
|
schedule_config->trigger.type = ESP_SCHEDULE_TYPE_RELATIVE;
|
||||||
|
schedule_config->trigger_cb = esp_rmaker_schedule_trigger_common_cb;
|
||||||
|
schedule_config->timestamp_cb = esp_rmaker_schedule_timestamp_common_cb;
|
||||||
|
} else {
|
||||||
|
int hours = schedule->trigger.minutes / 60;
|
||||||
|
int minutes = schedule->trigger.minutes % 60;
|
||||||
|
schedule_config->trigger.hours = hours;
|
||||||
|
schedule_config->trigger.minutes = minutes;
|
||||||
|
schedule_config->trigger_cb = esp_rmaker_schedule_trigger_common_cb;
|
||||||
|
|
||||||
|
if (schedule->trigger.type == TRIGGER_TYPE_DAYS_OF_WEEK) {
|
||||||
|
schedule_config->trigger.type = ESP_SCHEDULE_TYPE_DAYS_OF_WEEK;
|
||||||
|
schedule_config->trigger.day.repeat_days = schedule->trigger.day.repeat_days;
|
||||||
|
if (schedule->trigger.day.repeat_days == 0) {
|
||||||
|
schedule_config->timestamp_cb = esp_rmaker_schedule_timestamp_common_cb;
|
||||||
|
}
|
||||||
|
} else if (schedule->trigger.type == TRIGGER_TYPE_DATE) {
|
||||||
|
schedule_config->trigger.type = ESP_SCHEDULE_TYPE_DATE;
|
||||||
|
schedule_config->trigger.date.day = schedule->trigger.date.day;
|
||||||
|
schedule_config->trigger.date.repeat_months = schedule->trigger.date.repeat_months;
|
||||||
|
schedule_config->trigger.date.year = schedule->trigger.date.year;
|
||||||
|
schedule_config->trigger.date.repeat_every_year = schedule->trigger.date.repeat_every_year;
|
||||||
|
if (schedule->trigger.date.repeat_months == 0) {
|
||||||
|
schedule_config->timestamp_cb = esp_rmaker_schedule_timestamp_common_cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In esp_schedule, name should be unique and is used as the primary key.
|
||||||
|
We are setting the id in esp_rmaker_schedule as the name in esp_schedule */
|
||||||
|
strlcpy(schedule_config->name, schedule->id, sizeof(schedule_config->name));
|
||||||
|
/* Just passing the schedule pointer as priv_data could create a race condition between the schedule getting a
|
||||||
|
callback and the schedule getting removed. Using this unique index as the priv_data solves it to some extent. */
|
||||||
|
schedule_config->priv_data = (void *)schedule->index;
|
||||||
|
schedule_config->validity = schedule->validity;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_add(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
esp_schedule_config_t schedule_config = {0};
|
||||||
|
esp_rmaker_schedule_prepare_config(schedule, &schedule_config);
|
||||||
|
|
||||||
|
schedule->handle = esp_schedule_create(&schedule_config);
|
||||||
|
if (schedule->handle == NULL) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_operation_add(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
esp_err_t ret = esp_rmaker_schedule_add(schedule);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = esp_rmaker_schedule_add_to_list(schedule);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_operation_edit(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
esp_schedule_config_t schedule_config = {0};
|
||||||
|
esp_rmaker_schedule_prepare_config(schedule, &schedule_config);
|
||||||
|
|
||||||
|
esp_err_t ret = esp_schedule_edit(schedule->handle, &schedule_config);
|
||||||
|
if (schedule->enabled == true) {
|
||||||
|
/* If the schedule is already enabled, disable it and enable it again so that the new changes after the
|
||||||
|
edit are reflected. */
|
||||||
|
esp_rmaker_schedule_operation_disable(schedule);
|
||||||
|
esp_rmaker_schedule_operation_enable(schedule);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_remove(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
return esp_schedule_delete(schedule->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_operation_remove(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
esp_err_t ret = esp_rmaker_schedule_remove_from_list(schedule);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = esp_rmaker_schedule_remove(schedule);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_schedule_timesync_timer_work_cb(void *priv_data)
|
||||||
|
{
|
||||||
|
if (esp_rmaker_time_check() != true) {
|
||||||
|
esp_rmaker_schedule_timesync_timer_start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_rmaker_schedule_timesync_timer_deinit();
|
||||||
|
schedule_priv_data->time_sync_state = TIME_SYNC_DONE;
|
||||||
|
ESP_LOGI(TAG, "Time is synchronised now. Enabling the schedules.");
|
||||||
|
esp_rmaker_schedule_t *schedule = schedule_priv_data->schedule_list;
|
||||||
|
while (schedule) {
|
||||||
|
if (schedule->enabled == true) {
|
||||||
|
esp_rmaker_schedule_operation_enable(schedule);
|
||||||
|
}
|
||||||
|
schedule = schedule->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_schedule_timesync_timer_cb(TimerHandle_t timer)
|
||||||
|
{
|
||||||
|
esp_rmaker_work_queue_add_task(esp_rmaker_schedule_timesync_timer_work_cb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_timesync_timer_init(void)
|
||||||
|
{
|
||||||
|
schedule_priv_data->time_sync_timer = xTimerCreate("esp_rmaker_schedule", (TIME_SYNC_DELAY * 1000) / portTICK_PERIOD_MS, pdFALSE, NULL, esp_rmaker_schedule_timesync_timer_cb);
|
||||||
|
if (schedule_priv_data->time_sync_timer == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create timer for time sync");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_timesync_timer_deinit(void)
|
||||||
|
{
|
||||||
|
xTimerDelete(schedule_priv_data->time_sync_timer, portMAX_DELAY);
|
||||||
|
schedule_priv_data->time_sync_timer = NULL;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_timesync_timer_start(void)
|
||||||
|
{
|
||||||
|
xTimerStart(schedule_priv_data->time_sync_timer, portMAX_DELAY);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_operation_enable(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
/* Setting enabled to true even if time is not synced yet. This reports the correct enabled state when reporting the schedules.*/
|
||||||
|
schedule->enabled = true;
|
||||||
|
|
||||||
|
/* Check for time sync */
|
||||||
|
if (schedule_priv_data->time_sync_state == TIME_SYNC_NOT_STARTED) {
|
||||||
|
if (esp_rmaker_time_check() != true) {
|
||||||
|
ESP_LOGI(TAG, "Time is not synchronised yet. The schedule will actually be enabled when time is synchronised. This may take time.");
|
||||||
|
esp_rmaker_schedule_timesync_timer_init();
|
||||||
|
esp_rmaker_schedule_timesync_timer_start();
|
||||||
|
schedule_priv_data->time_sync_state = TIME_SYNC_STARTED;
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
schedule_priv_data->time_sync_state = TIME_SYNC_DONE;
|
||||||
|
} else if (schedule_priv_data->time_sync_state == TIME_SYNC_STARTED) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_rmaker_schedule_is_expired(schedule)) {
|
||||||
|
/* This schedule does not repeat anymore. Disable it. */
|
||||||
|
/* While time sync is happening, it might be possible that this schedule will be shown as enabled, but actually it is disabled. */
|
||||||
|
ESP_LOGI(TAG, "Schedule with id %s does not repeat anymore. Disabling it.", schedule->id);
|
||||||
|
esp_rmaker_schedule_operation_disable(schedule);
|
||||||
|
/* Since the enabled state has been changed, report this */
|
||||||
|
esp_rmaker_schedule_report_params();
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Time is synced. Enable the schedule */
|
||||||
|
return esp_schedule_enable(schedule->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_operation_disable(esp_rmaker_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
esp_err_t ret = esp_schedule_disable(schedule->handle);
|
||||||
|
schedule->trigger.next_timestamp = 0;
|
||||||
|
schedule->enabled = false;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule_operation_t esp_rmaker_schedule_get_operation_from_str(char *operation)
|
||||||
|
{
|
||||||
|
if (!operation) {
|
||||||
|
return OPERATION_INVALID;
|
||||||
|
}
|
||||||
|
if (strncmp(operation, "add", strlen(operation)) == 0) {
|
||||||
|
return OPERATION_ADD;
|
||||||
|
} else if (strncmp(operation, "edit", strlen(operation)) == 0) {
|
||||||
|
return OPERATION_EDIT;
|
||||||
|
} else if (strncmp(operation, "remove", strlen(operation)) == 0) {
|
||||||
|
return OPERATION_REMOVE;
|
||||||
|
} else if (strncmp(operation, "enable", strlen(operation)) == 0) {
|
||||||
|
return OPERATION_ENABLE;
|
||||||
|
} else if (strncmp(operation, "disable", strlen(operation)) == 0) {
|
||||||
|
return OPERATION_DISABLE;
|
||||||
|
}
|
||||||
|
return OPERATION_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static schedule_operation_t esp_rmaker_schedule_parse_operation(jparse_ctx_t *jctx, char *id)
|
||||||
|
{
|
||||||
|
char operation_str[MAX_OPERATION_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||||
|
schedule_operation_t operation = OPERATION_INVALID;
|
||||||
|
json_obj_get_string(jctx, "operation", operation_str, sizeof(operation_str));
|
||||||
|
if (strlen(operation_str) <= 0) {
|
||||||
|
ESP_LOGE(TAG, "Operation not found in schedule with id: %s", id);
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
operation = esp_rmaker_schedule_get_operation_from_str(operation_str);
|
||||||
|
if (operation == OPERATION_EDIT) {
|
||||||
|
/* Get schedule temporarily */
|
||||||
|
if (esp_rmaker_schedule_get_schedule_from_id(id) == NULL) {
|
||||||
|
/* Operation is edit, but schedule not present already. Consider this as add. */
|
||||||
|
ESP_LOGD(TAG, "Operation is edit, but schedule with id %s not found. Changing the operation to add.", id);
|
||||||
|
operation = OPERATION_ADD;
|
||||||
|
}
|
||||||
|
} else if (operation == OPERATION_INVALID) {
|
||||||
|
ESP_LOGE(TAG, "Invalid schedule operation found: %s", operation_str);
|
||||||
|
}
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_parse_action(jparse_ctx_t *jctx, esp_rmaker_schedule_action_t *action)
|
||||||
|
{
|
||||||
|
int data_len = 0;
|
||||||
|
json_obj_get_object_strlen(jctx, "action", &data_len);
|
||||||
|
if (data_len <= 0) {
|
||||||
|
ESP_LOGD(TAG, "Action not found in JSON");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
action->data_len = data_len + 1;
|
||||||
|
|
||||||
|
if (action->data) {
|
||||||
|
free(action->data);
|
||||||
|
}
|
||||||
|
action->data = (void *)MEM_CALLOC_EXTRAM(1, action->data_len);
|
||||||
|
if (!action->data) {
|
||||||
|
ESP_LOGE(TAG, "Could not allocate action");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
json_obj_get_object_str(jctx, "action", action->data, action->data_len);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_parse_trigger(jparse_ctx_t *jctx, esp_rmaker_schedule_trigger_t *trigger)
|
||||||
|
{
|
||||||
|
int total_triggers = 0;
|
||||||
|
int relative_seconds = 0, minutes = 0, repeat_days = 0, day = 0, repeat_months = 0, year = 0;
|
||||||
|
bool repeat_every_year = false;
|
||||||
|
int64_t timestamp = 0;
|
||||||
|
trigger_type_t type = TRIGGER_TYPE_INVALID;
|
||||||
|
if(json_obj_get_array(jctx, "triggers", &total_triggers) != 0) {
|
||||||
|
ESP_LOGD(TAG, "Trigger not found in JSON");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
if (total_triggers <= 0) {
|
||||||
|
ESP_LOGD(TAG, "No triggers found in trigger array");
|
||||||
|
json_obj_leave_array(jctx);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
if(json_arr_get_object(jctx, 0) == 0) {
|
||||||
|
json_obj_get_int64(jctx, "ts", ×tamp);
|
||||||
|
if (json_obj_get_int(jctx, "rsec", &relative_seconds) == 0) {
|
||||||
|
type = TRIGGER_TYPE_RELATIVE;
|
||||||
|
} else {
|
||||||
|
json_obj_get_int(jctx, "m", &minutes);
|
||||||
|
/* Check if it is of type day */
|
||||||
|
if (json_obj_get_int(jctx, "d", &repeat_days) == 0) {
|
||||||
|
type = TRIGGER_TYPE_DAYS_OF_WEEK;
|
||||||
|
}
|
||||||
|
if (json_obj_get_int(jctx, "dd", &day) == 0) {
|
||||||
|
type = TRIGGER_TYPE_DATE;
|
||||||
|
json_obj_get_int(jctx, "mm", &repeat_months);
|
||||||
|
json_obj_get_int(jctx, "yy", &year);
|
||||||
|
json_obj_get_bool(jctx, "r", &repeat_every_year);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json_arr_leave_object(jctx);
|
||||||
|
}
|
||||||
|
json_obj_leave_array(jctx);
|
||||||
|
|
||||||
|
trigger->type = type;
|
||||||
|
trigger->relative_seconds = relative_seconds;
|
||||||
|
trigger->minutes = minutes;
|
||||||
|
trigger->day.repeat_days = repeat_days;
|
||||||
|
trigger->date.day = day;
|
||||||
|
trigger->date.repeat_months = repeat_months;
|
||||||
|
trigger->date.year = year;
|
||||||
|
trigger->date.repeat_every_year = repeat_every_year;
|
||||||
|
trigger->next_timestamp = timestamp;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_parse_info_and_flags(jparse_ctx_t *jctx, char **info, uint32_t *flags)
|
||||||
|
{
|
||||||
|
char _info[MAX_INFO_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||||
|
int _flags = 0;
|
||||||
|
|
||||||
|
int err_code = json_obj_get_string(jctx, "info", _info, sizeof(_info));
|
||||||
|
if (err_code == OS_SUCCESS) {
|
||||||
|
if (*info) {
|
||||||
|
free(*info);
|
||||||
|
*info = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = strlen(_info);
|
||||||
|
if (len > 0) {
|
||||||
|
/* +1 for NULL termination */
|
||||||
|
*info = (char *)MEM_CALLOC_EXTRAM(1, len + 1);
|
||||||
|
if (*info) {
|
||||||
|
memcpy(*info, _info, len + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err_code = json_obj_get_int(jctx, "flags", &_flags);
|
||||||
|
if (err_code == OS_SUCCESS) {
|
||||||
|
if (flags) {
|
||||||
|
*flags = _flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_parse_validity(jparse_ctx_t *jctx, esp_schedule_validity_t *validity)
|
||||||
|
{
|
||||||
|
if (json_obj_get_object(jctx, "validity") == OS_SUCCESS) {
|
||||||
|
json_obj_get_int64(jctx, "start", (int64_t *)&validity->start_time);
|
||||||
|
json_obj_get_int64(jctx, "end", (int64_t *)&validity->end_time);
|
||||||
|
json_obj_leave_object(jctx);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_rmaker_schedule_t *esp_rmaker_schedule_find_or_create(jparse_ctx_t *jctx, char *id, schedule_operation_t operation)
|
||||||
|
{
|
||||||
|
char name[MAX_NAME_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||||
|
esp_rmaker_schedule_t *schedule = NULL;
|
||||||
|
if (operation == OPERATION_ADD) {
|
||||||
|
/* Checking if schedule with same id already exists. */
|
||||||
|
schedule = esp_rmaker_schedule_get_schedule_from_id(id);
|
||||||
|
if (schedule) {
|
||||||
|
ESP_LOGE(TAG, "Schedule with id %s already exists. Not adding it again.", id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get name */
|
||||||
|
json_obj_get_string(jctx, "name", name, sizeof(name));
|
||||||
|
if (strlen(name) <= 0) {
|
||||||
|
ESP_LOGE(TAG, "Name not found for schedule with id: %s", id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is a new schedule. Fill it. */
|
||||||
|
schedule = (esp_rmaker_schedule_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_schedule_t));
|
||||||
|
if (!schedule) {
|
||||||
|
ESP_LOGE(TAG, "Couldn't allocate schedule with id: %s", id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strlcpy(schedule->id, id, sizeof(schedule->id));
|
||||||
|
strlcpy(schedule->name, name, sizeof(schedule->name));
|
||||||
|
schedule->index = schedule_priv_data->index++;
|
||||||
|
} else {
|
||||||
|
/* This schedule should already be present */
|
||||||
|
schedule = esp_rmaker_schedule_get_schedule_from_id(id);
|
||||||
|
if (!schedule) {
|
||||||
|
ESP_LOGE(TAG, "Schedule with id %s not found", id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get name */
|
||||||
|
if (operation == OPERATION_EDIT) {
|
||||||
|
json_obj_get_string(jctx, "name", name, sizeof(name));
|
||||||
|
if (strlen(name) > 0) {
|
||||||
|
/* If there is name in the request, replace the name in the schedule with this new one */
|
||||||
|
memset(schedule->name, 0, sizeof(schedule->name));
|
||||||
|
strlcpy(schedule->name, name, sizeof(schedule->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return schedule;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_perform_operation(esp_rmaker_schedule_t *schedule, schedule_operation_t operation, bool enabled)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
switch (operation) {
|
||||||
|
case OPERATION_ADD:
|
||||||
|
if (schedule_priv_data->total_schedules < MAX_SCHEDULES) {
|
||||||
|
esp_rmaker_schedule_operation_add(schedule);
|
||||||
|
if (enabled == true) {
|
||||||
|
esp_rmaker_schedule_operation_enable(schedule);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Max schedules (%d) reached. Not adding this schedule with id %s", MAX_SCHEDULES,
|
||||||
|
schedule->id);
|
||||||
|
err = ESP_FAIL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPERATION_EDIT:
|
||||||
|
esp_rmaker_schedule_operation_edit(schedule);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPERATION_REMOVE:
|
||||||
|
esp_rmaker_schedule_operation_remove(schedule);
|
||||||
|
esp_rmaker_schedule_free(schedule);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPERATION_ENABLE:
|
||||||
|
esp_rmaker_schedule_operation_enable(schedule);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPERATION_DISABLE:
|
||||||
|
esp_rmaker_schedule_operation_disable(schedule);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "Invalid Operation: %d", operation);
|
||||||
|
err = ESP_FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_parse_json(void *data, size_t data_len, esp_rmaker_req_src_t src)
|
||||||
|
{
|
||||||
|
char id[MAX_ID_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||||
|
schedule_operation_t operation = OPERATION_INVALID;
|
||||||
|
bool enabled = true;
|
||||||
|
int current_schedule = 0;
|
||||||
|
esp_rmaker_schedule_t *schedule = NULL;
|
||||||
|
|
||||||
|
/* Get details from JSON */
|
||||||
|
jparse_ctx_t jctx;
|
||||||
|
if (json_parse_start(&jctx, (char *)data, data_len) != 0) {
|
||||||
|
ESP_LOGE(TAG, "Json parse start failed");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse all schedules */
|
||||||
|
while(json_arr_get_object(&jctx, current_schedule) == 0) {
|
||||||
|
/* Get ID */
|
||||||
|
json_obj_get_string(&jctx, "id", id, sizeof(id));
|
||||||
|
if (strlen(id) <= 0) {
|
||||||
|
ESP_LOGE(TAG, "ID not found in schedule JSON");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get operation */
|
||||||
|
if (src == ESP_RMAKER_REQ_SRC_INIT) {
|
||||||
|
/* Schedule loaded from NVS. Add it */
|
||||||
|
operation = OPERATION_ADD;
|
||||||
|
} else {
|
||||||
|
operation = esp_rmaker_schedule_parse_operation(&jctx, id);
|
||||||
|
if (operation == OPERATION_INVALID) {
|
||||||
|
ESP_LOGE(TAG, "Error getting operation");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find/Create new schedule */
|
||||||
|
schedule = esp_rmaker_schedule_find_or_create(&jctx, id, operation);
|
||||||
|
if (!schedule) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get other schedule details */
|
||||||
|
if (operation == OPERATION_ADD || operation == OPERATION_EDIT) {
|
||||||
|
/* Get enabled state */
|
||||||
|
if (operation == OPERATION_ADD) {
|
||||||
|
/* If loaded from NVS, check for previous enabled state. If new schedule, enable it */
|
||||||
|
if (src == ESP_RMAKER_REQ_SRC_INIT) {
|
||||||
|
json_obj_get_bool(&jctx, "enabled", &enabled);
|
||||||
|
} else {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get action */
|
||||||
|
esp_rmaker_schedule_parse_action(&jctx, &schedule->action);
|
||||||
|
|
||||||
|
/* Get trigger */
|
||||||
|
/* There is only one trigger for now. If more triggers are added, then they should be parsed here in a loop */
|
||||||
|
esp_rmaker_schedule_parse_trigger(&jctx, &schedule->trigger);
|
||||||
|
|
||||||
|
/* Get info and flags */
|
||||||
|
esp_rmaker_schedule_parse_info_and_flags(&jctx, &schedule->info, &schedule->flags);
|
||||||
|
|
||||||
|
/* Get validity */
|
||||||
|
esp_rmaker_schedule_parse_validity(&jctx, &schedule->validity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform operation */
|
||||||
|
esp_rmaker_schedule_perform_operation(schedule, operation, enabled);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
json_arr_leave_object(&jctx);
|
||||||
|
current_schedule++;
|
||||||
|
}
|
||||||
|
json_parse_end(&jctx);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t __esp_rmaker_schedule_get_params(char *buf, size_t *buf_size)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
esp_rmaker_schedule_t *schedule = schedule_priv_data->schedule_list;
|
||||||
|
json_gen_str_t jstr;
|
||||||
|
json_gen_str_start(&jstr, buf, *buf_size, NULL, NULL);
|
||||||
|
json_gen_start_array(&jstr);
|
||||||
|
while (schedule) {
|
||||||
|
json_gen_start_object(&jstr);
|
||||||
|
|
||||||
|
/* Add details */
|
||||||
|
json_gen_obj_set_string(&jstr, "name", schedule->name);
|
||||||
|
json_gen_obj_set_string(&jstr, "id", schedule->id);
|
||||||
|
json_gen_obj_set_bool(&jstr, "enabled", schedule->enabled);
|
||||||
|
/* If info and flags is not zero, add it. */
|
||||||
|
if (schedule->info != NULL) {
|
||||||
|
json_gen_obj_set_string(&jstr, "info", schedule->info);
|
||||||
|
}
|
||||||
|
if (schedule->flags != 0) {
|
||||||
|
json_gen_obj_set_int(&jstr, "flags", schedule->flags);
|
||||||
|
}
|
||||||
|
/* Add validity */
|
||||||
|
if (schedule->validity.start_time != 0 || schedule->validity.end_time != 0) {
|
||||||
|
json_gen_push_object(&jstr, "validity");
|
||||||
|
if (schedule->validity.start_time != 0) {
|
||||||
|
json_gen_obj_set_int(&jstr, "start", schedule->validity.start_time);
|
||||||
|
}
|
||||||
|
if (schedule->validity.end_time != 0) {
|
||||||
|
json_gen_obj_set_int(&jstr, "end", schedule->validity.end_time);
|
||||||
|
}
|
||||||
|
json_gen_pop_object(&jstr);
|
||||||
|
}
|
||||||
|
/* Add action */
|
||||||
|
json_gen_push_object_str(&jstr, "action", schedule->action.data);
|
||||||
|
|
||||||
|
/* Add trigger */
|
||||||
|
json_gen_push_array(&jstr, "triggers");
|
||||||
|
json_gen_start_object(&jstr);
|
||||||
|
if (schedule->trigger.type == TRIGGER_TYPE_RELATIVE) {
|
||||||
|
json_gen_obj_set_int(&jstr, "rsec", schedule->trigger.relative_seconds);
|
||||||
|
json_gen_obj_set_int(&jstr, "ts", schedule->trigger.next_timestamp);
|
||||||
|
} else {
|
||||||
|
json_gen_obj_set_int(&jstr, "m", schedule->trigger.minutes);
|
||||||
|
if (schedule->trigger.type == TRIGGER_TYPE_DAYS_OF_WEEK) {
|
||||||
|
json_gen_obj_set_int(&jstr, "d", schedule->trigger.day.repeat_days);
|
||||||
|
if (schedule->trigger.day.repeat_days == 0) {
|
||||||
|
json_gen_obj_set_int(&jstr, "ts", schedule->trigger.next_timestamp);
|
||||||
|
}
|
||||||
|
} else if (schedule->trigger.type == TRIGGER_TYPE_DATE) {
|
||||||
|
json_gen_obj_set_int(&jstr, "dd", schedule->trigger.date.day);
|
||||||
|
json_gen_obj_set_int(&jstr, "mm", schedule->trigger.date.repeat_months);
|
||||||
|
json_gen_obj_set_int(&jstr, "yy", schedule->trigger.date.year);
|
||||||
|
json_gen_obj_set_int(&jstr, "r", schedule->trigger.date.repeat_every_year);
|
||||||
|
if (schedule->trigger.date.repeat_months == 0) {
|
||||||
|
json_gen_obj_set_int(&jstr, "ts", schedule->trigger.next_timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json_gen_end_object(&jstr);
|
||||||
|
json_gen_pop_array(&jstr);
|
||||||
|
|
||||||
|
json_gen_end_object(&jstr);
|
||||||
|
|
||||||
|
/* Go to next schedule */
|
||||||
|
schedule = schedule->next;
|
||||||
|
}
|
||||||
|
if (json_gen_end_array(&jstr) < 0) {
|
||||||
|
ESP_LOGE(TAG, "Buffer size %lu not sufficient for reporting Schedule Params.", (unsigned long) *buf_size);
|
||||||
|
err = ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
*buf_size = json_gen_str_end(&jstr);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *esp_rmaker_schedule_get_params(void)
|
||||||
|
{
|
||||||
|
size_t req_size = 0;
|
||||||
|
esp_err_t err = __esp_rmaker_schedule_get_params(NULL, &req_size);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to get required size for schedules JSON.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char *data = MEM_CALLOC_EXTRAM(1, req_size);
|
||||||
|
if (!data) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate %lu bytes for schedule.", (unsigned long) req_size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
err = __esp_rmaker_schedule_get_params(data, &req_size);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Error occured while trying to populate schedules JSON.");
|
||||||
|
free(data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_schedule_report_params(void)
|
||||||
|
{
|
||||||
|
char *data = esp_rmaker_schedule_get_params();
|
||||||
|
esp_rmaker_param_val_t val = {
|
||||||
|
.type = RMAKER_VAL_TYPE_ARRAY,
|
||||||
|
.val.s = data,
|
||||||
|
};
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_device_get_param_by_type(schedule_priv_data->schedule_service, ESP_RMAKER_PARAM_SCHEDULES);
|
||||||
|
esp_rmaker_param_update_and_report(param, val);
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||||
|
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_SCHEDULES) != 0) {
|
||||||
|
ESP_LOGE(TAG, "Got callback for invalid param with name %s and type %s", esp_rmaker_param_get_name(param), esp_rmaker_param_get_type(param));
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (strlen(val.val.s) <= 0) {
|
||||||
|
ESP_LOGI(TAG, "Invalid length for params: %lu", (unsigned long) strlen(val.val.s));
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_rmaker_schedule_parse_json(val.val.s, strlen(val.val.s), ctx->src);
|
||||||
|
if (ctx->src != ESP_RMAKER_REQ_SRC_INIT) {
|
||||||
|
/* Since this is a persisting param, we get a write_cb while booting up. We need not report the param when the source is 'init' as this will get reported when the device first reports all the params. */
|
||||||
|
esp_rmaker_schedule_report_params();
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_schedule_enable(void)
|
||||||
|
{
|
||||||
|
schedule_priv_data = (esp_rmaker_schedule_priv_data_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_schedule_priv_data_t));
|
||||||
|
if (!schedule_priv_data) {
|
||||||
|
ESP_LOGE(TAG, "Couldn't allocate schedule_priv_data");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
esp_rmaker_time_sync_init(NULL);
|
||||||
|
|
||||||
|
esp_schedule_init(false, NULL, NULL);
|
||||||
|
|
||||||
|
schedule_priv_data->schedule_service = esp_rmaker_create_schedule_service("Schedule", write_cb, NULL, MAX_SCHEDULES, NULL);
|
||||||
|
if (!schedule_priv_data->schedule_service) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create Schedule Service");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), schedule_priv_data->schedule_service);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to add service Service");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Scheduling Service Enabled");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_idf_version.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include "esp_rmaker_secure_boot_digest.h"
|
||||||
|
|
||||||
|
static const char *TAG = "rmaker_secure_boot";
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECURE_BOOT_V2_ENABLED
|
||||||
|
|
||||||
|
static inline uint8_t to_hex_digit(unsigned val)
|
||||||
|
{
|
||||||
|
return (val < 10) ? ('0' + val) : ('a' + val - 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bytes_to_hex(uint8_t *src, uint8_t *dst, int in_len)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < in_len; i++) {
|
||||||
|
dst[2 * i] = to_hex_digit(src[i] >> 4);
|
||||||
|
dst[2 * i + 1] = to_hex_digit(src[i] & 0xf);
|
||||||
|
}
|
||||||
|
dst[2 * in_len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex representation of secure boot digest. +1 for NULL termination
|
||||||
|
#define SECURE_BOOT_DIGEST_LEN (ESP_SECURE_BOOT_DIGEST_LEN * 2 + 1)
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_secure_boot_digest_free(char **digest)
|
||||||
|
{
|
||||||
|
// cleanup
|
||||||
|
for(int i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||||
|
if (digest[i]) {
|
||||||
|
free(digest[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(digest);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** esp_rmaker_get_secure_boot_digest()
|
||||||
|
{
|
||||||
|
char **secure_boot_digest = NULL;
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
esp_secure_boot_key_digests_t trusted_keys;
|
||||||
|
#else
|
||||||
|
ets_secure_boot_key_digests_t trusted_keys;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||||
|
esp_err_t ret = esp_secure_boot_read_key_digests(&trusted_keys);
|
||||||
|
#else
|
||||||
|
esp_err_t ret = ets_secure_boot_read_key_digests(&trusted_keys);
|
||||||
|
#endif
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Could not read the secure boot key digests from efuse.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
secure_boot_digest = MEM_ALLOC_EXTRAM(SECURE_BOOT_NUM_BLOCKS * sizeof(char *));
|
||||||
|
if (!secure_boot_digest) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for secure boot digest");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for(int i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||||
|
secure_boot_digest[i] = NULL;
|
||||||
|
if (trusted_keys.key_digests[i] != NULL) {
|
||||||
|
secure_boot_digest[i] = MEM_ALLOC_EXTRAM(SECURE_BOOT_DIGEST_LEN);
|
||||||
|
if (!secure_boot_digest[i]) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for secure boot digest");
|
||||||
|
for (int k = 0; k < i; k++) {
|
||||||
|
free(secure_boot_digest[k]);
|
||||||
|
secure_boot_digest[k] = NULL;
|
||||||
|
}
|
||||||
|
free(secure_boot_digest);
|
||||||
|
secure_boot_digest = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bytes_to_hex((uint8_t *)trusted_keys.key_digests[i], (uint8_t *)secure_boot_digest[i], ESP_SECURE_BOOT_DIGEST_LEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return secure_boot_digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* CONFIG_SECURE_BOOT_V2_ENABLED */
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_secure_boot_digest_free(char **digest)
|
||||||
|
{
|
||||||
|
(void) digest;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** esp_rmaker_get_secure_boot_digest()
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Secure boot is not enabled. Could not get digest.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <esp_secure_boot.h>
|
||||||
|
#include <esp_efuse.h>
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#include "esp32/rom/secure_boot.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#include "esp32s2/rom/secure_boot.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#include "esp32c3/rom/secure_boot.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#include "esp32s3/rom/secure_boot.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||||
|
#include "esp32c2/rom/secure_boot.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||||
|
#include "esp32c6/rom/secure_boot.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||||
|
#include "esp32h2/rom/secure_boot.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get secure boot digest
|
||||||
|
*
|
||||||
|
* @return 2D pointer with secure boot digest array
|
||||||
|
* @note the memory allocated gets freed with \ref `esp_rmaker_secure_boot_digest_free` API
|
||||||
|
*/
|
||||||
|
char** esp_rmaker_get_secure_boot_digest();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief free secure boot digest buffer
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_secure_boot_digest_free(char **digest);
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include <esp_rmaker_standard_types.h>
|
||||||
|
#include <esp_rmaker_standard_params.h>
|
||||||
|
#include <esp_rmaker_standard_services.h>
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_system_service";
|
||||||
|
|
||||||
|
#define ESP_RMAKER_SYSTEM_SERV_NAME "System"
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_system_serv_write_cb(const esp_rmaker_device_t *device,
|
||||||
|
const esp_rmaker_param_t *param, const esp_rmaker_param_val_t val,
|
||||||
|
void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
esp_rmaker_system_serv_config_t *config = (esp_rmaker_system_serv_config_t *)priv_data;
|
||||||
|
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_REBOOT) == 0) {
|
||||||
|
if (val.val.b == true) {
|
||||||
|
err = esp_rmaker_reboot(config->reboot_seconds);
|
||||||
|
}
|
||||||
|
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_FACTORY_RESET) == 0) {
|
||||||
|
if (val.val.b == true) {
|
||||||
|
err = esp_rmaker_factory_reset(config->reset_seconds, config->reset_reboot_seconds);
|
||||||
|
}
|
||||||
|
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_WIFI_RESET) == 0) {
|
||||||
|
if (val.val.b == true) {
|
||||||
|
err = esp_rmaker_wifi_reset(config->reset_seconds, config->reset_reboot_seconds);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
esp_rmaker_param_update_and_report(param, val);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_system_service_enable(esp_rmaker_system_serv_config_t *config)
|
||||||
|
{
|
||||||
|
if ((config->flags & SYSTEM_SERV_FLAGS_ALL) == 0) {
|
||||||
|
ESP_LOGE(TAG, "Atleast one flag should be set for system service.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_rmaker_system_serv_config_t *priv_config = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_system_serv_config_t));
|
||||||
|
if (!priv_config) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate data for system service config.");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
*priv_config = *config;
|
||||||
|
esp_rmaker_device_t *service = esp_rmaker_create_system_service(ESP_RMAKER_SYSTEM_SERV_NAME, (void *)priv_config);
|
||||||
|
if (service) {
|
||||||
|
esp_rmaker_device_add_cb(service, esp_rmaker_system_serv_write_cb, NULL);
|
||||||
|
if (priv_config->flags & SYSTEM_SERV_FLAG_REBOOT) {
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_reboot_param_create(ESP_RMAKER_DEF_REBOOT_NAME));
|
||||||
|
}
|
||||||
|
if (priv_config->flags & SYSTEM_SERV_FLAG_FACTORY_RESET) {
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_factory_reset_param_create(ESP_RMAKER_DEF_FACTORY_RESET_NAME));
|
||||||
|
}
|
||||||
|
if (priv_config->flags & SYSTEM_SERV_FLAG_WIFI_RESET) {
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_wifi_reset_param_create(ESP_RMAKER_DEF_WIFI_RESET_NAME));
|
||||||
|
}
|
||||||
|
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), service);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "System service enabled.");
|
||||||
|
} else {
|
||||||
|
esp_rmaker_device_delete(service);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
} else {
|
||||||
|
free(priv_config);
|
||||||
|
ESP_LOGE(TAG, "Failed to create System service.");
|
||||||
|
}
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
82
components/esp_rainmaker/src/core/esp_rmaker_time_service.c
Normal file
82
components/esp_rainmaker/src/core/esp_rmaker_time_service.c
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <nvs.h>
|
||||||
|
#include <esp_rmaker_standard_types.h>
|
||||||
|
#include <esp_rmaker_standard_services.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_time_service";
|
||||||
|
|
||||||
|
#define ESP_RMAKER_TIME_SERV_NAME "Time"
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_time_service_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||||
|
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_FAIL;
|
||||||
|
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TIMEZONE) == 0) {
|
||||||
|
ESP_LOGI(TAG, "Received value = %s for %s - %s",
|
||||||
|
val.val.s, esp_rmaker_device_get_name(device), esp_rmaker_param_get_name(param));
|
||||||
|
err = esp_rmaker_time_set_timezone(val.val.s);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
char *tz_posix = esp_rmaker_time_get_timezone_posix();
|
||||||
|
if (tz_posix) {
|
||||||
|
esp_rmaker_param_t *tz_posix_param = esp_rmaker_device_get_param_by_type(
|
||||||
|
device, ESP_RMAKER_PARAM_TIMEZONE_POSIX);
|
||||||
|
esp_rmaker_param_update_and_report(tz_posix_param, esp_rmaker_str(tz_posix));
|
||||||
|
free(tz_posix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TIMEZONE_POSIX) == 0) {
|
||||||
|
ESP_LOGI(TAG, "Received value = %s for %s - %s",
|
||||||
|
val.val.s, esp_rmaker_device_get_name(device), esp_rmaker_param_get_name(param));
|
||||||
|
err = esp_rmaker_time_set_timezone_posix(val.val.s);
|
||||||
|
}
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
esp_rmaker_param_update_and_report(param, val);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_time_add_service(const char *tz, const char *tz_posix)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *service = esp_rmaker_time_service_create(ESP_RMAKER_TIME_SERV_NAME, tz, tz_posix, NULL);
|
||||||
|
if (!service) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create Time Service");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_rmaker_device_add_cb(service, esp_rmaker_time_service_cb, NULL);
|
||||||
|
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), service);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Time service enabled");
|
||||||
|
} else {
|
||||||
|
esp_rmaker_device_delete(service);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_timezone_service_enable(void)
|
||||||
|
{
|
||||||
|
char *tz_posix = esp_rmaker_time_get_timezone_posix();
|
||||||
|
char *tz = esp_rmaker_time_get_timezone();
|
||||||
|
esp_err_t err = esp_rmaker_time_add_service(tz, tz_posix);
|
||||||
|
if (tz_posix) {
|
||||||
|
free(tz_posix);
|
||||||
|
}
|
||||||
|
if (tz) {
|
||||||
|
free(tz);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
414
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.c
Normal file
414
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.c
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
#include <nvs.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
#include <json_generator.h>
|
||||||
|
#include <esp_rmaker_work_queue.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_user_mapping.h>
|
||||||
|
#include <esp_rmaker_mqtt.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include <esp_rmaker_common_events.h>
|
||||||
|
#include "esp_rmaker_user_mapping.pb-c.h"
|
||||||
|
#include "esp_rmaker_internal.h"
|
||||||
|
#include "esp_rmaker_mqtt_topics.h"
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
#include <network_provisioning/manager.h>
|
||||||
|
#else
|
||||||
|
#include <wifi_provisioning/manager.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_user_mapping";
|
||||||
|
|
||||||
|
#define USER_MAPPING_ENDPOINT "cloud_user_assoc"
|
||||||
|
#define USER_MAPPING_NVS_NAMESPACE "user_mapping"
|
||||||
|
#define USER_ID_NVS_NAME "user_id"
|
||||||
|
#define USER_RESET_ID "esp-rmaker"
|
||||||
|
#define USER_RESET_KEY "failed"
|
||||||
|
|
||||||
|
/* A delay large enough to allow the tasks to get the semaphore, but small
|
||||||
|
* enough to prevent tasks getting blocked for long.
|
||||||
|
*/
|
||||||
|
#define SEMAPHORE_DELAY_MSEC 5000
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *user_id;
|
||||||
|
char *secret_key;
|
||||||
|
int mqtt_msg_id;
|
||||||
|
bool sent;
|
||||||
|
} esp_rmaker_user_mapping_data_t;
|
||||||
|
|
||||||
|
static esp_rmaker_user_mapping_data_t *rmaker_user_mapping_data;
|
||||||
|
esp_rmaker_user_mapping_state_t rmaker_user_mapping_state;
|
||||||
|
SemaphoreHandle_t esp_rmaker_user_mapping_lock = NULL;
|
||||||
|
|
||||||
|
static void esp_rmaker_user_mapping_cleanup_data(void)
|
||||||
|
{
|
||||||
|
if (rmaker_user_mapping_data) {
|
||||||
|
if (rmaker_user_mapping_data->user_id) {
|
||||||
|
free(rmaker_user_mapping_data->user_id);
|
||||||
|
}
|
||||||
|
if (rmaker_user_mapping_data->secret_key) {
|
||||||
|
free(rmaker_user_mapping_data->secret_key);
|
||||||
|
}
|
||||||
|
free(rmaker_user_mapping_data);
|
||||||
|
rmaker_user_mapping_data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_user_mapping_event_handler(void* arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void* event_data)
|
||||||
|
{
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
if (event_base == NETWORK_PROV_EVENT) {
|
||||||
|
switch (event_id) {
|
||||||
|
case NETWORK_PROV_INIT: {
|
||||||
|
if (esp_rmaker_user_mapping_endpoint_create() != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create user mapping end point.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NETWORK_PROV_START:
|
||||||
|
if (esp_rmaker_user_mapping_endpoint_register() != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to register user mapping end point.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (event_base == WIFI_PROV_EVENT) {
|
||||||
|
switch (event_id) {
|
||||||
|
case WIFI_PROV_INIT: {
|
||||||
|
if (esp_rmaker_user_mapping_endpoint_create() != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create user mapping end point.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WIFI_PROV_START:
|
||||||
|
if (esp_rmaker_user_mapping_endpoint_register() != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to register user mapping end point.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else if ((event_base == RMAKER_COMMON_EVENT) && (event_id == RMAKER_MQTT_EVENT_PUBLISHED)) {
|
||||||
|
/* Checking for the PUBACK for the user node association message to be sure that the message
|
||||||
|
* has indeed reached the RainMaker cloud.
|
||||||
|
*/
|
||||||
|
int msg_id = *((int *)event_data);
|
||||||
|
if (xSemaphoreTake(esp_rmaker_user_mapping_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to take semaphore.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((rmaker_user_mapping_data != NULL) && (msg_id == rmaker_user_mapping_data->mqtt_msg_id)) {
|
||||||
|
ESP_LOGI(TAG, "User Node association message published successfully.");
|
||||||
|
if (strcmp(rmaker_user_mapping_data->user_id, USER_RESET_ID) == 0) {
|
||||||
|
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_RESET;
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_USER_NODE_MAPPING_RESET, NULL, 0);
|
||||||
|
} else {
|
||||||
|
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_DONE;
|
||||||
|
esp_rmaker_post_event(RMAKER_EVENT_USER_NODE_MAPPING_DONE, rmaker_user_mapping_data->user_id,
|
||||||
|
strlen(rmaker_user_mapping_data->user_id) + 1);
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USER_ID_CHECK
|
||||||
|
/* Store User Id in NVS since acknowledgement of the user-node association message is received */
|
||||||
|
nvs_handle handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, USER_MAPPING_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
nvs_set_blob(handle, USER_ID_NVS_NAME, rmaker_user_mapping_data->user_id, strlen(rmaker_user_mapping_data->user_id));
|
||||||
|
nvs_close(handle);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
esp_rmaker_user_mapping_cleanup_data();
|
||||||
|
esp_event_handler_unregister(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_PUBLISHED,
|
||||||
|
&esp_rmaker_user_mapping_event_handler);
|
||||||
|
}
|
||||||
|
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_user_mapping_cb(void *priv_data)
|
||||||
|
{
|
||||||
|
if (xSemaphoreTake(esp_rmaker_user_mapping_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to take semaphore.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* If there is no user node mapping data, or if the data is already sent, just return */
|
||||||
|
if (rmaker_user_mapping_data == NULL || rmaker_user_mapping_data->sent == true) {
|
||||||
|
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_PUBLISHED,
|
||||||
|
&esp_rmaker_user_mapping_event_handler, NULL);
|
||||||
|
char publish_payload[200];
|
||||||
|
json_gen_str_t jstr;
|
||||||
|
json_gen_str_start(&jstr, publish_payload, sizeof(publish_payload), NULL, NULL);
|
||||||
|
json_gen_start_object(&jstr);
|
||||||
|
char *node_id = esp_rmaker_get_node_id();
|
||||||
|
json_gen_obj_set_string(&jstr, "node_id", node_id);
|
||||||
|
json_gen_obj_set_string(&jstr, "user_id", rmaker_user_mapping_data->user_id);
|
||||||
|
json_gen_obj_set_string(&jstr, "secret_key", rmaker_user_mapping_data->secret_key);
|
||||||
|
if (esp_rmaker_user_node_mapping_get_state() != ESP_RMAKER_USER_MAPPING_DONE) {
|
||||||
|
json_gen_obj_set_bool(&jstr, "reset", true);
|
||||||
|
}
|
||||||
|
json_gen_end_object(&jstr);
|
||||||
|
json_gen_str_end(&jstr);
|
||||||
|
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||||
|
esp_rmaker_create_mqtt_topic(publish_topic, sizeof(publish_topic), USER_MAPPING_TOPIC_SUFFIX, USER_MAPPING_TOPIC_RULE);
|
||||||
|
esp_err_t err = esp_rmaker_mqtt_publish(publish_topic, publish_payload, strlen(publish_payload), RMAKER_MQTT_QOS1, &rmaker_user_mapping_data->mqtt_msg_id);
|
||||||
|
ESP_LOGI(TAG, "MQTT Publish: %s", publish_payload);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "MQTT Publish Error %d", err);
|
||||||
|
} else {
|
||||||
|
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_REQ_SENT;
|
||||||
|
rmaker_user_mapping_data->sent = true;
|
||||||
|
}
|
||||||
|
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool esp_rmaker_user_mapping_detect_reset(const char *user_id)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USER_ID_CHECK
|
||||||
|
bool reset_state = true;
|
||||||
|
nvs_handle handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, USER_MAPPING_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
char *nvs_user_id = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
if ((err = nvs_get_blob(handle, USER_ID_NVS_NAME, NULL, &len)) == ESP_OK) {
|
||||||
|
nvs_user_id = MEM_CALLOC_EXTRAM(1, len + 1); /* +1 for NULL termination */
|
||||||
|
if (nvs_user_id) {
|
||||||
|
nvs_get_blob(handle, USER_ID_NVS_NAME, nvs_user_id, &len);
|
||||||
|
/* If existing user id and new user id are same, this is not a reset state */
|
||||||
|
if (strcmp(nvs_user_id, user_id) == 0) {
|
||||||
|
reset_state = false;
|
||||||
|
} else {
|
||||||
|
/* Deleting the key in case of a mismatch. It will be stored only after the user node association
|
||||||
|
* message is acknowledged from the cloud.
|
||||||
|
*/
|
||||||
|
nvs_erase_key(handle, USER_ID_NVS_NAME);
|
||||||
|
}
|
||||||
|
free(nvs_user_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nvs_close(handle);
|
||||||
|
return reset_state;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_start_user_node_mapping(char *user_id, char *secret_key)
|
||||||
|
{
|
||||||
|
if (esp_rmaker_user_mapping_lock == NULL) {
|
||||||
|
ESP_LOGE(TAG, "User Node mapping not initialised.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (xSemaphoreTake(esp_rmaker_user_mapping_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to take semaphore.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (rmaker_user_mapping_data) {
|
||||||
|
esp_rmaker_user_mapping_cleanup_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
rmaker_user_mapping_data = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_user_mapping_data_t));
|
||||||
|
if (!rmaker_user_mapping_data) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for rmaker_user_mapping_data.");
|
||||||
|
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
rmaker_user_mapping_data->user_id = strdup(user_id);
|
||||||
|
if (!rmaker_user_mapping_data->user_id) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for user_id.");
|
||||||
|
goto user_mapping_error;
|
||||||
|
}
|
||||||
|
rmaker_user_mapping_data->secret_key = strdup(secret_key);
|
||||||
|
if (!rmaker_user_mapping_data->secret_key) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for secret_key.");
|
||||||
|
goto user_mapping_error;
|
||||||
|
}
|
||||||
|
if (esp_rmaker_user_mapping_detect_reset(user_id)) {
|
||||||
|
ESP_LOGI(TAG, "User Node mapping reset detected.");
|
||||||
|
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_STARTED;
|
||||||
|
} else {
|
||||||
|
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_DONE;
|
||||||
|
}
|
||||||
|
if (esp_rmaker_work_queue_add_task(esp_rmaker_user_mapping_cb, NULL) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to queue user mapping task.");
|
||||||
|
goto user_mapping_error;
|
||||||
|
}
|
||||||
|
esp_rmaker_user_mapping_prov_deinit();
|
||||||
|
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
user_mapping_error:
|
||||||
|
esp_rmaker_user_mapping_cleanup_data();
|
||||||
|
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_reset_user_node_mapping(void)
|
||||||
|
{
|
||||||
|
return esp_rmaker_start_user_node_mapping(USER_RESET_ID, USER_RESET_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int esp_rmaker_user_mapping_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, uint8_t **outbuf, ssize_t *outlen, void *priv_data)
|
||||||
|
{
|
||||||
|
Rainmaker__RMakerConfigPayload *data;
|
||||||
|
data = rainmaker__rmaker_config_payload__unpack(NULL, inlen, inbuf);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
ESP_LOGE(TAG, "No Data Received");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
switch (data->msg) {
|
||||||
|
case RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeCmdSetUserMapping: {
|
||||||
|
ESP_LOGI(TAG, "Received request for node details");
|
||||||
|
Rainmaker__RMakerConfigPayload resp;
|
||||||
|
Rainmaker__RespSetUserMapping payload;
|
||||||
|
rainmaker__rmaker_config_payload__init(&resp);
|
||||||
|
rainmaker__resp_set_user_mapping__init(&payload);
|
||||||
|
|
||||||
|
if (data->payload_case != RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_USER_MAPPING) {
|
||||||
|
ESP_LOGE(TAG, "Invalid payload type in the message: %d", data->payload_case);
|
||||||
|
payload.status = RAINMAKER__RMAKER_CONFIG_STATUS__InvalidParam;
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "Got user_id = %s, secret_key = %s", data->cmd_set_user_mapping->userid, data->cmd_set_user_mapping->secretkey);
|
||||||
|
if (esp_rmaker_start_user_node_mapping(data->cmd_set_user_mapping->userid,
|
||||||
|
data->cmd_set_user_mapping->secretkey) != ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Sending status Invalid Param");
|
||||||
|
payload.status = RAINMAKER__RMAKER_CONFIG_STATUS__InvalidParam;
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "Sending status SUCCESS");
|
||||||
|
payload.status = RAINMAKER__RMAKER_CONFIG_STATUS__Success;
|
||||||
|
payload.nodeid = esp_rmaker_get_node_id();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp.msg = RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeRespSetUserMapping;
|
||||||
|
resp.payload_case = RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_USER_MAPPING;
|
||||||
|
resp.resp_set_user_mapping = &payload;
|
||||||
|
|
||||||
|
*outlen = rainmaker__rmaker_config_payload__get_packed_size(&resp);
|
||||||
|
*outbuf = (uint8_t *)MEM_ALLOC_EXTRAM(*outlen);
|
||||||
|
rainmaker__rmaker_config_payload__pack(&resp, *outbuf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "Received invalid message type: %d", data->msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rainmaker__rmaker_config_payload__free_unpacked(data, NULL);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
esp_err_t esp_rmaker_user_mapping_endpoint_create(void)
|
||||||
|
{
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
esp_err_t err = network_prov_mgr_endpoint_create(USER_MAPPING_ENDPOINT);
|
||||||
|
#else
|
||||||
|
esp_err_t err = wifi_prov_mgr_endpoint_create(USER_MAPPING_ENDPOINT);
|
||||||
|
#endif
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_user_mapping_endpoint_register(void)
|
||||||
|
{
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
return network_prov_mgr_endpoint_register(USER_MAPPING_ENDPOINT, esp_rmaker_user_mapping_handler, NULL);
|
||||||
|
#else
|
||||||
|
return wifi_prov_mgr_endpoint_register(USER_MAPPING_ENDPOINT, esp_rmaker_user_mapping_handler, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_user_mapping_prov_init(void)
|
||||||
|
{
|
||||||
|
int ret = ESP_OK;
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
ret = esp_event_handler_register(NETWORK_PROV_EVENT, NETWORK_PROV_INIT, &esp_rmaker_user_mapping_event_handler, NULL);
|
||||||
|
#else
|
||||||
|
ret = esp_event_handler_register(WIFI_PROV_EVENT, WIFI_PROV_INIT,&esp_rmaker_user_mapping_event_handler, NULL);
|
||||||
|
#endif
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
ret = esp_event_handler_register(NETWORK_PROV_EVENT, NETWORK_PROV_START, &esp_rmaker_user_mapping_event_handler, NULL);
|
||||||
|
#else
|
||||||
|
ret = esp_event_handler_register(WIFI_PROV_EVENT, WIFI_PROV_START,&esp_rmaker_user_mapping_event_handler, NULL);
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_user_mapping_prov_deinit(void)
|
||||||
|
{
|
||||||
|
#if RMAKER_USING_NETWORK_PROV
|
||||||
|
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_INIT, &esp_rmaker_user_mapping_event_handler);
|
||||||
|
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_START, &esp_rmaker_user_mapping_event_handler);
|
||||||
|
#else
|
||||||
|
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_INIT, &esp_rmaker_user_mapping_event_handler);
|
||||||
|
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_START, &esp_rmaker_user_mapping_event_handler);
|
||||||
|
#endif
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_user_mapping_state_t esp_rmaker_user_node_mapping_get_state(void)
|
||||||
|
{
|
||||||
|
return rmaker_user_mapping_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_user_node_mapping_init(void)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USER_ID_CHECK
|
||||||
|
nvs_handle handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, USER_MAPPING_NVS_NAMESPACE, NVS_READONLY, &handle);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
size_t len = 0;
|
||||||
|
if ((err = nvs_get_blob(handle, USER_ID_NVS_NAME, NULL, &len)) == ESP_OK) {
|
||||||
|
/* Some User Id found, which means that user node association is already done */
|
||||||
|
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_DONE;
|
||||||
|
}
|
||||||
|
nvs_close(handle);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_DONE;
|
||||||
|
#endif
|
||||||
|
if (!esp_rmaker_user_mapping_lock) {
|
||||||
|
esp_rmaker_user_mapping_lock = xSemaphoreCreateMutex();
|
||||||
|
if (!esp_rmaker_user_mapping_lock) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create Mutex");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_user_node_mapping_deinit(void)
|
||||||
|
{
|
||||||
|
if (esp_rmaker_user_mapping_lock) {
|
||||||
|
vSemaphoreDelete(esp_rmaker_user_mapping_lock);
|
||||||
|
esp_rmaker_user_mapping_lock = NULL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
369
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.pb-c.c
Normal file
369
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.pb-c.c
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
|
||||||
|
/* Generated from: esp_rmaker_user_mapping.proto */
|
||||||
|
|
||||||
|
/* Do not generate deprecated warnings for self */
|
||||||
|
#ifndef PROTOBUF_C__NO_DEPRECATED
|
||||||
|
#define PROTOBUF_C__NO_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "esp_rmaker_user_mapping.pb-c.h"
|
||||||
|
void rainmaker__cmd_set_user_mapping__init
|
||||||
|
(Rainmaker__CmdSetUserMapping *message)
|
||||||
|
{
|
||||||
|
static const Rainmaker__CmdSetUserMapping init_value = RAINMAKER__CMD_SET_USER_MAPPING__INIT;
|
||||||
|
*message = init_value;
|
||||||
|
}
|
||||||
|
size_t rainmaker__cmd_set_user_mapping__get_packed_size
|
||||||
|
(const Rainmaker__CmdSetUserMapping *message)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rainmaker__cmd_set_user_mapping__descriptor);
|
||||||
|
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||||
|
}
|
||||||
|
size_t rainmaker__cmd_set_user_mapping__pack
|
||||||
|
(const Rainmaker__CmdSetUserMapping *message,
|
||||||
|
uint8_t *out)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rainmaker__cmd_set_user_mapping__descriptor);
|
||||||
|
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||||
|
}
|
||||||
|
size_t rainmaker__cmd_set_user_mapping__pack_to_buffer
|
||||||
|
(const Rainmaker__CmdSetUserMapping *message,
|
||||||
|
ProtobufCBuffer *buffer)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rainmaker__cmd_set_user_mapping__descriptor);
|
||||||
|
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||||
|
}
|
||||||
|
Rainmaker__CmdSetUserMapping *
|
||||||
|
rainmaker__cmd_set_user_mapping__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
return (Rainmaker__CmdSetUserMapping *)
|
||||||
|
protobuf_c_message_unpack (&rainmaker__cmd_set_user_mapping__descriptor,
|
||||||
|
allocator, len, data);
|
||||||
|
}
|
||||||
|
void rainmaker__cmd_set_user_mapping__free_unpacked
|
||||||
|
(Rainmaker__CmdSetUserMapping *message,
|
||||||
|
ProtobufCAllocator *allocator)
|
||||||
|
{
|
||||||
|
if(!message)
|
||||||
|
return;
|
||||||
|
assert(message->base.descriptor == &rainmaker__cmd_set_user_mapping__descriptor);
|
||||||
|
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||||
|
}
|
||||||
|
void rainmaker__resp_set_user_mapping__init
|
||||||
|
(Rainmaker__RespSetUserMapping *message)
|
||||||
|
{
|
||||||
|
static const Rainmaker__RespSetUserMapping init_value = RAINMAKER__RESP_SET_USER_MAPPING__INIT;
|
||||||
|
*message = init_value;
|
||||||
|
}
|
||||||
|
size_t rainmaker__resp_set_user_mapping__get_packed_size
|
||||||
|
(const Rainmaker__RespSetUserMapping *message)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rainmaker__resp_set_user_mapping__descriptor);
|
||||||
|
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||||
|
}
|
||||||
|
size_t rainmaker__resp_set_user_mapping__pack
|
||||||
|
(const Rainmaker__RespSetUserMapping *message,
|
||||||
|
uint8_t *out)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rainmaker__resp_set_user_mapping__descriptor);
|
||||||
|
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||||
|
}
|
||||||
|
size_t rainmaker__resp_set_user_mapping__pack_to_buffer
|
||||||
|
(const Rainmaker__RespSetUserMapping *message,
|
||||||
|
ProtobufCBuffer *buffer)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rainmaker__resp_set_user_mapping__descriptor);
|
||||||
|
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||||
|
}
|
||||||
|
Rainmaker__RespSetUserMapping *
|
||||||
|
rainmaker__resp_set_user_mapping__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
return (Rainmaker__RespSetUserMapping *)
|
||||||
|
protobuf_c_message_unpack (&rainmaker__resp_set_user_mapping__descriptor,
|
||||||
|
allocator, len, data);
|
||||||
|
}
|
||||||
|
void rainmaker__resp_set_user_mapping__free_unpacked
|
||||||
|
(Rainmaker__RespSetUserMapping *message,
|
||||||
|
ProtobufCAllocator *allocator)
|
||||||
|
{
|
||||||
|
if(!message)
|
||||||
|
return;
|
||||||
|
assert(message->base.descriptor == &rainmaker__resp_set_user_mapping__descriptor);
|
||||||
|
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||||
|
}
|
||||||
|
void rainmaker__rmaker_config_payload__init
|
||||||
|
(Rainmaker__RMakerConfigPayload *message)
|
||||||
|
{
|
||||||
|
static const Rainmaker__RMakerConfigPayload init_value = RAINMAKER__RMAKER_CONFIG_PAYLOAD__INIT;
|
||||||
|
*message = init_value;
|
||||||
|
}
|
||||||
|
size_t rainmaker__rmaker_config_payload__get_packed_size
|
||||||
|
(const Rainmaker__RMakerConfigPayload *message)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rainmaker__rmaker_config_payload__descriptor);
|
||||||
|
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||||
|
}
|
||||||
|
size_t rainmaker__rmaker_config_payload__pack
|
||||||
|
(const Rainmaker__RMakerConfigPayload *message,
|
||||||
|
uint8_t *out)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rainmaker__rmaker_config_payload__descriptor);
|
||||||
|
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||||
|
}
|
||||||
|
size_t rainmaker__rmaker_config_payload__pack_to_buffer
|
||||||
|
(const Rainmaker__RMakerConfigPayload *message,
|
||||||
|
ProtobufCBuffer *buffer)
|
||||||
|
{
|
||||||
|
assert(message->base.descriptor == &rainmaker__rmaker_config_payload__descriptor);
|
||||||
|
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||||
|
}
|
||||||
|
Rainmaker__RMakerConfigPayload *
|
||||||
|
rainmaker__rmaker_config_payload__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
return (Rainmaker__RMakerConfigPayload *)
|
||||||
|
protobuf_c_message_unpack (&rainmaker__rmaker_config_payload__descriptor,
|
||||||
|
allocator, len, data);
|
||||||
|
}
|
||||||
|
void rainmaker__rmaker_config_payload__free_unpacked
|
||||||
|
(Rainmaker__RMakerConfigPayload *message,
|
||||||
|
ProtobufCAllocator *allocator)
|
||||||
|
{
|
||||||
|
if(!message)
|
||||||
|
return;
|
||||||
|
assert(message->base.descriptor == &rainmaker__rmaker_config_payload__descriptor);
|
||||||
|
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||||
|
}
|
||||||
|
static const ProtobufCFieldDescriptor rainmaker__cmd_set_user_mapping__field_descriptors[2] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"UserID",
|
||||||
|
1,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_STRING,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(Rainmaker__CmdSetUserMapping, userid),
|
||||||
|
NULL,
|
||||||
|
&protobuf_c_empty_string,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SecretKey",
|
||||||
|
2,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_STRING,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(Rainmaker__CmdSetUserMapping, secretkey),
|
||||||
|
NULL,
|
||||||
|
&protobuf_c_empty_string,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static const unsigned rainmaker__cmd_set_user_mapping__field_indices_by_name[] = {
|
||||||
|
1, /* field[1] = SecretKey */
|
||||||
|
0, /* field[0] = UserID */
|
||||||
|
};
|
||||||
|
static const ProtobufCIntRange rainmaker__cmd_set_user_mapping__number_ranges[1 + 1] =
|
||||||
|
{
|
||||||
|
{ 1, 0 },
|
||||||
|
{ 0, 2 }
|
||||||
|
};
|
||||||
|
const ProtobufCMessageDescriptor rainmaker__cmd_set_user_mapping__descriptor =
|
||||||
|
{
|
||||||
|
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||||
|
"rainmaker.CmdSetUserMapping",
|
||||||
|
"CmdSetUserMapping",
|
||||||
|
"Rainmaker__CmdSetUserMapping",
|
||||||
|
"rainmaker",
|
||||||
|
sizeof(Rainmaker__CmdSetUserMapping),
|
||||||
|
2,
|
||||||
|
rainmaker__cmd_set_user_mapping__field_descriptors,
|
||||||
|
rainmaker__cmd_set_user_mapping__field_indices_by_name,
|
||||||
|
1, rainmaker__cmd_set_user_mapping__number_ranges,
|
||||||
|
(ProtobufCMessageInit) rainmaker__cmd_set_user_mapping__init,
|
||||||
|
NULL,NULL,NULL /* reserved[123] */
|
||||||
|
};
|
||||||
|
static const ProtobufCFieldDescriptor rainmaker__resp_set_user_mapping__field_descriptors[2] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"Status",
|
||||||
|
1,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_ENUM,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(Rainmaker__RespSetUserMapping, status),
|
||||||
|
&rainmaker__rmaker_config_status__descriptor,
|
||||||
|
NULL,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"NodeId",
|
||||||
|
2,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_STRING,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(Rainmaker__RespSetUserMapping, nodeid),
|
||||||
|
NULL,
|
||||||
|
&protobuf_c_empty_string,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static const unsigned rainmaker__resp_set_user_mapping__field_indices_by_name[] = {
|
||||||
|
1, /* field[1] = NodeId */
|
||||||
|
0, /* field[0] = Status */
|
||||||
|
};
|
||||||
|
static const ProtobufCIntRange rainmaker__resp_set_user_mapping__number_ranges[1 + 1] =
|
||||||
|
{
|
||||||
|
{ 1, 0 },
|
||||||
|
{ 0, 2 }
|
||||||
|
};
|
||||||
|
const ProtobufCMessageDescriptor rainmaker__resp_set_user_mapping__descriptor =
|
||||||
|
{
|
||||||
|
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||||
|
"rainmaker.RespSetUserMapping",
|
||||||
|
"RespSetUserMapping",
|
||||||
|
"Rainmaker__RespSetUserMapping",
|
||||||
|
"rainmaker",
|
||||||
|
sizeof(Rainmaker__RespSetUserMapping),
|
||||||
|
2,
|
||||||
|
rainmaker__resp_set_user_mapping__field_descriptors,
|
||||||
|
rainmaker__resp_set_user_mapping__field_indices_by_name,
|
||||||
|
1, rainmaker__resp_set_user_mapping__number_ranges,
|
||||||
|
(ProtobufCMessageInit) rainmaker__resp_set_user_mapping__init,
|
||||||
|
NULL,NULL,NULL /* reserved[123] */
|
||||||
|
};
|
||||||
|
static const ProtobufCFieldDescriptor rainmaker__rmaker_config_payload__field_descriptors[3] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"msg",
|
||||||
|
1,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_ENUM,
|
||||||
|
0, /* quantifier_offset */
|
||||||
|
offsetof(Rainmaker__RMakerConfigPayload, msg),
|
||||||
|
&rainmaker__rmaker_config_msg_type__descriptor,
|
||||||
|
NULL,
|
||||||
|
0, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd_set_user_mapping",
|
||||||
|
10,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_MESSAGE,
|
||||||
|
offsetof(Rainmaker__RMakerConfigPayload, payload_case),
|
||||||
|
offsetof(Rainmaker__RMakerConfigPayload, cmd_set_user_mapping),
|
||||||
|
&rainmaker__cmd_set_user_mapping__descriptor,
|
||||||
|
NULL,
|
||||||
|
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resp_set_user_mapping",
|
||||||
|
11,
|
||||||
|
PROTOBUF_C_LABEL_NONE,
|
||||||
|
PROTOBUF_C_TYPE_MESSAGE,
|
||||||
|
offsetof(Rainmaker__RMakerConfigPayload, payload_case),
|
||||||
|
offsetof(Rainmaker__RMakerConfigPayload, resp_set_user_mapping),
|
||||||
|
&rainmaker__resp_set_user_mapping__descriptor,
|
||||||
|
NULL,
|
||||||
|
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
|
||||||
|
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static const unsigned rainmaker__rmaker_config_payload__field_indices_by_name[] = {
|
||||||
|
1, /* field[1] = cmd_set_user_mapping */
|
||||||
|
0, /* field[0] = msg */
|
||||||
|
2, /* field[2] = resp_set_user_mapping */
|
||||||
|
};
|
||||||
|
static const ProtobufCIntRange rainmaker__rmaker_config_payload__number_ranges[2 + 1] =
|
||||||
|
{
|
||||||
|
{ 1, 0 },
|
||||||
|
{ 10, 1 },
|
||||||
|
{ 0, 3 }
|
||||||
|
};
|
||||||
|
const ProtobufCMessageDescriptor rainmaker__rmaker_config_payload__descriptor =
|
||||||
|
{
|
||||||
|
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||||
|
"rainmaker.RMakerConfigPayload",
|
||||||
|
"RMakerConfigPayload",
|
||||||
|
"Rainmaker__RMakerConfigPayload",
|
||||||
|
"rainmaker",
|
||||||
|
sizeof(Rainmaker__RMakerConfigPayload),
|
||||||
|
3,
|
||||||
|
rainmaker__rmaker_config_payload__field_descriptors,
|
||||||
|
rainmaker__rmaker_config_payload__field_indices_by_name,
|
||||||
|
2, rainmaker__rmaker_config_payload__number_ranges,
|
||||||
|
(ProtobufCMessageInit) rainmaker__rmaker_config_payload__init,
|
||||||
|
NULL,NULL,NULL /* reserved[123] */
|
||||||
|
};
|
||||||
|
static const ProtobufCEnumValue rainmaker__rmaker_config_status__enum_values_by_number[3] =
|
||||||
|
{
|
||||||
|
{ "Success", "RAINMAKER__RMAKER_CONFIG_STATUS__Success", 0 },
|
||||||
|
{ "InvalidParam", "RAINMAKER__RMAKER_CONFIG_STATUS__InvalidParam", 1 },
|
||||||
|
{ "InvalidState", "RAINMAKER__RMAKER_CONFIG_STATUS__InvalidState", 2 },
|
||||||
|
};
|
||||||
|
static const ProtobufCIntRange rainmaker__rmaker_config_status__value_ranges[] = {
|
||||||
|
{0, 0},{0, 3}
|
||||||
|
};
|
||||||
|
static const ProtobufCEnumValueIndex rainmaker__rmaker_config_status__enum_values_by_name[3] =
|
||||||
|
{
|
||||||
|
{ "InvalidParam", 1 },
|
||||||
|
{ "InvalidState", 2 },
|
||||||
|
{ "Success", 0 },
|
||||||
|
};
|
||||||
|
const ProtobufCEnumDescriptor rainmaker__rmaker_config_status__descriptor =
|
||||||
|
{
|
||||||
|
PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
|
||||||
|
"rainmaker.RMakerConfigStatus",
|
||||||
|
"RMakerConfigStatus",
|
||||||
|
"Rainmaker__RMakerConfigStatus",
|
||||||
|
"rainmaker",
|
||||||
|
3,
|
||||||
|
rainmaker__rmaker_config_status__enum_values_by_number,
|
||||||
|
3,
|
||||||
|
rainmaker__rmaker_config_status__enum_values_by_name,
|
||||||
|
1,
|
||||||
|
rainmaker__rmaker_config_status__value_ranges,
|
||||||
|
NULL,NULL,NULL,NULL /* reserved[1234] */
|
||||||
|
};
|
||||||
|
static const ProtobufCEnumValue rainmaker__rmaker_config_msg_type__enum_values_by_number[2] =
|
||||||
|
{
|
||||||
|
{ "TypeCmdSetUserMapping", "RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeCmdSetUserMapping", 0 },
|
||||||
|
{ "TypeRespSetUserMapping", "RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeRespSetUserMapping", 1 },
|
||||||
|
};
|
||||||
|
static const ProtobufCIntRange rainmaker__rmaker_config_msg_type__value_ranges[] = {
|
||||||
|
{0, 0},{0, 2}
|
||||||
|
};
|
||||||
|
static const ProtobufCEnumValueIndex rainmaker__rmaker_config_msg_type__enum_values_by_name[2] =
|
||||||
|
{
|
||||||
|
{ "TypeCmdSetUserMapping", 0 },
|
||||||
|
{ "TypeRespSetUserMapping", 1 },
|
||||||
|
};
|
||||||
|
const ProtobufCEnumDescriptor rainmaker__rmaker_config_msg_type__descriptor =
|
||||||
|
{
|
||||||
|
PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
|
||||||
|
"rainmaker.RMakerConfigMsgType",
|
||||||
|
"RMakerConfigMsgType",
|
||||||
|
"Rainmaker__RMakerConfigMsgType",
|
||||||
|
"rainmaker",
|
||||||
|
2,
|
||||||
|
rainmaker__rmaker_config_msg_type__enum_values_by_number,
|
||||||
|
2,
|
||||||
|
rainmaker__rmaker_config_msg_type__enum_values_by_name,
|
||||||
|
1,
|
||||||
|
rainmaker__rmaker_config_msg_type__value_ranges,
|
||||||
|
NULL,NULL,NULL,NULL /* reserved[1234] */
|
||||||
|
};
|
||||||
166
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.pb-c.h
Normal file
166
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.pb-c.h
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
|
||||||
|
/* Generated from: esp_rmaker_user_mapping.proto */
|
||||||
|
|
||||||
|
#ifndef PROTOBUF_C_esp_5frmaker_5fuser_5fmapping_2eproto__INCLUDED
|
||||||
|
#define PROTOBUF_C_esp_5frmaker_5fuser_5fmapping_2eproto__INCLUDED
|
||||||
|
|
||||||
|
#include <protobuf-c/protobuf-c.h>
|
||||||
|
|
||||||
|
PROTOBUF_C__BEGIN_DECLS
|
||||||
|
|
||||||
|
#if PROTOBUF_C_VERSION_NUMBER < 1003000
|
||||||
|
# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
|
||||||
|
#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
|
||||||
|
# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _Rainmaker__CmdSetUserMapping Rainmaker__CmdSetUserMapping;
|
||||||
|
typedef struct _Rainmaker__RespSetUserMapping Rainmaker__RespSetUserMapping;
|
||||||
|
typedef struct _Rainmaker__RMakerConfigPayload Rainmaker__RMakerConfigPayload;
|
||||||
|
|
||||||
|
|
||||||
|
/* --- enums --- */
|
||||||
|
|
||||||
|
typedef enum _Rainmaker__RMakerConfigStatus {
|
||||||
|
RAINMAKER__RMAKER_CONFIG_STATUS__Success = 0,
|
||||||
|
RAINMAKER__RMAKER_CONFIG_STATUS__InvalidParam = 1,
|
||||||
|
RAINMAKER__RMAKER_CONFIG_STATUS__InvalidState = 2
|
||||||
|
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RAINMAKER__RMAKER_CONFIG_STATUS)
|
||||||
|
} Rainmaker__RMakerConfigStatus;
|
||||||
|
typedef enum _Rainmaker__RMakerConfigMsgType {
|
||||||
|
RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeCmdSetUserMapping = 0,
|
||||||
|
RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeRespSetUserMapping = 1
|
||||||
|
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RAINMAKER__RMAKER_CONFIG_MSG_TYPE)
|
||||||
|
} Rainmaker__RMakerConfigMsgType;
|
||||||
|
|
||||||
|
/* --- messages --- */
|
||||||
|
|
||||||
|
struct _Rainmaker__CmdSetUserMapping
|
||||||
|
{
|
||||||
|
ProtobufCMessage base;
|
||||||
|
char *userid;
|
||||||
|
char *secretkey;
|
||||||
|
};
|
||||||
|
#define RAINMAKER__CMD_SET_USER_MAPPING__INIT \
|
||||||
|
{ PROTOBUF_C_MESSAGE_INIT (&rainmaker__cmd_set_user_mapping__descriptor) \
|
||||||
|
, (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string }
|
||||||
|
|
||||||
|
|
||||||
|
struct _Rainmaker__RespSetUserMapping
|
||||||
|
{
|
||||||
|
ProtobufCMessage base;
|
||||||
|
Rainmaker__RMakerConfigStatus status;
|
||||||
|
char *nodeid;
|
||||||
|
};
|
||||||
|
#define RAINMAKER__RESP_SET_USER_MAPPING__INIT \
|
||||||
|
{ PROTOBUF_C_MESSAGE_INIT (&rainmaker__resp_set_user_mapping__descriptor) \
|
||||||
|
, RAINMAKER__RMAKER_CONFIG_STATUS__Success, (char *)protobuf_c_empty_string }
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD__NOT_SET = 0,
|
||||||
|
RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_USER_MAPPING = 10,
|
||||||
|
RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_USER_MAPPING = 11
|
||||||
|
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD)
|
||||||
|
} Rainmaker__RMakerConfigPayload__PayloadCase;
|
||||||
|
|
||||||
|
struct _Rainmaker__RMakerConfigPayload
|
||||||
|
{
|
||||||
|
ProtobufCMessage base;
|
||||||
|
Rainmaker__RMakerConfigMsgType msg;
|
||||||
|
Rainmaker__RMakerConfigPayload__PayloadCase payload_case;
|
||||||
|
union {
|
||||||
|
Rainmaker__CmdSetUserMapping *cmd_set_user_mapping;
|
||||||
|
Rainmaker__RespSetUserMapping *resp_set_user_mapping;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
#define RAINMAKER__RMAKER_CONFIG_PAYLOAD__INIT \
|
||||||
|
{ PROTOBUF_C_MESSAGE_INIT (&rainmaker__rmaker_config_payload__descriptor) \
|
||||||
|
, RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeCmdSetUserMapping, RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD__NOT_SET, {0} }
|
||||||
|
|
||||||
|
|
||||||
|
/* Rainmaker__CmdSetUserMapping methods */
|
||||||
|
void rainmaker__cmd_set_user_mapping__init
|
||||||
|
(Rainmaker__CmdSetUserMapping *message);
|
||||||
|
size_t rainmaker__cmd_set_user_mapping__get_packed_size
|
||||||
|
(const Rainmaker__CmdSetUserMapping *message);
|
||||||
|
size_t rainmaker__cmd_set_user_mapping__pack
|
||||||
|
(const Rainmaker__CmdSetUserMapping *message,
|
||||||
|
uint8_t *out);
|
||||||
|
size_t rainmaker__cmd_set_user_mapping__pack_to_buffer
|
||||||
|
(const Rainmaker__CmdSetUserMapping *message,
|
||||||
|
ProtobufCBuffer *buffer);
|
||||||
|
Rainmaker__CmdSetUserMapping *
|
||||||
|
rainmaker__cmd_set_user_mapping__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data);
|
||||||
|
void rainmaker__cmd_set_user_mapping__free_unpacked
|
||||||
|
(Rainmaker__CmdSetUserMapping *message,
|
||||||
|
ProtobufCAllocator *allocator);
|
||||||
|
/* Rainmaker__RespSetUserMapping methods */
|
||||||
|
void rainmaker__resp_set_user_mapping__init
|
||||||
|
(Rainmaker__RespSetUserMapping *message);
|
||||||
|
size_t rainmaker__resp_set_user_mapping__get_packed_size
|
||||||
|
(const Rainmaker__RespSetUserMapping *message);
|
||||||
|
size_t rainmaker__resp_set_user_mapping__pack
|
||||||
|
(const Rainmaker__RespSetUserMapping *message,
|
||||||
|
uint8_t *out);
|
||||||
|
size_t rainmaker__resp_set_user_mapping__pack_to_buffer
|
||||||
|
(const Rainmaker__RespSetUserMapping *message,
|
||||||
|
ProtobufCBuffer *buffer);
|
||||||
|
Rainmaker__RespSetUserMapping *
|
||||||
|
rainmaker__resp_set_user_mapping__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data);
|
||||||
|
void rainmaker__resp_set_user_mapping__free_unpacked
|
||||||
|
(Rainmaker__RespSetUserMapping *message,
|
||||||
|
ProtobufCAllocator *allocator);
|
||||||
|
/* Rainmaker__RMakerConfigPayload methods */
|
||||||
|
void rainmaker__rmaker_config_payload__init
|
||||||
|
(Rainmaker__RMakerConfigPayload *message);
|
||||||
|
size_t rainmaker__rmaker_config_payload__get_packed_size
|
||||||
|
(const Rainmaker__RMakerConfigPayload *message);
|
||||||
|
size_t rainmaker__rmaker_config_payload__pack
|
||||||
|
(const Rainmaker__RMakerConfigPayload *message,
|
||||||
|
uint8_t *out);
|
||||||
|
size_t rainmaker__rmaker_config_payload__pack_to_buffer
|
||||||
|
(const Rainmaker__RMakerConfigPayload *message,
|
||||||
|
ProtobufCBuffer *buffer);
|
||||||
|
Rainmaker__RMakerConfigPayload *
|
||||||
|
rainmaker__rmaker_config_payload__unpack
|
||||||
|
(ProtobufCAllocator *allocator,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *data);
|
||||||
|
void rainmaker__rmaker_config_payload__free_unpacked
|
||||||
|
(Rainmaker__RMakerConfigPayload *message,
|
||||||
|
ProtobufCAllocator *allocator);
|
||||||
|
/* --- per-message closures --- */
|
||||||
|
|
||||||
|
typedef void (*Rainmaker__CmdSetUserMapping_Closure)
|
||||||
|
(const Rainmaker__CmdSetUserMapping *message,
|
||||||
|
void *closure_data);
|
||||||
|
typedef void (*Rainmaker__RespSetUserMapping_Closure)
|
||||||
|
(const Rainmaker__RespSetUserMapping *message,
|
||||||
|
void *closure_data);
|
||||||
|
typedef void (*Rainmaker__RMakerConfigPayload_Closure)
|
||||||
|
(const Rainmaker__RMakerConfigPayload *message,
|
||||||
|
void *closure_data);
|
||||||
|
|
||||||
|
/* --- services --- */
|
||||||
|
|
||||||
|
|
||||||
|
/* --- descriptors --- */
|
||||||
|
|
||||||
|
extern const ProtobufCEnumDescriptor rainmaker__rmaker_config_status__descriptor;
|
||||||
|
extern const ProtobufCEnumDescriptor rainmaker__rmaker_config_msg_type__descriptor;
|
||||||
|
extern const ProtobufCMessageDescriptor rainmaker__cmd_set_user_mapping__descriptor;
|
||||||
|
extern const ProtobufCMessageDescriptor rainmaker__resp_set_user_mapping__descriptor;
|
||||||
|
extern const ProtobufCMessageDescriptor rainmaker__rmaker_config_payload__descriptor;
|
||||||
|
|
||||||
|
PROTOBUF_C__END_DECLS
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* PROTOBUF_C_esp_5frmaker_5fuser_5fmapping_2eproto__INCLUDED */
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package rainmaker;
|
||||||
|
|
||||||
|
enum RMakerConfigStatus {
|
||||||
|
Success = 0;
|
||||||
|
InvalidParam = 1;
|
||||||
|
InvalidState = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CmdSetUserMapping {
|
||||||
|
string UserID = 1;
|
||||||
|
string SecretKey = 2;
|
||||||
|
}
|
||||||
|
message RespSetUserMapping {
|
||||||
|
RMakerConfigStatus Status = 1;
|
||||||
|
string NodeId = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RMakerConfigMsgType {
|
||||||
|
TypeCmdSetUserMapping = 0;
|
||||||
|
TypeRespSetUserMapping = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RMakerConfigPayload {
|
||||||
|
RMakerConfigMsgType msg = 1;
|
||||||
|
oneof payload {
|
||||||
|
CmdSetUserMapping cmd_set_user_mapping = 10;
|
||||||
|
RespSetUserMapping resp_set_user_mapping = 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
135
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt.c
Normal file
135
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt.c
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_rmaker_mqtt_glue.h>
|
||||||
|
#include <esp_rmaker_client_data.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
|
||||||
|
#include "esp_rmaker_mqtt.h"
|
||||||
|
#include "esp_rmaker_mqtt_budget.h"
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_mqtt";
|
||||||
|
static esp_rmaker_mqtt_config_t g_mqtt_config;
|
||||||
|
|
||||||
|
esp_rmaker_mqtt_conn_params_t *esp_rmaker_mqtt_get_conn_params(void)
|
||||||
|
{
|
||||||
|
if (g_mqtt_config.get_conn_params) {
|
||||||
|
return g_mqtt_config.get_conn_params();
|
||||||
|
} else {
|
||||||
|
return esp_rmaker_get_mqtt_conn_params();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_setup(esp_rmaker_mqtt_config_t mqtt_config)
|
||||||
|
{
|
||||||
|
g_mqtt_config = mqtt_config;
|
||||||
|
g_mqtt_config.setup_done = true;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_init(esp_rmaker_mqtt_conn_params_t *conn_params)
|
||||||
|
{
|
||||||
|
if (!g_mqtt_config.setup_done) {
|
||||||
|
esp_rmaker_mqtt_glue_setup(&g_mqtt_config);
|
||||||
|
}
|
||||||
|
if (g_mqtt_config.init) {
|
||||||
|
esp_err_t err = g_mqtt_config.init(conn_params);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
if (esp_rmaker_mqtt_budgeting_init() != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to initialise MQTT Budgeting.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "esp_rmaker_mqtt_init not registered");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_rmaker_mqtt_deinit(void)
|
||||||
|
{
|
||||||
|
esp_rmaker_mqtt_budgeting_deinit();
|
||||||
|
if (g_mqtt_config.deinit) {
|
||||||
|
return g_mqtt_config.deinit();
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "esp_rmaker_mqtt_deinit not registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_connect(void)
|
||||||
|
{
|
||||||
|
if (g_mqtt_config.connect) {
|
||||||
|
esp_err_t err = g_mqtt_config.connect();
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
esp_rmaker_mqtt_budgeting_start();
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "esp_rmaker_mqtt_connect not registered");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_disconnect(void)
|
||||||
|
{
|
||||||
|
esp_rmaker_mqtt_budgeting_stop();
|
||||||
|
if (g_mqtt_config.disconnect) {
|
||||||
|
return g_mqtt_config.disconnect();
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "esp_rmaker_mqtt_disconnect not registered");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_subscribe(const char *topic, esp_rmaker_mqtt_subscribe_cb_t cb, uint8_t qos, void *priv_data)
|
||||||
|
{
|
||||||
|
if (g_mqtt_config.subscribe) {
|
||||||
|
return g_mqtt_config.subscribe(topic, cb, qos, priv_data);
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "esp_rmaker_mqtt_subscribe not registered");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_unsubscribe(const char *topic)
|
||||||
|
{
|
||||||
|
if (g_mqtt_config.unsubscribe) {
|
||||||
|
return g_mqtt_config.unsubscribe(topic);
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "esp_rmaker_mqtt_unsubscribe not registered");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_publish(const char *topic, void *data, size_t data_len, uint8_t qos, int *msg_id)
|
||||||
|
{
|
||||||
|
if (esp_rmaker_mqtt_is_budget_available() != true) {
|
||||||
|
ESP_LOGE(TAG, "Out of MQTT Budget. Dropping publish message.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (g_mqtt_config.publish) {
|
||||||
|
esp_err_t err = g_mqtt_config.publish(topic, data, data_len, qos, msg_id);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
esp_rmaker_mqtt_decrease_budget(1);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "esp_rmaker_mqtt_publish not registered");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_rmaker_create_mqtt_topic(char *buf, size_t buf_size, const char *topic_suffix, const char *rule)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_MQTT_USE_BASIC_INGEST_TOPICS
|
||||||
|
snprintf(buf, buf_size, "$aws/rules/%s/node/%s/%s", rule, esp_rmaker_get_node_id(), topic_suffix);
|
||||||
|
#else
|
||||||
|
snprintf(buf, buf_size, "node/%s/%s", esp_rmaker_get_node_id(), topic_suffix);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
183
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt_budget.c
Normal file
183
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt_budget.c
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
static const char *TAG = "esp_rmaker_mqtt_budget";
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||||
|
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/timers.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
|
||||||
|
#define DEFAULT_BUDGET CONFIG_ESP_RMAKER_MQTT_DEFAULT_BUDGET
|
||||||
|
#define MAX_BUDGET CONFIG_ESP_RMAKER_MQTT_MAX_BUDGET
|
||||||
|
#define BUDGET_REVIVE_COUNT CONFIG_ESP_RMAKER_MQTT_BUDGET_REVIVE_COUNT
|
||||||
|
#define BUDGET_REVIVE_PERIOD CONFIG_ESP_RMAKER_MQTT_BUDGET_REVIVE_PERIOD
|
||||||
|
|
||||||
|
static int16_t mqtt_budget = DEFAULT_BUDGET;
|
||||||
|
static TimerHandle_t mqtt_budget_timer;
|
||||||
|
static SemaphoreHandle_t mqtt_budget_lock;
|
||||||
|
#define SEMAPHORE_DELAY_MSEC 500
|
||||||
|
|
||||||
|
bool esp_rmaker_mqtt_is_budget_available(void)
|
||||||
|
{
|
||||||
|
if (mqtt_budget_lock == NULL) {
|
||||||
|
ESP_LOGW(TAG, "MQTT budgeting not started yet. Allowing publish.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (xSemaphoreTake(mqtt_budget_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||||
|
ESP_LOGW(TAG, "Could not acquire MQTT budget lock. Allowing publish.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int16_t budget = mqtt_budget;
|
||||||
|
xSemaphoreGive(mqtt_budget_lock);
|
||||||
|
return budget ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_increase_budget(uint8_t budget)
|
||||||
|
{
|
||||||
|
if (mqtt_budget_lock == NULL) {
|
||||||
|
ESP_LOGW(TAG, "MQTT budgeting not started. Not increasing the budget.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (xSemaphoreTake(mqtt_budget_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to increase MQTT budget.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
mqtt_budget += budget;
|
||||||
|
if (mqtt_budget > MAX_BUDGET) {
|
||||||
|
mqtt_budget = MAX_BUDGET;
|
||||||
|
}
|
||||||
|
xSemaphoreGive(mqtt_budget_lock);
|
||||||
|
ESP_LOGD(TAG, "MQTT budget increased to %d", mqtt_budget);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_decrease_budget(uint8_t budget)
|
||||||
|
{
|
||||||
|
if (mqtt_budget_lock == NULL) {
|
||||||
|
ESP_LOGW(TAG, "MQTT budgeting not started. Not decreasing the budget.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (xSemaphoreTake(mqtt_budget_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to decrease MQTT budget.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
mqtt_budget -= budget;
|
||||||
|
if (mqtt_budget < 0) {
|
||||||
|
mqtt_budget = 0;
|
||||||
|
}
|
||||||
|
xSemaphoreGive(mqtt_budget_lock);
|
||||||
|
ESP_LOGD(TAG, "MQTT budget decreased to %d.", mqtt_budget);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_mqtt_revive_budget(TimerHandle_t handle)
|
||||||
|
{
|
||||||
|
esp_rmaker_mqtt_increase_budget(BUDGET_REVIVE_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_start(void)
|
||||||
|
{
|
||||||
|
if (mqtt_budget_timer) {
|
||||||
|
xTimerStart(mqtt_budget_timer, 0);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_stop(void)
|
||||||
|
{
|
||||||
|
if (mqtt_budget_timer) {
|
||||||
|
xTimerStop(mqtt_budget_timer, 100);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_deinit(void)
|
||||||
|
{
|
||||||
|
if (mqtt_budget_timer) {
|
||||||
|
esp_rmaker_mqtt_budgeting_stop();
|
||||||
|
xTimerDelete(mqtt_budget_timer, 100);
|
||||||
|
mqtt_budget_timer = NULL;
|
||||||
|
}
|
||||||
|
if (mqtt_budget_lock) {
|
||||||
|
vSemaphoreDelete(mqtt_budget_lock);
|
||||||
|
mqtt_budget_lock = NULL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_init(void)
|
||||||
|
{
|
||||||
|
if (mqtt_budget_timer) {
|
||||||
|
ESP_LOGI(TAG, "MQTT budgeting already initialised.");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
mqtt_budget_lock = xSemaphoreCreateMutex();
|
||||||
|
if (!mqtt_budget_lock) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mqtt_budget_timer = xTimerCreate("mqtt_budget_tm", (BUDGET_REVIVE_PERIOD * 1000) / portTICK_PERIOD_MS,
|
||||||
|
pdTRUE, NULL, esp_rmaker_mqtt_revive_budget);
|
||||||
|
if (mqtt_budget_timer) {
|
||||||
|
ESP_LOGI(TAG, "MQTT Budgeting initialised. Default: %d, Max: %d, Revive count: %d, Revive period: %d",
|
||||||
|
DEFAULT_BUDGET, MAX_BUDGET, BUDGET_REVIVE_COUNT, BUDGET_REVIVE_PERIOD);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* ! CONFIG_ESP_RMAKER_MQTT_ENABLE_BUDGETING */
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_init(void)
|
||||||
|
{
|
||||||
|
/* Adding a print only here, because this is always going to be the first function
|
||||||
|
* to be invoked since it is called from MQTT init. Else, MQTT itself is going to fail.
|
||||||
|
*/
|
||||||
|
ESP_LOGW(TAG, "MQTT Budgeting is not enabled.");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_deinit(void)
|
||||||
|
{
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_stop(void)
|
||||||
|
{
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_start(void)
|
||||||
|
{
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_increase_budget(uint8_t budget)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_decrease_budget(uint8_t budget)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool esp_rmaker_mqtt_is_budget_available(void)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ! CONFIG_ESP_RMAKER_MQTT_ENABLE_BUDGETING */
|
||||||
17
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt_budget.h
Normal file
17
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt_budget.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_init(void);
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_deinit(void);
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_stop(void);
|
||||||
|
esp_err_t esp_rmaker_mqtt_budgeting_start(void);
|
||||||
|
esp_err_t esp_rmaker_mqtt_increase_budget(uint8_t budget);
|
||||||
|
esp_err_t esp_rmaker_mqtt_decrease_budget(uint8_t budget);
|
||||||
726
components/esp_rainmaker/src/ota/esp_rmaker_ota.c
Normal file
726
components/esp_rainmaker/src/ota/esp_rmaker_ota.c
Normal file
@@ -0,0 +1,726 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/timers.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <esp_efuse.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_ota_ops.h>
|
||||||
|
#include <esp_partition.h>
|
||||||
|
#include <esp_https_ota.h>
|
||||||
|
#include <esp_wifi_types.h>
|
||||||
|
#include <esp_wifi.h>
|
||||||
|
#include <nvs.h>
|
||||||
|
#include <json_parser.h>
|
||||||
|
#if CONFIG_BT_ENABLED
|
||||||
|
#include <esp_bt.h>
|
||||||
|
#endif /* CONFIG_BT_ENABLED */
|
||||||
|
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include <esp_rmaker_common_events.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include "esp_rmaker_internal.h"
|
||||||
|
#include "esp_rmaker_ota_internal.h"
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||||
|
// Features supported in 4.4+
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USE_CERT_BUNDLE
|
||||||
|
#define ESP_RMAKER_USE_CERT_BUNDLE
|
||||||
|
#include <esp_crt_bundle.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_USE_CERT_BUNDLE
|
||||||
|
#warning "Certificate Bundle not supported below IDF v4.4. Using provided certificate instead."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !IDF4.4 */
|
||||||
|
static const char *TAG = "esp_rmaker_ota";
|
||||||
|
|
||||||
|
#define OTA_REBOOT_TIMER_SEC 10
|
||||||
|
#define DEF_HTTP_TX_BUFFER_SIZE 1024
|
||||||
|
#define DEF_HTTP_RX_BUFFER_SIZE CONFIG_ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE
|
||||||
|
#define RMAKER_OTA_ROLLBACK_WAIT_PERIOD CONFIG_ESP_RMAKER_OTA_ROLLBACK_WAIT_PERIOD
|
||||||
|
extern const char esp_rmaker_ota_def_cert[] asm("_binary_rmaker_ota_server_crt_start");
|
||||||
|
const char *ESP_RMAKER_OTA_DEFAULT_SERVER_CERT = esp_rmaker_ota_def_cert;
|
||||||
|
ESP_EVENT_DEFINE_BASE(RMAKER_OTA_EVENT);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OTA_OK = 0,
|
||||||
|
OTA_ERR,
|
||||||
|
OTA_DELAYED
|
||||||
|
} esp_rmaker_ota_action_t;
|
||||||
|
|
||||||
|
static esp_rmaker_ota_t *g_ota_priv;
|
||||||
|
|
||||||
|
char *esp_rmaker_ota_status_to_string(ota_status_t status)
|
||||||
|
{
|
||||||
|
switch (status) {
|
||||||
|
case OTA_STATUS_IN_PROGRESS:
|
||||||
|
return "in-progress";
|
||||||
|
case OTA_STATUS_SUCCESS:
|
||||||
|
return "success";
|
||||||
|
case OTA_STATUS_FAILED:
|
||||||
|
return "failed";
|
||||||
|
case OTA_STATUS_DELAYED:
|
||||||
|
return "delayed";
|
||||||
|
case OTA_STATUS_REJECTED:
|
||||||
|
return "rejected";
|
||||||
|
default:
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_ota_event_t esp_rmaker_ota_status_to_event(ota_status_t status)
|
||||||
|
{
|
||||||
|
switch (status) {
|
||||||
|
case OTA_STATUS_IN_PROGRESS:
|
||||||
|
return RMAKER_OTA_EVENT_IN_PROGRESS;
|
||||||
|
case OTA_STATUS_SUCCESS:
|
||||||
|
return RMAKER_OTA_EVENT_SUCCESSFUL;
|
||||||
|
case OTA_STATUS_FAILED:
|
||||||
|
return RMAKER_OTA_EVENT_FAILED;
|
||||||
|
case OTA_STATUS_DELAYED:
|
||||||
|
return RMAKER_OTA_EVENT_DELAYED;
|
||||||
|
case OTA_STATUS_REJECTED:
|
||||||
|
return RMAKER_OTA_EVENT_REJECTED;
|
||||||
|
default:
|
||||||
|
ESP_LOGD(TAG, "No Rmaker OTA Event for given status: %d: %s",
|
||||||
|
status, esp_rmaker_ota_status_to_string(status));
|
||||||
|
}
|
||||||
|
return RMAKER_OTA_EVENT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline esp_err_t esp_rmaker_ota_post_event(esp_rmaker_event_t event_id, void* data, size_t data_size)
|
||||||
|
{
|
||||||
|
return esp_event_post(RMAKER_OTA_EVENT, event_id, data, data_size, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_ota_report_status(esp_rmaker_ota_handle_t ota_handle, ota_status_t status, char *additional_info)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Reporting %s: %s", esp_rmaker_ota_status_to_string(status), additional_info);
|
||||||
|
|
||||||
|
if (!ota_handle) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)ota_handle;
|
||||||
|
esp_err_t err = ESP_FAIL;
|
||||||
|
if (ota->type == OTA_USING_PARAMS) {
|
||||||
|
err = esp_rmaker_ota_report_status_using_params(ota_handle, status, additional_info);
|
||||||
|
} else if (ota->type == OTA_USING_TOPICS) {
|
||||||
|
err = esp_rmaker_ota_report_status_using_topics(ota_handle, status, additional_info);
|
||||||
|
}
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)ota_handle;
|
||||||
|
ota->last_reported_status = status;
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_post_event(esp_rmaker_ota_status_to_event(status), additional_info, strlen(additional_info) + 1);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_rmaker_ota_common_cb(void *priv)
|
||||||
|
{
|
||||||
|
if (!priv) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)priv;
|
||||||
|
if (!ota->url) {
|
||||||
|
goto ota_finish;
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_data_t ota_data = {
|
||||||
|
.url = ota->url,
|
||||||
|
.filesize = ota->filesize,
|
||||||
|
.fw_version = ota->fw_version,
|
||||||
|
.ota_job_id = (char *)ota->transient_priv,
|
||||||
|
.server_cert = ota->server_cert,
|
||||||
|
.priv = ota->priv,
|
||||||
|
.metadata = ota->metadata
|
||||||
|
};
|
||||||
|
ota->ota_cb((esp_rmaker_ota_handle_t) ota, &ota_data);
|
||||||
|
ota_finish:
|
||||||
|
if (ota->type == OTA_USING_PARAMS) {
|
||||||
|
esp_rmaker_ota_finish_using_params(ota);
|
||||||
|
} else if (ota->type == OTA_USING_TOPICS) {
|
||||||
|
esp_rmaker_ota_finish_using_topics(ota);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t validate_image_header(esp_rmaker_ota_handle_t ota_handle,
|
||||||
|
esp_app_desc_t *new_app_info)
|
||||||
|
{
|
||||||
|
if (new_app_info == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||||
|
esp_app_desc_t running_app_info;
|
||||||
|
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
|
||||||
|
ESP_LOGD(TAG, "Running firmware version: %s", running_app_info.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_ESP_RMAKER_SKIP_PROJECT_NAME_CHECK
|
||||||
|
if (memcmp(new_app_info->project_name, running_app_info.project_name, sizeof(new_app_info->project_name)) != 0) {
|
||||||
|
ESP_LOGW(TAG, "OTA Image built for Project: %s. Expected: %s",
|
||||||
|
new_app_info->project_name, running_app_info.project_name);
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_REJECTED, "Project Name mismatch");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_ESP_RMAKER_SKIP_VERSION_CHECK
|
||||||
|
if (memcmp(new_app_info->version, running_app_info.version, sizeof(new_app_info->version)) == 0) {
|
||||||
|
ESP_LOGW(TAG, "Current running version is same as the new. We will not continue the update.");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_REJECTED, "Same version received");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_ESP_RMAKER_SKIP_SECURE_VERSION_CHECK
|
||||||
|
if (esp_efuse_check_secure_version(new_app_info->secure_version) == false) {
|
||||||
|
ESP_LOGW(TAG, "New secure version is lower than stored in efuse. We will not continue the update.");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_REJECTED, "Lower secure version received");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT
|
||||||
|
|
||||||
|
/* Retry delay for cases wherein time info itself is not available */
|
||||||
|
#define OTA_FETCH_RETRY_DELAY 30
|
||||||
|
#define MINUTES_IN_DAY (24 * 60)
|
||||||
|
#define OTA_DELAY_TIME_BUFFER 5
|
||||||
|
|
||||||
|
/* Check if time data is available in the metadata. Format
|
||||||
|
* {"download_window":{"end":1155,"start":1080},"validity":{"end":1665426600,"start":1665081000}}
|
||||||
|
*/
|
||||||
|
esp_rmaker_ota_action_t esp_rmaker_ota_handle_time(jparse_ctx_t *jptr, esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||||
|
{
|
||||||
|
bool time_info = false;
|
||||||
|
int start_min = -1, end_min = -1, start_date = -1, end_date = -1;
|
||||||
|
if (json_obj_get_object(jptr, "download_window") == 0) {
|
||||||
|
/* Download window means specific time of day. Eg, Between 02:00am and 05:00am only */
|
||||||
|
time_info = true;
|
||||||
|
json_obj_get_int(jptr, "start", &start_min);
|
||||||
|
json_obj_get_int(jptr, "end", &end_min);
|
||||||
|
json_obj_leave_object(jptr);
|
||||||
|
ESP_LOGI(TAG, "Download Window : %d %d", start_min, end_min);
|
||||||
|
}
|
||||||
|
if (json_obj_get_object(jptr, "validity") == 0) {
|
||||||
|
/* Validity indicates start and end epoch time, typicaly useful if OTA is to be performed between some dates */
|
||||||
|
time_info = true;
|
||||||
|
json_obj_get_int(jptr, "start", &start_date);
|
||||||
|
json_obj_get_int(jptr, "end", &end_date);
|
||||||
|
json_obj_leave_object(jptr);
|
||||||
|
ESP_LOGI(TAG, "Validity : %d %d", start_date, end_date);
|
||||||
|
}
|
||||||
|
if (time_info) {
|
||||||
|
/* If time info is present, but time is not yet synchronised, we will re-fetch OTA after some time */
|
||||||
|
if (esp_rmaker_time_check() != true) {
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_DELAYED, "No time information available yet.");
|
||||||
|
esp_rmaker_ota_fetch_with_delay(OTA_FETCH_RETRY_DELAY);
|
||||||
|
return OTA_DELAYED;
|
||||||
|
}
|
||||||
|
time_t current_timestamp = 0;
|
||||||
|
struct tm current_time = {0};
|
||||||
|
time(¤t_timestamp);
|
||||||
|
localtime_r(¤t_timestamp, ¤t_time);
|
||||||
|
|
||||||
|
/* Check for date validity first */
|
||||||
|
if ((start_date != -1) && (current_timestamp < start_date)) {
|
||||||
|
int delay_time = start_date - current_timestamp;
|
||||||
|
/* The delay logic here can include the start_min and end_min as well, but it makes the logic quite complex,
|
||||||
|
* just for a minor optimisation.
|
||||||
|
*/
|
||||||
|
ESP_LOGI(TAG, "Delaying OTA by %d seconds (%d min) as it is not valid yet.", delay_time, delay_time / 60);
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_DELAYED, "Not within valid window.");
|
||||||
|
esp_rmaker_ota_fetch_with_delay(delay_time + OTA_DELAY_TIME_BUFFER);
|
||||||
|
return OTA_DELAYED;
|
||||||
|
} else if ((end_date != -1) && (current_timestamp > end_date)) {
|
||||||
|
ESP_LOGE(TAG, "OTA download window lapsed");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "OTA download window lapsed.");
|
||||||
|
return OTA_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for download window */
|
||||||
|
if (start_min != -1) {
|
||||||
|
/* end_min is required if start_min is provided */
|
||||||
|
if (end_min == -1) {
|
||||||
|
ESP_LOGE(TAG, "Download window should have an end time if start time is specified.");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Invalid download window specified.");
|
||||||
|
return OTA_ERR;
|
||||||
|
}
|
||||||
|
int cur_min = current_time.tm_hour * 60 + current_time.tm_min;
|
||||||
|
if (start_min > end_min) {
|
||||||
|
/* This means that the window is across midnight (Eg. 23:00 to 02:00 i.e. 1380 to 120).
|
||||||
|
* We are just moving the window here such that start_min becomes 0 and the comparisons are simplified.
|
||||||
|
* For this example, diff_min will be 1440 - 1380 = 60.
|
||||||
|
* Effective end_min: 180
|
||||||
|
* If cur_time is 18:00, effective cur_time = 1080 + 60 = 1140
|
||||||
|
* If cur_time is 23:30, effective cur_time = 1410 + 60 = 1470 ( > MINUTES_IN_DAY)
|
||||||
|
* So, cur_time = 1470 - 1440 = 30
|
||||||
|
* */
|
||||||
|
int diff_min = MINUTES_IN_DAY - start_min;
|
||||||
|
start_min = 0;
|
||||||
|
end_min += diff_min;
|
||||||
|
cur_min += diff_min;
|
||||||
|
if (cur_min >= MINUTES_IN_DAY) {
|
||||||
|
cur_min -= MINUTES_IN_DAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Current time is within OTA download window */
|
||||||
|
if ((cur_min >= start_min) && (cur_min <= end_min)) {
|
||||||
|
ESP_LOGI(TAG, "OTA received within download window.");
|
||||||
|
return OTA_OK;
|
||||||
|
} else {
|
||||||
|
/* Delay the OTA if it is not in the download window. Even if it later goes outside the valid date range,
|
||||||
|
* that will be handled in subsequent ota fetch. Reporting failure here itself would mark the OTA job
|
||||||
|
* as failed and the node will no more get the OTA even if it tries to fetch it again due to a reboot or
|
||||||
|
* other action within the download window.
|
||||||
|
*/
|
||||||
|
int delay_min = start_min - cur_min;
|
||||||
|
if (delay_min < 0) {
|
||||||
|
delay_min += MINUTES_IN_DAY;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Delaying OTA by %d seconds (%d min) as it is not within download window.", delay_min * 60, delay_min);
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_DELAYED, "Not within download window.");
|
||||||
|
esp_rmaker_ota_fetch_with_delay(delay_min * 60 + OTA_DELAY_TIME_BUFFER);
|
||||||
|
return OTA_DELAYED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "OTA received within validity period.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OTA_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT */
|
||||||
|
|
||||||
|
esp_rmaker_ota_action_t esp_rmaker_ota_handle_metadata(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||||
|
{
|
||||||
|
if (!ota_data->metadata) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_action_t ota_action = OTA_OK;
|
||||||
|
jparse_ctx_t jctx;
|
||||||
|
if (json_parse_start(&jctx, ota_data->metadata, strlen(ota_data->metadata)) == 0) {
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT
|
||||||
|
/* Handle OTA timing data, if any */
|
||||||
|
ota_action = esp_rmaker_ota_handle_time(&jctx, ota_handle, ota_data);
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT */
|
||||||
|
json_parse_end(&jctx);
|
||||||
|
}
|
||||||
|
return ota_action;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_ota_default_cb(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||||
|
{
|
||||||
|
if (!ota_data->url) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
/* Handle OTA metadata, if any */
|
||||||
|
if (ota_data->metadata) {
|
||||||
|
if (esp_rmaker_ota_handle_metadata(ota_handle, ota_data) != OTA_OK) {
|
||||||
|
ESP_LOGW(TAG, "Cannot proceed with the OTA as per the metadata received.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_post_event(RMAKER_OTA_EVENT_STARTING, NULL, 0);
|
||||||
|
int buffer_size_tx = DEF_HTTP_TX_BUFFER_SIZE;
|
||||||
|
/* In case received url is longer, we will increase the tx buffer size
|
||||||
|
* to accomodate the longer url and other headers.
|
||||||
|
*/
|
||||||
|
if (strlen(ota_data->url) > buffer_size_tx) {
|
||||||
|
buffer_size_tx = strlen(ota_data->url) + 128;
|
||||||
|
}
|
||||||
|
esp_err_t ota_finish_err = ESP_OK;
|
||||||
|
esp_http_client_config_t config = {
|
||||||
|
.url = ota_data->url,
|
||||||
|
#ifdef ESP_RMAKER_USE_CERT_BUNDLE
|
||||||
|
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||||
|
#else
|
||||||
|
.cert_pem = ota_data->server_cert,
|
||||||
|
#endif
|
||||||
|
.timeout_ms = 5000,
|
||||||
|
.buffer_size = DEF_HTTP_RX_BUFFER_SIZE,
|
||||||
|
.buffer_size_tx = buffer_size_tx,
|
||||||
|
.keep_alive_enable = true
|
||||||
|
};
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_SKIP_COMMON_NAME_CHECK
|
||||||
|
config.skip_cert_common_name_check = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_https_ota_config_t ota_config = {
|
||||||
|
.http_config = &config,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ota_data->filesize) {
|
||||||
|
ESP_LOGD(TAG, "Received file size: %d", ota_data->filesize);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Starting OTA Upgrade");
|
||||||
|
|
||||||
|
/* Using a warning just to highlight the message */
|
||||||
|
ESP_LOGW(TAG, "Starting OTA. This may take time.");
|
||||||
|
esp_https_ota_handle_t https_ota_handle = NULL;
|
||||||
|
esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "ESP HTTPS OTA Begin failed");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||||
|
/* Get the current Wi-Fi power save type. In case OTA fails and we need this
|
||||||
|
* to restore power saving.
|
||||||
|
*/
|
||||||
|
wifi_ps_type_t ps_type;
|
||||||
|
esp_wifi_get_ps(&ps_type);
|
||||||
|
/* Disable Wi-Fi power save to speed up OTA, iff BT is controller is idle/disabled.
|
||||||
|
* Co-ex requirement, device panics otherwise.*/
|
||||||
|
#if CONFIG_BT_ENABLED
|
||||||
|
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||||
|
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||||
|
#endif /* CONFIG_BT_ENABLED */
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||||
|
|
||||||
|
esp_app_desc_t app_desc;
|
||||||
|
err = esp_https_ota_get_img_desc(https_ota_handle, &app_desc);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "esp_https_ota_read_img_desc failed");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Failed to read image decription");
|
||||||
|
goto ota_end;
|
||||||
|
}
|
||||||
|
err = validate_image_header(ota_handle, &app_desc);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "image header verification failed");
|
||||||
|
goto ota_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Downloading Firmware Image");
|
||||||
|
int count = 0;
|
||||||
|
while (1) {
|
||||||
|
err = esp_https_ota_perform(https_ota_handle);
|
||||||
|
if (err == ESP_ERR_INVALID_VERSION) {
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_REJECTED, "Chip revision mismatch");
|
||||||
|
goto ota_end;
|
||||||
|
}
|
||||||
|
if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* esp_https_ota_perform returns after every read operation which gives user the ability to
|
||||||
|
* monitor the status of OTA upgrade by calling esp_https_ota_get_image_len_read, which gives length of image
|
||||||
|
* data read so far.
|
||||||
|
* We are using a counter just to reduce the number of prints
|
||||||
|
*/
|
||||||
|
|
||||||
|
count++;
|
||||||
|
if (count == 50) {
|
||||||
|
ESP_LOGI(TAG, "Image bytes read: %d", esp_https_ota_get_image_len_read(https_ota_handle));
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed %s", esp_err_to_name(err));
|
||||||
|
char description[40];
|
||||||
|
snprintf(description, sizeof(description), "OTA failed: Error %s", esp_err_to_name(err));
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) {
|
||||||
|
// the OTA image was not completely received and user can customise the response to this situation.
|
||||||
|
ESP_LOGE(TAG, "Complete data was not received.");
|
||||||
|
} else {
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Firmware Image download complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
ota_end:
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||||
|
#ifdef CONFIG_BT_ENABLED
|
||||||
|
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||||
|
esp_wifi_set_ps(ps_type);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
esp_wifi_set_ps(ps_type);
|
||||||
|
#endif /* CONFIG_BT_ENABLED */
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||||
|
ota_finish_err = esp_https_ota_finish(https_ota_handle);
|
||||||
|
if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) {
|
||||||
|
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
|
||||||
|
nvs_handle handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
uint8_t ota_update = 1;
|
||||||
|
nvs_set_blob(handle, RMAKER_OTA_UPDATE_FLAG_NVS_NAME, &ota_update, sizeof(ota_update));
|
||||||
|
nvs_close(handle);
|
||||||
|
}
|
||||||
|
/* Success will be reported after a reboot since Rollback is enabled */
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Rebooting into new firmware");
|
||||||
|
#else
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_SUCCESS, "OTA Upgrade finished successfully");
|
||||||
|
#endif
|
||||||
|
#ifndef CONFIG_ESP_RMAKER_OTA_DISABLE_AUTO_REBOOT
|
||||||
|
ESP_LOGI(TAG, "OTA upgrade successful. Rebooting in %d seconds...", OTA_REBOOT_TIMER_SEC);
|
||||||
|
esp_rmaker_reboot(OTA_REBOOT_TIMER_SEC);
|
||||||
|
#else
|
||||||
|
ESP_LOGI(TAG, "OTA upgrade successful. Auto reboot is disabled. Requesting a Reboot via Event handler.");
|
||||||
|
esp_rmaker_ota_post_event(RMAKER_OTA_EVENT_REQ_FOR_REBOOT, NULL, 0);
|
||||||
|
#endif
|
||||||
|
return ESP_OK;
|
||||||
|
} else {
|
||||||
|
if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||||
|
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Image validation failed");
|
||||||
|
} else {
|
||||||
|
/* Not reporting status here, because relevant error will already be reported
|
||||||
|
* in some earlier step
|
||||||
|
*/
|
||||||
|
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed %d", ota_finish_err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void* event_data)
|
||||||
|
{
|
||||||
|
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)arg;
|
||||||
|
esp_rmaker_ota_diag_status_t diag_status = OTA_DIAG_STATUS_SUCCESS;
|
||||||
|
if (ota->ota_diag) {
|
||||||
|
esp_rmaker_ota_diag_priv_t ota_diag_priv = {
|
||||||
|
.state = OTA_DIAG_STATE_POST_MQTT,
|
||||||
|
.rmaker_ota = ota->validation_in_progress
|
||||||
|
};
|
||||||
|
diag_status = ota->ota_diag(&ota_diag_priv, ota->priv);
|
||||||
|
}
|
||||||
|
if (diag_status == OTA_DIAG_STATUS_SUCCESS) {
|
||||||
|
esp_rmaker_ota_mark_valid();
|
||||||
|
} else if (diag_status == OTA_DIAG_STATUS_FAIL) {
|
||||||
|
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
|
||||||
|
esp_rmaker_ota_mark_invalid();
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Waiting for application to validate OTA.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_erase_rollback_flag(void)
|
||||||
|
{
|
||||||
|
nvs_handle handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
nvs_erase_key(handle, RMAKER_OTA_UPDATE_FLAG_NVS_NAME);
|
||||||
|
nvs_commit(handle);
|
||||||
|
nvs_close(handle);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_ota_mark_valid(void)
|
||||||
|
{
|
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||||
|
esp_ota_img_states_t ota_state;
|
||||||
|
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||||
|
if (ota_state != ESP_OTA_IMG_PENDING_VERIFY) {
|
||||||
|
ESP_LOGW(TAG, "OTA Already marked as valid");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_event_handler_unregister(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &event_handler);
|
||||||
|
esp_rmaker_ota_t *ota = g_ota_priv;
|
||||||
|
if (!ota) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_ota_mark_app_valid_cancel_rollback();
|
||||||
|
esp_rmaker_erase_rollback_flag();
|
||||||
|
ota->ota_in_progress = false;
|
||||||
|
if (ota->rollback_timer) {
|
||||||
|
xTimerStop(ota->rollback_timer, portMAX_DELAY);
|
||||||
|
xTimerDelete(ota->rollback_timer, portMAX_DELAY);
|
||||||
|
ota->rollback_timer = NULL;
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_report_status((esp_rmaker_ota_handle_t )ota, OTA_STATUS_SUCCESS, "OTA Upgrade finished and verified successfully");
|
||||||
|
if (ota->type == OTA_USING_TOPICS) {
|
||||||
|
if (esp_rmaker_ota_fetch_with_delay(RMAKER_OTA_FETCH_DELAY) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create OTA Fetch timer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_ota_mark_invalid(void)
|
||||||
|
{
|
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||||
|
esp_ota_img_states_t ota_state;
|
||||||
|
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||||
|
if (ota_state != ESP_OTA_IMG_PENDING_VERIFY) {
|
||||||
|
ESP_LOGE(TAG, "Cannot rollback due to invalid OTA state.");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void esp_ota_rollback(TimerHandle_t handle)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Could not verify firmware even after %d seconds since boot-up. Rolling back.",
|
||||||
|
RMAKER_OTA_ROLLBACK_WAIT_PERIOD);
|
||||||
|
esp_rmaker_ota_mark_invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_ota_check_for_mqtt(esp_rmaker_ota_t *ota)
|
||||||
|
{
|
||||||
|
ota->rollback_timer = xTimerCreate("ota_rollback_tm", (RMAKER_OTA_ROLLBACK_WAIT_PERIOD * 1000) / portTICK_PERIOD_MS,
|
||||||
|
pdTRUE, NULL, esp_ota_rollback);
|
||||||
|
if (ota->rollback_timer) {
|
||||||
|
xTimerStart(ota->rollback_timer, 0);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Could not create rollback timer. Will require manual reboot if firmware verification fails");
|
||||||
|
}
|
||||||
|
|
||||||
|
return esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &event_handler, ota);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_ota_manage_rollback(esp_rmaker_ota_t *ota)
|
||||||
|
{
|
||||||
|
/* If rollback is enabled, and the ota update flag is found, it means that the OTA validation is pending
|
||||||
|
*/
|
||||||
|
nvs_handle handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
uint8_t ota_update = 0;
|
||||||
|
size_t len = sizeof(ota_update);
|
||||||
|
if ((err = nvs_get_blob(handle, RMAKER_OTA_UPDATE_FLAG_NVS_NAME, &ota_update, &len)) == ESP_OK) {
|
||||||
|
ota->validation_in_progress = true;
|
||||||
|
}
|
||||||
|
nvs_close(handle);
|
||||||
|
}
|
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||||
|
esp_ota_img_states_t ota_state;
|
||||||
|
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "OTA state = %d", ota_state);
|
||||||
|
/* Not checking for CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE here because the firmware may have
|
||||||
|
* it disabled, but bootloader may have it enabled, in which case, we will have to
|
||||||
|
* handle this state.
|
||||||
|
*/
|
||||||
|
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||||
|
ESP_LOGI(TAG, "First Boot after an OTA");
|
||||||
|
/* Run diagnostic function */
|
||||||
|
esp_rmaker_ota_diag_status_t diag_status = OTA_DIAG_STATUS_SUCCESS;
|
||||||
|
if (ota->ota_diag) {
|
||||||
|
esp_rmaker_ota_diag_priv_t ota_diag_priv = {
|
||||||
|
.state = OTA_DIAG_STATE_INIT,
|
||||||
|
.rmaker_ota = ota->validation_in_progress
|
||||||
|
};
|
||||||
|
diag_status = ota->ota_diag(&ota_diag_priv, ota->priv);
|
||||||
|
}
|
||||||
|
if (diag_status != OTA_DIAG_STATUS_FAIL) {
|
||||||
|
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
|
||||||
|
/* Will not mark the image valid here immediately, but instead will wait for
|
||||||
|
* MQTT connection. The below flag will tell the OTA functions that the earlier
|
||||||
|
* OTA is still in progress.
|
||||||
|
*/
|
||||||
|
ota->ota_in_progress = true;
|
||||||
|
esp_ota_check_for_mqtt(ota);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
|
||||||
|
esp_rmaker_ota_mark_invalid();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* If rollback is enabled, and the ota update flag is found, it means that the firmware was rolled back
|
||||||
|
*/
|
||||||
|
if (ota->validation_in_progress) {
|
||||||
|
ota->rolled_back = true;
|
||||||
|
esp_rmaker_erase_rollback_flag();
|
||||||
|
if (ota->type == OTA_USING_PARAMS) {
|
||||||
|
/* Calling this only for OTA_USING_PARAMS, because for OTA_USING_TOPICS,
|
||||||
|
* the work queue function will manage the status reporting later.
|
||||||
|
*/
|
||||||
|
esp_rmaker_ota_report_status((esp_rmaker_ota_handle_t )ota,
|
||||||
|
OTA_STATUS_REJECTED, "Firmware rolled back");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const esp_rmaker_ota_config_t ota_default_config = {
|
||||||
|
.server_cert = esp_rmaker_ota_def_cert,
|
||||||
|
};
|
||||||
|
/* Enable the ESP RainMaker specific OTA */
|
||||||
|
esp_err_t esp_rmaker_ota_enable(esp_rmaker_ota_config_t *ota_config, esp_rmaker_ota_type_t type)
|
||||||
|
{
|
||||||
|
if (ota_config == NULL) {
|
||||||
|
ota_config = (esp_rmaker_ota_config_t *)&ota_default_config;
|
||||||
|
}
|
||||||
|
if ((type != OTA_USING_PARAMS) && (type != OTA_USING_TOPICS)) {
|
||||||
|
ESP_LOGE(TAG,"Invalid arguments for esp_rmaker_ota_enable()");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
static bool ota_init_done;
|
||||||
|
if (ota_init_done) {
|
||||||
|
ESP_LOGE(TAG, "OTA already initialised");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_t *ota = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_ota_t));
|
||||||
|
if (!ota) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for esp_rmaker_ota_t");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
if (ota_config->ota_cb) {
|
||||||
|
ota->ota_cb = ota_config->ota_cb;
|
||||||
|
} else {
|
||||||
|
ota->ota_cb = esp_rmaker_ota_default_cb;
|
||||||
|
}
|
||||||
|
ota->ota_diag = ota_config->ota_diag;
|
||||||
|
ota->priv = ota_config->priv;
|
||||||
|
ota->server_cert = ota_config->server_cert;
|
||||||
|
esp_err_t err = ESP_FAIL;
|
||||||
|
ota->type = type;
|
||||||
|
if (type == OTA_USING_PARAMS) {
|
||||||
|
err = esp_rmaker_ota_enable_using_params(ota);
|
||||||
|
} else if (type == OTA_USING_TOPICS) {
|
||||||
|
err = esp_rmaker_ota_enable_using_topics(ota);
|
||||||
|
}
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
esp_rmaker_ota_manage_rollback(ota);
|
||||||
|
ota_init_done = true;
|
||||||
|
g_ota_priv = ota;
|
||||||
|
} else {
|
||||||
|
free(ota);
|
||||||
|
ESP_LOGE(TAG, "Failed to enable OTA");
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT
|
||||||
|
esp_rmaker_time_sync_init(NULL);
|
||||||
|
#endif
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_ota_enable_default(void)
|
||||||
|
{
|
||||||
|
return esp_rmaker_ota_enable(NULL, OTA_USING_TOPICS);
|
||||||
|
}
|
||||||
55
components/esp_rainmaker/src/ota/esp_rmaker_ota_internal.h
Normal file
55
components/esp_rainmaker/src/ota/esp_rmaker_ota_internal.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/timers.h>
|
||||||
|
#include <esp_rmaker_ota.h>
|
||||||
|
|
||||||
|
#define RMAKER_OTA_NVS_NAMESPACE "rmaker_ota"
|
||||||
|
#define RMAKER_OTA_JOB_ID_NVS_NAME "rmaker_ota_id"
|
||||||
|
#define RMAKER_OTA_UPDATE_FLAG_NVS_NAME "ota_update"
|
||||||
|
#define RMAKER_OTA_FETCH_DELAY 5
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_rmaker_ota_type_t type;
|
||||||
|
esp_rmaker_ota_cb_t ota_cb;
|
||||||
|
void *priv;
|
||||||
|
esp_rmaker_post_ota_diag_t ota_diag;
|
||||||
|
TimerHandle_t rollback_timer;
|
||||||
|
const char *server_cert;
|
||||||
|
char *url;
|
||||||
|
char *fw_version;
|
||||||
|
int filesize;
|
||||||
|
bool ota_in_progress;
|
||||||
|
bool validation_in_progress;
|
||||||
|
bool rolled_back;
|
||||||
|
ota_status_t last_reported_status;
|
||||||
|
void *transient_priv;
|
||||||
|
char *metadata;
|
||||||
|
} esp_rmaker_ota_t;
|
||||||
|
|
||||||
|
char *esp_rmaker_ota_status_to_string(ota_status_t status);
|
||||||
|
void esp_rmaker_ota_common_cb(void *priv);
|
||||||
|
void esp_rmaker_ota_finish_using_params(esp_rmaker_ota_t *ota);
|
||||||
|
void esp_rmaker_ota_finish_using_topics(esp_rmaker_ota_t *ota);
|
||||||
|
esp_err_t esp_rmaker_ota_enable_using_params(esp_rmaker_ota_t *ota);
|
||||||
|
esp_err_t esp_rmaker_ota_report_status_using_params(esp_rmaker_ota_handle_t ota_handle,
|
||||||
|
ota_status_t status, char *additional_info);
|
||||||
|
esp_err_t esp_rmaker_ota_enable_using_topics(esp_rmaker_ota_t *ota);
|
||||||
|
esp_err_t esp_rmaker_ota_report_status_using_topics(esp_rmaker_ota_handle_t ota_handle,
|
||||||
|
ota_status_t status, char *additional_info);
|
||||||
114
components/esp_rainmaker/src/ota/esp_rmaker_ota_using_params.c
Normal file
114
components/esp_rainmaker/src/ota/esp_rmaker_ota_using_params.c
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_system.h>
|
||||||
|
#include <esp_rmaker_work_queue.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_standard_types.h>
|
||||||
|
#include <esp_rmaker_standard_params.h>
|
||||||
|
#include <esp_rmaker_standard_services.h>
|
||||||
|
#include <esp_rmaker_ota.h>
|
||||||
|
|
||||||
|
#include "esp_rmaker_ota_internal.h"
|
||||||
|
#include "esp_rmaker_mqtt.h"
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_ota_using_params";
|
||||||
|
|
||||||
|
#define ESP_RMAKER_OTA_SERV_NAME "OTA"
|
||||||
|
|
||||||
|
void esp_rmaker_ota_finish_using_params(esp_rmaker_ota_t *ota)
|
||||||
|
{
|
||||||
|
if (ota->url) {
|
||||||
|
free(ota->url);
|
||||||
|
ota->url = NULL;
|
||||||
|
}
|
||||||
|
ota->filesize = 0;
|
||||||
|
if (ota->transient_priv) {
|
||||||
|
ota->transient_priv = NULL;
|
||||||
|
}
|
||||||
|
if (ota->metadata) {
|
||||||
|
free(ota->metadata);
|
||||||
|
ota->metadata = NULL;
|
||||||
|
}
|
||||||
|
ota->ota_in_progress = false;
|
||||||
|
}
|
||||||
|
static esp_err_t esp_rmaker_ota_service_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||||
|
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)priv_data;
|
||||||
|
if (!ota) {
|
||||||
|
ESP_LOGE(TAG, "No OTA specific data received in callback");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (ota->ota_in_progress) {
|
||||||
|
ESP_LOGE(TAG, "OTA already in progress. Please try later.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_OTA_URL) == 0) {
|
||||||
|
ESP_LOGI(TAG, "Received value = %s for %s - %s",
|
||||||
|
val.val.s, esp_rmaker_device_get_name(device), esp_rmaker_param_get_name(param));
|
||||||
|
if (ota->url) {
|
||||||
|
free(ota->url);
|
||||||
|
ota->url = NULL;
|
||||||
|
}
|
||||||
|
ota->url = strdup(val.val.s);
|
||||||
|
if (ota->url) {
|
||||||
|
ota->filesize = 0;
|
||||||
|
ota->ota_in_progress = true;
|
||||||
|
ota->transient_priv = (void *)device;
|
||||||
|
ota->metadata = NULL;
|
||||||
|
if (esp_rmaker_work_queue_add_task(esp_rmaker_ota_common_cb, ota) != ESP_OK) {
|
||||||
|
esp_rmaker_ota_finish_using_params(ota);
|
||||||
|
} else {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_ota_report_status_using_params(esp_rmaker_ota_handle_t ota_handle, ota_status_t status, char *additional_info)
|
||||||
|
{
|
||||||
|
const esp_rmaker_device_t *device = esp_rmaker_node_get_device_by_name(esp_rmaker_get_node(), ESP_RMAKER_OTA_SERV_NAME);
|
||||||
|
if (!device) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_rmaker_param_t *info_param = esp_rmaker_device_get_param_by_type(device, ESP_RMAKER_PARAM_OTA_INFO);
|
||||||
|
esp_rmaker_param_t *status_param = esp_rmaker_device_get_param_by_type(device, ESP_RMAKER_PARAM_OTA_STATUS);
|
||||||
|
|
||||||
|
esp_rmaker_param_update_and_report(info_param, esp_rmaker_str(additional_info));
|
||||||
|
esp_rmaker_param_update_and_report(status_param, esp_rmaker_str(esp_rmaker_ota_status_to_string(status)));
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable the ESP RainMaker specific OTA */
|
||||||
|
esp_err_t esp_rmaker_ota_enable_using_params(esp_rmaker_ota_t *ota)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *service = esp_rmaker_ota_service_create(ESP_RMAKER_OTA_SERV_NAME, ota);
|
||||||
|
if (!service) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create OTA Service");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_rmaker_device_add_cb(service, esp_rmaker_ota_service_cb, NULL);
|
||||||
|
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), service);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "OTA enabled with Params");
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
346
components/esp_rainmaker/src/ota/esp_rmaker_ota_using_topics.c
Normal file
346
components/esp_rainmaker/src/ota/esp_rmaker_ota_using_topics.c
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <json_parser.h>
|
||||||
|
#include <json_generator.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/timers.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_system.h>
|
||||||
|
#include <nvs.h>
|
||||||
|
#include <esp_rmaker_work_queue.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_ota.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
|
||||||
|
#include "esp_rmaker_internal.h"
|
||||||
|
#include "esp_rmaker_ota_internal.h"
|
||||||
|
#include "esp_rmaker_mqtt.h"
|
||||||
|
#include "esp_rmaker_mqtt_topics.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_OTA_AUTOFETCH
|
||||||
|
#include <esp_timer.h>
|
||||||
|
static esp_timer_handle_t ota_autofetch_timer;
|
||||||
|
/* Autofetch period in hours */
|
||||||
|
#define OTA_AUTOFETCH_PERIOD CONFIG_ESP_RMAKER_OTA_AUTOFETCH_PERIOD
|
||||||
|
/* Autofetch period in micro-seconds */
|
||||||
|
static uint64_t ota_autofetch_period = (OTA_AUTOFETCH_PERIOD * 60 * 60 * 1000000LL);
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_OTA_AUTOFETCH */
|
||||||
|
|
||||||
|
static const char *TAG = "esp_rmaker_ota_using_topics";
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_ota_report_status_using_topics(esp_rmaker_ota_handle_t ota_handle, ota_status_t status, char *additional_info)
|
||||||
|
{
|
||||||
|
if (!ota_handle) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)ota_handle;
|
||||||
|
|
||||||
|
char publish_payload[200];
|
||||||
|
json_gen_str_t jstr;
|
||||||
|
json_gen_str_start(&jstr, publish_payload, sizeof(publish_payload), NULL, NULL);
|
||||||
|
json_gen_start_object(&jstr);
|
||||||
|
if (ota->transient_priv) {
|
||||||
|
json_gen_obj_set_string(&jstr, "ota_job_id", (char *)ota->transient_priv);
|
||||||
|
} else {
|
||||||
|
/* This will get executed only when the OTA status is being reported after a reboot, either to
|
||||||
|
* indicate successful verification of new firmware, or to indicate that firmware was rolled back
|
||||||
|
*/
|
||||||
|
nvs_handle handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
char job_id[64] = {0};
|
||||||
|
size_t len = sizeof(job_id);
|
||||||
|
if ((err = nvs_get_blob(handle, RMAKER_OTA_JOB_ID_NVS_NAME, job_id, &len)) == ESP_OK) {
|
||||||
|
json_gen_obj_set_string(&jstr, "ota_job_id", job_id);
|
||||||
|
nvs_erase_key(handle, RMAKER_OTA_JOB_ID_NVS_NAME);
|
||||||
|
}
|
||||||
|
nvs_close(handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "Not reporting any status, since there is no Job ID available");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json_gen_obj_set_string(&jstr, "status", esp_rmaker_ota_status_to_string(status));
|
||||||
|
json_gen_obj_set_string(&jstr, "additional_info", additional_info);
|
||||||
|
json_gen_end_object(&jstr);
|
||||||
|
json_gen_str_end(&jstr);
|
||||||
|
|
||||||
|
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||||
|
esp_rmaker_create_mqtt_topic(publish_topic, sizeof(publish_topic), OTASTATUS_TOPIC_SUFFIX, OTASTATUS_TOPIC_RULE);
|
||||||
|
ESP_LOGI(TAG, "%s",publish_payload);
|
||||||
|
esp_err_t err = esp_rmaker_mqtt_publish(publish_topic, publish_payload, strlen(publish_payload),
|
||||||
|
RMAKER_MQTT_QOS1, NULL);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "esp_rmaker_mqtt_publish_data returned error %d",err);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_rmaker_ota_finish_using_topics(esp_rmaker_ota_t *ota)
|
||||||
|
{
|
||||||
|
if (ota->url) {
|
||||||
|
free(ota->url);
|
||||||
|
ota->url = NULL;
|
||||||
|
}
|
||||||
|
ota->filesize = 0;
|
||||||
|
if (ota->transient_priv) {
|
||||||
|
free(ota->transient_priv);
|
||||||
|
ota->transient_priv = NULL;
|
||||||
|
}
|
||||||
|
if (ota->metadata) {
|
||||||
|
free(ota->metadata);
|
||||||
|
ota->metadata = NULL;
|
||||||
|
}
|
||||||
|
if (ota->fw_version) {
|
||||||
|
free(ota->fw_version);
|
||||||
|
ota->fw_version = NULL;
|
||||||
|
}
|
||||||
|
ota->ota_in_progress = false;
|
||||||
|
}
|
||||||
|
static void ota_url_handler(const char *topic, void *payload, size_t payload_len, void *priv_data)
|
||||||
|
{
|
||||||
|
if (!priv_data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_handle_t ota_handle = priv_data;
|
||||||
|
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)ota_handle;
|
||||||
|
if (ota->ota_in_progress) {
|
||||||
|
ESP_LOGE(TAG, "OTA already in progress. Please try later.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ota->ota_in_progress = true;
|
||||||
|
/* Starting Firmware Upgrades */
|
||||||
|
ESP_LOGI(TAG, "Upgrade Handler got:%.*s on %s topic\n", (int) payload_len, (char *)payload, topic);
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"ota_job_id": "<ota_job_id>",
|
||||||
|
"url": "<fw_url>",
|
||||||
|
"fw_version": "<fw_version>",
|
||||||
|
"filesize": <size_in_bytes>
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
jparse_ctx_t jctx;
|
||||||
|
char *url = NULL, *ota_job_id = NULL, *fw_version = NULL;
|
||||||
|
int ret = json_parse_start(&jctx, (char *)payload, (int) payload_len);
|
||||||
|
if (ret != 0) {
|
||||||
|
ESP_LOGE(TAG, "Invalid JSON received: %s", (char *)payload);
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. JSON Payload error");
|
||||||
|
ota->ota_in_progress = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int len = 0;
|
||||||
|
ret = json_obj_get_strlen(&jctx, "ota_job_id", &len);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Aborted. OTA Job ID not found in JSON");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. OTA Updated ID not found in JSON");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
len++; /* Increment for NULL character */
|
||||||
|
ota_job_id = MEM_CALLOC_EXTRAM(1, len);
|
||||||
|
if (!ota_job_id) {
|
||||||
|
ESP_LOGE(TAG, "Aborted. OTA Job ID memory allocation failed");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. OTA Updated ID memory allocation failed");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
json_obj_get_string(&jctx, "ota_job_id", ota_job_id, len);
|
||||||
|
nvs_handle handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
nvs_set_blob(handle, RMAKER_OTA_JOB_ID_NVS_NAME, ota_job_id, strlen(ota_job_id));
|
||||||
|
nvs_close(handle);
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "OTA Job ID: %s", ota_job_id);
|
||||||
|
ota->transient_priv = ota_job_id;
|
||||||
|
len = 0;
|
||||||
|
ret = json_obj_get_strlen(&jctx, "url", &len);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Aborted. URL not found in JSON");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. URL not found in JSON");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
len++; /* Increment for NULL character */
|
||||||
|
url = MEM_CALLOC_EXTRAM(1, len);
|
||||||
|
if (!url) {
|
||||||
|
ESP_LOGE(TAG, "Aborted. URL memory allocation failed");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. URL memory allocation failed");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
json_obj_get_string(&jctx, "url", url, len);
|
||||||
|
ESP_LOGI(TAG, "URL: %s", url);
|
||||||
|
|
||||||
|
int filesize = 0;
|
||||||
|
json_obj_get_int(&jctx, "file_size", &filesize);
|
||||||
|
ESP_LOGI(TAG, "File Size: %d", filesize);
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
ret = json_obj_get_strlen(&jctx, "fw_version", &len);
|
||||||
|
if (ret == ESP_OK && len > 0) {
|
||||||
|
len++; /* Increment for NULL character */
|
||||||
|
fw_version = MEM_CALLOC_EXTRAM(1, len);
|
||||||
|
if (!fw_version) {
|
||||||
|
ESP_LOGE(TAG, "Aborted. Firmware version memory allocation failed");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. Firmware version memory allocation failed");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
json_obj_get_string(&jctx, "fw_version", fw_version, len);
|
||||||
|
ESP_LOGI(TAG, "Firmware version: %s", fw_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadata_size = 0;
|
||||||
|
char *metadata = NULL;
|
||||||
|
ret = json_obj_get_object_strlen(&jctx, "metadata", &metadata_size);
|
||||||
|
if (ret == ESP_OK && metadata_size > 0) {
|
||||||
|
metadata_size++; /* Increment for NULL character */
|
||||||
|
metadata = MEM_CALLOC_EXTRAM(1, metadata_size);
|
||||||
|
if (!metadata) {
|
||||||
|
ESP_LOGE(TAG, "Aborted. OTA metadata memory allocation failed");
|
||||||
|
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. OTA metadata memory allocation failed");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
json_obj_get_object_str(&jctx, "metadata", metadata, metadata_size);
|
||||||
|
ota->metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_parse_end(&jctx);
|
||||||
|
if (ota->url) {
|
||||||
|
free(ota->url);
|
||||||
|
}
|
||||||
|
ota->url = url;
|
||||||
|
ota->fw_version = fw_version;
|
||||||
|
ota->filesize = filesize;
|
||||||
|
ota->ota_in_progress = true;
|
||||||
|
if (esp_rmaker_work_queue_add_task(esp_rmaker_ota_common_cb, ota) != ESP_OK) {
|
||||||
|
esp_rmaker_ota_finish_using_topics(ota);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
end:
|
||||||
|
if (url) {
|
||||||
|
free(url);
|
||||||
|
}
|
||||||
|
if (fw_version) {
|
||||||
|
free(fw_version);
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_finish_using_topics(ota);
|
||||||
|
json_parse_end(&jctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_ota_fetch(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Fetching OTA details, if any.");
|
||||||
|
esp_rmaker_node_info_t *info = esp_rmaker_node_get_info(esp_rmaker_get_node());
|
||||||
|
if (!info) {
|
||||||
|
ESP_LOGE(TAG, "Node info not found. Cant send otafetch request");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
char publish_payload[150];
|
||||||
|
json_gen_str_t jstr;
|
||||||
|
json_gen_str_start(&jstr, publish_payload, sizeof(publish_payload), NULL, NULL);
|
||||||
|
json_gen_start_object(&jstr);
|
||||||
|
json_gen_obj_set_string(&jstr, "node_id", esp_rmaker_get_node_id());
|
||||||
|
json_gen_obj_set_string(&jstr, "fw_version", info->fw_version);
|
||||||
|
json_gen_end_object(&jstr);
|
||||||
|
json_gen_str_end(&jstr);
|
||||||
|
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||||
|
esp_rmaker_create_mqtt_topic(publish_topic, sizeof(publish_topic), OTAFETCH_TOPIC_SUFFIX, OTAFETCH_TOPIC_RULE);
|
||||||
|
esp_err_t err = esp_rmaker_mqtt_publish(publish_topic, publish_payload, strlen(publish_payload),
|
||||||
|
RMAKER_MQTT_QOS1, NULL);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "OTA Fetch Publish Error %d", err);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_rmaker_ota_autofetch_timer_cb(void *priv)
|
||||||
|
{
|
||||||
|
esp_rmaker_ota_fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_rmaker_ota_subscribe(void *priv_data)
|
||||||
|
{
|
||||||
|
char subscribe_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||||
|
|
||||||
|
snprintf(subscribe_topic, sizeof(subscribe_topic),"node/%s/%s", esp_rmaker_get_node_id(), OTAURL_TOPIC_SUFFIX);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Subscribing to: %s", subscribe_topic);
|
||||||
|
/* First unsubscribe, in case there is a stale subscription */
|
||||||
|
esp_rmaker_mqtt_unsubscribe(subscribe_topic);
|
||||||
|
esp_err_t err = esp_rmaker_mqtt_subscribe(subscribe_topic, ota_url_handler, RMAKER_MQTT_QOS1, priv_data);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "OTA URL Subscription Error %d", err);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_ota_work_fn(void *priv_data)
|
||||||
|
{
|
||||||
|
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)priv_data;
|
||||||
|
/* If the firmware was rolled back, indicate that first */
|
||||||
|
if (ota->rolled_back) {
|
||||||
|
esp_rmaker_ota_report_status((esp_rmaker_ota_handle_t )ota, OTA_STATUS_REJECTED, "Firmware rolled back");
|
||||||
|
ota->rolled_back = false;
|
||||||
|
}
|
||||||
|
esp_rmaker_ota_subscribe(priv_data);
|
||||||
|
#ifdef CONFIG_ESP_RMAKER_OTA_AUTOFETCH
|
||||||
|
if (ota->ota_in_progress != true) {
|
||||||
|
if (esp_rmaker_ota_fetch_with_delay(RMAKER_OTA_FETCH_DELAY) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create OTA Fetch timer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ota_autofetch_period > 0) {
|
||||||
|
esp_timer_create_args_t autofetch_timer_conf = {
|
||||||
|
.callback = esp_rmaker_ota_autofetch_timer_cb,
|
||||||
|
.arg = priv_data,
|
||||||
|
.dispatch_method = ESP_TIMER_TASK,
|
||||||
|
.name = "ota_autofetch_tm"
|
||||||
|
};
|
||||||
|
if (esp_timer_create(&autofetch_timer_conf, &ota_autofetch_timer) == ESP_OK) {
|
||||||
|
esp_timer_start_periodic(ota_autofetch_timer, ota_autofetch_period);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to create OTA Autofetch timer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_RMAKER_OTA_AUTOFETCH */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable the ESP RainMaker specific OTA */
|
||||||
|
esp_err_t esp_rmaker_ota_enable_using_topics(esp_rmaker_ota_t *ota)
|
||||||
|
{
|
||||||
|
esp_err_t err = esp_rmaker_work_queue_add_task(esp_rmaker_ota_work_fn, ota);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "OTA enabled with Topics");
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_rmaker_ota_fetch_timer_cb(TimerHandle_t xTimer)
|
||||||
|
{
|
||||||
|
esp_rmaker_ota_fetch();
|
||||||
|
xTimerDelete(xTimer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_ota_fetch_with_delay(int time)
|
||||||
|
{
|
||||||
|
TimerHandle_t timer = xTimerCreate(NULL, (time * 1000) / portTICK_PERIOD_MS, pdFALSE, NULL, esp_rmaker_ota_fetch_timer_cb);
|
||||||
|
if (timer == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
} else {
|
||||||
|
xTimerStart(timer, 0);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
#include <esp_rmaker_standard_types.h>
|
||||||
|
#include <esp_rmaker_standard_params.h>
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_switch_device_create(const char *dev_name,
|
||||||
|
void *priv_data, bool power)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *device = esp_rmaker_device_create(dev_name, ESP_RMAKER_DEVICE_SWITCH, priv_data);
|
||||||
|
if (device) {
|
||||||
|
esp_rmaker_device_add_param(device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, dev_name));
|
||||||
|
esp_rmaker_param_t *primary = esp_rmaker_power_param_create(ESP_RMAKER_DEF_POWER_NAME, power);
|
||||||
|
esp_rmaker_device_add_param(device, primary);
|
||||||
|
esp_rmaker_device_assign_primary_param(device, primary);
|
||||||
|
}
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_lightbulb_device_create(const char *dev_name,
|
||||||
|
void *priv_data, bool power)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *device = esp_rmaker_device_create(dev_name, ESP_RMAKER_DEVICE_LIGHTBULB, priv_data);
|
||||||
|
if (device) {
|
||||||
|
esp_rmaker_device_add_param(device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, dev_name));
|
||||||
|
esp_rmaker_param_t *primary = esp_rmaker_power_param_create(ESP_RMAKER_DEF_POWER_NAME, power);
|
||||||
|
esp_rmaker_device_add_param(device, primary);
|
||||||
|
esp_rmaker_device_assign_primary_param(device, primary);
|
||||||
|
}
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_fan_device_create(const char *dev_name,
|
||||||
|
void *priv_data, bool power)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *device = esp_rmaker_device_create(dev_name, ESP_RMAKER_DEVICE_FAN, priv_data);
|
||||||
|
if (device) {
|
||||||
|
esp_rmaker_device_add_param(device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, dev_name));
|
||||||
|
esp_rmaker_param_t *primary = esp_rmaker_power_param_create(ESP_RMAKER_DEF_POWER_NAME, power);
|
||||||
|
esp_rmaker_device_add_param(device, primary);
|
||||||
|
esp_rmaker_device_assign_primary_param(device, primary);
|
||||||
|
}
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_temp_sensor_device_create(const char *dev_name,
|
||||||
|
void *priv_data, float temperature)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *device = esp_rmaker_device_create(dev_name, ESP_RMAKER_DEVICE_TEMP_SENSOR, priv_data);
|
||||||
|
if (device) {
|
||||||
|
esp_rmaker_device_add_param(device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, dev_name));
|
||||||
|
esp_rmaker_param_t *primary = esp_rmaker_temperature_param_create(ESP_RMAKER_DEF_TEMPERATURE_NAME, temperature);
|
||||||
|
esp_rmaker_device_add_param(device, primary);
|
||||||
|
esp_rmaker_device_assign_primary_param(device, primary);
|
||||||
|
}
|
||||||
|
return device;
|
||||||
|
}
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_standard_types.h>
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_name_param_create(const char *param_name, const char *val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_NAME,
|
||||||
|
esp_rmaker_str(val), PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_power_param_create(const char *param_name, bool val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_POWER,
|
||||||
|
esp_rmaker_bool(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
if (param) {
|
||||||
|
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_TOGGLE);
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_brightness_param_create(const char *param_name, int val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_BRIGHTNESS,
|
||||||
|
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
if (param) {
|
||||||
|
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_SLIDER);
|
||||||
|
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(100), esp_rmaker_int(1));
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_hue_param_create(const char *param_name, int val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_HUE,
|
||||||
|
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
if (param) {
|
||||||
|
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_HUE_SLIDER);
|
||||||
|
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(360), esp_rmaker_int(1));
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_saturation_param_create(const char *param_name, int val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_SATURATION,
|
||||||
|
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
if (param) {
|
||||||
|
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_SLIDER);
|
||||||
|
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(100), esp_rmaker_int(1));
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_intensity_param_create(const char *param_name, int val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_INTENSITY,
|
||||||
|
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
if (param) {
|
||||||
|
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_SLIDER);
|
||||||
|
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(100), esp_rmaker_int(1));
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_cct_param_create(const char *param_name, int val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_CCT,
|
||||||
|
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
if (param) {
|
||||||
|
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_SLIDER);
|
||||||
|
esp_rmaker_param_add_bounds(param, esp_rmaker_int(2700), esp_rmaker_int(6500), esp_rmaker_int(100));
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_direction_param_create(const char *param_name, int val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_DIRECTION,
|
||||||
|
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
if (param) {
|
||||||
|
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_DROPDOWN);
|
||||||
|
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(1), esp_rmaker_int(1));
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_speed_param_create(const char *param_name, int val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_SPEED,
|
||||||
|
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
if (param) {
|
||||||
|
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_SLIDER);
|
||||||
|
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(5), esp_rmaker_int(1));
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_temperature_param_create(const char *param_name, float val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TEMPERATURE,
|
||||||
|
esp_rmaker_float(val), PROP_FLAG_READ | PROP_FLAG_TIME_SERIES);
|
||||||
|
if (param) {
|
||||||
|
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_TEXT);
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_ota_status_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_OTA_STATUS,
|
||||||
|
esp_rmaker_str(""), PROP_FLAG_READ);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_ota_info_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_OTA_INFO,
|
||||||
|
esp_rmaker_str(""), PROP_FLAG_READ);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_ota_url_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_OTA_URL,
|
||||||
|
esp_rmaker_str(""), PROP_FLAG_WRITE);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_timezone_param_create(const char *param_name, const char *val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TIMEZONE,
|
||||||
|
esp_rmaker_str(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_timezone_posix_param_create(const char *param_name, const char *val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TIMEZONE_POSIX,
|
||||||
|
esp_rmaker_str(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_schedules_param_create(const char *param_name, int max_schedules)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_SCHEDULES,
|
||||||
|
esp_rmaker_array("[]"), PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||||
|
esp_rmaker_param_add_array_max_count(param, max_schedules);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_scenes_param_create(const char *param_name, int max_scenes)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_SCENES,
|
||||||
|
esp_rmaker_array("[]"), PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||||
|
esp_rmaker_param_add_array_max_count(param, max_scenes);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_reboot_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_REBOOT,
|
||||||
|
esp_rmaker_bool(false), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_factory_reset_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_FACTORY_RESET,
|
||||||
|
esp_rmaker_bool(false), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_wifi_reset_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_WIFI_RESET,
|
||||||
|
esp_rmaker_bool(false), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_local_control_pop_param_create(const char *param_name, const char *val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_LOCAL_CONTROL_POP,
|
||||||
|
esp_rmaker_str(val), PROP_FLAG_READ);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_param_t *esp_rmaker_local_control_type_param_create(const char *param_name, int val)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_LOCAL_CONTROL_TYPE,
|
||||||
|
esp_rmaker_int(val), PROP_FLAG_READ);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
#include <esp_rmaker_standard_types.h>
|
||||||
|
#include <esp_rmaker_standard_params.h>
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_ota_service_create(const char *serv_name, void *priv_data)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_OTA, priv_data);
|
||||||
|
if (service) {
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_ota_status_param_create(ESP_RMAKER_DEF_OTA_STATUS_NAME));
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_ota_info_param_create(ESP_RMAKER_DEF_OTA_INFO_NAME));
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_ota_url_param_create(ESP_RMAKER_DEF_OTA_URL_NAME));
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_time_service_create(const char *serv_name, const char *timezone,
|
||||||
|
const char *timezone_posix, void *priv_data)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_TIME, priv_data);
|
||||||
|
if (service) {
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_timezone_param_create(
|
||||||
|
ESP_RMAKER_DEF_TIMEZONE_NAME, timezone));
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_timezone_posix_param_create(
|
||||||
|
ESP_RMAKER_DEF_TIMEZONE_POSIX_NAME, timezone_posix));
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_create_schedule_service(const char *serv_name, esp_rmaker_device_write_cb_t write_cb,
|
||||||
|
esp_rmaker_device_read_cb_t read_cb, int max_schedules, void *priv_data)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_SCHEDULE, priv_data);
|
||||||
|
if (service) {
|
||||||
|
esp_rmaker_device_add_cb(service, write_cb, read_cb);
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_schedules_param_create(ESP_RMAKER_DEF_SCHEDULE_NAME, max_schedules));
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_create_scenes_service(const char *serv_name, esp_rmaker_device_write_cb_t write_cb,
|
||||||
|
esp_rmaker_device_read_cb_t read_cb, int max_scenes, bool deactivation_support, void *priv_data)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_SCENES, priv_data);
|
||||||
|
if (service) {
|
||||||
|
esp_rmaker_device_add_cb(service, write_cb, read_cb);
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_scenes_param_create(ESP_RMAKER_DEF_SCENES_NAME, max_scenes));
|
||||||
|
esp_rmaker_device_add_attribute(service, "deactivation_support", deactivation_support ? "yes" : "no");
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_create_system_service(const char *serv_name, void *priv_data)
|
||||||
|
{
|
||||||
|
return esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_SYSTEM, priv_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_create_local_control_service(const char *serv_name, const char *pop, int sec_type, void *priv_data)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_LOCAL_CONTROL, priv_data);
|
||||||
|
if (service) {
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_local_control_pop_param_create(ESP_RMAKER_DEF_LOCAL_CONTROL_POP, pop));
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_local_control_type_param_create(ESP_RMAKER_DEF_LOCAL_CONTROL_TYPE, sec_type));
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
196
components/esp_rainmaker/src/thread_br/esp_rmaker_thread_br.c
Normal file
196
components/esp_rainmaker/src/thread_br/esp_rmaker_thread_br.c
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <esp_check.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_rmaker_thread_br.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "esp_rmaker_thread_br_priv.h"
|
||||||
|
|
||||||
|
static const char *TAG = "thread_br";
|
||||||
|
static const esp_rmaker_device_t *thread_br_service = NULL;
|
||||||
|
|
||||||
|
static int char_to_num(char ch)
|
||||||
|
{
|
||||||
|
if (ch >= '0' && ch <= '9') {
|
||||||
|
return ch - '0';
|
||||||
|
} else if (ch >= 'A' && ch <= 'F') {
|
||||||
|
return ch - 'A' + 10;
|
||||||
|
} else if (ch >= 'a' && ch <= 'f') {
|
||||||
|
return ch - 'a' + 10;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_bytes(uint8_t *buffer, size_t buffer_size, const char *str)
|
||||||
|
{
|
||||||
|
if (!str || strlen(str) % 2 != 0 || strlen(str) / 2 > buffer_size) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < strlen(str) / 2; ++i) {
|
||||||
|
int byte_h = char_to_num(str[2 * i]);
|
||||||
|
int byte_l = char_to_num(str[2 * i + 1]);
|
||||||
|
if (byte_h < 0 || byte_l < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
buffer[i] = (byte_h << 4) + byte_l;
|
||||||
|
}
|
||||||
|
return strlen(str) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool convert_bytes_to_str(const uint8_t *bytes, size_t bytes_size, char *str, size_t str_size)
|
||||||
|
{
|
||||||
|
if (str_size < bytes_size * 2 + 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < bytes_size; ++i) {
|
||||||
|
uint8_t byte_h = bytes[i] >> 4;
|
||||||
|
uint8_t byte_l = bytes[i] & 0xF;
|
||||||
|
str[2 * i] = byte_h > 9 ? (byte_h - 0xA + 'A') : (byte_h + '0');
|
||||||
|
str[2 * i + 1] = byte_l > 9 ? (byte_l - 0xA + 'A') : (byte_l + '0');
|
||||||
|
}
|
||||||
|
str[2 * bytes_size] = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||||
|
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->src == ESP_RMAKER_REQ_SRC_INIT) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TBR_ACTIVE_DATASET) == 0) {
|
||||||
|
if (val.type != RMAKER_VAL_TYPE_STRING || !val.val.s) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
otOperationalDatasetTlvs active_dataset;
|
||||||
|
otOperationalDatasetTlvs current_active_dataset;
|
||||||
|
int len = parse_bytes(active_dataset.mTlvs, sizeof(active_dataset.mTlvs), val.val.s);
|
||||||
|
if (len < 0) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
active_dataset.mLength = len;
|
||||||
|
if (thread_br_get_active_dataset_tlvs(¤t_active_dataset) != ESP_OK ||
|
||||||
|
current_active_dataset.mLength != active_dataset.mLength ||
|
||||||
|
memcmp(current_active_dataset.mTlvs, active_dataset.mTlvs, len)) {
|
||||||
|
ESP_RETURN_ON_ERROR(thread_br_set_thread_enabled(false), TAG, "Failed to disable Thread");
|
||||||
|
ESP_RETURN_ON_ERROR(thread_br_set_active_dataset_tlvs(&active_dataset), TAG, "Failed to set active dataset");
|
||||||
|
}
|
||||||
|
ESP_RETURN_ON_ERROR(thread_br_set_thread_enabled(true), TAG, "Failed to enable Thread");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_rmaker_param_update_and_report(param, val), TAG,
|
||||||
|
"Failed to update and report active dataset");
|
||||||
|
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TBR_PENDING_DATASET) == 0) {
|
||||||
|
if (val.type != RMAKER_VAL_TYPE_STRING || !val.val.s) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
otOperationalDatasetTlvs pending_dataset;
|
||||||
|
int len = parse_bytes(pending_dataset.mTlvs, sizeof(pending_dataset.mTlvs), val.val.s);
|
||||||
|
if (len < 0) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
pending_dataset.mLength = len;
|
||||||
|
ESP_RETURN_ON_ERROR(thread_br_set_pending_dataset_tlvs(&pending_dataset), TAG, "Failed to set pending dataset");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_rmaker_param_update_and_report(param, val), TAG,
|
||||||
|
"Failed to update and report active dataset");
|
||||||
|
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TBR_CMD) == 0) {
|
||||||
|
if (val.type != RMAKER_VAL_TYPE_INTEGER) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (val.val.i == ESP_RMAKER_THREAD_BR_CMD_GEN_DATASET) {
|
||||||
|
ESP_RETURN_ON_ERROR(thread_br_set_thread_enabled(false), TAG, "Failed to disable Thread");
|
||||||
|
ESP_RETURN_ON_ERROR(thread_br_generate_and_commit_new_dataset(), TAG, "Failed to generate a new dataset");
|
||||||
|
ESP_RETURN_ON_ERROR(thread_br_set_thread_enabled(true), TAG, "Failed to enable Thread");
|
||||||
|
// We need to report the generated active dataset.
|
||||||
|
otOperationalDatasetTlvs active_dataset;
|
||||||
|
ESP_RETURN_ON_ERROR(thread_br_get_active_dataset_tlvs(&active_dataset), TAG, "Failed to get active dataset");
|
||||||
|
char *dataset_str = (char *)MEM_CALLOC_EXTRAM(2 * active_dataset.mLength + 1, 1);
|
||||||
|
if (!dataset_str) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
convert_bytes_to_str(active_dataset.mTlvs, active_dataset.mLength, dataset_str, 2 * active_dataset.mLength + 1);
|
||||||
|
esp_rmaker_param_val_t report_val = esp_rmaker_str(dataset_str);
|
||||||
|
esp_rmaker_param_t *report_param = esp_rmaker_device_get_param_by_type(device, ESP_RMAKER_PARAM_TBR_ACTIVE_DATASET);
|
||||||
|
esp_err_t err = esp_rmaker_param_update_and_report(report_param, report_val);
|
||||||
|
free(dataset_str);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to update and report active dataset");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_thread_br_enable(const esp_openthread_platform_config_t *platform_config,
|
||||||
|
const esp_rcp_update_config_t *rcp_update_config)
|
||||||
|
{
|
||||||
|
if (thread_br_service) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
thread_br_service = esp_rmaker_thread_br_service_create(ESP_RMAKER_DEF_THREAD_BR_SERVICE, write_cb, NULL, NULL);
|
||||||
|
if (!thread_br_service) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create thread br service");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), thread_br_service);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
esp_rmaker_device_delete(thread_br_service);
|
||||||
|
thread_br_service = NULL;
|
||||||
|
}
|
||||||
|
thread_border_router_start(platform_config, rcp_update_config);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_thread_br_report_device_role(void)
|
||||||
|
{
|
||||||
|
if (!thread_br_service) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_device_get_param_by_type(thread_br_service,
|
||||||
|
ESP_RMAKER_PARAM_TBR_DEVICE_ROLE);
|
||||||
|
if (!param) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
otDeviceRole role = thread_br_get_device_role();
|
||||||
|
esp_rmaker_param_val_t val = esp_rmaker_int(role);
|
||||||
|
ESP_RETURN_ON_ERROR(esp_rmaker_param_update_and_report(param, val), TAG,
|
||||||
|
"Failed to update and report Thread device role");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_thread_br_report_border_agent_id(void)
|
||||||
|
{
|
||||||
|
if (!thread_br_service) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_device_get_param_by_type(thread_br_service,
|
||||||
|
ESP_RMAKER_PARAM_TBR_BORDER_AGENT_ID);
|
||||||
|
if (!param) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
otBorderAgentId border_agent_id;
|
||||||
|
ESP_RETURN_ON_ERROR(thread_br_get_border_agent_id(&border_agent_id), TAG, "Failed to get Border Agent ID");
|
||||||
|
char border_agent_id_str[sizeof(border_agent_id.mId) * 2 + 1];
|
||||||
|
convert_bytes_to_str(border_agent_id.mId, sizeof(border_agent_id.mId), border_agent_id_str,
|
||||||
|
sizeof(border_agent_id_str));
|
||||||
|
esp_rmaker_param_val_t report_val = esp_rmaker_str(border_agent_id_str);
|
||||||
|
ESP_RETURN_ON_ERROR(esp_rmaker_param_update_and_report(param, report_val), TAG,
|
||||||
|
"Failed to update and report border agent id");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <esp_check.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_openthread.h>
|
||||||
|
#include <esp_openthread_lock.h>
|
||||||
|
|
||||||
|
#include <openthread/border_agent.h>
|
||||||
|
#include <openthread/dataset.h>
|
||||||
|
#include <openthread/dataset_ftd.h>
|
||||||
|
#include <openthread/error.h>
|
||||||
|
#include <openthread/logging.h>
|
||||||
|
#include <openthread/thread.h>
|
||||||
|
|
||||||
|
#include "esp_rmaker_thread_br_priv.h"
|
||||||
|
|
||||||
|
static const char* TAG = "thread_br";
|
||||||
|
|
||||||
|
esp_err_t thread_br_set_thread_enabled(bool enabled)
|
||||||
|
{
|
||||||
|
otInstance *instance = esp_openthread_get_instance();
|
||||||
|
if (!instance) {
|
||||||
|
ESP_LOGE(TAG, "Thread not initialized");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
bool is_enabled = (otThreadGetDeviceRole(instance) != OT_DEVICE_ROLE_DISABLED);
|
||||||
|
bool is_ip6_enabled = otIp6IsEnabled(instance);
|
||||||
|
if (enabled && !is_ip6_enabled) {
|
||||||
|
if (otIp6SetEnabled(instance, enabled) != OT_ERROR_NONE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to %s netif", enabled ? "enable" : "disable");
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enabled != is_enabled) {
|
||||||
|
if (otThreadSetEnabled(instance, enabled) != OT_ERROR_NONE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to %s thread", enabled ? "enable" : "disable");
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!enabled && is_ip6_enabled) {
|
||||||
|
if (otIp6SetEnabled(instance, enabled) != OT_ERROR_NONE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to %s netif", enabled ? "enable" : "disable");
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t thread_br_set_active_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs)
|
||||||
|
{
|
||||||
|
if (!dataset_tlvs) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
otInstance *instance = esp_openthread_get_instance();
|
||||||
|
if (!instance) {
|
||||||
|
ESP_LOGE(TAG, "Thread not initialized");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
if (otDatasetSetActiveTlvs(instance, dataset_tlvs) != OT_ERROR_NONE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set active Thread DatasetTlvs");
|
||||||
|
err = ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t thread_br_get_active_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs)
|
||||||
|
{
|
||||||
|
if (!dataset_tlvs) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
otInstance *instance = esp_openthread_get_instance();
|
||||||
|
if (!instance) {
|
||||||
|
ESP_LOGE(TAG, "Thread not initialized");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
if (otDatasetGetActiveTlvs(instance, dataset_tlvs) != OT_ERROR_NONE) {
|
||||||
|
ESP_LOGI(TAG, "Active Dataset not set");
|
||||||
|
err = ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t thread_br_set_pending_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs)
|
||||||
|
{
|
||||||
|
if (!dataset_tlvs) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
otInstance *instance = esp_openthread_get_instance();
|
||||||
|
if (!instance) {
|
||||||
|
ESP_LOGE(TAG, "Thread not initialized");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
if (otDatasetSetPendingTlvs(instance, dataset_tlvs) != OT_ERROR_NONE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set pending Thread DatasetTlvs");
|
||||||
|
err = ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t thread_br_get_border_agent_id(otBorderAgentId *border_agent_id)
|
||||||
|
{
|
||||||
|
if (!border_agent_id) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
otInstance *instance = esp_openthread_get_instance();
|
||||||
|
if (!instance) {
|
||||||
|
ESP_LOGE(TAG, "Thread not initialized");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
if (otBorderAgentGetId(instance, border_agent_id) != OT_ERROR_NONE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to get Border Agent Id");
|
||||||
|
err = ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
otDeviceRole thread_br_get_device_role()
|
||||||
|
{
|
||||||
|
if (!esp_openthread_get_instance()) {
|
||||||
|
return OT_DEVICE_ROLE_DISABLED;
|
||||||
|
}
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
otDeviceRole role = otThreadGetDeviceRole(esp_openthread_get_instance());
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t thread_br_generate_and_commit_new_dataset()
|
||||||
|
{
|
||||||
|
otInstance *instance = esp_openthread_get_instance();
|
||||||
|
if (!instance) {
|
||||||
|
ESP_LOGE(TAG, "Thread not initialized");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
if (thread_br_get_device_role() != OT_DEVICE_ROLE_DISABLED) {
|
||||||
|
ESP_LOGE(TAG, "The device role should be disabled when calling generate_and_commit_new_dataset");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
otOperationalDataset dataset;
|
||||||
|
if (otDatasetCreateNewNetwork(instance, &dataset) != OT_ERROR_NONE) {
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (otDatasetSetActive(instance, &dataset) != OT_ERROR_NONE) {
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this
|
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <esp_check.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
#include <esp_event_base.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_netif.h>
|
||||||
|
#include <esp_openthread.h>
|
||||||
|
#include <esp_openthread_border_router.h>
|
||||||
|
#include <esp_openthread_lock.h>
|
||||||
|
#include <esp_openthread_netif_glue.h>
|
||||||
|
#include <esp_openthread_types.h>
|
||||||
|
#include <esp_openthread_cli.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_thread_br.h>
|
||||||
|
#include <esp_vfs_dev.h>
|
||||||
|
#include <esp_vfs_eventfd.h>
|
||||||
|
#include <esp_wifi_types.h>
|
||||||
|
#include <mdns.h>
|
||||||
|
#include <openthread/logging.h>
|
||||||
|
#include <openthread/thread.h>
|
||||||
|
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||||
|
#include <esp_rcp_update.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "esp_rmaker_thread_br_priv.h"
|
||||||
|
|
||||||
|
static const char *TAG = "thread_br";
|
||||||
|
|
||||||
|
static esp_openthread_platform_config_t s_platform_config;
|
||||||
|
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||||
|
static bool s_rcp_update = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||||
|
#define RCP_VERSION_MAX_SIZE 100
|
||||||
|
static void update_rcp(void)
|
||||||
|
{
|
||||||
|
// Deinit uart to transfer UART to the serial loader
|
||||||
|
esp_openthread_rcp_deinit();
|
||||||
|
if (esp_rcp_update() == ESP_OK) {
|
||||||
|
esp_rcp_mark_image_verified(true);
|
||||||
|
} else {
|
||||||
|
esp_rcp_mark_image_verified(false);
|
||||||
|
}
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void try_update_ot_rcp(const esp_openthread_platform_config_t *config)
|
||||||
|
{
|
||||||
|
char internal_rcp_version[RCP_VERSION_MAX_SIZE];
|
||||||
|
const char *running_rcp_version = otPlatRadioGetVersionString(esp_openthread_get_instance());
|
||||||
|
|
||||||
|
if (esp_rcp_load_version_in_storage(internal_rcp_version, sizeof(internal_rcp_version)) == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Internal RCP Version: %s", internal_rcp_version);
|
||||||
|
ESP_LOGI(TAG, "Running RCP Version: %s", running_rcp_version);
|
||||||
|
if (strcmp(internal_rcp_version, running_rcp_version) == 0) {
|
||||||
|
esp_rcp_mark_image_verified(true);
|
||||||
|
} else {
|
||||||
|
update_rcp();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "RCP firmware not found in storage, will reboot to try next image");
|
||||||
|
esp_rcp_mark_image_verified(false);
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // CONFIG_AUTO_UPDATE_RCP
|
||||||
|
|
||||||
|
static void rcp_failure_handler(void)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||||
|
esp_rcp_mark_image_unusable();
|
||||||
|
char internal_rcp_version[RCP_VERSION_MAX_SIZE];
|
||||||
|
if (esp_rcp_load_version_in_storage(internal_rcp_version, sizeof(internal_rcp_version)) == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Internal RCP Version: %s", internal_rcp_version);
|
||||||
|
update_rcp();
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "RCP firmware not found in storage, will reboot to try next image");
|
||||||
|
esp_rcp_mark_image_verified(false);
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
#endif // CONFIG_AUTO_UPDATE_RCP
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ot_task_worker(void *aContext)
|
||||||
|
{
|
||||||
|
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD();
|
||||||
|
esp_netif_t *openthread_netif = esp_netif_new(&cfg);
|
||||||
|
assert(openthread_netif != NULL);
|
||||||
|
|
||||||
|
esp_openthread_register_rcp_failure_handler(rcp_failure_handler);
|
||||||
|
// Initialize the OpenThread stack
|
||||||
|
ESP_ERROR_CHECK(esp_openthread_init(&s_platform_config));
|
||||||
|
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||||
|
try_update_ot_rcp(&s_platform_config);
|
||||||
|
#endif
|
||||||
|
// Initialize border routing features
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
ESP_ERROR_CHECK(esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init(&s_platform_config)));
|
||||||
|
#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC
|
||||||
|
(void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL);
|
||||||
|
#endif
|
||||||
|
otInstance *instance = esp_openthread_get_instance();
|
||||||
|
if (otDatasetIsCommissioned(instance)) {
|
||||||
|
(void)otIp6SetEnabled(instance, true);
|
||||||
|
(void)otThreadSetEnabled(instance, true);
|
||||||
|
}
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
|
||||||
|
esp_openthread_launch_mainloop();
|
||||||
|
// Clean up
|
||||||
|
esp_netif_destroy(openthread_netif);
|
||||||
|
esp_openthread_netif_glue_deinit();
|
||||||
|
esp_vfs_eventfd_unregister();
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void thread_br_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id,
|
||||||
|
void* event_data)
|
||||||
|
{
|
||||||
|
if (event_base == OPENTHREAD_EVENT && event_id == OPENTHREAD_EVENT_ROLE_CHANGED) {
|
||||||
|
esp_openthread_role_changed_event_t *role_changed = (esp_openthread_role_changed_event_t *)event_data;
|
||||||
|
if (!role_changed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_rmaker_thread_br_report_device_role();
|
||||||
|
if (role_changed->current_role == OT_DEVICE_ROLE_ROUTER ||
|
||||||
|
role_changed->current_role == OT_DEVICE_ROLE_LEADER) {
|
||||||
|
esp_rmaker_thread_br_report_border_agent_id();
|
||||||
|
}
|
||||||
|
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
|
||||||
|
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||||
|
// Create link local address for Wi-Fi Station interface
|
||||||
|
esp_netif_create_ip6_linklocal(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"));
|
||||||
|
static bool border_router_initialized = false;
|
||||||
|
if (!border_router_initialized) {
|
||||||
|
ESP_ERROR_CHECK(esp_openthread_border_router_init());
|
||||||
|
border_router_initialized = true;
|
||||||
|
}
|
||||||
|
esp_openthread_lock_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t thread_border_router_start(const esp_openthread_platform_config_t *platform_config,
|
||||||
|
const esp_rcp_update_config_t *rcp_update_config)
|
||||||
|
{
|
||||||
|
static bool thread_br_started = false;
|
||||||
|
if (thread_br_started) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
if (!platform_config) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
memcpy(&s_platform_config, platform_config, sizeof(esp_openthread_platform_config_t));
|
||||||
|
esp_vfs_eventfd_config_t eventfd_config = {
|
||||||
|
.max_fds = 4,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(esp_vfs_eventfd_register(&eventfd_config), TAG, "Failed to register eventfd");
|
||||||
|
esp_openthread_set_backbone_netif(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"));
|
||||||
|
ESP_ERROR_CHECK(mdns_init());
|
||||||
|
#define MDNS_MAX_NAME_LEN 64
|
||||||
|
char hostname[MDNS_MAX_NAME_LEN];
|
||||||
|
if (mdns_hostname_get(hostname) != ESP_OK) {
|
||||||
|
// if hostname is not set we will set it with the rainmaker node id.
|
||||||
|
ESP_ERROR_CHECK(mdns_hostname_set(esp_rmaker_get_node_id()));
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||||
|
if (rcp_update_config) {
|
||||||
|
esp_rcp_update_init(rcp_update_config);
|
||||||
|
s_rcp_update = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#define THREAD_TASK_STACK_SIZE 8192
|
||||||
|
if (xTaskCreate(ot_task_worker, "ot_br", THREAD_TASK_STACK_SIZE, NULL, 5, NULL) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to start openthread task for thread br");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_ROLE_CHANGED, thread_br_event_handler, NULL);
|
||||||
|
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, thread_br_event_handler, NULL);
|
||||||
|
thread_br_started = true;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_rcp_update.h"
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_openthread.h>
|
||||||
|
#include <openthread/border_agent.h>
|
||||||
|
|
||||||
|
/********** THREAD BR PARAMS NAME ***********/
|
||||||
|
#define ESP_RMAKER_DEF_BORDER_AGENT_ID "BorderAgentId"
|
||||||
|
#define ESP_RMAKER_DEF_ACTIVE_DATASET "ActiveDataset"
|
||||||
|
#define ESP_RMAKER_DEF_PENDING_DATASET "PendingDataset"
|
||||||
|
#define ESP_RMAKER_DEF_DEVICE_ROLE "DeviceRole"
|
||||||
|
#define ESP_RMAKER_DEF_THREAD_BR_CMD "ThreadCmd"
|
||||||
|
|
||||||
|
/********** THREAD BR SERVICE NAME ***********/
|
||||||
|
#define ESP_RMAKER_DEF_THREAD_BR_SERVICE "TBRService"
|
||||||
|
|
||||||
|
/********** THREAD BR PARAM TYPES ***********/
|
||||||
|
#define ESP_RMAKER_PARAM_TBR_BORDER_AGENT_ID "esp.param.tbr-border-agent-id"
|
||||||
|
#define ESP_RMAKER_PARAM_TBR_ACTIVE_DATASET "esp.param.tbr-active-dataset"
|
||||||
|
#define ESP_RMAKER_PARAM_TBR_PENDING_DATASET "esp.param.tbr-pending-dataset"
|
||||||
|
#define ESP_RMAKER_PARAM_TBR_DEVICE_ROLE "esp.param.tbr-device-role"
|
||||||
|
#define ESP_RMAKER_PARAM_TBR_CMD "esp.param.tbr-cmd"
|
||||||
|
|
||||||
|
/********** THREAD BR SERVICE TYPE ***********/
|
||||||
|
#define ESP_RMAKER_SERVICE_THREAD_BR "esp.service.thread-br"
|
||||||
|
|
||||||
|
/********** THREAD BR CMD TYPE ***********/
|
||||||
|
#define ESP_RMAKER_THREAD_BR_CMD_GEN_DATASET 1
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_thread_br_service_create(const char *serv_name, esp_rmaker_device_write_cb_t write_cb,
|
||||||
|
esp_rmaker_device_read_cb_t read_cb, void *priv_data);
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_thread_br_report_device_role(void);
|
||||||
|
|
||||||
|
esp_err_t esp_rmaker_thread_br_report_border_agent_id(void);
|
||||||
|
|
||||||
|
esp_err_t thread_br_set_thread_enabled(bool enabled);
|
||||||
|
|
||||||
|
esp_err_t thread_br_set_active_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs);
|
||||||
|
|
||||||
|
esp_err_t thread_br_get_active_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs);
|
||||||
|
|
||||||
|
esp_err_t thread_br_set_pending_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs);
|
||||||
|
|
||||||
|
esp_err_t thread_br_get_border_agent_id(otBorderAgentId *border_agent_id);
|
||||||
|
|
||||||
|
otDeviceRole thread_br_get_device_role(void);
|
||||||
|
|
||||||
|
esp_err_t thread_br_generate_and_commit_new_dataset(void);
|
||||||
|
|
||||||
|
esp_err_t thread_border_router_start(const esp_openthread_platform_config_t *platform_config,
|
||||||
|
const esp_rcp_update_config_t *rcp_update_config);
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_rmaker_core.h>
|
||||||
|
#include <esp_rmaker_standard_types.h>
|
||||||
|
#include <esp_rmaker_thread_br_priv.h>
|
||||||
|
|
||||||
|
static esp_rmaker_param_t *esp_rmaker_thread_br_border_agent_id_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TBR_BORDER_AGENT_ID,
|
||||||
|
esp_rmaker_str(""), PROP_FLAG_READ);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_rmaker_param_t *esp_rmaker_thread_br_active_dataset_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TBR_ACTIVE_DATASET,
|
||||||
|
esp_rmaker_str(""),
|
||||||
|
PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_rmaker_param_t *esp_rmaker_thread_br_pending_dataset_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TBR_PENDING_DATASET,
|
||||||
|
esp_rmaker_str(""),
|
||||||
|
PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_rmaker_param_t *esp_rmaker_thread_br_thread_role_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TBR_DEVICE_ROLE,
|
||||||
|
esp_rmaker_int(0), PROP_FLAG_READ);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_rmaker_param_t *esp_rmaker_thread_br_cmd_param_create(const char *param_name)
|
||||||
|
{
|
||||||
|
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TBR_CMD,
|
||||||
|
esp_rmaker_int(0), PROP_FLAG_WRITE);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rmaker_device_t *esp_rmaker_thread_br_service_create(const char *serv_name, esp_rmaker_device_write_cb_t write_cb,
|
||||||
|
esp_rmaker_device_read_cb_t read_cb, void *priv_data)
|
||||||
|
{
|
||||||
|
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_THREAD_BR, priv_data);
|
||||||
|
if (service) {
|
||||||
|
esp_rmaker_device_add_cb(service, write_cb, read_cb);
|
||||||
|
esp_rmaker_device_add_param(
|
||||||
|
service, esp_rmaker_thread_br_border_agent_id_param_create(ESP_RMAKER_DEF_BORDER_AGENT_ID));
|
||||||
|
esp_rmaker_device_add_param(
|
||||||
|
service, esp_rmaker_thread_br_active_dataset_param_create(ESP_RMAKER_DEF_ACTIVE_DATASET));
|
||||||
|
esp_rmaker_device_add_param(
|
||||||
|
service, esp_rmaker_thread_br_pending_dataset_param_create(ESP_RMAKER_DEF_PENDING_DATASET));
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_thread_br_thread_role_param_create(ESP_RMAKER_DEF_DEVICE_ROLE));
|
||||||
|
esp_rmaker_device_add_param(service, esp_rmaker_thread_br_cmd_param_create(ESP_RMAKER_DEF_THREAD_BR_CMD));
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
10
components/esp_schedule/CMakeLists.txt
Normal file
10
components/esp_schedule/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
set(component_srcs "src/esp_schedule.c"
|
||||||
|
"src/esp_schedule_nvs.c")
|
||||||
|
|
||||||
|
idf_component_register(SRCS "${component_srcs}"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
PRIV_INCLUDE_DIRS "src"
|
||||||
|
PRIV_REQUIRES "rmaker_common"
|
||||||
|
REQUIRES "nvs_flash")
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function")
|
||||||
201
components/esp_schedule/LICENSE
Normal file
201
components/esp_schedule/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
50
components/esp_schedule/README.md
Normal file
50
components/esp_schedule/README.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# ESP Scheduling
|
||||||
|
|
||||||
|
[](https://components.espressif.com/components/espressif/esp_schedule)
|
||||||
|
|
||||||
|
This component is used internally by ESP RainMaker to implement schedules.
|
||||||
|
|
||||||
|
> Note: By default, the time is w.r.t. UTC. If the timezone has been set, then the time is w.r.t. the specified timezone.
|
||||||
|
|
||||||
|
## Test code:
|
||||||
|
|
||||||
|
```
|
||||||
|
#include <esp_schedule.h>
|
||||||
|
|
||||||
|
void app_schedule_trigger_cb(esp_schedule_handle_t handle, void *priv_data)
|
||||||
|
{
|
||||||
|
printf("priv_data: %.*s\n", (char *)priv_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *priv_data_global = "from app";
|
||||||
|
|
||||||
|
void app_schedule_set()
|
||||||
|
{
|
||||||
|
esp_schedule_config_t schedule_config = {
|
||||||
|
.name = "test",
|
||||||
|
.trigger.type = ESP_SCHEDULE_TYPE_DAYS_OF_WEEK,
|
||||||
|
.trigger.hours = 13,
|
||||||
|
.trigger.minutes = 30,
|
||||||
|
.trigger.day.repeat_days = ESP_SCHEDULE_DAY_MONDAY | ESP_SCHEDULE_DAY_THURSDAY,
|
||||||
|
.trigger_cb = app_schedule_trigger_cb,
|
||||||
|
.priv_data = priv_data_global,
|
||||||
|
};
|
||||||
|
esp_schedule_create(&schedule_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_schedule_init()
|
||||||
|
{
|
||||||
|
uint8_t schedule_count;
|
||||||
|
esp_schedule_handle_t *schedule_list = esp_schedule_init(true, NULL, &schedule_count);
|
||||||
|
if (schedule_count > 0 && schedule_list != NULL) {
|
||||||
|
ESP_LOGI(TAG, "Found %d schedule(s) in NVS.", schedule_count);
|
||||||
|
for (size_t i = 0; i < schedule_count; i++) {
|
||||||
|
esp_schedule_config_t schedule_config;
|
||||||
|
esp_schedule_get(schedule_list[i], &schedule_config);
|
||||||
|
schedule_config.trigger_cb = app_schedule_trigger_cb;
|
||||||
|
schedule_config.priv_data = priv_data_global;
|
||||||
|
esp_schedule_edit(schedule_list[i], &schedule_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
5
components/esp_schedule/component.mk
Normal file
5
components/esp_schedule/component.mk
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
COMPONENT_ADD_INCLUDEDIRS := include
|
||||||
|
|
||||||
|
COMPONENT_SRCDIRS := src
|
||||||
|
|
||||||
|
CFLAGS += -Wno-unused-function
|
||||||
9
components/esp_schedule/idf_component.yml
Normal file
9
components/esp_schedule/idf_component.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
## IDF Component Manager Manifest File
|
||||||
|
version: "1.2.0"
|
||||||
|
description: ESP Schedules, used in RainMaker
|
||||||
|
url: https://github.com/espressif/esp-rainmaker/tree/master/components/esp_schedule
|
||||||
|
repository: https://github.com/espressif/esp-rainmaker.git
|
||||||
|
issues: https://github.com/espressif/esp-rainmaker/issues
|
||||||
|
dependencies:
|
||||||
|
espressif/rmaker_common:
|
||||||
|
version: "~1.4.2"
|
||||||
236
components/esp_schedule/include/esp_schedule.h
Normal file
236
components/esp_schedule/include/esp_schedule.h
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/** Schedule Handle */
|
||||||
|
typedef void *esp_schedule_handle_t;
|
||||||
|
|
||||||
|
/** Maximum length of the schedule name allowed. This value cannot be more than 16 as it is used for NVS key. */
|
||||||
|
#define MAX_SCHEDULE_NAME_LEN 16
|
||||||
|
|
||||||
|
/** Callback for schedule trigger
|
||||||
|
*
|
||||||
|
* This callback is called when the schedule is triggered.
|
||||||
|
*
|
||||||
|
* @param[in] handle Schedule handle.
|
||||||
|
* @param[in] priv_data Pointer to the private data passed while creating/editing the schedule.
|
||||||
|
*/
|
||||||
|
typedef void (*esp_schedule_trigger_cb_t)(esp_schedule_handle_t handle, void *priv_data);
|
||||||
|
|
||||||
|
/** Callback for schedule timestamp
|
||||||
|
*
|
||||||
|
* This callback is called when the next trigger timestamp of the schedule is changed. This might be useful to check if
|
||||||
|
* one time schedules have already passed while the device was powered off.
|
||||||
|
*
|
||||||
|
* @param[in] handle Schedule handle.
|
||||||
|
* @param[in] next_timestamp timestamp at which the schedule will trigger next.
|
||||||
|
* @param[in] priv_data Pointer to the user data passed while creating/editing the schedule.
|
||||||
|
*/
|
||||||
|
typedef void (*esp_schedule_timestamp_cb_t)(esp_schedule_handle_t handle, uint32_t next_timestamp, void *priv_data);
|
||||||
|
|
||||||
|
/** Schedule type */
|
||||||
|
typedef enum esp_schedule_type {
|
||||||
|
ESP_SCHEDULE_TYPE_INVALID = 0,
|
||||||
|
ESP_SCHEDULE_TYPE_DAYS_OF_WEEK,
|
||||||
|
ESP_SCHEDULE_TYPE_DATE,
|
||||||
|
ESP_SCHEDULE_TYPE_RELATIVE,
|
||||||
|
} esp_schedule_type_t;
|
||||||
|
|
||||||
|
/** Schedule days. Used for ESP_SCHEDULE_TYPE_DAYS_OF_WEEK. */
|
||||||
|
typedef enum esp_schedule_days {
|
||||||
|
ESP_SCHEDULE_DAY_ONCE = 0,
|
||||||
|
ESP_SCHEDULE_DAY_EVERYDAY = 0b1111111,
|
||||||
|
ESP_SCHEDULE_DAY_MONDAY = 1 << 0,
|
||||||
|
ESP_SCHEDULE_DAY_TUESDAY = 1 << 1,
|
||||||
|
ESP_SCHEDULE_DAY_WEDNESDAY = 1 << 2,
|
||||||
|
ESP_SCHEDULE_DAY_THURSDAY = 1 << 3,
|
||||||
|
ESP_SCHEDULE_DAY_FRIDAY = 1 << 4,
|
||||||
|
ESP_SCHEDULE_DAY_SATURDAY = 1 << 5,
|
||||||
|
ESP_SCHEDULE_DAY_SUNDAY = 1 << 6,
|
||||||
|
} esp_schedule_days_t;
|
||||||
|
|
||||||
|
/** Schedule months. Used for ESP_SCHEDULE_TYPE_DATE. */
|
||||||
|
typedef enum esp_schedule_months {
|
||||||
|
ESP_SCHEDULE_MONTH_ONCE = 0,
|
||||||
|
ESP_SCHEDULE_MONTH_ALL = 0b1111111,
|
||||||
|
ESP_SCHEDULE_MONTH_JANUARY = 1 << 0,
|
||||||
|
ESP_SCHEDULE_MONTH_FEBRUARY = 1 << 1,
|
||||||
|
ESP_SCHEDULE_MONTH_MARCH = 1 << 2,
|
||||||
|
ESP_SCHEDULE_MONTH_APRIL = 1 << 3,
|
||||||
|
ESP_SCHEDULE_MONTH_MAY = 1 << 4,
|
||||||
|
ESP_SCHEDULE_MONTH_JUNE = 1 << 5,
|
||||||
|
ESP_SCHEDULE_MONTH_JULY = 1 << 6,
|
||||||
|
ESP_SCHEDULE_MONTH_AUGUST = 1 << 7,
|
||||||
|
ESP_SCHEDULE_MONTH_SEPTEMBER = 1 << 8,
|
||||||
|
ESP_SCHEDULE_MONTH_OCTOBER = 1 << 9,
|
||||||
|
ESP_SCHEDULE_MONTH_NOVEMBER = 1 << 10,
|
||||||
|
ESP_SCHEDULE_MONTH_DECEMBER = 1 << 11,
|
||||||
|
} esp_schedule_months_t;
|
||||||
|
|
||||||
|
/** Trigger details of the schedule */
|
||||||
|
typedef struct esp_schedule_trigger {
|
||||||
|
/** Type of schedule */
|
||||||
|
esp_schedule_type_t type;
|
||||||
|
/** Hours in 24 hour format. Accepted values: 0-23 */
|
||||||
|
uint8_t hours;
|
||||||
|
/** Minutes in the given hour. Accepted values: 0-59. */
|
||||||
|
uint8_t minutes;
|
||||||
|
/** For type ESP_SCHEDULE_TYPE_DAYS_OF_WEEK */
|
||||||
|
struct {
|
||||||
|
/** 'OR' list of esp_schedule_days_t */
|
||||||
|
uint8_t repeat_days;
|
||||||
|
} day;
|
||||||
|
/** For type ESP_SCHEDULE_TYPE_DATE */
|
||||||
|
struct {
|
||||||
|
/** Day of the month. Accepted values: 1-31. */
|
||||||
|
uint8_t day;
|
||||||
|
/* 'OR' list of esp_schedule_months_t */
|
||||||
|
uint16_t repeat_months;
|
||||||
|
/** Year */
|
||||||
|
uint16_t year;
|
||||||
|
/** If the schedule is to be repeated every year. */
|
||||||
|
bool repeat_every_year;
|
||||||
|
} date;
|
||||||
|
/** For type ESP_SCHEDULE_TYPE_SECONDS */
|
||||||
|
int relative_seconds;
|
||||||
|
/** Used for passing the next schedule timestamp for
|
||||||
|
* ESP_SCHEDULE_TYPE_RELATIVE */
|
||||||
|
time_t next_scheduled_time_utc;
|
||||||
|
} esp_schedule_trigger_t;
|
||||||
|
|
||||||
|
/** Schedule Validity
|
||||||
|
* Start and end time within which the schedule will be applicable.
|
||||||
|
*/
|
||||||
|
typedef struct esp_schedule_validity {
|
||||||
|
/* Start time as UTC timestamp */
|
||||||
|
time_t start_time;
|
||||||
|
/* End time as UTC timestamp */
|
||||||
|
time_t end_time;
|
||||||
|
} esp_schedule_validity_t;
|
||||||
|
|
||||||
|
/** Schedule config */
|
||||||
|
typedef struct esp_schedule_config {
|
||||||
|
/** Name of the schedule. This is like a primary key for the schedule. This is required. +1 for NULL termination. */
|
||||||
|
char name[MAX_SCHEDULE_NAME_LEN + 1];
|
||||||
|
/** Trigger details */
|
||||||
|
esp_schedule_trigger_t trigger;
|
||||||
|
/** Trigger callback */
|
||||||
|
esp_schedule_trigger_cb_t trigger_cb;
|
||||||
|
/** Timestamp callback */
|
||||||
|
esp_schedule_timestamp_cb_t timestamp_cb;
|
||||||
|
/** Private data associated with the schedule. This will be passed to callbacks. */
|
||||||
|
void *priv_data;
|
||||||
|
/** Validity of schedules. */
|
||||||
|
esp_schedule_validity_t validity;
|
||||||
|
} esp_schedule_config_t;
|
||||||
|
|
||||||
|
/** Initialize ESP Schedule
|
||||||
|
*
|
||||||
|
* This initializes ESP Schedule. This must be called first before calling any of the other APIs.
|
||||||
|
* This API also gets all the schedules from NVS (if it has been enabled).
|
||||||
|
*
|
||||||
|
* Note: After calling this API, the pointers to the callbacks should be updated for all the schedules by calling
|
||||||
|
* esp_schedule_get() followed by esp_schedule_edit() with the correct callbacks.
|
||||||
|
*
|
||||||
|
* @param[in] enable_nvs If NVS is to be enabled or not.
|
||||||
|
* @param[in] nvs_partition (Optional) The NVS partition to be used. If NULL is passed, the default partition is used.
|
||||||
|
* @param[out] schedule_count Number of active schedules found in NVS.
|
||||||
|
*
|
||||||
|
* @return Array of schedule handles if any schedules have been found.
|
||||||
|
* @return NULL if no schedule is found in NVS (or if NVS is not enabled).
|
||||||
|
*/
|
||||||
|
esp_schedule_handle_t *esp_schedule_init(bool enable_nvs, char *nvs_partition, uint8_t *schedule_count);
|
||||||
|
|
||||||
|
/** Create Schedule
|
||||||
|
*
|
||||||
|
* This API can be used to create a new schedule. The schedule still needs to be enabled using
|
||||||
|
* esp_schedule_enable().
|
||||||
|
*
|
||||||
|
* @param[in] schedule_config Configuration of the schedule to be created.
|
||||||
|
*
|
||||||
|
* @return Schedule handle if successfully created.
|
||||||
|
* @return NULL in case of error.
|
||||||
|
*/
|
||||||
|
esp_schedule_handle_t esp_schedule_create(esp_schedule_config_t *schedule_config);
|
||||||
|
|
||||||
|
/** Remove Schedule
|
||||||
|
*
|
||||||
|
* This API can be used to remove an existing schedule.
|
||||||
|
*
|
||||||
|
* @param[in] handle Schedule handle for the schedule to be removed.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_schedule_delete(esp_schedule_handle_t handle);
|
||||||
|
|
||||||
|
/** Edit Schedule
|
||||||
|
*
|
||||||
|
* This API can be used to edit an existing schedule.
|
||||||
|
* The schedule name should be same as when the schedule was created. The complete config must be provided
|
||||||
|
* or the previously stored config might be over-written.
|
||||||
|
*
|
||||||
|
* Note: If a schedule is edited when it is on-going, the new changes will not be reflected.
|
||||||
|
* You will need to disable the schedule, edit it, and then enable it again.
|
||||||
|
*
|
||||||
|
* @param[in] handle Schedule handle for the schedule to be edited.
|
||||||
|
* @param[in] schedule_config Configuration of the schedule to be edited.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_schedule_edit(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config);
|
||||||
|
|
||||||
|
/** Enable Schedule
|
||||||
|
*
|
||||||
|
* This API can be used to enable an existing schedule.
|
||||||
|
* It can be used to enable a schedule after it has been created using esp_schedule_create()
|
||||||
|
* or if the schedule has been disabled using esp_schedule_disable().
|
||||||
|
*
|
||||||
|
* @param[in] handle Schedule handle for the schedule to be enabled.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_schedule_enable(esp_schedule_handle_t handle);
|
||||||
|
|
||||||
|
/** Disable Schedule
|
||||||
|
*
|
||||||
|
* This API can be used to disable an on-going schedule.
|
||||||
|
* It does not remove the schedule, just stops it. The schedule can be enabled again using
|
||||||
|
* esp_schedule_enable().
|
||||||
|
*
|
||||||
|
* @param[in] handle Schedule handle for the schedule to be disabled.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_schedule_disable(esp_schedule_handle_t handle);
|
||||||
|
|
||||||
|
/** Get Schedule
|
||||||
|
*
|
||||||
|
* This API can be used to get details of an existing schedule.
|
||||||
|
* The schedule_config is populated with the schedule details.
|
||||||
|
*
|
||||||
|
* @param[in] handle Schedule handle.
|
||||||
|
* @param[out] schedule_config Details of the schedule whose handle is passed.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_schedule_get(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
600
components/esp_schedule/src/esp_schedule.c
Normal file
600
components/esp_schedule/src/esp_schedule.c
Normal file
@@ -0,0 +1,600 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_sntp.h>
|
||||||
|
#include <esp_rmaker_utils.h>
|
||||||
|
#include "esp_schedule_internal.h"
|
||||||
|
|
||||||
|
static const char *TAG = "esp_schedule";
|
||||||
|
|
||||||
|
#define SECONDS_TILL_2020 ((2020 - 1970) * 365 * 24 * 3600)
|
||||||
|
#define SECONDS_IN_DAY (60 * 60 * 24)
|
||||||
|
|
||||||
|
static bool init_done = false;
|
||||||
|
|
||||||
|
static int esp_schedule_get_no_of_days(esp_schedule_trigger_t *trigger, struct tm *current_time, struct tm *schedule_time)
|
||||||
|
{
|
||||||
|
/* for day, monday = 0, sunday = 6. */
|
||||||
|
int next_day = 0;
|
||||||
|
/* struct tm has tm_wday with sunday as 0. Whereas we have monday as 0. Converting struct tm to our format */
|
||||||
|
int today = ((current_time->tm_wday + 7 - 1) % 7);
|
||||||
|
|
||||||
|
esp_schedule_days_t today_bit = 1 << today;
|
||||||
|
uint8_t repeat_days = trigger->day.repeat_days;
|
||||||
|
int current_seconds = (current_time->tm_hour * 60 + current_time->tm_min) * 60 + current_time->tm_sec;
|
||||||
|
int schedule_seconds = (schedule_time->tm_hour * 60 + schedule_time->tm_min) * 60;
|
||||||
|
|
||||||
|
/* Handling for one time schedule */
|
||||||
|
if (repeat_days == ESP_SCHEDULE_DAY_ONCE) {
|
||||||
|
if (schedule_seconds > current_seconds) {
|
||||||
|
/* The schedule is today and is yet to go off */
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
/* The schedule is tomorrow */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handling for repeating schedules */
|
||||||
|
/* Check if it is today */
|
||||||
|
if ((repeat_days & today_bit)) {
|
||||||
|
if (schedule_seconds > current_seconds) {
|
||||||
|
/* The schedule is today and is yet to go off. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Check if it is this week or next week */
|
||||||
|
if ((repeat_days & (today_bit ^ 0xFF)) > today_bit) {
|
||||||
|
/* Next schedule is yet to come in this week */
|
||||||
|
next_day = ffs(repeat_days & (0xFF << (today + 1))) - 1;
|
||||||
|
return (next_day - today);
|
||||||
|
} else {
|
||||||
|
/* First scheduled day of the next week */
|
||||||
|
next_day = ffs(repeat_days) - 1;
|
||||||
|
if (next_day == today) {
|
||||||
|
/* Same day, next week */
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
return (7 - today + next_day);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGE(TAG, "No of days could not be found. This should not happen.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t esp_schedule_get_next_month(esp_schedule_trigger_t *trigger, struct tm *current_time, struct tm *schedule_time)
|
||||||
|
{
|
||||||
|
int current_seconds = (current_time->tm_hour * 60 + current_time->tm_min) * 60 + current_time->tm_sec;
|
||||||
|
int schedule_seconds = (schedule_time->tm_hour * 60 + schedule_time->tm_min) * 60;
|
||||||
|
/* +1 is because struct tm has months starting from 0, whereas we have them starting from 1 */
|
||||||
|
uint8_t current_month = current_time->tm_mon + 1;
|
||||||
|
/* -1 because month_bit starts from 0b1. So for January, it should be 1 << 0. And current_month starts from 1. */
|
||||||
|
uint16_t current_month_bit = 1 << (current_month - 1);
|
||||||
|
uint8_t next_schedule_month = 0;
|
||||||
|
uint16_t repeat_months = trigger->date.repeat_months;
|
||||||
|
|
||||||
|
/* Check if month is not specified */
|
||||||
|
if (repeat_months == ESP_SCHEDULE_MONTH_ONCE) {
|
||||||
|
if (trigger->date.day == current_time->tm_mday) {
|
||||||
|
/* The schedule day is same. Check if time has already passed */
|
||||||
|
if (schedule_seconds > current_seconds) {
|
||||||
|
/* The schedule is today and is yet to go off */
|
||||||
|
return current_month;
|
||||||
|
} else {
|
||||||
|
/* Today's time has passed */
|
||||||
|
return (current_month + 1);
|
||||||
|
}
|
||||||
|
} else if (trigger->date.day > current_time->tm_mday) {
|
||||||
|
/* The day is yet to come in this month */
|
||||||
|
return current_month;
|
||||||
|
} else {
|
||||||
|
/* The day has passed in the current month */
|
||||||
|
return (current_month + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if schedule is not this year itself, it is in future. */
|
||||||
|
if (trigger->date.year > (current_time->tm_year + 1900)) {
|
||||||
|
/* Find first schedule month of next year */
|
||||||
|
next_schedule_month = ffs(repeat_months);
|
||||||
|
/* Year will be handled by the caller. So no need to add any additional months */
|
||||||
|
return next_schedule_month;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if schedule is this month and is yet to come */
|
||||||
|
if (current_month_bit & repeat_months) {
|
||||||
|
if (trigger->date.day == current_time->tm_mday) {
|
||||||
|
/* The schedule day is same. Check if time has already passed */
|
||||||
|
if (schedule_seconds > current_seconds) {
|
||||||
|
/* The schedule is today and is yet to go off */
|
||||||
|
return current_month;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (trigger->date.day > current_time->tm_mday) {
|
||||||
|
/* The day is yet to come in this month */
|
||||||
|
return current_month;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if schedule is this year */
|
||||||
|
if ((repeat_months & (current_month_bit ^ 0xFFFF)) > current_month_bit) {
|
||||||
|
/* Next schedule month is yet to come in this year */
|
||||||
|
next_schedule_month = ffs(repeat_months & (0xFFFF << (current_month)));
|
||||||
|
return next_schedule_month;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if schedule is for this year and does not repeat */
|
||||||
|
if (!trigger->date.repeat_every_year) {
|
||||||
|
if (trigger->date.year <= (current_time->tm_year + 1900)) {
|
||||||
|
ESP_LOGE(TAG, "Schedule does not repeat next year, but get_next_month has been called.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Schedule is not this year */
|
||||||
|
/* Find first schedule month of next year */
|
||||||
|
next_schedule_month = ffs(repeat_months);
|
||||||
|
/* +12 because the schedule is next year */
|
||||||
|
return (next_schedule_month + 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t esp_schedule_get_next_year(esp_schedule_trigger_t *trigger, struct tm *current_time, struct tm *schedule_time)
|
||||||
|
{
|
||||||
|
uint16_t current_year = current_time->tm_year + 1900;
|
||||||
|
uint16_t schedule_year = trigger->date.year;
|
||||||
|
if (schedule_year > current_year) {
|
||||||
|
return schedule_year;
|
||||||
|
}
|
||||||
|
/* If the schedule is set to repeat_every_year, we return the current year */
|
||||||
|
/* If the schedule has already passed in this year, we still return current year, as the additional months will be handled in get_next_month */
|
||||||
|
return current_year;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t esp_schedule_get_next_schedule_time_diff(const char *schedule_name, esp_schedule_trigger_t *trigger)
|
||||||
|
{
|
||||||
|
struct tm current_time, schedule_time;
|
||||||
|
time_t now;
|
||||||
|
char time_str[64];
|
||||||
|
int32_t time_diff;
|
||||||
|
|
||||||
|
/* Get current time */
|
||||||
|
time(&now);
|
||||||
|
/* Handling ESP_SCHEDULE_TYPE_RELATIVE first since it doesn't require any
|
||||||
|
* computation based on days, hours, minutes, etc.
|
||||||
|
*/
|
||||||
|
if (trigger->type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||||
|
/* If next scheduled time is already set, just compute the difference
|
||||||
|
* between current time and next scheduled time and return that diff.
|
||||||
|
*/
|
||||||
|
time_t target;
|
||||||
|
if (trigger->next_scheduled_time_utc > 0) {
|
||||||
|
target = (time_t)trigger->next_scheduled_time_utc;
|
||||||
|
time_diff = difftime(target, now);
|
||||||
|
} else {
|
||||||
|
target = now + (time_t)trigger->relative_seconds;
|
||||||
|
time_diff = trigger->relative_seconds;
|
||||||
|
}
|
||||||
|
localtime_r(&target, &schedule_time);
|
||||||
|
trigger->next_scheduled_time_utc = mktime(&schedule_time);
|
||||||
|
/* Print schedule time */
|
||||||
|
memset(time_str, 0, sizeof(time_str));
|
||||||
|
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &schedule_time);
|
||||||
|
ESP_LOGI(TAG, "Schedule %s will be active on: %s. DST: %s", schedule_name, time_str, schedule_time.tm_isdst ? "Yes" : "No");
|
||||||
|
return time_diff;
|
||||||
|
}
|
||||||
|
localtime_r(&now, ¤t_time);
|
||||||
|
|
||||||
|
/* Get schedule time */
|
||||||
|
localtime_r(&now, &schedule_time);
|
||||||
|
schedule_time.tm_sec = 0;
|
||||||
|
schedule_time.tm_min = trigger->minutes;
|
||||||
|
schedule_time.tm_hour = trigger->hours;
|
||||||
|
mktime(&schedule_time);
|
||||||
|
|
||||||
|
/* Adjust schedule day */
|
||||||
|
if (trigger->type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||||
|
int no_of_days = 0;
|
||||||
|
no_of_days = esp_schedule_get_no_of_days(trigger, ¤t_time, &schedule_time);
|
||||||
|
schedule_time.tm_sec += no_of_days * SECONDS_IN_DAY;
|
||||||
|
}
|
||||||
|
if (trigger->type == ESP_SCHEDULE_TYPE_DATE) {
|
||||||
|
schedule_time.tm_mday = trigger->date.day;
|
||||||
|
schedule_time.tm_mon = esp_schedule_get_next_month(trigger, ¤t_time, &schedule_time) - 1;
|
||||||
|
schedule_time.tm_year = esp_schedule_get_next_year(trigger, ¤t_time, &schedule_time) - 1900;
|
||||||
|
if (schedule_time.tm_mon < 0) {
|
||||||
|
ESP_LOGE(TAG, "Invalid month found: %d. Setting it to next month.", schedule_time.tm_mon);
|
||||||
|
schedule_time.tm_mon = current_time.tm_mon + 1;
|
||||||
|
}
|
||||||
|
if (schedule_time.tm_mon >= 12) {
|
||||||
|
schedule_time.tm_year += schedule_time.tm_mon / 12;
|
||||||
|
schedule_time.tm_mon = schedule_time.tm_mon % 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mktime(&schedule_time);
|
||||||
|
|
||||||
|
/* Adjust time according to DST */
|
||||||
|
time_t dst_adjust = 0;
|
||||||
|
if (!current_time.tm_isdst && schedule_time.tm_isdst) {
|
||||||
|
dst_adjust = -3600;
|
||||||
|
} else if (current_time.tm_isdst && !schedule_time.tm_isdst ) {
|
||||||
|
dst_adjust = 3600;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "DST adjust seconds: %lld", (long long) dst_adjust);
|
||||||
|
schedule_time.tm_sec += dst_adjust;
|
||||||
|
mktime(&schedule_time);
|
||||||
|
|
||||||
|
/* Print schedule time */
|
||||||
|
memset(time_str, 0, sizeof(time_str));
|
||||||
|
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &schedule_time);
|
||||||
|
ESP_LOGI(TAG, "Schedule %s will be active on: %s. DST: %s", schedule_name, time_str, schedule_time.tm_isdst ? "Yes" : "No");
|
||||||
|
|
||||||
|
/* Calculate difference */
|
||||||
|
time_diff = difftime((mktime(&schedule_time)), mktime(¤t_time));
|
||||||
|
|
||||||
|
/* For one time schedules to check for expiry after a reboot. If NVS is enabled, this should be stored in NVS. */
|
||||||
|
trigger->next_scheduled_time_utc = mktime(&schedule_time);
|
||||||
|
|
||||||
|
return time_diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool esp_schedule_is_expired(esp_schedule_trigger_t *trigger)
|
||||||
|
{
|
||||||
|
time_t current_timestamp = 0;
|
||||||
|
struct tm current_time = {0};
|
||||||
|
time(¤t_timestamp);
|
||||||
|
localtime_r(¤t_timestamp, ¤t_time);
|
||||||
|
|
||||||
|
if (trigger->type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||||
|
if (trigger->next_scheduled_time_utc > 0 && trigger->next_scheduled_time_utc <= current_timestamp) {
|
||||||
|
/* Relative seconds based schedule has expired */
|
||||||
|
return true;
|
||||||
|
} else if (trigger->next_scheduled_time_utc == 0) {
|
||||||
|
/* Schedule has been disabled , so it is as good as expired. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (trigger->type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||||
|
if (trigger->day.repeat_days == ESP_SCHEDULE_DAY_ONCE) {
|
||||||
|
if (trigger->next_scheduled_time_utc > 0 && trigger->next_scheduled_time_utc <= current_timestamp) {
|
||||||
|
/* One time schedule has expired */
|
||||||
|
return true;
|
||||||
|
} else if (trigger->next_scheduled_time_utc == 0) {
|
||||||
|
/* Schedule has been disabled , so it is as good as expired. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (trigger->type == ESP_SCHEDULE_TYPE_DATE) {
|
||||||
|
if (trigger->date.repeat_months == 0) {
|
||||||
|
if (trigger->next_scheduled_time_utc > 0 && trigger->next_scheduled_time_utc <= current_timestamp) {
|
||||||
|
/* One time schedule has expired */
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (trigger->date.repeat_every_year == true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tm schedule_time = {0};
|
||||||
|
localtime_r(¤t_timestamp, &schedule_time);
|
||||||
|
schedule_time.tm_sec = 0;
|
||||||
|
schedule_time.tm_min = trigger->minutes;
|
||||||
|
schedule_time.tm_hour = trigger->hours;
|
||||||
|
schedule_time.tm_mday = trigger->date.day;
|
||||||
|
/* For expiry, just check the last month of the repeat_months. */
|
||||||
|
/* '-1' because struct tm has months starting from 0 and we have months starting from 1. */
|
||||||
|
schedule_time.tm_mon = fls(trigger->date.repeat_months) - 1;
|
||||||
|
/* '-1900' because struct tm has number of years after 1900 */
|
||||||
|
schedule_time.tm_year = trigger->date.year - 1900;
|
||||||
|
time_t schedule_timestamp = mktime(&schedule_time);
|
||||||
|
|
||||||
|
if (schedule_timestamp < current_timestamp) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Invalid type. Mark as expired */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_schedule_stop_timer(esp_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
xTimerStop(schedule->timer, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_schedule_start_timer(esp_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
time_t current_time = 0;
|
||||||
|
time(¤t_time);
|
||||||
|
if (current_time < SECONDS_TILL_2020) {
|
||||||
|
ESP_LOGE(TAG, "Time is not updated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule->next_scheduled_time_diff = esp_schedule_get_next_schedule_time_diff(schedule->name, &schedule->trigger);
|
||||||
|
ESP_LOGI(TAG, "Starting a timer for %"PRIu32" seconds for schedule %s", schedule->next_scheduled_time_diff, schedule->name);
|
||||||
|
|
||||||
|
if (schedule->timestamp_cb) {
|
||||||
|
schedule->timestamp_cb((esp_schedule_handle_t)schedule, schedule->trigger.next_scheduled_time_utc, schedule->priv_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
xTimerStop(schedule->timer, portMAX_DELAY);
|
||||||
|
xTimerChangePeriod(schedule->timer, (schedule->next_scheduled_time_diff * 1000) / portTICK_PERIOD_MS, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_schedule_common_timer_cb(TimerHandle_t timer)
|
||||||
|
{
|
||||||
|
void *priv_data = pvTimerGetTimerID(timer);
|
||||||
|
if (priv_data == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_schedule_t *schedule = (esp_schedule_t *)priv_data;
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
struct tm validity_time;
|
||||||
|
char time_str[64] = {0};
|
||||||
|
if (schedule->validity.start_time != 0) {
|
||||||
|
if (now < schedule->validity.start_time) {
|
||||||
|
memset(time_str, 0, sizeof(time_str));
|
||||||
|
localtime_r(&schedule->validity.start_time, &validity_time);
|
||||||
|
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &validity_time);
|
||||||
|
ESP_LOGW(TAG, "Schedule %s skipped. It will be active only after: %s. DST: %s.", schedule->name, time_str, validity_time.tm_isdst ? "Yes" : "No");
|
||||||
|
/* TODO: Start the timer such that the next time it triggeres, it will be within the valid window.
|
||||||
|
* Currently, it will just keep triggering and then get skipped if not in valid range.
|
||||||
|
*/
|
||||||
|
goto restart_schedule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (schedule->validity.end_time != 0) {
|
||||||
|
if (now > schedule->validity.end_time) {
|
||||||
|
localtime_r(&schedule->validity.end_time, &validity_time);
|
||||||
|
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &validity_time);
|
||||||
|
ESP_LOGW(TAG, "Schedule %s skipped. It can't be active after: %s. DST: %s.", schedule->name, time_str, validity_time.tm_isdst ? "Yes" : "No");
|
||||||
|
/* Return from here will ensure that the timer does not start again for this schedule */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Schedule %s triggered", schedule->name);
|
||||||
|
if (schedule->trigger_cb) {
|
||||||
|
schedule->trigger_cb((esp_schedule_handle_t)schedule, schedule->priv_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
restart_schedule:
|
||||||
|
|
||||||
|
if (esp_schedule_is_expired(&schedule->trigger)) {
|
||||||
|
/* Not deleting the schedule here. Just not starting it again. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_schedule_start_timer(schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_schedule_delete_timer(esp_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
xTimerDelete(schedule->timer, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_schedule_create_timer(esp_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
if (esp_schedule_nvs_is_enabled()) {
|
||||||
|
/* This is just used for calculating next_scheduled_time_utc for ESP_SCHEDULE_DAY_ONCE (in case of ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) or for ESP_SCHEDULE_MONTH_ONCE (in case of ESP_SCHEDULE_TYPE_DATE), and only used when NVS is enabled. And if NVS is enabled, time will already be synced and the time will be correctly calculated. */
|
||||||
|
schedule->next_scheduled_time_diff = esp_schedule_get_next_schedule_time_diff(schedule->name, &schedule->trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Temporarily setting the timer for 1 (anything greater than 0) tick. This will get changed when xTimerChangePeriod() is called. */
|
||||||
|
schedule->timer = xTimerCreate("schedule", 1, pdFALSE, (void *)schedule, esp_schedule_common_timer_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_schedule_get(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config)
|
||||||
|
{
|
||||||
|
if (schedule_config == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (handle == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||||
|
|
||||||
|
strcpy(schedule_config->name, schedule->name);
|
||||||
|
schedule_config->trigger.type = schedule->trigger.type;
|
||||||
|
schedule_config->trigger.hours = schedule->trigger.hours;
|
||||||
|
schedule_config->trigger.minutes = schedule->trigger.minutes;
|
||||||
|
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||||
|
schedule_config->trigger.day.repeat_days = schedule->trigger.day.repeat_days;
|
||||||
|
} else if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DATE) {
|
||||||
|
schedule_config->trigger.date.day = schedule->trigger.date.day;
|
||||||
|
schedule_config->trigger.date.repeat_months = schedule->trigger.date.repeat_months;
|
||||||
|
schedule_config->trigger.date.year = schedule->trigger.date.year;
|
||||||
|
schedule_config->trigger.date.repeat_every_year = schedule->trigger.date.repeat_every_year;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule_config->trigger_cb = schedule->trigger_cb;
|
||||||
|
schedule_config->timestamp_cb = schedule->timestamp_cb;
|
||||||
|
schedule_config->priv_data = schedule->priv_data;
|
||||||
|
schedule_config->validity = schedule->validity;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_schedule_enable(esp_schedule_handle_t handle)
|
||||||
|
{
|
||||||
|
if (handle == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||||
|
esp_schedule_start_timer(schedule);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_schedule_disable(esp_schedule_handle_t handle)
|
||||||
|
{
|
||||||
|
if (handle == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||||
|
esp_schedule_stop_timer(schedule);
|
||||||
|
/* Disabling a schedule should also reset the next_scheduled_time.
|
||||||
|
* It would be re-computed after enabling.
|
||||||
|
*/
|
||||||
|
schedule->trigger.next_scheduled_time_utc = 0;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_schedule_set(esp_schedule_t *schedule, esp_schedule_config_t *schedule_config)
|
||||||
|
{
|
||||||
|
/* Setting everything apart from name. */
|
||||||
|
schedule->trigger.type = schedule_config->trigger.type;
|
||||||
|
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||||
|
schedule->trigger.relative_seconds = schedule_config->trigger.relative_seconds;
|
||||||
|
schedule->trigger.next_scheduled_time_utc = schedule_config->trigger.next_scheduled_time_utc;
|
||||||
|
} else {
|
||||||
|
schedule->trigger.hours = schedule_config->trigger.hours;
|
||||||
|
schedule->trigger.minutes = schedule_config->trigger.minutes;
|
||||||
|
|
||||||
|
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||||
|
schedule->trigger.day.repeat_days = schedule_config->trigger.day.repeat_days;
|
||||||
|
} else if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DATE) {
|
||||||
|
schedule->trigger.date.day = schedule_config->trigger.date.day;
|
||||||
|
schedule->trigger.date.repeat_months = schedule_config->trigger.date.repeat_months;
|
||||||
|
schedule->trigger.date.year = schedule_config->trigger.date.year;
|
||||||
|
schedule->trigger.date.repeat_every_year = schedule_config->trigger.date.repeat_every_year;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule->trigger_cb = schedule_config->trigger_cb;
|
||||||
|
schedule->timestamp_cb = schedule_config->timestamp_cb;
|
||||||
|
schedule->priv_data = schedule_config->priv_data;
|
||||||
|
schedule->validity = schedule_config->validity;
|
||||||
|
esp_schedule_nvs_add(schedule);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_schedule_edit(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config)
|
||||||
|
{
|
||||||
|
if (handle == NULL || schedule_config == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||||
|
if (strncmp(schedule->name, schedule_config->name, sizeof(schedule->name)) != 0) {
|
||||||
|
ESP_LOGE(TAG, "Schedule name mismatch. Expected: %s, Passed: %s", schedule->name, schedule_config->name);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Editing a schedule with relative time should also reset it. */
|
||||||
|
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||||
|
schedule->trigger.next_scheduled_time_utc = 0;
|
||||||
|
}
|
||||||
|
esp_schedule_set(schedule, schedule_config);
|
||||||
|
ESP_LOGD(TAG, "Schedule %s edited", schedule->name);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_schedule_delete(esp_schedule_handle_t handle)
|
||||||
|
{
|
||||||
|
if (handle == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||||
|
ESP_LOGI(TAG, "Deleting schedule %s", schedule->name);
|
||||||
|
if (schedule->timer) {
|
||||||
|
esp_schedule_stop_timer(schedule);
|
||||||
|
esp_schedule_delete_timer(schedule);
|
||||||
|
}
|
||||||
|
esp_schedule_nvs_remove(schedule);
|
||||||
|
free(schedule);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_schedule_handle_t esp_schedule_create(esp_schedule_config_t *schedule_config)
|
||||||
|
{
|
||||||
|
if (schedule_config == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (strlen(schedule_config->name) <= 0) {
|
||||||
|
ESP_LOGE(TAG, "Set schedule failed. Please enter a unique valid name for the schedule.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schedule_config->trigger.type == ESP_SCHEDULE_TYPE_INVALID) {
|
||||||
|
ESP_LOGE(TAG, "Schedule type is invalid.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_schedule_t *schedule = (esp_schedule_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_schedule_t));
|
||||||
|
if (schedule == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Could not allocate handle");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strlcpy(schedule->name, schedule_config->name, sizeof(schedule->name));
|
||||||
|
|
||||||
|
esp_schedule_set(schedule, schedule_config);
|
||||||
|
|
||||||
|
esp_schedule_create_timer(schedule);
|
||||||
|
ESP_LOGD(TAG, "Schedule %s created", schedule->name);
|
||||||
|
return (esp_schedule_handle_t)schedule;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_schedule_handle_t *esp_schedule_init(bool enable_nvs, char *nvs_partition, uint8_t *schedule_count)
|
||||||
|
{
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||||
|
if (!esp_sntp_enabled()) {
|
||||||
|
ESP_LOGI(TAG, "Initializing SNTP");
|
||||||
|
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||||
|
esp_sntp_setservername(0, "pool.ntp.org");
|
||||||
|
esp_sntp_init();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (!sntp_enabled()) {
|
||||||
|
ESP_LOGI(TAG, "Initializing SNTP");
|
||||||
|
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||||
|
sntp_setservername(0, "pool.ntp.org");
|
||||||
|
sntp_init();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!enable_nvs) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for time to be updated here */
|
||||||
|
|
||||||
|
|
||||||
|
/* Below this is initialising schedules from NVS */
|
||||||
|
esp_schedule_nvs_init(nvs_partition);
|
||||||
|
|
||||||
|
/* Get handle list from NVS */
|
||||||
|
esp_schedule_handle_t *handle_list = NULL;
|
||||||
|
*schedule_count = 0;
|
||||||
|
handle_list = esp_schedule_nvs_get_all(schedule_count);
|
||||||
|
if (handle_list == NULL) {
|
||||||
|
ESP_LOGI(TAG, "No schedules found in NVS");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Schedules found in NVS: %"PRIu8, *schedule_count);
|
||||||
|
/* Start/Delete the schedules */
|
||||||
|
esp_schedule_t *schedule = NULL;
|
||||||
|
for (size_t handle_count = 0; handle_count < *schedule_count; handle_count++) {
|
||||||
|
schedule = (esp_schedule_t *)handle_list[handle_count];
|
||||||
|
schedule->trigger_cb = NULL;
|
||||||
|
schedule->timer = NULL;
|
||||||
|
/* Check for ONCE and expired schedules and delete them. */
|
||||||
|
if (esp_schedule_is_expired(&schedule->trigger)) {
|
||||||
|
/* This schedule has already expired. */
|
||||||
|
ESP_LOGI(TAG, "Schedule %s does not repeat and has already expired. Deleting it.", schedule->name);
|
||||||
|
esp_schedule_delete((esp_schedule_handle_t)schedule);
|
||||||
|
/* Removing the schedule from the list */
|
||||||
|
handle_list[handle_count] = handle_list[*schedule_count - 1];
|
||||||
|
(*schedule_count)--;
|
||||||
|
handle_count--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
esp_schedule_create_timer(schedule);
|
||||||
|
esp_schedule_start_timer(schedule);
|
||||||
|
}
|
||||||
|
init_done = true;
|
||||||
|
return handle_list;
|
||||||
|
}
|
||||||
28
components/esp_schedule/src/esp_schedule_internal.h
Normal file
28
components/esp_schedule/src/esp_schedule_internal.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/timers.h>
|
||||||
|
#include <esp_schedule.h>
|
||||||
|
|
||||||
|
typedef struct esp_schedule {
|
||||||
|
char name[MAX_SCHEDULE_NAME_LEN + 1];
|
||||||
|
esp_schedule_trigger_t trigger;
|
||||||
|
uint32_t next_scheduled_time_diff;
|
||||||
|
TimerHandle_t timer;
|
||||||
|
esp_schedule_trigger_cb_t trigger_cb;
|
||||||
|
esp_schedule_timestamp_cb_t timestamp_cb;
|
||||||
|
void *priv_data;
|
||||||
|
esp_schedule_validity_t validity;
|
||||||
|
} esp_schedule_t;
|
||||||
|
|
||||||
|
esp_err_t esp_schedule_nvs_add(esp_schedule_t *schedule);
|
||||||
|
esp_err_t esp_schedule_nvs_remove(esp_schedule_t *schedule);
|
||||||
|
esp_schedule_handle_t *esp_schedule_nvs_get_all(uint8_t *schedule_count);
|
||||||
|
bool esp_schedule_nvs_is_enabled(void);
|
||||||
|
esp_err_t esp_schedule_nvs_init(char *nvs_partition);
|
||||||
296
components/esp_schedule/src/esp_schedule_nvs.c
Normal file
296
components/esp_schedule/src/esp_schedule_nvs.c
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <nvs.h>
|
||||||
|
#include "esp_schedule_internal.h"
|
||||||
|
|
||||||
|
static const char *TAG = "esp_schedule_nvs";
|
||||||
|
|
||||||
|
#define ESP_SCHEDULE_NVS_NAMESPACE "schd"
|
||||||
|
#define ESP_SCHEDULE_COUNT_KEY "schd_count"
|
||||||
|
|
||||||
|
static char *esp_schedule_nvs_partition = NULL;
|
||||||
|
static bool nvs_enabled = false;
|
||||||
|
|
||||||
|
esp_err_t esp_schedule_nvs_add(esp_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
if (!nvs_enabled) {
|
||||||
|
ESP_LOGD(TAG, "NVS not enabled. Not adding to NVS.");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
nvs_handle_t nvs_handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if this is new schedule or editing an existing schedule */
|
||||||
|
size_t buf_size;
|
||||||
|
bool editing_schedule = true;
|
||||||
|
err = nvs_get_blob(nvs_handle, schedule->name, NULL, &buf_size);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||||
|
editing_schedule = false;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "Updating the existing schedule %s", schedule->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nvs_set_blob(nvs_handle, schedule->name, schedule, sizeof(esp_schedule_t));
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS set failed with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (editing_schedule == false) {
|
||||||
|
uint8_t schedule_count;
|
||||||
|
err = nvs_get_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, &schedule_count);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||||
|
schedule_count = 0;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schedule_count++;
|
||||||
|
err = nvs_set_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, schedule_count);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS set failed for schedule count with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nvs_commit(nvs_handle);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
ESP_LOGI(TAG, "Schedule %s added in NVS", schedule->name);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_schedule_nvs_remove_all(void)
|
||||||
|
{
|
||||||
|
if (!nvs_enabled) {
|
||||||
|
ESP_LOGD(TAG, "NVS not enabled. Not removing from NVS.");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
nvs_handle_t nvs_handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = nvs_erase_all(nvs_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS erase all keys failed with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
nvs_commit(nvs_handle);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
ESP_LOGI(TAG, "All schedules removed from NVS");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_schedule_nvs_remove(esp_schedule_t *schedule)
|
||||||
|
{
|
||||||
|
if (!nvs_enabled) {
|
||||||
|
ESP_LOGD(TAG, "NVS not enabled. Not removing from NVS.");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
nvs_handle_t nvs_handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = nvs_erase_key(nvs_handle, schedule->name);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS erase key failed with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
uint8_t schedule_count;
|
||||||
|
err = nvs_get_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, &schedule_count);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS get failed for schedule count with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
schedule_count--;
|
||||||
|
err = nvs_set_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, schedule_count);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS set failed for schedule count with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
nvs_commit(nvs_handle);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
ESP_LOGI(TAG, "Schedule %s removed from NVS", schedule->name);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t esp_schedule_nvs_get_count(void)
|
||||||
|
{
|
||||||
|
if (!nvs_enabled) {
|
||||||
|
ESP_LOGD(TAG, "NVS not enabled. Not getting count from NVS.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
nvs_handle_t nvs_handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t schedule_count;
|
||||||
|
err = nvs_get_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, &schedule_count);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS get failed for schedule count with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
ESP_LOGI(TAG, "Schedules in NVS: %d", schedule_count);
|
||||||
|
return schedule_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_schedule_handle_t esp_schedule_nvs_get(char *nvs_key)
|
||||||
|
{
|
||||||
|
if (!nvs_enabled) {
|
||||||
|
ESP_LOGD(TAG, "NVS not enabled. Not getting from NVS.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
size_t buf_size;
|
||||||
|
nvs_handle_t nvs_handle;
|
||||||
|
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
err = nvs_get_blob(nvs_handle, nvs_key, NULL, &buf_size);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
esp_schedule_t *schedule = (esp_schedule_t *)malloc(buf_size);
|
||||||
|
if (schedule == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Could not allocate handle");
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
err = nvs_get_blob(nvs_handle, nvs_key, schedule, &buf_size);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
free(schedule);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
nvs_close(nvs_handle);
|
||||||
|
ESP_LOGI(TAG, "Schedule %s found in NVS", schedule->name);
|
||||||
|
return (esp_schedule_handle_t) schedule;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_schedule_handle_t *esp_schedule_nvs_get_all(uint8_t *schedule_count)
|
||||||
|
{
|
||||||
|
if (!nvs_enabled) {
|
||||||
|
ESP_LOGD(TAG, "NVS not enabled. Not Initialising NVS.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*schedule_count = esp_schedule_nvs_get_count();
|
||||||
|
if (*schedule_count == 0) {
|
||||||
|
ESP_LOGI(TAG, "No Entries found in NVS");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
esp_schedule_handle_t *handle_list = (esp_schedule_handle_t *)malloc(sizeof(esp_schedule_handle_t) * (*schedule_count));
|
||||||
|
if (handle_list == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Could not allocate schedule list");
|
||||||
|
*schedule_count = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int handle_count = 0;
|
||||||
|
|
||||||
|
nvs_entry_info_t nvs_entry;
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
nvs_iterator_t nvs_iterator = NULL;
|
||||||
|
esp_err_t err = nvs_entry_find(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_TYPE_BLOB, &nvs_iterator);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "No entry found in NVS");
|
||||||
|
return NULL;;
|
||||||
|
}
|
||||||
|
while (err == ESP_OK) {
|
||||||
|
nvs_entry_info(nvs_iterator, &nvs_entry);
|
||||||
|
ESP_LOGI(TAG, "Found schedule in NVS with key: %s", nvs_entry.key);
|
||||||
|
handle_list[handle_count] = esp_schedule_nvs_get(nvs_entry.key);
|
||||||
|
if (handle_list[handle_count] != NULL) {
|
||||||
|
/* Increase count only if nvs_get was successful */
|
||||||
|
handle_count++;
|
||||||
|
}
|
||||||
|
err = nvs_entry_next(&nvs_iterator);
|
||||||
|
}
|
||||||
|
nvs_release_iterator(nvs_iterator);
|
||||||
|
#else
|
||||||
|
nvs_iterator_t nvs_iterator = nvs_entry_find(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_TYPE_BLOB);
|
||||||
|
if (nvs_iterator == NULL) {
|
||||||
|
ESP_LOGE(TAG, "No entry found in NVS");
|
||||||
|
return NULL;;
|
||||||
|
}
|
||||||
|
while (nvs_iterator != NULL) {
|
||||||
|
nvs_entry_info(nvs_iterator, &nvs_entry);
|
||||||
|
ESP_LOGI(TAG, "Found schedule in NVS with key: %s", nvs_entry.key);
|
||||||
|
handle_list[handle_count] = esp_schedule_nvs_get(nvs_entry.key);
|
||||||
|
if (handle_list[handle_count] != NULL) {
|
||||||
|
/* Increase count only if nvs_get was successful */
|
||||||
|
handle_count++;
|
||||||
|
}
|
||||||
|
nvs_iterator = nvs_entry_next(nvs_iterator);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
*schedule_count = handle_count;
|
||||||
|
ESP_LOGI(TAG, "Found %d schedules in NVS", *schedule_count);
|
||||||
|
return handle_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool esp_schedule_nvs_is_enabled(void)
|
||||||
|
{
|
||||||
|
return nvs_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_schedule_nvs_init(char *nvs_partition)
|
||||||
|
{
|
||||||
|
if (nvs_enabled) {
|
||||||
|
ESP_LOGI(TAG, "NVS already enabled");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
if (nvs_partition) {
|
||||||
|
esp_schedule_nvs_partition = strndup(nvs_partition, strlen(nvs_partition));
|
||||||
|
} else {
|
||||||
|
esp_schedule_nvs_partition = strndup("nvs", strlen("nvs"));
|
||||||
|
}
|
||||||
|
if (esp_schedule_nvs_partition == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Could not allocate nvs_partition");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
nvs_enabled = true;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
21
components/rmaker_common/.github/workflows/upload_components.yml
vendored
Normal file
21
components/rmaker_common/.github/workflows/upload_components.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Push components to Espressif Component Registry
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
upload_components:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: "recursive"
|
||||||
|
|
||||||
|
- name: Upload components to component registry
|
||||||
|
uses: espressif/upload-components-ci-action@v1
|
||||||
|
with:
|
||||||
|
namespace: "espressif"
|
||||||
|
name: "rmaker_common"
|
||||||
|
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||||
23
components/rmaker_common/CMakeLists.txt
Normal file
23
components/rmaker_common/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
set(srcs "src/work_queue.c" "src/factory.c" "src/time.c" "src/timezone.c" "src/utils.c"
|
||||||
|
"src/cmd_resp.c" "src/console/rmaker_common_cmds.c" "src/console/rmaker_console.c")
|
||||||
|
|
||||||
|
set(priv_req mqtt nvs_flash console nvs_flash esp_wifi driver)
|
||||||
|
set(requires esp_event)
|
||||||
|
|
||||||
|
# esp_timer component was introduced in v4.2
|
||||||
|
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.1")
|
||||||
|
list(APPEND priv_req esp_timer)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#if(CONFIG_ESP_RMAKER_LIB_ESP_MQTT)
|
||||||
|
list(APPEND srcs "src/esp-mqtt/esp-mqtt-glue.c")
|
||||||
|
#endif()
|
||||||
|
if(CONFIG_ESP_RMAKER_MQTT_SEND_USERNAME)
|
||||||
|
list(APPEND srcs "src/create_APN3_PPI_string.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${srcs}
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
PRIV_INCLUDE_DIRS
|
||||||
|
REQUIRES ${requires}
|
||||||
|
PRIV_REQUIRES ${priv_req})
|
||||||
181
components/rmaker_common/Kconfig
Normal file
181
components/rmaker_common/Kconfig
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
menu "ESP RainMaker Common"
|
||||||
|
|
||||||
|
choice ESP_RMAKER_MQTT_GLUE_LIB
|
||||||
|
bool "MQTT Library"
|
||||||
|
default ESP_RMAKER_LIB_ESP_MQTT
|
||||||
|
help
|
||||||
|
MQTT Library to be used
|
||||||
|
|
||||||
|
config ESP_RMAKER_LIB_ESP_MQTT
|
||||||
|
bool "ESP-MQTT"
|
||||||
|
config ESP_RMAKER_LIB_AWS_IOT
|
||||||
|
bool "AWS-IOT"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_GLUE_LIB
|
||||||
|
int
|
||||||
|
default 1 if ESP_RMAKER_LIB_ESP_MQTT
|
||||||
|
default 2 if ESP_RMAKER_LIB_AWS_IOT
|
||||||
|
|
||||||
|
choice ESP_RMAKER_MQTT_PORT
|
||||||
|
bool "MQTT Port"
|
||||||
|
default ESP_RMAKER_MQTT_PORT_443
|
||||||
|
help
|
||||||
|
ESP RainMaker MQTT Broker can be connected to either on Port 8883 or port 443.
|
||||||
|
Port 443 is recommended as it is generally not blocked by any firewalls,
|
||||||
|
since it is standard HTTPS port.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_PORT_443
|
||||||
|
bool "443"
|
||||||
|
config ESP_RMAKER_MQTT_PORT_8883
|
||||||
|
bool "8883"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_PORT
|
||||||
|
int
|
||||||
|
default 1 if ESP_RMAKER_MQTT_PORT_443
|
||||||
|
default 2 if ESP_RMAKER_MQTT_PORT_8883
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_PERSISTENT_SESSION
|
||||||
|
bool "Use Persisent MQTT sessions"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Use persistent MQTT sessions. This improves reliability as QOS1 messages missed
|
||||||
|
out due to some network issue are received after the MQTT reconnects. The broker
|
||||||
|
caches messages for a period of upto 1 hour. However, a side-effect of this is that
|
||||||
|
messages can be received at unexpected time. Enable this option only if it suits
|
||||||
|
your use case. Please read MQTT specs to understand more about persistent sessions
|
||||||
|
and the cleanSession flag.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_SEND_USERNAME
|
||||||
|
bool "Send MQTT Username"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Send a Username during MQTT Connect. This is generally required only for tracking
|
||||||
|
the MQTT client types, platform, SDK, etc. in AWS.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_PRODUCT_NAME
|
||||||
|
string "Product Name"
|
||||||
|
depends on ESP_RMAKER_MQTT_SEND_USERNAME
|
||||||
|
default "RMDev"
|
||||||
|
help
|
||||||
|
Approved AWS product name. Please get in touch with your Espressif/AWS representative for more info.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_PRODUCT_VERSION
|
||||||
|
string "Product Version"
|
||||||
|
depends on ESP_RMAKER_MQTT_SEND_USERNAME
|
||||||
|
default "1x0"
|
||||||
|
help
|
||||||
|
Approved AWS product version. Please get in touch with your Espressif/AWS representative for more info.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_PRODUCT_SKU
|
||||||
|
string "Product SKU"
|
||||||
|
depends on ESP_RMAKER_MQTT_SEND_USERNAME
|
||||||
|
default "EX00"
|
||||||
|
help
|
||||||
|
Product SKU. Please get in touch with your Espressif/AWS representative for more info.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_USE_CERT_BUNDLE
|
||||||
|
bool "Use Certificate Bundle"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Use Certificate Bundle for server authentication. Enabling this is recommended to safeguard
|
||||||
|
against any changes in the server certificates in future. This has an impact on the binary
|
||||||
|
size as well as heap requirement.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MAX_MQTT_SUBSCRIPTIONS
|
||||||
|
int "Maximum number of MQTT Subscriptions"
|
||||||
|
default 10
|
||||||
|
help
|
||||||
|
This value controls the maximum number of topics that the device can subscribe to.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MQTT_KEEP_ALIVE_INTERVAL
|
||||||
|
int "MQTT Keep Alive Internal"
|
||||||
|
default 120
|
||||||
|
range 30 1200
|
||||||
|
help
|
||||||
|
MQTT Keep Alive Interval. Note that it can take upto 1.5x of keep alive interval for a device
|
||||||
|
to be reported by offline by the MQTT Broker. Change this only if required.
|
||||||
|
|
||||||
|
choice ESP_RMAKER_NETWORK_PROTOCOL_TYPE
|
||||||
|
prompt "ESP RainMaker Network Type"
|
||||||
|
default ESP_RMAKER_NETWORK_OVER_WIFI
|
||||||
|
help
|
||||||
|
Network protocol type over which the ESP RainMaker will run.
|
||||||
|
|
||||||
|
config ESP_RMAKER_NETWORK_OVER_WIFI
|
||||||
|
# ESP_WIFI_ENABLED was introduced in IDF v5.0
|
||||||
|
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || ESP_WIFI_ENABLED
|
||||||
|
bool "ESP RainMaker over Wi-Fi"
|
||||||
|
|
||||||
|
config ESP_RMAKER_NETWORK_OVER_THREAD
|
||||||
|
depends on OPENTHREAD_ENABLED
|
||||||
|
bool "ESP RainMaker over Thread"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config ESP_RMAKER_WORK_QUEUE_TASK_STACK
|
||||||
|
int "ESP RainMaker Work Queue Task stack"
|
||||||
|
default 4096
|
||||||
|
help
|
||||||
|
Stack size for the ESP RainMaker Work Queue Task.
|
||||||
|
|
||||||
|
config ESP_RMAKER_WORK_QUEUE_TASK_PRIORITY
|
||||||
|
int "ESP RainMaker Work Queue Task priority"
|
||||||
|
default 5
|
||||||
|
help
|
||||||
|
Priority for the ESP RainMaker Work Queue Task. Not recommended to be changed
|
||||||
|
unless you really need it.
|
||||||
|
|
||||||
|
config ESP_RMAKER_FACTORY_PARTITION_NAME
|
||||||
|
string "ESP RainMaker Factory Partition Name"
|
||||||
|
default "fctry"
|
||||||
|
help
|
||||||
|
Factory NVS Partition name which will have the MQTT connectivity credentials.
|
||||||
|
|
||||||
|
config ESP_RMAKER_FACTORY_NAMESPACE
|
||||||
|
string "ESP RainMaker Factory Namespace"
|
||||||
|
default "rmaker_creds"
|
||||||
|
help
|
||||||
|
Namespace in the Factory NVS Partition name which will have the MQTT
|
||||||
|
connectivity credentials.
|
||||||
|
|
||||||
|
config ESP_RMAKER_ENCRYPT_FACTORY_PARTITION
|
||||||
|
bool "Encrypt Rainmaker Factory partition"
|
||||||
|
default false
|
||||||
|
depends on NVS_ENCRYPTION
|
||||||
|
help
|
||||||
|
Enable this option if the factory partition is pre-encrypted before flashing and the encryption keys
|
||||||
|
are flashed in partition with subtype nvs_keys specified by CONFIG_ESP_RMAKER_FACTORY_NVS_KEYS_PARTITION_NAME.
|
||||||
|
If an unencrypted factory partition is flashed, the device would not be able to read its data and
|
||||||
|
the partition would be considered empty.
|
||||||
|
If nvs encryption keys are not flashed onto device, they would be auto-generated and any previous data
|
||||||
|
in nvs/factory partition would become invalid.
|
||||||
|
|
||||||
|
config ESP_RMAKER_FACTORY_NVS_KEYS_PARTITION_NAME
|
||||||
|
string "ESP Rainmaker Factory NVS keys partition label"
|
||||||
|
default "nvs_key"
|
||||||
|
depends on ESP_RMAKER_ENCRYPT_FACTORY_PARTITION
|
||||||
|
help
|
||||||
|
Label of the partition of subtype "nvs_keys" used for encrypting/decrypting Rainmaker factory partition.
|
||||||
|
|
||||||
|
config ESP_RMAKER_DEF_TIMEZONE
|
||||||
|
string "Default Timezone"
|
||||||
|
default "Asia/Shanghai"
|
||||||
|
help
|
||||||
|
Default Timezone to use. Eg. "Asia/Shanghai", "America/Los_Angeles".
|
||||||
|
Check documentation for complete list of valid values. This value
|
||||||
|
will be used only if no timezone is set using the C APIs.
|
||||||
|
|
||||||
|
config ESP_RMAKER_SNTP_SERVER_NAME
|
||||||
|
string "ESP RainMaker SNTP Server Name"
|
||||||
|
default "pool.ntp.org"
|
||||||
|
help
|
||||||
|
Default SNTP Server which is used for time synchronization.
|
||||||
|
|
||||||
|
config ESP_RMAKER_MAX_COMMANDS
|
||||||
|
int "Maximum commands supported for command-response"
|
||||||
|
default 10
|
||||||
|
help
|
||||||
|
Maximum number of commands supported by the command-response framework
|
||||||
|
|
||||||
|
endmenu
|
||||||
201
components/rmaker_common/LICENSE
Normal file
201
components/rmaker_common/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
9
components/rmaker_common/README.md
Normal file
9
components/rmaker_common/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# ESP RainMaker Common modules
|
||||||
|
|
||||||
|
This component consists of some common modules used by ESP RainMaker and ESP RainMaker Diagnostics repos.
|
||||||
|
Currently, it consists of
|
||||||
|
- MQTT glue layer
|
||||||
|
- Timing APIs (SNTP helpers, timezone, etc.)
|
||||||
|
- Factory NVS helpers
|
||||||
|
- Work Queue
|
||||||
|
|
||||||
5
components/rmaker_common/component.mk
Normal file
5
components/rmaker_common/component.mk
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
COMPONENT_ADD_INCLUDEDIRS := include
|
||||||
|
COMPONENT_SRCDIRS := src
|
||||||
|
ifdef CONFIG_ESP_RMAKER_LIB_ESP_MQTT
|
||||||
|
COMPONENT_SRCDIRS += src/esp-mqtt
|
||||||
|
endif
|
||||||
3
components/rmaker_common/idf_component.yml
Normal file
3
components/rmaker_common/idf_component.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version: "1.4.6"
|
||||||
|
description: ESP RainMaker firmware agent Common component
|
||||||
|
url: https://github.com/espressif/esp-rainmaker-common
|
||||||
184
components/rmaker_common/include/esp_rmaker_cmd_resp.h
Normal file
184
components/rmaker_common/include/esp_rmaker_cmd_resp.h
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Super Admin User Flag*/
|
||||||
|
#define ESP_RMAKER_USER_ROLE_SUPER_ADMIN (1 << 0)
|
||||||
|
|
||||||
|
/** Primary User Flag */
|
||||||
|
#define ESP_RMAKER_USER_ROLE_PRIMARY_USER (1 << 1)
|
||||||
|
|
||||||
|
/** Secondary User Flag */
|
||||||
|
#define ESP_RMAKER_USER_ROLE_SECONDARY_USER (1 << 2)
|
||||||
|
|
||||||
|
|
||||||
|
/** RainMaker Command Response TLV8 Types */
|
||||||
|
typedef enum {
|
||||||
|
/** Request Id : Variable length string, max 32 characters*/
|
||||||
|
ESP_RMAKER_TLV_TYPE_REQ_ID = 1,
|
||||||
|
/** User Role : 1 byte */
|
||||||
|
ESP_RMAKER_TLV_TYPE_USER_ROLE,
|
||||||
|
/** Status : 1 byte */
|
||||||
|
ESP_RMAKER_TLV_TYPE_STATUS,
|
||||||
|
/** Timestamp : TBD */
|
||||||
|
ESP_RMAKER_TLV_TYPE_TIMESTAMP,
|
||||||
|
/** Command : 2 bytes*/
|
||||||
|
ESP_RMAKER_TLV_TYPE_CMD,
|
||||||
|
/** Data : Variable length */
|
||||||
|
ESP_RMAKER_TLV_TYPE_DATA
|
||||||
|
} esp_rmaker_tlv_type_t;
|
||||||
|
|
||||||
|
/* RainMaker Command Response Status */
|
||||||
|
typedef enum {
|
||||||
|
/** Success */
|
||||||
|
ESP_RMAKER_CMD_STATUS_SUCCESS = 0,
|
||||||
|
/** Generic Failure */
|
||||||
|
ESP_RMAKER_CMD_STATUS_FAILED,
|
||||||
|
/** Invalid Command */
|
||||||
|
ESP_RMAKER_CMD_STATUS_CMD_INVALID,
|
||||||
|
/** Authentication Failed */
|
||||||
|
ESP_RMAKER_CMD_STATUS_AUTH_FAIL,
|
||||||
|
/** Command not found */
|
||||||
|
ESP_RMAKER_CMD_STATUS_NOT_FOUND,
|
||||||
|
/** Last status value */
|
||||||
|
ESP_RMAKER_CMD_STATUS_MAX,
|
||||||
|
} esp_rmaker_cmd_status_t;
|
||||||
|
|
||||||
|
#define REQ_ID_LEN 32
|
||||||
|
typedef struct {
|
||||||
|
/** Command id */
|
||||||
|
uint16_t cmd;
|
||||||
|
/** Request id */
|
||||||
|
char req_id[REQ_ID_LEN];
|
||||||
|
/** User Role */
|
||||||
|
uint8_t user_role;
|
||||||
|
} esp_rmaker_cmd_ctx_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/** Standard command: Set Parameters */
|
||||||
|
ESP_RMAKER_CMD_TYPE_SET_PARAMS = 1,
|
||||||
|
/** Last Standard command */
|
||||||
|
ESP_RMAKER_CMD_STANDARD_LAST = 0xfff,
|
||||||
|
/** Custom commands can start from here */
|
||||||
|
ESP_RMAKER_CMD_CUSTOM_START = 0x1000
|
||||||
|
} esp_rmaker_cmd_t;
|
||||||
|
|
||||||
|
/** Command Response Handler
|
||||||
|
*
|
||||||
|
* If any command data is received from any of the supported transports (which are outside the scope of this core framework),
|
||||||
|
* this function should be called to handle it and fill in the response.
|
||||||
|
*
|
||||||
|
* @param[in] input Pointer to input data.
|
||||||
|
* @param[in] input_len data len.
|
||||||
|
* @param[in] output Pointer to output data which should be set by the handler.
|
||||||
|
* @param[out] output_len Length of output generated.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error on failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_cmd_response_handler(const void *input, size_t input_len, void **output, size_t *output_len);
|
||||||
|
|
||||||
|
/** Prototype for Command Handler
|
||||||
|
*
|
||||||
|
* The handler to be invoked when a given command is received.
|
||||||
|
*
|
||||||
|
* @param[in] in_data Pointer to input data.
|
||||||
|
* @param[in] in_len data len.
|
||||||
|
* @param[in] out_data Pointer to output data which should be set by the handler.
|
||||||
|
* @param[out] out_len Length of output generated.
|
||||||
|
* @param[in] ctx Command Context.
|
||||||
|
* @param[in] priv Private data, if specified while registering command.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error on failure.
|
||||||
|
*/
|
||||||
|
typedef esp_err_t (*esp_rmaker_cmd_handler_t)(const void *in_data, size_t in_len, void **out_data, size_t *out_len, esp_rmaker_cmd_ctx_t *ctx, void *priv);
|
||||||
|
|
||||||
|
/** Register a new command
|
||||||
|
*
|
||||||
|
* @param[in] cmd Command Identifier. Custom commands should start beyond ESP_RMAKER_CMD_STANDARD_LAST
|
||||||
|
* @param[in] access User Access for the command. Can be an OR of the various user role flags like ESP_RMAKER_USER_ROLE_SUPER_ADMIN,
|
||||||
|
* ESP_RMAKER_USER_ROLE_PRIMARY_USER and ESP_RMAKER_USER_ROLE_SECONDARY_USER
|
||||||
|
* @param[in] handler The handler to be invoked when the given command is received.
|
||||||
|
* @param[in] free_on_return Flag to indicate of the framework should free the output after it has been sent as response.
|
||||||
|
* @param[in] priv Optional private data to be passed to the handler.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error on failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_cmd_register(uint16_t cmd, uint8_t access, esp_rmaker_cmd_handler_t handler, bool free_on_return, void *priv);
|
||||||
|
|
||||||
|
/** De-register a command
|
||||||
|
*
|
||||||
|
* @param[in] cmd Command Identifier. Custom commands should start beyond ESP_RMAKER_CMD_STANDARD_LAST
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error on failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_cmd_deregister(uint16_t cmd);
|
||||||
|
|
||||||
|
/* Prepare an empty command response
|
||||||
|
*
|
||||||
|
* This can be used to populate the request to be sent to get all pending commands
|
||||||
|
*
|
||||||
|
* @param[in] out_data Pointer to output data. This function will allocate memory and set this pointer
|
||||||
|
* accordingly.
|
||||||
|
* @param[out] out_len Length of output generated.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error on failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_cmd_prepare_empty_response(void **output, size_t *output_len);
|
||||||
|
|
||||||
|
/** Prototype for Command sending function (TESTING only)
|
||||||
|
*
|
||||||
|
* @param[in] data Pointer to the data to be sent.
|
||||||
|
* @param[in] data_len Size of data to be sent.
|
||||||
|
* @param[in] priv Private data, if applicable.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error on failure.
|
||||||
|
*/
|
||||||
|
typedef esp_err_t (*esp_rmaker_cmd_send_t)(const void *data, size_t data_len, void *priv);
|
||||||
|
|
||||||
|
/** Send Test command (TESTING only)
|
||||||
|
*
|
||||||
|
* @param[in] req_id NULL terminated request id of max 32 characters.
|
||||||
|
* @param[in] role User Role flag.
|
||||||
|
* @param[in] cmd Command Identifier.
|
||||||
|
* @param[in] data Pointer to data for the command.
|
||||||
|
* @param[in] data_size Size of the data.
|
||||||
|
* @param[in] cmd_send Transport specific function to send the command data.
|
||||||
|
* @param[in] priv Private data (if any) to be sent to cmd_send.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error on failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_cmd_resp_test_send(const char *req_id, uint8_t role, uint16_t cmd, const void *data, size_t data_size, esp_rmaker_cmd_send_t cmd_send, void *priv);
|
||||||
|
|
||||||
|
/** Parse response (TESTING only)
|
||||||
|
*
|
||||||
|
* @param[in] response Pointer to the response received
|
||||||
|
* @param[in] response_len Length of the response
|
||||||
|
* @param[in] priv Private data, if any. Can be NULL.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error on failure.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_cmd_resp_parse_response(const void *response, size_t response_len, void *priv);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
47
components/rmaker_common/include/esp_rmaker_common_console.h
Normal file
47
components/rmaker_common/include/esp_rmaker_common_console.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <esp_err.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize console
|
||||||
|
*
|
||||||
|
* Initializes serial console and adds basic commands.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success.
|
||||||
|
* @return error in case of failures.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_rmaker_common_console_init(void);
|
||||||
|
|
||||||
|
/* Reference for adding custom console commands:
|
||||||
|
#include <esp_console.h>
|
||||||
|
|
||||||
|
static int command_console_handler(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
// Command code here
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_console_command()
|
||||||
|
{
|
||||||
|
const esp_console_cmd_t cmd = {
|
||||||
|
.command = "<command_name>",
|
||||||
|
.help = "<help_details>",
|
||||||
|
.func = &command_console_handler,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user