diff --git a/.clang-format b/.clang-format index ab7ebe8e..123d6a84 100644 --- a/.clang-format +++ b/.clang-format @@ -9,7 +9,7 @@ AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: true AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All +AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: All AllowShortLoopsOnASingleLine: false @@ -37,7 +37,7 @@ BraceWrapping: SplitEmptyNamespace: false BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeColon -ColumnLimit: 120 +ColumnLimit: 200 CompactNamespaces: true Cpp11BracedListStyle: false FixNamespaceComments: true diff --git a/.github/labeler.yml b/.github/labeler.yml deleted file mode 100644 index af34653a..00000000 --- a/.github/labeler.yml +++ /dev/null @@ -1,642 +0,0 @@ ---- - -# see https://github.com/fuxingloh/multi-labeler -version: v1 -labels: - - # these labels are used to draft releases. - - label: "enhancement" - matcher: - branch: "^(feat|feature)-.*" - - label: "bugfix" - matcher: - branch: "^(bugfix|fix)-.*" - - label: "doc" - matcher: - branch: "^(doc|documentation)-.*" - - label: "ci" - matcher: - branch: "^ci-.*" - - label: "chore" - matcher: - branch: "^chore-.*" - - # area indicates which files are modified in the PR. - - label: "area:docs" - sync: true - matcher: - files: - any: - - "docs/**" - - "*.md" - - label: "area:docs-only" - sync: true - matcher: - files: - any: - - "docs/**" - - "*.md" - all: - - "!components" - - "!.github/**" - - label: "area:ci" - sync: true - matcher: - files: - - ".github/**" - - label: "area:components" - sync: true - matcher: - files: "components/**" - - label: "area:components:ads111x" - sync: true - matcher: - files: - any: - - "components/ads111x/**" - - "components/ads111x/.eil.yml" - - "examples/ads111x/**" - - label: "area:components:aht" - sync: true - matcher: - files: - any: - - "components/aht/**" - - "components/aht/.eil.yml" - - "examples/aht/**" - - label: "area:components:bh1750" - sync: true - matcher: - files: - any: - - "components/bh1750/**" - - "components/bh1750/.eil.yml" - - "examples/bh1750/**" - - label: "area:components:bh1900nux" - sync: true - matcher: - files: - any: - - "components/bh1900nux/**" - - "components/bh1900nux/.eil.yml" - - "examples/bh1900nux/**" - - label: "area:components:bme680" - sync: true - matcher: - files: - any: - - "components/bme680/**" - - "components/bme680/.eil.yml" - - "examples/bme680/**" - - label: "area:components:bmp180" - sync: true - matcher: - files: - any: - - "components/bmp180/**" - - "components/bmp180/.eil.yml" - - "examples/bmp180/**" - - label: "area:components:bmp280" - sync: true - matcher: - files: - any: - - "components/bmp280/**" - - "components/bmp280/.eil.yml" - - "examples/bmp280/**" - - label: "area:components:button" - sync: true - matcher: - files: - any: - - "components/button/**" - - "components/button/.eil.yml" - - "examples/button/**" - - label: "area:components:ccs811" - sync: true - matcher: - files: - any: - - "components/ccs811/**" - - "components/ccs811/.eil.yml" - - "examples/ccs811/**" - - label: "area:components:color" - sync: true - matcher: - files: - any: - - "components/color/**" - - "components/color/.eil.yml" - - "examples/color/**" - - label: "area:components:dht" - sync: true - matcher: - files: - any: - - "components/dht/**" - - "components/dht/.eil.yml" - - "examples/dht/**" - - label: "area:components:ds1302" - sync: true - matcher: - files: - any: - - "components/ds1302/**" - - "components/ds1302/.eil.yml" - - "examples/ds1302/**" - - label: "area:components:ds1307" - sync: true - matcher: - files: - any: - - "components/ds1307/**" - - "components/ds1307/.eil.yml" - - "examples/ds1307/**" - - label: "area:components:ds18x20" - sync: true - matcher: - files: - any: - - "components/ds18x20/**" - - "components/ds18x20/.eil.yml" - - "examples/ds18x20/**" - - label: "area:components:ds3231" - sync: true - matcher: - files: - any: - - "components/ds3231/**" - - "components/ds3231/.eil.yml" - - "examples/ds3231/**" - - label: "area:components:ds3502" - sync: true - matcher: - files: - any: - - "components/ds3502/**" - - "components/ds3502/.eil.yml" - - "examples/ds3502/**" - - label: "area:components:encoder" - sync: true - matcher: - files: - any: - - "components/encoder/**" - - "components/encoder/.eil.yml" - - "examples/encoder/**" - - label: "area:components:esp_idf_lib_helpers" - sync: true - matcher: - files: - any: - - "components/esp_idf_lib_helpers/**" - - "components/esp_idf_lib_helpers/.eil.yml" - - "examples/esp_idf_lib_helpers/**" - - label: "area:components:example" - sync: true - matcher: - files: - any: - - "components/example/**" - - "components/example/.eil.yml" - - "examples/example/**" - - label: "area:components:framebuffer" - sync: true - matcher: - files: - any: - - "components/framebuffer/**" - - "components/framebuffer/.eil.yml" - - "examples/framebuffer/**" - - label: "area:components:hd44780" - sync: true - matcher: - files: - any: - - "components/hd44780/**" - - "components/hd44780/.eil.yml" - - "examples/hd44780/**" - - label: "area:components:hdc1000" - sync: true - matcher: - files: - any: - - "components/hdc1000/**" - - "components/hdc1000/.eil.yml" - - "examples/hdc1000/**" - - label: "area:components:hmc5883l" - sync: true - matcher: - files: - any: - - "components/hmc5883l/**" - - "components/hmc5883l/.eil.yml" - - "examples/hmc5883l/**" - - label: "area:components:ht16k33" - sync: true - matcher: - files: - any: - - "components/ht16k33/**" - - "components/ht16k33/.eil.yml" - - "examples/ht16k33/**" - - label: "area:components:hts221" - sync: true - matcher: - files: - any: - - "components/hts221/**" - - "components/hts221/.eil.yml" - - "examples/hts221/**" - - label: "area:components:hx711" - sync: true - matcher: - files: - any: - - "components/hx711/**" - - "components/hx711/.eil.yml" - - "examples/hx711/**" - - label: "area:components:i2cdev" - sync: true - matcher: - files: - any: - - "components/i2cdev/**" - - "components/i2cdev/.eil.yml" - - "examples/i2cdev/**" - - label: "area:components:ina219" - sync: true - matcher: - files: - any: - - "components/ina219/**" - - "components/ina219/.eil.yml" - - "examples/ina219/**" - - label: "area:components:ina260" - sync: true - matcher: - files: - any: - - "components/ina260/**" - - "components/ina260/.eil.yml" - - "examples/ina260/**" - - label: "area:components:ina3221" - sync: true - matcher: - files: - any: - - "components/ina3221/**" - - "components/ina3221/.eil.yml" - - "examples/ina3221/**" - - label: "area:components:lc709203f" - sync: true - matcher: - files: - any: - - "components/lc709203f/**" - - "components/lc709203f/.eil.yml" - - "examples/lc709203f/**" - - label: "area:components:led_strip" - sync: true - matcher: - files: - any: - - "components/led_strip/**" - - "components/led_strip/.eil.yml" - - "examples/led_strip/**" - - label: "area:components:led_strip_spi" - sync: true - matcher: - files: - any: - - "components/led_strip_spi/**" - - "components/led_strip_spi/.eil.yml" - - "examples/led_strip_spi/**" - - label: "area:components:lib8tion" - sync: true - matcher: - files: - any: - - "components/lib8tion/**" - - "components/lib8tion/.eil.yml" - - "examples/lib8tion/**" - - label: "area:components:lm75" - sync: true - matcher: - files: - any: - - "components/lm75/**" - - "components/lm75/.eil.yml" - - "examples/lm75/**" - - label: "area:components:ls7366r" - sync: true - matcher: - files: - any: - - "components/ls7366r/**" - - "components/ls7366r/.eil.yml" - - "examples/ls7366r/**" - - label: "area:components:max31725" - sync: true - matcher: - files: - any: - - "components/max31725/**" - - "components/max31725/.eil.yml" - - "examples/max31725/**" - - label: "area:components:max31855" - sync: true - matcher: - files: - any: - - "components/max31855/**" - - "components/max31855/.eil.yml" - - "examples/max31855/**" - - label: "area:components:max31865" - sync: true - matcher: - files: - any: - - "components/max31865/**" - - "components/max31865/.eil.yml" - - "examples/max31865/**" - - label: "area:components:max7219" - sync: true - matcher: - files: - any: - - "components/max7219/**" - - "components/max7219/.eil.yml" - - "examples/max7219/**" - - label: "area:components:mcp23008" - sync: true - matcher: - files: - any: - - "components/mcp23008/**" - - "components/mcp23008/.eil.yml" - - "examples/mcp23008/**" - - label: "area:components:mcp23x17" - sync: true - matcher: - files: - any: - - "components/mcp23x17/**" - - "components/mcp23x17/.eil.yml" - - "examples/mcp23x17/**" - - label: "area:components:mcp342x" - sync: true - matcher: - files: - any: - - "components/mcp342x/**" - - "components/mcp342x/.eil.yml" - - "examples/mcp342x/**" - - label: "area:components:mcp4725" - sync: true - matcher: - files: - any: - - "components/mcp4725/**" - - "components/mcp4725/.eil.yml" - - "examples/mcp4725/**" - - label: "area:components:mcp960x" - sync: true - matcher: - files: - any: - - "components/mcp960x/**" - - "components/mcp960x/.eil.yml" - - "examples/mcp960x/**" - - label: "area:components:mcp9808" - sync: true - matcher: - files: - any: - - "components/mcp9808/**" - - "components/mcp9808/.eil.yml" - - "examples/mcp9808/**" - - label: "area:components:mhz19b" - sync: true - matcher: - files: - any: - - "components/mhz19b/**" - - "components/mhz19b/.eil.yml" - - "examples/mhz19b/**" - - label: "area:components:ms5611" - sync: true - matcher: - files: - any: - - "components/ms5611/**" - - "components/ms5611/.eil.yml" - - "examples/ms5611/**" - - label: "area:components:noise" - sync: true - matcher: - files: - any: - - "components/noise/**" - - "components/noise/.eil.yml" - - "examples/noise/**" - - label: "area:components:onewire" - sync: true - matcher: - files: - any: - - "components/onewire/**" - - "components/onewire/.eil.yml" - - "examples/onewire/**" - - label: "area:components:pca9557" - sync: true - matcher: - files: - any: - - "components/pca9557/**" - - "components/pca9557/.eil.yml" - - "examples/pca9557/**" - - label: "area:components:pca9685" - sync: true - matcher: - files: - any: - - "components/pca9685/**" - - "components/pca9685/.eil.yml" - - "examples/pca9685/**" - - label: "area:components:pcf8563" - sync: true - matcher: - files: - any: - - "components/pcf8563/**" - - "components/pcf8563/.eil.yml" - - "examples/pcf8563/**" - - label: "area:components:pcf8574" - sync: true - matcher: - files: - any: - - "components/pcf8574/**" - - "components/pcf8574/.eil.yml" - - "examples/pcf8574/**" - - label: "area:components:pcf8575" - sync: true - matcher: - files: - any: - - "components/pcf8575/**" - - "components/pcf8575/.eil.yml" - - "examples/pcf8575/**" - - label: "area:components:pcf8591" - sync: true - matcher: - files: - any: - - "components/pcf8591/**" - - "components/pcf8591/.eil.yml" - - "examples/pcf8591/**" - - label: "area:components:qmc5883l" - sync: true - matcher: - files: - any: - - "components/qmc5883l/**" - - "components/qmc5883l/.eil.yml" - - "examples/qmc5883l/**" - - label: "area:components:rda5807m" - sync: true - matcher: - files: - any: - - "components/rda5807m/**" - - "components/rda5807m/.eil.yml" - - "examples/rda5807m/**" - - label: "area:components:scd30" - sync: true - matcher: - files: - any: - - "components/scd30/**" - - "components/scd30/.eil.yml" - - "examples/scd30/**" - - label: "area:components:scd4x" - sync: true - matcher: - files: - any: - - "components/scd4x/**" - - "components/scd4x/.eil.yml" - - "examples/scd4x/**" - - label: "area:components:sgp40" - sync: true - matcher: - files: - any: - - "components/sgp40/**" - - "components/sgp40/.eil.yml" - - "examples/sgp40/**" - - label: "area:components:sht3x" - sync: true - matcher: - files: - any: - - "components/sht3x/**" - - "components/sht3x/.eil.yml" - - "examples/sht3x/**" - - label: "area:components:sht4x" - sync: true - matcher: - files: - any: - - "components/sht4x/**" - - "components/sht4x/.eil.yml" - - "examples/sht4x/**" - - label: "area:components:si7021" - sync: true - matcher: - files: - any: - - "components/si7021/**" - - "components/si7021/.eil.yml" - - "examples/si7021/**" - - label: "area:components:sts21" - sync: true - matcher: - files: - any: - - "components/sts21/**" - - "components/sts21/.eil.yml" - - "examples/sts21/**" - - label: "area:components:tca9548" - sync: true - matcher: - files: - any: - - "components/tca9548/**" - - "components/tca9548/.eil.yml" - - "examples/tca9548/**" - - label: "area:components:tca95x5" - sync: true - matcher: - files: - any: - - "components/tca95x5/**" - - "components/tca95x5/.eil.yml" - - "examples/tca95x5/**" - - label: "area:components:tda74xx" - sync: true - matcher: - files: - any: - - "components/tda74xx/**" - - "components/tda74xx/.eil.yml" - - "examples/tda74xx/**" - - label: "area:components:tsl2561" - sync: true - matcher: - files: - any: - - "components/tsl2561/**" - - "components/tsl2561/.eil.yml" - - "examples/tsl2561/**" - - label: "area:components:tsl2591" - sync: true - matcher: - files: - any: - - "components/tsl2591/**" - - "components/tsl2591/.eil.yml" - - "examples/tsl2591/**" - - label: "area:components:tsl4531" - sync: true - matcher: - files: - any: - - "components/tsl4531/**" - - "components/tsl4531/.eil.yml" - - "examples/tsl4531/**" - - label: "area:components:tsys01" - sync: true - matcher: - files: - any: - - "components/tsys01/**" - - "components/tsys01/.eil.yml" - - "examples/tsys01/**" - - label: "area:components:ultrasonic" - sync: true - matcher: - files: - any: - - "components/ultrasonic/**" - - "components/ultrasonic/.eil.yml" - - "examples/ultrasonic/**" - - label: "area:components:wiegand" - sync: true - matcher: - files: - any: - - "components/wiegand/**" - - "components/wiegand/.eil.yml" - - "examples/wiegand/**" diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 3c7f2be4..00000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- - -# configuration for release-drafter. -# https://github.com/release-drafter/release-drafter -name-template: "$NEXT_PATCH_VERSION" -tag-template: "$NEXT_PATCH_VERSION" - -# map labels to categories. -# to label PRs, see .github/workflows/pr-labeler.yml -categories: - - title: Features - label: feature - - title: Bug Fixes - labels: - - bugfix - - fix - - title: Documentation - label: documentation - - title: CI - label: ci -change-template: "* $TITLE (#$NUMBER)" -template: | - ## Release $NEXT_PATCH_VERSION - - $CHANGES diff --git a/.github/workflows/README.md b/.github/workflows/README.md deleted file mode 100644 index 78ca4a31..00000000 --- a/.github/workflows/README.md +++ /dev/null @@ -1,238 +0,0 @@ -# Workflows - -This document describes how GitHub Actions workflows work in the repository. - - - -* [Overview](#overview) -* [Labeler workflow](#labeler-workflow) - * [Labels used by the CI](#labels-used-by-the-ci) - * [The triggering labeler workflow and Personal Access Token](#the-triggering-labeler-workflow-and-personal-access-token) - * [Creating a Personal Access Token](#creating-a-personal-access-token) - * [Using a Personal Access Token](#using-a-personal-access-token) - * [Security considerations](#security-considerations) -* [Action workflow](#action-workflow) - * [Security considerations](#security-considerations-1) - - - -## Overview - -The workflows consist of _action_ and _labeler_ workflows. - -An action workflow is a typical workflow that builds sources, runs lint -programs, or performs other CI tests. Other actions include "Add a comment to -PR", or "Mention code owners when their code is modified by the PR", or -"Create a draft release notes". - -Action workflows are triggered by labels added or removed to PRs. This is -achieved by a job in the action workflows that tests if the PR has a specific -label. - -The responsibility of an action workflow is to take actions. An action -workflow should not decide when to perform actions, which is a responsibility -of labeler workflows. - -A labeler action manages which action workflow to invoke. `labeler*.yml` add -or remove labels to/from the PR. These workflows control action workflows -that perform actions. For instance, [labeler.yml](labeler.yml) and its -configuration file, [labeler.yml](../labeler.yml), add labels to -the PR by modified files. The labels are used by action workflows, and if the -label is the one defined in the action workflow, the action workflow runs. - -The responsibility of a labeler action is to add or remove labels, or when to -invoke action workflow. A labeler action should not take actions, such as -build sources. - -## Labeler workflow - -labeler workflow is the logic for action workflows. By removing logics (when -a workflow should run) from the workflow, it is easier to see why a workflow -runs or not. - -In a labeler workflow, add or remove labels by using -[actions/github-script](https://github.com/actions/github-script), which -enable to use JavaScript code and GitHub REST APIs. It is possible to use -shell instead, but not very practical. - -### Labels used by the CI - -| Label | Description | -|-------------------|--------------------------------------| -| `area:components` | Code under `components` is modified. | -| `area:components:${NAME}` | Code of a specific component is modified. `${NAME}` is the name of the component. | -| `area:ci` | The CI is modified. | - -### The triggering labeler workflow and Personal Access Token - -Events triggered by a workflow do not run another workflow. -[Triggering a workflow from a workflow](https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow) -says: - -> When you use the repository's GITHUB_TOKEN to perform tasks, events -> triggered by the GITHUB_TOKEN will not create a new workflow run. This -> prevents you from accidentally creating recursive workflow runs. - -That means `pull_request` event with type `labeled` will not be fired when a -workflow labels a PR (but it will be if you manually label a PR). - -To trigger other workflows from a labeler, the labeler workflow needs a -Personal Access Token, or a PAT, instead of `GITHUB_TOKEN`. - -> If you do want to trigger a workflow from within a workflow run, you can use -> a personal access token instead of GITHUB_TOKEN to trigger events that -> require a token. - -### Creating a Personal Access Token - -To create a PAT, visit [Settings](https://github.com/settings/profile) in your -profile, and `Developer settings` > `Personal access token`. Click `Generate -new token`. - -Choose `Expiration`. If you choose an expiration date for the token, the token -will expire after GitHub send a notification that reminds you of the -expiration. Obviously, you need to create a new token after the expiration -date. If you choose `No expiration`, the token will not expire, but GitHub -does not recommend it. - -Choose `public_repo` scope for the access token. - -Click `Generate token`. - -See -[Creating a personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) -for details. - -### Using a Personal Access Token - -To use the generated PAT, it must be passed to workflows by a repository -secret. That way, the token is available `${{ secrets.* }}` in workflows -without hard-coding it in public code. - -After creating a PAT, create a repository secret. The name should be -`LABELER_TOKEN` (this is the key of repository secrets used in labeler -workflows), and the value should be the token. See -[Managing encrypted secrets for your repository and organization for Codespaces](https://docs.github.com/en/codespaces/managing-codespaces-for-your-organization/managing-encrypted-secrets-for-your-repository-and-organization-for-codespaces) -for details. - -```yaml -jobs: - labeler: - name: Labeler - runs-on: ubuntu-latest - steps: - - uses: fuxingloh/multi-labeler@v1 - with: - github-token: ${{secrets.LABELER_TOKEN}} - config-path: .github/labeler.yml -``` - -Note that if the labeler merely labels PRs just for human-being, not to run -another workflow, the labeler does not need a PAT. Use `GITHUB_TOKEN` in that -case. - -```yaml -jobs: - labeler: - name: Labeler - runs-on: ubuntu-latest - steps: - - uses: fuxingloh/multi-labeler@v1 - with: - github-token: ${{secrets.GITHUB_TOKEN}} - config-path: .github/labeler.yml -``` - -### Security considerations - -Labeler workflow needs write access to the repository because they modifies -PRs by adding or removing labels. This means labeler workflows have to run in -the privileged context. In that context, the workflow has full access to the -repository and the security token. - -On the other hand, PRs are opened by repository members and (possibly -untrusted) third-party members. This means a labeler workflow is run by a -third-party with privileged access to the repository. For this reason, a -labeler workflow should add or remove label only. - -A labeler workflow must use `pull_request_target` event as a trigger. - -```yaml ---- -name: Label PR -on: - pull_request_target: -``` -See -[pull_request_target](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target) -in the official documentation for details. - -## Action workflow - -Action workflow is, typically, a workflow that performs tests, such as -linting, or building sources. Other actions include "Writing a comment in the -PR". - -In an action workflow, see if the PR has a specific label first, and if the PR -has the label, run the actions. Otherwise, skip. - -Note that the action should run not only when an label is added (`labeled`), -but also when the PR is updated (`synchronize`). Typically, you need the -following `on` condition in the workflow: - -```yaml ---- -name: Build examples -on: - pull_request: - branches: - - main - types: - - labeled - - synchronize -``` - -If an action workflow needs to add a label depending on the outcome of the -test, use [labeler-by-workflow-status.yml](labeler-by-workflow-status.yml) -because, in the `pull_request` context, it is not possible. - -See an example at: [FIXME] - -### Security considerations - -Most of action workflow do not need privileged access to the repository. -Typically, an action workflow should run in unprivileged context. That means -most of action workflows should use `pull_request` event as a trigger. - -```yaml -name: An example action workflow -on: - pull_request: - branches: - - master -``` - -In the unprivileged context, the action workflow does not have write access to -the repository. The action workflow may build untrusted code (i.e. the code -from forks). The action workflow cannot modify the repository (i.e. adding -comment or labels). - -Action workflow might need write access to the repository if the action to take -is an action that modifies the repository, such as adding a comment to PRs. -When this is the case, the action workflow should use `pull_request_target` -event. - -```yaml -name: An example action workflow with privileged access -on: - pull_request_target: -``` - -In this context, some extra privileges are granted. The `pull_request_target` -is designed for adding labels to PRs, or writing comments to PRs. The action -workflow must not build sources, or run untrusted code (i.e. the code from -forks). - -See -[pull_request_target](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target) -in the official documentation for details. diff --git a/.github/workflows/action-check-components-and-examples.yml b/.github/workflows/action-check-components-and-examples.yml deleted file mode 100644 index 99137335..00000000 --- a/.github/workflows/action-check-components-and-examples.yml +++ /dev/null @@ -1,83 +0,0 @@ ---- -# A workflow to test all the components have one or more of examples. -name: Check components and examples -on: - pull_request: - types: - - labeled - - synchronize - -jobs: - pre_build: - runs-on: ubuntu-latest - outputs: - should_skip: ${{ steps.skip_check.outputs.result }} - steps: - - id: skip_check - uses: actions/github-script@v6 - with: - result-encoding: string - script: | - console.log("context"); - console.log(JSON.stringify(context, null, 2)); - let should_skip = false; - - switch(context.payload.action) { - case "labeled": - if (context.payload.label.name != "area:components") { - should_skip = true; - } - if (context.payload.label.name == "area:ci") { - should_skip = false; - } - break; - case "synchronize": - let labels = context.payload.pull_request.labels.map(label => { return label.name }); - if (!labels.includes("area:components")) { - should_skip = true; - } - if (labels.includes("area:ci")) { - should_skip = false; - } - break; - } - return should_skip; - - check_components_and_examples: - runs-on: ubuntu-latest - needs: pre_build - if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Check components and examples - run: | - all_components=`ls -1 components` - exit_status=0 - - for component in ${all_components}; do - echo "Checking component \"${component}\"" - echo -n "it has examples/${component} directory: " - if [ -d "examples/${component}" ]; then - echo "yes" - else - echo "no" - echo "the example directory for ${component} does not exist. create examples/${component} directory, and create an example for ${component} under examples/${component}/default." - exit_status=1 - fi - echo -n "it has the default example directory: " - - # allow both directory and symlink - if [ -d "examples/${component}/default" ] || [ -L "examples/${component}/default" ]; then - echo "yes" - else - echo "no" - echo "the default example directory for ${component} does not exist. create examples/${component}/default directory, and create an example in that directory" - exit_status=1 - fi - done - if [ $exit_status -eq 1 ]; then - echo "Some tests failed. See the above log for details" - fi - exit ${exit_status} diff --git a/.github/workflows/action-check-components-label.yml b/.github/workflows/action-check-components-label.yml deleted file mode 100644 index 43caac2b..00000000 --- a/.github/workflows/action-check-components-label.yml +++ /dev/null @@ -1,110 +0,0 @@ ---- -# A workflow to test all the components have a label for components, such as -# `area:components:foo` -name: Check components label -on: - pull_request: - types: - - labeled - - synchronize - -jobs: - pre_build: - runs-on: ubuntu-latest - outputs: - should_skip: ${{ steps.skip_check.outputs.result }} - steps: - - id: skip_check - uses: actions/github-script@v6 - with: - result-encoding: string - script: | - console.log("context"); - console.log(JSON.stringify(context, null, 2)); - let should_skip = false; - - switch(context.payload.action) { - case "labeled": - if (context.payload.label.name != "area:components") { - should_skip = true; - } - if (context.payload.label.name == "area:ci") { - should_skip = false; - } - break; - case "synchronize": - let labels = context.payload.pull_request.labels.map(label => { return label.name }); - if (!labels.includes("area:components")) { - should_skip = true; - } - if (labels.includes("area:ci")) { - should_skip = false; - } - break; - } - return should_skip; - check_labels: - runs-on: ubuntu-latest - needs: pre_build - if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - outputs: - should_skip: ${{ steps.skip_check.outputs.result }} - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install yq to parse YAML files - run: | - YQ_VERSION=v4.25.3 - YQ_BINARY=yq_linux_amd64 - YQ_COMMAND="/usr/bin/yq" - wget "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/${YQ_BINARY}" -O ./yq - sudo mv ./yq "${YQ_COMMAND}" - sudo chmod +x "${YQ_COMMAND}" - "${YQ_COMMAND}" --version - - - name: Check all components have a label for components - run: | - COMPONENTS_DIR="components" - echo "COMPONENTS_DIR: $COMPONENTS_DIR" - YAML_FILE=".github/labeler.yml" - echo "YAML_FILE: ${YAML_FILE}" - ALL_COMPONENTS=`ls -1 "${COMPONENTS_DIR}"` - echo "ALL_COMPONENTS:" - echo "${ALL_COMPONENTS}" - COMPONENTS_LABELS=`yq '.labels[] | select (.label == "area:components:*") | .label' "${YAML_FILE}"` - echo "COMPONENTS_LABELS:" - echo "${COMPONENTS_LABELS}" - EXIT_STATUS=0 - MISSING_COMPONENTS="" - for COMPONENT in ${ALL_COMPONENTS}; do - echo "Check if \"${COMPONENT}\" has its own label, \"area:components:${COMPONENT}\", in \"${YAML_FILE}\"" - FOUND=0 - for COMPONENTS_LABEL in ${COMPONENTS_LABELS}; do - if [ "area:components:${COMPONENT}" = "${COMPONENTS_LABEL}" ]; then - FOUND=1 - fi - done - if [ ${FOUND} -eq 1 ]; then - echo "Found \"${COMPONENT}\" in COMPONENTS_LABELS" - else - echo "\"${COMPONENT}\" could not be found in COMPONENTS_LABELS" - EXIT_STATUS=1 - MISSING_COMPONENTS="${MISSING_COMPONENTS} ${COMPONENT}" - fi - done - if [ ${EXIT_STATUS} -eq 1 ]; then - echo "Some components could not be found in COMPONENTS_LABELS" - echo "MISSING_COMPONENTS: \"${MISSING_COMPONENTS}\"" - echo "Add the following to \"${YAML_FILE}\"" - for NAME in ${MISSING_COMPONENTS}; do - echo " - label: \"area:components:${NAME}\"" - echo " sync: true" - echo " matcher:" - echo " files:" - echo " any:" - echo " - \"components/${NAME}/**\"" - echo " - \"examples/${NAME}/**\"" - done - fi - exit "${EXIT_STATUS}" diff --git a/.github/workflows/actions-on-pr-by-label.yml b/.github/workflows/actions-on-pr-by-label.yml deleted file mode 100644 index 89acaf91..00000000 --- a/.github/workflows/actions-on-pr-by-label.yml +++ /dev/null @@ -1,55 +0,0 @@ ---- -name: Actions on PR by label -on: - pull_request_target: - types: - - labeled - -# by default, do not allow write access as pull_request_target has more write -# access than pull_request. assign necessary permissions as required in jobs. -permissions: read-all - -jobs: - on-labeled-ci-failure: - # when a workflow fails, the PR is labeled with - # `ci/${WORKFLOW_NAME}/failure`. - if: ${{ startsWith(github.event.label.name, 'ci/') && endsWith(github.event.label.name, '/failure') }} - runs-on: ubuntu-latest - # this job needs write access to PRs because it modifies the PR by - # creating a comment. - permissions: - pull-requests: write - steps: - - name: Dump context for debugging - uses: actions/github-script@v6 - with: - script: | - console.log(JSON.stringify(context, null, 2)) - - - name: ci/Metadata/failure - if: ${{ github.event.label.name == 'ci/Metadata/failure' }} - uses: actions/github-script@v6 - with: - script: | - const util = require("util"); - try { - await github.rest.issues.createComment({ - issue_number: context.payload.pull_request.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: util.format( - "@%s CI `Metadata` failed. Did you update README.md? Please run:\n" + - "\n" + - "```console\n" + - "bundle exec rake -C devtools readme > README.md\n" + - "```\n" + - "\n" + - "at the repository root directory and commit the changes.\n" + - "See also [Updating README](https://github.com/UncleRus/esp-idf-lib/blob/master/CONTRIBUTING.md#updating-readmemd)" + - "section in CONTRIBUTING.md. Thank you.", - context.payload.pull_request.user.login) - }); - } catch(e) { - core.error(e); - core.setFailed(e.message); - } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 9d3585cc..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,1305 +0,0 @@ ---- -name: Build examples -on: - pull_request: - types: - - labeled - - synchronize - -jobs: - pre_build: - - # A job to see if the entrire jobs should be skipped. each job for a - # target should have: - # - # needs: pre_build - # if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - # - # Another output, component_labels, includes all the component labels of - # the PR, e.g. "area:components:foo,area:components:bar". - runs-on: ubuntu-latest - outputs: - should_skip: ${{ steps.skip_check.outputs.result }} - component_labels: ${{ steps.get_component_labels.outputs.result }} - components_to_build: ${{ steps.select_components_to_build.outputs.components_to_build }} - steps: - - id: skip_check - uses: actions/github-script@v6 - with: - result-encoding: string - script: | - console.log("context"); - console.log(JSON.stringify(context, null, 2)); - let should_skip = false; - - switch(context.payload.action) { - case "labeled": - console.log("context.payload.label.name"); - console.log(JSON.stringify(context.payload.label.name)); - console.log("skip if the added label is not area:components nor area:components:*"); - // " - if (context.payload.label.name != "area:components" && !context.payload.label.name.startsWith("area:components:")) { - // " - should_skip = true; - } - - console.log("but if it is area:ci label, don't skip because changes to the CI affect everything."); - if (context.payload.label.name == "area:ci") { - should_skip = false; - } - break; - case "synchronize": - let labels = context.payload.pull_request.labels.map(label => { return label.name }); - console.log("labels"); - console.log(JSON.stringify(labels)); - - console.log("skip if labels does not include area:components"); - if (!labels.includes("area:components")) { - console.log("area:components found in the labels"); - should_skip = true; - } - - console.log("but if labels include area:components:, do not skip"); - for (let i = 0; i < labels.length; i++) { - if (labels[i].startsWith("area:components:")) { // " - console.log("they does, do not skip"); - should_skip = false; - } - } - - console.log("but if labels include area:ci label, don't skip because changes to the CI affect everything."); - if (labels.includes("area:ci")) { - console.log("they does, do not skip"); - should_skip = false; - } - break; - } - return should_skip; - - - name: See if the PR has area:ci label - id: has_area_ci_label - uses: actions/github-script@v6 - with: - result-encoding: string - script: | - let result = false; - try { - pr = await github.rest.issues.get({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.number, - }); - console.log("pr"); - console.log(JSON.stringify(pr, null, 2)); - for (i = 0; i < pr.data.labels.length; i++) { - if (pr.data.labels[i].name == "area:ci") { - result = true; - } - } - - } catch(e) { - core.error(e); - core.setFailed(e.message); - } - return result; - - - name: Get all area:components:* lables - # this step returns a list of labels that start with "area:components:". - # the output includes comma-separated list of labels, such as "area: - # components:foo,area:components:bar". - id: get_component_labels - uses: actions/github-script@v6 - with: - result-encoding: string - script: | - console.log("context"); - console.log(JSON.stringify(context, null, 2)); - let i = 0; - let component_labels = []; - - switch(context.payload.action) { - case "labeled": - try { - pr = await github.rest.issues.get({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.number, - }); - console.log("pr"); - console.log(JSON.stringify(pr, null, 2)); - - for (i = 0; i < pr.data.labels.length; i++) { - if (pr.data.labels[i].name.startsWith("area:components:")) { - // " vim syntax cannot parse above line, breaking syntax highlight - component_labels.push(pr.data.labels[i].name); - } - } - } catch(e) { - core.error(e); - core.setFailed(e.message); - } - break; - // append all labels that start with "area:components:" - case "synchronize": - let labels = context.payload.pull_request.labels.map(label => { return label.name }); - for (i = 0; i < labels.length; i++) { - if (labels[i].startsWith("area:components:")) { - // " - component_labels.push(labels[i]); - } - } - break; - } - return component_labels; - - - name: Checkout - uses: actions/checkout@v2 - - - name: Select components to build - # this step returns a list of components to build - id: select_components_to_build - if: ${{ steps.skip_check.outputs.should_skip != 'true' }} - run: | - component_labels=`echo ${{ steps.get_component_labels.outputs.result }} | sed -e 's/,/ /g'` - echo "The PR has the following component labels:" - for component_label in ${component_labels}; do - echo "${component_label}" - done - all_components=`ls -1 components` - echo "The repository has the following components:" - for component in ${all_components}; do - echo "${component}" - done - components_to_build="" - for component_label in ${component_labels}; do - component_name=`echo ${component_label} | cut -f 3 -d':'` - components_to_build="${components_to_build} ${component_name}" - echo "components to build by labels" - echo "${components_to_build}" - done - - echo "Get a list of components by REQUIRES" - extra_components_to_build_by_require="" - - # scan all component directories and if a component requires - # one of components_to_build, add it to extra_components_to_build_by_require - for component in ${components_to_build}; do - for c in ${all_components}; do - # the output is: - # REQUIRES:log;color - required=`cmake -DCOMPONENT_NAME:STRING=${c} devtools/cmake-get-requires 2>&1 >/dev/null | grep 'REQUIRES:' | cut -f2 -d':' | sed -e 's/;/ /g'` - echo "component ${c} requires ${required}" - for r in ${required}; do - if echo "${all_components}" | grep -q "${r}"; then - echo "component ${r} is in all_components" - if [ "${r}" = "${component}" ]; then - echo "component ${c} requires ${component}, adding to extra_components_to_build_by_require" - extra_components_to_build_by_require="${extra_components_to_build_by_require} ${c}" - else - echo "component ${c} does not require ${component}, ignoring" - fi - else - echo "component ${r} is not in all_components, ignoring" - fi - done - done - done - # remove duplicated name from extra_components_to_build_by_require - extra_components_to_build_by_require=`echo "${extra_components_to_build_by_require}" | tr ' ' '\n' | sort -u | tr '\n' ' '` - - echo "The list of components to build by REQUIRES:" - echo "${extra_components_to_build_by_require}" - - # and merge everything - components_to_build="${components_to_build} ${extra_components_to_build_by_require}" - echo "components to build:" - echo "${components_to_build}" - - # if component_labels has "area:ci" build all components - if [ ${{ steps.has_area_ci_label.outputs.result }} == "true" ]; then - echo "the PR has area:ci label, going to build all components" - components_to_build="${all_components}" - fi - echo "The components to build:" - components_to_build=`echo ${components_to_build} | tr "\n" " "` - echo ${components_to_build} - echo "::set-output name=components_to_build::${components_to_build}" - - # XXX create multiple jobs for major versions - # - # for those who want to _refactor_ the jobs: - # - # in the previous CI implementation, all builds share a single build - # process. that way, you can remove duplications. however, every time a - # version changes the build process, the change affects all other build - # processes. I am tired of tracking changes and workarounds in the build - # process. the result is many `if`s. assuming major version does not change - # (a lot) its build process, creating multiple jobs, and using matrix is the - # only sane way. as GitHub Actions does not support reusable steps, there - # are many duplications. but no need to modify the entire build process to - # adopt changes in master. - build_esp32_master: - needs: pre_build - if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - target: - - esp32 - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install python - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - - name: Install dependencies - run: | - sudo apt-get install \ - bison \ - ccache \ - flex \ - gcc \ - git \ - gperf \ - libffi-dev \ - libncurses-dev \ - libssl-dev \ - make \ - wget - - - name: Set environment variables - id: set_env - run: | - REPO_DIR=`pwd` - EXAMPLE_DIR="${REPO_DIR}/examples" - __PROJECT_PATH=`pwd` - - # XXX actions/checkout does not allow to checkout a repository other - # than under __PROJECT_PATH - IDF_PATH="${__PROJECT_PATH}/idf" - - echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV} - echo "IDF_TARGET=${{ matrix.target }}" >> ${GITHUB_ENV} - - # XXX prefix all the environment variables with `__PROJECT_` to avoid pollution - echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV} - - - name: Checkout the SDK - uses: actions/checkout@v2 - with: - repository: espressif/esp-idf - path: idf - submodules: recursive - ref: master - - - name: Run idf_tools.py install - run: | - ${IDF_PATH}/tools/idf_tools.py install - - - name: Run idf_tools.py install-python-env - run: | - ${IDF_PATH}/tools/idf_tools.py install-python-env - - - name: Build (idf.py) - run: | - components_to_build="${{ needs.pre_build.outputs.components_to_build }}" - - echo "The component whose examples need to be built:" - echo "${components_to_build}" - - # find out drivers that do not support the target - EXCLUDE_COMPONENTS="" - ALL_COMPONENTS=`ls -1 components` - for C in ${ALL_COMPONENTS}; do - echo "see if component ${C} supports the target ${IDF_TARGET}" - if [ ! -f "components/${C}/.eil.yml" ]; then - echo "metadata file for component ${C}, components/${C}/.eil.yml, does not exist" - exit 1 - fi - if yq '.components[].targets[].name' "components/${C}/.eil.yml" | grep -q ${IDF_TARGET}; then - echo "component ${C} supports ${IDF_TARGET}" - else - echo "component ${C} does not support ${IDF_TARGET}. adding it to EXCLUDE_COMPONENTS" - EXCLUDE_COMPONENTS="${EXCLUDE_COMPONENTS} ${C}" - fi - done - echo "EXCLUDE_COMPONENTS=${EXCLUDE_COMPONENTS}" - export EXCLUDE_COMPONENTS - - # remove components in EXCLUDE_COMPONENTS from components_to_build - for ec in ${EXCLUDE_COMPONENTS}; do - components_to_build=`echo ${components_to_build} | tr ' ' '\n' | grep -v ${ec} | tr '\n' ' '` - done - - . ${IDF_PATH}/export.sh - - # XXX share cache between examples. - # see "Compiling In Different Directories" in ccache(1) - # | | 4.0.1 | master | - # |----------------------------------------|---------|---------| - # | without ccache | 33m 42s | 50m 27s | - # | CCACHE_BASEDIR and CCACHE_NOHASHDIR | 10m 41s | 16m 38s | - export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}" - export CCACHE_NOHASHDIR=true - - echo "final components_to_build:" - echo "${components_to_build}" - for component_to_build in ${components_to_build}; do - cd ${GITHUB_WORKSPACE} - echo "Building examples for ${component_to_build} under directory ${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - for i in $(ls -d *); do - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}/${i}" - echo "Building example ${i} for component ${component_to_build} in directory ${PWD}..." - idf.py --ccache build - done - done - - build_esp32_v4_x: - needs: pre_build - if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - build_method: - - idf - - make - branch: - - # for supported versions by espressif, see: - # https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html - - v4.1.3 - - v4.2.3 - - v4.3.4 - - v4.4.2 - target: - - esp32 - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install python - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - - name: Install dependencies - run: | - sudo apt-get install \ - bison \ - ccache \ - flex \ - gcc \ - git \ - gperf \ - libffi-dev \ - libncurses-dev \ - libssl-dev \ - make \ - wget - - - name: Set environment variables - id: set_env - run: | - SDK_NAME="esp-idf" - GCC_PREFIX="xtensa-${{ matrix.target }}-elf" - GCC_FILE="${GCC_PREFIX}-gcc" - TOOLCHAIN_DIR="${HOME}/.espressif/tools" - case "${{ matrix.branch }}" in - v4.0.*) - TOOLCHAIN_VERSION="esp-2020r3-8.4.0" - ;; - v4.1.*) - TOOLCHAIN_VERSION="esp-2020r3-8.4.0" - ;; - v4.2.*) - TOOLCHAIN_VERSION="esp-2020r3-8.4.0" - ;; - v4.3.*) - TOOLCHAIN_VERSION="esp-2021r2-patch3-8.4.0" - ;; - v4.4.*) - TOOLCHAIN_VERSION="esp-2021r2-patch3-8.4.0" - ;; - *) - echo "Unknown matrix.branch: ${{ matrix.branch }}" - exit 1 - ;; - esac - REPO_DIR=`pwd` - EXAMPLE_DIR="${REPO_DIR}/examples" - DISTFILE_DIR="${HOME}/distfiles" - __PROJECT_PATH=`pwd` - - # XXX actions/checkout does not allow to checkout a repository other - # than under __PROJECT_PATH - IDF_PATH="${__PROJECT_PATH}/idf" - - echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV} - echo "IDF_TARGET=${{ matrix.target }}" >> ${GITHUB_ENV} - - # cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR - echo "::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}" - - # XXX prefix all the environment variables with `__PROJECT_` to avoid pollution - echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV} - echo "__PROJECT_GCC_FILE=${GCC_FILE}" >> ${GITHUB_ENV} - echo "__PROJECT_GCC_PREFIX=${GCC_PREFIX}" >> ${GITHUB_ENV} - echo "__PROJECT_SDK_NAME=${SDK_NAME}" >> ${GITHUB_ENV} - echo "__PROJECT_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}" >> ${GITHUB_ENV} - echo "__PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}" >> ${GITHUB_ENV} - echo "__PROJECT_TOOLCHAIN_VERSION=${TOOLCHAIN_VERSION}" >> ${GITHUB_ENV} - echo "__PROJECT_DISTFILE_DIR=${DISTFILE_DIR}" >> ${GITHUB_ENV} - echo "__PROJECT_PATH=${__PROJECT_PATH}" >> ${GITHUB_ENV} - echo "__PROJECT_BUILD_COMMAND=${__PROJECT_BUILD_COMMAND}" >> ${GITHUB_ENV} - echo "__PROJECT_BUILD_COMMAND_ARG=${__PROJECT_BUILD_COMMAND_ARG}" >> ${GITHUB_ENV} - - - name: Checkout the SDK - uses: actions/checkout@v2 - with: - repository: espressif/esp-idf - path: idf - submodules: recursive - ref: ${{ matrix.branch }} - - - name: Cache esp-idf tools - - # cache esp-idf tools. each tagged branch has fixed versions of tools. - # the versions do not change. the master is an exception as it is a - # moving target. do NOT cache tools if the branch is master. - uses: actions/cache@v2 - id: cache-tools - with: - path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }} - key: ${{ runner.os }}-${{ matrix.branch }}-${{ matrix.target }}-cache-tools - - - name: Run idf_tools.py install - if: ${{ steps.cache-tools.outputs.cache-hit != 'true' }} - run: | - ${IDF_PATH}/tools/idf_tools.py install - - - name: Run idf_tools.py install-python-env - run: | - ${IDF_PATH}/tools/idf_tools.py install-python-env - - - name: Build (idf.py) - if: ${{ matrix.build_method == 'idf' }} - run: | - components_to_build="${{ needs.pre_build.outputs.components_to_build }}" - - echo "The component whose examples need to be built:" - echo "${components_to_build}" - - # find out drivers that do not support the target - EXCLUDE_COMPONENTS="" - ALL_COMPONENTS=`ls -1 components` - for C in ${ALL_COMPONENTS}; do - echo "see if component ${C} supports the target ${IDF_TARGET}" - if [ ! -f "components/${C}/.eil.yml" ]; then - echo "metadata file for component ${C}, components/${C}/.eil.yml, does not exist" - exit 1 - fi - if yq '.components[].targets[].name' "components/${C}/.eil.yml" | grep -q ${IDF_TARGET}; then - echo "component ${C} supports ${IDF_TARGET}" - else - echo "component ${C} does not support ${IDF_TARGET}. adding it to EXCLUDE_COMPONENTS" - EXCLUDE_COMPONENTS="${EXCLUDE_COMPONENTS} ${C}" - fi - done - echo "EXCLUDE_COMPONENTS=${EXCLUDE_COMPONENTS}" - export EXCLUDE_COMPONENTS - - # remove components in EXCLUDE_COMPONENTS from components_to_build - for ec in ${EXCLUDE_COMPONENTS}; do - components_to_build=`echo ${components_to_build} | tr ' ' '\n' | grep -v ${ec} | tr '\n' ' '` - done - - . ${IDF_PATH}/export.sh - - # XXX share cache between examples. - # see "Compiling In Different Directories" in ccache(1) - # | | 4.0.1 | master | - # |----------------------------------------|---------|---------| - # | without ccache | 33m 42s | 50m 27s | - # | CCACHE_BASEDIR and CCACHE_NOHASHDIR | 10m 41s | 16m 38s | - export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}" - export CCACHE_NOHASHDIR=true - - echo "final components_to_build:" - echo "${components_to_build}" - for component_to_build in ${components_to_build}; do - cd ${GITHUB_WORKSPACE} - echo "Building examples for ${component_to_build} under directory ${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - for i in $(ls -d *); do - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}/${i}" - echo "Building example ${i} for component ${component_to_build} in directory ${PWD}..." - idf.py --ccache build - done - done - - - name: Setup ccache (make) - - # XXX ccache needs to be configured here - # unlike idf.py, esp-idf does nothing to setup ccache for make. - # IDF_CCACHE_ENABLE does not work either. - if: ${{ matrix.build_method == 'make' }} - run: | - GCC_BIN_DIR="${__PROJECT_TOOLCHAIN_DIR}/${__PROJECT_GCC_PREFIX}/${__PROJECT_TOOLCHAIN_VERSION}/${__PROJECT_GCC_PREFIX}/bin" - CCACHE_BIN_DIR="${HOME}/ccache_bin" - mkdir -p "${CCACHE_BIN_DIR}" - (cd "${CCACHE_BIN_DIR}" && ln -s /usr/bin/ccache "${__PROJECT_GCC_FILE}") - export PATH="${CCACHE_BIN_DIR}:$PATH:${GCC_BIN_DIR}" - echo "PATH=${PATH}" >> ${GITHUB_ENV} - echo "CCACHE_BASEDIR=${__PROJECT_EXAMPLE_DIR}" >> ${GITHUB_ENV} - echo "CCACHE_NOHASHDIR=true" >> ${GITHUB_ENV} - - - name: Build (make) - if: ${{ matrix.build_method == 'make' }} - run: | - components_to_build="${{ needs.pre_build.outputs.components_to_build }}" - - echo "The component whose examples need to be built:" - echo "${components_to_build}" - - # make sure gcc is in $PATH - echo ${PATH} - ${__PROJECT_GCC_FILE} --version - - # find out drivers that do not support the target - EXCLUDE_COMPONENTS="" - ALL_COMPONENTS=`ls -1 components` - for C in ${ALL_COMPONENTS}; do - echo "see if component ${C} supports the target ${IDF_TARGET}" - if [ ! -f "components/${C}/.eil.yml" ]; then - echo "metadata file for component ${C}, components/${C}/.eil.yml, does not exist" - exit 1 - fi - if yq '.components[].targets[].name' "components/${C}/.eil.yml" | grep -q ${IDF_TARGET}; then - echo "component ${C} supports ${IDF_TARGET}" - else - echo "component ${C} does not support ${IDF_TARGET}. adding it to EXCLUDE_COMPONENTS" - EXCLUDE_COMPONENTS="${EXCLUDE_COMPONENTS} ${C}" - fi - done - echo "EXCLUDE_COMPONENTS=${EXCLUDE_COMPONENTS}" - export EXCLUDE_COMPONENTS - - # remove components in EXCLUDE_COMPONENTS from components_to_build - for ec in ${EXCLUDE_COMPONENTS}; do - components_to_build=`echo ${components_to_build} | tr ' ' '\n' | grep -v ${ec} | tr '\n' ' '` - done - - . ${IDF_PATH}/export.sh - - # XXX share cache between examples. - # see "Compiling In Different Directories" in ccache(1) - # | | 4.0.1 | master | - # |----------------------------------------|---------|---------| - # | without ccache | 33m 42s | 50m 27s | - # | CCACHE_BASEDIR and CCACHE_NOHASHDIR | 10m 41s | 16m 38s | - export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}" - export CCACHE_NOHASHDIR=true - - echo "final components_to_build:" - echo "${components_to_build}" - for component_to_build in ${components_to_build}; do - cd ${GITHUB_WORKSPACE} - echo "Building examples for ${component_to_build} under directory ${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - for i in $(ls -d *); do - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}/${i}" - echo "Building example ${i} for component ${component_to_build} in directory ${PWD}..." - make defconfig - make -j$(nproc) - done - done - - build_esp8266: - runs-on: ubuntu-latest - needs: pre_build - if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - strategy: - fail-fast: false - matrix: - build_method: - - # XXX build examples with make only - # idf.py in ESP8266 RTOS SDK is broken in many ways. - - make - branch: - - v3.4 - - master - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install python - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - - name: Install dependencies - run: | - sudo apt-get install \ - bison \ - ccache \ - flex \ - gcc \ - git \ - gperf \ - libffi-dev \ - libncurses-dev \ - libssl-dev \ - make \ - wget - - - name: Install yq to parse YAML files - run: | - YQ_VERSION=v4.25.3 - YQ_BINARY=yq_linux_amd64 - YQ_COMMAND="/usr/bin/yq" - wget "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/${YQ_BINARY}" -O ./yq - sudo mv ./yq "${YQ_COMMAND}" - sudo chmod +x "${YQ_COMMAND}" - "${YQ_COMMAND}" --version - - - name: Set environment variables - id: set_env - run: | - SDK_NAME="ESP8266_RTOS_SDK" - GCC_PREFIX="xtensa-lx106-elf" - GCC_FILE="${GCC_PREFIX}-gcc" - TOOLCHAIN_DIR="${HOME}/.espressif/tools" - REPO_DIR=`pwd` - EXAMPLE_DIR="${REPO_DIR}/examples" - __PROJECT_PATH=`pwd` - __PROJECT_TOOLCHAIN_VERSION="esp-2020r3-49-gd5524c1-8.4.0" - - # XXX actions/checkout does not allow to checkout a repository other - # than under __PROJECT_PATH - IDF_PATH="${__PROJECT_PATH}/idf" - - echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV} - echo "IDF_TARGET=esp8266" >> ${GITHUB_ENV} - - # cache-idf-tools needs PROJECT_TOOLCHAIN_DIR - echo "::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}" - - # XXX prefix all the environment variables with `__PROJECT_` to avoid pollution - echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV} - echo "__PROJECT_GCC_FILE=${GCC_FILE}" >> ${GITHUB_ENV} - echo "__PROJECT_GCC_PREFIX=${GCC_PREFIX}" >> ${GITHUB_ENV} - echo "__PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}" >> ${GITHUB_ENV} - echo "__PROJECT_PATH=${__PROJECT_PATH}" >> ${GITHUB_ENV} - echo "__PROJECT_BUILD_COMMAND=${__PROJECT_BUILD_COMMAND}" >> ${GITHUB_ENV} - echo "__PROJECT_BUILD_COMMAND_ARG=${__PROJECT_BUILD_COMMAND_ARG}" >> ${GITHUB_ENV} - echo "__PROJECT_TOOLCHAIN_VERSION=${__PROJECT_TOOLCHAIN_VERSION}" >> ${GITHUB_ENV} - - - name: Checkout the SDK - uses: actions/checkout@v2 - with: - repository: espressif/ESP8266_RTOS_SDK - path: idf - submodules: recursive - ref: ${{ matrix.branch }} - # XXX git.eclipse.org does not allow to fetch a commit. fetch all - # the commits. - fetch-depth: 0 - - - name: Install python requirements (pip) - run: | - python -m pip install --user -r ${IDF_PATH}/requirements.txt - - - name: Cache toolchain - id: cache-idf-tools - if: ${{ matrix.branch != 'master' }} - uses: actions/cache@v2 - with: - path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }} - key: ${{ runner.os }}-${{ matrix.branch }}-esp8266-cache-tools - - - name: Install toolchain - if: ${{ steps.cache-idf-tools.outputs.cache-hit != 'true' || matrix.branch == 'master' }} - run: | - ${IDF_PATH}/install.sh - - - name: Setup ccache (make) - run: | - __PROJECT_CCACHE_BIN_DIR="${HOME}/ccache_bin" - mkdir -p "${__PROJECT_CCACHE_BIN_DIR}" - (cd "${__PROJECT_CCACHE_BIN_DIR}" && ln -s /usr/bin/ccache "${__PROJECT_GCC_FILE}") - echo "PATH=${__PROJECT_CCACHE_BIN_DIR}:$PATH:${__PROJECT_TOOLCHAIN_DIR}/${__PROJECT_GCC_PREFIX}/${__PROJECT_TOOLCHAIN_VERSION}/${__PROJECT_GCC_PREFIX}/bin" >> ${GITHUB_ENV} - echo "CCACHE_BASEDIR=${__PROJECT_EXAMPLE_DIR}" >> ${GITHUB_ENV} - echo "CCACHE_NOHASHDIR=true" >> ${GITHUB_ENV} - - - name: Build (make) - if: ${{ matrix.build_method == 'make' }} - run: | - components_to_build="${{ needs.pre_build.outputs.components_to_build }}" - - echo "The component whose examples need to be built:" - echo "${components_to_build}" - - # make sure gcc is in $PATH - echo ${PATH} - ${__PROJECT_GCC_FILE} --version - - # find out drivers that do not support the target - EXCLUDE_COMPONENTS="" - ALL_COMPONENTS=`ls -1 components` - for C in ${ALL_COMPONENTS}; do - echo "see if component ${C} supports the target ${IDF_TARGET}" - if [ ! -f "components/${C}/.eil.yml" ]; then - echo "metadata file for component ${C}, components/${C}/.eil.yml, does not exist" - exit 1 - fi - if yq '.components[].targets[].name' "components/${C}/.eil.yml" | grep -q ${IDF_TARGET}; then - echo "component ${C} supports ${IDF_TARGET}" - else - echo "component ${C} does not support ${IDF_TARGET}. adding it to EXCLUDE_COMPONENTS" - EXCLUDE_COMPONENTS="${EXCLUDE_COMPONENTS} ${C}" - fi - done - echo "EXCLUDE_COMPONENTS=${EXCLUDE_COMPONENTS}" - export EXCLUDE_COMPONENTS - - # remove components in EXCLUDE_COMPONENTS from components_to_build - for ec in ${EXCLUDE_COMPONENTS}; do - components_to_build=`echo ${components_to_build} | tr ' ' '\n' | grep -v ${ec} | tr '\n' ' '` - done - - # XXX share cache between examples. - # see "Compiling In Different Directories" in ccache(1) - # | | 4.0.1 | master | - # |----------------------------------------|---------|---------| - # | without ccache | 33m 42s | 50m 27s | - # | CCACHE_BASEDIR and CCACHE_NOHASHDIR | 10m 41s | 16m 38s | - export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}" - export CCACHE_NOHASHDIR=true - - echo "final components_to_build:" - echo "${components_to_build}" - for component_to_build in ${components_to_build}; do - cd ${GITHUB_WORKSPACE} - echo "Building examples for ${component_to_build} under directory ${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - for i in $(ls -d *); do - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}/${i}" - echo "Building example ${i} for component ${component_to_build} in directory ${PWD}..." - make defconfig - make -j$(nproc) - done - done - - build_esp8266_v3_3_x: - runs-on: ubuntu-latest - needs: pre_build - if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - strategy: - fail-fast: false - matrix: - build_method: - - # XXX build examples with make only - # idf.py in ESP8266 RTOS SDK is broken in many ways. - - make - branch: - - v3.3 - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install python - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - - name: Install dependencies - run: | - sudo apt-get install \ - bison \ - ccache \ - flex \ - gcc \ - git \ - gperf \ - libffi-dev \ - libncurses-dev \ - libssl-dev \ - make \ - wget - - - name: Set environment variables - id: set_env - run: | - SDK_NAME="ESP8266_RTOS_SDK" - GCC_PREFIX="xtensa-lx106-elf" - GCC_FILE="${GCC_PREFIX}-gcc" - TOOLCHAIN_DIR="${HOME}/esp32" - case "${{ matrix.branch }}" in - v3.3) - TOOLCHAIN_FILE="xtensa-lx106-elf-linux64-1.22.0-100-ge567ec7-5.2.0.tar.gz" - ;; - v3.4) - TOOLCHAIN_FILE="xtensa-lx106-elf-gcc8_4_0-esp-2020r3-linux-amd64.tar.gz" - ;; - *) - echo "unknown matrix.branch ${{ matrix.branch }}" - exit 1 - ;; - esac - REPO_DIR=`pwd` - EXAMPLE_DIR="${REPO_DIR}/examples" - DISTFILE_DIR="${HOME}/distfiles" - __PROJECT_PATH=`pwd` - - # XXX actions/checkout does not allow to checkout a repository other - # than under __PROJECT_PATH - IDF_PATH="${__PROJECT_PATH}/idf" - - echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV} - echo "IDF_TARGET=esp8266" >> ${GITHUB_ENV} - - # cache-idf-tools needs PROJECT_DISTFILE_DIR - echo "::set-output name=PROJECT_DISTFILE_DIR::${DISTFILE_DIR}" - - # XXX prefix all the environment variables with `__PROJECT_` to avoid pollution - echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV} - echo "__PROJECT_GCC_FILE=${GCC_FILE}" >> ${GITHUB_ENV} - echo "__PROJECT_GCC_PREFIX=${GCC_PREFIX}" >> ${GITHUB_ENV} - echo "__PROJECT_SDK_NAME=${SDK_NAME}" >> ${GITHUB_ENV} - echo "__PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}" >> ${GITHUB_ENV} - echo "__PROJECT_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}" >> ${GITHUB_ENV} - echo "__PROJECT_DISTFILE_DIR=${DISTFILE_DIR}" >> ${GITHUB_ENV} - echo "__PROJECT_PATH=${__PROJECT_PATH}" >> ${GITHUB_ENV} - echo "__PROJECT_BUILD_COMMAND=${__PROJECT_BUILD_COMMAND}" >> ${GITHUB_ENV} - echo "__PROJECT_BUILD_COMMAND_ARG=${__PROJECT_BUILD_COMMAND_ARG}" >> ${GITHUB_ENV} - - - name: Checkout the SDK - uses: actions/checkout@v2 - with: - repository: espressif/ESP8266_RTOS_SDK - path: idf - submodules: recursive - ref: ${{ matrix.branch }} - # XXX git.eclipse.org does not allow to fetch a commit. fetch all - # the commits. - fetch-depth: 0 - - - name: Install python requirements (pip) - run: | - - # XXX upgrade pyOpenSSL because older pyOpenSSL caused issues in - # the past. this might not be true anymore. - python -m pip install --user --upgrade pyOpenSSL - python -m pip install --user -r ${IDF_PATH}/requirements.txt - - - name: Create __PROJECT_DISTFILE_DIR - run: | - mkdir -p "${__PROJECT_DISTFILE_DIR}" - - - name: Cache toolchain - id: cache-idf-tools - uses: actions/cache@v2 - with: - path: ${{ steps.set_env.outputs.PROJECT_DISTFILE_DIR }} - key: ${{ runner.os }}-${{ matrix.branch }}-esp8266-cache-tools - - - name: Download toolchain if it does not exist - run: | - if [ ! -f "${__PROJECT_DISTFILE_DIR}/${__PROJECT_TOOLCHAIN_FILE}" ]; then - wget -O "${__PROJECT_DISTFILE_DIR}/${__PROJECT_TOOLCHAIN_FILE}" "https://dl.espressif.com/dl/${__PROJECT_TOOLCHAIN_FILE}" - fi - - - name: Extract the toolchain - run: | - mkdir -p "${__PROJECT_TOOLCHAIN_DIR}" - tar -xz -C "${__PROJECT_TOOLCHAIN_DIR}" -f "${__PROJECT_DISTFILE_DIR}/${__PROJECT_TOOLCHAIN_FILE}" - - - name: Setup ccache - run: | - __PROJECT_CCACHE_BIN_DIR="${HOME}/ccache_bin" - mkdir -p "${__PROJECT_CCACHE_BIN_DIR}" - (cd "${__PROJECT_CCACHE_BIN_DIR}" && ln -s /usr/bin/ccache "${__PROJECT_GCC_FILE}") - echo "PATH=${__PROJECT_CCACHE_BIN_DIR}:$PATH:${__PROJECT_TOOLCHAIN_DIR}/${__PROJECT_GCC_PREFIX}/bin" >> ${GITHUB_ENV} - echo "CCACHE_BASEDIR=${__PROJECT_EXAMPLE_DIR}" >> ${GITHUB_ENV} - echo "CCACHE_NOHASHDIR=true" >> ${GITHUB_ENV} - - - name: Build (make) - if: ${{ matrix.build_method == 'make' }} - run: | - components_to_build="${{ needs.pre_build.outputs.components_to_build }}" - - echo "The component whose examples need to be built:" - echo "${components_to_build}" - - # make sure gcc is in $PATH - echo ${PATH} - ${__PROJECT_GCC_FILE} --version - - # find out drivers that do not support the target - EXCLUDE_COMPONENTS="" - ALL_COMPONENTS=`ls -1 components` - for C in ${ALL_COMPONENTS}; do - echo "see if component ${C} supports the target ${IDF_TARGET}" - if [ ! -f "components/${C}/.eil.yml" ]; then - echo "metadata file for component ${C}, components/${C}/.eil.yml, does not exist" - exit 1 - fi - if yq '.components[].targets[].name' "components/${C}/.eil.yml" | grep -q ${IDF_TARGET}; then - echo "component ${C} supports ${IDF_TARGET}" - else - echo "component ${C} does not support ${IDF_TARGET}. adding it to EXCLUDE_COMPONENTS" - EXCLUDE_COMPONENTS="${EXCLUDE_COMPONENTS} ${C}" - fi - done - echo "EXCLUDE_COMPONENTS=${EXCLUDE_COMPONENTS}" - export EXCLUDE_COMPONENTS - - # remove components in EXCLUDE_COMPONENTS from components_to_build - for ec in ${EXCLUDE_COMPONENTS}; do - components_to_build=`echo ${components_to_build} | tr ' ' '\n' | grep -v ${ec} | tr '\n' ' '` - done - - # XXX share cache between examples. - # see "Compiling In Different Directories" in ccache(1) - # | | 4.0.1 | master | - # |----------------------------------------|---------|---------| - # | without ccache | 33m 42s | 50m 27s | - # | CCACHE_BASEDIR and CCACHE_NOHASHDIR | 10m 41s | 16m 38s | - export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}" - export CCACHE_NOHASHDIR=true - - echo "final components_to_build:" - echo "${components_to_build}" - for component_to_build in ${components_to_build}; do - cd ${GITHUB_WORKSPACE} - echo "Building examples for ${component_to_build} under directory ${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - for i in $(ls -d *); do - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}/${i}" - echo "Building example ${i} for component ${component_to_build} in directory ${PWD}..." - make defconfig - make -j$(nproc) - done - done - - # XXX esp32s2 support was introduced in v4.2. older esp-idf does not install - # toolchains for esp32s2. thus, you cannot add `esp32s2` target to - # build_esp32_v4_x. - # - # this job can be removed when either one of the followings are met: - # - # * GitHub Actions supports "early exit" (successfully exit if a condition is - # true). - # * all branches in build_esp32_v4_x supports esp32s2 - # - # additionally, esp32s2 build requires idf.py. make is not supported. - build_esp32s2_v4_x: - runs-on: ubuntu-latest - needs: pre_build - if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - strategy: - fail-fast: false - matrix: - build_method: - - idf - branch: - - # esp32s2 support since v4.2.x - - master - - v4.2.3 - - v4.3.4 - - v4.4.2 - target: - - esp32s2 - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install python - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - - name: Install dependencies - run: | - sudo apt-get install \ - bison \ - ccache \ - flex \ - gcc \ - git \ - gperf \ - libffi-dev \ - libncurses-dev \ - libssl-dev \ - make \ - wget - - - name: Set environment variables - id: set_env - run: | - TOOLCHAIN_DIR="${HOME}/.espressif/tools" - REPO_DIR=`pwd` - EXAMPLE_DIR="${REPO_DIR}/examples" - __PROJECT_PATH=`pwd` - IDF_PATH="${__PROJECT_PATH}/idf" - - # cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR - echo "::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}" - - echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV} - echo "IDF_TARGET=${{ matrix.target }}" >> ${GITHUB_ENV} - echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV} - - - name: Checkout the SDK - uses: actions/checkout@v2 - with: - repository: espressif/esp-idf - path: idf - submodules: recursive - ref: ${{ matrix.branch }} - - - name: Cache esp-idf tools - - # cache esp-idf tools. each tagged branch has fixed versions of tools. - # the versions do not change. the master is an exception as it is a - # moving target. do NOT cache tools if the branch is master. - uses: actions/cache@v2 - id: cache-tools - if: ${{ matrix.branch != 'master' }} - with: - path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }} - key: ${{ runner.os }}-${{ matrix.branch }}-${{ matrix.target }}-cache-tools - - - name: Run install.sh - if: ${{ steps.cache-tools.outputs.cache-hit != 'true' || matrix.branch == 'master' }} - run: | - ${IDF_PATH}/install.sh - - - name: Run idf_tools.py install-python-env - run: | - ${IDF_PATH}/tools/idf_tools.py install-python-env - - - name: Build (idf.py) - if: ${{ matrix.build_method == 'idf' }} - run: | - components_to_build="${{ needs.pre_build.outputs.components_to_build }}" - - echo "The component whose examples need to be built:" - echo "${components_to_build}" - - # find out drivers that do not support the target - EXCLUDE_COMPONENTS="" - ALL_COMPONENTS=`ls -1 components` - for C in ${ALL_COMPONENTS}; do - echo "see if component ${C} supports the target ${IDF_TARGET}" - if [ ! -f "components/${C}/.eil.yml" ]; then - echo "metadata file for component ${C}, components/${C}/.eil.yml, does not exist" - exit 1 - fi - if yq '.components[].targets[].name' "components/${C}/.eil.yml" | grep -q ${IDF_TARGET}; then - echo "component ${C} supports ${IDF_TARGET}" - else - echo "component ${C} does not support ${IDF_TARGET}. adding it to EXCLUDE_COMPONENTS" - EXCLUDE_COMPONENTS="${EXCLUDE_COMPONENTS} ${C}" - fi - done - echo "EXCLUDE_COMPONENTS=${EXCLUDE_COMPONENTS}" - export EXCLUDE_COMPONENTS - - # remove components in EXCLUDE_COMPONENTS from components_to_build - for ec in ${EXCLUDE_COMPONENTS}; do - components_to_build=`echo ${components_to_build} | tr ' ' '\n' | grep -v ${ec} | tr '\n' ' '` - done - - . ${IDF_PATH}/export.sh - - # XXX share cache between examples. - # see "Compiling In Different Directories" in ccache(1) - # | | 4.0.1 | master | - # |----------------------------------------|---------|---------| - # | without ccache | 33m 42s | 50m 27s | - # | CCACHE_BASEDIR and CCACHE_NOHASHDIR | 10m 41s | 16m 38s | - export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}" - export CCACHE_NOHASHDIR=true - - echo "final components_to_build:" - echo "${components_to_build}" - for component_to_build in ${components_to_build}; do - cd ${GITHUB_WORKSPACE} - echo "Building examples for ${component_to_build} under directory ${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - for i in $(ls -d *); do - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}/${i}" - echo "Building example ${i} for component ${component_to_build} in directory ${PWD}..." - idf.py --ccache build - done - done - - build_esp32c3_v4_x: - runs-on: ubuntu-latest - needs: pre_build - if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - strategy: - fail-fast: false - matrix: - build_method: - - idf - branch: - # esp32c3 support was introduced in v4.3. - - master - - v4.3.4 - - v4.4.2 - target: - - esp32c3 - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install python - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - - name: Install dependencies - run: | - sudo apt-get install \ - bison \ - ccache \ - flex \ - gcc \ - git \ - gperf \ - libffi-dev \ - libncurses-dev \ - libssl-dev \ - make \ - wget - - - name: Set environment variables - id: set_env - run: | - TOOLCHAIN_DIR="${HOME}/.espressif/tools" - REPO_DIR=`pwd` - EXAMPLE_DIR="${REPO_DIR}/examples" - __PROJECT_PATH=`pwd` - IDF_PATH="${__PROJECT_PATH}/idf" - - # cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR - echo "::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}" - - echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV} - echo "IDF_TARGET=${{ matrix.target }}" >> ${GITHUB_ENV} - echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV} - - - name: Checkout the SDK - uses: actions/checkout@v2 - with: - repository: espressif/esp-idf - path: idf - submodules: recursive - ref: ${{ matrix.branch }} - - - name: Cache esp-idf tools - - # cache esp-idf tools. each tagged branch has fixed versions of tools. - # the versions do not change. the master is an exception as it is a - # moving target. do NOT cache tools if the branch is master. - uses: actions/cache@v2 - id: cache-tools - if: ${{ matrix.branch != 'master' }} - with: - path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }} - key: ${{ runner.os }}-${{ matrix.branch }}-${{ matrix.target }}-cache-tools-1 - - - name: Run install.sh - if: ${{ steps.cache-tools.outputs.cache-hit != 'true' || matrix.branch == 'master' }} - run: | - ${IDF_PATH}/install.sh - - - name: Run idf_tools.py install-python-env - run: | - ${IDF_PATH}/tools/idf_tools.py install-python-env - - - name: Build (idf.py) - if: ${{ matrix.build_method == 'idf' }} - run: | - components_to_build="${{ needs.pre_build.outputs.components_to_build }}" - - echo "The component whose examples need to be built:" - echo "${components_to_build}" - - # find out drivers that do not support the target - EXCLUDE_COMPONENTS="" - ALL_COMPONENTS=`ls -1 components` - for C in ${ALL_COMPONENTS}; do - echo "see if component ${C} supports the target ${IDF_TARGET}" - if [ ! -f "components/${C}/.eil.yml" ]; then - echo "metadata file for component ${C}, components/${C}/.eil.yml, does not exist" - exit 1 - fi - if yq '.components[].targets[].name' "components/${C}/.eil.yml" | grep -q ${IDF_TARGET}; then - echo "component ${C} supports ${IDF_TARGET}" - else - echo "component ${C} does not support ${IDF_TARGET}. adding it to EXCLUDE_COMPONENTS" - EXCLUDE_COMPONENTS="${EXCLUDE_COMPONENTS} ${C}" - fi - done - echo "EXCLUDE_COMPONENTS=${EXCLUDE_COMPONENTS}" - export EXCLUDE_COMPONENTS - - # remove components in EXCLUDE_COMPONENTS from components_to_build - for ec in ${EXCLUDE_COMPONENTS}; do - components_to_build=`echo ${components_to_build} | tr ' ' '\n' | grep -v ${ec} | tr '\n' ' '` - done - - . ${IDF_PATH}/export.sh - - # XXX share cache between examples. - # see "Compiling In Different Directories" in ccache(1) - # | | 4.0.1 | master | - # |----------------------------------------|---------|---------| - # | without ccache | 33m 42s | 50m 27s | - # | CCACHE_BASEDIR and CCACHE_NOHASHDIR | 10m 41s | 16m 38s | - export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}" - export CCACHE_NOHASHDIR=true - - echo "final components_to_build:" - echo "${components_to_build}" - for component_to_build in ${components_to_build}; do - cd ${GITHUB_WORKSPACE} - echo "Building examples for ${component_to_build} under directory ${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}" - for i in $(ls -d *); do - cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}/${i}" - echo "Building example ${i} for component ${component_to_build} in directory ${PWD}..." - idf.py --ccache build - done - done - all_build: - # a meta job that requires all of the above so that repository - # admin can choose a single test name in "Require status checks to pass - # before merging". A trick obtained from: - # - # https://github.com/jazzband/pip-tools/issues/1085#issuecomment-619172509 - name: All build - runs-on: ubuntu-latest - needs: - - build_esp32_master - - build_esp32_v4_x - - build_esp8266 - - build_esp8266_v3_3_x - - build_esp32s2_v4_x - - build_esp32c3_v4_x - steps: - - name: - run: | - echo "All builds finished" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..6ff99faa --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,392 @@ +name: Main CI process +on: + workflow_dispatch: + pull_request: + types: + - opened + - reopened + - synchronize + +jobs: + + prepare: + name: Prepare CI + runs-on: ubuntu-latest + outputs: + _ci_readme: ${{ steps.ci.outputs._ci_readme }} + _ci_docs: ${{ steps.ci.outputs._ci_docs }} + _ci_build_esp32: ${{ steps.ci.outputs._ci_build_esp32 }} + _ci_build_esp32c2: ${{ steps.ci.outputs._ci_build_esp32c2 }} + _ci_build_esp32c3: ${{ steps.ci.outputs._ci_build_esp32c3 }} + _ci_build_esp32c6: ${{ steps.ci.outputs._ci_build_esp32c6 }} + _ci_build_esp32h2: ${{ steps.ci.outputs._ci_build_esp32h2 }} + _ci_build_esp32h4: ${{ steps.ci.outputs._ci_build_esp32h4 }} + _ci_build_esp32s2: ${{ steps.ci.outputs._ci_build_esp32s2 }} + _ci_build_esp32s3: ${{ steps.ci.outputs._ci_build_esp32s3 }} + _ci_build_esp8266: ${{ steps.ci.outputs._ci_build_esp8266 }} + steps: + - id: setup_python + name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - id: checkout + name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: '0' + + - id: requirements + name: Install devtool requirements + run: pip install -r ./devtools/requirements.txt + + - id: ci + name: Build list of CI jobs + run: python ./devtools/devtool.py ci >> $GITHUB_OUTPUT + + readme: + name: Test README.md + needs: + - prepare + if: ${{ needs.prepare.outputs._ci_readme == '1' }} + runs-on: ubuntu-latest + steps: + - id: setup_python + name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - id: checkout + name: Checkout + uses: actions/checkout@v3 + + - id: requirements + name: Install devtool requirements + run: pip install -r ./devtools/requirements.txt + + - id: check + name: Check README.md + run: | + python ./devtools/devtool.py render + git diff --exit-code README.md + + docs: + name: Documentation build check + runs-on: ubuntu-latest + needs: + - prepare + if: ${{ needs.prepare.outputs._ci_docs == '1' }} + steps: + - id: checkout + name: Checkout + uses: actions/checkout@v3 + + - id: dependencies + name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install python3-sphinx python3-sphinx-rtd-theme python3-breathe doxygen + + # FIXME: extract sphinx errors and warnings, not just run make! + - id: build + name: Build docs + run: | + cd docs + make dirhtml + if [ -s doxygen.log ]; then + echo "=========================================================" + echo "Found errors:" + echo "=========================================================" + cat doxygen.log + exit 1 + fi + + build_esp32xx: + name: Build for ESP32xx + runs-on: ubuntu-latest + needs: + - prepare + if: >- + ${{ needs.prepare.outputs._ci_build_esp32 != '' + || needs.prepare.outputs._ci_build_esp32c2 != '' + || needs.prepare.outputs._ci_build_esp32c3 != '' + || needs.prepare.outputs._ci_build_esp32c6 != '' + || needs.prepare.outputs._ci_build_esp32h2 != '' + || needs.prepare.outputs._ci_build_esp32h4 != '' + || needs.prepare.outputs._ci_build_esp32s2 != '' + || needs.prepare.outputs._ci_build_esp32s3 != '' }} + strategy: + # run other jobs even when one job failed + fail-fast: false + # releases, see https://github.com/espressif/esp-idf#esp-idf-release-support-schedule + matrix: + esp_idf_version: + - latest + - release-v5.1 + - release-v5.0 + - release-v4.4 + - release-v4.3 + target: + - esp32 + - esp32s2 + - esp32s3 + - esp32c2 + - esp32c3 + - esp32c6 + - esp32h2 + steps: + - id: checkout + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + + - id: ccache + name: Install ccache + run: | + sudo apt-get update + sudo apt-get -y install ccache + + - id: build_esp32 + name: Build + uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: ${{ matrix.esp_idf_version }} + target: ${{ matrix.target }} + path: examples + command: | + # XXX share cache between examples. + # see "Compiling In Different Directories" in ccache(1) + export CCACHE_BASEDIR="$(pwd)" + export CCACHE_NOHASHDIR=true + + # see suppot matrix at https://github.com/espressif/esp-idf#esp-idf-release-and-soc-compatibility + examples= + + # ESP32: all + if [ "${{ matrix.target }}" = "esp32" ]; then + examples="${{ needs.prepare.outputs._ci_build_esp32 }}" + fi + + # ESP32-S2: all + if [ "${{ matrix.target }}" = "esp32s2" ]; then + examples="${{ needs.prepare.outputs._ci_build_esp32s2 }}" + fi + + # ESP32-C3: 4.3, 4.4, 5.0, 5.1 + if [ "${{ matrix.target }}" = "esp32c3" ]; then + if [ "${{ matrix.esp_idf_version }}" != "release-v4.2" ]; then + examples="${{ needs.prepare.outputs._ci_build_esp32c3 }}" + fi + fi + + # ESP32-S3: 4.4, 5.0, 5.1 + if [ "${{ matrix.target }}" = "esp32s3" ]; then + if [ "${{ matrix.esp_idf_version }}" != "release-v4.2" \ + -a "${{ matrix.esp_idf_version }}" != "release-v4.3"]; then + examples="${{ needs.prepare.outputs._ci_build_esp32s3 }}" + fi + fi + + # ESP32-C2: 5.0, 5.1 + if [ "${{ matrix.target }}" = "esp32c2" ]; then + if [ "${{ matrix.esp_idf_version }}" != "release-v4.2" \ + -a "${{ matrix.esp_idf_version }}" != "release-v4.3" \ + -a "${{ matrix.esp_idf_version }}" != "release-v4.4" ]; then + examples="${{ needs.prepare.outputs._ci_build_esp32c2 }}" + fi + fi + + # ESP32-C6: 5.1 + if [ "${{ matrix.target }}" = "esp32c6" ]; then + if [ "${{ matrix.esp_idf_version }}" != "release-v4.2" \ + -a "${{ matrix.esp_idf_version }}" != "release-v4.3" \ + -a "${{ matrix.esp_idf_version }}" != "release-v4.4" \ + -a "${{ matrix.esp_idf_version }}" != "release-v5.0" ]; then + examples="${{ needs.prepare.outputs._ci_build_esp32c6 }}" + fi + fi + + # ESP32-H2: 5.1 + if [ "${{ matrix.target }}" = "esp32h2" ]; then + if [ "${{ matrix.esp_idf_version }}" != "release-v4.2" \ + -a "${{ matrix.esp_idf_version }}" != "release-v4.3" \ + -a "${{ matrix.esp_idf_version }}" != "release-v4.4" \ + -a "${{ matrix.esp_idf_version }}" != "release-v5.0" ]; then + examples="${{ needs.prepare.outputs._ci_build_esp32h2 }}" + fi + fi + + if [ -z "${examples}" ]; then + echo "Nothing to build: no component supports ${{ matrix.target }} or " \ + "ESP-IDF ${{ matrix.esp_idf_version }} does not support ${{ matrix.target }}" + exit 0 + fi + + echo "=========================================================" + echo "Examples to build for components:" + echo "=========================================================" + echo "${examples}" + + for component in ${examples}; do + for dir in ${component}/*; do + cd ${dir} + idf.py --ccache build; res=$?; [ $res -ne 0 ] && exit $res + rm -rf build + cd ../.. + done + done + + build_esp8266: + name: Build for ESP8266 + runs-on: ubuntu-latest + needs: + - prepare + if: ${{ needs.prepare.outputs._ci_build_esp8266 != '' }} + strategy: + fail-fast: false + matrix: + esp_open_rtos: + - v3.4 + - master + build_method: + # XXX build examples with make only + # idf.py in ESP8266 RTOS SDK is broken in many ways. + - make + steps: + - id: checkout + name: Checkout + uses: actions/checkout@v3 + + - id: setup_python + name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - id: devtool_requirements + name: Install devtool requirements + run: pip install -r ./devtools/requirements.txt + + - id: prerequisites + name: Install prerequisites + run: | + sudo apt-get update + sudo apt-get -y install \ + bison \ + ccache \ + flex \ + gcc \ + git \ + gperf \ + libffi-dev \ + libncurses-dev \ + libssl-dev \ + make \ + wget + + - name: Set environment variables + id: set_env + run: | + SDK_NAME="ESP8266_RTOS_SDK" + GCC_PREFIX="xtensa-lx106-elf" + GCC_FILE="${GCC_PREFIX}-gcc" + TOOLCHAIN_DIR="${HOME}/.espressif/tools" + REPO_DIR=`pwd` + EXAMPLE_DIR="${REPO_DIR}/examples" + __PROJECT_PATH=`pwd` + __PROJECT_TOOLCHAIN_VERSION="esp-2020r3-49-gd5524c1-8.4.0" + + # XXX actions/checkout does not allow to checkout a repository other + # than under __PROJECT_PATH + IDF_PATH="${__PROJECT_PATH}/idf" + + echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV} + echo "IDF_TARGET=esp8266" >> ${GITHUB_ENV} + + # cache-idf-tools needs PROJECT_TOOLCHAIN_DIR + echo "PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}" >> $GITHUB_OUTPUT + + # XXX prefix all the environment variables with `__PROJECT_` to avoid pollution + echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV} + echo "__PROJECT_GCC_FILE=${GCC_FILE}" >> ${GITHUB_ENV} + echo "__PROJECT_GCC_PREFIX=${GCC_PREFIX}" >> ${GITHUB_ENV} + echo "__PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}" >> ${GITHUB_ENV} + echo "__PROJECT_PATH=${__PROJECT_PATH}" >> ${GITHUB_ENV} + echo "__PROJECT_BUILD_COMMAND=${__PROJECT_BUILD_COMMAND}" >> ${GITHUB_ENV} + echo "__PROJECT_BUILD_COMMAND_ARG=${__PROJECT_BUILD_COMMAND_ARG}" >> ${GITHUB_ENV} + echo "__PROJECT_TOOLCHAIN_VERSION=${__PROJECT_TOOLCHAIN_VERSION}" >> ${GITHUB_ENV} + + - name: Checkout the SDK + uses: actions/checkout@v3 + with: + repository: espressif/ESP8266_RTOS_SDK + path: idf + submodules: recursive + ref: ${{ matrix.branch }} + fetch-depth: 0 + + - name: Install python requirements (pip) + run: | + python -m pip install --user -r ${IDF_PATH}/requirements.txt + + - id: cache-idf-tools + name: Cache toolchain + if: ${{ matrix.branch != 'master' }} + uses: actions/cache@v3 + with: + path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }} + key: ${{ runner.os }}-${{ matrix.branch }}-esp8266-cache-tools + + - name: Install toolchain + if: ${{ steps.cache-idf-tools.outputs.cache-hit != 'true' || matrix.branch == 'master' }} + run: | + ${IDF_PATH}/install.sh + + - name: Setup ccache (make) + run: | + __PROJECT_CCACHE_BIN_DIR="${HOME}/ccache_bin" + mkdir -p "${__PROJECT_CCACHE_BIN_DIR}" + (cd "${__PROJECT_CCACHE_BIN_DIR}" && ln -s /usr/bin/ccache "${__PROJECT_GCC_FILE}") + echo "PATH=${__PROJECT_CCACHE_BIN_DIR}:$PATH:${__PROJECT_TOOLCHAIN_DIR}/${__PROJECT_GCC_PREFIX}/${__PROJECT_TOOLCHAIN_VERSION}/${__PROJECT_GCC_PREFIX}/bin" >> ${GITHUB_ENV} + echo "CCACHE_BASEDIR=${__PROJECT_EXAMPLE_DIR}" >> ${GITHUB_ENV} + echo "CCACHE_NOHASHDIR=true" >> ${GITHUB_ENV} + + - name: Build (make) + if: ${{ matrix.build_method == 'make' }} + run: | + # make sure gcc is in $PATH + echo ${PATH} + ${__PROJECT_GCC_FILE} --version + + # find out drivers that do not support the target + EXCLUDE_COMPONENTS="$(python ./devtools/devtool.py target esp8266 -x)" + echo "EXCLUDE_COMPONENTS=${EXCLUDE_COMPONENTS}" + export EXCLUDE_COMPONENTS + + components_to_build="${{ needs.prepare.outputs._ci_build_esp8266 }}" + + # XXX share cache between examples. + # see "Compiling In Different Directories" in ccache(1) + # | | 4.0.1 | master | + # |----------------------------------------|---------|---------| + # | without ccache | 33m 42s | 50m 27s | + # | CCACHE_BASEDIR and CCACHE_NOHASHDIR | 10m 41s | 16m 38s | + export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}" + export CCACHE_NOHASHDIR=true + + echo "final components_to_build:" + echo "${components_to_build}" + for component_to_build in ${components_to_build}; do + cd ${GITHUB_WORKSPACE} + echo "Building examples for ${component_to_build} under directory ${__PROJECT_EXAMPLE_DIR}/${component_to_build}" + cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}" + for i in $(ls -d *); do + cd "${__PROJECT_EXAMPLE_DIR}/${component_to_build}/${i}" + echo "Building example ${i} for component ${component_to_build} in directory ${PWD}..." + make defconfig + make -j$(nproc) + done + done diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml deleted file mode 100644 index 5ad9d7c8..00000000 --- a/.github/workflows/doc.yml +++ /dev/null @@ -1,59 +0,0 @@ ---- -name: "Build the documentation" -on: - pull_request: - types: - - labeled - - synchronize - -jobs: - pre_build: - runs-on: ubuntu-latest - outputs: - should_skip: ${{ steps.skip_check.outputs.result }} - steps: - - id: skip_check - uses: actions/github-script@v6 - with: - result-encoding: string - script: | - console.log("context"); - console.log(JSON.stringify(context, null, 2)); - let should_skip = false; - - switch(context.payload.action) { - case "labeled": - if (context.payload.label.name != "area:docs") { - should_skip = true; - } - if (context.payload.label.name == "area:ci") { - should_skip = false; - } - break; - case "synchronize": - let labels = context.payload.pull_request.labels.map(label => { return label.name }); - if (!labels.includes("area:docs")) { - should_skip = true; - } - if (labels.includes("area:ci")) { - should_skip = false; - } - break; - } - return should_skip; - docs: - runs-on: ubuntu-latest - needs: pre_build - if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install python3-sphinx python3-sphinx-rtd-theme python3-breathe doxygen - - - name: Build docs - run: | - make -C docs diff --git a/.github/workflows/labeler-by-workflow-status.yml b/.github/workflows/labeler-by-workflow-status.yml deleted file mode 100644 index 86722255..00000000 --- a/.github/workflows/labeler-by-workflow-status.yml +++ /dev/null @@ -1,84 +0,0 @@ ---- -name: Label PRs by workflow conclusion -on: - workflow_run: - # when the workflows listed here complete, this workflow runs - workflows: - - Build examples - - Build the documentation - - Metadata - - types: - - completed - -jobs: - on-completed: - runs-on: ubuntu-latest - steps: - - name: Dump context for debugging - uses: actions/github-script@v6 - with: - script: | - // steps receive a context from the workflow_run event - // see: - // https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_run - console.log(JSON.stringify(context, null, 2)) - - on-success: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} - steps: - - name: Remove failure label - uses: actions/github-script@v6 - with: - script: | - const util = require("util"); - - label_failure_name = util.format("ci/%s/failure", context.payload.workflow_run.name); - - // when pull_requests.length is zero, that means the workflow_run - // has no PR, e.g. workflow_run was invoked in `master`, and no - // PR to label. - if (context.payload.workflow_run.pull_requests.length == 0) { - return; - } - try { - await github.rest.issues.removeLabel({ - issue_number: context.payload.workflow_run.pull_requests[0].number, - owner: context.repo.owner, - repo: context.repo.repo, - name: label_failure_name - }); - } catch(e) { - if (!e.message.includes('Label does not exist')) { - core.error(e); - core.setFailed(e.message); - } - } - - on-failure: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'failure' }} - steps: - - name: Add failure label - uses: actions/github-script@v6 - with: - script: | - const util = require("util"); - - label_failure_name = util.format("ci/%s/failure", context.payload.workflow_run.name); - - if (context.payload.workflow_run.pull_requests.length == 0) { - return; - } - try { - await github.rest.issues.addLabels({ - issue_number: context.payload.workflow_run.pull_requests[0].number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: [label_failure_name] - }); - } catch(e) { - core.error(e); - core.setFailed(e.message); - } diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml deleted file mode 100644 index 6048551c..00000000 --- a/.github/workflows/labeler.yml +++ /dev/null @@ -1,33 +0,0 @@ ---- -name: Label PR -on: - # use pull_request_target here to label PRs. from the documentation: - # - # "This event runs in the context of the base of the pull request, rather - # than in the context of the merge commit, as the pull_request event does" - # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target - # - # this workflow is potentially dangerous because the workflow has access to - # GitHub APIs, i.e. secrets.GITHUB_TOKEN, and the author of PRs, who might - # not be a member of the repository, can modify the workflow. - # - # see also: - # Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests - # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ - pull_request_target: - -# this workflow needs write access to PRs because it add labels to PRs, or -# remove labels from PRs. -permissions: - contents: read - pull-requests: write - -jobs: - labeler: - name: Labeler - runs-on: ubuntu-latest - steps: - - uses: fuxingloh/multi-labeler@v1 - with: - github-token: ${{secrets.LABELER_TOKEN}} - config-path: .github/labeler.yml diff --git a/.github/workflows/metadata.yml b/.github/workflows/metadata.yml deleted file mode 100644 index 78aab4e1..00000000 --- a/.github/workflows/metadata.yml +++ /dev/null @@ -1,61 +0,0 @@ ---- -name: Metadata -on: - pull_request: - types: - - labeled - - synchronize - -jobs: - pre_build: - runs-on: ubuntu-latest - outputs: - should_skip: ${{ steps.skip_check.outputs.result }} - steps: - - id: skip_check - uses: actions/github-script@v6 - with: - result-encoding: string - script: | - console.log("context"); - console.log(JSON.stringify(context, null, 2)); - let should_skip = false; - - switch(context.payload.action) { - case "labeled": - if (context.payload.label.name != "area:components") { - should_skip = true; - } - if (context.payload.label.name == "area:ci") { - should_skip = false; - } - break; - case "synchronize": - let labels = context.payload.pull_request.labels.map(label => { return label.name }); - if (!labels.includes("area:components")) { - should_skip = true; - } - if (labels.includes("area:ci")) { - should_skip = false; - } - break; - } - return should_skip; - test: - runs-on: ubuntu-latest - needs: pre_build - if: ${{ needs.pre_build.outputs.should_skip != 'true' }} - steps: - - uses: actions/checkout@v2 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.7 - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - - run: bundle exec rake -C devtools - - run: | - # Test if README.md is up-to-date with the metadata. If this test - # fails, update README.md by: - # - # bundle exec rake -C devtools readme > README.md - bundle exec rake -C devtools readme > README.md - git diff --exit-code README.md diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml deleted file mode 100644 index 08f5b093..00000000 --- a/.github/workflows/release-drafter.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Release Drafter -on: - push: - branches: - - main - -jobs: - update_release_draft: - runs-on: ubuntu-latest - steps: - - uses: release-drafter/release-drafter@v5 - with: - config-name: release-drafter.yml - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index f707810e..18d8846f 100644 --- a/.gitignore +++ b/.gitignore @@ -62,7 +62,7 @@ dkms.conf /__garbage__/ **/.vscode/ **/.devcontainer -/.idea/ +.idea/ cmake-build-debug/ /esp-idf-lib.code-workspace @@ -71,3 +71,10 @@ Gemfile.lock # macOS .DS_Store and .AppleDouble files .DS_Store .AppleDouble + +# bundled files by ruby bundler and configuration +/.bundle/ +/vendor/ + +# python cache +__pycache__/ diff --git a/.readthedocs.yml b/.readthedocs.yml index 01dce747..5541f991 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -5,20 +5,16 @@ # Required version: 2 +build: + os: ubuntu-22.04 + tools: + python: "3.12" + # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/source/conf.py -# Build documentation with MkDocs -#mkdocs: -# configuration: mkdocs.yml - -# Optionally build your docs in additional formats such as PDF and ePub -#formats: -# - pdf - # Optionally set the version of Python and requirements required to build your docs python: - version: 3.7 install: - requirements: docs/requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index e2f9b063..1f332dd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,77 @@ # Changelog +## v.0.9.4 + +### Features +- (sgm58031): Driver for SGM58031 16-bit I2C ADC by @jmpmscorp in https://github.com/UncleRus/esp-idf-lib/pull/511 +- (sts3x): Driver for Sensirion STS30/STS31/STS35 digital temperature sensor by @slimcdk in https://github.com/UncleRus/esp-idf-lib/pull/532 +- (mpu6050): Driver for MPU6000/MPU6050 6-axis MotionTracking device by @horsemann07 in https://github.com/UncleRus/esp-idf-lib/pull/455 +- (max1704x): Driver for MAX17043/MAX17044/MAX17048/MAX17049 battery fuel gauge by @shuki25 in https://github.com/UncleRus/esp-idf-lib/pull/542 +- (calibration): Multi-point calibration library by @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/545 + +### Bugfixes +- (veml7700): Fixed wrong resolution divider by @Throows in https://github.com/UncleRus/esp-idf-lib/pull/534 +- (docs): Fixed section in docs for max7219 by @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/529 +- (mcp23x17): Fixed function name in mcp23x17.c to match the one in the header by @oisalb in https://github.com/UncleRus/esp-idf-lib/pull/536 +- (tsl2591): Added sensor init to default tsl2591 example by @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/541 +- (ci): New devtool, simpler metadata, simpler CI by @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/546 +- (docs): Fixed doxygen comments and docs, updated doxygen config by @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/553 + +## New Contributors +* @slimcdk made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/532 +* @Throows made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/534 +* @oisalb made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/536 +* @horsemann07 made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/455 + +### Documentation: https://esp-idf-lib.readthedocs.io/en/0.9.4/ + +## v.0.9.3 + +### Features +- (icm42670): Driver for TDK ICM-42670-P 6-Axis IMU by @janveeh in https://github.com/UncleRus/esp-idf-lib/pull/485 +- (veml7700): Driver for ambient light sensor VEML7700 by @Th3Link in https://github.com/UncleRus/esp-idf-lib/pull/492 +- (am2320): Driver for temperature and humidity sensor AM2320 (I2C mode) by @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/508 + +### Bugfixes +- (ci): Added esp-idf 5.x build to CI by @trombik in https://github.com/UncleRus/esp-idf-lib/pull/483 +- (hx711): Fixed signed division of HX711 average read by @gaialucas in https://github.com/UncleRus/esp-idf-lib/pull/487 +- (esp_idf_lib_helpers): Added support for esp32c2 by @vaemc in https://github.com/UncleRus/esp-idf-lib/pull/488 +- (esp_idf_lib_helpers): Added support for esp32c6 by @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/524 +- (noise): Added cpp guards by @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/490 +- (doc): Fixed multiple documentation errors @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/493 +- (encoder): Fixed incorrect pin configuration by @klaasjanhorlings in https://github.com/UncleRus/esp-idf-lib/pull/526 +- (si7021): Fixed reversed bytes in command words by @stvnjns and @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/527 + +## New Contributors +* @gaialucas made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/487 +* @vaemc made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/488 +* @janveeh made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/485 +* @Th3Link made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/492 +* @klaasjanhorlings made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/526 + +### Documentation: https://esp-idf-lib.readthedocs.io/en/0.9.3/ + +## v.0.9.2 + +### Features +- (ds3231): Added ds3231_get_squarewave_freq by @dizcza in https://github.com/UncleRus/esp-idf-lib/pull/447 +- (ads130e08): Driver for ADS130E08 ADC by @weslleymfd in https://github.com/UncleRus/esp-idf-lib/pull/462 +- (dps310): DPS310 driver by @trombik in https://github.com/UncleRus/esp-idf-lib/pull/463 + +### Bugfixes +- (pca9557): Fixed incorrect I2C address and register bug by @AxelLin in https://github.com/UncleRus/esp-idf-lib/pull/453 +- (max7219) Fix string bounds check by @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/471 +- (ci) Updated esp-idf versions by @trombik +- (ci) Updated actions/checkout to v3 by @trombik in https://github.com/UncleRus/esp-idf-lib/pull/476 +- (esp_idf_lib_helpers): Fixed ets_sys.h error by @UncleRus in https://github.com/UncleRus/esp-idf-lib/pull/479 +- (i2cdev): Fixed i2c param config and driver install order for esp32 target by @AxelLin in https://github.com/UncleRus/esp-idf-lib/pull/475 + +## New Contributors +* @AxelLin made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/453 +* @weslleymfd made their first contribution in https://github.com/UncleRus/esp-idf-lib/pull/462 + +### Documentation: https://esp-idf-lib.readthedocs.io/en/0.9.2/ + ## v.0.9.1 ### Features diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 27571111..6964031b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -456,10 +456,10 @@ for the supported keywords. Each component has a `.eil.yml` file in its component directory. The file is a metadata file of the component. If you change the file, you need to update the `README.md` in the project root directory. The `README.md` is generated from -the metadata and a template, `README.md.erb`. Generate `README.md` by: +the metadata and a template. ```console -bundle exec rake -C devtools readme > README.md +./devtools/devtool.py --repo=. render ``` See also [`Metadata.md`](Metadata.md). diff --git a/FAQ.md b/FAQ.md index c546bb4b..db7e1b97 100644 --- a/FAQ.md +++ b/FAQ.md @@ -9,6 +9,8 @@ * [How to use internal pull-up resistors](#how-to-use-internal-pull-up-resistors) * [Can I use I2C device drivers from interrupts?](#can-i-use-i2c-device-drivers-from-interrupts) * [Porting I2C libs to I2Cdev](#porting-i2c-libs-to-i2cdev) +* [My DHT sensor doesn't work well/doesn't work at all.](#my-dht-sensor-doesnt-work-welldoesnt-work-at-all) +* [How can I include ets_sys.h in my code without complex macros and remain compatible with different targets?](#how-can-i-include-ets_sysh-in-my-code-without-complex-macros-and-remain-compatible-with-different-targets) @@ -24,7 +26,7 @@ Common causes of I2C issues are: When any of I2C-based drivers does not work, follow the steps below. -Build an [_I2C scanner_ device](examples/i2c_scanner). The device is not +Build an [_I2C scanner_ device](examples/i2cdev/default). The device is not necessarily an ESP device. There are many examples for various platforms. Search by keyword `i2c scanner`. @@ -142,4 +144,18 @@ after enabling this option all i2c device drivers will become non-thread safe. ## Porting I2C libs to I2Cdev -See [porting.md](docs/porting.md). +See [Porting.md](Porting.md). + + +## My DHT sensor doesn't work well/doesn't work at all! + +1. Check if the sensor is connected correctly. +2. Use an external 4k7 pullup resistor! With an internal pullup resistor, operation will be unstable, if at all. +3. Shorten the wires that connect the sensor. +4. Use 5V for powering the sensor, not 3.3V. ESP chips are 5V tolerant. +5. Use shielded wires where the shield is connected to the GND. + +## How can I include ets_sys.h in my code without complex macros and remain compatible with different targets? + +Just add the `esp_idf_lib_helpers` component to the list of required ones and after that you can simply write `#include ` in your code. +See, for example, source code of `scd30` component. diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 5ae946d0..00000000 --- a/Gemfile +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -gem "irb" -gem "rake" -gem "rspec" -gem "rubocop" -gem "io-console", ">= 0.5.11" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index d97be9be..00000000 --- a/Gemfile.lock +++ /dev/null @@ -1,58 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - ast (2.4.2) - diff-lcs (1.4.4) - io-console (0.5.11) - irb (1.3.7) - reline (>= 0.2.7) - parallel (1.21.0) - parser (3.0.2.0) - ast (~> 2.4.1) - rainbow (3.0.0) - rake (13.0.6) - regexp_parser (2.1.1) - reline (0.2.7) - io-console (~> 0.5) - rexml (3.2.5) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-mocks (3.10.2) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-support (3.10.3) - rubocop (1.22.3) - parallel (~> 1.10) - parser (>= 3.0.0.0) - rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml - rubocop-ast (>= 1.12.0, < 2.0) - ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.13.0) - parser (>= 3.0.1.1) - ruby-progressbar (1.11.0) - unicode-display_width (2.1.0) - -PLATFORMS - amd64-freebsd-14 - ruby - x86_64-linux - -DEPENDENCIES - io-console (>= 0.5.11) - irb - rake - rspec - rubocop - -BUNDLED WITH - 2.2.19 diff --git a/Metadata.md b/Metadata.md index e7e54173..fc5cfc6e 100644 --- a/Metadata.md +++ b/Metadata.md @@ -62,15 +62,15 @@ Resources defined here represents various objects used in the metadata. A resource has unique `name` as a primary key. -When referring to a resource in another resource, use `name` as key and its +When referring to a resource in another resource, use `name` as value to identify the resource. As a shorthand, you may use the name of a -resource as `String`. In this case, the value is assumed to be `name: $VALUE`. +resource as `String`. In this case, the value is assumed to be `$VALUE`. When a resource expects a `Person` as a value, ```yaml foo: - name: trombik + - trombik ``` This is a shorthand version of the above example: @@ -84,13 +84,13 @@ foo: trombik A `Person` represents a person. `Person` is used to describe a copyrights holder and a code owner. A `Person` must be defined in `persons.yml` file. -| Name | Type | Description | Required | -|------|------|-------------|----------| -| `name` | `String` | A unique ID string of the person. Use GitHub account or GitHub project if the person has one | Yes | -| `full_name` | `String` | Full name of the person or the project | No | -| `gh_id` | `String` | GitHub account name or project name | No | -| `email` | `String` | Email address of the person | No | -| `website` | `String` | Web site URL | No | +| Name | Type | Description | Required | +|-------------|----------|----------------------------------------------------------------------------------------------|----------| +| `name` | `String` | A unique ID string of the person. Use GitHub account or GitHub project if the person has one | Yes | +| `full_name` | `String` | Full name of the person or the project | No | +| `gh_id` | `String` | GitHub account name or project name | No | +| `email` | `String` | Email address of the person | No | +| `url` | `String` | Web site URL | No | When any of `gh_id`, `email`, or `website` is not available, `person` must have a full name because it is used to identify the source of code. @@ -108,7 +108,7 @@ name: trombik gh_id: trombik full_name: Tomoyuki Sakurai email: y@trombik.org -website: https://github.com/trombik +url: https://github.com/trombik ``` ```yaml @@ -119,9 +119,9 @@ full_name: Foo `bar` buz ### Target -| Name | Type | Description | Required | -|------|------|-------------|----------| -| `name` | `String` | Name of the build target in `esp-idf`, or `esp8266`. | Yes | +| Name | Type | Description | Required | +|--------|----------|------------------------------------------------------|----------| +| `name` | `String` | Name of the build target in `esp-idf`, or `esp8266`. | Yes | An example: @@ -131,9 +131,9 @@ name: esp32 ### License -| Name | Type | Description | Required | -|------|------|-------------|----------| -| `name` | `String` | SPDX License Identifier (see [the list of licenses](https://spdx.org/licenses/)) | Yes | +| Name | Type | Description | Required | +|--------|----------|----------------------------------------------------------------------------------|----------| +| `name` | `String` | SPDX License Identifier (see [the list of licenses](https://spdx.org/licenses/)) | Yes | An example: @@ -143,11 +143,11 @@ name: BSD-3 ### Copyright -| Name | Type | Description | Required | -|------|------|-------------|----------| -| `author` | `Person` | Copyrights holder. See also `Person`. | No | -| `name` | `String` | The value of `name` of `Person`. A shorthand for `author` | No | -| `year` | `Integer` | Registration year of the copyrights | Yes | +| Name | Type | Description | Required | +|----------|-----------|-----------------------------------------------------------|----------| +| `author` | `Person` | Copyrights holder. See also `Person`. | No | +| `name` | `String` | The value of `name` of `Person`. A shorthand for `author` | No | +| `year` | `Integer` | Registration year of the copyrights | Yes | `Copyright` must have only one of `author` and `name`, not both. @@ -171,10 +171,10 @@ year: 2021 A `Group` represents a group of `Component`s. A `Group` must be in `groups.yml`. -| Name | Type | Description | Required | -|------|------|-------------|----------| -| `name` | `String` | A unique ID of the group | Yes | -| `description` | `String` | Description of the group | Yes | +| Name | Type | Description | Required | +|---------------|----------|--------------------------|----------| +| `name` | `String` | A unique ID of the group | Yes | +| `description` | `String` | Description of the group | Yes | `name` should be short, and memorable. Use `-` as a word separator. It must not include spaces (`[0-9a-zA-Z-]+` in regular expression). @@ -202,36 +202,32 @@ components: ### Component -| Name | Type | Description | Required | -|------|------|-------------|----------| -| `name` | `String` | The name of the component. Must be unique. | Yes | -| `description` | `String` | A short description of the component. | Yes | -| `group` | `Group` | The primary group name of the component. | Yes | -| `groups` | A list of `Group` | A list of zero or more of `Group` | No | -| `code_owners` | A list of `Person` | A list of one or more of `Person` | Yes | -| `depends` | A list of `Component` | Zero or more of `component` that the component depends on | No | -| `thread_safe` | `Strnig` | One of `yes`, `no`, and `N/A` | Yes | -| `targets` | A list of `Target` | One or more of supported `target` | Yes | -| `licenses` | A list of `License` | One or more of licenses used in the component | Yes | -| `copyrights` | A list of `Copyright` | One or more of copyright holder | Yes | +| Name | Type | Description | Required | +|---------------|-----------------------|-----------------------------------------------------------|----------| +| `name` | `String` | The name of the component. Must be unique. | Yes | +| `description` | `String` | A short description of the component. | Yes | +| `version` | `String` | Component version. | Yes | +| `groups` | A list of `Group` | A list of one or more of `Group` | No | +| `code_owners` | A list of `Person` | A list of one or more of `Person` | Yes | +| `depends` | A list of `Component` | Zero or more of `component` that the component depends on | No | +| `thread_safe` | `Strnig` | One of `yes`, `no`, and `N/A` | Yes | +| `targets` | A list of `Target` | One or more of supported `target` | Yes | +| `license` | `License` | License used in the component | Yes | +| `copyrights` | A list of `Copyright` | One or more of copyright holder | Yes | FIXME `depends` must be a list because some drivers have conditional `REQUIRES` in `CMakeLists.txt`. ## Usages of metadata in the project -The current implementation uses `ruby` and `rspec` ruby gem to validate -metadata in all components, and generate `README.md`. - Requirements are: -* `ruby` 2.7 (other version should also work) -* [`bundler`](https://bundler.io/) +* `python` >=3.10 After installing requirements, run: ```console -bundle install +pip install -r devtool/requirements.txt ``` ### Validating metadata of components @@ -239,34 +235,17 @@ bundle install To validate metadata, run: ```console -bundle exec rake -C devtools rspec +python ./devtools/devtool.py check ``` -The implementation uses `rspec` to validate metadata because: - -1. the output is readable -2. requires less `ruby` knowledge to maintain the spec than validating - everything in ruby code -3. porting tests to other languages is easier than porting ruby code - -Under `spec` directory, there are: - -* `spec_helper.rb`, which is a helper for the test -* `*_spec.rb`, which is a test script -* other ruby files, such as `person.rb`, which are class definitions used in - the test - -The ruby classes for the test validate minimum requirements only, such as the -`.eil.yml` file exists, or a resource has a required primary key. Actual -test should be performed in `*_spec.rb` files. ### Generating `README.md` -`README.md` is generated from the metadata and `README.md.erb`. To update +`README.md` is generated from the metadata and `devtools/devtools/template/README.md`. To update `README.md`, run the following command at the repository root directory: ```console -bundle exec rake -C devtools readme > README.md +python ./devtools/devtool.py render ``` ## Known issues @@ -279,17 +258,17 @@ the following case. ```yaml # for esp32 depends: - - name: driver - - name: freertos - - name: log + - driver + - freertos + - log ``` ```yaml # for esp8266 depends: - - name: esp8266 - - name: freertos - - name: log + - esp8266 + - freertos + - log ``` A possible solution: @@ -298,12 +277,12 @@ A possible solution: depends: - name: driver target: - - name: esp32 - - name: esp32s2 - - name: esp32c3 + - esp32 + - esp32s2 + - esp32c3 - name: esp8266 target: - - name: esp8266 - - name: freertos - - name: log + - esp8266 + - freertos + - log ``` diff --git a/docs/porting.md b/Porting.md similarity index 100% rename from docs/porting.md rename to Porting.md diff --git a/README.md b/README.md index 1c791c3d..611e3a22 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # ESP-IDF Components library -[![Build Status](https://github.com/UncleRus/esp-idf-lib/workflows/Build%20examples/badge.svg)](https://github.com/UncleRus/esp-idf-lib/actions?query=workflow%3A%22Build+examples%22) -[![Build the documentation](https://github.com/UncleRus/esp-idf-lib/workflows/Build%20the%20documentation/badge.svg)](https://github.com/UncleRus/esp-idf-lib/actions?query=workflow%3A%22Build+the+documentation%22) +[![Main CI process](https://github.com/UncleRus/esp-idf-lib/actions/workflows/ci.yml/badge.svg)](https://github.com/UncleRus/esp-idf-lib/actions/workflows/ci.yml) [![Docs Status](https://readthedocs.org/projects/esp-idf-lib/badge/?version=latest&style=flat)](https://esp-idf-lib.readthedocs.io/en/latest/) Components for Espressif ESP32 [ESP-IDF framework](https://github.com/espressif/esp-idf) @@ -11,22 +10,16 @@ Part of them ported from [esp-open-rtos](https://github.com/SuperHouse/esp-open- ## Supported versions of frameworks and devices -| Chip | Framework | Versions -|----------------|--------------------|----------------------- -| ESP32 | ESP-IDF | All officially supported versions (see [Support Period Policy](https://github.com/espressif/esp-idf/blob/master/SUPPORT_POLICY.md)) and `master` -| ESP32-S2 *[1]* | ESP-IDF | All officially supported versions and `master` -| ESP32-C3 *[1]* | ESP-IDF | All officially supported versions and `master` -| ESP8266 *[2]* | ESP8266 RTOS SDK | `master`, v3.4 +| Chip | Framework | Versions | +|----------|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| ESP32-xx | ESP-IDF | All officially supported versions (see [Support Period Policy](https://github.com/espressif/esp-idf/blob/master/SUPPORT_POLICY.md)) and `master` | +| ESP8266 | ESP8266 RTOS SDK | `master`, v3.4 | -[1] *Use "`idf.py set-target esp32s2`" or "`idf.py set-target esp32c3`" before "`idf.py menuconfig`" to change -the chip type.* - -[2] *Due to the incompatibility of ESP8266 drivers and hardware, some -libraries are not* *supported on ESP8266 (see "ESP8266" column in the tables).* +*See "Supported on" column for each of the components.* ## How to use -### ESP32 +### ESP32-xx Clone this repository somewhere, e.g.: @@ -35,17 +28,9 @@ cd ~/myprojects/esp git clone https://github.com/UncleRus/esp-idf-lib.git ``` -Add path to components in your [project makefile](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system-legacy.html), +Add path to components in your [CMakeLists.txt](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html): e.g: -```Makefile -PROJECT_NAME := my-esp-project -EXTRA_COMPONENT_DIRS := /home/user/myprojects/esp/esp-idf-lib/components -include $(IDF_PATH)/make/project.mk -``` - -or in [CMakeLists.txt](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html): - ```CMake cmake_minimum_required(VERSION 3.5) set(EXTRA_COMPONENT_DIRS /home/user/myprojects/esp/esp-idf-lib/components) @@ -99,168 +84,217 @@ or [GitLab examples](https://gitlab.com/UncleRus/esp-idf-lib/tree/master/example ### ADC/DAC libraries -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **ads111x** | Driver for ADS1113/ADS1114/ADS1115 and ADS1013/ADS1014/ADS1015 I2C ADC | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **hx711** | Driver for HX711 24-bit ADC for weigh scales | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No -| **mcp342x** | Driver for 18-Bit, delta-sigma ADC MCP3426/MCP3427/MCP3428 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **mcp4725** | Driver for 12-bit DAC MCP4725 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **pcf8591** | Driver for 8-bit ADC and an 8-bit DAC PCF8591 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **ads111x** | Driver for ADS1113/ADS1114/ADS1115 and ADS1013/ADS1014/ADS1015 I2C ADC | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **ads130e08** | Driver for ADS130E08 ADC | MIT | esp32, esp32s3 | yes | +| **hx711** | Driver for HX711 24-bit ADC for weigh scales | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | no | +| **mcp342x** | Driver for 18-Bit, delta-sigma ADC MCP3426/MCP3427/MCP3428 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **mcp4725** | Driver for 12-bit DAC MCP4725 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **pcf8591** | Driver for 8-bit ADC and an 8-bit DAC PCF8591 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **sgm58031** | Driver for SGM58031 16-bit I2C ADC | ISC | esp32, esp8266, esp32s2, esp32s3, esp32c3 | yes | + ### Air quality sensors -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **ccs811** | Driver for AMS CCS811 digital gas sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **mhz19b** | Driver for MH-Z19B NDIR CO₂ sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No -| **scd30** | Driver for SCD30 CO₂ sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **scd4x** | Driver for SCD40/SCD41 miniature CO₂ sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **sgp40** | Driver for SGP40 Indoor Air Quality Sensor for VOC Measurements | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **ccs811** | Driver for AMS CCS811 digital gas sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **mhz19b** | Driver for MH-Z19B NDIR CO₂ sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | no | +| **scd30** | Driver for SCD30 CO₂ sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **scd4x** | Driver for SCD40/SCD41 miniature CO₂ sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **sfa3x** | Driver for SFA30 formaldehyde detection module (I2C) | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **sgp40** | Driver for SGP40 Indoor Air Quality Sensor for VOC Measurements | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | + + +### Battery controllers + +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **lc709203f** | Driver for LC709203F battery fuel gauge | ISC | esp32, esp8266, esp32s2, esp32c3 | yes | +| **max1704x** | Driver for MAX17043/MAX17044/MAX17048/MAX17049 battery fuel gauge | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **mp2660** | Driver for MP2660 5V USB, 500mA, I2C-Controlled Linear Charger with Power Path Management for Single-Cell Li-Ion Battery | BSD-3-Clause | esp32, esp32s2, esp32c3 | yes | + ### Common libraries -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **color** | Common library for RGB and HSV colors | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **esp_idf_lib_helpers** | Common support library for esp-idf-lib | ISC | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **framebuffer** | RGB framebuffer component | MIT | `esp32`, `esp32s2`, `esp32c3` | Yes -| **i2cdev** | ESP-IDF I2C master thread-safe utilities | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **lib8tion** | Math functions specifically designed for LED programming | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **noise** | Noise generation functions | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **onewire** | Bit-banging 1-Wire driver | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **calibration** | Multi-point calibration library | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | n/a | +| **color** | Common library for RGB and HSV colors | MIT | esp32, esp8266, esp32s2, esp32c3 | n/a | +| **esp_idf_lib_helpers** | Common support library for esp-idf-lib | ISC | esp32, esp8266, esp32s2, esp32c3 | n/a | +| **framebuffer** | RGB framebuffer component | MIT | esp32, esp32s2, esp32c3 | n/a | +| **i2cdev** | ESP-IDF I2C master thread-safe utilities | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **lib8tion** | Math functions specifically designed for LED programming | MIT | esp32, esp8266, esp32s2, esp32c3 | n/a | +| **noise** | Noise generation functions | MIT | esp32, esp8266, esp32s2, esp32c3 | n/a | +| **onewire** | Bit-banging 1-Wire driver | MIT | esp32, esp8266, esp32s2, esp32c3 | no | -### Current and power sensors -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **ina219** | Driver for INA219/INA220 bidirectional current/power monitor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **ina260** | Driver for INA260 precision digital current and power monitor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **ina3221** | Driver for INA3221 shunt and bus voltage monitor | ISC | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes +### Current and power sensors -### GPIO expanders +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **ina219** | Driver for INA219/INA220 bidirectional current/power monitor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **ina260** | Driver for INA260 precision digital current and power monitor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **ina3221** | Driver for INA3221 shunt and bus voltage monitor | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **mcp23008** | Driver for 8-bit I2C GPIO expander MCP23008 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **mcp23x17** | Driver for I2C/SPI 16 bit GPIO expanders MCP23017/MCP23S17 | BSD-3 | `esp32`, `esp32s2`, `esp32c3` | Yes -| **pca9557** | Driver for PCA9537/PCA9557/TCA9534 remote 4/8-bit I/O expanders for I2C-bus | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **pcf8574** | Driver for PCF8574 remote 8-bit I/O expander for I2C-bus | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **pcf8575** | Driver for PCF8575 remote 16-bit I/O expander for I2C-bus | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **tca95x5** | Driver for TCA9535/TCA9555 remote 16-bit I/O expanders for I2C-bus | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes ### Gas sensors -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **ccs811** | Driver for AMS CCS811 digital gas sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **mhz19b** | Driver for MH-Z19B NDIR CO₂ sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No -| **scd30** | Driver for SCD30 CO₂ sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **scd4x** | Driver for SCD40/SCD41 miniature CO₂ sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **ccs811** | Driver for AMS CCS811 digital gas sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **mhz19b** | Driver for MH-Z19B NDIR CO₂ sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | no | +| **scd30** | Driver for SCD30 CO₂ sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **scd4x** | Driver for SCD40/SCD41 miniature CO₂ sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **sfa3x** | Driver for SFA30 formaldehyde detection module (I2C) | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | + + +### GPIO expanders + +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **mcp23008** | Driver for 8-bit I2C GPIO expander MCP23008 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **mcp23x17** | Driver for I2C/SPI 16 bit GPIO expanders MCP23017/MCP23S17 | BSD-3-Clause | esp32, esp32s2, esp32c3 | yes | +| **pca9557** | Driver for PCA9536/PCA9537/PCA9557/TCA9534 remote 4/8-bit I/O expanders for I2C-bus | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **pcf8574** | Driver for PCF8574 remote 8-bit I/O expander for I2C-bus | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **pcf8575** | Driver for PCF8575 remote 16-bit I/O expander for I2C-bus | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **tca6424a** | Driver for TCA6424A low-voltage 24-bit I2C I/O expander | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **tca95x5** | Driver for TCA9535/TCA9555 remote 16-bit I/O expanders for I2C-bus | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | + ### Humidity sensors -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **aht** | Driver for AHT10/AHT15/AHT20 temperature and humidity sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **bme680** | Driver for BME680 digital environmental sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **dht** | Driver for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), Itead Si7021 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No -| **hdc1000** | Driver for HDC1000 temperature and humidity sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **hts221** | Driver for HTS221 temperature and humidity sensor. | ISC | `esp32`, `esp32s2`, `esp32c3` | Yes -| **sht3x** | Driver for Sensirion SHT30/SHT31/SHT35 digital temperature and humidity sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **sht4x** | Driver for Sensirion SHT40/SHT41/SHT45 digital temperature and humidity sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **si7021** | Driver for Si7013/Si7020/Si7021/HTU2xD/SHT2x and compatible temperature and humidity sensors | BSD-3 | `esp32`, `esp32c3`, `esp8266`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **aht** | Driver for AHT10/AHT15/AHT20 temperature and humidity sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **am2320** | Driver for AM2320 temperature and humidity sensor (I2C) | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **bme680** | Driver for BME680 digital environmental sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **dht** | Driver for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), Itead Si7021 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | no | +| **hdc1000** | Driver for HDC1000 temperature and humidity sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **hts221** | Driver for HTS221 temperature and humidity sensor | ISC | esp32, esp32s2, esp32c3 | yes | +| **sfa3x** | Driver for SFA30 formaldehyde detection module (I2C) | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **sht3x** | Driver for Sensirion SHT30/SHT31/SHT35 digital temperature and humidity sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **sht4x** | Driver for Sensirion SHT40/SHT41/SHT45 digital temperature and humidity sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **si7021** | Driver for Si7013/Si7020/Si7021/HTU2xD/SHT2x and compatible temperature and humidity sensors | BSD-3-Clause | esp32, esp32c3, esp8266, esp32s2, esp32c3 | yes | + + +### Inertial measurement units + +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **icm42670** | Driver for TDK ICM-42670-P 6-Axis IMU | ISC | esp32, esp8266, esp32s2, esp32c3 | yes | +| **l3gx** | Driver for L3Gx(L3GD20/L3G4200D) 3-axis gyroscope sensors | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **lsm303** | Driver for LSM303 3-axis accelerometer and magnetometer sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **mpu6050** | Driver for MPU6000/MPU6050 6-axis MotionTracking device | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | + ### Input device drivers -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **button** | HW timer-based driver for GPIO buttons | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **encoder** | HW timer-based driver for incremental rotary encoders | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **ls7366r** | Driver for LS7366R Quadrature Encoder Counter | MIT | `esp32`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **button** | HW timer-based driver for GPIO buttons | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **encoder** | HW timer-based driver for incremental rotary encoders | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **ls7366r** | Driver for LS7366R Quadrature Encoder Counter | MIT | esp32, esp32s2, esp32c3 | yes | + ### LED drivers -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **ht16k33** | HT16K33 LED controller driver | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **led_strip** | RMT-based driver for WS2812B/SK6812/APA106/SM16703 LED strips | MIT | `esp32`, `esp32s2`, `esp32c3` | Yes -| **led_strip_spi** | SPI-based driver for SK9822/APA102 LED strips | MIT | `esp32`, `esp32c3`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **max7219** | Driver for 8-Digit LED display drivers, MAX7219/MAX7221 | BSD-3 | `esp32`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **ht16k33** | HT16K33 LED controller driver | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **led_strip** | RMT-based driver for WS2812B/SK6812/APA106/SM16703 LED strips | MIT | esp32, esp32s2, esp32c3 | yes | +| **led_strip_spi** | SPI-based driver for SK9822/APA102 LED strips | MIT | esp32, esp32c3, esp8266, esp32s2, esp32c3 | yes | +| **max7219** | Driver for 8-Digit LED display drivers, MAX7219/MAX7221 | BSD-3-Clause | esp32, esp32s2, esp32c3 | yes | + ### Light sensors -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **bh1750** | Driver for BH1750 light sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **tsl2561** | Driver for light-to-digital converter TSL2561 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **tsl2591** | Driver for light-to-digital converter TSL2591 | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **tsl4531** | Driver for digital ambient light sensor TSL4531 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **bh1750** | Driver for BH1750 light sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **tsl2561** | Driver for light-to-digital converter TSL2561 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **tsl2591** | Driver for light-to-digital converter TSL2591 | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **tsl4531** | Driver for digital ambient light sensor TSL4531 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **veml7700** | Driver for VEML7700 ambient light sensor | ISC | esp32, esp8266, esp32s2, esp32c3 | yes | + ### Magnetic sensors -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **hmc5883l** | Driver for 3-axis digital compass HMC5883L and HMC5983L | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **qmc5883l** | Driver for QMC5883L 3-axis magnetic sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **hmc5883l** | Driver for 3-axis digital compass HMC5883L and HMC5983L | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **lsm303** | Driver for LSM303 3-axis accelerometer and magnetometer sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **qmc5883l** | Driver for QMC5883L 3-axis magnetic sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | + ### Other misc libraries -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **ds3502** | Driver for nonvolatile digital potentiometer DS3502 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **example** | An example component | ISC | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **hd44780** | Driver for HD44780 compatible LCD text displays | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No -| **lc709203f** | Driver for LC709203F battery fuel gauge | ISC | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **pca9685** | Driver for 16-channel, 12-bit PWM PCA9685 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **rda5807m** | Driver for single-chip broadcast FM radio tuner RDA5807M | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **tca9548** | Driver for TCA9548A/PCA9548A low-voltage 8-channel I2C switch | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **tda74xx** | Driver for TDA7439/TDA7439DS/TDA7440D audioprocessors | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **ultrasonic** | Driver for ultrasonic range meters, e.g. HC-SR04, HY-SRF05 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No -| **wiegand** | Wiegand protocol receiver | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **ds3502** | Driver for nonvolatile digital potentiometer DS3502 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **example** | An example component | ISC | esp32, esp8266, esp32s2, esp32c3 | n/a | +| **hd44780** | Driver for HD44780 compatible LCD text displays | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | no | +| **pca9685** | Driver for 16-channel, 12-bit PWM PCA9685 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **rda5807m** | Driver for single-chip broadcast FM radio tuner RDA5807M | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **tca9548** | Driver for TCA9548A/PCA9548A low-voltage 8-channel I2C switch | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **tda74xx** | Driver for TDA7439/TDA7439DS/TDA7440D audioprocessors | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **ultrasonic** | Driver for ultrasonic range meters, e.g. HC-SR04, HY-SRF05 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | no | +| **wiegand** | Wiegand protocol receiver | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | no | + ### Pressure sensors -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **bme680** | Driver for BME680 digital environmental sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **bmp180** | Driver for BMP180 digital pressure sensor | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **bmp280** | Driver for BMP280/BME280 digital pressure sensor | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **ms5611** | Driver for barometic pressure sensor MS5611-01BA03 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **bme680** | Driver for BME680 digital environmental sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **bmp180** | Driver for BMP180 digital pressure sensor | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **bmp280** | Driver for BMP280/BME280 digital pressure sensor | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **dps310** | Driver for DPS310 barometric pressure sensor | ISC | esp32, esp8266, esp32s2, esp32c3 | yes | +| **ms5611** | Driver for barometic pressure sensor MS5611-01BA03 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **qmp6988** | Driver for QMP6988 digital temperature and pressure sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | + ### Real-time clocks -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **ds1302** | Driver for DS1302 RTC module | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No -| **ds1307** | Driver for DS1307 RTC module | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **ds3231** | Driver for DS1337 RTC and DS3231 high precision RTC module | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **pcf8563** | Driver for PCF8563 real-time clock/calendar | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **ds1302** | Driver for DS1302 RTC module | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | no | +| **ds1307** | Driver for DS1307 RTC module | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **ds3231** | Driver for DS1337 RTC and DS3231 high precision RTC module | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **pcf8563** | Driver for PCF8563 (BM8563) real-time clock/calendar | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | + ### Temperature sensors -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -| **aht** | Driver for AHT10/AHT15/AHT20 temperature and humidity sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **bh1900nux** | Driver for BH1900NUX temperature sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **bme680** | Driver for BME680 digital environmental sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **bmp180** | Driver for BMP180 digital pressure sensor | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **bmp280** | Driver for BMP280/BME280 digital pressure sensor | MIT | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **dht** | Driver for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), Itead Si7021 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No -| **ds18x20** | Driver for DS18B20/DS18S20 families of 1-Wire temperature sensor ICs | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | No -| **hdc1000** | Driver for HDC1000 temperature and humidity sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **hts221** | Driver for HTS221 temperature and humidity sensor. | ISC | `esp32`, `esp32s2`, `esp32c3` | Yes -| **lm75** | Driver for LM75, a digital temperature sensor and thermal watchdog | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **max31725** | Driver for MAX31725/MAX31726 temperature sensors | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **max31855** | Driver for MAX31855 cold-junction compensated thermocouple-to-digital converter | BSD-3 | `esp32`, `esp32s2`, `esp32c3` | Yes -| **max31865** | Driver for MAX31865 resistance converter for platinum RTDs | BSD-3 | `esp32`, `esp32s2`, `esp32c3` | Yes -| **mcp960x** | Driver for MCP9600/MCP9601, thermocouple EMF to temperature converter | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **mcp9808** | Driver for MCP9808 Digital Temperature Sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **ms5611** | Driver for barometic pressure sensor MS5611-01BA03 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **sht3x** | Driver for Sensirion SHT30/SHT31/SHT35 digital temperature and humidity sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **sht4x** | Driver for Sensirion SHT40/SHT41/SHT45 digital temperature and humidity sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **si7021** | Driver for Si7013/Si7020/Si7021/HTU2xD/SHT2x and compatible temperature and humidity sensors | BSD-3 | `esp32`, `esp32c3`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **sts21** | Driver for STS21 temperature sensor | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes -| **tsys01** | Driver for precision digital temperature sensor TSYS01 | BSD-3 | `esp32`, `esp8266`, `esp32s2`, `esp32c3` | Yes +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +| **aht** | Driver for AHT10/AHT15/AHT20 temperature and humidity sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **am2320** | Driver for AM2320 temperature and humidity sensor (I2C) | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **bh1900nux** | Driver for BH1900NUX temperature sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **bme680** | Driver for BME680 digital environmental sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **bmp180** | Driver for BMP180 digital pressure sensor | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **bmp280** | Driver for BMP280/BME280 digital pressure sensor | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | +| **dht** | Driver for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), Itead Si7021 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | no | +| **dps310** | Driver for DPS310 barometric pressure sensor | ISC | esp32, esp8266, esp32s2, esp32c3 | yes | +| **ds18x20** | Driver for DS18B20/DS18S20 families of 1-Wire temperature sensor ICs | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | no | +| **hdc1000** | Driver for HDC1000 temperature and humidity sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **hts221** | Driver for HTS221 temperature and humidity sensor | ISC | esp32, esp32s2, esp32c3 | yes | +| **lm75** | Driver for LM75, a digital temperature sensor and thermal watchdog | ISC | esp32, esp8266, esp32s2, esp32c3 | yes | +| **max31725** | Driver for MAX31725/MAX31726 temperature sensors | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **max31855** | Driver for MAX31855 cold-junction compensated thermocouple-to-digital converter | BSD-3-Clause | esp32, esp32s2, esp32c3 | yes | +| **max31865** | Driver for MAX31865 resistance converter for platinum RTDs | BSD-3-Clause | esp32, esp32s2, esp32c3 | yes | +| **mcp960x** | Driver for MCP9600/MCP9601, thermocouple EMF to temperature converter | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **mcp9808** | Driver for MCP9808 digital temperature sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **ms5611** | Driver for barometic pressure sensor MS5611-01BA03 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **qmp6988** | Driver for QMP6988 digital temperature and pressure sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **sfa3x** | Driver for SFA30 formaldehyde detection module (I2C) | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **sht3x** | Driver for Sensirion SHT30/SHT31/SHT35 digital temperature and humidity sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **sht4x** | Driver for Sensirion SHT40/SHT41/SHT45 digital temperature and humidity sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **si7021** | Driver for Si7013/Si7020/Si7021/HTU2xD/SHT2x and compatible temperature and humidity sensors | BSD-3-Clause | esp32, esp32c3, esp8266, esp32s2, esp32c3 | yes | +| **sts21** | Driver for STS21 temperature sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **sts3x** | Driver for Sensirion STS30/STS31/STS35 digital temperature sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **tsys01** | Driver for precision digital temperature sensor TSYS01 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | ## Library maintainers @@ -269,31 +303,44 @@ or [GitLab examples](https://gitlab.com/UncleRus/esp-idf-lib/tree/master/example ## Credits -- [Tomoyuki Sakurai](https://github.com/trombik), developer of the LM75 and - SK9822/APA102 drivers, author of the RTOS SDK ESP82666 support, master CI -- [Gunar Schorcht](https://github.com/gschorcht), developer of SHT3x, BME680 - and CCS811 drivers -- [Brian Schwind](https://github.com/bschwind), developer of TS2561 and - TSL4531 drivers -- [Andrej Krutak](https://github.com/andree182), developer of BH1750 driver -- Frank Bargstedt, developer of BMP180 driver -- [sheinz](https://github.com/sheinz), developer of BMP280 driver -- [Jonathan Hartsuiker](https://github.com/jsuiker), developer of DHT driver -- [Grzegorz Hetman](https://github.com/hetii), developer of DS18B20 driver -- [Alex Stewart](https://github.com/astewart-consensus), developer of DS18B20 driver -- [Richard A Burton](mailto:richardaburton@gmail.com), developer of DS3231 driver -- [Bhuvanchandra DV](https://github.com/bhuvanchandra), developer of DS3231 driver -- [Zaltora](https://github.com/Zaltora), developer of INA3231 driver -- [Bernhard Guillon](https://gitlab.com/mrnice), developer of MS5611-01BA03 driver -- [Pham Ngoc Thanh](https://github.com/panoti), developer of PCF8591 driver -- [Lucio Tarantino](https://github.com/dianlight), developer of ADS111x driver -- [Julian Dörner](https://github.com/juliandoerner), developer of TSL2591 driver -- [FastLED community](https://github.com/FastLED), developers of `lib8tion`, - `color` and `noise` libraries -- [Erriez](https://github.com/Erriez), developer of MH-Z19B driver -- [David Douard](https://github.com/douardda), developer of MH-Z19B driver -- [Nate Usher](https://github.com/nated0g), developer of SCD30 driver -- [Josh Kallus](https://github.com/Jkallus), developer of LS7366R driver -- [saasaa](https://github.com/saasaa), developer of HTS221 driver -- [Timofei Korostelev](https://github.com/chudsaviet), developer of HT16K33 driver -- [Jose Manuel Perez](https://github.com/jmpmscorp), developer of LC709203F driver +- [Alex Stewart](https://github.com/astewart-consensus): `ds18x20` +- [Alexander Bodenseher](https://github.com/saasaa): `hts221` +- [Andrej Krutak](https://github.com/andree182): `bh1750` +- Angelo Elias Dalzotto: `mpu6050` +- [BernhardG](https://gitlab.com/mrnice): `ms5611` +- [BhuvanchandraD](https://github.com/bhuvanchandra): `ds3231` +- [Brian Schwind](https://github.com/bschwind): `tsl2561` `tsl4531` +- [Cedric von Gunten](https://github.com/vonguced): `qmp6988` +- [Christian Skjerning](https://github.com/slimcdk): `sts3x` +- [David Douard](https://github.com/douardda): `mhz19b` +- [Erriez](https://github.com/Erriez): `mhz19b` +- [FastLED project](https://github.com/FastLED): `color` `lib8tion` `noise` +- Frank Bargstedt: `bmp180` +- Gabriel Boni Vicari: `mpu6050` +- [Grupo de Pesquisa em Cultura Digital](http://gepid.upf.br/): `mpu6050` +- GrzegorzH: `ds18x20` +- [Gunar Schorcht](https://github.com/gschorcht): `bme680` `ccs811` `sht3x` `sts3x` +- [Jakub Turek](https://github.com/QB4-dev): `l3gx` `lsm303` +- [Jan Veeh](https://github.com/janveeh): `icm42670` +- [Jeff Rowberg](https://www.i2cdevlib.com/): `mpu6050` +- [Jose Manuel Perez](https://github.com/jmpmscorp): `lc709203f` `sgm58031` +- [Joshua Butler](https://github.com/shuki25): `max1704x` +- [Joshua Kallus](https://github.com/Jkallus): `ls7366r` +- [jsuiker](https://github.com/jsuiker): `dht` +- [Julian Doerner](https://github.com/juliandoerner): `tsl2591` +- [Lucio Tarantino](https://github.com/dianlight): `ads111x` +- [Manuel Markwort](https://github.com/mmarkwort): `mp2660` +- [Marc Luehr](https://github.com/th3link): `veml7700` +- [Nate Usher](https://github.com/nated0g): `scd30` +- Pavel Merzlyakov: `ds1302` +- [Raghav Jha](https://github.com/horsemann07): `mpu6050` +- RichardA: `ds3231` +- [Ruslan V. Uss](https://github.com/UncleRus): `ads111x` `aht` `am2320` `bh1750` `bh1900nux` `bme680` `bmp180` `bmp280` `button` `calibration` `ccs811` `dht` `ds1302` `ds1307` `ds18x20` `ds3231` `ds3502` `encoder` `framebuffer` `hd44780` `hdc1000` `hmc5883l` `hx711` `i2cdev` `ina219` `ina260` `ina3221` `led_strip` `led_strip_spi` `max31725` `max31855` `max31865` `max7219` `mcp23008` `mcp23x17` `mcp342x` `mcp4725` `mcp960x` `mcp9808` `mpu6050` `ms5611` `onewire` `pca9557` `pca9685` `pcf8563` `pcf8574` `pcf8575` `pcf8591` `qmc5883l` `qmp6988` `rda5807m` `scd30` `scd4x` `sfa3x` `sgp40` `sht3x` `sht4x` `si7021` `sts21` `sts3x` `tca6424a` `tca9548` `tca95x5` `tda74xx` `tsl2561` `tsl4531` `tsys01` `ultrasonic` `wiegand` +- [Sensirion AG](https://github.com/Sensirion): `scd30` `scd4x` `sfa3x` +- [sheinz](https://github.com/sheinz): `bmp280` +- [Thanh Pham](https://github.com/panoti): `pcf8591` +- [Timofei Korostelev](https://github.com/chudsaviet): `ht16k33` +- [Tomoyuki Sakurai](https://github.com/trombik): `dps310` `esp_idf_lib_helpers` `example` `led_strip_spi` `lm75` +- [Weslley Duarte](https://github.com/weslleymfd): `ads130e08` +- [Zaltora](https://github.com/Zaltora): `ina3221` +- zeroday: `onewire` \ No newline at end of file diff --git a/components/ads111x/.eil.yml b/components/ads111x/.eil.yml index ca2ea18c..17160688 100644 --- a/components/ads111x/.eil.yml +++ b/components/ads111x/.eil.yml @@ -1,26 +1,22 @@ ---- -components: - - name: ads111x - description: | - Driver for ADS1113/ADS1114/ADS1115 and ADS1013/ADS1014/ADS1015 I2C ADC - group: adc-dac - groups: [] - code_owners: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2016 - - name: dianlight - year: 2020 +name: ads111x +description: Driver for ADS1113/ADS1114/ADS1115 and ADS1013/ADS1014/ADS1015 I2C ADC +version: 1.1.2 +groups: + - adc-dac +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2016 + - name: dianlight + year: 2020 diff --git a/components/ads111x/ads111x.c b/components/ads111x/ads111x.c index 598da1bd..148b70e7 100644 --- a/components/ads111x/ads111x.c +++ b/components/ads111x/ads111x.c @@ -139,6 +139,9 @@ static esp_err_t write_conf_bits(i2c_dev_t *dev, uint16_t val, uint8_t offs, I2C_DEV_TAKE_MUTEX(dev); I2C_DEV_CHECK(dev, read_reg(dev, REG_CONFIG, &old)); + // Issue #593 + if (offs != OS_OFFSET || mask != OS_MASK) + old &= ~(OS_MASK << OS_OFFSET); I2C_DEV_CHECK(dev, write_reg(dev, REG_CONFIG, (old & ~(mask << offs)) | (val << offs))); I2C_DEV_GIVE_MUTEX(dev); diff --git a/components/ads111x/ads111x.h b/components/ads111x/ads111x.h index 726d44c5..23902d36 100644 --- a/components/ads111x/ads111x.h +++ b/components/ads111x/ads111x.h @@ -33,6 +33,8 @@ * * ESP-IDF driver for ADS1113/ADS1114/ADS1115, ADS1013/ADS1014/ADS1015 I2C ADC * + * Version: 1.1.2 + * * Ported from esp-open-rtos * * Copyright (c) 2016, 2018 Ruslan V. Uss diff --git a/components/ads130e08/.eil.yml b/components/ads130e08/.eil.yml new file mode 100644 index 00000000..72ae391e --- /dev/null +++ b/components/ads130e08/.eil.yml @@ -0,0 +1,17 @@ +name: ads130e08 +description: Driver for ADS130E08 ADC +version: 1.0.0 +groups: + - adc-dac +code_owners: weslleymfd +depends: + - driver + - log +thread_safe: yes +targets: + - esp32 + - esp32s3 +license: MIT +copyrights: + - name: weslleymfd + year: 2021 diff --git a/components/ads130e08/CMakeLists.txt b/components/ads130e08/CMakeLists.txt new file mode 100644 index 00000000..aac1808e --- /dev/null +++ b/components/ads130e08/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "ads130e08.c" + INCLUDE_DIRS "." + REQUIRES driver log +) diff --git a/components/ads130e08/LICENSE b/components/ads130e08/LICENSE new file mode 100644 index 00000000..1116aa45 --- /dev/null +++ b/components/ads130e08/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 Weslley M. F. Duarte + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/components/ads130e08/ads130e08.c b/components/ads130e08/ads130e08.c new file mode 100644 index 00000000..2be6d10f --- /dev/null +++ b/components/ads130e08/ads130e08.c @@ -0,0 +1,627 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Weslley M. F. Duarte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @file ads130e08.c + * + * ESP-IDF driver for ADS130E08 ADC + * + * Copyright (c) 2021 Weslley M. F. Duarte + * + * MIT Licensed as described in the file LICENSE + */ + +#include "ads130e08.h" +#include +#include +#include +#include + +#define ADS130E08_CMD_WAKEUP (0x02) +#define ADS130E08_CMD_STANDBY (0x04) +#define ADS130E08_CMD_RESET (0x06) +#define ADS130E08_CMD_START (0x08) +#define ADS130E08_CMD_STOP (0x0A) +#define ADS130E08_CMD_RDATAC (0x10) +#define ADS130E08_CMD_SDATAC (0x11) +#define ADS130E08_CMD_RDATA (0x12) +#define ADS130E08_CMD_RREG (0x20) +#define ADS130E08_CMD_WREG (0x40) + +#define ADS130E08_REG_ID (0x00) +#define ADS130E08_REG_CONFIG1 (0x01) +#define ADS130E08_REG_CONFIG2 (0x02) +#define ADS130E08_REG_CONFIG3 (0x03) +#define ADS130E08_REG_FAULT (0x04) +#define ADS130E08_REG_CH1SET (0x05) +#define ADS130E08_REG_CH2SET (0x06) +#define ADS130E08_REG_CH3SET (0x07) +#define ADS130E08_REG_CH4SET (0x08) +#define ADS130E08_REG_CH5SET (0x09) +#define ADS130E08_REG_CH6SET (0x0A) +#define ADS130E08_REG_CH7SET (0x0B) +#define ADS130E08_REG_CH8SET (0x0C) +#define ADS130E08_REG_FAULT_STATP (0x12) +#define ADS130E08_REG_FAULT_STATN (0x13) +#define ADS130E08_REG_GPIO (0x14) + +#define ID_MASK_LOW_BITS (0x08) +#define ID_MASK_HIGH_BITS (0x10) + +#define CONFIG1_MASK_LOW_BITS (0xDE) +#define CONFIG1_MASK_HIGH_BITS (0x01) +#define CONFIG1_MASK_BITS_CLK_EN BIT(5) + +#define CONFIG2_MASK_LOW_BITS (0x88) +#define CONFIG2_MASK_HIGH_BITS (0x60) +#define CONFIG2_MASK_BITS_INT_TEST BIT(4) +#define CONFIG2_MASK_BITS_TEST_AMP BIT(2) +#define CONFIG2_MASK_BITS_TEST_FREQ (BIT(1) | BIT(0)) + +#define CONFIG3_MASK_LOW_BITS (0x13) +#define CONFIG3_MASK_HIGH_BITS (0x40) +#define CONFIG3_MASK_BITS_PD_REFBUF BIT(7) +#define CONFIG3_MASK_BITS_VREF_4V BIT(5) +#define CONFIG3_MASK_BITS_OPAMP_REF BIT(3) +#define CONFIG3_MASK_BITS_PD_OPAMP BIT(2) + +#define FAULT_MASK_LOW_BITS (0x1F) +#define FAULT_MASK_BITS_COMP_TH (BIT(7) | BIT(6) | BIT(5)) + +#define CHnSET_MASK_LOW_BITS (0x08) +#define CHnSET_MASK_BITS_PD BIT(7) +#define CHnSET_MASK_BITS_PGA (BIT(6) | BIT(5) | BIT(4)) +#define CHnSET_MASK_BITS_MUX (BIT(2) | BIT(1) | BIT(0)) + +#define MASK_BITS_IN1P_FAULT BIT(0) +#define MASK_BITS_IN2P_FAULT BIT(1) +#define MASK_BITS_IN3P_FAULT BIT(2) +#define MASK_BITS_IN4P_FAULT BIT(3) +#define MASK_BITS_IN5P_FAULT BIT(4) +#define MASK_BITS_IN6P_FAULT BIT(5) +#define MASK_BITS_IN7P_FAULT BIT(6) +#define MASK_BITS_IN8P_FAULT BIT(7) + +#define MASK_BITS_IN1N_FAULT BIT(0) +#define MASK_BITS_IN2N_FAULT BIT(1) +#define MASK_BITS_IN3N_FAULT BIT(2) +#define MASK_BITS_IN4N_FAULT BIT(3) +#define MASK_BITS_IN5N_FAULT BIT(4) +#define MASK_BITS_IN6N_FAULT BIT(5) +#define MASK_BITS_IN7N_FAULT BIT(6) +#define MASK_BITS_IN8N_FAULT BIT(7) + +#define MASK_BITS_GPIOC1 BIT(0) +#define MASK_BITS_GPIOC2 BIT(1) +#define MASK_BITS_GPIOC3 BIT(2) +#define MASK_BITS_GPIOC4 BIT(3) +#define MASK_BITS_GPIOD1 BIT(4) +#define MASK_BITS_GPIOD2 BIT(5) +#define MASK_BITS_GPIOD3 BIT(6) +#define MASK_BITS_GPIOD4 BIT(7) + +#define CLOCK_SPEED_HZ (4096000) /**< 4MHz */ + +#define ADS130E08_CONVERSION_CONST 0.0000732421875f /* 2.4 / 32768 */ + +#define CHECK(x) \ + do \ + { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) \ + return __; \ + } \ + while (0) +#define CHECK_ARG(VAL) \ + do \ + { \ + if (!(VAL)) \ + return ESP_ERR_INVALID_ARG; \ + } \ + while (0) +#define BV(x) (1 << (x)) + +static const char *TAG_ADS130E08 = "ads130e08"; + +static esp_err_t write_reg_8(ads130e08_t *dev, uint8_t reg, uint8_t val) +{ + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + + uint8_t tx[] = { (reg | ADS130E08_CMD_WREG), 0, val, 0 }; + + t.tx_buffer = tx; + t.length = sizeof(tx) * 8; + + return spi_device_transmit(dev->spi_dev, &t); +} + +static esp_err_t read_reg_8(ads130e08_t *dev, uint8_t reg, uint8_t *val) +{ + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + + uint8_t tx[] = { (reg | ADS130E08_CMD_RREG), 0, 0, 0 }; + uint8_t rx[sizeof(tx)]; + + t.tx_buffer = tx; + t.rx_buffer = rx; + t.length = sizeof(tx) * 8; + CHECK(spi_device_transmit(dev->spi_dev, &t)); + + *val = rx[2]; + + return ESP_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +esp_err_t ads130e08_init_desc(ads130e08_t *dev, spi_host_device_t host, gpio_num_t cs_pin) +{ + CHECK_ARG(dev); + + memset(&dev->spi_cfg, 0, sizeof(dev->spi_cfg)); + dev->spi_cfg.spics_io_num = cs_pin; + dev->spi_cfg.clock_speed_hz = CLOCK_SPEED_HZ; + dev->spi_cfg.mode = 1; + dev->spi_cfg.queue_size = 1; + dev->spi_cfg.cs_ena_pretrans = 1; + + return spi_bus_add_device(host, &dev->spi_cfg, &dev->spi_dev); +} + +esp_err_t ads130e08_free_desc(ads130e08_t *dev) +{ + CHECK_ARG(dev); + + return spi_bus_remove_device(dev->spi_dev); +} + +esp_err_t ads130e08_send_system_cmd(ads130e08_t *dev, ads130e08_system_cmd_t cmd) +{ + CHECK_ARG(dev); + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + + uint8_t tx[] = { (cmd), 0 }; + + t.tx_buffer = tx; + t.length = sizeof(tx) * 8; + + return spi_device_transmit(dev->spi_dev, &t); +} + +esp_err_t ads130e08_send_data_read_cmd(ads130e08_t *dev, ads130e08_data_read_cmd_t cmd) +{ + CHECK_ARG(dev); + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + + uint8_t tx[] = { (cmd), 0 }; + + t.tx_buffer = tx; + t.length = sizeof(tx) * 8; + + return spi_device_transmit(dev->spi_dev, &t); +} + +esp_err_t ads130e08_get_device_id(ads130e08_t *dev, uint8_t *id) +{ + CHECK_ARG(dev); + + uint8_t val; + + CHECK(read_reg_8(dev, ADS130E08_REG_ID, &val)); + + *id = val; + + return ESP_OK; +} + +esp_err_t ads130e08_set_device_config(ads130e08_t *dev, ads130e08_dev_config_t config) +{ + CHECK_ARG(dev); + + uint8_t config1 = 0x00, config2 = 0x00, config3 = 0x00; + + config1 = (config.clk_en | CONFIG1_MASK_HIGH_BITS) & ~(CONFIG1_MASK_LOW_BITS); + + CHECK(write_reg_8(dev, ADS130E08_REG_CONFIG1, config1)); + + config2 + = (config.int_test | config.test_amp | config.test_freq | CONFIG2_MASK_HIGH_BITS) & ~(CONFIG2_MASK_LOW_BITS); + + CHECK(write_reg_8(dev, ADS130E08_REG_CONFIG2, config2)); + + config3 = (config.pd_refbuf | config.vref_4v | config.opamp_ref | config.pd_opamp | CONFIG3_MASK_HIGH_BITS) + & ~(CONFIG3_MASK_LOW_BITS); + + CHECK(write_reg_8(dev, ADS130E08_REG_CONFIG3, config3)); + + return ESP_OK; +} + +esp_err_t ads130e08_get_device_config(ads130e08_t *dev, ads130e08_dev_config_t *config) +{ + CHECK_ARG(dev && config); + + uint8_t config1, config2, config3; + + CHECK(read_reg_8(dev, ADS130E08_REG_CONFIG1, &config1)); + + config->clk_en = (config1 & CONFIG1_MASK_BITS_CLK_EN); + + CHECK(read_reg_8(dev, ADS130E08_REG_CONFIG2, &config2)); + + config->int_test = (config2 & CONFIG2_MASK_BITS_INT_TEST); + config->test_amp = (config2 & CONFIG2_MASK_BITS_TEST_AMP); + config->test_freq = (config2 & CONFIG2_MASK_BITS_TEST_FREQ); + + CHECK(read_reg_8(dev, ADS130E08_REG_CONFIG3, &config3)); + + config->pd_refbuf = (config3 & CONFIG3_MASK_BITS_PD_REFBUF); + config->vref_4v = (config3 & CONFIG3_MASK_BITS_VREF_4V); + config->opamp_ref = (config3 & CONFIG3_MASK_BITS_OPAMP_REF); + config->pd_opamp = (config3 & CONFIG3_MASK_BITS_PD_OPAMP); + + return ESP_OK; +} + +#define UPPER_1_BITS 0x80 +#define LOWER_7_BITS 0x7f + +esp_err_t ads130e08_get_rdata(ads130e08_t *dev, ads130e08_raw_data_t *raw_data) +{ + // (24 status bits + 16 bits × 8 channels) = 152 bits data per device + 1 dummy bit when using daisy + // The format of the 24 status bits is (1100 + FAULT_STATP + FAULT_STATN + bits[7:4] of the GPIO: General- Purpose + // IO Register). The data format for each channel data is twos complement, MSB first. When channels are powered down + // using user register settings, the corresponding channel output is set to '0'. + + CHECK_ARG(dev); + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + + uint8_t tx[] = { (ADS130E08_CMD_RDATA), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + uint8_t rx[sizeof(tx)] = { 0 }; + + t.tx_buffer = tx; + t.rx_buffer = rx; + t.length = sizeof(tx) * 8; + + CHECK(spi_device_transmit(dev->spi_dev, &t)); + + uint32_t status_bits; + + status_bits = ((rx[1] | rx[2] | rx[3]) & 0x00FFFFFF); + + raw_data->fault_statp = (uint8_t)(status_bits & 0x000FF000); + raw_data->fault_statn = (uint8_t)(status_bits & 0x00000FF0); + raw_data->gpios_level = (uint8_t)(status_bits & 0x0000000F); + + uint16_t adc_raw_two_complememt; + + size_t j = 0; + + for (size_t i = 0; i < 8; i++) + { + adc_raw_two_complememt = ((uint16_t)(rx[4 + j] << 8) | (uint16_t)(rx[4 + j + 1] << 0)); + raw_data->channels_raw[i] = (int16_t)adc_raw_two_complememt; + + j += 2; + } + + return ESP_OK; +} + +esp_err_t ads130e08_convert_raw_to_voltage(int16_t raw, uint8_t gain, float *volts) +{ + *volts = (ADS130E08_CONVERSION_CONST * raw) / gain; + + return ESP_OK; +} + +esp_err_t ads130e08_set_fault_detect_control(ads130e08_t *dev, ads130e08_fault_threshold_t fault_mode) +{ + CHECK_ARG(dev); + + uint8_t val = 0x00; + + val = (fault_mode) & ~(FAULT_MASK_LOW_BITS); + + CHECK(write_reg_8(dev, ADS130E08_REG_FAULT, val)); + + return ESP_OK; +} + +esp_err_t ads130e08_get_fault_detect_control(ads130e08_t *dev, ads130e08_fault_threshold_t *fault_mode) +{ + CHECK_ARG(dev); + + uint8_t val; + + CHECK(read_reg_8(dev, ADS130E08_REG_FAULT, &val)); + + *fault_mode = val; + + return ESP_OK; +} + +esp_err_t ads130e08_set_channel_config(ads130e08_t *dev, ads130e08_channel_t channel, ads130e08_channel_config_t config) +{ + CHECK_ARG(dev && channel); + + uint8_t val = 0x00; + + val = (config.enable | config.pga_gain | config.mode) & ~(CHnSET_MASK_LOW_BITS); + + CHECK(write_reg_8(dev, channel, val)); + + return ESP_OK; +} + +esp_err_t ads130e08_get_channel_config(ads130e08_t *dev, ads130e08_channel_t channel, + ads130e08_channel_config_t *config) +{ + CHECK_ARG(dev && config); + + uint8_t val; + CHECK(read_reg_8(dev, channel, &val)); + + config->enable = val & CHnSET_MASK_BITS_PD; + config->pga_gain = val & CHnSET_MASK_BITS_PGA; + config->mode = val & CHnSET_MASK_BITS_MUX; + + return ESP_OK; +} + +esp_err_t ads130e08_set_gpio_pin_mode(ads130e08_t *dev, ads130e08_gpio_pin_t gpio_pin, ads130e08_gpio_mode_t gpio_mode) +{ + CHECK_ARG(dev && gpio_pin && gpio_mode); + + uint8_t val; + + CHECK(read_reg_8(dev, ADS130E08_REG_GPIO, &val)); + + if (gpio_pin == ADS130E08_GPIO1) + { + val |= (gpio_mode & MASK_BITS_GPIOC1); + } + else if (gpio_pin == ADS130E08_GPIO2) + { + val |= (gpio_mode & MASK_BITS_GPIOC2); + } + else if (gpio_pin == ADS130E08_GPIO3) + { + val |= (gpio_mode & MASK_BITS_GPIOC3); + } + else if (gpio_pin == ADS130E08_GPIO4) + { + val |= (gpio_mode & MASK_BITS_GPIOC4); + } + else + { + return ESP_ERR_INVALID_ARG; + } + + CHECK(write_reg_8(dev, ADS130E08_REG_GPIO, val)); + + return ESP_OK; +} + +esp_err_t ads130e08_get_gpio_pin_mode(ads130e08_t *dev, ads130e08_gpio_pin_t gpio_pin, ads130e08_gpio_mode_t *gpio_mode) +{ + CHECK_ARG(dev && gpio_pin && gpio_mode); + + uint8_t val; + + CHECK(read_reg_8(dev, ADS130E08_REG_GPIO, &val)); + + if (gpio_pin == ADS130E08_GPIO1) + { + *gpio_mode = (val & MASK_BITS_GPIOC1); + } + else if (gpio_pin == ADS130E08_GPIO2) + { + *gpio_mode = (val & MASK_BITS_GPIOC2); + } + else if (gpio_pin == ADS130E08_GPIO3) + { + *gpio_mode = (val & MASK_BITS_GPIOC3); + } + else if (gpio_pin == ADS130E08_GPIO4) + { + *gpio_mode = (val & MASK_BITS_GPIOC4); + } + else + { + return ESP_ERR_INVALID_ARG; + } + + return ESP_OK; +} + +esp_err_t ads130e08_set_gpio_pin_level(ads130e08_t *dev, ads130e08_gpio_pin_t gpio_pin, + ads130e08_gpio_level_t gpio_level) +{ + CHECK_ARG(dev && gpio_pin && gpio_level); + + uint8_t val; + + CHECK(read_reg_8(dev, ADS130E08_REG_GPIO, &val)); + + if (gpio_pin == ADS130E08_GPIO1) + { + val |= (gpio_level & MASK_BITS_GPIOD1); + } + else if (gpio_pin == ADS130E08_GPIO2) + { + val |= (gpio_level & MASK_BITS_GPIOD2); + } + else if (gpio_pin == ADS130E08_GPIO3) + { + val |= (gpio_level & MASK_BITS_GPIOD3); + } + else if (gpio_pin == ADS130E08_GPIO4) + { + val |= (gpio_level & MASK_BITS_GPIOD4); + } + else + { + return ESP_ERR_INVALID_ARG; + } + + CHECK(write_reg_8(dev, ADS130E08_REG_GPIO, val)); + + return ESP_OK; +} + +esp_err_t ads130e08_get_gpio_pin_level(ads130e08_t *dev, ads130e08_gpio_pin_t gpio_pin, + ads130e08_gpio_level_t *gpio_level) +{ + CHECK_ARG(dev && gpio_pin && gpio_level); + + uint8_t val; + + CHECK(read_reg_8(dev, ADS130E08_REG_GPIO, &val)); + + if (gpio_pin == ADS130E08_GPIO1) + { + *gpio_level = (val & MASK_BITS_GPIOD1); + } + else if (gpio_pin == ADS130E08_GPIO2) + { + *gpio_level = (val & MASK_BITS_GPIOD2); + } + else if (gpio_pin == ADS130E08_GPIO3) + { + *gpio_level = (val & MASK_BITS_GPIOD3); + } + else if (gpio_pin == ADS130E08_GPIO4) + { + *gpio_level = (val & MASK_BITS_GPIOD4); + } + else + { + return ESP_ERR_INVALID_ARG; + } + + return ESP_OK; +} + +esp_err_t ads130e08_detect_fault_auto(ads130e08_t *dev, uint8_t *fault_statp, uint8_t *fault_statn) +{ + CHECK_ARG(dev); + + uint8_t fault_bits; + CHECK(read_reg_8(dev, ADS130E08_REG_FAULT_STATP, &fault_bits)); + *fault_statp = fault_bits; + + if (fault_bits & MASK_BITS_IN1P_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on positive input 1"); + } + + if (fault_bits & MASK_BITS_IN2P_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on positive input 2"); + } + + if (fault_bits & MASK_BITS_IN3P_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on positive input 3"); + } + + if (fault_bits & MASK_BITS_IN4P_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on positive input 4"); + } + + if (fault_bits & MASK_BITS_IN5P_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on positive input 5"); + } + + if (fault_bits & MASK_BITS_IN6P_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on positive input 6"); + } + + if (fault_bits & MASK_BITS_IN7P_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on positive input 7"); + } + + if (fault_bits & MASK_BITS_IN8P_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on positive input 8"); + } + + CHECK(read_reg_8(dev, ADS130E08_REG_FAULT_STATN, &fault_bits)); + *fault_statn = fault_bits; + + if (fault_bits & MASK_BITS_IN1N_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on negative input 1"); + } + + if (fault_bits & MASK_BITS_IN2N_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on negative input 2"); + } + + if (fault_bits & MASK_BITS_IN3N_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on negative input 3"); + } + + if (fault_bits & MASK_BITS_IN4N_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on negative input 4"); + } + + if (fault_bits & MASK_BITS_IN5N_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on negative input 5"); + } + + if (fault_bits & MASK_BITS_IN6N_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on negative input 6"); + } + + if (fault_bits & MASK_BITS_IN7N_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on negative input 7"); + } + + if (fault_bits & MASK_BITS_IN8N_FAULT) + { + ESP_LOGE(TAG_ADS130E08, "Automatic fault detection on negative input 8"); + } + + return ESP_OK; +} \ No newline at end of file diff --git a/components/ads130e08/ads130e08.h b/components/ads130e08/ads130e08.h new file mode 100644 index 00000000..796d6038 --- /dev/null +++ b/components/ads130e08/ads130e08.h @@ -0,0 +1,420 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Weslley M. F. Duarte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @file ads130e08.h + * @defgroup ads130e08 ads130e08 + * @{ + * + * ESP-IDF driver for ADS130E08 ADC + * + * Copyright (c) 2021 Weslley M. F. Duarte + * + * MIT Licensed as described in the file LICENSE + */ + +#ifndef __ADS130E08_H__ +#define __ADS130E08_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + ADS130E08_CMD_WAKEUP = 0x02, + ADS130E08_CMD_STANDBY = 0x04, + ADS130E08_CMD_RESET = 0x06, + ADS130E08_CMD_START = 0x08, + ADS130E08_CMD_STOP = 0x0A +} ads130e08_system_cmd_t; + +typedef enum { + ADS130E08_CMD_RDATAC = 0x10, + ADS130E08_CMD_SDATAC = 0x11, + ADS130E08_CMD_RDATA = 0x12 +} ads130e08_data_read_cmd_t; + +typedef enum { + ADS130E08_CLK_OUT_DISABLED = 0x00, /**< Oscillator clock output disabled (default) */ + ADS130E08_CLK_OUT_ENABLED = 0x20 /**< Oscillator clock output enabled */ +} ads130e08_clk_en_t; + +typedef enum { + ADS130E08_INT_TEST_EXTERNAL = 0x00, /**< Test signals are driven externally (default) */ + ADS130E08_INT_TEST_INTERNAL = 0x10 /**< Test signals are generated internally */ +} ads130e08_int_test_t; + +typedef enum { + ADS130E08_TEST_AMP_CALIB_1X = 0x00, /**< 1 × –(VREFP – VREFN) / 2.4 mV (default) */ + ADS130E08_TEST_AMP_CALIB_2X = 0x04 /**< 2 × –(VREFP – VREFN) / 2.4 mV */ +} ads130e08_test_amp_t; + +typedef enum { + ADS130E08_TEST_FREQ_EXP_21 = 0x00, /**< Pulsed at fCLK / 2^21 (default) */ + ADS130E08_TEST_FREQ_EXP_20 = 0x01, /**< Pulsed at fCLK / 2^20 */ + ADS130E08_TEST_FREQ_AT_DC = 0x11 /**< At dc */ +} ads130e08_test_freq_t; + +typedef enum { + ADS130E08_INTERNAL_REF_BUFFER_DISABLED = 0x00, /**< Power-down internal reference buffer (default) */ + ADS130E08_INTERNAL_REF_BUFFER_ENABLED = 0x80 /**< Enable internal reference buffer */ +} ads130e08_pd_refbuf_t; + +typedef enum { + ADS130E08_REF_VOLTAGE_2_4V = 0x00, /**< VREFP is set to 2.4 V (default) */ + ADS130E08_REF_VOLTAGE_4_0V = 0x20, /**< VREFP is set to 4 V (only use with a 5-V analog supply) */ +} ads130e08_vref_4v_t; + +typedef enum { + ADS130E08_NON_INVERTING_CONNECT_OPAMP = 0x00, /**< Noninverting input connected to the OPAMPP pin (default) */ + ADS130E08_NON_INVERTING_CONNECT_AV = 0x08, /**< Noninverting input connected to (AVDD + AVSS) / 2 */ +} ads130e08_opamp_ref_t; + +typedef enum { + ADS130E08_OPAMP_DISABLED = 0x00, /**< Power-down op amp (default) */ + ADS130E08_OPAMP_ENABLED = 0x04, /**< Enable op amp */ +} ads130e08_pd_opamp_t; + +/** + * Channels + */ +typedef enum { + ADS130E08_CHANNEL_1 = 0x05, + ADS130E08_CHANNEL_2 = 0x06, + ADS130E08_CHANNEL_3 = 0x07, + ADS130E08_CHANNEL_4 = 0x08, + ADS130E08_CHANNEL_5 = 0x09, + ADS130E08_CHANNEL_6 = 0x0A, + ADS130E08_CHANNEL_7 = 0x0B, + ADS130E08_CHANNEL_8 = 0x0C +} ads130e08_channel_t; + +/** + * Fault detect comparator threshold + */ +typedef enum { + ADS130E08_MODE_1 = 0x00, /**< Comparator positive threshold: 95% , negative threshold: 5% (default) */ + ADS130E08_MODE_2 = 0x20, /**< Comparator positive threshold: 92.5% , negative threshold: 7.5% (default) */ + ADS130E08_MODE_3 = 0x40, /**< Comparator positive threshold: 90% , negative threshold: 10% (default) */ + ADS130E08_MODE_4 = 0x60, /**< Comparator positive threshold: 87.5% , negative threshold: 12.5% (default) */ + ADS130E08_MODE_5 = 0x80, /**< Comparator positive threshold: 85% , negative threshold: 15% (default) */ + ADS130E08_MODE_6 = 0xA0, /**< Comparator positive threshold: 80% , negative threshold: 20% (default) */ + ADS130E08_MODE_7 = 0xC0, /**< Comparator positive threshold: 75% , negative threshold: 25% (default) */ + ADS130E08_MODE_8 = 0xE0 /**< Comparator positive threshold: 70% , negative threshold: 30% (default) */ +} ads130e08_fault_threshold_t; + +/** + * Power down + */ +typedef enum { + ADS130E08_NORMAL_OPERATION = 0x00, /**< Normal operation (default) */ + ADS130E08_POWER_DOWN = 0x80 /**< Channel power-down */ +} ads130e08_pd_t; + +/** + * PGA gain + */ +typedef enum { + ADS130E08_PGA_1 = 0x10, /**< x1 */ + ADS130E08_PGA_2 = 0x20, /**< x2 */ + ADS130E08_PGA_8 = 0x50 /**< x8 */ +} ads130e08_pga_gain_t; + +/** + * MUX + */ +typedef enum { + ADS130E08_NORMAL_INPUT = 0x00, /**< Normal input (default) */ + ADS130E08_INPUT_SHORTED = 0x01, /**< Input shorted (for offset or noise measurements) */ + ADS130E08_MVDD = 0x03, /**< MVDD for supply measurement */ + ADS130E08_TEMPERATURE_SENSOR = 0x04, /**< Temperature sensor */ + ADS130E08_TEST_SIGNAL = 0x05 /**< Test signal */ +} ads130e08_mux_t; + +typedef enum { ADS130E08_FAULT_STATP = 0x12, ADS130E08_FAULT_STATN = 0x13 } ads130e08_fault_t; + +/** + * Fault status + */ +typedef enum { + ADS130E08_NO_FAULT_PRESENT = 0x00, /**< No fault present (default) */ + ADS130E08_FAULT_PRESENT = 0x01 /**< Fault present */ +} ads130e08_fault_status_t; + +/** + * GPIO mode + */ +typedef enum { + ADS130E08_GPIO_OUTPUT = 0x00, /**< Output */ + ADS130E08_GPIO_INPUT = 0x01 /**< Input (default) */ +} ads130e08_gpio_mode_t; + +/** + * GPIO level + */ +typedef enum { ADS130E08_GPIO_RESET = 0x00, ADS130E08_GPIO_SET = 0x01 } ads130e08_gpio_level_t; + +/** + * GPIOs + */ +typedef enum { + ADS130E08_GPIO1 = 0x01, + ADS130E08_GPIO2 = 0x02, + ADS130E08_GPIO3 = 0x03, + ADS130E08_GPIO4 = 0x04 +} ads130e08_gpio_pin_t; + +/** + * Number of devices + */ +typedef enum { ADS130E08_DEVICES_1 = 0x01, ADS130E08_DEVICES_2 = 0x02 } ads130e08_devices_n_t; + +/** + * Device descriptor + */ +typedef struct +{ + spi_device_interface_config_t spi_cfg; /**< SPI device configuration */ + spi_device_handle_t spi_dev; /**< SPI device handler */ +} ads130e08_t; + +/** + * Device configuration + */ +typedef struct +{ + ads130e08_clk_en_t clk_en; + ads130e08_int_test_t int_test; + ads130e08_test_amp_t test_amp; + ads130e08_test_freq_t test_freq; + ads130e08_pd_refbuf_t pd_refbuf; + ads130e08_vref_4v_t vref_4v; + ads130e08_opamp_ref_t opamp_ref; + ads130e08_pd_opamp_t pd_opamp; +} ads130e08_dev_config_t; + +/** + * Channel configuration + */ +typedef struct +{ + ads130e08_pd_t enable; + ads130e08_pga_gain_t pga_gain; + ads130e08_mux_t mode; +} ads130e08_channel_config_t; + +typedef struct +{ + uint8_t fault_statp; + uint8_t fault_statn; + uint8_t gpios_level; + int16_t channels_raw[8]; +} ads130e08_raw_data_t; + +/** + * @brief Initialize device descriptor + * + * @param dev Device descriptor + * @param host SPI host + * @param cs_pin CS GPIO number + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_init_desc(ads130e08_t *dev, spi_host_device_t host, gpio_num_t cs_pin); + +/** + * @brief Free device descriptor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_free_desc(ads130e08_t *dev); + +/** + * @brief Send system_command to device + * + * @param dev Device descriptor + * @param cmd Command + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_send_system_cmd(ads130e08_t *dev, ads130e08_system_cmd_t cmd); + +/** + * @brief Send data_read_command to device + * + * @param dev Device descriptor + * @param cmd Command + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_send_data_read_cmd(ads130e08_t *dev, ads130e08_data_read_cmd_t cmd); + +/** + * @brief Get device id + * + * @param dev Device descriptor + * @param id Id + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_get_device_id(ads130e08_t *dev, uint8_t *id); + +/** + * @brief Set device configuration + * + * @param dev Device descriptor + * @param config Device configurations + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_set_device_config(ads130e08_t *dev, ads130e08_dev_config_t config); + +/** + * @brief Get device configuration + * + * @param dev Device descriptor + * @param config Device configurations + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_get_device_config(ads130e08_t *dev, ads130e08_dev_config_t *config); + +/** + * @brief Reads raw data in "Read data by command" mode + * + * @param dev Device descriptor + * @param[out] raw_data Raw data + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_get_rdata(ads130e08_t *dev, ads130e08_raw_data_t *raw_data); + +/** + * @brief Converts raw adc value to voltage + * + * @param raw Raw adc value + * @param gain Channel gain + * @param[out] volts Voltage value + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_convert_raw_to_voltage(int16_t raw, uint8_t gain, float *volts); + +/** + * @brief Set fault detect control + * + * @param dev Pointer to device descriptor + * @param fault_mode Fault mode + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_set_fault_detect_control(ads130e08_t *dev, ads130e08_fault_threshold_t fault_mode); + +/** + * @brief Get fault detect control + * + * @param dev Pointer to device descriptor + * @param[out] fault_mode Fault mode + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_get_fault_detect_control(ads130e08_t *dev, ads130e08_fault_threshold_t *fault_mode); + +/** + * @brief Set channel configuration + * + * @param dev Pointer to device descriptor + * @param channel Channel + * @param config Channel configuration + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_set_channel_config(ads130e08_t *dev, ads130e08_channel_t channel, + ads130e08_channel_config_t config); + +/** + * @brief Get channel configuration + * + * @param dev Pointer to device descriptor + * @param channel Channel + * @param[out] config Channel configuration + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_get_channel_config(ads130e08_t *dev, ads130e08_channel_t channel, + ads130e08_channel_config_t *config); + +/** + * @brief Set GPIO pin mode + * + * @param dev Pointer to device descriptor + * @param gpio_pin GPIO pin number + * @param gpio_mode GPIO pin mode + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_set_gpio_pin_mode(ads130e08_t *dev, ads130e08_gpio_pin_t gpio_pin, ads130e08_gpio_mode_t gpio_mode); + +/** + * @brief Get GPIO pin mode + * + * @param dev Pointer to device descriptor + * @param gpio_pin GPIO pin number + * @param[out] gpio_mode GPIO pin mode + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_get_gpio_pin_mode(ads130e08_t *dev, ads130e08_gpio_pin_t gpio_pin, + ads130e08_gpio_mode_t *gpio_mode); + +/** + * @brief Set GPIO pin level + * + * @param dev Pointer to device descriptor + * @param gpio_pin GPIO pin number + * @param gpio_level GPIO pin level + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_set_gpio_pin_level(ads130e08_t *dev, ads130e08_gpio_pin_t gpio_pin, + ads130e08_gpio_level_t gpio_level); + +/** + * @brief Get GPIO pin level + * + * @param dev Pointer to device descriptor + * @param gpio_pin GPIO pin number + * @param[out] gpio_level GPIO pin level + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_get_gpio_pin_level(ads130e08_t *dev, ads130e08_gpio_pin_t gpio_pin, + ads130e08_gpio_level_t *gpio_level); + +/** + * @brief Run automatical fault detection cycle + * + * @param dev Device descriptor + * @param [out] fault_statp See datasheet + * @param [out] fault_statn See datasheet + * @return `ESP_OK` on success + */ +esp_err_t ads130e08_detect_fault_auto(ads130e08_t *dev, uint8_t *fault_statp, uint8_t *fault_statn); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __ADS130E08_H__ */ diff --git a/components/ads130e08/component.mk b/components/ads130e08/component.mk new file mode 100644 index 00000000..2b58ce41 --- /dev/null +++ b/components/ads130e08/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = driver log diff --git a/components/aht/.eil.yml b/components/aht/.eil.yml index 27ee7700..af3718f0 100644 --- a/components/aht/.eil.yml +++ b/components/aht/.eil.yml @@ -1,25 +1,22 @@ ---- -components: - - name: aht - description: | - Driver for AHT10/AHT15/AHT20 temperature and humidity sensor - group: temperature - groups: - - humidity - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2021 +name: aht +description: Driver for AHT10/AHT15/AHT20 temperature and humidity sensor +version: 1.0.0 +groups: + - temperature + - humidity +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2021 diff --git a/components/am2320/.eil.yml b/components/am2320/.eil.yml new file mode 100644 index 00000000..2cc311ff --- /dev/null +++ b/components/am2320/.eil.yml @@ -0,0 +1,22 @@ +name: am2320 +description: Driver for AM2320 temperature and humidity sensor (I2C) +version: 1.0.0 +groups: + - temperature + - humidity +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2023 diff --git a/components/am2320/CMakeLists.txt b/components/am2320/CMakeLists.txt new file mode 100644 index 00000000..7a4e35a6 --- /dev/null +++ b/components/am2320/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS am2320.c + INCLUDE_DIRS . + REQUIRES i2cdev log esp_idf_lib_helpers +) diff --git a/components/am2320/LICENSE b/components/am2320/LICENSE new file mode 100644 index 00000000..f7f05cb2 --- /dev/null +++ b/components/am2320/LICENSE @@ -0,0 +1,26 @@ +Copyright 2023 Ruslan V. Uss + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/components/am2320/am2320.c b/components/am2320/am2320.c new file mode 100644 index 00000000..cf65db45 --- /dev/null +++ b/components/am2320/am2320.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2023 Ruslan V. Uss + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file am2320.c + * + * ESP-IDF driver for humidty/temperature sensors AM2320 + * + * Copyright (c) 2023 Ruslan V. Uss + * + * BSD Licensed as described in the file LICENSE + */ +#include "am2320.h" +#include +#include +#include +#include + +#define I2C_FREQ_HZ (100000) // 100kHz + +static const char *TAG = "am2320"; + +#define MODBUS_READ (0x03) + +#define REG_RH_H (0x00) +#define REG_T_H (0x02) +#define REG_MODEL_H (0x08) +#define REG_VER (0x0a) +#define REG_DEV_ID_H (0x0b) + +#define DELAY_T1_US (800 + 100) // minimum delay + extra +#define DELAY_T2_US (1500 + 100) + +#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) +#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) + +static uint16_t crc16(uint8_t *data, size_t len) +{ + uint16_t crc = 0xffff; + while (len--) + { + crc ^= *data++; + for (int i = 0; i < 8; i++) + { + if (crc & 0x01) + { + crc >>= 1; + crc ^= 0xa001; + } else crc >>= 1; + } + } + return crc; +} + +/* + * Request: [3 bytes] CMD, START_REG, BYTES + * Response: [BYTES + 4] CMD, BYTES, DATA0, ... DATAn, CRC16_LOW, CRC16_HIGH + */ +static esp_err_t read_reg_modbus(i2c_dev_t *dev, uint8_t reg, uint8_t len, uint8_t *buf) +{ + uint8_t req[] = { MODBUS_READ, reg, len }; + uint8_t resp[len + 4]; + esp_err_t err = ESP_FAIL; + + I2C_DEV_TAKE_MUTEX(dev); + + /* Wake up the sensor. See 8.2.4 I2C Communication Timing */ + err = i2c_dev_probe(dev, I2C_DEV_READ); + if (err == ESP_FAIL) + { + /* the sensor does not send ACK for wakeup command, ignore the error + */ + ESP_LOGD(TAG, "i2c_dev_probe(): %s", esp_err_to_name(err)); + } + ets_delay_us(DELAY_T1_US); + + err = i2c_dev_write(dev, NULL, 0, req, sizeof(req)); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "i2c_dev_write(): %s", esp_err_to_name(err)); + goto fail; + } + ets_delay_us(DELAY_T2_US); + + err = i2c_dev_read(dev, NULL, 0, resp, sizeof(resp)); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "i2c_dev_read(): %s", esp_err_to_name(err)); + goto fail; + } + + if (resp[0] != MODBUS_READ) + { + ESP_LOGE(TAG, "Invalid MODBUS reply (%d != 0x03)", resp[0]); + err = ESP_ERR_INVALID_RESPONSE; + goto fail; + } + if (resp[1] != len) + { + ESP_LOGE(TAG, "Invalid MODBUS reply length (%d != %d)", resp[1], len); + err = ESP_ERR_INVALID_RESPONSE; + goto fail; + } + + /* CRC16 in little endian */ + if (crc16(resp, len + 2) != ((uint16_t)resp[len + 3] << 8) + resp[len + 2]) + { + ESP_LOGE(TAG, "Invalid CRC in MODBUS reply"); + err = ESP_ERR_INVALID_CRC; + goto fail; + } + memcpy(buf, resp + 2, len); + +fail: + I2C_DEV_GIVE_MUTEX(dev); + return err; +} + +static float convert_temperature(uint16_t raw) +{ + if (raw == 0xffff) + return NAN; + float res = raw & 0x8000 + ? (float)-(int16_t)(raw & 0x7fff) + : (float)(raw); + return res / 10.0f; +} + +static inline float convert_humidity(uint16_t raw) +{ + return raw != 0xffff + ? (float)raw / 10.0f + : NAN; +} + +/////////////////////////////////////////////////////////////////////////////// + +esp_err_t am2320_init_desc(i2c_dev_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + dev->port = port; + dev->addr = AM2320_I2C_ADDR; + dev->cfg.sda_io_num = sda_gpio; + dev->cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + return i2c_dev_create_mutex(dev); +} + +esp_err_t am2320_free_desc(i2c_dev_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(dev); +} + +esp_err_t am2320_get_rht(i2c_dev_t *dev, float *temperature, float *humidity) +{ + CHECK_ARG(dev && (temperature || humidity)); + + if (temperature && humidity) + { + // read both values + uint8_t buf[4] = { 0xff, 0xff, 0xff, 0xff }; + CHECK(read_reg_modbus(dev, REG_RH_H, 4, buf)); + *humidity = convert_humidity(((uint16_t)buf[0] << 8) + buf[1]); + *temperature = convert_temperature(((uint16_t)buf[2] << 8) + buf[3]); + } + else if (temperature) + { + // read only temperature + uint8_t buf[2] = { 0xff, 0xff }; + CHECK(read_reg_modbus(dev, REG_T_H, 2, buf)); + *temperature = convert_temperature(((uint16_t)buf[0] << 8) + buf[1]); + } + else + { + // read only humidity + uint8_t buf[2] = { 0xff, 0xff }; + CHECK(read_reg_modbus(dev, REG_RH_H, 2, buf)); + *humidity = convert_humidity(((uint16_t)buf[0] << 8) + buf[1]); + } + + return ESP_OK; +} + +esp_err_t am2320_get_model(i2c_dev_t *dev, uint16_t *model) +{ + CHECK_ARG(dev && model); + + uint8_t buf[2] = { 0 }; + CHECK(read_reg_modbus(dev, REG_T_H, 2, buf)); + *model = ((uint16_t)buf[0] << 8) + buf[1]; + + return ESP_OK; +} + +esp_err_t am2320_get_version(i2c_dev_t *dev, uint8_t *version) +{ + CHECK_ARG(dev && version); + + CHECK(read_reg_modbus(dev, REG_VER, 1, version)); + + return ESP_OK; +} + +esp_err_t am2320_get_device_id(i2c_dev_t *dev, uint32_t *id) +{ + CHECK_ARG(dev && id); + + uint8_t buf[4] = { 0 }; + CHECK(read_reg_modbus(dev, REG_DEV_ID_H, 4, buf)); + *id = ((uint32_t)buf[0] << 24) + ((uint32_t)buf[1] << 16) + ((uint32_t)buf[2] << 8) + buf[3]; + + return ESP_OK; +} diff --git a/components/am2320/am2320.h b/components/am2320/am2320.h new file mode 100644 index 00000000..1385e124 --- /dev/null +++ b/components/am2320/am2320.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023 Ruslan V. Uss + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file am2320.h + * @defgroup am2320 am2320 + * @{ + * + * ESP-IDF driver for humidty/temperature sensors AM2320 + * + * Copyright (c) 2023 Ruslan V. Uss + * + * BSD Licensed as described in the file LICENSE + */ +#ifndef __AM2320_H__ +#define __AM2320_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define AM2320_I2C_ADDR (0x5c) + +/** + * @brief Initialize device descriptor + * + * @param dev Device descriptor + * @param port I2C port + * @param sda_gpio SDA GPIO + * @param scl_gpio SCL GPIO + * @return `ESP_OK` on success + */ +esp_err_t am2320_init_desc(i2c_dev_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t am2320_free_desc(i2c_dev_t *dev); + +/** + * @brief Get temperature and relative humidity + * + * @param dev Device descriptor + * @param[out] temperature Temperature, degrees Celsius + * @param[out] humidity Relative humidity, percents + * @return `ESP_OK` on success + */ +esp_err_t am2320_get_rht(i2c_dev_t *dev, float *temperature, float *humidity); + +/** + * @brief Get device model ID + * + * @param dev Device descriptor + * @param[out] model Device model ID + * @return `ESP_OK` on success + */ +esp_err_t am2320_get_model(i2c_dev_t *dev, uint16_t *model); + +/** + * @brief Get device version + * + * @param dev Device descriptor + * @param[out] version Device version + * @return `ESP_OK` on success + */ +esp_err_t am2320_get_version(i2c_dev_t *dev, uint8_t *version); + +/** + * @brief Get device ID + * + * @param dev Device descriptor + * @param[out] id Device ID + * @return `ESP_OK` on success + */ +esp_err_t am2320_get_device_id(i2c_dev_t *dev, uint32_t *id); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __AM2320_H__ */ diff --git a/components/am2320/component.mk b/components/am2320/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/am2320/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/bh1750/.eil.yml b/components/bh1750/.eil.yml index 8b338d6e..912c3661 100644 --- a/components/bh1750/.eil.yml +++ b/components/bh1750/.eil.yml @@ -1,26 +1,23 @@ ---- -components: - - name: bh1750 - description: Driver for BH1750 light sensor - group: - name: light - groups: [] - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2018 - - name: Andrej - year: 2017 +name: bh1750 +description: Driver for BH1750 light sensor +version: 1.1.0 +groups: + - light +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2018 + - name: Andrej + year: 2017 diff --git a/components/bh1900nux/.eil.yml b/components/bh1900nux/.eil.yml index ac77e773..b8a783d1 100644 --- a/components/bh1900nux/.eil.yml +++ b/components/bh1900nux/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: bh1900nux - description: Driver for BH1900NUX temperature sensor - group: temperature - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2021 +name: bh1900nux +description: Driver for BH1900NUX temperature sensor +version: 1.0.0 +groups: + - temperature +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2021 diff --git a/components/bme680/.eil.yml b/components/bme680/.eil.yml index 0480d03b..7134374d 100644 --- a/components/bme680/.eil.yml +++ b/components/bme680/.eil.yml @@ -1,29 +1,25 @@ ---- -components: - - name: bme680 - description: | - Driver for BME680 digital environmental sensor - group: - name: pressure - groups: - - name: humidity - - name: temperature - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2019 - - name: gschorcht - year: 2017 +name: bme680 +description: Driver for BME680 digital environmental sensor +version: 1.0.0 +groups: + - pressure + - humidity + - temperature +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: gschorcht + year: 2017 + - name: UncleRus + year: 2019 diff --git a/components/bmp180/.eil.yml b/components/bmp180/.eil.yml index dbf998ae..0fe95249 100644 --- a/components/bmp180/.eil.yml +++ b/components/bmp180/.eil.yml @@ -1,28 +1,24 @@ ---- -components: - - name: bmp180 - description: | - Driver for BMP180 digital pressure sensor - group: - name: pressure - groups: - - name: temperature - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - name: UncleRus - year: 2018 - - name: FrankB - year: 2015 +name: bmp180 +description: Driver for BMP180 digital pressure sensor +version: 1.0.0 +groups: + - pressure + - temperature +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: FrankB + year: 2015 + - name: UncleRus + year: 2018 diff --git a/components/bmp280/.eil.yml b/components/bmp280/.eil.yml index 9e5fd2ae..e8c48cda 100644 --- a/components/bmp280/.eil.yml +++ b/components/bmp280/.eil.yml @@ -1,28 +1,24 @@ ---- -components: - - name: bmp280 - description: | - Driver for BMP280/BME280 digital pressure sensor - group: - name: pressure - groups: - - name: temperature - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - name: UncleRus - year: 2018 - - name: sheinz - year: 2016 +name: bmp280 +description: Driver for BMP280/BME280 digital pressure sensor +version: 1.0.0 +groups: + - pressure + - temperature +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: sheinz + year: 2016 + - name: UncleRus + year: 2018 diff --git a/components/button/.eil.yml b/components/button/.eil.yml index fbb949ef..20093e1c 100644 --- a/components/button/.eil.yml +++ b/components/button/.eil.yml @@ -1,22 +1,19 @@ ---- -components: - - name: button - description: | - HW timer-based driver for GPIO buttons - group: input - groups: [] - code_owners: - - name: UncleRus - depends: - - name: driver - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - name: UncleRus - year: 2021 +name: button +description: HW timer-based driver for GPIO buttons +version: 1.0.0 +groups: + - input +code_owners: + - UncleRus +depends: + - driver +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: UncleRus + year: 2021 diff --git a/components/calibration/.eil.yml b/components/calibration/.eil.yml new file mode 100644 index 00000000..844b220f --- /dev/null +++ b/components/calibration/.eil.yml @@ -0,0 +1,19 @@ +name: calibration +description: Multi-point calibration library +version: 0.1.0 +groups: + - common +code_owners: + - UncleRus +depends: + - log +thread_safe: n/a +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2023 diff --git a/components/calibration/CMakeLists.txt b/components/calibration/CMakeLists.txt new file mode 100644 index 00000000..95965328 --- /dev/null +++ b/components/calibration/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS calibration.c + INCLUDE_DIRS . + REQUIRES log +) diff --git a/components/calibration/LICENSE b/components/calibration/LICENSE new file mode 100644 index 00000000..f7f05cb2 --- /dev/null +++ b/components/calibration/LICENSE @@ -0,0 +1,26 @@ +Copyright 2023 Ruslan V. Uss + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/components/calibration/calibration.c b/components/calibration/calibration.c new file mode 100644 index 00000000..1a03eb91 --- /dev/null +++ b/components/calibration/calibration.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2023 Ruslan V. Uss + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file calibration.c + * + * ESP-IDF Multi-point calibration library + * + * Copyright (c) 2023 Ruslan V. Uss + * + * BSD Licensed as described in the file LICENSE + */ +#include "calibration.h" +#include +#include +#include + +#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) +#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) + +static const char *TAG = "calibration"; + +static float calc_linear_value(calibration_point_t *p1, calibration_point_t *p2, float code) +{ + float kx = (p2->value - p1->value) / (p2->code - p1->code); + float sx = kx * p1->code - p1->value; + return code * kx - sx; +} + +//////////////////////////////////////////////////////////////////////////////// + +esp_err_t calibration_init(calibration_handle_t *handler, size_t count, calibration_method_t type) +{ + CHECK_ARG(handler && count > 1); + + if (type != CALIBRATION_LINEAR) + { + ESP_LOGE(TAG, "Only type CALIBRATION_LINEAR is supported"); + return ESP_ERR_NOT_SUPPORTED; + } + + if (handler->points) + { + ESP_LOGE(TAG, "Handler already initialized"); + return ESP_FAIL; + } + + handler->type = type; + handler->count = count; + handler->filled = 0; + handler->points = calloc(sizeof(calibration_point_t), handler->count); + if (!handler->points) + { + ESP_LOGE(TAG, "Could not allocate memory for calibration points"); + return ESP_ERR_NO_MEM; + } + + return ESP_OK; +} + +esp_err_t calibration_add_point(calibration_handle_t *handler, float code, float value) +{ + CHECK_ARG(handler && handler->points); + + // FIXME: inefficient + for (size_t i = 0; i < handler->filled; i++) + if (handler->points[i].code == code) + { + handler->points[i].value = value; + return ESP_OK; + } + + if (handler->filled == handler->count) + { + ESP_LOGE(TAG, "List of calibration points is full"); + return ESP_ERR_NO_MEM; + } + + size_t pos; + for (pos = 0; pos < handler->filled; pos++) + if (handler->points[pos].code > code) + break; + + if (pos < handler->filled) + memmove(handler->points + pos + 1, handler->points + pos, sizeof(calibration_point_t) * (handler->filled - pos)); + + handler->points[pos].code = code; + handler->points[pos].value = value; + + handler->filled++; + + return ESP_OK; +} + +esp_err_t calibration_add_points(calibration_handle_t *handler, const calibration_point_t *points, size_t count) +{ + CHECK_ARG(points && count <= handler->count); + + for (size_t i = 0; i < count; i++) + CHECK(calibration_add_point(handler, points[i].code, points[i].value)); + + return ESP_OK; +} + +esp_err_t calibration_get_value(calibration_handle_t *handler, float code, float *value) +{ + CHECK_ARG(handler && handler->points && value); + + if (handler->filled < 2) + { + ESP_LOGE(TAG, "Not enough calibration points, need at least two"); + return ESP_FAIL; + } + + // looking for a segment + size_t pos; + for (pos = 0; pos < handler->filled; pos++) + { + if (handler->points[pos].code == code) + { + *value = handler->points[pos].value; + return ESP_OK; + } + if (handler->points[pos].code > code) + break; + } + if (pos > 0) + pos--; + if (pos == handler->filled - 1) + pos--; + + *value = calc_linear_value(handler->points + pos, handler->points + pos + 1, code); + + return ESP_OK; +} + +esp_err_t calibration_free(calibration_handle_t *handler) +{ + CHECK_ARG(handler); + + if (handler->points) + { + free(handler->points); + handler->points = NULL; + } + + return ESP_OK; +} diff --git a/components/calibration/calibration.h b/components/calibration/calibration.h new file mode 100644 index 00000000..d87015c7 --- /dev/null +++ b/components/calibration/calibration.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2023 Ruslan V. Uss + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file calibration.h + * @defgroup calibration calibration + * @{ + * + * ESP-IDF Multi-point calibration library + * + * Copyright (c) 2023 Ruslan V. Uss + * + * BSD Licensed as described in the file LICENSE + */ +#ifndef __CALIBRATION_H__ +#define __CALIBRATION_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Approximation methods + */ +typedef enum { + CALIBRATION_LINEAR = 0, //!< Fast linear approximation. The more points, the more accurate approximation +} calibration_method_t; + +/** + * Calibration point + */ +typedef struct +{ + float code; //!< Raw value + float value; //!< Calibrated value +} calibration_point_t; + +/** + * Calibration handler + */ +typedef struct +{ + calibration_method_t type; //!< Approximation method + calibration_point_t *points; //!< Ordered list of calibration points + size_t count; //!< Maximum number of calibration points + size_t filled; //!< Current number of calibration points +} calibration_handle_t; + +/** + * @brief Init calibration handle + * + * Allocates memory to store calibration points, fills handle structure. + * + * @param handler Pointer to calibration handle structure + * @param count Maximum number of calibration points + * @param type Approximation type + * + * @return `ESP_OK` on success + */ +esp_err_t calibration_init(calibration_handle_t *handler, size_t count, calibration_method_t type); + +/** + * @brief Add calibration point + * + * @param handler Pointer to calibration handle structure + * @param code Raw value + * @param value Calibrated value + * + * @return `ESP_OK` on success + */ +esp_err_t calibration_add_point(calibration_handle_t *handler, float code, float value); + +/** + * @brief Add multiple calibration points + * + * @param handler Pointer to calibration handle structure + * @param points Array of calibration points + * @param count Number of calibration points to add + * + * @return `ESP_OK` on success + */ +esp_err_t calibration_add_points(calibration_handle_t *handler, const calibration_point_t *points, size_t count); + +/** + * @brief Get calibrated value by raw value + * + * @param handler Pointer to calibration handle structure + * @param code Raw value + * @param[out] value Calculated calibrated value + * + * @return `ESP_OK` on success + */ +esp_err_t calibration_get_value(calibration_handle_t *handler, float code, float *value); + +/** + * @brief Free calibration handle + * + * @param handler Pointer to calibration handle structure + * + * @return `ESP_OK` on success + */ +esp_err_t calibration_free(calibration_handle_t *handler); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __CALIBRATION_H__ */ diff --git a/components/calibration/component.mk b/components/calibration/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/calibration/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/ccs811/.eil.yml b/components/ccs811/.eil.yml index edd08bd5..8b5f51c8 100644 --- a/components/ccs811/.eil.yml +++ b/components/ccs811/.eil.yml @@ -1,27 +1,24 @@ ---- -components: - - name: ccs811 - description: | - Driver for AMS CCS811 digital gas sensor - group: air-quality - groups: - - name: gas - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2021 - - name: gschorcht - year: 2017 +name: ccs811 +description: Driver for AMS CCS811 digital gas sensor +version: 1.0.0 +groups: + - air-quality + - gas +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2021 + - name: gschorcht + year: 2017 diff --git a/components/color/.eil.yml b/components/color/.eil.yml index cc2ef67c..0c8c9c97 100644 --- a/components/color/.eil.yml +++ b/components/color/.eil.yml @@ -1,22 +1,19 @@ ---- -components: - - name: color - description: | - Common library for RGB and HSV colors - group: common - groups: [] - code_owners: - - name: UncleRus - depends: - - name: lib8tion - thread_safe: N/A - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - name: FastLED - year: 2013 +name: color +description: Common library for RGB and HSV colors +version: 1.0.0 +groups: + - common +code_owners: + - UncleRus +depends: + - lib8tion +thread_safe: n/a +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: FastLED + year: 2013 diff --git a/components/dht/.eil.yml b/components/dht/.eil.yml index 8bfd2586..b3ad95e8 100644 --- a/components/dht/.eil.yml +++ b/components/dht/.eil.yml @@ -1,28 +1,25 @@ ---- -components: - - name: dht - description: | - Driver for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), Itead Si7021 - group: humidity - groups: - - name: temperature - code_owners: - - name: UncleRus - depends: - - name: log - - name: esp_idf_lib_helpers - - name: freertos - - name: driver - thread_safe: no - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2018 - - name: jsuiker - year: 2016 +name: dht +description: Driver for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), Itead Si7021 +version: 1.1.0 +groups: + - humidity + - temperature +code_owners: + - UncleRus +depends: + - log + - esp_idf_lib_helpers + - freertos + - driver +thread_safe: no +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2018 + - name: jsuiker + year: 2016 diff --git a/components/dps310/.eil.yml b/components/dps310/.eil.yml new file mode 100644 index 00000000..62f21ef7 --- /dev/null +++ b/components/dps310/.eil.yml @@ -0,0 +1,22 @@ +name: dps310 +description: Driver for DPS310 barometric pressure sensor +version: 1.0.0 +groups: + - pressure + - temperature +code_owners: + - trombik +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: ISC +copyrights: + - name: trombik + year: 2022 diff --git a/components/dps310/CMakeLists.txt b/components/dps310/CMakeLists.txt new file mode 100644 index 00000000..b4337654 --- /dev/null +++ b/components/dps310/CMakeLists.txt @@ -0,0 +1,22 @@ +idf_component_register( + + # sources to compile + SRCS src/dps310.c src/helper_i2c.c + + # public headers + INCLUDE_DIRS include + + # private headers + PRIV_INCLUDE_DIRS priv_include + + # Component Requirements + # https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html#component-requirements + + # components whose header files are #included from the public header files + # of this component. + REQUIRES log i2cdev + + # components whose header files are #included from any source files in + # this component, unless already listed in REQUIRES + PRIV_REQUIRES esp_idf_lib_helpers i2cdev +) diff --git a/components/dps310/Kconfig b/components/dps310/Kconfig new file mode 100644 index 00000000..90df0e02 --- /dev/null +++ b/components/dps310/Kconfig @@ -0,0 +1,10 @@ +menu "DPS310 driver" + choice DPS310_PROTOCOL + prompt "Choose protocol for digital interface" + default DPS310_PROTOCOL_USING_I2C + help + The protocol to use for digital interface + config DPS310_PROTOCOL_USING_I2C + bool "I2C" + endchoice +endmenu diff --git a/components/dps310/LICENSE b/components/dps310/LICENSE new file mode 100644 index 00000000..be64f346 --- /dev/null +++ b/components/dps310/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2022 Tomoyuki Sakurai + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/components/dps310/component.mk b/components/dps310/component.mk new file mode 100644 index 00000000..250ccfe5 --- /dev/null +++ b/components/dps310/component.mk @@ -0,0 +1,4 @@ +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers +COMPONENT_ADD_INCLUDEDIRS = include +COMPONENT_PRIV_INCLUDEDIRS = priv_include +COMPONENT_SRCDIRS := src diff --git a/components/dps310/include/dps310.h b/components/dps310/include/dps310.h new file mode 100644 index 00000000..fe7cd228 --- /dev/null +++ b/components/dps310/include/dps310.h @@ -0,0 +1,946 @@ +/* + * Copyright (c) 2022 Tomoyuki Sakurai + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored by @beriberikix + */ + +/** + * @file dps310.h + * @defgroup dps310 dps310 + * @{ + * + * ESP-IDF driver for DPS310 barometric pressure sensor. Sponsored by beriberikix. + * + * DPS310 supports I2C and SPI (3-wires and 4-wires) as digital interface. The + * driver currently supports: + * + * * I2C + * + * The driver currently does not support: + * + * * SPI + * * read measurements by interrupt + * * multi-master I2C configuration + * + * Note that the unit of pressure in this driver is pascal (Pa), not + * hectopascals (hPa). + * + * Note that the unit of altitude in this driver is meter. + * + */ +#if !defined(__DPS310_H__) +#define __DPS310_H__ + +/* standard headers */ +#include +#include +#include + +/* esp-idf headers */ +#include + +/* esp-idf-lib headers */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DPS310_I2C_ADDRESS_0 0x76 //!< I2C address when SDO pin is low +#define DPS310_I2C_ADDRESS_1 0x77 //!< I2C address when SDO pin is high + +#define DPS310_PRODUCT_ID 0x01 //!< Product ID +#define DPS310_REVISION_ID 0x00 //!< Revision ID + +#define DPS310_AVERAGE_SEA_LEVEL_PRESSURE_hPa (1013.25) //!< Average sea-level pressure in hPa +#define DPS310_AVERAGE_SEA_LEVEL_PRESSURE_Pa (DPS310_AVERAGE_SEA_LEVEL_PRESSURE_hPa * 10) //!< Average sea-level pressure in Pa + +/** + * Mode of DPS310 module operation. See 4.1 Operating Modes. + */ +typedef enum { + DPS310_MODE_STANDBY = 0b000, //!< Standby mode + DPS310_MODE_COMMAND_PRESSURE = 0b001, //!< Command mode, pressure measurement + DPS310_MODE_COMMAND_TEMPERATURE = 0b010, //!< Command mode, temperature measurement + DPS310_MODE_BACKGROUND_PRESSURE = 0b101, //!< Background mode, continuous pressure measurement + DPS310_MODE_BACKGROUND_TEMPERATURE = 0b110, //!< Background mode, continuous temperature measurement + DPS310_MODE_BACKGROUND_ALL = 0b111, //!< Background mode, continuous pressure and temperature measurement +} dps310_mode_t; + +/** + * Pressure measurement rate. + */ +typedef enum { + DPS310_PM_RATE_1 = 0b000, //!< 1 measurements / sec + DPS310_PM_RATE_2 = 0b001, //!< 2 measurements / sec + DPS310_PM_RATE_4 = 0b010, //!< 4 measurements / sec + DPS310_PM_RATE_8 = 0b011, //!< 8 measurements / sec + DPS310_PM_RATE_16 = 0b100, //!< 16 measurements / sec + DPS310_PM_RATE_32 = 0b101, //!< 32 measurements / sec + DPS310_PM_RATE_64 = 0b110, //!< 64 measurements / sec + DPS310_PM_RATE_128 = 0b111, //!< 128 measurements / sec +} dps310_pm_rate_t; + +/** + * Pressure resolution, or oversampling rate. + */ +typedef enum { + DPS310_PM_PRC_1 = 0b000, //!< Single (Low Precision) + DPS310_PM_PRC_2 = 0b001, //!< 2 times (Low Power). + DPS310_PM_PRC_4 = 0b010, //!< 4 times + DPS310_PM_PRC_8 = 0b011, //!< 8 times + DPS310_PM_PRC_16 = 0b100, //!< 16 times (Standard) + DPS310_PM_PRC_32 = 0b101, //!< 32 times + DPS310_PM_PRC_64 = 0b110, //!< 64 times (High Precision) + DPS310_PM_PRC_128 = 0b111, //!< 128 times +} dps310_pm_oversampling_t; + +/** + * Temperature measurement source. Used for temperature measurement and + * temperature coefficients. + */ + +typedef enum { + DPS310_TMP_SRC_INTERNAL = 0, //!< Internal sensor (in ASIC) + DPS310_TMP_SRC_EXTERNAL = 1, //!< External sensor (in pressure sensor MEMS element) +} dps310_tmp_src_ext_t; + +/** + * Temperature measurement rate. + */ +typedef enum { + DPS310_TMP_RATE_1 = 0b000, //!< 1 measurements / sec + DPS310_TMP_RATE_2 = 0b001, //!< 2 measurements / sec + DPS310_TMP_RATE_4 = 0b010, //!< 4 measurements / sec + DPS310_TMP_RATE_8 = 0b011, //!< 8 measurements / sec + DPS310_TMP_RATE_16 = 0b100, //!< 16 measurements / sec + DPS310_TMP_RATE_32 = 0b101, //!< 32 measurements / sec + DPS310_TMP_RATE_64 = 0b110, //!< 64 measurements / sec + DPS310_TMP_RATE_128 = 0b111, //!< 128 measurements / sec +} dps310_tmp_rate_t; + +/** + * Pressure resolution, or oversampling rate. + */ +typedef enum { + DPS310_TMP_PRC_1 = 0b000, //!< Single (Low Precision) + DPS310_TMP_PRC_2 = 0b001, //!< 2 times (Low Power). + DPS310_TMP_PRC_4 = 0b010, //!< 4 times + DPS310_TMP_PRC_8 = 0b011, //!< 8 times + DPS310_TMP_PRC_16 = 0b100, //!< 16 times (Standard) + DPS310_TMP_PRC_32 = 0b101, //!< 32 times + DPS310_TMP_PRC_64 = 0b110, //!< 64 times (High Precision) + DPS310_TMP_PRC_128 = 0b111, //!< 128 times +} dps310_tmp_oversampling_t; + +/** + * Interupt (on SDO pin) active level. + */ +typedef enum { + DPS310_INT_HL_ACTIVE_LOW = 0, //!< Active low + DPS310_INT_HL_ACTIVE_HIGH = 1, //!< Active high +} dps310_int_hl_active_level_t; + +/** + * Mode of interupt when the FIFO is full. + */ +typedef enum { + DPS310_INT_FIFO_DISABLE = 0, //!< Disable interrupt when the FIFO is full + DPS310_INT_FIFO_ENABLE = 1, //!< Enable interrupt when the FIFO is full +} dps310_int_fifo_mode_t; + +/** + * Mode of interupt when a temperature measurement is ready + */ +typedef enum { + DPS310_INT_TMP_DISABLE = 0, //!< Disable interrupt when a temperature measurement is ready + DPS310_INT_TMP_ENABLE = 1, //!< Enable interrupt when a temperature measurement is ready +} dps310_int_tmp_mode_t; + +/** + * Mode of interupt when a pressure measurement is ready + */ +typedef enum { + DPS310_INT_PRS_DISABLE = 0, //!< Disable interrupt when a pressure measurement is ready + DPS310_INT_PRS_ENABLE = 1, //!< Enable interrupt when a pressure measurement is ready +} dps310_int_prs_mode_t; + +/** + * Mode of temperature result bit-shift. + */ +typedef enum { + DPS310_T_SHIFT_DISABLE = 0, //!< No shift. + DPS310_T_SHIFT_ENABLE = 1, //!< Shift result right in data register. + // Must be set to '1' when the oversampling + // rate is >8 times. +} dps310_t_shift_mode_t; + +/** + * Mode of pressure result bit-shift. + */ +typedef enum { + DPS310_P_SHIFT_DISABLE = 0, //!< No shift. + DPS310_P_SHIFT_ENABLE = 1, //!< Shift result right in data register. + // Must be set to '1' when the oversampling + // rate is >8 times. +} dps310_p_shift_mode_t; + +/** + * Mode of FIFO. + */ +typedef enum { + DPS310_FIFO_DISABLE = 0, //!< Disable FIFO. + DPS310_FIFO_ENABLE = 1, //!< Enable FIFO. +} dps310_fifo_en_mode_t; + +/** + * SPI mode. + */ +typedef enum { + DPS310_SPI_MODE_4WIRE = 0, //!< SPI 4-wires + DPS310_SPI_MODE_3WIRE = 1, //!< SPI 3-wires +} dps310_spi_mode_t; + +/** + * Type of measurement result in FIFO. + * + * When the type is DPS310_MEASUREMENT_EMPTY, the result is always zero. + * Otherwise, the result is the compensated value of each type. + */ +typedef enum { + DPS310_MEASUREMENT_TEMPERATURE = 0, //!< Temperature + DPS310_MEASUREMENT_PRESSURE, //!< Pressure + DPS310_MEASUREMENT_EMPTY, //!< Empty, no measurement available +} dps310_fifo_measurement_type_t; + +typedef struct { + dps310_fifo_measurement_type_t type; + float result; +} dps310_fifo_measurement_t; + +/** + * Configuration parameters for DPS310. + */ +typedef struct { + dps310_pm_rate_t pm_rate; + dps310_pm_oversampling_t pm_oversampling; + dps310_tmp_rate_t tmp_rate; + dps310_tmp_oversampling_t tmp_oversampling; + dps310_tmp_src_ext_t tmp_src; + dps310_tmp_src_ext_t tmp_coef; + dps310_int_fifo_mode_t int_fifo_mode; + dps310_int_tmp_mode_t int_tmp_mode; + dps310_int_prs_mode_t int_prs_mode; + dps310_t_shift_mode_t t_shift_mode; + dps310_p_shift_mode_t p_shift_mode; + dps310_fifo_en_mode_t fifo_en_mode; + dps310_spi_mode_t spi_mode; + +} dps310_config_t; + +/** + * A macro to set default dps310_config_t. + */ + +#define DPS310_CONFIG_DEFAULT() { \ + .pm_rate = DPS310_PM_RATE_1, \ + .pm_oversampling = DPS310_PM_PRC_16, \ + .tmp_rate = DPS310_TMP_RATE_1, \ + .tmp_oversampling = DPS310_TMP_PRC_16, \ + .tmp_src = DPS310_TMP_SRC_EXTERNAL, \ + .tmp_coef = DPS310_TMP_SRC_EXTERNAL, \ + .int_fifo_mode = DPS310_INT_FIFO_DISABLE, \ + .int_tmp_mode = DPS310_INT_TMP_DISABLE, \ + .int_prs_mode = DPS310_INT_PRS_DISABLE, \ + .t_shift_mode = DPS310_T_SHIFT_ENABLE, \ + .p_shift_mode = DPS310_P_SHIFT_ENABLE, \ + .fifo_en_mode = DPS310_FIFO_DISABLE, \ + .spi_mode = DPS310_SPI_MODE_4WIRE, \ +} + +/** + * Calibration Coefficients (COEF). + */ +typedef struct { + int32_t c0; + int32_t c1; + int32_t c00; + int32_t c10; + int32_t c01; + int32_t c11; + int32_t c20; + int32_t c21; + int32_t c30; +} dps310_coef_t; + +/** + * Device descriptor. + */ +typedef struct { + i2c_dev_t i2c_dev; //!< I2C device descriptor + uint8_t prod_id; //!< Product ID + uint8_t prod_rev; //!< Product revision + uint8_t t_rate; //!< latest P_rate + uint8_t p_rate; //!< latest T_rate + int32_t t_raw; //!< latest T_raw + dps310_coef_t coef; //!< coefficients + float offset; //!< offset in meter + float pressure_s; //!< calculated pressure at sea-level +} dps310_t; + +/** + * DPS310 registers + */ + +/* 7 Register Map */ +#define DPS310_REG_PRS_B2 0x00 +#define DPS310_REG_PRS_B1 0x01 +#define DPS310_REG_PRS_B0 0x02 +#define DPS310_REG_TMP_B2 0x03 +#define DPS310_REG_TMP_B1 0x04 +#define DPS310_REG_TMP_B0 0x05 +#define DPS310_REG_PRS_CFG 0x06 +#define DPS310_REG_TMP_CFG 0x07 +#define DPS310_REG_MEAS_CFG 0x08 +#define DPS310_REG_CFG_REG 0x09 +#define DPS310_REG_INT_STS 0x0a +#define DPS310_REG_FIFO_STS 0x0b +#define DPS310_REG_RESET 0x0c +#define DPS310_REG_ID 0x0d +#define DPS310_REG_COEF 0x10 +#define DPS310_REG_COEF_LEN (18) +#define DPS310_REG_COEF_SRCE 0x28 + +/* various masks */ +#define DPS310_REG_ID_REV_MASK (0x0f) +#define DPS310_REG_ID_PROD_MASK (0xf0) +#define DPS310_REG_RESET_FIFO_FLUSH_MASK (1 << 7) +#define DPS310_REG_RESET_SOFT_RST_MASK (0x0f) +#define DPS310_REG_PRS_CFG_PM_RATE_MASK (0b111 << 4) +#define DPS310_REG_PRS_CFG_TMP_RATE_MASK (0b111 << 4) +#define DPS310_REG_PRS_CFG_PM_PRC_MASK (0b1111) +#define DPS310_REG_PRS_CFG_TMP_EXT_MASK (1 << 7) +#define DPS310_REG_TMP_CFG_TMP_PRC_MASK (0b1111) +#define DPS310_REG_CFG_REG_INT_HL_MASK (1 << 7) +#define DPS310_REG_CFG_REG_INT_FIFO_MASK (1 << 6) +#define DPS310_REG_CFG_REG_INT_TMP_MASK (1 << 5) +#define DPS310_REG_CFG_REG_INT_PRS_MASK (1 << 4) +#define DPS310_REG_CFG_REG_T_SHIFT_MASK (1 << 3) +#define DPS310_REG_CFG_REG_P_SHIFT_MASK (1 << 2) +#define DPS310_REG_CFG_REG_FIFO_EN_MASK (1 << 1) +#define DPS310_REG_CFG_REG_SPI_MODE_MASK (1 << 0) +#define DPS310_REG_MEAS_CFG_COEF_RDY_MASK (1 << 7) +#define DPS310_REG_MEAS_CFG_SENSOR_RDY_MASK (1 << 6) +#define DPS310_REG_MEAS_CFG_TMP_RDY_MASK (1 << 5) +#define DPS310_REG_MEAS_CFG_PRS_RDY_MASK (1 << 4) +#define DPS310_REG_MEAS_CFG_MEAS_CTRL_MASK (0b111) +#define DPS310_REG_COEF_SRCE_MASK (1 << 7) +#define DPS310_REG_FIFO_STS_FIFO_EMPTY_MASK (1) +#define DPS310_REG_FIFO_STS_FIFO_FULL_MASK (1 << 1) + +/* See 3.6 Timing Characteristics */ +#define DPS310_I2C_FREQ_MAX_HZ (3400000) // Max 3.4 MHz + // +/* I2C master driver does not support higher than 1MHz + * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/i2c.html#_CPPv4N12i2c_config_t9clk_speedE + */ +#define DPS310_I2C_FREQ_MAX_ESP_IDF_HZ (1000000) +#define DPS310_SPI_FREQ_MAX_HZ (10000000) // Max 10 MHz + // +/* See 4.3 Start-up sequence + * + * XXX the datasheet is ambiguous in the start-up sequence. Trim_complete is + * mentioned in nowhere. the DPS310-Pressure-Sensor by Infineon uses 50 ms. + * don't know what the "40ms" in the chart means. to be safe, use the sum of + * all the numbers in the chart. + */ +#define DPS310_TRIM_READY_DELAY_MS (3) // 2.5 ms +#define DPS310_SENSOR_READY_DELAY_MS (12) +#define DPS310_COEFFICIENTS_READY_DELAY_MS (40) +#define DPS310_STARTUP_DELAY_MS (DPS310_TRIM_READY_DELAY_MS + DPS310_SENSOR_READY_DELAY_MS + DPS310_COEFFICIENTS_READY_DELAY_MS) + +#define DPS310_PROD_ID 0x01 + +/* temperature and pressure use three resisters for 24 bits values. */ +#define DPS310_REG_SENSOR_VALUE_LEN (3) + +/* 4.8 FIFO Operation */ +#define DPS310_REG_FIFO DPS310_REG_PRS_B2 //! Resister address of FIFO. +#define DPS310_FIFO_EMPTY (0xff800000) //! the value of two's complement in the resisters when no measurement is in the FIFO. + +/* See 8.9 Soft Reset and FIFO flush (RESET) */ +#define DPS310_FIFO_FLUSH_VALUE (1 << 7) +#define DPS310_SOFT_RST_VALUE (0b1001) + +/** + * @brief Initialize device descriptor + * + * @param[out] dev The device descriptor. + * @param[in] addr DPS310's I2C address + * @param[in] port I2C port number to use. + * See available I2C port at: + * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/i2c.html#_CPPv410i2c_port_t + * @param[in] sda_gpio GPIO pin for SDA + * @param[in] scl_gpio GPIO pin for SCL + * @return `ESP_OK` on success + */ +esp_err_t dps310_init_desc(dps310_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free the device descriptor. + * + * The device descriptor must NOT be NULL. `dps310_free_desc()` does not + * `free()` the device descriptor on error. + * + * @param[out] dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t dps310_free_desc(dps310_t *dev); + +/** + * @brief Initialize DPS310 module + * + * The function does the followings: + * + * - read the DPS310_REG_ID, and identify the product ID. Return ESP_FAIL if + * the product ID does not match expected product ID. + * - reset the chip + * - perform a quirk + * + * @param[in] dev Device descriptor + * @param[in] config Configuration + * @return `ESP_OK` on success + */ +esp_err_t dps310_init(dps310_t *dev, dps310_config_t *config); + +/** + * @brief Reset the device. + * + * Perform "soft reset" and ensure the chip is fully functional by delaying + * `DPS310_STARTUP_DELAY_MS`. + * + * @param[in] dev The device descriptor + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `config` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_reset(dps310_t *dev); + +/** + * @brief Get pressure measurement rate. + * + * @param[in] dev The device descriptor + * @param[out] value the value in the resister + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` and/or + * `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_rate_p(dps310_t *dev, dps310_pm_rate_t *value); + +/** + * @brief Set pressure measurement rate. + * + * @param[in] dev The device descriptor. + * @param[in] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `config` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_rate_p(dps310_t *dev, dps310_pm_rate_t value); + +/** + * @brief Get temperature measurement rate. + * + * @param[in] dev The device descriptor + * @param[out] value the value in the resister + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` and/or + * `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_rate_t(dps310_t *dev, dps310_tmp_rate_t *value); + +/** + * @brief Set temperature measurement rate. + * + * @param[in] dev The device descriptor. + * @param[in] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `config` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_rate_t(dps310_t *dev, dps310_tmp_rate_t value); + +/** + * @brief Get pressure oversampling rate. + * + * @param[in] dev The device descriptor + * @param[out] value the value in the resister + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` and/or + * `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_oversampling_p(dps310_t *dev, dps310_pm_oversampling_t *value); + +/** + * @brief Set pressure oversampling rate. + * + * @param[in] dev The device descriptor. + * @param[in] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `config` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_oversampling_p(dps310_t *dev, dps310_pm_oversampling_t value); + +/** + * @brief Get temperature oversampling rate. + * + * @param[in] dev The device descriptor + * @param[out] value the value in the resister + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` and/or + * `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_oversampling_t(dps310_t *dev, dps310_tmp_oversampling_t *value); + +/** + * @brief Set temperature oversampling rate. + * + * @param[in] dev The device descriptor. + * @param[in] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `config` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_oversampling_t(dps310_t *dev, dps310_tmp_oversampling_t value); + +/** + * @brief Get temperature measurement source. + * + * @param[in] dev The device descriptor. + * @param[out] value the value in the resister. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` and/or `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_tmp_ext(dps310_t *dev, dps310_tmp_src_ext_t *value); + +/** + * @brief Set temperature measurement source. + * + * @param[in] dev The device descriptor. + * @param[in] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_tmp_ext(dps310_t *dev, dps310_tmp_src_ext_t value); + +/** + * @brief Set temperature coefficient source. + * + * @param[in] dev The device descriptor. + * @param[in] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_tmp_coef_ext(dps310_t *dev, dps310_tmp_src_ext_t value); + +/** + * @brief Get interrupt active level. + * + * @param[in] dev The device descriptor. + * @param[out] value the value in the resister. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_int_hl(dps310_t *dev, dps310_int_hl_active_level_t *value); + +/** + * @brief Set interrupt active level. + * + * @param[in] dev The device descriptor. + * @param[in] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_int_hl(dps310_t *dev, dps310_int_hl_active_level_t value); + +/** + * @brief Get the status of FIFO interrupt. + * + * @param[in] dev The device descriptor. + * @param[out] value Current configuration of INT_FIFO. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_int_fifo(dps310_t *dev, dps310_int_fifo_mode_t *value); + +/** + * @brief Set the status of FIFO interrupt. + * + * @param[in] dev The device descriptor. + * @param[out] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_int_fifo(dps310_t *dev, dps310_int_fifo_mode_t value); + +/** + * @brief Get the status of temperature interrupt. + * + * @param[in] dev The device descriptor. + * @param[out] value Current configuration of INT_TMP. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_int_tmp(dps310_t *dev, dps310_int_tmp_mode_t *value); + +/** + * @brief Set the status of temperature interrupt. + * + * @param[in] dev The device descriptor. + * @param[out] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_int_tmp(dps310_t *dev, dps310_int_tmp_mode_t value); + +/** + * @brief Get the status of pressure interrupt. + * + * @param[in] dev The device descriptor. + * @param[out] value Current configuration of INT_PRS. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_int_prs(dps310_t *dev, dps310_int_prs_mode_t *value); + +/** + * @brief Set the status of pressure interrupt. + * + * @param[in] dev The device descriptor. + * @param[out] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_int_prs(dps310_t *dev, dps310_int_prs_mode_t value); + +/** + * @brief Get the status of temperature result bit-shift. + * + * @param[in] dev The device descriptor. + * @param[out] value Current configuration of T_SHIFT. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_t_shift(dps310_t *dev, dps310_t_shift_mode_t *value); + +/** + * @brief Set the status of temperature result bit-shift. + * + * Must be set to DPS310_T_SHIFT_ENABLE when the oversampling rate is >8 times. + * + * @param[in] dev The device descriptor. + * @param[out] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_t_shift(dps310_t *dev, dps310_t_shift_mode_t value); + +/** + * @brief Get the status of pressure result bit-shift. + * + * @param[in] dev The device descriptor. + * @param[out] value Current configuration of T_SHIFT. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_p_shift(dps310_t *dev, dps310_p_shift_mode_t *value); + +/** + * @brief Set the status of pressure result bit-shift. + * + * Must be set to DPS310_P_SHIFT_ENABLE when the oversampling rate is >8 times. + * + * @param[in] dev The device descriptor. + * @param[out] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_p_shift(dps310_t *dev, dps310_p_shift_mode_t value); + +/** + * @brief Get the status of FIFO. + * + * @param[in] dev The device descriptor. + * @param[out] value Current configuration of FIFO_EN. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_fifo_en(dps310_t *dev, dps310_fifo_en_mode_t *value); + +/** + * @brief Set the status of FIFO. + * + * @param[in] dev The device descriptor. + * @param[out] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_fifo_en(dps310_t *dev, dps310_fifo_en_mode_t value); + +/** + * @brief Get the mode of SPI. + * + * @param[in] dev The device descriptor. + * @param[out] value Current configuration of SPI_MODE. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` and/or `value` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_spi_mode(dps310_t *dev, dps310_spi_mode_t *value); + +/** + * @brief Set the mode of SPI. + * + * @param[in] dev The device descriptor. + * @param[out] value The value to set. + * @return `ESP_OK` on success, `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_spi_mode(dps310_t *dev, dps310_spi_mode_t value); + +/** + * @brief Get Calibration Coefficients (COEF), update COEF in the device + * descriptor. + * + * @param[in] dev The device descriptor. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_get_coef(dps310_t *dev); + +/** + * @brief Get operating mode. + * + * @param[in] dev The device descriptor. + * @param[out] mode The operating mode. + */ +esp_err_t dps310_get_mode(dps310_t *dev, dps310_mode_t *mode); + +/** + * @brief Set operating mode. + * + * @param[in] dev The device descriptor. + * @param[in] mode The operating mode. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_set_mode(dps310_t *dev, dps310_mode_t mode); + +/** + * @brief Flush FIFO. + * + * @param[in] dev The device descriptor. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_flush_fifo(dps310_t *dev); + +/** + * @brief Enable or disable FIFO. + * + * The function performs flush (`dps310_flush_fifo()`) before disabling FIFO. + * + * @param[in] dev The device descriptor. + * @param[in] enable Enable FIFO when true, disable FIFO when false. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_enable_fifo(dps310_t *dev, bool enable); + +/** + * @brief Read the raw sensor value from resisters. + * + * The real raw value is 2's complement. The function internally converts the + * value from the 2's complement to uint32_t number. + * + * @param[in] dev The device descriptor. + * @param[in] reg Either `DPS310_REG_TMP_B2` or `DPS310_REG_PRS_B2`. + * @param[out] value The raw value in the three resisters. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_read_raw(dps310_t *dev, uint8_t reg, int32_t *value); + +/** + * @brief Read compensated pressure value. + * + * @param[in] dev The device descriptor. + * @param[out] pressure Compensated pressure value in Pascal (not hPa). + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_read_pressure(dps310_t *dev, float *pressure); + +/** + * @brief Read compensated temperature value after waiting for PRES_RDY bit. + * + * @param[in] dev The device descriptor. + * @param[in] delay_ms Time in microseconds to wait when the value is not ready. + * @param[in] max_attempt Number of attempt to read. + * @param[out] pressure Compensated pressure value in Pascal (not hPa). + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL. ESP_ERR_TIMEOUT when failed to read the measurement within max_attempt, or other errors when I2C communication fails. + */ +esp_err_t dps310_read_pressure_wait(dps310_t *dev, uint16_t delay_ms, uint8_t max_attempt, float *pressure); + +/** + * @brief Read compensated temperature value. + * + * @param[in] dev The device descriptor. + * @param[out] temperature Compensated temperature value. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_read_temp(dps310_t *dev, float *temperature); + +/** + * @brief Read compensated temperature value after waiting for TMP_RDY bit. + * + * @param[in] dev The device descriptor. + * @param[in] delay_ms Time in microseconds to wait when the value is not ready. + * @param[in] max_attempt Number of attempt to read. + * @param[out] temperature Compensated temperature value. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL. ESP_ERR_TIMEOUT when failed to read the measurement within max_attempt, or other errors when I2C communication fails. + */ +esp_err_t dps310_read_temp_wait(dps310_t *dev, uint16_t delay_ms, uint8_t max_attempt, float *temperature); + +/** + * @brief Test if a single bit in a resister is set. + * + * @param[in] dev The device descriptor. + * @param[in] reg The resister + * @param[in] mask bit mask to test + * @param[out] ready true when the bit is set, false when the bit is cleared. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_is_ready_for(dps310_t *dev, uint8_t reg, uint8_t mask, bool *ready); + +/** + * @brief Test COEF_RDY in MEAS_CFG resister is set. + * + * @param[in] dev The device descriptor. + * @param[out] ready true when the bit is set, false when the bit is cleared. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_is_ready_for_coef(dps310_t *dev, bool *ready); + +/** + * @brief Test SENSOR_RDY in MEAS_CFG resister is set. + * + * @param[in] dev The device descriptor. + * @param[out] ready true when the bit is set, false when the bit is cleared. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_is_ready_for_sensor(dps310_t *dev, bool *ready); + +/** + * @brief Test TMP_RDY in MEAS_CFG resister is set. + * + * @param[in] dev The device descriptor. + * @param[out] ready true when the bit is set, false when the bit is cleared. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_is_ready_for_temp(dps310_t *dev, bool *ready); + +/** + * @brief Test PRS_RDY in MEAS_CFG resister is set. + * + * @param[in] dev The device descriptor. + * @param[out] ready true when the bit is set, false when the bit is cleared. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_is_ready_for_pressure(dps310_t *dev, bool *ready); + +/** + * @brief Reset undocumented internal resisters. + * + * The function is supposed to fix an issue in the sensor by writing magic + * values to magic resisters. However, the issue is not documented. The + * latest data sheet does not mention the issue, nor an errata. + * + * After issuing magic commands, the function re-reads COEF and temperature + * once so that the subsequent pressure reads return compensated values with + * internal cached parameters. + * + * See: + * https://github.com/Infineon/DPS310-Pressure-Sensor#temperature-measurement-issue + * https://github.com/Infineon/DPS310-Pressure-Sensor/blob/3edb0e58dfd7691491ae8d7f6a86277b001ad93f/src/DpsClass.cpp#L442-L461 + * https://github.com/Infineon/DPS310-Pressure-Sensor/blob/ed02f803fc780cbcab54ed8b35dd3d718f2ebbda/src/Dps310.cpp#L84-L86 + * https://github.com/Infineon/DPS310-Pressure-Sensor/issues/15#issuecomment-475394536 + * + * @param[in] dev The device descriptor. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_quirk(dps310_t *dev); + +/** + * @brief See if FIFO is empty. + * + * @param[in] dev The device descriptor. + * @param[out] result The result. true if empty, false otherwise. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_is_fifo_empty(dps310_t *dev, bool *result); + +/** + * @brief Read measurement result from FIFO. + * + * @param[in] dev The device descriptor. + * @param[out] measurement Measured value. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_read_fifo(dps310_t *dev, dps310_fifo_measurement_t *measurement); + +/** + * @brief Start background measurement. + * + * This function is a syntax-sugar of `dps310_set_mode()` just for readbility + * and for an emphasis on a fact that measurement starts immediately after + * this. + * + * @param[in] dev The device descriptor. + * @param[in] mode The mode of background measurement. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_backgorund_start(dps310_t *dev, dps310_mode_t mode); + +/** + * @brief Stop background measurement. + * + * This function is a syntax-sugar of `dps310_set_mode()`. + * + * @param[in] dev The device descriptor. + * @return `ESP_OK` on success. `ESP_ERR_INVALID_ARG` when `dev` is NULL, or other errors when I2C communication fails. + */ +esp_err_t dps310_backgorund_stop(dps310_t *dev); + +/** + * @brief Calibrate altitude offset from the altitude of the device. + * + * Call this function before dps310_read_altitude() for higher accuracy. + * + * By default, the driver calculates altitude using average sea-level + * pressure. This function updates internal offset of altitude by reading + * pressure from the sensor, and given altitude. There are public web services + * that provide altitude at a specific location, such as Google Earth. + * + * The function attempts to keep original oversampling rates during + * calibration. When it fails to do so due to errors, the oversampling rates + * might be different. + * + * @param[in] dev The device descriptor. + * @param[in] altitude_real Real (known) altitude. + */ +esp_err_t dps310_calibrate_altitude(dps310_t *dev, float altitude_real); + +/** + * @brief Calculate altitude from pressure. + * + * Calculates altitude from pressure given. Call dps310_calibrate_altitude() + * before this function for higher accuracy. The function adds the offset to + * calculated altitude. + * + * @param[in] dev The device descriptor. + * @param[in] pressure The pressure. + * @param[out] altitude The calicurated altitude. + */ +esp_err_t dps310_calc_altitude(dps310_t *dev, float pressure, float *altitude); + +/** + * @brief Read pressure from the sensor, calculates altitude. + * + * Make sure that pressure measurement value is available. + * + * @param[in] dev The device descriptor. + * @param[out] altitude The calculated altitude. + */ +esp_err_t dps310_read_altitude(dps310_t *dev, float *altitude); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif // __DPS310_H__ diff --git a/components/dps310/priv_include/helper_i2c.h b/components/dps310/priv_include/helper_i2c.h new file mode 100644 index 00000000..43ed3ba1 --- /dev/null +++ b/components/dps310/priv_include/helper_i2c.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022 Tomoyuki Sakurai + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * A private header for I2C interface. Most of them are a wrapper of i2cdev + * functions. + * + * Functions in the header should be prefixed with `_` to indicate they are + * a private function. + * + * A postfix, `_nolock` in function name means the function does not acquire + * I2C lock in `i2cdev`. + */ + +#if !defined(__DPS310__HELPER_I2C__H__) +#define __DPS310__HELPER_I2C__H__ + +/* standard headers */ +#include + +/* esp-idf headers */ +#include +#include + +/* private headers */ +#include "helper_macro.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Read a single byte from a 8-bit resister with locking. + */ +esp_err_t _read_reg(i2c_dev_t *dev, uint8_t reg, uint8_t *val); + +/** + * @brief Read a masked value from a 8-bit resister with locking. + * + * The returned value is bit-shifted. When `mask` is `0b1111000`, and the + * value in the resister is `0b00010000`, `val` is `0b0001`. + */ +esp_err_t _read_reg_mask(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t *val); + +/** + * @brief Write a single byte to a 8-bit resister with locking. + */ +esp_err_t _write_reg(i2c_dev_t *dev, uint8_t reg, uint8_t *value); + +/** + * @brief Update a 8-bit resister with a masked value without locking. + * + * `mask` is the mask to update, and `val` is the value. The function reads + * the resister, and see if the bit values in the resister needs update. If + * the bit value is identical with `val`, it does not update the resister. + * + * `val` is automatically bit-shifted when updating the resister. + * + * Useful to update specific bits in a resister. + * + * See also: _update_reg(). + */ +esp_err_t _update_reg_nolock(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t val); + +/** + * @brief Update a 8-bit resister with a masked value. + */ +esp_err_t _update_reg(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t val); + +esp_err_t _wait_for_reg_bits(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t val, uint8_t max_attempt, uint16_t delay_ms); + +#ifdef __cplusplus +} +#endif + +#endif // __DPS310__HELPER_I2C__H__ diff --git a/components/dps310/priv_include/helper_macro.h b/components/dps310/priv_include/helper_macro.h new file mode 100644 index 00000000..9213ec61 --- /dev/null +++ b/components/dps310/priv_include/helper_macro.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Tomoyuki Sakurai + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__DPS310_HELPER_MACRO_H__) +#define __DPS310_HELPER_MACRO_H__ + +#define BV(x) ((uint8_t)(1 << (x))) +#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) +#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) +#define CHECK_LOGE(dev, x, msg, ...) do { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) { \ + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); \ + ESP_LOGE(TAG, msg, ## __VA_ARGS__); \ + return __; \ + } \ + } while (0) + +#endif diff --git a/components/dps310/src/dps310.c b/components/dps310/src/dps310.c new file mode 100644 index 00000000..61b2db3c --- /dev/null +++ b/components/dps310/src/dps310.c @@ -0,0 +1,1212 @@ +/* + * Copyright (c) 2022 Tomoyuki Sakurai + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file dps310.c + * + * ESP-IDF driver for DPS310. Sponserd by @beriberikix. + * + * Note that the driver always tries to compensate raw values from the sensor. + * When compensation is not required, or undesired, users should implement + * their own functions to bypass compensation. + */ + +/* standard headers */ +#include +#include +#include +#include + +/* esp-idf headers */ +#include +#include +#include +#include +#include +#include + +/* private headers */ +#include "helper_macro.h" +#include "helper_i2c.h" + +/* public headers */ +#include "dps310.h" + +#define DPS310_QUIRK_DELAY_MS (10) +#define DPS310_QUIRK_MAX_ATTEMPT (5) + +static const char *TAG = "dps310"; + +/* see 4.9.3 Compensation Scale Factors */ +#define N_SCALE_FACTORS (8) +static const int32_t scale_factors[N_SCALE_FACTORS] = { + 524288, + 1572864, + 3670016, + 7864320, + 253952, + 516096, + 1040384, + 2088960 +}; + +esp_err_t dps310_quirk(dps310_t *dev) +{ + esp_err_t err = ESP_FAIL; + const int magic_command_len = 5; + float ignore = 0; + const uint8_t magic_commands[5][2] = { + + /* reg address, value */ + { 0xA5, 0x0E }, + { 0x0F, 0x96 }, + { 0x62, 0x02 }, + { 0x0E, 0x00 }, + { 0x0F, 0x00 }, + }; + dps310_mode_t original_mode = 0; + + CHECK_ARG(dev); + ESP_LOGD(TAG, "dps310_quirk(): resetting resisters with magic numbers"); + for (int i = 0; i < magic_command_len; i++) + { + uint8_t reg = magic_commands[i][0]; + uint8_t value = magic_commands[i][1]; + err = _write_reg(&dev->i2c_dev, reg, &value); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "_write_reg(): %s (reg: 0x%02X)", esp_err_to_name(err), reg); + break; + } + } + if (err != ESP_OK) + { + goto fail; + } + vTaskDelay(pdMS_TO_TICKS(DPS310_STARTUP_DELAY_MS)); + + /* sensor is ready? */ + err = _wait_for_reg_bits(&dev->i2c_dev, DPS310_REG_MEAS_CFG, DPS310_REG_MEAS_CFG_SENSOR_RDY_MASK, 1, DPS310_QUIRK_MAX_ATTEMPT, DPS310_QUIRK_DELAY_MS); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "_wait_for_reg_bits(): %s", esp_err_to_name(err)); + goto fail; + } + /* coef is ready? */ + err = _wait_for_reg_bits(&dev->i2c_dev, DPS310_REG_MEAS_CFG, DPS310_REG_MEAS_CFG_COEF_RDY_MASK, 1, DPS310_QUIRK_MAX_ATTEMPT, DPS310_QUIRK_DELAY_MS); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "_wait_for_reg_bits(): %s", esp_err_to_name(err)); + goto fail; + } + + ESP_LOGD(TAG, "dps310_quirk(): reading COEF"); + err = dps310_get_coef(dev); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_get_coef(): %s", esp_err_to_name(err)); + goto fail; + } + + ESP_LOGD(TAG, "dps310_quirk(): keep the original operation mode"); + err = dps310_get_mode(dev, &original_mode); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_get_mode(): %s", esp_err_to_name(err)); + goto fail; + } + + ESP_LOGD(TAG, "dps310_quirk(): setting mode to DPS310_MODE_COMMAND_TEMPERATURE"); + err = dps310_set_mode(dev, DPS310_MODE_COMMAND_TEMPERATURE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "dps310_set_mode(): %s", esp_err_to_name(err)); + goto fail; + } + + ESP_LOGD(TAG, "dps310_quirk(): reading temperature just once"); + err = dps310_read_temp_wait(dev, DPS310_QUIRK_DELAY_MS, DPS310_QUIRK_MAX_ATTEMPT, &ignore); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_temp_wait(): %s", esp_err_to_name(err)); + goto fail; + } + + ESP_LOGD(TAG, "dps310_quirk(): restore the original mode"); + err = dps310_set_mode(dev, original_mode); + if (err != ESP_OK) { + ESP_LOGE(TAG, "dps310_set_mode(): %s", esp_err_to_name(err)); + goto fail; + } + +fail: + return err; +} + +esp_err_t dps310_init_desc(dps310_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + esp_err_t err = ESP_FAIL; + + if (dev == NULL) + { + err = ESP_ERR_INVALID_ARG; + goto fail; + } + if (addr != DPS310_I2C_ADDRESS_0 && addr != DPS310_I2C_ADDRESS_1) + { + ESP_LOGE(TAG, "Invalid I2C address: expected %0X or %0X, got %0X", + DPS310_I2C_ADDRESS_0, DPS310_I2C_ADDRESS_1, addr); + err = ESP_ERR_INVALID_ARG; + goto fail; + } + + dev->i2c_dev.port = port; + dev->i2c_dev.addr = addr; + dev->i2c_dev.cfg.sda_io_num = sda_gpio; + dev->i2c_dev.cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->i2c_dev.cfg.master.clk_speed = DPS310_I2C_FREQ_MAX_ESP_IDF_HZ; +#endif + + ESP_LOGD(TAG, "Port: %u SCL: GPIO%i SDA: GPIO%i", + port, + dev->i2c_dev.cfg.scl_io_num, + dev->i2c_dev.cfg.sda_io_num); + err = i2c_dev_create_mutex(&dev->i2c_dev); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "i2c_dev_create_mutex(): %s", esp_err_to_name(err)); + goto fail; + } + dev->pressure_s = DPS310_AVERAGE_SEA_LEVEL_PRESSURE_Pa; + dev->offset = 0; + +fail: + return err; + +} + +esp_err_t dps310_free_desc(dps310_t *dev) +{ + esp_err_t err = ESP_FAIL; + + err = i2c_dev_delete_mutex(&dev->i2c_dev); + if (err != ESP_OK) + { + ESP_LOGW(TAG, "i2c_dev_delete_mutex(): %s", esp_err_to_name(err)); + goto fail; + } +fail: + return err; +} + +static int pow_int(int base, int x) +{ + int result = 1; + + for (int i = 0; i < x ; ++i) + { + result *= base; + } + return result; +} + +esp_err_t dps310_init(dps310_t *dev, dps310_config_t *config) +{ + uint8_t reg_value = 0; + esp_err_t err = ESP_FAIL; + + if (dev == NULL) + { + ESP_LOGE(TAG, "dps310_init(): invalid dev"); + err = ESP_ERR_INVALID_ARG; + goto fail; + } + if (config == NULL) + { + ESP_LOGE(TAG, "dps310_init(): invalid config"); + err = ESP_ERR_INVALID_ARG; + goto fail; + } + + err = _read_reg(&dev->i2c_dev, DPS310_REG_ID, ®_value); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "Device not found"); + goto fail; + } + + dev->prod_id = (DPS310_REG_ID_PROD_MASK & reg_value) >> 4; + dev->prod_rev = DPS310_REG_ID_REV_MASK & reg_value; + ESP_LOGD(TAG, "prod_id: 0x%x prod_rev: 0x%x", dev->prod_id, dev->prod_rev); + if (dev->prod_id != DPS310_PROD_ID) + { + ESP_LOGE(TAG, "Invalid prod ID: expected: 0x%x (DPS310) got: 0x%x", DPS310_PROD_ID, dev->prod_id); + err = ESP_FAIL; + goto fail; + } + + err = dps310_reset(dev); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_reset(): %s", esp_err_to_name(err)); + goto fail; + } + + err = dps310_quirk(dev); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_quirk(): %s", esp_err_to_name(err)); + goto fail; + } + + ESP_LOGD(TAG, "Pressure measurement rate: %i measurements / sec", pow_int(2, config->pm_rate)); + err = dps310_set_rate_p(dev, config->pm_rate); + if (err != ESP_OK) + { + goto fail; + } + ESP_LOGD(TAG, "Pressure oversampling: %i time(s)", pow_int(2, config->pm_oversampling)); + err = dps310_set_oversampling_p(dev, config->pm_oversampling); + if (err != ESP_OK) + { + goto fail; + } + ESP_LOGD(TAG, "Temperature measurement rate: %i measurements / sec", pow_int(2, config->tmp_rate)); + err = dps310_set_rate_t(dev, config->tmp_rate); + if (err != ESP_OK) + { + goto fail; + } + ESP_LOGD(TAG, "Temperature oversampling: %i time(s)", pow_int(2, config->tmp_oversampling)); + err = dps310_set_oversampling_t(dev, config->tmp_oversampling); + if (err != ESP_OK) + { + goto fail; + } + ESP_LOGD(TAG, "Temperature source: %s", config->tmp_src == DPS310_TMP_SRC_INTERNAL ? "internal" : "external"); + err = dps310_set_tmp_ext(dev, config->tmp_src); + if (err != ESP_OK) + { + goto fail; + } + ESP_LOGD(TAG, "Temperature COEF source: %s", config->tmp_coef == DPS310_TMP_SRC_INTERNAL ? "internal" : "external"); + err = dps310_set_tmp_coef_ext(dev, config->tmp_coef); + if (err != ESP_OK) + { + goto fail; + } + if (config->tmp_src != config->tmp_coef) + { + + /* XXX don't know when DPS310_TMP_SRC_INTERNAL should be used. the + * datasheet does not mention the differece. + */ + ESP_LOGW(TAG, "tmp_src and tmp_coef should be an identical source. Use DPS310_TMP_SRC_EXTERNAL in the config"); + } + ESP_LOGD(TAG, "Interrupt FIFO: %s", config->int_fifo_mode == DPS310_INT_FIFO_ENABLE ? "enabled" : "disabled"); + err = dps310_set_int_fifo(dev, config->int_fifo_mode); + if (err != ESP_OK) + { + goto fail; + } + ESP_LOGD(TAG, "Interrupt temperature: %s", config->int_tmp_mode == DPS310_INT_TMP_ENABLE ? "enabled" : "disabled"); + err = dps310_set_int_tmp(dev, config->int_tmp_mode); + if (err != ESP_OK) + { + goto fail; + } + ESP_LOGD(TAG, "Interrupt pressure: %s", config->int_prs_mode == DPS310_INT_PRS_ENABLE ? "enabled" : "disabled"); + err = dps310_set_int_prs(dev, config->int_prs_mode); + if (err != ESP_OK) + { + goto fail; + } + + ESP_LOGD(TAG, "Temperature result bit-shift: %s", config->t_shift_mode == DPS310_T_SHIFT_ENABLE ? "enabled" : "disabled"); + if (config->tmp_oversampling > DPS310_TMP_PRC_8 && config->t_shift_mode != DPS310_T_SHIFT_ENABLE) + { + ESP_LOGW(TAG, "Temperature result bit-shift must be enabled, but is disabled. Set DPS310_T_SHIFT_ENABLE"); + } + err = dps310_set_t_shift(dev, config->t_shift_mode); + if (err != ESP_OK) + { + goto fail; + } + + ESP_LOGD(TAG, "Pressure result bit-shift: %s", config->p_shift_mode == DPS310_P_SHIFT_ENABLE ? "enabled" : "disabled"); + if (config->pm_oversampling > DPS310_PM_PRC_8 && config->p_shift_mode != DPS310_P_SHIFT_ENABLE) + { + ESP_LOGW(TAG, "Pressure result bit-shift must be enabled, but is disabled. Set DPS310_P_SHIFT_ENABLE"); + } + err = dps310_set_p_shift(dev, config->p_shift_mode); + if (err != ESP_OK) + { + goto fail; + } + + ESP_LOGD(TAG, "FIFO: %s", config->fifo_en_mode == DPS310_FIFO_ENABLE ? "enabled" : "disabled"); + err = dps310_set_fifo_en(dev, config->fifo_en_mode); + if (err != ESP_OK) + { + goto fail; + } + + err = ESP_OK; + +fail: + return err; +} + +esp_err_t dps310_get_rate_p(dps310_t *dev, dps310_pm_rate_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_PRS_CFG, DPS310_REG_PRS_CFG_PM_RATE_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_rate_p(dps310_t *dev, dps310_pm_rate_t value) +{ + CHECK_ARG(dev); + + return _update_reg(&dev->i2c_dev, DPS310_REG_PRS_CFG, DPS310_REG_PRS_CFG_PM_RATE_MASK, value); +} + +esp_err_t dps310_get_rate_t(dps310_t *dev, dps310_tmp_rate_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_TMP_CFG, DPS310_REG_PRS_CFG_TMP_RATE_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_rate_t(dps310_t *dev, dps310_tmp_rate_t value) +{ + CHECK_ARG(dev); + + return _update_reg(&dev->i2c_dev, DPS310_REG_TMP_CFG, DPS310_REG_PRS_CFG_TMP_RATE_MASK, value); +} + +esp_err_t dps310_reset(dps310_t *dev) +{ + esp_err_t err = ESP_FAIL; + uint8_t value = DPS310_SOFT_RST_VALUE; + + CHECK_ARG(dev); + + ESP_LOGD(TAG, "Resetting the device"); + err = _write_reg(&dev->i2c_dev, DPS310_REG_RESET, &value); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "failed to reset the device"); + goto fail; + } + vTaskDelay(pdMS_TO_TICKS(DPS310_STARTUP_DELAY_MS)); +fail: + return err; +} + +esp_err_t dps310_get_oversampling_p(dps310_t *dev, dps310_pm_oversampling_t *value) +{ + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev && value); + err = _read_reg_mask(&dev->i2c_dev, DPS310_REG_PRS_CFG, DPS310_REG_PRS_CFG_PM_PRC_MASK, (uint8_t *)value); + if (err == ESP_OK) + { + /* XXX when new p_rate is available, always keep it in dev as a cache */ + dev->p_rate = *value; + } + return err; +} + +esp_err_t dps310_set_oversampling_p(dps310_t *dev, dps310_pm_oversampling_t value) +{ + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev); + err = _update_reg(&dev->i2c_dev, DPS310_REG_PRS_CFG, DPS310_REG_PRS_CFG_PM_PRC_MASK, value); + if (err == ESP_OK) + { + /* XXX when new p_rate is available, always keep it in dev as a cache */ + dev->p_rate = value; + } + return err; +} + +esp_err_t dps310_get_oversampling_t(dps310_t *dev, dps310_tmp_oversampling_t *value) +{ + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev && value); + err = _read_reg_mask(&dev->i2c_dev, DPS310_REG_TMP_CFG, DPS310_REG_TMP_CFG_TMP_PRC_MASK, (uint8_t *)value); + if (err == ESP_OK) + { + /* XXX when new t_rate is available, always keep it in dev as a cache */ + dev->t_rate = *value; + } + return err; +} + +esp_err_t dps310_set_oversampling_t(dps310_t *dev, dps310_tmp_oversampling_t value) +{ + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev); + err = _update_reg(&dev->i2c_dev, DPS310_REG_TMP_CFG, DPS310_REG_TMP_CFG_TMP_PRC_MASK, value); + if (err == ESP_OK) + { + /* XXX when new t_rate is available, always keep it in dev as a cache */ + dev->t_rate = value; + } + return err; +} + +esp_err_t dps310_get_tmp_ext(dps310_t *dev, dps310_tmp_src_ext_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_TMP_CFG, DPS310_REG_PRS_CFG_TMP_EXT_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_tmp_ext(dps310_t *dev, dps310_tmp_src_ext_t value) +{ + CHECK_ARG(dev); + + return _update_reg(&dev->i2c_dev, DPS310_REG_TMP_CFG, DPS310_REG_PRS_CFG_TMP_EXT_MASK, value); +} + +esp_err_t dps310_set_tmp_coef_ext(dps310_t *dev, dps310_tmp_src_ext_t value) +{ + CHECK_ARG(dev); + + return _update_reg(&dev->i2c_dev, DPS310_REG_COEF_SRCE, DPS310_REG_COEF_SRCE_MASK, value); +} + +esp_err_t dps310_get_int_hl(dps310_t *dev, dps310_int_hl_active_level_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_INT_HL_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_int_hl(dps310_t *dev, dps310_int_hl_active_level_t value) +{ + CHECK_ARG(dev); + + return _update_reg(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_PRS_CFG_TMP_EXT_MASK, value); +} + +esp_err_t dps310_get_int_fifo(dps310_t *dev, dps310_int_fifo_mode_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_INT_FIFO_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_int_fifo(dps310_t *dev, dps310_int_fifo_mode_t value) +{ + CHECK_ARG(dev); + + return _update_reg(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_INT_FIFO_MASK, value); +} + +esp_err_t dps310_get_int_tmp(dps310_t *dev, dps310_int_tmp_mode_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_INT_TMP_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_int_tmp(dps310_t *dev, dps310_int_tmp_mode_t value) +{ + return _update_reg(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_INT_TMP_MASK, value); +} + +esp_err_t dps310_get_int_prs(dps310_t *dev, dps310_int_prs_mode_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_INT_PRS_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_int_prs(dps310_t *dev, dps310_int_prs_mode_t value) +{ + CHECK_ARG(dev); + + return _update_reg(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_INT_PRS_MASK, value); +} + +esp_err_t dps310_get_t_shift(dps310_t *dev, dps310_t_shift_mode_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_T_SHIFT_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_t_shift(dps310_t *dev, dps310_t_shift_mode_t value) +{ + CHECK_ARG(dev); + + return _update_reg(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_T_SHIFT_MASK, value); +} + +esp_err_t dps310_get_p_shift(dps310_t *dev, dps310_p_shift_mode_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_P_SHIFT_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_p_shift(dps310_t *dev, dps310_p_shift_mode_t value) +{ + CHECK_ARG(dev); + + return _update_reg(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_P_SHIFT_MASK, value); +} + +esp_err_t dps310_get_fifo_en(dps310_t *dev, dps310_fifo_en_mode_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_FIFO_EN_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_fifo_en(dps310_t *dev, dps310_fifo_en_mode_t value) +{ + return _update_reg(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_FIFO_EN_MASK, value); +} + +esp_err_t dps310_get_spi_mode(dps310_t *dev, dps310_spi_mode_t *value) +{ + CHECK_ARG(dev && value); + + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_SPI_MODE_MASK, (uint8_t *)value); +} + +esp_err_t dps310_set_spi_mode(dps310_t *dev, dps310_spi_mode_t value) +{ + CHECK_ARG(dev); + + return _update_reg(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_SPI_MODE_MASK, value); +} + +static int32_t two_complement_of(uint32_t value, uint8_t length) +{ + int32_t result = (uint32_t)value; + if (value & ((uint32_t)1 << (length - (uint32_t)1))) + { + result -= ((uint32_t) 1 << length); + } + return result; +} + +esp_err_t dps310_get_coef(dps310_t *dev) +{ + uint8_t reg_values[DPS310_REG_COEF_LEN]; + + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev); + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + err = i2c_dev_read_reg(&dev->i2c_dev, DPS310_REG_COEF, reg_values, DPS310_REG_COEF_LEN); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "i2c_dev_read_reg(): %s", esp_err_to_name(err)); + goto fail; + } + + dev->coef.c0 = ((uint32_t)reg_values[0] << 4) | (((uint32_t)reg_values[1] >> 4) & 0x0F); + dev->coef.c0 = two_complement_of(dev->coef.c0, 12); + + dev->coef.c1 = (((uint32_t)reg_values[1] & 0x0F) << 8) | (uint32_t)reg_values[2]; + dev->coef.c1 = two_complement_of(dev->coef.c1, 12); + + dev->coef.c00 = ((uint32_t)reg_values[3] << 12) | ((uint32_t)reg_values[4] << 4) | (((uint32_t)reg_values[5] >> 4) & 0x0F); + dev->coef.c00 = two_complement_of(dev->coef.c00, 20); + + dev->coef.c10 = (((uint32_t)reg_values[5] & 0x0F) << 16) | ((uint32_t)reg_values[6] << 8) | (uint32_t)reg_values[7]; + dev->coef.c10 = two_complement_of(dev->coef.c10, 20); + + dev->coef.c01 = ((uint32_t)reg_values[8] << 8) | (uint32_t)reg_values[9]; + dev->coef.c01 = two_complement_of(dev->coef.c01, 16); + + dev->coef.c11 = ((uint32_t)reg_values[10] << 8) | (uint32_t)reg_values[11]; + dev->coef.c11 = two_complement_of(dev->coef.c11, 16); + + dev->coef.c20 = ((uint32_t)reg_values[12] << 8) | (uint32_t)reg_values[13]; + dev->coef.c20 = two_complement_of(dev->coef.c20, 16); + + dev->coef.c21 = ((uint32_t)reg_values[14] << 8) | (uint32_t)reg_values[15]; + dev->coef.c21 = two_complement_of(dev->coef.c21, 16); + + dev->coef.c30 = ((uint32_t)reg_values[16] << 8) | (uint32_t)reg_values[17]; + dev->coef.c30 = two_complement_of(dev->coef.c30, 16); +fail: + return err; + +} + +esp_err_t dps310_get_mode(dps310_t *dev, dps310_mode_t *mode) +{ + return _read_reg_mask(&dev->i2c_dev, DPS310_REG_MEAS_CFG, DPS310_REG_MEAS_CFG_MEAS_CTRL_MASK, (uint8_t *)mode); +} + +esp_err_t dps310_set_mode(dps310_t *dev, dps310_mode_t mode) +{ + return _update_reg(&dev->i2c_dev, DPS310_REG_MEAS_CFG, DPS310_REG_MEAS_CFG_MEAS_CTRL_MASK, mode); +} + +esp_err_t dps310_flush_fifo(dps310_t *dev) +{ + uint8_t value = DPS310_FIFO_FLUSH_VALUE; + + CHECK_ARG(dev); + return _write_reg(&dev->i2c_dev, DPS310_REG_RESET, &value); +} + +esp_err_t dps310_enable_fifo(dps310_t *dev, bool enable) +{ + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev); + if (!enable) + { + err = dps310_flush_fifo(dev); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_flush_fifo(): %s", esp_err_to_name(err)); + goto fail; + } + } + err = _update_reg(&dev->i2c_dev, DPS310_REG_CFG_REG, DPS310_REG_CFG_REG_FIFO_EN_MASK, enable ? 1 : 0); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "_update_reg(): %s", esp_err_to_name(err)); + goto fail; + } +fail: + return err; +} + +esp_err_t dps310_read_raw(dps310_t *dev, uint8_t reg, int32_t *value) +{ + uint8_t reg_values[DPS310_REG_SENSOR_VALUE_LEN] = {0}; + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev && value); + if (reg != DPS310_REG_TMP_B2 && reg != DPS310_REG_PRS_B2) + { + err = ESP_ERR_INVALID_ARG; + goto fail; + } + err = i2c_dev_read_reg(&dev->i2c_dev, reg, reg_values, DPS310_REG_SENSOR_VALUE_LEN); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "i2c_dev_read_reg(): %s", esp_err_to_name(err)); + goto fail; + } + *value = ((uint32_t)reg_values[0] << 16) | ((uint32_t)reg_values[1] << 8) | (uint32_t)reg_values[2]; + *value = two_complement_of(*value, DPS310_REG_SENSOR_VALUE_LEN * 8); + ESP_LOGD(TAG, "raw value in %s resister: %" PRIi32, reg == DPS310_REG_TMP_B2 ? "temperature" : "pressure", *value); +fail: + return err; +} + +/* calcurate and return kT, or kP. */ +static float raw_to_scaled(int32_t raw, uint8_t rate) +{ + int32_t k = 0; + + assert(rate <= N_SCALE_FACTORS - 1); + k = scale_factors[rate]; + ESP_LOGD(TAG, "scale_factor: %" PRIi32, k); + + /* Traw_sc = Traw / kT */ + assert(k != 0); + return (float)raw / (float)k; +} + +static float compensate_temp(dps310_t *dev, uint32_t T_raw, uint8_t rate) +{ + + /* 4.9.2 How to Calculate Compensated Temperature Values */ + float result = 0; + float T_raw_scaled = 0; + + CHECK_ARG(dev); + T_raw_scaled = raw_to_scaled(T_raw, rate); + + /* Tcomp (°C) = c0 * 0.5 + c1 * Traw_sc */ + result = ((float)dev->coef.c0 * 0.5) + ((float)dev->coef.c1 * T_raw_scaled); + return result; +} + +static float compensate_pressure(dps310_t *dev, int32_t T_raw, int32_t T_rate, int32_t P_raw, int32_t P_rate) +{ + + /* 4.9.1 How to Calculate Compensated Pressure Values */ + float T_raw_scaled = 0; + float P_raw_scaled = 0; + + CHECK_ARG(dev); + + T_raw_scaled = raw_to_scaled(T_raw, T_rate); + P_raw_scaled = raw_to_scaled(P_raw, P_rate); + + /* Pcomp(Pa) = c00 + * + Praw_sc * (c10 + Praw_sc * (c20 + Praw_sc * c30)) + * + Traw_sc * c01 + * + Traw_sc * Praw_sc * (c11 + Praw_sc * c21) + */ + return (float)dev->coef.c00 + + P_raw_scaled * ((float)dev->coef.c10 + P_raw_scaled * ((float)dev->coef.c20 + P_raw_scaled * (float)dev->coef.c30)) + + T_raw_scaled * (float)dev->coef.c01 + + T_raw_scaled * P_raw_scaled * ((float)dev->coef.c11 + P_raw_scaled * (float)dev->coef.c21); +} + +esp_err_t dps310_read_pressure(dps310_t *dev, float *pressure) +{ + esp_err_t err = ESP_FAIL; + int32_t T_raw = 0; + int32_t P_raw = 0; + dps310_tmp_oversampling_t T_rate = 0; + dps310_pm_oversampling_t P_rate = 0; + + CHECK_ARG(dev && pressure); + err = dps310_get_oversampling_t(dev, &T_rate); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_get_oversampling_t(): %s", esp_err_to_name(err)); + goto fail; + } + + err = dps310_get_oversampling_p(dev, &P_rate); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_get_oversampling_p(): %s", esp_err_to_name(err)); + goto fail; + } + + err = dps310_read_raw(dev, DPS310_REG_TMP_B2, &T_raw); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_raw(): %s", esp_err_to_name(err)); + goto fail; + } + err = dps310_read_raw(dev, DPS310_REG_PRS_B2, &P_raw); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_raw(): %s", esp_err_to_name(err)); + goto fail; + } + *pressure = compensate_pressure(dev, T_raw, T_rate, P_raw, P_rate); +fail: + return err; +} + +esp_err_t dps310_read_pressure_wait(dps310_t *dev, uint16_t delay_ms, uint8_t max_attempt, float *pressure) +{ + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev && pressure); + err = _wait_for_reg_bits(&dev->i2c_dev, DPS310_REG_MEAS_CFG, DPS310_REG_MEAS_CFG_PRS_RDY_MASK, 1, max_attempt, delay_ms); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "_wait_for_reg_bits(): %s", esp_err_to_name(err)); + goto fail; + } + err = dps310_read_pressure(dev, pressure); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_pressure(): %s", esp_err_to_name(err)); + goto fail; + } +fail: + return err; +} + +esp_err_t dps310_read_temp(dps310_t *dev, float *temperature) +{ + esp_err_t err = ESP_FAIL; + int32_t T_raw = 0; + dps310_tmp_oversampling_t rate = 0; + + CHECK_ARG(dev && temperature); + err = dps310_get_oversampling_t(dev, &rate); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_get_oversampling_t(): %s", esp_err_to_name(err)); + goto fail; + } + err = dps310_read_raw(dev, DPS310_REG_TMP_B2, &T_raw); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_raw(): %s", esp_err_to_name(err)); + goto fail; + } + + /* XXX when latest t_raw is available, always keep it in dev as a cache */ + dev->t_raw = T_raw; + *temperature = compensate_temp(dev, dev->t_raw, rate); +fail: + return err; +} + +esp_err_t dps310_read_temp_wait(dps310_t *dev, uint16_t delay_ms, uint8_t max_attempt, float *temperature) +{ + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev && temperature); + err = _wait_for_reg_bits(&dev->i2c_dev, DPS310_REG_MEAS_CFG, DPS310_REG_MEAS_CFG_TMP_RDY_MASK, 1, max_attempt, delay_ms); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "_wait_for_reg_bits(): %s", esp_err_to_name(err)); + goto fail; + } + err = dps310_read_temp(dev, temperature); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_temp(): %s", esp_err_to_name(err)); + goto fail; + } +fail: + return err; +} + +esp_err_t dps310_is_ready_for(dps310_t *dev, uint8_t reg, uint8_t mask, bool *ready) +{ + esp_err_t err = ESP_FAIL; + uint8_t reg_value = 0; + + CHECK_ARG(dev && ready); + + err = _read_reg_mask(&dev->i2c_dev, reg, mask, ®_value); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "_read_reg_mask(): %s", esp_err_to_name(err)); + goto fail; + } + *ready = reg_value == 1 ? true : false; +fail: + return err; +} + +esp_err_t dps310_is_ready_for_coef(dps310_t *dev, bool *ready) +{ + CHECK_ARG(dev && ready); + return dps310_is_ready_for(dev, DPS310_REG_MEAS_CFG, DPS310_REG_MEAS_CFG_COEF_RDY_MASK, ready); +} + +esp_err_t dps310_is_ready_for_sensor(dps310_t *dev, bool *ready) +{ + CHECK_ARG(dev && ready); + return dps310_is_ready_for(dev, DPS310_REG_MEAS_CFG, DPS310_REG_MEAS_CFG_SENSOR_RDY_MASK, ready); +} + +esp_err_t dps310_is_ready_for_temp(dps310_t *dev, bool *ready) +{ + CHECK_ARG(dev && ready); + return dps310_is_ready_for(dev, DPS310_REG_MEAS_CFG, DPS310_REG_MEAS_CFG_TMP_RDY_MASK, ready); +} + +esp_err_t dps310_is_ready_for_pressure(dps310_t *dev, bool *ready) +{ + CHECK_ARG(dev && ready); + return dps310_is_ready_for(dev, DPS310_REG_MEAS_CFG, DPS310_REG_MEAS_CFG_PRS_RDY_MASK, ready); +} + +static inline bool dps310_is_pressure_result(int32_t data) +{ + return (uint8_t)data & 0x01; +} + +static inline bool dps310_is_temp_result(int32_t data) +{ + return !dps310_is_pressure_result(data); +} + +static esp_err_t dps310_read_reg_sensor_raw(dps310_t *dev, uint8_t reg, int32_t *value) +{ + uint8_t reg_values[DPS310_REG_SENSOR_VALUE_LEN] = {0}; + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev && value); + if (reg != DPS310_REG_TMP_B2 && reg != DPS310_REG_PRS_B2) + { + err = ESP_ERR_INVALID_ARG; + goto fail; + } + err = i2c_dev_read_reg(&dev->i2c_dev, reg, reg_values, DPS310_REG_SENSOR_VALUE_LEN); + *value = (uint32_t)reg_values[0] << 16 | (uint32_t)reg_values[1] << 8 | (uint32_t)reg_values[2]; + ESP_LOGD(TAG, "reg_values: %" PRIi32, *value); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "i2c_dev_read_reg(): %s", esp_err_to_name(err)); + goto fail; + } +fail: + return err; +} + +esp_err_t dps310_is_fifo_empty(dps310_t *dev, bool *result) +{ + esp_err_t err = ESP_FAIL; + uint8_t value = 0; + + CHECK_ARG(dev && result); + err = _read_reg_mask(&dev->i2c_dev, DPS310_REG_FIFO_STS, DPS310_REG_FIFO_STS_FIFO_EMPTY_MASK, &value); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "_read_reg_mask(): %s", esp_err_to_name(err)); + goto fail; + } + *result = value == 1 ? true : false; + +fail: + return err; + +} + +esp_err_t dps310_read_fifo(dps310_t *dev, dps310_fifo_measurement_t *measurement) +{ + int32_t raw_value = 0; + esp_err_t err = ESP_OK; + + CHECK_ARG(dev && measurement); + + /* Read a measurement from FIFO. to compensate the value, additional + * parameters, such as t_rate, are necessary. Use cached parameters in the + * device descriptor so that the value can be returned by only one I2C + * reading. This means that the driver does not work in multi-master + * configuration. As long as cached parameters are in sync with the real + * values in registers, the returned value is always correct even if a + * parameter is modified during the background mode. + */ + err = dps310_read_reg_sensor_raw(dev, DPS310_REG_FIFO, &raw_value); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_reg_sensor_raw(): %s", esp_err_to_name(err)); + goto fail; + } + measurement->type = dps310_is_pressure_result(raw_value) ? DPS310_MEASUREMENT_PRESSURE : DPS310_MEASUREMENT_TEMPERATURE; + raw_value = two_complement_of(raw_value, 24); + if (raw_value == DPS310_FIFO_EMPTY) + { + measurement->type = DPS310_MEASUREMENT_EMPTY; + } + + switch (measurement->type) + { + case DPS310_MEASUREMENT_TEMPERATURE: + ESP_LOGD(TAG, "DPS310_MEASUREMENT_TEMPERATURE: raw_value: %" PRIi32, raw_value); + measurement->result = compensate_temp(dev, raw_value, dev->t_rate); + + /* XXX when new t_raw is available, always keep it in dev as a + * cache */ + dev->t_raw = raw_value; + break; + ;; + case DPS310_MEASUREMENT_PRESSURE: + ESP_LOGD(TAG, "DPS310_MEASUREMENT_PRESSURE: raw_value: %" PRIi32, raw_value); + measurement->result = compensate_pressure(dev, dev->t_raw, dev->t_rate, raw_value, dev->p_rate); + break; + ;; + case DPS310_MEASUREMENT_EMPTY: + ESP_LOGD(TAG, "DPS310_MEASUREMENT_EMPTY: raw_value %" PRIi32, raw_value); + measurement->result = 0; + break; + default: + + /* NOT REACHED */ + abort(); + ;; + } + +fail: + return err; +} + +inline esp_err_t dps310_backgorund_start(dps310_t *dev, dps310_mode_t mode) +{ + CHECK_ARG(dev); + return dps310_set_mode(dev, mode); +} + +inline esp_err_t dps310_backgorund_stop(dps310_t *dev) +{ + CHECK_ARG(dev); + return dps310_set_mode(dev, DPS310_MODE_STANDBY); +} + +static int32_t dps310_calc_sea_level_pressure(float pressure, float altitude) +{ + return (int32_t)(pressure / pow(1.0 - altitude / 44330, 5.255)); +} + +static inline float calc_altitude(float pressure, float pressure_s) +{ + return 44330 * (1.0 - pow(pressure / pressure_s, 0.1903)); +} + +esp_err_t dps310_calibrate_altitude(dps310_t *dev, float altitude_real) +{ + esp_err_t err = ESP_FAIL; + float pressure = 0; + float temperature = 0; + float altitude_guess = 0; + dps310_pm_oversampling_t orig_oversmapling_p = 0; + dps310_tmp_oversampling_t orig_oversmapling_t = 0; + + CHECK_ARG(dev); + + /* keep original oversampling values */ + err = dps310_get_oversampling_p(dev, &orig_oversmapling_p); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_get_oversampling_p(): %s", esp_err_to_name(err)); + goto get_oversampling_fail; + } + err = dps310_get_oversampling_t(dev, &orig_oversmapling_t); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_get_oversampling_t(): %s", esp_err_to_name(err)); + goto get_oversampling_fail; + } + + /* modify oversampling values for accuracy */ + err = dps310_set_oversampling_p(dev, DPS310_PM_PRC_64); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_set_oversampling_p(): %s", esp_err_to_name(err)); + goto fail; + } + err = dps310_set_oversampling_t(dev, DPS310_TMP_PRC_64); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_set_oversampling_t(): %s", esp_err_to_name(err)); + goto fail; + } + + /* read temperature once in command mode */ + err = dps310_set_mode(dev, DPS310_MODE_COMMAND_TEMPERATURE); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_set_mode(): %s", esp_err_to_name(err)); + goto fail; + } + err = dps310_read_temp_wait(dev, 100, 10, &temperature); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_temp(): %s", esp_err_to_name(err)); + goto fail; + } + + /* read pressure once in command mode */ + err = dps310_set_mode(dev, DPS310_MODE_COMMAND_PRESSURE); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_set_mode(): %s", esp_err_to_name(err)); + goto fail; + } + err = dps310_read_pressure_wait(dev, 100, 10, &pressure); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_pressure(): %s", esp_err_to_name(err)); + goto fail; + } + + /* calibrate offset */ + dev->pressure_s = dps310_calc_sea_level_pressure(pressure, altitude_real); + altitude_guess = calc_altitude(pressure, dev->pressure_s); + dev->offset = altitude_real - altitude_guess; + + ESP_LOGI(TAG, "Calibration result:"); + ESP_LOGI(TAG, "\tPressure: %0.2f (Pa)", pressure); + ESP_LOGI(TAG, "\tCalculated Pressure at sea level: %0.2f (Pa)", dev->pressure_s); + ESP_LOGI(TAG, "\tCalculated altitude: %0.2f (m)", altitude_guess); + ESP_LOGI(TAG, "\tReal altitude: %0.2f (m)", altitude_real); + ESP_LOGI(TAG, "\tOffset: %0.2f (m)", dev->offset); +fail: + if (dps310_set_oversampling_t(dev, orig_oversmapling_t) != ESP_OK) + { + ESP_LOGW(TAG, "Restoring temperature oversampling failed"); + } + if (dps310_set_oversampling_p(dev, orig_oversmapling_p) != ESP_OK) + { + ESP_LOGW(TAG, "Restoring pressure oversampling failed"); + } +get_oversampling_fail: + return err; +} + + +static float pressure_to_altitude(float pressure, float pressure_s, float *altitude) +{ + esp_err_t err = ESP_FAIL; + + CHECK_ARG(altitude); + if (pressure_s == 0) + { + err = ESP_ERR_INVALID_ARG; + goto fail; + } + *altitude = 44330 * (1.0 - pow(pressure / pressure_s, 0.1903)); + err = ESP_OK; + +fail: + return err; +} + +esp_err_t dps310_calc_altitude(dps310_t *dev, float pressure, float *altitude) +{ + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev && altitude); + + err = pressure_to_altitude(pressure, dev->pressure_s, altitude); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "pressure_to_altitude(): %s", esp_err_to_name(err)); + goto fail; + } + *altitude = *altitude + dev->offset; +fail: + return err; +} + +esp_err_t dps310_read_altitude(dps310_t *dev, float *altitude) +{ + esp_err_t err = ESP_FAIL; + float pressure = 0; + + CHECK_ARG(dev && altitude); + + err = dps310_read_pressure(dev, &pressure); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_pressure(): %s", esp_err_to_name(err)); + goto fail; + } + err = dps310_calc_altitude(dev, pressure, altitude); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_calc_altitude(): %s", esp_err_to_name(err)); + goto fail; + } + +fail: + return err; +} diff --git a/components/dps310/src/helper_i2c.c b/components/dps310/src/helper_i2c.c new file mode 100644 index 00000000..21768a64 --- /dev/null +++ b/components/dps310/src/helper_i2c.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2022 Tomoyuki Sakurai + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__DPS310__HELPER_I2C__H__) +#define __DPS310__HELPER_I2C__H__ + +/* standard headers */ +#include + +/* esp-idf headers */ +#include +#include +#include +#include +#include + +/* private headers */ +#include "helper_macro.h" +#include "helper_i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static const char *TAG = "dps310/helper_i2c"; + +/** + * @brief Read a single byte from a 8-bit resister without locking, + */ +inline esp_err_t _read_reg_nolock(i2c_dev_t *dev, uint8_t reg, uint8_t *val); + +/** + * @brief Write a single byte to a 8-bit resister without locking. + */ +inline esp_err_t _write_reg_nolock(i2c_dev_t *dev, uint8_t reg, uint8_t val); + +/* count the number of trailing zero in a value, usually bitmasks. */ +static uint8_t _count_trailing_zero_bits(uint8_t v) +{ + uint8_t count = 0; + if (v) + { + v = (v ^ (v - 1)) >> 1; + for (count = 0; v; count++) + { + v >>= 1; + } + } + else + { + count = 8; + } + return count; +} + +esp_err_t _read_reg_nolock(i2c_dev_t *dev, uint8_t reg, uint8_t *val) +{ + return i2c_dev_read_reg(dev, reg, val, 1); +} + +esp_err_t _read_reg(i2c_dev_t *dev, uint8_t reg, uint8_t *val) +{ + CHECK_ARG(dev); + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, _read_reg_nolock(dev, reg, val)); + I2C_DEV_GIVE_MUTEX(dev); + return ESP_OK; +} + +esp_err_t _read_reg_mask(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t *val) +{ + uint8_t reg_value = 0; + + CHECK_ARG(dev && val); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, _read_reg_nolock(dev, reg, ®_value)); + I2C_DEV_GIVE_MUTEX(dev); + *val = (reg_value & mask) >> _count_trailing_zero_bits(mask); + ESP_LOGV(TAG, "reg_value: %02X, val: %02X", reg_value, *val); + return ESP_OK; +} + +esp_err_t _write_reg_nolock(i2c_dev_t *dev, uint8_t reg, uint8_t val) +{ + return i2c_dev_write_reg(dev, reg, &val, 1); +} + +esp_err_t _update_reg_nolock(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t val) +{ + uint8_t reg_value = 0; + uint8_t n_shift = 0; + + CHECK_ARG(dev); + CHECK(_read_reg_nolock(dev, reg, ®_value)); + + n_shift = _count_trailing_zero_bits(mask); + if (((reg_value >> n_shift) & val) == val) + { + ESP_LOGD(TAG, "register unchanged"); + } + else + { + reg_value = (reg_value & (~mask)) | (val << n_shift); + CHECK(_write_reg_nolock(dev, reg, reg_value)); + } + return ESP_OK; +} + +esp_err_t _write_reg(i2c_dev_t *dev, uint8_t reg, uint8_t *value) +{ + esp_err_t err = ESP_FAIL; + + CHECK_ARG(dev); + I2C_DEV_TAKE_MUTEX(dev); + err = _write_reg_nolock(dev, reg, *value); + I2C_DEV_GIVE_MUTEX(dev); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "i2c_dev_write_reg(): %s", esp_err_to_name(err)); + } + return err; +} + +esp_err_t _update_reg(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t val) +{ + CHECK_ARG(dev); + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, _update_reg_nolock(dev, reg, mask, val)); + I2C_DEV_GIVE_MUTEX(dev); + return ESP_OK; +} + +esp_err_t _wait_for_reg_bits(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t val, uint8_t max_attempt, uint16_t delay_ms) +{ + esp_err_t err = ESP_FAIL; + uint8_t reg_value = 0; + uint8_t attempts = 0; + + while (attempts < max_attempt) + { + attempts++; + err = _read_reg_mask(dev, reg, mask, ®_value); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "_wait_for_reg_bits(): %s", esp_err_to_name(err)); + return err; + } + if (reg_value == val) + { + return ESP_OK; + } + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + } + ESP_LOGE(TAG, "Timeout. delay_ms: %" PRIu16 "max_attempt: %" PRIu8, delay_ms, max_attempt); + return ESP_ERR_TIMEOUT; +} + +#ifdef __cplusplus +} +#endif + +#endif // __DPS310__HELPER_I2C__H__ diff --git a/components/ds1302/.eil.yml b/components/ds1302/.eil.yml index 9c11e2fb..9a30e19c 100644 --- a/components/ds1302/.eil.yml +++ b/components/ds1302/.eil.yml @@ -1,27 +1,24 @@ ---- -components: - - name: ds1302 - description: | - Driver for DS1302 RTC module - group: rtc - groups: [] - code_owners: - - name: UncleRus - depends: - - name: log - - name: esp_idf_lib_helpers - - name: freertos - - name: driver - thread_safe: no - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2021 - - name: PavelM - year: 2016 +name: ds1302 +description: Driver for DS1302 RTC module +version: 1.1.0 +groups: + - rtc +code_owners: + - UncleRus +depends: + - log + - esp_idf_lib_helpers + - freertos + - driver +thread_safe: no +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: PavelM + year: 2016 + - name: UncleRus + year: 2021 diff --git a/components/ds1307/.eil.yml b/components/ds1307/.eil.yml index dc4386cc..54778e53 100644 --- a/components/ds1307/.eil.yml +++ b/components/ds1307/.eil.yml @@ -1,24 +1,21 @@ ---- -components: - - name: ds1307 - description: | - Driver for DS1307 RTC module - group: rtc - groups: [] - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2016 +name: ds1307 +description: Driver for DS1307 RTC module +version: 1.0.1 +groups: + - rtc +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2016 diff --git a/components/ds18x20/.eil.yml b/components/ds18x20/.eil.yml index 0de093b6..9c089404 100644 --- a/components/ds18x20/.eil.yml +++ b/components/ds18x20/.eil.yml @@ -1,29 +1,26 @@ ---- -components: - - name: ds18x20 - description: | - Driver for DS18B20/DS18S20 families of 1-Wire temperature sensor ICs - group: temperature - groups: [] - code_owners: - - name: UncleRus - depends: - - name: log - - name: esp_idf_lib_helpers - - name: onewire - - name: freertos - thread_safe: no - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2018 - - name: AlexS - year: 2016 - - name: GrzegorzH - year: 2016 +name: ds18x20 +description: Driver for DS18B20/DS18S20 families of 1-Wire temperature sensor ICs +version: 1.2.0 +groups: + - temperature +code_owners: + - UncleRus +depends: + - log + - esp_idf_lib_helpers + - onewire + - freertos +thread_safe: no +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: GrzegorzH + year: 2016 + - name: AlexS + year: 2016 + - name: UncleRus + year: 2018 diff --git a/components/ds18x20/ds18x20.c b/components/ds18x20/ds18x20.c index 643cba4d..5563f2e9 100644 --- a/components/ds18x20/ds18x20.c +++ b/components/ds18x20/ds18x20.c @@ -76,7 +76,7 @@ static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; static const char *TAG = "ds18x20"; -esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait) +esp_err_t ds18x20_measure(gpio_num_t pin, onewire_addr_t addr, bool wait) { if (!onewire_reset(pin)) return ESP_ERR_INVALID_RESPONSE; @@ -102,7 +102,7 @@ esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait) return ESP_OK; } -esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer) +esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, onewire_addr_t addr, uint8_t *buffer) { CHECK_ARG(buffer); @@ -133,7 +133,7 @@ esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t * return ESP_OK; } -esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer) +esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, onewire_addr_t addr, uint8_t *buffer) { CHECK_ARG(buffer); @@ -152,7 +152,7 @@ esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t return ESP_OK; } -esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr) +esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, onewire_addr_t addr) { if (!onewire_reset(pin)) return ESP_ERR_INVALID_RESPONSE; @@ -176,51 +176,70 @@ esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr) return ESP_OK; } -esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, float *temperature) +esp_err_t ds18s20_read_temperature(gpio_num_t pin, onewire_addr_t addr, float *temperature) { CHECK_ARG(temperature); uint8_t scratchpad[8]; - int16_t temp; - CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); - temp = scratchpad[1] << 8 | scratchpad[0]; - - *temperature = ((float)temp * 625.0) / 10000; + int16_t temp = (((scratchpad[1] << 8) | (scratchpad[0] & 0xfe)) << 3) | ((0x10 - scratchpad[6]) & 0x0f); + *temperature = (float)temp * 0.0625f - 0.250f; return ESP_OK; } -esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, float *temperature) +esp_err_t ds18b20_read_temperature(gpio_num_t pin, onewire_addr_t addr, float *temperature) { CHECK_ARG(temperature); uint8_t scratchpad[8]; - int16_t temp; - CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); - temp = scratchpad[1] << 8 | scratchpad[0]; + uint16_t temp = scratchpad[1] << 8 | scratchpad[0]; + int sign = 1; + if (temp > 2047) + { + temp = ~temp + 1; + sign = -1; + } + *temperature = (float)temp * (float)sign * 0.0625f; + + return ESP_OK; +} - temp = ((temp & 0xfffe) << 3) + (16 - scratchpad[6]) - 4; - *temperature = ((float)temp * 625.0) / 10000 - 0.25; +esp_err_t max31850_read_temperature(gpio_num_t pin, onewire_addr_t addr, float *temperature) +{ + CHECK_ARG(temperature); + + uint8_t scratchpad[8]; + CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); + + int16_t temp = scratchpad[1] << 8 | (scratchpad[0] & 0xfc); + *temperature = (float)temp * 0.0625f; return ESP_OK; } -esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, float *temperature) +esp_err_t ds18x20_read_temperature(gpio_num_t pin, onewire_addr_t addr, float *temperature) { - if ((uint8_t)addr == DS18B20_FAMILY_ID) { - return ds18b20_read_temperature(pin, addr, temperature); - } - else + uint8_t family = (uint8_t)addr; + switch (family) { - return ds18s20_read_temperature(pin, addr, temperature); + case DS18X20_FAMILY_DS18S20: + case DS18X20_FAMILY_DS1822: + return ds18s20_read_temperature(pin, addr, temperature); + case DS18X20_FAMILY_DS18B20: + return ds18b20_read_temperature(pin, addr, temperature); + case DS18X20_FAMILY_MAX31850: + return max31850_read_temperature(pin, addr, temperature); + default: + ESP_LOGE(TAG, "Unknown sensor family %02x. Please use an explicit read/measure function", family); } + return ESP_ERR_NOT_SUPPORTED; } -esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, float *temperature) +esp_err_t ds18b20_measure_and_read(gpio_num_t pin, onewire_addr_t addr, float *temperature) { CHECK_ARG(temperature); @@ -228,7 +247,7 @@ esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, float *t return ds18b20_read_temperature(pin, addr, temperature); } -esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, float *temperature) +esp_err_t ds18s20_measure_and_read(gpio_num_t pin, onewire_addr_t addr, float *temperature) { CHECK_ARG(temperature); @@ -236,7 +255,15 @@ esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, float *t return ds18s20_read_temperature(pin, addr, temperature); } -esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, float *temperature) +esp_err_t max31850_measure_and_read(gpio_num_t pin, onewire_addr_t addr, float *temperature) +{ + CHECK_ARG(temperature); + + CHECK(ds18x20_measure(pin, addr, true)); + return max31850_read_temperature(pin, addr, temperature); +} + +esp_err_t ds18x20_measure_and_read(gpio_num_t pin, onewire_addr_t addr, float *temperature) { CHECK_ARG(temperature); @@ -244,7 +271,7 @@ esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, float *t return ds18x20_read_temperature(pin, addr, temperature); } -esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, float *result_list) +esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, onewire_addr_t *addr_list, size_t addr_count, float *result_list) { CHECK_ARG(result_list && addr_count); @@ -253,7 +280,7 @@ esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t *addr_li return ds18x20_read_temp_multi(pin, addr_list, addr_count, result_list); } -esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, size_t *found) +esp_err_t ds18x20_scan_devices(gpio_num_t pin, onewire_addr_t *addr_list, size_t addr_count, size_t *found) { CHECK_ARG(addr_list && addr_count); @@ -265,7 +292,10 @@ esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) { uint8_t family_id = (uint8_t)addr; - if (family_id == DS18B20_FAMILY_ID || family_id == DS18S20_FAMILY_ID) + if (family_id == DS18X20_FAMILY_DS18S20 + || family_id == DS18X20_FAMILY_DS1822 + || family_id == DS18X20_FAMILY_DS18B20 + || family_id == DS18X20_FAMILY_MAX31850) { if (*found < addr_count) addr_list[*found] = addr; @@ -276,7 +306,7 @@ esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t return ESP_OK; } -esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, float *result_list) +esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, onewire_addr_t *addr_list, size_t addr_count, float *result_list) { CHECK_ARG(result_list); @@ -289,4 +319,3 @@ esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, siz } return res; } - diff --git a/components/ds18x20/ds18x20.h b/components/ds18x20/ds18x20.h index 11b451dc..c80b3771 100644 --- a/components/ds18x20/ds18x20.h +++ b/components/ds18x20/ds18x20.h @@ -57,11 +57,13 @@ typedef onewire_addr_t ds18x20_addr_t; /** An address value which can be used to indicate "any device on the bus" */ #define DS18X20_ANY ONEWIRE_NONE -/** Family ID (lower address byte) of DS18B20 sensors */ -#define DS18B20_FAMILY_ID 0x28 - -/** Family ID (lower address byte) of DS18S20 sensors */ -#define DS18S20_FAMILY_ID 0x10 +/** Family ID (lower address byte) of sensors */ +typedef enum { + DS18X20_FAMILY_DS18S20 = 0x10, //!< DS1820/DS18S20 9-bit +/-0.5°C + DS18X20_FAMILY_DS1822 = 0x22, //!< DS1822 12-bit +/-2°C + DS18X20_FAMILY_DS18B20 = 0x28, //!< DS18B20 12-bit +/-0.5°C + DS18X20_FAMILY_MAX31850 = 0x3b, //!< MAX31850 14-bit +/-0.25°C +} ds18x20_family_id_t; /** * @brief Find the addresses of all ds18x20 devices on the bus. @@ -70,8 +72,8 @@ typedef onewire_addr_t ds18x20_addr_t; * array. If there are more than `addr_count` devices on the bus, only the * first `addr_count` are recorded. * - * @param pin The GPIO pin connected to the ds18x20 bus - * @param addr_list A pointer to an array of ::ds18x20_addr_t values. + * @param pin The GPIO pin connected to the DS18x20 bus + * @param addr_list A pointer to an array of ::onewire_addr_t values. * This will be populated with the addresses of the found * devices. * @param addr_count Number of slots in the `addr_list` array. At most this @@ -82,7 +84,7 @@ typedef onewire_addr_t ds18x20_addr_t; * * @returns `ESP_OK` if the command was successfully issued */ -esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, size_t *found); +esp_err_t ds18x20_scan_devices(gpio_num_t pin, onewire_addr_t *addr_list, size_t addr_count, size_t *found); /** * @brief Tell one or more sensors to perform a temperature measurement and @@ -99,7 +101,7 @@ esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t * and then depower the bus using onewire_depower() or by issuing another * command once conversion is done. * - * @param pin The GPIO pin connected to the ds18x20 device + * @param pin The GPIO pin connected to the DS18x20 device * @param addr The 64-bit address of the device on the bus. This can be set * to ::DS18X20_ANY to send the command to all devices on the bus * at the same time. @@ -109,7 +111,7 @@ esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t * * @returns `ESP_OK` if the command was successfully issued */ -esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait); +esp_err_t ds18x20_measure(gpio_num_t pin, onewire_addr_t addr, bool wait); /** * @brief Read the value from the last CONVERT_T operation. @@ -117,7 +119,24 @@ esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait); * This should be called after ds18x20_measure() to fetch the result of the * temperature measurement. * - * @param pin The GPIO pin connected to the ds18x20 device + * @param pin The GPIO pin connected to the DS18x20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param temperature The temperature in degrees Celsius + * + * @returns `ESP_OK` if the command was successfully issued + */ +esp_err_t ds18x20_read_temperature(gpio_num_t pin, onewire_addr_t addr, float *temperature); + +/** + * @brief Read the value from the last CONVERT_T operation (DS18B20/DS1822 version). + * + * This should be called after ds18x20_measure() to fetch the result of the + * temperature measurement. + * + * @param pin The GPIO pin connected to the DS18B20/DS1822 device * @param addr The 64-bit address of the device to read. This can be set * to ::DS18X20_ANY to read any device on the bus (but note * that this will only work if there is exactly one device @@ -126,15 +145,15 @@ esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait); * * @returns `ESP_OK` if the command was successfully issued */ -esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, float *temperature); +esp_err_t ds18b20_read_temperature(gpio_num_t pin, onewire_addr_t addr, float *temperature); /** - * @brief Read the value from the last CONVERT_T operation (ds18b20 version). + * @brief Read the value from the last CONVERT_T operation (DS18S20 version). * * This should be called after ds18x20_measure() to fetch the result of the * temperature measurement. * - * @param pin The GPIO pin connected to the ds18x20 device + * @param pin The GPIO pin connected to the DS18S20 device * @param addr The 64-bit address of the device to read. This can be set * to ::DS18X20_ANY to read any device on the bus (but note * that this will only work if there is exactly one device @@ -143,15 +162,15 @@ esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, float *t * * @returns `ESP_OK` if the command was successfully issued */ -esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, float *temperature); +esp_err_t ds18s20_read_temperature(gpio_num_t pin, onewire_addr_t addr, float *temperature); /** - * @brief Read the value from the last CONVERT_T operation (ds18s20 version). + * @brief Read the value from the last CONVERT_T operation (MAX31850 version). * * This should be called after ds18x20_measure() to fetch the result of the * temperature measurement. * - * @param pin The GPIO pin connected to the ds18x20 device + * @param pin The GPIO pin connected to the MAX31850 device * @param addr The 64-bit address of the device to read. This can be set * to ::DS18X20_ANY to read any device on the bus (but note * that this will only work if there is exactly one device @@ -160,7 +179,7 @@ esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, float *t * * @returns `ESP_OK` if the command was successfully issued */ -esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, float *temperature); +esp_err_t max31850_read_temperature(gpio_num_t pin, onewire_addr_t addr, float *temperature); /** * @brief Read the value from the last CONVERT_T operation for multiple devices. @@ -172,49 +191,63 @@ esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, float *t * @param addr_list A list of addresses for devices to read. * @param addr_count The number of entries in `addr_list`. * @param result_list An array of floats to hold the returned temperature - * values. It should have at least `addr_count` entries. + * values. It should have at least `addr_count` entries. * * @returns `ESP_OK` if all temperatures were fetched successfully */ -esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, float *result_list); +esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, onewire_addr_t *addr_list, size_t addr_count, float *result_list); -/** Perform a ds18x20_measure() followed by ds18s20_read_temperature() +/** Perform a ds18x20_measure() followed by ds18x20_read_temperature() * - * @param pin The GPIO pin connected to the ds18s20 device + * @param pin The GPIO pin connected to the DS18x20 device * @param addr The 64-bit address of the device to read. This can be set * to ::DS18X20_ANY to read any device on the bus (but note * that this will only work if there is exactly one device * connected, or they will corrupt each others' transmissions) * @param temperature The temperature in degrees Celsius */ -esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, float *temperature); +esp_err_t ds18x20_measure_and_read(gpio_num_t pin, onewire_addr_t addr, float *temperature); -/** Perform a ds18x20_measure() followed by ds18b20_read_temperature() +/** + * @brief Perform a ds18x20_measure() followed by ds18s20_read_temperature() * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius + * @param pin The GPIO pin connected to the DS18S20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param temperature The temperature in degrees Celsius */ -esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, float *temperature); +esp_err_t ds18s20_measure_and_read(gpio_num_t pin, onewire_addr_t addr, float *temperature); -/** Perform a ds18x20_measure() followed by ds18x20_read_temperature() +/** + * @brief Perform a ds18x20_measure() followed by ds18b20_read_temperature() * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius + * @param pin The GPIO pin connected to the DS18B20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param temperature The temperature in degrees Celsius + */ +esp_err_t ds18b20_measure_and_read(gpio_num_t pin, onewire_addr_t addr, float *temperature); + +/** + * @brief Perform a ds18x20_measure() followed by max31850_read_temperature() + * + * @param pin The GPIO pin connected to the MAX31850 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param temperature The temperature in degrees Celsius */ -esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, float *temperature); +esp_err_t max31850_measure_and_read(gpio_num_t pin, onewire_addr_t addr, float *temperature); /** * @brief Perform a ds18x20_measure() followed by ds18x20_read_temp_multi() * - * @param pin The GPIO pin connected to the ds18x20 bus + * @param pin The GPIO pin connected to the DS18x20 bus * @param addr_list A list of addresses for devices to read. * @param addr_count The number of entries in `addr_list`. * @param result_list An array of floats to hold the returned temperature @@ -222,7 +255,7 @@ esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, float *t * * @returns `ESP_OK` if all temperatures were fetched successfully */ -esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, float *result_list); +esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, onewire_addr_t *addr_list, size_t addr_count, float *result_list); /** * @brief Read the scratchpad data for a particular ds18x20 device. @@ -230,7 +263,7 @@ esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t *addr_li * This is not generally necessary to do directly. It is done automatically * as part of ds18x20_read_temperature(). * - * @param pin The GPIO pin connected to the ds18x20 device + * @param pin The GPIO pin connected to the DS18X20 device * @param addr The 64-bit address of the device to read. This can be set * to ::DS18X20_ANY to read any device on the bus (but note * that this will only work if there is exactly one device @@ -239,12 +272,12 @@ esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t *addr_li * * @returns `ESP_OK` if the command was successfully issued */ -esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer); +esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, onewire_addr_t addr, uint8_t *buffer); /** * @brief Write the scratchpad data for a particular ds18x20 device. * - * @param pin The GPIO pin connected to the ds18x20 device + * @param pin The GPIO pin connected to the DS18x20 device * @param addr The 64-bit address of the device to write. This can be set * to ::DS18X20_ANY to read any device on the bus (but note * that this will only work if there is exactly one device @@ -253,13 +286,13 @@ esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t * * * @returns `ESP_OK` if the command was successfully issued */ -esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer); +esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, onewire_addr_t addr, uint8_t *buffer); /** * @brief Issue the copy scratchpad command, copying current scratchpad to * EEPROM. * - * @param pin The GPIO pin connected to the ds18x20 device + * @param pin The GPIO pin connected to the DS18x20 device * @param addr The 64-bit address of the device to command. This can be set * to ::DS18X20_ANY to read any device on the bus (but note * that this will only work if there is exactly one device @@ -267,7 +300,7 @@ esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t * * @returns `ESP_OK` if the command was successfully issued */ -esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr); +esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, onewire_addr_t addr); #ifdef __cplusplus diff --git a/components/ds3231/.eil.yml b/components/ds3231/.eil.yml index 5ef0cdb1..9393a6fa 100644 --- a/components/ds3231/.eil.yml +++ b/components/ds3231/.eil.yml @@ -1,28 +1,25 @@ ---- -components: - - name: ds3231 - description: | - Driver for DS1337 RTC and DS3231 high precision RTC module - group: rtc - groups: [] - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - name: UncleRus - year: 2018 - - name: BhuvanchandraD - year: 2016 - - name: RichardA - year: 2015 +name: ds3231 +description: Driver for DS1337 RTC and DS3231 high precision RTC module +version: 1.1.0 +groups: + - rtc +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: RichardA + year: 2015 + - name: BhuvanchandraD + year: 2016 + - name: UncleRus + year: 2018 diff --git a/components/ds3231/ds3231.c b/components/ds3231/ds3231.c index 159d1e3d..2faa19a5 100644 --- a/components/ds3231/ds3231.c +++ b/components/ds3231/ds3231.c @@ -36,6 +36,7 @@ * * MIT Licensed as described in the file LICENSE */ +#include #include #include #include "ds3231.h" @@ -77,6 +78,9 @@ enum { DS3231_REPLACE }; +static const int days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static const int days_per_month_leap_year[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + static uint8_t bcd2dec(uint8_t val) { return (val >> 4) * 10 + (val & 0x0f); @@ -87,6 +91,25 @@ static uint8_t dec2bcd(uint8_t val) return ((val / 10) << 4) + (val % 10); } +// Function to convert year, month, and day to days since January 1st +static inline int days_since_january_1st(int year, int month, int day) +{ + int days = day - 1; + const int *ptr = days_per_month; + + // Handle leap year + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) + ptr = days_per_month_leap_year; + + // Add days from previous months + for (int i = 0; i < month; i++) + { + days += ptr[i]; + } + + return days; +} + esp_err_t ds3231_init_desc(i2c_dev_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) { CHECK_ARG(dev); @@ -433,6 +456,7 @@ esp_err_t ds3231_get_time(i2c_dev_t *dev, struct tm *time) time->tm_mon = bcd2dec(data[5] & DS3231_MONTH_MASK) - 1; time->tm_year = bcd2dec(data[6]) + 100; time->tm_isdst = 0; + time->tm_yday = days_since_january_1st(time->tm_year, time->tm_mon, time->tm_mday); // apply a time zone (if you are not using localtime on the rtc or you want to check/apply DST) //applyTZ(time); diff --git a/components/ds3502/.eil.yml b/components/ds3502/.eil.yml index 5f1f3a99..69605694 100644 --- a/components/ds3502/.eil.yml +++ b/components/ds3502/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: ds3502 - description: | - Driver for nonvolatile digital potentiometer DS3502 - group: misc - groups: [] - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2021 +name: ds3502 +description: Driver for nonvolatile digital potentiometer DS3502 +version: 1.0.0 +groups: + - misc +code_owners: + - UncleRus +depends: + - i2cdev + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2021 diff --git a/components/encoder/.eil.yml b/components/encoder/.eil.yml index 32ce9e93..d741eb2d 100644 --- a/components/encoder/.eil.yml +++ b/components/encoder/.eil.yml @@ -1,24 +1,21 @@ ---- -components: - - name: encoder - description: | - HW timer-based driver for incremental rotary encoders - group: input - groups: [] - code_owners: - - name: UncleRus - depends: - - name: driver - - name: freertos - - name: log - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2019 +name: encoder +description: HW timer-based driver for incremental rotary encoders +version: 1.0.0 +groups: + - input +code_owners: + - UncleRus +depends: + - driver + - freertos + - log +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2019 diff --git a/components/encoder/Kconfig b/components/encoder/Kconfig index 5ef2a56c..d1990a0b 100644 --- a/components/encoder/Kconfig +++ b/components/encoder/Kconfig @@ -6,7 +6,8 @@ menu "Rotary encoders" config RE_INTERVAL_US int "Polling interval, us" - default 1000 + default 10000 if IDF_TARGET_ESP8266 + default 1000 if !IDF_TARGET_ESP8266 config RE_BTN_DEAD_TIME_US int "Button dead time, us" diff --git a/components/encoder/encoder.c b/components/encoder/encoder.c index b84f8bcd..4be63aa4 100644 --- a/components/encoder/encoder.c +++ b/components/encoder/encoder.c @@ -48,6 +48,10 @@ #define BTN_PRESSED_LEVEL 1 #endif +#if defined(CONFIG_IDF_TARGET_ESP8266) && CONFIG_RE_INTERVAL_US < 10000 +#error Too small CONFIG_RE_INTERVAL_US! For ESP8266 it should be >= 10000 +#endif + static const char *TAG = "encoder"; static rotary_encoder_t *encs[CONFIG_RE_MAX] = { 0 }; static const int8_t valid_states[] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; @@ -204,7 +208,13 @@ esp_err_t rotary_encoder_add(rotary_encoder_t *re) gpio_config_t io_conf; memset(&io_conf, 0, sizeof(gpio_config_t)); io_conf.mode = GPIO_MODE_INPUT; - io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + if (BTN_PRESSED_LEVEL == 0) { + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + } else { + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE; + } io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.pin_bit_mask = GPIO_BIT(re->pin_a) | GPIO_BIT(re->pin_b); if (re->pin_btn < GPIO_NUM_MAX) diff --git a/components/esp_idf_lib_helpers/.eil.yml b/components/esp_idf_lib_helpers/.eil.yml index 111ed010..eb4f9e0d 100644 --- a/components/esp_idf_lib_helpers/.eil.yml +++ b/components/esp_idf_lib_helpers/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: esp_idf_lib_helpers - description: | - Common support library for esp-idf-lib - group: common - groups: [] - code_owners: - - name: UncleRus - - name: trombik - depends: - - name: freertos - thread_safe: N/A - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: ISC - copyrights: - - name: trombik - year: 2019 +name: esp_idf_lib_helpers +description: Common support library for esp-idf-lib +version: 1.2.0 +groups: + - common +code_owners: + - trombik + - UncleRus +depends: + - freertos +thread_safe: n/a +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: ISC +copyrights: + - name: trombik + year: 2019 diff --git a/components/esp_idf_lib_helpers/esp_idf_lib_helpers.h b/components/esp_idf_lib_helpers/esp_idf_lib_helpers.h index 7c644db9..2c74e3bc 100644 --- a/components/esp_idf_lib_helpers/esp_idf_lib_helpers.h +++ b/components/esp_idf_lib_helpers/esp_idf_lib_helpers.h @@ -43,7 +43,10 @@ #if defined(CONFIG_IDF_TARGET_ESP32) \ || defined(CONFIG_IDF_TARGET_ESP32S2) \ || defined(CONFIG_IDF_TARGET_ESP32S3) \ - || defined(CONFIG_IDF_TARGET_ESP32C3) + || defined(CONFIG_IDF_TARGET_ESP32C2) \ + || defined(CONFIG_IDF_TARGET_ESP32C3) \ + || defined(CONFIG_IDF_TARGET_ESP32C6) \ + || defined(CONFIG_IDF_TARGET_ESP32H2) #define HELPER_TARGET_IS_ESP32 (1) #define HELPER_TARGET_IS_ESP8266 (0) @@ -71,6 +74,7 @@ #define VALUE(x) VALUE_TO_STRING(x) #define VAR_NAME_VALUE(var) #var "=" VALUE(var) #pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32C3)) +#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32H2)) #pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32S2)) #pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32)) #pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP8266)) diff --git a/components/esp_idf_lib_helpers/ets_sys.h b/components/esp_idf_lib_helpers/ets_sys.h index 05851663..eb834813 100644 --- a/components/esp_idf_lib_helpers/ets_sys.h +++ b/components/esp_idf_lib_helpers/ets_sys.h @@ -1,13 +1,21 @@ #if CONFIG_IDF_TARGET_ESP32 #include -#elif CONFIG_IDF_TARGET_ESP32S2 -#include -#elif CONFIG_IDF_TARGET_ESP32S3 -#include +#elif CONFIG_IDF_TARGET_ESP32C2 +#include #elif CONFIG_IDF_TARGET_ESP32C3 #include +#elif CONFIG_IDF_TARGET_ESP32C6 +#include #elif CONFIG_IDF_TARGET_ESP32H2 #include -#elif CONFIG_IDF_TARGET_ESP32C2 -#include +#elif CONFIG_IDF_TARGET_ESP32H4 +#include +#elif CONFIG_IDF_TARGET_ESP32S2 +#include +#elif CONFIG_IDF_TARGET_ESP32S3 +#include +#elif CONFIG_IDF_TARGET_ESP8266 +#include +#else +#error "ets_sys: Unknown target" #endif diff --git a/components/example/.eil.yml b/components/example/.eil.yml index d46f8292..a83433fa 100644 --- a/components/example/.eil.yml +++ b/components/example/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: example - description: An example component - group: misc - groups: [] - code_owners: trombik - depends: - # FIXME conditional depends - - name: driver - - name: log - thread_safe: N/A - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: ISC - copyrights: - - author: - name: trombik - year: 2021 +name: example +description: An example component +version: 0.0.1 +groups: + - misc +code_owners: trombik +depends: + # FIXME conditional depends + - driver + - log +thread_safe: n/a # yes, no, n/a +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: ISC +copyrights: + - name: trombik + year: 2021 diff --git a/components/framebuffer/.eil.yml b/components/framebuffer/.eil.yml index 0d8ec259..87915ba5 100644 --- a/components/framebuffer/.eil.yml +++ b/components/framebuffer/.eil.yml @@ -1,21 +1,18 @@ ---- -components: - - name: framebuffer - description: RGB framebuffer component - group: common - groups: [] - code_owners: UncleRus - depends: - - name: log - - name: color - thread_safe: N/A - targets: - - name: esp32 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: UncleRus - year: 2021 +name: framebuffer +description: RGB framebuffer component +version: 0.0.1 +groups: + - common +code_owners: UncleRus +depends: + - log + - color +thread_safe: N/A +targets: + - esp32 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: UncleRus + year: 2021 diff --git a/components/hd44780/.eil.yml b/components/hd44780/.eil.yml index a2a05f24..ac0ac973 100644 --- a/components/hd44780/.eil.yml +++ b/components/hd44780/.eil.yml @@ -1,22 +1,19 @@ ---- -components: - - name: hd44780 - description: | - Driver for HD44780 compatible LCD text displays - group: misc - groups: [] - code_owners: - - name: UncleRus - depends: - - name: driver - thread_safe: no - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2016 +name: hd44780 +description: Driver for HD44780 compatible LCD text displays +version: 1.2.0 +groups: + - misc +code_owners: + - UncleRus +depends: + - driver +thread_safe: no +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2016 diff --git a/components/hdc1000/.eil.yml b/components/hdc1000/.eil.yml index cefea3da..b0f88c49 100644 --- a/components/hdc1000/.eil.yml +++ b/components/hdc1000/.eil.yml @@ -1,25 +1,22 @@ ---- -components: - - name: hdc1000 - description: | - Driver for HDC1000 temperature and humidity sensor - group: temperature - groups: - - humidity - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2021 +name: hdc1000 +description: Driver for HDC1000 temperature and humidity sensor +version: 1.0.0 +groups: + - temperature + - humidity +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2021 diff --git a/components/hmc5883l/.eil.yml b/components/hmc5883l/.eil.yml index 013bd3cc..6f85b124 100644 --- a/components/hmc5883l/.eil.yml +++ b/components/hmc5883l/.eil.yml @@ -1,24 +1,21 @@ ---- -components: - - name: hmc5883l - description: | - Driver for 3-axis digital compass HMC5883L and HMC5983L - group: magnetic - groups: [] - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2016 +name: hmc5883l +description: Driver for 3-axis digital compass HMC5883L and HMC5983L +version: 1.0.0 +groups: + - magnetic +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2016 diff --git a/components/ht16k33/.eil.yml b/components/ht16k33/.eil.yml index 8fce5308..4ed1438f 100644 --- a/components/ht16k33/.eil.yml +++ b/components/ht16k33/.eil.yml @@ -1,22 +1,19 @@ ---- -components: - - name: ht16k33 - description: HT16K33 LED controller driver - group: led - groups: [] - code_owners: chudsaviet - depends: - - name: i2cdev - - name: log - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: chudsaviet - year: 2022 +name: ht16k33 +description: HT16K33 LED controller driver +version: 1.0.0 +groups: + - led +code_owners: chudsaviet +depends: + - i2cdev + - log +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: chudsaviet + year: 2022 diff --git a/components/ht16k33/ht16k33.h b/components/ht16k33/ht16k33.h index 5a78b0a9..ed918b5b 100644 --- a/components/ht16k33/ht16k33.h +++ b/components/ht16k33/ht16k33.h @@ -63,6 +63,7 @@ typedef enum { * @param port I2C port number * @param sda_gpio GPIO pin number for SDA * @param scl_gpio GPIO pin number for SCL + * @param addr I2C address * @return `ESP_OK` on success */ esp_err_t ht16k33_init_desc(i2c_dev_t *dev, i2c_port_t port, @@ -108,6 +109,7 @@ esp_err_t ht16k33_display_setup(i2c_dev_t *dev, uint8_t on_flag, /** * @brief Write whole HT16K33_RAM_SIZE_BYTES into RAM. * + * @param dev I2C device descriptor * @param data Bytes to write. * @return ESP_OK to indicate success */ diff --git a/components/hts221/.eil.yml b/components/hts221/.eil.yml index cdc440a9..2c4fdf95 100644 --- a/components/hts221/.eil.yml +++ b/components/hts221/.eil.yml @@ -1,24 +1,21 @@ ---- -components: - - name: hts221 - description: | - Driver for HTS221 temperature and humidity sensor. - group: temperature - groups: - - humidity - code_owners: - -name: saasaa - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: N/A - targets: - - name: esp32 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: ISC - copyrights: - - name: saasaa - year: 2021 +name: hts221 +description: Driver for HTS221 temperature and humidity sensor +version: 1.0.0 +groups: + - temperature + - humidity +code_owners: + - saasaa +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp32s2 + - esp32c3 +license: ISC +copyrights: + - name: saasaa + year: 2021 diff --git a/components/hx711/.eil.yml b/components/hx711/.eil.yml index cc7f449f..e70ae64b 100644 --- a/components/hx711/.eil.yml +++ b/components/hx711/.eil.yml @@ -1,24 +1,21 @@ ---- -components: - - name: hx711 - description: | - Driver for HX711 24-bit ADC for weigh scales - group: adc-dac - groups: [] - code_owners: - - name: UncleRus - depends: - - name: driver - - name: freertos - - name: esp_idf_lib_helpers - thread_safe: no - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2019 +name: hx711 +description: Driver for HX711 24-bit ADC for weigh scales +version: 1.0.0 +groups: + - adc-dac +code_owners: + - UncleRus +depends: + - driver + - freertos + - esp_idf_lib_helpers +thread_safe: no +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2019 diff --git a/components/hx711/hx711.c b/components/hx711/hx711.c index 12dcf46c..c0ebbe78 100644 --- a/components/hx711/hx711.c +++ b/components/hx711/hx711.c @@ -167,7 +167,7 @@ esp_err_t hx711_read_average(hx711_t *dev, size_t times, int32_t *data) CHECK(hx711_read_data(dev, &v)); *data += v; } - *data /= times; + *data /= (int32_t) times; return ESP_OK; } diff --git a/components/i2cdev/.eil.yml b/components/i2cdev/.eil.yml index 5f404d61..10c8abb6 100644 --- a/components/i2cdev/.eil.yml +++ b/components/i2cdev/.eil.yml @@ -1,24 +1,21 @@ ---- -components: - - name: i2cdev - description: | - ESP-IDF I2C master thread-safe utilities - group: common - groups: [] - code_owners: - - name: UncleRus - depends: - - name: driver - - name: freertos - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - name: UncleRus - year: 2018 +name: i2cdev +description: ESP-IDF I2C master thread-safe utilities +version: 1.5.0 +groups: + - common +code_owners: + - UncleRus +depends: + - driver + - freertos + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: UncleRus + year: 2018 diff --git a/components/i2cdev/i2cdev.c b/components/i2cdev/i2cdev.c index 53a4b448..7f1bba28 100644 --- a/components/i2cdev/i2cdev.c +++ b/components/i2cdev/i2cdev.c @@ -180,7 +180,9 @@ inline static bool cfg_equal(const i2c_config_t *a, const i2c_config_t *b) #if HELPER_TARGET_IS_ESP32 && a->master.clk_speed == b->master.clk_speed #elif HELPER_TARGET_IS_ESP8266 - && a->clk_stretch_tick == b->clk_stretch_tick + && ((a->clk_stretch_tick && a->clk_stretch_tick == b->clk_stretch_tick) + || (!a->clk_stretch_tick && b->clk_stretch_tick == I2CDEV_MAX_STRETCH_TIME) + ) // see line 232 #endif && a->scl_pullup_en == b->scl_pullup_en && a->sda_pullup_en == b->sda_pullup_en; @@ -205,11 +207,19 @@ static esp_err_t i2c_setup_port(const i2c_dev_t *dev) states[dev->port].installed = false; } #if HELPER_TARGET_IS_ESP32 +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) + // See https://github.com/espressif/esp-idf/issues/10163 + if ((res = i2c_driver_install(dev->port, temp.mode, 0, 0, 0)) != ESP_OK) + return res; + if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) + return res; +#else if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) return res; if ((res = i2c_driver_install(dev->port, temp.mode, 0, 0, 0)) != ESP_OK) return res; #endif +#endif #if HELPER_TARGET_IS_ESP8266 // Clock Stretch time, depending on CPU frequency temp.clk_stretch_tick = dev->timeout_ticks ? dev->timeout_ticks : I2CDEV_MAX_STRETCH_TIME; diff --git a/components/icm42670/.eil.yml b/components/icm42670/.eil.yml new file mode 100644 index 00000000..008cd962 --- /dev/null +++ b/components/icm42670/.eil.yml @@ -0,0 +1,20 @@ +name: icm42670 +description: Driver for TDK ICM-42670-P 6-Axis IMU +version: 1.0.0 +groups: + - imu +code_owners: janveeh +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: ISC +copyrights: + - name: janveeh + year: 2022 diff --git a/components/icm42670/CMakeLists.txt b/components/icm42670/CMakeLists.txt new file mode 100644 index 00000000..0bc4e7f4 --- /dev/null +++ b/components/icm42670/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS icm42670.c + INCLUDE_DIRS . + REQUIRES i2cdev log esp_idf_lib_helpers +) diff --git a/components/icm42670/LICENSE b/components/icm42670/LICENSE new file mode 100644 index 00000000..41953274 --- /dev/null +++ b/components/icm42670/LICENSE @@ -0,0 +1,16 @@ + +SPDX-License-Identifier: ISC + +Copyright (c) 2022 Jan Veeh + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/components/icm42670/component.mk b/components/icm42670/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/icm42670/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/icm42670/icm42670.c b/components/icm42670/icm42670.c new file mode 100644 index 00000000..933d8cc4 --- /dev/null +++ b/components/icm42670/icm42670.c @@ -0,0 +1,722 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2022 Jan Veeh + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file icm42670.c + * + * ESP-IDF driver for TDK ICM-42670-P IMU (found on ESP-RS board) + * + * Copyright (c) 2022 Jan Veeh (jan.veeh@motius.de) + * + * ISC Licensed as described in the file LICENSE + * + * Open TODOs: + * - FIFO reading and handling + * - APEX functions like pedometer, tilt-detection, low-g detection, freefall detection, ... + * + * + */ + +#include +#include +#include +#include +#include +#include "icm42670.h" + +#define I2C_FREQ_HZ 1000000 // 1MHz + +static const char *TAG = "icm42670"; + +// register structure definitions +#define ICM42670_MCLK_RDY_BITS 0x08 // ICM42670_REG_MCLK_RDY<3> +#define ICM42670_MCLK_RDY_SHIFT 3 // ICM42670_REG_MCLK_RDY<3> + +#define ICM42670_SPI_AP_4WIRE_BITS 0x04 // ICM42670_REG_DEVICE_CONFIG<2> +#define ICM42670_SPI_AP_4WIRE_SHIFT 2 // ICM42670_REG_DEVICE_CONFIG<2> +#define ICM42670_SPI_MODE_BITS 0x01 // ICM42670_REG_DEVICE_CONFIG<0> +#define ICM42670_SPI_MODE_SHIFT 0 // ICM42670_REG_DEVICE_CONFIG<0> + +#define ICM42670_SOFT_RESET_DEVICE_CONFIG_BITS 0x10 // ICM42670_REG_SIGNAL_PATH_RESET<4> +#define ICM42670_SOFT_RESET_DEVICE_CONFIG_SHIFT 4 // ICM42670_REG_SIGNAL_PATH_RESET<4> +#define ICM42670_FIFO_FLUSH_BITS 0x04 // ICM42670_REG_SIGNAL_PATH_RESET<2> +#define ICM42670_FIFO_FLUSH_SHIFT 2 // ICM42670_REG_SIGNAL_PATH_RESET<2> + +#define ICM42670_I3C_DDR_SLEW_RATE_BITS 0x38 // ICM42670_REG_DRIVE_CONFIG1<5:3> +#define ICM42670_I3C_DDR_SLEW_RATE_SHIFT 3 // ICM42670_REG_DRIVE_CONFIG1<5:3> +#define ICM42670_I3C_SDR_SLEW_RATE_BITS 0x07 // ICM42670_REG_DRIVE_CONFIG1<2:0> +#define ICM42670_I3C_SDR_SLEW_RATE_SHIFT 0 // ICM42670_REG_DRIVE_CONFIG1<2:0> + +#define ICM42670_I2C_DDR_SLEW_RATE_BITS 0x38 // ICM42670_REG_DRIVE_CONFIG2<5:3> +#define ICM42670_I2C_DDR_SLEW_RATE_SHIFT 3 // ICM42670_REG_DRIVE_CONFIG2<5:3> +#define ICM42670_I2C_SDR_SLEW_RATE_BITS 0x07 // ICM42670_REG_DRIVE_CONFIG2<2:0> +#define ICM42670_I2C_SDR_SLEW_RATE_SHIFT 0 // ICM42670_REG_DRIVE_CONFIG2<2:0> + +#define ICM42670_SPI_SLEW_RATE_BITS 0x07 // ICM42670_REG_DRIVE_CONFIG3<2:0> +#define ICM42670_SPI_SLEW_RATE_SHIFT 0 // ICM42670_REG_DRIVE_CONFIG3<2:0> + +#define ICM42670_INT2_MODE_BITS 0x20 // ICM42670_REG_INT_CONFIG<5> +#define ICM42670_INT2_MODE_SHIFT 5 // ICM42670_REG_INT_CONFIG<5> +#define ICM42670_INT2_DRIVE_CIRCUIT_BITS 0x10 // ICM42670_REG_INT_CONFIG<4> +#define ICM42670_INT2_DRIVE_CIRCUIT_SHIFT 4 // ICM42670_REG_INT_CONFIG<4> +#define ICM42670_INT2_POLARITY_BITS 0x08 // ICM42670_REG_INT_CONFIG<3> +#define ICM42670_INT2_POLARITY_SHIFT 3 // ICM42670_REG_INT_CONFIG<3> +#define ICM42670_INT1_MODE_BITS 0x04 // ICM42670_REG_INT_CONFIG<2> +#define ICM42670_INT1_MODE_SHIFT 2 // ICM42670_REG_INT_CONFIG<2> +#define ICM42670_INT1_DRIVE_CIRCUIT_BITS 0x02 // ICM42670_REG_INT_CONFIG<1> +#define ICM42670_INT1_DRIVE_CIRCUIT_SHIFT 1 // ICM42670_REG_INT_CONFIG<1> +#define ICM42670_INT1_POLARITY_BITS 0x01 // ICM42670_REG_INT_CONFIG<0> +#define ICM42670_INT1_POLARITY_SHIFT 0 // ICM42670_REG_INT_CONFIG<0> + +#define ICM42670_ACCEL_LP_CLK_SEL_BITS 0x80 // ICM42670_REG_PWR_MGMT0<7> +#define ICM42670_ACCEL_LP_CLK_SEL_SHIFT 7 // ICM42670_REG_PWR_MGMT0<7> +#define ICM42670_IDLE_BITS 0x10 // ICM42670_REG_PWR_MGMT0<4> +#define ICM42670_IDLE_SHIFT 4 // ICM42670_REG_PWR_MGMT0<4> +#define ICM42670_GYRO_MODE_BITS 0x0C // ICM42670_REG_PWR_MGMT0<3:2> +#define ICM42670_GYRO_MODE_SHIFT 2 // ICM42670_REG_PWR_MGMT0<3:2> +#define ICM42670_ACCEL_MODE_BITS 0x03 // ICM42670_REG_PWR_MGMT0<1:0> +#define ICM42670_ACCEL_MODE_SHIFT 0 // ICM42670_REG_PWR_MGMT0<1:0> + +#define ICM42670_GYRO_UI_FS_SEL_BITS 0x60 // ICM42670_REG_GYRO_CONFIG0<6:5> +#define ICM42670_GYRO_UI_FS_SEL_SHIFT 5 // ICM42670_REG_GYRO_CONFIG0<6:5> +#define ICM42670_GYRO_ODR_BITS 0x0F // ICM42670_REG_GYRO_CONFIG0<3:0> +#define ICM42670_GYRO_ODR_SHIFT 0 // ICM42670_REG_GYRO_CONFIG0<3:0> + +#define ICM42670_ACCEL_UI_FS_SEL_BITS 0x60 // ICM42670_REG_ACCEL_CONFIG0<6:5> +#define ICM42670_ACCEL_UI_FS_SEL_SHIFT 5 // ICM42670_REG_ACCEL_CONFIG0<6:5> +#define ICM42670_ACCEL_ODR_BITS 0x0F // ICM42670_REG_ACCEL_CONFIG0<3:0> +#define ICM42670_ACCEL_ODR_SHIFT 0 // ICM42670_REG_ACCEL_CONFIG0<3:0> + +#define ICM42670_TEMP_FILT_BW_BITS 0x70 // ICM42670_REG_TEMP_CONFIG0<6:4> +#define ICM42670_TEMP_FILT_BW_SHIFT 4 // ICM42670_REG_TEMP_CONFIG0<6:4> + +#define ICM42670_GYRO_UI_FILT_BW_BITS 0x07 // ICM42670_REG_GYRO_CONFIG1<2:0> +#define ICM42670_GYRO_UI_FILT_BW_SHIFT 0 // ICM42670_REG_GYRO_CONFIG1<2:0> + +#define ICM42670_ACCEL_UI_AVG_BITS 0x70 // ICM42670_REG_ACCEL_CONFIG1<6:4> +#define ICM42670_ACCEL_UI_AVG_SHIFT 4 // ICM42670_REG_ACCEL_CONFIG1<6:4> +#define ICM42670_ACCEL_UI_FILT_BW_BITS 0x07 // ICM42670_REG_ACCEL_CONFIG1<2:0> +#define ICM42670_ACCEL_UI_FILT_BW_SHIFT 0 // ICM42670_REG_ACCEL_CONFIG1<2:0> + +#define ICM42670_DMP_POWER_SAVE_EN_BITS 0x08 // ICM42670_REG_APEX_CONFIG0<3> +#define ICM42670_DMP_POWER_SAVE_EN_SHIFT 3 // ICM42670_REG_APEX_CONFIG0<3> +#define ICM42670_DMP_INIT_EN_BITS 0x04 // ICM42670_REG_APEX_CONFIG0<2> +#define ICM42670_DMP_INIT_EN_SHIFT 2 // ICM42670_REG_APEX_CONFIG0<2> +#define ICM42670_DMP_MEM_RESET_EN_BITS 0x01 // ICM42670_REG_APEX_CONFIG0<0> +#define ICM42670_DMP_MEM_RESET_EN_SHIFT 0 // ICM42670_REG_APEX_CONFIG0<0> + +#define ICM42670_SMD_ENABLE_BITS 0x40 // ICM42670_REG_APEX_CONFIG1<6> +#define ICM42670_SMD_ENABLE_SHIFT 6 // ICM42670_REG_APEX_CONFIG1<6> +#define ICM42670_FF_ENABLE_BITS 0x20 // ICM42670_REG_APEX_CONFIG1<5> +#define ICM42670_FF_ENABLE_SHIFT 5 // ICM42670_REG_APEX_CONFIG1<5> +#define ICM42670_TILT_ENABLE_BITS 0x10 // ICM42670_REG_APEX_CONFIG1<4> +#define ICM42670_TILT_ENABLE_SHIFT 4 // ICM42670_REG_APEX_CONFIG1<4> +#define ICM42670_PED_ENABLE_BITS 0x08 // ICM42670_REG_APEX_CONFIG1<3> +#define ICM42670_PED_ENABLE_SHIFT 3 // ICM42670_REG_APEX_CONFIG1<3> +#define ICM42670_DMP_ODR_BITS 0x03 // ICM42670_REG_APEX_CONFIG1<1:0> +#define ICM42670_DMP_ODR_SHIFT 0 // ICM42670_REG_APEX_CONFIG1<1:0> + +#define ICM42670_WOM_INT_DUR_BITS 0x18 // ICM42670_REG_WOM_CONFIG<4:3> +#define ICM42670_WOM_INT_DUR_SHIFT 3 // ICM42670_REG_WOM_CONFIG<4:3> +#define ICM42670_WOM_INT_MODE_BITS 0x04 // ICM42670_REG_WOM_CONFIG<2> +#define ICM42670_WOM_INT_MODE_SHIFT 2 // ICM42670_REG_WOM_CONFIG<2> +#define ICM42670_WOM_MODE_BITS 0x02 // ICM42670_REG_WOM_CONFIG<1> +#define ICM42670_WOM_MODE_SHIFT 1 // ICM42670_REG_WOM_CONFIG<1> +#define ICM42670_WOM_EN_BITS 0x01 // ICM42670_REG_WOM_CONFIG<0> +#define ICM42670_WOM_EN_SHIFT 0 // ICM42670_REG_WOM_CONFIG<0> + +#define ICM42670_FIFO_MODE_BITS 0x02 // ICM42670_REG_FIFO_CONFIG1<1> +#define ICM42670_FIFO_MODE_SHIFT 1 // ICM42670_REG_FIFO_CONFIG1<1> +#define ICM42670_FIFO_BYPASS_BITS 0x01 // ICM42670_REG_FIFO_CONFIG1<0> +#define ICM42670_FIFO_BYPASS_SHIFT 0 // ICM42670_REG_FIFO_CONFIG1<0> + +#define ICM42670_ST_INT1_EN_BITS 0x80 // ICM42670_REG_INT_SOURCE0<7> +#define ICM42670_ST_INT1_EN_SHIFT 7 // ICM42670_REG_INT_SOURCE0<7> +#define ICM42670_FSYNC_INT1_EN_BITS 0x40 // ICM42670_REG_INT_SOURCE0<6> +#define ICM42670_FSYNC_INT1_EN_SHIFT 6 // ICM42670_REG_INT_SOURCE0<6> +#define ICM42670_PLL_RDY_INT1_EN_BITS 0x20 // ICM42670_REG_INT_SOURCE0<5> +#define ICM42670_PLL_RDY_INT1_EN_SHIFT 5 // ICM42670_REG_INT_SOURCE0<5> +#define ICM42670_RESET_DONE_INT1_EN_BITS 0x10 // ICM42670_REG_INT_SOURCE0<4> +#define ICM42670_RESET_DONE_INT1_EN_SHIFT 4 // ICM42670_REG_INT_SOURCE0<4> +#define ICM42670_DRDY_INT1_EN_BITS 0x08 // ICM42670_REG_INT_SOURCE0<3> +#define ICM42670_DRDY_INT1_EN_SHIFT 3 // ICM42670_REG_INT_SOURCE0<3> +#define ICM42670_FIFO_THS_INT1_EN_BITS 0x04 // ICM42670_REG_INT_SOURCE0<2> +#define ICM42670_FIFO_THS_INT1_EN_SHIFT 2 // ICM42670_REG_INT_SOURCE0<2> +#define ICM42670_FIFO_FULL_INT1_EN_BITS 0x02 // ICM42670_REG_INT_SOURCE0<1> +#define ICM42670_FIFO_FULL_INT1_EN_SHIFT 1 // ICM42670_REG_INT_SOURCE0<1> +#define ICM42670_AGC_RDY_INT1_EN_BITS 0x01 // ICM42670_REG_INT_SOURCE0<0> +#define ICM42670_AGC_RDY_INT1_EN_SHIFT 0 // ICM42670_REG_INT_SOURCE0<0> + +#define ICM42670_I3C_PROTOCOL_ERROR_INT1_EN_BITS 0x40 // ICM42670_REG_INT_SOURCE1<6> +#define ICM42670_I3C_PROTOCOL_ERROR_INT1_EN_SHIFT 6 // ICM42670_REG_INT_SOURCE1<6> +#define ICM42670_SMD_INT1_EN_BITS 0x08 // ICM42670_REG_INT_SOURCE1<3> +#define ICM42670_SMD_INT1_EN_SHIFT 3 // ICM42670_REG_INT_SOURCE1<3> +#define ICM42670_WOM_Z_INT1_EN_BITS 0x04 // ICM42670_REG_INT_SOURCE1<2> +#define ICM42670_WOM_Z_INT1_EN_SHIFT 2 // ICM42670_REG_INT_SOURCE1<2> +#define ICM42670_WOM_Y_INT1_EN_BITS 0x02 // ICM42670_REG_INT_SOURCE1<1> +#define ICM42670_WOM_Y_INT1_EN_SHIFT 1 // ICM42670_REG_INT_SOURCE1<1> +#define ICM42670_WOM_X_INT1_EN_BITS 0x01 // ICM42670_REG_INT_SOURCE1<0> +#define ICM42670_WOM_X_INT1_EN_SHIFT 0 // ICM42670_REG_INT_SOURCE1<0> + +// ICM42670_REG_INT_SOURCE3 and ICM42670_REG_INT_SOURCE4 same as 0 and 1 + +#define ICM42670_DMP_IDLE_BITS 0x04 // ICM42670_REG_APEX_DATA3<2> +#define ICM42670_DMP_IDLE_SHIFT 2 // ICM42670_REG_APEX_DATA3<2> +#define ICM42670_ACTIVITY_CLASS_BITS 0x03 // ICM42670_REG_APEX_DATA3<1:0> +#define ICM42670_ACTIVITY_CLASS_SHIFT 0 // ICM42670_REG_APEX_DATA3<1:0> + +#define ICM42670_FIFO_COUNT_FORMAT_BITS 0x40 // ICM42670_REG_INTF_CONFIG0<6> +#define ICM42670_FIFO_COUNT_FORMAT_SHIFT 6 // ICM42670_REG_INTF_CONFIG0<6> +#define ICM42670_FIFO_COUNT_ENDIAN_BITS 0x20 // ICM42670_REG_INTF_CONFIG0<5> +#define ICM42670_FIFO_COUNT_ENDIAN_SHIFT 5 // ICM42670_REG_INTF_CONFIG0<5> +#define ICM42670_SENSOR_DATA_ENDIAN_BITS 0x10 // ICM42670_REG_INTF_CONFIG0<4> +#define ICM42670_SENSOR_DATA_ENDIAN_SHIFT 4 // ICM42670_REG_INTF_CONFIG0<4> + +#define ICM42670_I3C_SDR_EN_BITS 0x08 // ICM42670_REG_INTF_CONFIG1<3> +#define ICM42670_I3C_SDR_EN_SHIFT 3 // ICM42670_REG_INTF_CONFIG1<3> +#define ICM42670_I3C_DDR_EN_BITS 0x04 // ICM42670_REG_INTF_CONFIG1<2> +#define ICM42670_I3C_DDR_EN_SHIFT 2 // ICM42670_REG_INTF_CONFIG1<2> +#define ICM42670_CLKSEL_BITS 0x03 // ICM42670_REG_INTF_CONFIG1<1:0> +#define ICM42670_CLKSEL_SHIFT 0 // ICM42670_REG_INTF_CONFIG1<1:0> + +#define ICM42670_DATA_RDY_INT_BITS 0x01 // ICM42670_REG_INT_STATUS_DRDY<0> +#define ICM42670_DATA_RDY_INT_SHIFT 0 // ICM42670_REG_INT_STATUS_DRDY<0> + +#define ICM42670_ST_INT_BITS 0x80 // ICM42670_REG_INT_STATUS<7> +#define ICM42670_ST_INT_SHIFT 7 // ICM42670_REG_INT_STATUS<7> +#define ICM42670_FSYNC_INT_BITS 0x40 // ICM42670_REG_INT_STATUS<6> +#define ICM42670_FSYNC_INT_SHIFT 6 // ICM42670_REG_INT_STATUS<6> +#define ICM42670_PLL_RDY_INT_BITS 0x20 // ICM42670_REG_INT_STATUS<5> +#define ICM42670_PLL_RDY_INT_SHIFT 5 // ICM42670_REG_INT_STATUS<5> +#define ICM42670_RESET_DONE_INT_BITS 0x10 // ICM42670_REG_INT_STATUS<4> +#define ICM42670_RESET_DONE_INT_SHIFT 4 // ICM42670_REG_INT_STATUS<4> +#define ICM42670_FIFO_THS_INT_BITS 0x04 // ICM42670_REG_INT_STATUS<2> +#define ICM42670_FIFO_THS_INT_SHIFT 2 // ICM42670_REG_INT_STATUS<2> +#define ICM42670_FIFO_FULL_INT_BITS 0x02 // ICM42670_REG_INT_STATUS<1> +#define ICM42670_FIFO_FULL_INT_SHIFT 1 // ICM42670_REG_INT_STATUS<1> +#define ICM42670_AGC_RDY_INT_BITS 0x01 // ICM42670_REG_INT_STATUS<0> +#define ICM42670_AGC_RDY_INT_SHIFT 0 // ICM42670_REG_INT_STATUS<0> + +#define ICM42670_SMD_INT_BITS 0x08 // ICM42670_REG_INT_STATUS2<3> +#define ICM42670_SMD_INT_SHIFT 3 // ICM42670_REG_INT_STATUS2<3> +#define ICM42670_WOM_X_INT_BITS 0x04 // ICM42670_REG_INT_STATUS2<2> +#define ICM42670_WOM_X_INT_SHIFT 2 // ICM42670_REG_INT_STATUS2<2> +#define ICM42670_WOM_Y_INT_BITS 0x02 // ICM42670_REG_INT_STATUS2<1> +#define ICM42670_WOM_Y_INT_SHIFT 1 // ICM42670_REG_INT_STATUS2<1> +#define ICM42670_WOM_Z_INT_BITS 0x01 // ICM42670_REG_INT_STATUS2<0> +#define ICM42670_WOM_Z_INT_SHIFT 0 // ICM42670_REG_INT_STATUS2<0> + +#define ICM42670_STEP_DET_INT_BITS 0x20 // ICM42670_REG_INT_STATUS3<5> +#define ICM42670_STEP_DET_INT_SHIFT 5 // ICM42670_REG_INT_STATUS3<5> +#define ICM42670_STEP_CNT_OVF_INT_BITS 0x10 // ICM42670_REG_INT_STATUS3<4> +#define ICM42670_STEP_CNT_OVF_INT_SHIFT 4 // ICM42670_REG_INT_STATUS3<4> +#define ICM42670_TILT_DET_INT_BITS 0x08 // ICM42670_REG_INT_STATUS3<3> +#define ICM42670_TILT_DET_INT_SHIFT 3 // ICM42670_REG_INT_STATUS3<3> +#define ICM42670_FF_DET_INT_BITS 0x04 // ICM42670_REG_INT_STATUS3<2> +#define ICM42670_FF_DET_INT_SHIFT 2 // ICM42670_REG_INT_STATUS3<2> +#define ICM42670_LOWG_DET_INT_BITS 0x02 // ICM42670_REG_INT_STATUS3<1> +#define ICM42670_LOWG_DET_INT_SHIFT 1 // ICM42670_REG_INT_STATUS3<1> + +#define CHECK(x) \ + do \ + { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) \ + return __; \ + } \ + while (0) +#define CHECK_ARG(VAL) \ + do \ + { \ + if (!(VAL)) \ + return ESP_ERR_INVALID_ARG; \ + } \ + while (0) + +static inline esp_err_t write_register(icm42670_t *dev, uint8_t reg, uint8_t value) +{ + CHECK_ARG(dev); + + return i2c_dev_write_reg(&dev->i2c_dev, reg, &value, 1); +} + +static inline esp_err_t read_register(icm42670_t *dev, uint8_t reg, uint8_t *value) +{ + CHECK_ARG(dev && value); + + return i2c_dev_read_reg(&dev->i2c_dev, reg, value, 1); +} + +static inline esp_err_t read_register_16(icm42670_t *dev, uint8_t upper_byte_reg, int16_t *value) +{ + CHECK_ARG(dev && value); + + esp_err_t err; + uint8_t reg_0, reg_1; + err = read_register(dev, upper_byte_reg, ®_1); + err = read_register(dev, upper_byte_reg + 1, ®_0); + *value = reg_0 | (reg_1 << 8); + + return err; +} + +static inline esp_err_t manipulate_register(icm42670_t *dev, uint8_t reg_addr, uint8_t mask, uint8_t shift, + uint8_t value) +{ + CHECK_ARG(dev); + + uint8_t reg; + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, read_register(dev, reg_addr, ®)); + reg = (reg & ~mask) | (value << shift); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, reg_addr, reg)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +static inline esp_err_t read_mreg_register(icm42670_t *dev, icm42670_mreg_number_t mreg_num, uint8_t reg, + uint8_t *value) +{ + CHECK_ARG(dev && value); + + bool mclk_rdy; + CHECK(icm42670_get_mclk_rdy(dev, &mclk_rdy)); + if (!mclk_rdy) + { + ESP_LOGE(TAG, "MCLK not running, required to access MREG"); + return ESP_ERR_INVALID_RESPONSE; + } + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_BLK_SEL_R, mreg_num)); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_MADDR_R, reg)); + ets_delay_us(10); // Wait for 10us until MREG write is complete + I2C_DEV_CHECK(&dev->i2c_dev, read_register(dev, ICM42670_REG_M_R, value)); + ets_delay_us(10); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +static inline esp_err_t write_mreg_register(icm42670_t *dev, icm42670_mreg_number_t mreg_num, uint8_t reg, + uint8_t value) +{ + CHECK_ARG(dev); + + bool mclk_rdy; + CHECK(icm42670_get_mclk_rdy(dev, &mclk_rdy)); + if (!mclk_rdy) + { + ESP_LOGE(TAG, "MCLK not running, required to access MREG"); + return ESP_ERR_INVALID_RESPONSE; + } + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_BLK_SEL_W, mreg_num)); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_MADDR_W, reg)); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_M_W, value)); + ets_delay_us(10); // Wait for 10us until MREG write is complete + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +static inline esp_err_t manipulate_mreg_register(icm42670_t *dev, icm42670_mreg_number_t mreg_num, uint8_t reg_addr, + uint8_t mask, uint8_t shift, uint8_t value) +{ + CHECK_ARG(dev); + + uint8_t reg; + CHECK(read_mreg_register(dev, mreg_num, reg_addr, ®)); + reg = (reg & ~mask) | (value << shift); + CHECK(write_mreg_register(dev, mreg_num, reg_addr, reg)); + + return ESP_OK; +} +/////////////////////////////////////////////////////////////////////////////// + +esp_err_t icm42670_init_desc(icm42670_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + if (addr != ICM42670_I2C_ADDR_GND && addr != ICM42670_I2C_ADDR_VCC) + { + ESP_LOGE(TAG, "Invalid I2C address `0x%x`: must be one of 0x%x, 0x%x", addr, ICM42670_I2C_ADDR_GND, + ICM42670_I2C_ADDR_VCC); + return ESP_ERR_INVALID_ARG; + } + + dev->i2c_dev.port = port; + dev->i2c_dev.addr = addr; + dev->i2c_dev.cfg.sda_io_num = sda_gpio; + dev->i2c_dev.cfg.scl_io_num = scl_gpio; + dev->i2c_dev.timeout_ticks = 0; // set to default +#if HELPER_TARGET_IS_ESP32 + dev->i2c_dev.cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + return i2c_dev_create_mutex(&dev->i2c_dev); +} + +esp_err_t icm42670_free_desc(icm42670_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(&dev->i2c_dev); +} + +esp_err_t icm42670_init(icm42670_t *dev) +{ + CHECK_ARG(dev); + uint8_t reg; + + // check who_am_i register + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, read_register(dev, ICM42670_REG_WHO_AM_I, ®)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + if (reg != 0x67) + { + ESP_LOGE(TAG, "Error initializing ICM42670, who_am_i register did not return 0x67"); + return ESP_ERR_INVALID_RESPONSE; + } + ESP_LOGD(TAG, "Init: Chip ICM42670 detected"); + + // flush FIFO + CHECK(icm42670_flush_fifo(dev)); + // perform signal path reset + CHECK(icm42670_reset(dev)); + ESP_LOGD(TAG, "Init: Soft-Reset performed"); + + // wait 10ms + vTaskDelay(pdMS_TO_TICKS(10)); + + // set device in IDLE power state + CHECK(icm42670_set_idle_pwr_mode(dev, true)); + + // wait 10ms + vTaskDelay(pdMS_TO_TICKS(10)); + + // check if internal clock is running + bool mclk_rdy = false; + CHECK(icm42670_get_mclk_rdy(dev, &mclk_rdy)); + if (!mclk_rdy) + { + ESP_LOGE(TAG, "Error initializing icm42670, Internal clock not running"); + return ESP_ERR_INVALID_RESPONSE; + } + ESP_LOGD(TAG, "Init: Internal clock running"); + + return ESP_OK; +} + +esp_err_t icm42670_set_idle_pwr_mode(icm42670_t *dev, bool enable_idle) +{ + CHECK_ARG(dev); + + CHECK(manipulate_register(dev, ICM42670_REG_PWR_MGMT0, ICM42670_IDLE_BITS, ICM42670_IDLE_SHIFT, + (uint8_t)enable_idle)); + + return ESP_OK; +} + +esp_err_t icm42670_set_gyro_pwr_mode(icm42670_t *dev, icm42670_gyro_pwr_mode_t pwr_mode) +{ + CHECK_ARG(dev); + + CHECK(manipulate_register(dev, ICM42670_REG_PWR_MGMT0, ICM42670_GYRO_MODE_BITS, ICM42670_GYRO_MODE_SHIFT, pwr_mode)); + // no register writes should be performed within the next 200us + ets_delay_us(300); + + return ESP_OK; +} + +esp_err_t icm42670_set_accel_pwr_mode(icm42670_t *dev, icm42670_accel_pwr_mode_t pwr_mode) +{ + CHECK_ARG(dev); + + // certain odr and avg settings are not allowed in LP or LN mode + icm42670_accel_odr_t odr; + icm42670_accel_avg_t avg; + CHECK(icm42670_get_accel_odr(dev, &odr)); + CHECK(icm42670_get_accel_avg(dev, &avg)); + + if ((pwr_mode == ICM42670_ACCEL_ENABLE_LP_MODE) + && ((odr == ICM42670_ACCEL_ODR_800HZ) || (odr == ICM42670_ACCEL_ODR_1_6KHZ) + || ((odr == ICM42670_ACCEL_ODR_200HZ) && (avg == ICM42670_ACCEL_AVG_64X)))) + { + ESP_LOGE(TAG, "Accel ODR and AVG settings invalid for Low-power mode"); + return ESP_ERR_INVALID_ARG; + } + + if ((pwr_mode == ICM42670_ACCEL_ENABLE_LN_MODE) + && ((odr == ICM42670_ACCEL_ODR_6_25HZ) || (odr == ICM42670_ACCEL_ODR_3_125HZ) + || (odr == ICM42670_ACCEL_ODR_1_5625HZ))) + { + ESP_LOGE(TAG, "Accel ODR settings invalid for Low-noise mode"); + return ESP_ERR_INVALID_ARG; + } + + CHECK(manipulate_register(dev, ICM42670_REG_PWR_MGMT0, ICM42670_ACCEL_MODE_BITS, ICM42670_ACCEL_MODE_SHIFT, + pwr_mode)); + + return ESP_OK; +} + +esp_err_t icm42670_set_low_power_clock(icm42670_t *dev, icm42670_lp_clock_source_t clock_source) +{ + CHECK_ARG(dev); + + CHECK(manipulate_register(dev, ICM42670_REG_PWR_MGMT0, ICM42670_ACCEL_LP_CLK_SEL_BITS, + ICM42670_ACCEL_LP_CLK_SEL_SHIFT, clock_source)); + + return ESP_OK; +} + +esp_err_t icm42670_read_raw_data(icm42670_t *dev, uint8_t data_register, int16_t *data) +{ + CHECK_ARG(dev && data); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, read_register_16(dev, data_register, data)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +esp_err_t icm42670_read_temperature(icm42670_t *dev, float *temperature) +{ + CHECK_ARG(dev && temperature); + + int16_t reg; + CHECK(icm42670_read_raw_data(dev, ICM42670_REG_TEMP_DATA1, ®)); + *temperature = (reg / 128.0) + 25; + + return ESP_OK; +} + +esp_err_t icm42670_reset(icm42670_t *dev) +{ + CHECK_ARG(dev); + + uint8_t reg = 1 << ICM42670_SOFT_RESET_DEVICE_CONFIG_SHIFT; + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_SIGNAL_PATH_RESET, reg)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +esp_err_t icm42670_flush_fifo(icm42670_t *dev) +{ + CHECK_ARG(dev); + + uint8_t reg = 1 << ICM42670_FIFO_FLUSH_SHIFT; + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_SIGNAL_PATH_RESET, reg)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + ets_delay_us(2); // flush is done within 1.5us + + return ESP_OK; +} + +esp_err_t icm42670_set_gyro_fsr(icm42670_t *dev, icm42670_gyro_fsr_t range) +{ + CHECK_ARG(dev); + return manipulate_register(dev, ICM42670_REG_GYRO_CONFIG0, ICM42670_GYRO_UI_FS_SEL_BITS, + ICM42670_GYRO_UI_FS_SEL_SHIFT, range); +} + +esp_err_t icm42670_set_gyro_odr(icm42670_t *dev, icm42670_gyro_odr_t odr) +{ + CHECK_ARG(dev); + return manipulate_register(dev, ICM42670_REG_GYRO_CONFIG0, ICM42670_GYRO_ODR_BITS, ICM42670_GYRO_ODR_SHIFT, odr); +} + +esp_err_t icm42670_set_accel_fsr(icm42670_t *dev, icm42670_accel_fsr_t range) +{ + CHECK_ARG(dev); + return manipulate_register(dev, ICM42670_REG_ACCEL_CONFIG0, ICM42670_ACCEL_UI_FS_SEL_BITS, + ICM42670_ACCEL_UI_FS_SEL_SHIFT, range); +} + +esp_err_t icm42670_set_accel_odr(icm42670_t *dev, icm42670_accel_odr_t odr) +{ + CHECK_ARG(dev); + return manipulate_register(dev, ICM42670_REG_ACCEL_CONFIG0, ICM42670_ACCEL_ODR_BITS, ICM42670_ACCEL_ODR_SHIFT, odr); +} + +esp_err_t icm42670_set_temp_lpf(icm42670_t *dev, icm42670_temp_lfp_t lpf_bw) +{ + CHECK_ARG(dev); + return manipulate_register(dev, ICM42670_REG_TEMP_CONFIG0, ICM42670_TEMP_FILT_BW_BITS, ICM42670_TEMP_FILT_BW_SHIFT, + lpf_bw); +} + +esp_err_t icm42670_set_gyro_lpf(icm42670_t *dev, icm42670_gyro_lfp_t lpf_bw) +{ + CHECK_ARG(dev); + return manipulate_register(dev, ICM42670_REG_GYRO_CONFIG1, ICM42670_GYRO_UI_FILT_BW_BITS, + ICM42670_GYRO_UI_FILT_BW_SHIFT, lpf_bw); +} + +esp_err_t icm42670_set_accel_lpf(icm42670_t *dev, icm42670_accel_lfp_t lpf_bw) +{ + CHECK_ARG(dev); + return manipulate_register(dev, ICM42670_REG_ACCEL_CONFIG1, ICM42670_ACCEL_UI_FILT_BW_BITS, + ICM42670_ACCEL_UI_FILT_BW_SHIFT, lpf_bw); +} + +esp_err_t icm42670_set_accel_avg(icm42670_t *dev, icm42670_accel_avg_t avg) +{ + CHECK_ARG(dev); + return manipulate_register(dev, ICM42670_REG_ACCEL_CONFIG1, ICM42670_ACCEL_UI_AVG_BITS, ICM42670_ACCEL_UI_AVG_SHIFT, + avg); +} + +esp_err_t icm42670_config_int_pin(icm42670_t *dev, uint8_t int_pin, icm42670_int_config_t config) +{ + CHECK_ARG(dev && int_pin < 3 && int_pin > 0); + + uint8_t reg = config.mode << 2 | config.drive << 1 | config.polarity; + if (int_pin == 2) + { + return manipulate_register(dev, ICM42670_REG_INT_CONFIG, 0b00111000, ICM42670_INT2_POLARITY_SHIFT, reg); + } + else + { + return manipulate_register(dev, ICM42670_REG_INT_CONFIG, 0b00000111, ICM42670_INT1_POLARITY_SHIFT, reg); + } +} + +esp_err_t icm42670_set_int_sources(icm42670_t *dev, uint8_t int_pin, icm42670_int_source_t sources) +{ + CHECK_ARG(dev && int_pin < 3 && int_pin > 0); + + uint8_t reg1 = 0, reg2 = 0; + if (sources.self_test_done) + reg1 = reg1 | (1 << ICM42670_ST_INT1_EN_SHIFT); + if (sources.fsync) + reg1 = reg1 | (1 << ICM42670_FSYNC_INT1_EN_SHIFT); + if (sources.pll_ready) + reg1 = reg1 | (1 << ICM42670_PLL_RDY_INT1_EN_SHIFT); + if (sources.reset_done) + reg1 = reg1 | (1 << ICM42670_RESET_DONE_INT1_EN_SHIFT); + if (sources.data_ready) + reg1 = reg1 | (1 << ICM42670_DRDY_INT1_EN_SHIFT); + if (sources.fifo_threshold) + reg1 = reg1 | (1 << ICM42670_FIFO_THS_INT1_EN_SHIFT); + if (sources.fifo_full) + reg1 = reg1 | (1 << ICM42670_FIFO_FULL_INT1_EN_SHIFT); + if (sources.agc_ready) + reg1 = reg1 | (1 << ICM42670_AGC_RDY_INT1_EN_SHIFT); + if (sources.i3c_error) + reg2 = reg2 | (1 << ICM42670_I3C_PROTOCOL_ERROR_INT1_EN_SHIFT); + if (sources.smd) + reg2 = reg2 | (1 << ICM42670_SMD_INT1_EN_SHIFT); + if (sources.wom_z) + reg2 = reg2 | (1 << ICM42670_WOM_Z_INT1_EN_SHIFT); + if (sources.wom_y) + reg2 = reg2 | (1 << ICM42670_WOM_Y_INT1_EN_SHIFT); + if (sources.wom_x) + reg2 = reg2 | (1 << ICM42670_WOM_X_INT1_EN_SHIFT); + + if (int_pin == 1) + { + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_INT_SOURCE0, reg1)); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_INT_SOURCE1, reg2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + } + else + { + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_INT_SOURCE3, reg1)); + I2C_DEV_CHECK(&dev->i2c_dev, write_register(dev, ICM42670_REG_INT_SOURCE4, reg2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + } + + return ESP_OK; +} + +esp_err_t icm42670_config_wom(icm42670_t *dev, icm42670_wom_config_t config) +{ + CHECK_ARG(dev); + + CHECK(manipulate_register(dev, ICM42670_REG_WOM_CONFIG, ICM42670_WOM_INT_DUR_BITS, ICM42670_WOM_INT_DUR_SHIFT, + config.trigger)); + CHECK(manipulate_register(dev, ICM42670_REG_WOM_CONFIG, ICM42670_WOM_INT_MODE_BITS, ICM42670_WOM_INT_MODE_SHIFT, + config.logical_mode)); + CHECK(manipulate_register(dev, ICM42670_REG_WOM_CONFIG, ICM42670_WOM_MODE_BITS, ICM42670_WOM_MODE_SHIFT, + config.reference)); + + // WoM threshold values + CHECK(write_mreg_register(dev, ICM42670_MREG1_RW, ICM42670_REG_ACCEL_WOM_X_THR, config.wom_x_threshold)); + CHECK(write_mreg_register(dev, ICM42670_MREG1_RW, ICM42670_REG_ACCEL_WOM_Y_THR, config.wom_y_threshold)); + CHECK(write_mreg_register(dev, ICM42670_MREG1_RW, ICM42670_REG_ACCEL_WOM_Z_THR, config.wom_z_threshold)); + + return ESP_OK; +} + +esp_err_t icm42670_enable_wom(icm42670_t *dev, bool enable) +{ + CHECK_ARG(dev); + return manipulate_register(dev, ICM42670_REG_WOM_CONFIG, ICM42670_WOM_EN_BITS, ICM42670_WOM_EN_SHIFT, enable); +} + +esp_err_t icm42670_get_mclk_rdy(icm42670_t *dev, bool *mclk_rdy) +{ + CHECK_ARG(dev && mclk_rdy); + + uint8_t reg; + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, read_register(dev, ICM42670_REG_MCLK_RDY, ®)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + if ((reg & ICM42670_MCLK_RDY_BITS) >> ICM42670_MCLK_RDY_SHIFT) + *mclk_rdy = true; + else + *mclk_rdy = false; + + return ESP_OK; +} + +esp_err_t icm42670_get_accel_odr(icm42670_t *dev, icm42670_accel_odr_t *odr) +{ + CHECK_ARG(dev && odr); + + uint8_t reg; + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, read_register(dev, ICM42670_REG_ACCEL_CONFIG0, ®)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + *odr = (reg & ICM42670_ACCEL_ODR_BITS) >> ICM42670_ACCEL_ODR_SHIFT; + + return ESP_OK; +} + +esp_err_t icm42670_get_accel_avg(icm42670_t *dev, icm42670_accel_avg_t *avg) +{ + CHECK_ARG(dev && avg); + + uint8_t reg; + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, read_register(dev, ICM42670_REG_ACCEL_CONFIG1, ®)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + *avg = (reg & ICM42670_ACCEL_UI_AVG_BITS) >> ICM42670_ACCEL_UI_AVG_SHIFT; + + return ESP_OK; +} \ No newline at end of file diff --git a/components/icm42670/icm42670.h b/components/icm42670/icm42670.h new file mode 100644 index 00000000..39fdbac3 --- /dev/null +++ b/components/icm42670/icm42670.h @@ -0,0 +1,606 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2022 Jan Veeh + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file icm42670.h + * @defgroup icm42670 icm42670 + * @{ + * + * ESP-IDF driver for TDK ICM-42670-P IMU (found on ESP-RS board) + * + * Copyright (c) 2022 Jan Veeh (jan.veeh@motius.de) + * + * ISC Licensed as described in the file LICENSE + */ + +#ifndef __ICM42670_H__ +#define __ICM42670_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICM42670_I2C_ADDR_GND 0x68 +#define ICM42670_I2C_ADDR_VCC 0x69 + +// Registers USER BANK 0 +#define ICM42670_REG_MCLK_RDY 0x00 +#define ICM42670_REG_DEVICE_CONFIG 0x01 +#define ICM42670_REG_SIGNAL_PATH_RESET 0x02 +#define ICM42670_REG_DRIVE_CONFIG1 0x03 +#define ICM42670_REG_DRIVE_CONFIG2 0x04 +#define ICM42670_REG_DRIVE_CONFIG3 0x05 +#define ICM42670_REG_INT_CONFIG 0x06 +#define ICM42670_REG_TEMP_DATA1 0x09 +#define ICM42670_REG_TEMP_DATA0 0x0A +#define ICM42670_REG_ACCEL_DATA_X1 0x0B +#define ICM42670_REG_ACCEL_DATA_X0 0x0C +#define ICM42670_REG_ACCEL_DATA_Y1 0x0D +#define ICM42670_REG_ACCEL_DATA_Y0 0x0E +#define ICM42670_REG_ACCEL_DATA_Z1 0x0F +#define ICM42670_REG_ACCEL_DATA_Z0 0x10 +#define ICM42670_REG_GYRO_DATA_X1 0x11 +#define ICM42670_REG_GYRO_DATA_X0 0x12 +#define ICM42670_REG_GYRO_DATA_Y1 0x13 +#define ICM42670_REG_GYRO_DATA_Y0 0x14 +#define ICM42670_REG_GYRO_DATA_Z1 0x15 +#define ICM42670_REG_GYRO_DATA_Z0 0x16 +#define ICM42670_REG_TMST_FSYNCH 0x17 +#define ICM42670_REG_TMST_FSYNCL 0x18 +#define ICM42670_REG_APEX_DATA4 0x1D +#define ICM42670_REG_APEX_DATA5 0x1E +#define ICM42670_REG_PWR_MGMT0 0x1F +#define ICM42670_REG_GYRO_CONFIG0 0x20 +#define ICM42670_REG_ACCEL_CONFIG0 0x21 +#define ICM42670_REG_TEMP_CONFIG0 0x22 +#define ICM42670_REG_GYRO_CONFIG1 0x23 +#define ICM42670_REG_ACCEL_CONFIG1 0x24 +#define ICM42670_REG_APEX_CONFIG0 0x25 +#define ICM42670_REG_APEX_CONFIG1 0x26 +#define ICM42670_REG_WOM_CONFIG 0x27 +#define ICM42670_REG_FIFO_CONFIG1 0x28 +#define ICM42670_REG_FIFO_CONFIG2 0x29 +#define ICM42670_REG_FIFO_CONFIG3 0x2A +#define ICM42670_REG_INT_SOURCE0 0x2B +#define ICM42670_REG_INT_SOURCE1 0x2C +#define ICM42670_REG_INT_SOURCE3 0x2D +#define ICM42670_REG_INT_SOURCE4 0x2E +#define ICM42670_REG_FIFO_LOST_PKT0 0x2F +#define ICM42670_REG_FIFO_LOST_PKT1 0x30 +#define ICM42670_REG_APEX_DATA0 0x31 +#define ICM42670_REG_APEX_DATA1 0x32 +#define ICM42670_REG_APEX_DATA2 0x33 +#define ICM42670_REG_APEX_DATA3 0x34 +#define ICM42670_REG_INTF_CONFIG0 0x35 +#define ICM42670_REG_INTF_CONFIG1 0x36 +#define ICM42670_REG_INT_STATUS_DRDY 0x39 +#define ICM42670_REG_INT_STATUS 0x3A +#define ICM42670_REG_INT_STATUS2 0x3B +#define ICM42670_REG_INT_STATUS3 0x3C +#define ICM42670_REG_FIFO_COUNTH 0x3D +#define ICM42670_REG_FIFO_COUNTL 0x3E +#define ICM42670_REG_FIFO_DATA 0x3F +#define ICM42670_REG_WHO_AM_I 0x75 +#define ICM42670_REG_BLK_SEL_W 0x79 +#define ICM42670_REG_MADDR_W 0x7A +#define ICM42670_REG_M_W 0x7B +#define ICM42670_REG_BLK_SEL_R 0x7C +#define ICM42670_REG_MADDR_R 0x7D +#define ICM42670_REG_M_R 0x7E + +// MREG1 registers +#define ICM42670_REG_TMST_CONFIG1 0x00 +#define ICM42670_REG_FIFO_CONFIG5 0x01 +#define ICM42670_REG_FIFO_CONFIG6 0x02 +#define ICM42670_REG_FSYNC_CONFIG 0x03 +#define ICM42670_REG_INT_CONFIG0 0x04 +#define ICM42670_REG_INT_CONFIG1 0x05 +#define ICM42670_REG_SENSOR_CONFIG3 0x06 +#define ICM42670_REG_ST_CONFIG 0x13 +#define ICM42670_REG_SELFTEST 0x14 +#define ICM42670_REG_INTF_CONFIG6 0x23 +#define ICM42670_REG_INTF_CONFIG10 0x25 +#define ICM42670_REG_INTF_CONFIG7 0x28 +#define ICM42670_REG_OTP_CONFIG 0x2B +#define ICM42670_REG_INT_SOURCE6 0x2F +#define ICM42670_REG_INT_SOURCE7 0x30 +#define ICM42670_REG_INT_SOURCE8 0x31 +#define ICM42670_REG_INT_SOURCE9 0x32 +#define ICM42670_REG_INT_SOURCE10 0x33 +#define ICM42670_REG_APEX_CONFIG2 0x44 +#define ICM42670_REG_APEX_CONFIG3 0x45 +#define ICM42670_REG_APEX_CONFIG4 0x46 +#define ICM42670_REG_APEX_CONFIG5 0x47 +#define ICM42670_REG_APEX_CONFIG9 0x48 +#define ICM42670_REG_APEX_CONFIG10 0x49 +#define ICM42670_REG_APEX_CONFIG11 0x4A +#define ICM42670_REG_ACCEL_WOM_X_THR 0x4B +#define ICM42670_REG_ACCEL_WOM_Y_THR 0x4C +#define ICM42670_REG_ACCEL_WOM_Z_THR 0x4D +#define ICM42670_REG_OFFSET_USER0 0x4E +#define ICM42670_REG_OFFSET_USER1 0x4F +#define ICM42670_REG_OFFSET_USER2 0x50 +#define ICM42670_REG_OFFSET_USER3 0x51 +#define ICM42670_REG_OFFSET_USER4 0x52 +#define ICM42670_REG_OFFSET_USER5 0x53 +#define ICM42670_REG_OFFSET_USER6 0x54 +#define ICM42670_REG_OFFSET_USER7 0x55 +#define ICM42670_REG_OFFSET_USER8 0x56 +#define ICM42670_REG_ST_STATUS1 0x63 +#define ICM42670_REG_ST_STATUS2 0x64 +#define ICM42670_REG_FDR_CONFIG 0x66 +#define ICM42670_REG_APEX_CONFIG12 0x67 + +// MREG2 registers +#define ICM42670_REG_OTP_CTRL7 0x06 + +// MREG3 registers +#define ICM42670_REG_XA_ST_DATA 0x00 +#define ICM42670_REG_YA_ST_DATA 0x01 +#define ICM42670_REG_ZA_ST_DATA 0x02 +#define ICM42670_REG_XG_ST_DATA 0x03 +#define ICM42670_REG_YG_ST_DATA 0x04 +#define ICM42670_REG_ZG_ST_DATA 0x05 + +/* Gyro power mode */ +typedef enum { + ICM42670_GYRO_DISABLE = 0b00, + ICM42670_GYRO_STANDBY = 0b01, + ICM42670_GYRO_ENABLE_LN_MODE = 0b11 +} icm42670_gyro_pwr_mode_t; + +/* Accelerometer power mode */ +typedef enum { + ICM42670_ACCEL_DISABLE = 0b00, + ICM42670_ACCEL_ENABLE_LP_MODE = 0b10, + ICM42670_ACCEL_ENABLE_LN_MODE = 0b11 +} icm42670_accel_pwr_mode_t; + +/* Accelerometer low power mode clock source */ +typedef enum { + ICM42670_LP_CLK_WUO = 0, + ICM42670_LP_CLK_RCO = 1 +} icm42670_lp_clock_source_t; + +/* Gyro FSR (full scale range) */ +typedef enum { + ICM42670_GYRO_RANGE_2000DPS = 0b00, + ICM42670_GYRO_RANGE_1000DPS = 0b01, + ICM42670_GYRO_RANGE_500DPS = 0b10, + ICM42670_GYRO_RANGE_250DPS = 0b11 +} icm42670_gyro_fsr_t; + +/* Gyro ODR (output data rate) */ +typedef enum { + ICM42670_GYRO_ODR_12_5HZ = 0b1100, + ICM42670_GYRO_ODR_25HZ = 0b1011, + ICM42670_GYRO_ODR_50HZ = 0b1010, + ICM42670_GYRO_ODR_100HZ = 0b1001, + ICM42670_GYRO_ODR_200HZ = 0b1000, + ICM42670_GYRO_ODR_400HZ = 0b0111, + ICM42670_GYRO_ODR_800HZ = 0b0110, + ICM42670_GYRO_ODR_1_6KHZ = 0b0101 +} icm42670_gyro_odr_t; + +/* Accelerometer FSR (full scale range) */ +typedef enum { + ICM42670_ACCEL_RANGE_16G = 0b00, + ICM42670_ACCEL_RANGE_8G = 0b01, + ICM42670_ACCEL_RANGE_4G = 0b10, + ICM42670_ACCEL_RANGE_2G = 0b11 +} icm42670_accel_fsr_t; + +/* Accelerometer ODR (output data rate) */ +typedef enum { + ICM42670_ACCEL_ODR_1_5625HZ = 0b1111, + ICM42670_ACCEL_ODR_3_125HZ = 0b1110, + ICM42670_ACCEL_ODR_6_25HZ = 0b1101, + ICM42670_ACCEL_ODR_12_5HZ = 0b1100, + ICM42670_ACCEL_ODR_25HZ = 0b1011, + ICM42670_ACCEL_ODR_50HZ = 0b1010, + ICM42670_ACCEL_ODR_100HZ = 0b1001, + ICM42670_ACCEL_ODR_200HZ = 0b1000, + ICM42670_ACCEL_ODR_400HZ = 0b0111, + ICM42670_ACCEL_ODR_800HZ = 0b0110, + ICM42670_ACCEL_ODR_1_6KHZ = 0b0101 +} icm42670_accel_odr_t; + +/* Temperature LPF (low pass filter) */ +typedef enum { + ICM42670_TEMP_LFP_BYPASSED = 0b000, + ICM42670_TEMP_LFP_180HZ = 0b001, + ICM42670_TEMP_LFP_72HZ = 0b010, + ICM42670_TEMP_LFP_34HZ = 0b011, + ICM42670_TEMP_LFP_16HZ = 0b100, + ICM42670_TEMP_LFP_8HZ = 0b101, + ICM42670_TEMP_LFP_4HZ = 0b110 +} icm42670_temp_lfp_t; + +/* Gyro LPF (low pass filter) */ +typedef enum { + ICM42670_GYRO_LFP_BYPASSED = 0b000, + ICM42670_GYRO_LFP_180HZ = 0b001, + ICM42670_GYRO_LFP_121HZ = 0b010, + ICM42670_GYRO_LFP_73HZ = 0b011, + ICM42670_GYRO_LFP_53HZ = 0b100, + ICM42670_GYRO_LFP_34HZ = 0b101, + ICM42670_GYRO_LFP_25HZ = 0b110, + ICM42670_GYRO_LFP_16HZ = 0b111 +} icm42670_gyro_lfp_t; + +/* Accelerometer LPF (low pass filter) */ +typedef enum { + ICM42670_ACCEL_LFP_BYPASSED = 0b000, + ICM42670_ACCEL_LFP_180HZ = 0b001, + ICM42670_ACCEL_LFP_121HZ = 0b010, + ICM42670_ACCEL_LFP_73HZ = 0b011, + ICM42670_ACCEL_LFP_53HZ = 0b100, + ICM42670_ACCEL_LFP_34HZ = 0b101, + ICM42670_ACCEL_LFP_25HZ = 0b110, + ICM42670_ACCEL_LFP_16HZ = 0b111 +} icm42670_accel_lfp_t; + +/* Accelerometer averaging (for low power mode) */ +typedef enum { + ICM42670_ACCEL_AVG_2X = 0b000, + ICM42670_ACCEL_AVG_4X = 0b001, + ICM42670_ACCEL_AVG_8X = 0b010, + ICM42670_ACCEL_AVG_16X = 0b011, + ICM42670_ACCEL_AVG_32X = 0b100, + ICM42670_ACCEL_AVG_64X = 0b101 +} icm42670_accel_avg_t; + +/* Interrupt pin signal mode */ +typedef enum { + ICM42670_INT_MODE_PULSED = 0, + ICM42670_INT_MODE_LATCHED = 1 +} icm42670_int_mode_t; + +/* Interrupt pin signal type */ +typedef enum { + ICM42670_INT_DRIVE_OPEN_DRAIN = 0, + ICM42670_INT_DRIVE_PUSH_PULL = 1 +} icm42670_int_drive_t; + +/* Interrupt pin signal polarity */ +typedef enum { + ICM42670_INT_POLARITY_ACTIVE_LOW = 0, + ICM42670_INT_POLARITY_ACTIVE_HIGH = 1 +} icm42670_int_polarity_t; + +/* Interrupt pin configuration */ +typedef struct +{ + icm42670_int_mode_t mode; + icm42670_int_drive_t drive; + icm42670_int_polarity_t polarity; +} icm42670_int_config_t; + +/* Interrupt source */ +typedef struct +{ + bool self_test_done; + bool fsync; + bool pll_ready; + bool reset_done; + bool data_ready; + bool fifo_threshold; + bool fifo_full; + bool agc_ready; + bool i3c_error; + bool smd; + bool wom_z; + bool wom_y; + bool wom_x; +} icm42670_int_source_t; + +/* Wake on Motion interrupt assertion */ +typedef enum { + ICM42670_WOM_INT_DUR_FIRST = 0b00, + ICM42670_WOM_INT_DUR_SECOND = 0b01, + ICM42670_WOM_INT_DUR_THIRD = 0b10, + ICM42670_WOM_INT_DUR_FOURTH = 0b11 +} icm42670_wom_int_dur_t; + +/* Wake on Motion interrupt logical trigger */ +typedef enum { + ICM42670_WOM_INT_MODE_ALL_OR = 0, + ICM42670_WOM_INT_MODE_ALL_AND = 1 +} icm42670_wom_int_mode_t; + +/* Wake on Motion reference sample */ +typedef enum { + ICM42670_WOM_MODE_REF_INITIAL = 0, + ICM42670_WOM_MODE_REF_LAST = 1 +} icm42670_wom_mode_t; + +/* Wake on Motion configuration */ +typedef struct +{ + icm42670_wom_int_dur_t trigger; + icm42670_wom_int_mode_t logical_mode; + icm42670_wom_mode_t reference; + uint8_t wom_x_threshold; // 8-bit value between 0 and 1g (Resolution 1g/256=~3.9 mg) + uint8_t wom_y_threshold; // 8-bit value between 0 and 1g (Resolution 1g/256=~3.9 mg) + uint8_t wom_z_threshold; // 8-bit value between 0 and 1g (Resolution 1g/256=~3.9 mg) +} icm42670_wom_config_t; + +/* MREG 1-3 access */ +typedef enum { + ICM42670_MREG1_RW = 0x00, + ICM42670_MREG2_RW = 0x28, + ICM42670_MREG3_RW = 0x50 +} icm42670_mreg_number_t; + +/** + * Device descriptor + */ +typedef struct +{ + i2c_dev_t i2c_dev; + // TODO: add more vars for configuration +} icm42670_t; + +/** + * @brief Initialize device descriptor + * + * @param dev Device descriptor + * @param addr I2C device address, `ICM42670_I2C_ADDR_...` const + * @param port I2C port + * @param sda_gpio SDA GPIO pin + * @param scl_gpio SCL GPIO pin + * @return `ESP_OK` on success + */ +esp_err_t icm42670_init_desc(icm42670_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t icm42670_free_desc(icm42670_t *dev); + +/** + * @brief Initialize device + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t icm42670_init(icm42670_t *dev); + +/** + * @brief Set device power mode + * + * @param dev Device descriptor + * @param enable_idle bool to enable idle mode + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_idle_pwr_mode(icm42670_t *dev, bool enable_idle); + +/** + * @brief Set gyro power mode + * + * @param dev Device descriptor + * @param pwr_mode struct of type icm42670_gyro_pwr_mode_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_gyro_pwr_mode(icm42670_t *dev, icm42670_gyro_pwr_mode_t pwr_mode); + +/** + * @brief Set accel power mode + * + * @param dev Device descriptor + * @param pwr_mode struct of type icm42670_accel_pwr_mode_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_accel_pwr_mode(icm42670_t *dev, icm42670_accel_pwr_mode_t pwr_mode); + +/** + * @brief Set clock source in LP mode + * + * @param dev Device descriptor + * @param clock_source struct of type icm42670_lp_clock_source_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_low_power_clock(icm42670_t *dev, icm42670_lp_clock_source_t clock_source); + +/** + * @brief Read temperature from device + * + * @param dev Device descriptor + * @param[out] temperature temperature, degree C + * @return `ESP_OK` on success + */ +esp_err_t icm42670_read_temperature(icm42670_t *dev, float *temperature); + +/** + * @brief Read 16-bit raw data registers (accelerometer and gyro values) + * + * @param dev Device descriptor + * @param data_register data register to read from + * @param[out] data accel or gyro data + * @return `ESP_OK` on success + */ +esp_err_t icm42670_read_raw_data(icm42670_t *dev, uint8_t data_register, int16_t *data); + +/** + * @brief Performs a soft-reset + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t icm42670_reset(icm42670_t *dev); + +/** + * @brief Wipes the FIFO + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t icm42670_flush_fifo(icm42670_t *dev); + +/** + * @brief Set the measurement FSR (Full Scale Range) of the gyro + * + * @param dev Device descriptor + * @param range struct of type icm42670_gyro_fsr_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_gyro_fsr(icm42670_t *dev, icm42670_gyro_fsr_t range); + +/** + * @brief Set the measurement ODR (Output Data Rate) of the gyro + * + * @param dev Device descriptor + * @param odr struct of type icm42670_gyro_odr_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_gyro_odr(icm42670_t *dev, icm42670_gyro_odr_t odr); + +/** + * @brief Set the measurement FSR (Full Scale Range) of the accelerometer + * + * @param dev Device descriptor + * @param range struct of type icm42670_accel_fsr_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_accel_fsr(icm42670_t *dev, icm42670_accel_fsr_t range); + +/** + * @brief Set the measurement ODR (Output Data Rate) of the accelerometer + * + * @param dev Device descriptor + * @param odr struct of type icm42670_accel_odr_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_accel_odr(icm42670_t *dev, icm42670_accel_odr_t odr); + +/** + * @brief Set the digital Low-Pass-Filter (LPF) of the temperature sensor + * + * @param dev Device descriptor + * @param lpf_bw struct of type icm42670_temp_lfp_t (bandwidth) + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_temp_lpf(icm42670_t *dev, icm42670_temp_lfp_t lpf_bw); + +/** + * @brief Set the digital Low-Pass-Filter (LPF) of the gyro + * + * @param dev Device descriptor + * @param lpf_bw struct of type icm42670_gyro_lfp_t (bandwidth) + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_gyro_lpf(icm42670_t *dev, icm42670_gyro_lfp_t lpf_bw); + +/** + * @brief Set the digital Low-Pass-Filter (LPF) of the accelerometer + * + * @param dev Device descriptor + * @param lpf_bw struct of type icm42670_accel_lfp_t (bandwidth) + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_accel_lpf(icm42670_t *dev, icm42670_accel_lfp_t lpf_bw); + +/** + * @brief Set the averaging filter of the accelerometer (ONLY IN LOW POWER MODE (LPM)) + * This field can not be changed, when accel sensor is in LPM! + * + * @param dev Device descriptor + * @param avg struct of type icm42670_accel_avg_t (averaging) + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_accel_avg(icm42670_t *dev, icm42670_accel_avg_t avg); + +/** + * @brief Configures the behaviour of an interrupt pin + * + * @param dev Device descriptor + * @param int_pin interrupt pin (1 or 2) + * @param config struct of type icm42670_int_config_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_config_int_pin(icm42670_t *dev, uint8_t int_pin, icm42670_int_config_t config); + +/** + * @brief Configures the sources for an interrupt + * + * @param dev Device descriptor + * @param int_pin interrupt pin (1 or 2) + * @param sources struct of type icm42670_int_source_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_set_int_sources(icm42670_t *dev, uint8_t int_pin, icm42670_int_source_t sources); + +/** + * @brief Configures the Wake on Motion (WoM) behaviour + * WoM can only be configured if WoM is not enabled + * + * @param dev Device descriptor + * @param config struct of type icm42670_wom_config_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_config_wom(icm42670_t *dev, icm42670_wom_config_t config); + +/** + * @brief Enable or Disable Wake on Motion (WoM) + * + * @param dev Device descriptor + * @param enable true to enable, false to disable + * @return `ESP_OK` on success + */ +esp_err_t icm42670_enable_wom(icm42670_t *dev, bool enable); + +/** + * @brief Get the status of the internal clock + * + * @param dev Device descriptor + * @param mclk_rdy true if internal clock is running + * @return `ESP_OK` on success + */ +esp_err_t icm42670_get_mclk_rdy(icm42670_t *dev, bool *mclk_rdy); + +/** + * @brief Get the output data rate (ODR) of the accel + * + * @param dev Device descriptor + * @param odr pointer to icm42670_accel_odr_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_get_accel_odr(icm42670_t *dev, icm42670_accel_odr_t *odr); + +/** + * @brief Get the status of the accel averaging + * + * @param dev Device descriptor + * @param avg pointer to icm42670_accel_avg_t + * @return `ESP_OK` on success + */ +esp_err_t icm42670_get_accel_avg(icm42670_t *dev, icm42670_accel_avg_t *avg); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif // __ICM42670_H__ diff --git a/components/ina219/.eil.yml b/components/ina219/.eil.yml index e6661747..cb1f422c 100644 --- a/components/ina219/.eil.yml +++ b/components/ina219/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: ina219 - description: Driver for INA219/INA220 bidirectional current/power monitor - group: current - groups: [] - code_owners: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2019 +name: ina219 +description: Driver for INA219/INA220 bidirectional current/power monitor +version: 1.0.0 +groups: + - current +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2019 diff --git a/components/ina219/ina219.c b/components/ina219/ina219.c index 9a432cea..cf3401d3 100644 --- a/components/ina219/ina219.c +++ b/components/ina219/ina219.c @@ -220,7 +220,7 @@ esp_err_t ina219_get_mode(ina219_t *dev, ina219_mode_t *mode) return read_conf_bits(dev, MASK_MODE, BIT_MODE, (uint16_t *)mode); } -esp_err_t ina219_calibrate(ina219_t *dev, float i_expected_max, float r_shunt) +esp_err_t ina219_calibrate(ina219_t *dev, float r_shunt) { CHECK_ARG(dev); @@ -237,7 +237,7 @@ esp_err_t ina219_calibrate(ina219_t *dev, float i_expected_max, float r_shunt) uint16_t cal = (uint16_t)((0.04096) / (dev->i_lsb * r_shunt)); - ESP_LOGD(TAG, "Calibration: %.04f A, %.04f Ohm, 0x%04x", i_expected_max, r_shunt, cal); + ESP_LOGD(TAG, "Calibration: %.04f Ohm, 0x%04x", r_shunt, cal); return write_reg_16(dev, REG_CALIBRATION, cal); } @@ -260,8 +260,8 @@ esp_err_t ina219_get_bus_voltage(ina219_t *dev, float *voltage) { CHECK_ARG(dev && voltage); - int16_t raw; - CHECK(read_reg_16(dev, REG_BUS_U, (uint16_t *)&raw)); + uint16_t raw; + CHECK(read_reg_16(dev, REG_BUS_U, &raw)); *voltage = (raw >> 3) * 0.004; diff --git a/components/ina219/ina219.h b/components/ina219/ina219.h index 55c4fe27..2651b5bf 100644 --- a/components/ina219/ina219.h +++ b/components/ina219/ina219.h @@ -232,11 +232,10 @@ esp_err_t ina219_get_mode(ina219_t *dev, ina219_mode_t *mode); * Current readings will be valid only after calibration * * @param dev Device descriptor - * @param i_expected_max Maximum expected current, A * @param r_shunt Shunt resistance, Ohm * @return `ESP_OK` on success */ -esp_err_t ina219_calibrate(ina219_t *dev, float i_expected_max, float r_shunt); +esp_err_t ina219_calibrate(ina219_t *dev, float r_shunt); /** * @brief Trigger single conversion diff --git a/components/ina260/.eil.yml b/components/ina260/.eil.yml index f8a9aa4d..4681511b 100644 --- a/components/ina260/.eil.yml +++ b/components/ina260/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: ina260 - description: Driver for INA260 precision digital current and power monitor - group: current - groups: [] - code_owners: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2020 +name: ina260 +description: Driver for INA260 precision digital current and power monitor +version: 1.0.0 +groups: + - current +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2020 diff --git a/components/ina3221/.eil.yml b/components/ina3221/.eil.yml index 2e02cf7c..a73468fb 100644 --- a/components/ina3221/.eil.yml +++ b/components/ina3221/.eil.yml @@ -1,26 +1,22 @@ ---- -components: - - name: ina3221 - description: Driver for INA3221 shunt and bus voltage monitor - group: current - groups: [] - code_owners: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: ISC - copyrights: - - author: - name: UncleRus - year: 2019 - - author: - name: Zaltora - year: 2016 +name: ina3221 +description: Driver for INA3221 shunt and bus voltage monitor +version: 1.1.0 +groups: + - current +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: Zaltora + year: 2016 + - name: UncleRus + year: 2019 diff --git a/components/l3gx/.eil.yml b/components/l3gx/.eil.yml new file mode 100644 index 00000000..4efd8250 --- /dev/null +++ b/components/l3gx/.eil.yml @@ -0,0 +1,22 @@ +name: l3gx +description: Driver for L3Gx(L3GD20/L3G4200D) 3-axis gyroscope sensors +version: 1.0.0 +groups: + - imu +code_owners: + - qb4-dev +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: qb4-dev + year: 2023 + diff --git a/components/l3gx/CMakeLists.txt b/components/l3gx/CMakeLists.txt new file mode 100644 index 00000000..ad2f7d94 --- /dev/null +++ b/components/l3gx/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS l3gx.c + INCLUDE_DIRS . + REQUIRES i2cdev log esp_idf_lib_helpers +) diff --git a/components/l3gx/LICENSE b/components/l3gx/LICENSE new file mode 100644 index 00000000..7b93da51 --- /dev/null +++ b/components/l3gx/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2023 Jakub Turek + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/l3gx/component.mk b/components/l3gx/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/l3gx/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/l3gx/l3gx.c b/components/l3gx/l3gx.c new file mode 100644 index 00000000..3cc12fa0 --- /dev/null +++ b/components/l3gx/l3gx.c @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2023 Jakub Turek + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file l3gx.c + * + * ESP-IDF Driver for L3Gx 3-axis gyroscope sensor + * + * Copyright (c) 2023 Jakub Turek + * + * BSD Licensed as described in the file LICENSE + */ + +#include "l3gx.h" + +#include +#include + +#define I2C_FREQ_HZ 400000 // 400kHz + +/* identification number */ +#define L3G4200D_WHO_AM_I_ID 0xD3 +#define L3GD20_WHO_AM_I_ID 0xD4 + +/* registers addresses */ +#define L3GX_REG_WHO_AM_I 0x0F +#define L3GX_REG_CTRL_REG1 0x20 +#define L3GX_REG_CTRL_REG2 0x21 +#define L3GX_REG_CTRL_REG3 0x22 +#define L3GX_REG_CTRL_REG4 0x23 +#define L3GX_REG_CTRL_REG5 0x24 +#define L3GX_REG_REFERENCE 0x25 +#define L3GX_REG_OUT_TEMP 0x26 +#define L3GX_REG_STATUS_REG 0x27 +#define L3GX_REG_OUT_X_L 0x28 +#define L3GX_REG_OUT_X_H 0x29 +#define L3GX_REG_OUT_Y_L 0x2A +#define L3GX_REG_OUT_Y_H 0x2B +#define L3GX_REG_OUT_Z_L 0x2C +#define L3GX_REG_OUT_Z_H 0x2D +#define L3GX_REG_FIFO_CTRL_REG 0x2E +#define L3GX_REG_FIFO_SRC_REG 0x2F +#define L3GX_REG_INT1_CFG 0x30 +#define L3GX_REG_INT1_SRC 0x31 +#define L3GX_REG_INT1_THS_XH 0x32 +#define L3GX_REG_INT1_THS_XL 0x33 +#define L3GX_REG_INT1_THS_YH 0x34 +#define L3GX_REG_INT1_THS_YL 0x35 +#define L3GX_REG_INT1_THS_ZH 0x36 +#define L3GX_REG_INT1_THS_ZL 0x37 +#define L3GX_REG_INT1_DURATION 0x38 + +#define L3GX_AUTOINCREMENT 0x80 + +// CTRL_REG1: +#define L3GX_ENABLE_ALL_AXES 0x07 +#define L3GX_ENABLE_NORMAL_POWER_MODE 0x01 +#define L3GX_DTBW_MASK 0xF0 + +// CTRL_REG3: +#define L3GX_ENABLE_DR_INTERRUPT 0x08 + +// CTRL_REG4: +#define L3GX_ENABLE_BDU 0x80 +#define L3GX_SCALE_MASK 0x30 + +// STATUS_REG: +#define L3GX_STATUS_ZYXOR 0x80 +#define L3GX_STATUS_ZOR 0x40 +#define L3GX_STATUS_YOR 0x20 +#define L3GX_STATUS_XOR 0x10 +#define L3GX_STATUS_ZYXDA 0x08 +#define L3GX_STATUS_ZDA 0x04 +#define L3GX_STATUS_YDA 0x02 +#define L3GX_STATUS_XDA 0x01 + +static const char *TAG = "l3gx"; + +#define CHECK(x) \ + do \ + { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) \ + return __; \ + } \ + while (0) +#define CHECK_ARG(VAL) \ + do \ + { \ + if (!(VAL)) \ + return ESP_ERR_INVALID_ARG; \ + } \ + while (0) + +inline static esp_err_t write_reg_nolock(l3gx_t *dev, uint8_t reg, uint8_t val) +{ + return i2c_dev_write_reg(&dev->i2c_dev, reg, &val, 1); +} + +inline static esp_err_t read_reg_nolock(l3gx_t *dev, uint8_t reg, uint8_t *val) +{ + return i2c_dev_read_reg(&dev->i2c_dev, reg, val, 1); +} + +static esp_err_t update_reg_nolock(l3gx_t *dev, uint8_t reg, uint8_t val, uint8_t mask) +{ + uint8_t v; + CHECK(read_reg_nolock(dev, reg, &v)); + CHECK(write_reg_nolock(dev, reg, (v & ~mask) | val)); + return write_reg_nolock(dev, reg, v); +} + +esp_err_t l3gx_init_desc(l3gx_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + dev->i2c_dev.port = port; + dev->i2c_dev.addr = addr; + dev->i2c_dev.cfg.sda_io_num = sda_gpio; + dev->i2c_dev.cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->i2c_dev.cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + dev->sensor_type = L3GX_TYPE_UNKNOWN; + dev->scale = L3GX_SCALE_250; + dev->datarate_bandwith = L3GX_DRBW_100_125; + + return i2c_dev_create_mutex(&dev->i2c_dev); +} + +esp_err_t l3gx_init(l3gx_t *dev) +{ + CHECK_ARG(dev); + int type; + uint8_t v = 0; + + static const uint8_t expected_ids[] = { + [L3GX_TYPE_L3G4200D] = L3G4200D_WHO_AM_I_ID, + [L3GX_TYPE_L3GD20] = L3GD20_WHO_AM_I_ID + }; + static const char *sensor_type_str[] = { + [L3GX_TYPE_L3G4200D] = "L3G4200D", + [L3GX_TYPE_L3GD20] = "L3GD20" + }; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, L3GX_REG_WHO_AM_I, &v, 1)); + + for (type = 0; type < L3GX_TYPE_UNKNOWN; type++) + { + if (v == expected_ids[type]) + { + dev->sensor_type = type; + ESP_LOGD(TAG, "sensor %s connected", sensor_type_str[dev->sensor_type]); + break; + } + } + if (type == L3GX_TYPE_UNKNOWN) + { + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + ESP_LOGE(TAG, "Unknown ID: 0x%02x", v); + return ESP_ERR_NOT_FOUND; + } + + /* setup CTRL_REG1 + [7:6] DR - Output data rate + [5:4] BW - Bandwidth + [3] PD - Power-down mode + [2] ZEN - Z axis enable + [1] YEN - Y axis enable + [0] XEN - X axis enable + */ + + v = 0x00; + v |= (dev->datarate_bandwith << 4); + v |= (L3GX_ENABLE_NORMAL_POWER_MODE << 3); + v |= (L3GX_ENABLE_ALL_AXES << 0); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, L3GX_REG_CTRL_REG1, &v, 1)); + + /* setup CTRL_REG2 + [7:6] Reserved + [5:4] HPM1, HPM0 - High Pass filter Mode Selection, Default value: 00: + 00 Normal mode (reset reading HP_RESET_FILTER) + 01 Reference signal for filtering + 10 Normal mode + 11 Autoreset on interrupt event + [3:0] HPCF3, HPCF2, HPCF1, HPCF0 - High Pass filter Cut Off frequency selection: + HPCF3 ODR= 100 Hz ODR= 200 Hz ODR= 400 Hz ODR= 800 Hz + 0000 8 15 30 56 + 0001 4 8 15 30 + 0010 2 4 8 15 + 0011 1 2 4 8 + 0100 0.5 1 2 4 + 0101 0.2 0.5 1 2 + 0110 0.1 0.2 0.5 1 + 0111 0.05 0.1 0.2 0.5 + 1000 0.02 0.05 0.1 0.2 + 1001 0.01 0.02 0.05 0.1 + */ + + v = 0x00; + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, L3GX_REG_CTRL_REG2, &v, 1)); + + /* setup CTRL_REG3 + [7] I1_Int1 Interrupt enable on INT1 pin. Default value: 0. (0: Disable; 1: Enable) + [6] I1_Boot Boot status available on INT1. Default value: 0. (0: Disable; 1: Enable) + [5] H_Lactive Interrupt active configuration on INT1. Default value: 0. (0: High; 1:Low) + [4] PP_OD Push- Pull / Open drain. Default value: 0. (0: Push- Pull; 1: Open drain) + [3] I2_DRDY Date Ready on DRDY/INT2. Default value: 0. (0: Disable; 1: Enable) + [2] I2_WTM FIFO Watermark interrupt on DRDY/INT2. Default value: 0. (0: Disable; 1: Enable) + [1] I2_ORun FIFO Overrun interrupt on DRDY/INT2 Default value: 0. (0: Disable; 1: Enable) + [0] I2_Empty FIFO Empty interrupt on DRDY/INT2. Default value: 0. (0: Disable; 1: Enable) + */ + v = 0x00; + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, L3GX_REG_CTRL_REG3, &v, 1)); + + /* setup CTRL_REG4 + [7] BDU - Block Data Update. Default value: 0 (0: continous update; 1: output registers not updated until MSB and LSB reading) + [6] BLE - Big/Little Endian Data Selection. Default value: 0. + [5:4] FS1 FS0 - Full Scale selection. Default value: 00 (00: 250 dps; 01: 500 dps; 10: 2000 dps; 11: 2000 dps) ST1-ST0 Self Test Enable. Default value: 00 + +/- 250DPS -> 8.75 mDPS/LSB + +/- 1000DPS -> 17.5 mDPS/LSB + +/- 2000DPS -> 70.0 mDPS/LSB + [3] RESERVED - + [2:1] ST1 ST0 - Self Test Enable. Default value: 00 (00: Self Test Disabled;) + [0] SIM - SIM SPI Serial Interface Mode selection. Default value: 0 (0: 4-wire interface; 1: 3-wire interface). + + */ + v = 0x00; + v |= L3GX_ENABLE_BDU; + v |= (dev->scale << 4); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, L3GX_REG_CTRL_REG4, &v, 1)); + + /* setup CTRL_REG5 + [7] BOOT Reboot memory content. Default value: 0 (0: normal mode; 1: reboot memory content) + [6] FIFO_EN FIFO_EN FIFO enable. Default value: 0 (0: FIFO disable; 1: FIFO Enable) + [5] RESERVED - + [4] HPen HPen High Pass filter Enable. Default value: 0 (0: HPF disabled; 1: HPF enabled. See Figure 20) + [3:2] INT1_Sel1, INT1_Sel0 INT1 selection configuration. Default value: 0 + [1:0] Out_Sel1, Out_Sel0 Out selection configuration. Default value: 0 + */ + v = 0x00; + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, L3GX_REG_CTRL_REG5, &v, 1)); + + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + return ESP_OK; +} + +esp_err_t l3gx_free_desc(l3gx_t *dev) +{ + CHECK_ARG(dev); + return i2c_dev_delete_mutex(&dev->i2c_dev); +} + +esp_err_t l3gd20_get_chip_id(l3gx_t *dev, uint8_t *id) +{ + CHECK_ARG(dev && id); + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, read_reg_nolock(dev, L3GX_REG_WHO_AM_I, id)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + return ESP_OK; +} + +esp_err_t l3gd20_set_scale(l3gx_t *dev, l3gx_scale_t scale) +{ + CHECK_ARG(dev && scale >= L3GX_SCALE_250 && scale <= L3GX_SCALE_2000); + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + dev->scale = scale; + I2C_DEV_CHECK(&dev->i2c_dev, update_reg_nolock(dev, L3GX_REG_CTRL_REG4, (dev->scale << 4), L3GX_SCALE_MASK)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + return ESP_OK; +} + +esp_err_t l3gd20_set_datarate_and_bandwith(l3gx_t *dev, l3gx_drbw_t drbw) +{ + CHECK_ARG(dev && drbw >= L3GX_DRBW_100_125 && drbw <= L3GX_DRBW_800_110); + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + dev->datarate_bandwith = drbw; + I2C_DEV_CHECK(&dev->i2c_dev, update_reg_nolock(dev, L3GX_REG_CTRL_REG1, (dev->datarate_bandwith << 4), L3GX_DTBW_MASK)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + return ESP_OK; +} + +esp_err_t l3gx_data_ready(l3gx_t *dev, bool *ready) +{ + CHECK_ARG(dev && ready); + + uint8_t v; + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, read_reg_nolock(dev, L3GX_REG_STATUS_REG, &v)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + *ready = v & L3GX_STATUS_ZYXDA; + return ESP_OK; +} + +esp_err_t l3gx_get_raw_data(l3gx_t *dev, l3gx_raw_data_t *raw) +{ + CHECK_ARG(dev && raw); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + esp_err_t ret = i2c_dev_read_reg(&dev->i2c_dev, L3GX_REG_OUT_X_L | L3GX_AUTOINCREMENT, raw, 6); + if (ret != ESP_OK) + ESP_LOGE(TAG, "Could not read data register, err = %d", ret); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + return ret; +} + +esp_err_t l3gd20_raw_to_dps(l3gx_t *dev, l3gx_raw_data_t *raw, l3gx_data_t *data) +{ + /* sensitivity factors, datasheet pg. 9 */ + static const float sensivity_factors[] = { + [L3GX_SCALE_250] = 0.00875, // 8.75 mdps/digit + [L3GX_SCALE_500] = 0.0175, // 17.5 mdps/digit + [L3GX_SCALE_2000] = 0.0700 // 70.0 mdps/digit + }; + data->x = raw->x * sensivity_factors[dev->scale]; + data->y = raw->y * sensivity_factors[dev->scale]; + data->z = raw->z * sensivity_factors[dev->scale]; + return ESP_OK; +} + +esp_err_t l3gx_get_data(l3gx_t *dev, l3gx_data_t *data) +{ + CHECK_ARG(dev && data); + + l3gx_raw_data_t raw; + CHECK(l3gx_get_raw_data(dev, &raw)); + return l3gd20_raw_to_dps(dev, &raw, data); +} + +esp_err_t l3gx_get_raw_temp(l3gx_t *dev, int16_t *temp) +{ + CHECK_ARG(dev && temp); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + esp_err_t ret = i2c_dev_read_reg(&dev->i2c_dev, L3GX_REG_OUT_TEMP | L3GX_AUTOINCREMENT, temp, 2); + if (ret != ESP_OK) + ESP_LOGE(TAG, "Could not read OUT_TEMP register, err = %d", ret); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + return ret; +} diff --git a/components/l3gx/l3gx.h b/components/l3gx/l3gx.h new file mode 100644 index 00000000..b9361533 --- /dev/null +++ b/components/l3gx/l3gx.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2023 Jakub Turek + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file l3gx.h + * @defgroup l3gx l3gx + * @{ + * + * ESP-IDF Driver for L3GDx: L3G4200D and L3GD20 3-axis gyroscope sensors + * + * Copyright (c) 2023 Jakub Turek + * + * BSD Licensed as described in the file LICENSE + */ +#ifndef __L3GX_H__ +#define __L3GX_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Default I2C address + */ + +#define L3G4200D_I2C_ADDR_DEF 0x68 +#define L3GD20_I2C_ADDR_DEF 0x6A + +/** + * Sensor type + */ +typedef enum { L3GX_TYPE_L3G4200D, L3GX_TYPE_L3GD20, L3GX_TYPE_UNKNOWN } l3gx_sensor_type_t; + +/** + * Scales + */ +typedef enum { + L3GX_SCALE_250 = 0b00, // full scale to 250 dps + L3GX_SCALE_500 = 0b01, // full scale to 500 dps + L3GX_SCALE_2000 = 0b10 // full scale to 2000 dps +} l3gx_scale_t; + +/** + * Data rates and bandwith + */ +typedef enum { + L3GX_DRBW_100_125 = 0, // 100 Hz ODR, 12.5 Hz bandwidth + L3GX_DRBW_100_25a, + L3GX_DRBW_100_25b, + L3GX_DRBW_100_25c, + L3GX_DRBW_200_125, + L3GX_DRBW_200_25, + L3GX_DRBW_200_50, + L3GX_DRBW_200_70, + L3GX_DRBW_400_20, + L3GX_DRBW_400_25, + L3GX_DRBW_400_50, + L3GX_DRBW_400_110, + L3GX_DRBW_800_30, + L3GX_DRBW_800_35, + L3GX_DRBW_800_50, + L3GX_DRBW_800_110 // 800 Hz ODR, 110 Hz bandwidth +} l3gx_drbw_t; + +/** + * Raw measurement result + */ +typedef struct +{ + int16_t x; + int16_t y; + int16_t z; +} l3gx_raw_data_t; + +/** + * Measurement result, degrees per second + */ +typedef struct +{ + float x; + float y; + float z; +} l3gx_data_t; + +/** + * Device descriptor + */ +typedef struct +{ + i2c_dev_t i2c_dev; + l3gx_sensor_type_t sensor_type; + l3gx_scale_t scale; + l3gx_drbw_t datarate_bandwith; +} l3gx_t; + +/** + * @brief Initialize device descriptor + * + * @param dev Device descriptor + * @param port I2C port number + * @param addr I2C address + * @param sda_gpio SDA GPIO + * @param scl_gpio SCL GPIO + * @return `ESP_OK` on success + */ +esp_err_t l3gx_init_desc(l3gx_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t l3gx_free_desc(l3gx_t *dev); + +/** + * @brief Initialize device + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t l3gx_init(l3gx_t *dev); + +/** + * @brief Read chip ID + * + * @param dev Device descriptor + * @param[out] id Chip ID + * @return `ESP_OK` on success + */ +esp_err_t l3gd20_get_chip_id(l3gx_t *dev, uint8_t *id); + +/** + * @brief Set scaling factor + * + * @param dev Device descriptor + * @param[in] scale Scale factor + * @return `ESP_OK` on success + */ +esp_err_t l3gd20_set_scale(l3gx_t *dev, l3gx_scale_t scale); + +/** + * @brief Set data rate and bandwith + * + * @param dev Device descriptor + * @param[in] drbw Data rate and bandwidth setup + * @return `ESP_OK` on success + */ +esp_err_t l3gd20_set_datarate_and_bandwith(l3gx_t *dev, l3gx_drbw_t drbw); + +/** + * @brief Get gyro data state + * + * @param dev Device descriptor + * @param[out] ready Gyro data ready to read if true + * @return `ESP_OK` on success + */ +esp_err_t l3gx_data_ready(l3gx_t *dev, bool *ready); + +/** + * @brief Read raw gyro data + * + * @param dev Device descriptor + * @param[out] raw Raw gyro data + * @return `ESP_OK` on success + */ +esp_err_t l3gx_get_raw_data(l3gx_t *dev, l3gx_raw_data_t *raw); + +/** + * @brief Convert raw gyro data to dps [degrees per second] + * + * @param dev Device descriptor + * @param[in] raw Raw gyro data + * @param[out] data Gyro data in dps + * @return `ESP_OK` on success + */ +esp_err_t l3gd20_raw_to_dps(l3gx_t *dev, l3gx_raw_data_t *raw, l3gx_data_t *data); + +/** + * @brief Read gyro data in degrees per second + * + * @param dev Device descriptor + * @param[out] data Gyro data in dps + * @return `ESP_OK` on success + */ +esp_err_t l3gx_get_data(l3gx_t *dev, l3gx_data_t *data); + +/** + * @brief Read raw temperature data (see datasheet) + * + * @param dev Device descriptor + * @param[out] temp Raw temperature data + * @return `ESP_OK` on success + */ +esp_err_t l3gx_get_raw_temp(l3gx_t *dev, int16_t *temp); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __L3GX_H__ */ diff --git a/components/lc709203f/.eil.yml b/components/lc709203f/.eil.yml index 7ff98a34..a6a5be0e 100644 --- a/components/lc709203f/.eil.yml +++ b/components/lc709203f/.eil.yml @@ -1,23 +1,19 @@ ---- -components: - - name: lc709203f - description: | - Driver for LC709203F battery fuel gauge - group: misc - groups: [] - code_owners: jmpmscorp - depends: - - name: i2cdev - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: ISC - copyrights: - - author: - name: jmpmscorp - year: 2022 +name: lc709203f +description: Driver for LC709203F battery fuel gauge +version: 1.0.0 +groups: + - battery +code_owners: jmpmscorp +depends: + - i2cdev + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: ISC +copyrights: + - name: jmpmscorp + year: 2022 diff --git a/components/lc709203f/lc709203f.h b/components/lc709203f/lc709203f.h index 7447d1f2..ee67a8ec 100644 --- a/components/lc709203f/lc709203f.h +++ b/components/lc709203f/lc709203f.h @@ -331,7 +331,7 @@ esp_err_t lc709203f_set_alarm_low_voltage(i2c_dev_t *dev, uint16_t voltage); * @brief Set APA (adjustment pack application) * * @param[in] dev Device descriptor - * @param[in] value Value to set + * @param[in] apa Value to set * @return `ESP_OK` on success */ esp_err_t lc709203f_set_apa(i2c_dev_t *dev, uint8_t apa); @@ -340,7 +340,7 @@ esp_err_t lc709203f_set_apa(i2c_dev_t *dev, uint8_t apa); * @brief Set APA (adjustment pack thermistor) * * @param[in] dev Device descriptor - * @param[in] value Value to set + * @param[in] apt Value to set * @return * @return * `ESP_INVALID_ARG` null dev diff --git a/components/led_strip/.eil.yml b/components/led_strip/.eil.yml index 830f1dd5..72de2962 100644 --- a/components/led_strip/.eil.yml +++ b/components/led_strip/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: led_strip - description: RMT-based driver for WS2812B/SK6812/APA106/SM16703 LED strips - group: led - groups: [] - code_owners: UncleRus - depends: - - name: driver - - name: log - - name: color - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: UncleRus - year: 2020 +name: led_strip +description: RMT-based driver for WS2812B/SK6812/APA106/SM16703 LED strips +version: 1.2.0 +groups: + - led +code_owners: UncleRus +depends: + - driver + - log + - color + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: UncleRus + year: 2020 diff --git a/components/led_strip_spi/.eil.yml b/components/led_strip_spi/.eil.yml index fcb797e0..55b65aae 100644 --- a/components/led_strip_spi/.eil.yml +++ b/components/led_strip_spi/.eil.yml @@ -1,28 +1,24 @@ ---- -components: - - name: led_strip_spi - description: SPI-based driver for SK9822/APA102 LED strips - group: led - groups: [] - code_owners: trombik - depends: - # XXX conditional depends - - name: log - - name: color - - name: esp_idf_lib_helpers - thread_safe: N/A - targets: - - name: esp32 - - name: esp32c3 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: UncleRus - year: 2020 - - author: - name: trombik - year: 2021 +name: led_strip_spi +description: SPI-based driver for SK9822/APA102 LED strips +version: 1.0.0 +groups: + - led +code_owners: trombik +depends: + # XXX conditional depends + - log + - color + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp32c3 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: trombik + year: 2021 + - name: UncleRus + year: 2020 diff --git a/components/led_strip_spi/led_strip_spi_esp32.h b/components/led_strip_spi/led_strip_spi_esp32.h index 8877e570..9aa99181 100644 --- a/components/led_strip_spi/led_strip_spi_esp32.h +++ b/components/led_strip_spi/led_strip_spi_esp32.h @@ -41,9 +41,16 @@ #define LED_STRIP_SPI_DEFAULT_HOST_DEVICE SPI2_HOST ///< Default is `SPI2_HOST` (`HSPI_HOST` if `esp-idf` version is v3.x). #endif -#if defined(CONFIG_IDF_TARGET_ESP32C3) +#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) \ + || defined(CONFIG_IDF_TARGET_ESP32C6) #define LED_STRIP_SPI_DEFAULT_MOSI_IO_NUM (7) #define LED_STRIP_SPI_DEFAULT_SCLK_IO_NUM (6) +#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) +#define LED_STRIP_SPI_DEFAULT_MOSI_IO_NUM (11) +#define LED_STRIP_SPI_DEFAULT_SCLK_IO_NUM (12) +#elif defined(CONFIG_IDF_TARGET_ESP32H2) +#define LED_STRIP_SPI_DEFAULT_MOSI_IO_NUM (5) +#define LED_STRIP_SPI_DEFAULT_SCLK_IO_NUM (4) #else #define LED_STRIP_SPI_DEFAULT_MOSI_IO_NUM (13) ///< GPIO pin number of `LED_STRIP_SPI_DEFAULT_HOST_DEVICE`'s MOSI (default is 13 for ESP32, 7 for ESP32C3) #define LED_STRIP_SPI_DEFAULT_SCLK_IO_NUM (14) ///< GPIO pin number of `LED_STRIP_SPI_DEFAULT_HOST_DEVICE`'s SCLK (default is 14 for ESP32, 6 for ESP32C3) diff --git a/components/lib8tion/.eil.yml b/components/lib8tion/.eil.yml index 69210efa..8343c24e 100644 --- a/components/lib8tion/.eil.yml +++ b/components/lib8tion/.eil.yml @@ -1,20 +1,17 @@ ---- -components: - - name: lib8tion - description: Math functions specifically designed for LED programming - group: common - groups: [] - code_owners: UncleRus - depends: [] - thread_safe: N/A - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: FastLED - year: 2013 +name: lib8tion +description: Math functions specifically designed for LED programming +version: 1.0.0 +groups: + - common +code_owners: UncleRus +depends: [] +thread_safe: N/A +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: FastLED + year: 2013 diff --git a/components/lm75/.eil.yml b/components/lm75/.eil.yml index 18e32228..2e895b54 100644 --- a/components/lm75/.eil.yml +++ b/components/lm75/.eil.yml @@ -1,24 +1,21 @@ ---- -components: - - name: lm75 - description: | - Driver for LM75, a digital temperature sensor and thermal watchdog - group: temperature - groups: [] - code_owners: - - name: trombik - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: trombik - year: 2019 +name: lm75 +description: Driver for LM75, a digital temperature sensor and thermal watchdog +version: 1.0.0 +groups: + - temperature +code_owners: + - trombik +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: ISC +copyrights: + - name: trombik + year: 2019 diff --git a/components/ls7366r/.eil.yml b/components/ls7366r/.eil.yml index 7f38a4b9..11d346f0 100644 --- a/components/ls7366r/.eil.yml +++ b/components/ls7366r/.eil.yml @@ -1,20 +1,17 @@ ---- -components: - - name: ls7366r - description: Driver for LS7366R Quadrature Encoder Counter - group: input - groups: [] - code_owners: Jkallus - depends: - - name: driver - thread_safe: yes - targets: - - name: esp32 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: Jkallus - year: 2021 +name: ls7366r +description: Driver for LS7366R Quadrature Encoder Counter +version: 1.0.0 +groups: + - input +code_owners: Jkallus +depends: + - driver +thread_safe: yes +targets: + - esp32 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: Jkallus + year: 2021 diff --git a/components/ls7366r/ls7366r.h b/components/ls7366r/ls7366r.h index 7f4c106b..aedd1988 100644 --- a/components/ls7366r/ls7366r.h +++ b/components/ls7366r/ls7366r.h @@ -248,9 +248,11 @@ esp_err_t ls7366r_clear_counter(ls7366r_t *dev); * @return `ESP_OK` on success */ esp_err_t ls7366r_counter_enable(ls7366r_t *dev, bool enable); - + #ifdef __cplusplus } #endif +/**@}*/ + #endif /* __LS7366R_H__ */ diff --git a/components/lsm303/.eil.yml b/components/lsm303/.eil.yml new file mode 100644 index 00000000..cd6160d9 --- /dev/null +++ b/components/lsm303/.eil.yml @@ -0,0 +1,22 @@ +name: lsm303 +description: Driver for LSM303 3-axis accelerometer and magnetometer sensor +version: 1.0.0 +groups: + - imu + - magnetic +code_owners: + - qb4-dev +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: qb4-dev + year: 2023 diff --git a/components/lsm303/CMakeLists.txt b/components/lsm303/CMakeLists.txt new file mode 100644 index 00000000..9c4c4d35 --- /dev/null +++ b/components/lsm303/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS lsm303.c + INCLUDE_DIRS . + REQUIRES i2cdev log esp_idf_lib_helpers +) diff --git a/components/lsm303/LICENSE b/components/lsm303/LICENSE new file mode 100644 index 00000000..7b93da51 --- /dev/null +++ b/components/lsm303/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2023 Jakub Turek + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/lsm303/component.mk b/components/lsm303/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/lsm303/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/lsm303/lsm303.c b/components/lsm303/lsm303.c new file mode 100644 index 00000000..15e57b2c --- /dev/null +++ b/components/lsm303/lsm303.c @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2023 Jakub Turek + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file lsm303.c + * + * ESP-IDF Driver for LSM303: 3-axis accelerometer and magnetometer sensors + * + * Copyright (c) 2023 Jakub Turek + * + * BSD Licensed as described in the file LICENSE + */ + +#include "lsm303.h" + +#include +#include + +#define I2C_FREQ_HZ 400000 // 400kHz +#define LSM303_AUTOINCREMENT 0x80 + +/* accelerometer registers */ +#define LSM303_REG_ACC_CTRL_REG1_A 0x20 +#define LSM303_REG_ACC_CTRL_REG2_A 0x21 +#define LSM303_REG_ACC_CTRL_REG3_A 0x22 +#define LSM303_REG_ACC_CTRL_REG4_A 0x23 +#define LSM303_REG_ACC_CTRL_REG5_A 0x24 +#define LSM303_REG_ACC_CTRL_REG6_A 0x25 +#define LSM303_REG_ACC_REFERENCE_A 0x26 +#define LSM303_REG_ACC_STATUS_REG_A 0x27 +#define LSM303_REG_ACC_OUT_X_L_A 0x28 +#define LSM303_REG_ACC_OUT_X_H_A 0x29 +#define LSM303_REG_ACC_OUT_Y_L_A 0x2A +#define LSM303_REG_ACC_OUT_Y_H_A 0x2B +#define LSM303_REG_ACC_OUT_Z_L_A 0x2C +#define LSM303_REG_ACC_OUT_Z_H_A 0x2D +#define LSM303_REG_ACC_FIFO_CTRL_REG_A 0x2E +#define LSM303_REG_ACC_FIFO_SRC_REG_A 0x2F +#define LSM303_REG_ACC_INT1_CFG_A 0x30 +#define LSM303_REG_ACC_INT1_SOURCE_A 0x31 +#define LSM303_REG_ACC_INT1_THS_A 0x32 +#define LSM303_REG_ACC_INT1_DURATION_A 0x33 +#define LSM303_REG_ACC_INT2_CFG_A 0x34 +#define LSM303_REG_ACC_INT2_SOURCE_A 0x35 +#define LSM303_REG_ACC_INT2_THS_A 0x36 +#define LSM303_REG_ACC_INT2_DURATION_A 0x37 +#define LSM303_REG_ACC_CLICK_CFG_A 0x38 +#define LSM303_REG_ACC_CLICK_SRC_A 0x39 +#define LSM303_REG_ACC_CLICK_THS_A 0x3A +#define LSM303_REG_ACC_TIME_LIMIT_A 0x3B +#define LSM303_REG_ACC_TIME_LATENCY_A 0x3C +#define LSM303_REG_ACC_TIME_WINDOW_A 0x3D + +#define LSM303_REG_WHO_AM_I 0x0F + +/* accelerometer CTRL_REG1_A */ +#define LSM303_XEN (1 << 0) // X axis enable. Default value: 1 +#define LSM303_YEN (1 << 1) // Y axis enable. Default value: 1 +#define LSM303_ZEN (1 << 2) // Z axis enable. Default value: 1 +#define LSM303_LPEN (1 << 3) // Low-power mode enable. Default value: 0 + +/* accelerometer CTRL_REG4_A */ +#define LSM303_SIM (1 << 0) // SPI serial interface mode selection. Default value: 0 +#define LSM303_HR (1 << 3) // High resolution output mode: Default value: 0 +#define LSM303_BLE (1 << 6) // Big/little endian data selection. Default value 0 +#define LSM303_BDU (1 << 7) // Block data update. Default value: 0 + +/* accelerometer STATUS_REG_A */ +#define LSM303_ACC_STATUS_ZYXOR 0x80 +#define LSM303_ACC_STATUS_ZOR 0x40 +#define LSM303_ACC_STATUS_YOR 0x20 +#define LSM303_ACC_STATUS_XOR 0x10 +#define LSM303_ACC_STATUS_ZYXDA 0x08 +#define LSM303_ACC_STATUS_ZDA 0x04 +#define LSM303_ACC_STATUS_YDA 0x02 +#define LSM303_ACC_STATUS_XDA 0x01 + +#define LSM303_ACC_GRAVITY_STANDARD (9.80665f) // Earth's gravity in m/s^2 + +/* magnetometer registers */ +#define LSM303_REG_MAG_CRA_REG_M 0x00 +#define LSM303_REG_MAG_CRB_REG_M 0x01 +#define LSM303_REG_MAG_MR_REG_M 0x02 +#define LSM303_REG_MAG_OUT_X_H_M 0x03 +#define LSM303_REG_MAG_OUT_X_L_M 0x04 +#define LSM303_REG_MAG_OUT_Z_H_M 0x05 +#define LSM303_REG_MAG_OUT_Z_L_M 0x06 +#define LSM303_REG_MAG_OUT_Y_H_M 0x07 +#define LSM303_REG_MAG_OUT_Y_L_M 0x08 +#define LSM303_REG_MAG_SR_REG_M 0x09 +#define LSM303_REG_MAG_IRA_REG_M 0x0A +#define LSM303_REG_MAG_IRB_REG_M 0x0B +#define LSM303_REG_MAG_IRC_REG_M 0x0C +#define LSM303_REG_MAG_TEMP_OUT_H_M 0x31 +#define LSM303_REG_MAG_TEMP_OUT_L_M 0x32 + +/* magnetometer REG_MAG_CRA_REG_M */ +#define LSM303_TEMP_EN 0x80 + +/* magnetometer REG_SR_REG_M */ +#define LSM303_MAG_STATUS_LOCK 0x02 +#define LSM303_MAG_STATUS_DRDY 0x01 + +/* mag data order: high before low (different than accel) */ +#define LSM303_MAG_XH 0 +#define LSM303_MAG_XL 1 +#define LSM303_MAG_ZH 2 +#define LSM303_MAG_ZL 3 +#define LSM303_MAG_YH 4 +#define LSM303_MAG_YL 5 + +#define LSM303_MAG_GAUSS_TO_MICROTESLA (100.0f) // Gauss to micro-Tesla multiplier + +static const char *TAG = "lsm303"; + +#define CHECK(x) \ + do \ + { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) \ + return __; \ + } \ + while (0) +#define CHECK_ARG(VAL) \ + do \ + { \ + if (!(VAL)) \ + return ESP_ERR_INVALID_ARG; \ + } \ + while (0) + +inline static esp_err_t read_acc_reg_nolock(lsm303_t *dev, uint8_t reg, uint8_t *val) +{ + return i2c_dev_read_reg(&dev->i2c_dev_acc, reg, val, 1); +} + +inline static esp_err_t read_mag_reg_nolock(lsm303_t *dev, uint8_t reg, uint8_t *val) +{ + return i2c_dev_read_reg(&dev->i2c_dev_mag, reg, val, 1); +} + +esp_err_t lsm303_init_desc(lsm303_t *dev, uint8_t acc_addr, uint8_t mag_addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + dev->i2c_dev_acc.port = port; + dev->i2c_dev_acc.addr = acc_addr; + dev->i2c_dev_acc.cfg.sda_io_num = sda_gpio; + dev->i2c_dev_acc.cfg.scl_io_num = scl_gpio; + + dev->i2c_dev_mag.port = port; + dev->i2c_dev_mag.addr = mag_addr; + dev->i2c_dev_mag.cfg.sda_io_num = sda_gpio; + dev->i2c_dev_mag.cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->i2c_dev_acc.cfg.master.clk_speed = I2C_FREQ_HZ; + dev->i2c_dev_mag.cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + dev->acc_mode = LSM303_ACC_MODE_NORMAL; + dev->acc_rate = LSM303_ODR_10_HZ; + dev->acc_scale = LSM303_ACC_SCALE_2G; + + dev->mag_mode = LSM303_MAG_MODE_CONT; + dev->mag_rate = LSM303_MAG_RATE_3_0; + dev->mag_gain = LSM303_MAG_GAIN_1_3; + + CHECK(i2c_dev_create_mutex(&dev->i2c_dev_acc)); + CHECK(i2c_dev_create_mutex(&dev->i2c_dev_mag)); + return ESP_OK; +} + +esp_err_t lsm303_free_desc(lsm303_t *dev) +{ + CHECK_ARG(dev); + CHECK(i2c_dev_delete_mutex(&dev->i2c_dev_acc)); + CHECK(i2c_dev_delete_mutex(&dev->i2c_dev_mag)); + return ESP_OK; +} + +esp_err_t lsm303_init(lsm303_t *dev) +{ + CHECK_ARG(dev); + uint8_t v = 0; + + /* Check accelerometer connection */ + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_acc); + I2C_DEV_CHECK(&dev->i2c_dev_acc, i2c_dev_read_reg(&dev->i2c_dev_acc, LSM303_REG_WHO_AM_I, &v, 1)); + + if (v != 0x33) + { + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_acc); + ESP_LOGE(TAG, "Unknown acc ID: 0x%02x", v); + return ESP_ERR_NOT_FOUND; + } + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_acc); + + /* Check magnetometer connection */ + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_mag); + I2C_DEV_CHECK(&dev->i2c_dev_mag, i2c_dev_read_reg(&dev->i2c_dev_mag, LSM303_REG_MAG_IRA_REG_M, &v, 1)); + + if (v != 0x48) + { + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_mag); + ESP_LOGE(TAG, "Unknown mag ID: 0x%02x", v); + return ESP_ERR_NOT_FOUND; + } + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_mag); + + /* Initialize accelerometer */ + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_acc); + + /* setup CTRL_REG1 + [7:4] ODR - Output data rate + [3] LPen - Low Power mode + [2] ZEN - Z axis enable + [1] YEN - Y axis enable + [0] XEN - X axis enable + */ + v = 0x00; + v |= LSM303_XEN | LSM303_YEN | LSM303_ZEN; + v |= (dev->acc_rate << 4); + v |= (dev->acc_mode == LSM303_ACC_MODE_LOW_POWER) ? LSM303_LPEN : 0x00; + I2C_DEV_CHECK(&dev->i2c_dev_acc, i2c_dev_write_reg(&dev->i2c_dev_acc, LSM303_REG_ACC_CTRL_REG1_A, &v, 1)); + + /* setup CTRL_REG4 + [7] BDU - Block Data Update + [6] BLE - Big/Little Endian Data Selection + [5:4] FS1 FS0 - Full Scale selection + [3] HR - High Resolution + [2:1] ST1 ST0 - Self Test Enable + [0] SIM - SIM SPI Serial Interface Mode selection + + */ + v = 0x00; + v |= LSM303_BDU; + v |= (dev->acc_scale << 4); + v |= (dev->acc_mode == LSM303_ACC_MODE_HIGH_RESOLUTION) ? LSM303_HR : 0x00; + + I2C_DEV_CHECK(&dev->i2c_dev_acc, i2c_dev_write_reg(&dev->i2c_dev_acc, LSM303_REG_ACC_CTRL_REG4_A, &v, 1)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_acc); + + /* Initialize magnetometer */ + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_mag); + + /* setup LSM303_REG_MAG_CRA_REG_M + [7] TEMP_EN - Temperature sensor enable + [6:5] reserved + [4:2] DO = Data output rate bits + [1:0] reserved + */ + v = 0x00; + v |= LSM303_TEMP_EN; + v |= (dev->mag_rate << 2); + I2C_DEV_CHECK(&dev->i2c_dev_acc, i2c_dev_write_reg(&dev->i2c_dev_mag, LSM303_REG_MAG_CRA_REG_M, &v, 1)); + + /* setup LSM303_REG_MAG_CRB_REG_M + [7:5] GN - Gain configuration bits + [4:0] reserved + */ + v = 0x00; + v |= (dev->mag_gain << 5); + I2C_DEV_CHECK(&dev->i2c_dev_acc, i2c_dev_write_reg(&dev->i2c_dev_mag, LSM303_REG_MAG_CRB_REG_M, &v, 1)); + + /* setup LSM303_REG_MAG_MR_REG_M + [7:2] reserved + [1:0] Mode select bits + */ + v = 0x00; + I2C_DEV_CHECK(&dev->i2c_dev_acc, i2c_dev_write_reg(&dev->i2c_dev_mag, LSM303_REG_MAG_MR_REG_M, &v, 1)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_mag); + return ESP_OK; +} + +esp_err_t lsm303_acc_set_config(lsm303_t *dev, lsm303_acc_mode_t mode, lsm303_acc_rate_t rate, lsm303_acc_scale_t scale) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_acc); + dev->acc_mode = mode; + dev->acc_rate = rate; + dev->acc_scale = scale; + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_acc); + return lsm303_init(dev); +} + +esp_err_t lsm303_acc_get_config(lsm303_t *dev, lsm303_acc_mode_t *mode, lsm303_acc_rate_t *rate, lsm303_acc_scale_t *scale) +{ + CHECK_ARG(dev && mode && rate && scale); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_acc); + *mode = dev->acc_mode; + *rate = dev->acc_rate; + *scale = dev->acc_scale; + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_acc); + return ESP_OK; +} + +esp_err_t lsm303_acc_data_ready(lsm303_t *dev, bool *ready) +{ + CHECK_ARG(dev && ready); + + uint8_t v; + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_acc); + I2C_DEV_CHECK(&dev->i2c_dev_acc, read_acc_reg_nolock(dev, LSM303_REG_ACC_STATUS_REG_A, &v)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_acc); + *ready = v & LSM303_ACC_STATUS_ZYXDA; + return ESP_OK; +} + +esp_err_t lsm303_acc_get_raw_data(lsm303_t *dev, lsm303_acc_raw_data_t *raw) +{ + CHECK_ARG(dev && raw); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_acc); + esp_err_t ret = i2c_dev_read_reg(&dev->i2c_dev_acc, LSM303_REG_ACC_OUT_X_L_A | LSM303_AUTOINCREMENT, raw, 6); + if (ret != ESP_OK) + ESP_LOGE(TAG, "Could not read data register, err = %d", ret); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_acc); + return ret; +} + +esp_err_t lsm303_acc_raw_to_g(lsm303_t *dev, lsm303_acc_raw_data_t *raw, lsm303_acc_data_t *data) +{ + CHECK_ARG(dev && raw && data); + + static const float lsb[][4] = { + [LSM303_ACC_MODE_NORMAL] = { + [LSM303_ACC_SCALE_2G] = 0.0039, + [LSM303_ACC_SCALE_4G] = 0.00782, + [LSM303_ACC_SCALE_8G] = 0.01563, + [LSM303_ACC_SCALE_16G] = 0.0469 + }, + [LSM303_ACC_MODE_HIGH_RESOLUTION] = { + [LSM303_ACC_SCALE_2G] = 0.00098, + [LSM303_ACC_SCALE_4G] = 0.00195, + [LSM303_ACC_SCALE_8G] = 0.0039, + [LSM303_ACC_SCALE_16G] = 0.01172 + }, + [LSM303_ACC_MODE_LOW_POWER] = { + [LSM303_ACC_SCALE_2G] = 0.01563, + [LSM303_ACC_SCALE_4G] = 0.03126, + [LSM303_ACC_SCALE_8G] = 0.06252, + [LSM303_ACC_SCALE_16G] = 0.18758 + }, + }; + static const int shift[] = { + [LSM303_ACC_MODE_NORMAL] = 6, // 10-bit + [LSM303_ACC_MODE_HIGH_RESOLUTION] = 4, // 12-bit + [LSM303_ACC_MODE_LOW_POWER] = 8 // 8-bit + }; + data->x = (raw->x >> shift[dev->acc_mode]) * lsb[dev->acc_mode][dev->acc_scale]; + data->y = (raw->y >> shift[dev->acc_mode]) * lsb[dev->acc_mode][dev->acc_scale]; + data->z = (raw->z >> shift[dev->acc_mode]) * lsb[dev->acc_mode][dev->acc_scale]; + return ESP_OK; +} + +esp_err_t lsm303_acc_get_data(lsm303_t *dev, lsm303_acc_data_t *data) +{ + CHECK_ARG(dev && data); + + lsm303_acc_raw_data_t raw; + CHECK(lsm303_acc_get_raw_data(dev, &raw)); + return lsm303_acc_raw_to_g(dev, &raw, data); +} + +esp_err_t lsm303_mag_set_config(lsm303_t *dev, lsm303_mag_mode_t mode, lsm303_mag_rate_t rate, lsm303_mag_gain_t gain) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_mag); + dev->mag_mode = mode; + dev->mag_rate = rate; + dev->mag_gain = gain; + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_mag); + return lsm303_init(dev); +} + +esp_err_t lsm303_mag_get_config(lsm303_t *dev, lsm303_mag_mode_t *mode, lsm303_mag_rate_t *rate, lsm303_mag_gain_t *gain) +{ + CHECK_ARG(dev && mode && rate && gain); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_mag); + *mode = dev->mag_mode; + *rate = dev->mag_rate; + *gain = dev->mag_gain; + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_mag); + return ESP_OK; +} + +esp_err_t lsm303_mag_data_ready(lsm303_t *dev, bool *ready) +{ + CHECK_ARG(dev && ready); + + uint8_t v; + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_mag); + I2C_DEV_CHECK(&dev->i2c_dev_mag, read_mag_reg_nolock(dev, LSM303_REG_MAG_SR_REG_M, &v)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_mag); + *ready = v & LSM303_MAG_STATUS_DRDY; + return ESP_OK; +} + +esp_err_t lsm303_mag_get_raw_data(lsm303_t *dev, lsm303_mag_raw_data_t *raw) +{ + CHECK_ARG(dev && raw); + uint8_t buf[6] = { 0 }; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_mag); + esp_err_t ret = i2c_dev_read_reg(&dev->i2c_dev_mag, LSM303_REG_MAG_OUT_X_H_M | LSM303_AUTOINCREMENT, buf, 6); + if (ret != ESP_OK) + ESP_LOGE(TAG, "Could not read data register, err = %d", ret); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_mag); + + raw->x = (int16_t)(buf[LSM303_MAG_XL] | (buf[LSM303_MAG_XH] << 8)); + raw->y = (int16_t)(buf[LSM303_MAG_YL] | (buf[LSM303_MAG_YH] << 8)); + raw->z = (int16_t)(buf[LSM303_MAG_ZL] | (buf[LSM303_MAG_ZH] << 8)); + return ret; +} + +esp_err_t lsm303_mag_raw_to_uT(lsm303_t *dev, lsm303_mag_raw_data_t *raw, lsm303_mag_data_t *data) +{ + CHECK_ARG(dev && raw && data); + /* gain for XY axis is different from Z axis */ + enum { GAIN_XY = 0, GAIN_Z = 1 }; + /* { xy , z} */ + static const float gauss_lsb[][2] = { + [LSM303_MAG_GAIN_1_3] = { 1100, 980 }, + [LSM303_MAG_GAIN_1_9] = { 855, 760 }, + [LSM303_MAG_GAIN_2_5] = { 670, 600 }, + [LSM303_MAG_GAIN_4_0] = { 450, 400 }, + [LSM303_MAG_GAIN_4_7] = { 400, 355 }, + [LSM303_MAG_GAIN_5_6] = { 330, 295 }, + [LSM303_MAG_GAIN_8_1] = { 230, 205 }, + }; + + data->x = (float)raw->x / gauss_lsb[dev->mag_gain][GAIN_XY] * LSM303_MAG_GAUSS_TO_MICROTESLA; + data->y = (float)raw->y / gauss_lsb[dev->mag_gain][GAIN_XY] * LSM303_MAG_GAUSS_TO_MICROTESLA; + data->z = (float)raw->z / gauss_lsb[dev->mag_gain][GAIN_Z] * LSM303_MAG_GAUSS_TO_MICROTESLA; + return ESP_OK; +} + +esp_err_t lsm303_mag_get_data(lsm303_t *dev, lsm303_mag_data_t *data) +{ + CHECK_ARG(dev && data); + + lsm303_mag_raw_data_t raw; + CHECK(lsm303_mag_get_raw_data(dev, &raw)); + return lsm303_mag_raw_to_uT(dev, &raw, data); +} + +esp_err_t lsm303_mag_get_temp(lsm303_t *dev, float *temp) +{ + CHECK_ARG(dev && temp); + uint8_t buf[2] = { 0 }; + int16_t t; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev_mag); + esp_err_t ret = i2c_dev_read_reg(&dev->i2c_dev_mag, LSM303_REG_MAG_TEMP_OUT_H_M | LSM303_AUTOINCREMENT, buf, 2); + if (ret != ESP_OK) + ESP_LOGE(TAG, "Could not read TEMP_OUT register, err = %d", ret); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev_mag); + t = (((int16_t)((buf[0] << 8) | buf[1])) >> 4) + (20 << 3); // 12bit and 20*C + *temp = (float)t / 10; + return ret; +} diff --git a/components/lsm303/lsm303.h b/components/lsm303/lsm303.h new file mode 100644 index 00000000..60825f66 --- /dev/null +++ b/components/lsm303/lsm303.h @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2023 Jakub Turek + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file lsm303.h + * @defgroup lsm303 lsm303 + * @{ + * + * ESP-IDF Driver for LSM303: 3-axis accelerometer and magnetometer sensors + * + * Copyright (c) 2023 Jakub Turek + * + * BSD Licensed as described in the file LICENSE + */ +#ifndef __LSM303_H__ +#define __LSM303_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Default I2C address + */ + +#define LSM303_ADDR_ACC 0x19 +#define LSM303_ADDR_MAG 0x1E + +/** + * Accelerometer modes + */ +typedef enum { + LSM303_ACC_MODE_NORMAL, //!< Normal measurement mode; 10-bit + LSM303_ACC_MODE_HIGH_RESOLUTION, //!< High resolution mode; 12-bit + LSM303_ACC_MODE_LOW_POWER, //!< Low power mode; 8-bit +} lsm303_acc_mode_t; + +/** + * Accelerometer data rates + */ +typedef enum { + LSM303_ODR_POWER_DOWN = 0b0000, //!< Power-down mode + LSM303_ODR_1_HZ = 0b0001, //!< Normal / low-power mode (1 Hz) + LSM303_ODR_10_HZ = 0b0010, //!< Normal / low-power mode (10 Hz) + LSM303_ODR_25_HZ = 0b0011, //!< Normal / low-power mode (25 Hz) + LSM303_ODR_50_HZ = 0b0100, //!< Normal / low-power mode (50 Hz) + LSM303_ODR_100_HZ = 0b0101, //!< Normal / low-power mode (100 Hz) + LSM303_ODR_200_HZ = 0b0110, //!< Normal / low-power mode (200 Hz) + LSM303_ODR_400_HZ = 0b0111, //!< Normal / low-power mode (400 Hz) + LSM303_ODR_1620_HZ = 0b1000, //!< Low-power mode (1.620 kHz) + LSM303_ODR_5376_HZ = 0b1001 //!< Normal (1.344 kHz) / low-power mode (5.376 KHz) +} lsm303_acc_rate_t; + +/** + * Accelerometer scales + */ +typedef enum { + LSM303_ACC_SCALE_2G = 0b00, //!< 1 mg/LSB, +- 2G + LSM303_ACC_SCALE_4G = 0b01, //!< 2 mg/LSB, +- 4G + LSM303_ACC_SCALE_8G = 0b10, //!< 4 mg/LSB, +- 8G + LSM303_ACC_SCALE_16G = 0b11 //!< 12 mg/LSB, +- 16G +} lsm303_acc_scale_t; + +/** + * Magnetometer modes + */ +typedef enum { + LSM303_MAG_MODE_CONT = 0x00, //!< Continuous-conversion mode + LSM303_MAG_MODE_SINGLE = 0x01, //!< Single-conversion mode + LSM303_MAG_MODE_SLEEP1 = 0x02, //!< Sleep-mode. Device is placed in sleep-mode + LSM303_MAG_MODE_SLEEP2 = 0x03 //!< Sleep-mode. Device is placed in sleep-mode +} lsm303_mag_mode_t; + +/** + * Magnetometer rates + */ +typedef enum { + LSM303_MAG_RATE_0_75, //!< 0.75 Hz + LSM303_MAG_RATE_1_5, //!< 1.5 Hz + LSM303_MAG_RATE_3_0, //!< 3.0 Hz + LSM303_MAG_RATE_7_5, //!< 7.5 Hz + LSM303_MAG_RATE_15, //!< 15 Hz + LSM303_MAG_RATE_30, //!< 30 Hz + LSM303_MAG_RATE_75, //!< 75 Hz + LSM303_MAG_RATE_220 //!< 220 Hz +} lsm303_mag_rate_t; + +/** + * Magnetometer gains + */ +typedef enum { + LSM303_MAG_GAIN_1_3, //!< +/- 1.3 + LSM303_MAG_GAIN_1_9, //!< +/- 1.9 + LSM303_MAG_GAIN_2_5, //!< +/- 2.5 + LSM303_MAG_GAIN_4_0, //!< +/- 4.0 + LSM303_MAG_GAIN_4_7, //!< +/- 4.7 + LSM303_MAG_GAIN_5_6, //!< +/- 5.6 + LSM303_MAG_GAIN_8_1 //!< +/- 8.1 +} lsm303_mag_gain_t; + +/** + * Raw acceleration measurement result + */ +typedef struct +{ + int16_t x; + int16_t y; + int16_t z; +} lsm303_acc_raw_data_t; + +/** + * Raw magnetometer measurement result + */ +typedef struct +{ + int16_t x; + int16_t y; + int16_t z; +} lsm303_mag_raw_data_t; + +/** + * Linear acceleration measurement result: g + */ +typedef struct +{ + float x; + float y; + float z; +} lsm303_acc_data_t; + +/** + * Magnetic field measurement result: uT + */ +typedef struct +{ + float x; + float y; + float z; +} lsm303_mag_data_t; + +/** + * Device descriptor + */ +typedef struct +{ + i2c_dev_t i2c_dev_acc; + i2c_dev_t i2c_dev_mag; + + lsm303_acc_mode_t acc_mode; + lsm303_acc_rate_t acc_rate; + lsm303_acc_scale_t acc_scale; + + lsm303_mag_mode_t mag_mode; + lsm303_mag_rate_t mag_rate; + lsm303_mag_gain_t mag_gain; +} lsm303_t; + +/** + * @brief Initialize device descriptor + * + * @param dev Device descriptor + * @param port I2C port number + * @param acc_addr I2C accelerometer address + * @param mag_addr I2C magnetometer address + * @param sda_gpio SDA GPIO + * @param scl_gpio SCL GPIO + * @return `ESP_OK` on success + */ +esp_err_t lsm303_init_desc(lsm303_t *dev, uint8_t acc_addr, uint8_t mag_addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t lsm303_free_desc(lsm303_t *dev); + +/** + * @brief Initialize device + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t lsm303_init(lsm303_t *dev); + +/** + * @brief Set accelerometer configuration + * + * @param dev Device descriptor + * @param mode Power mode + * @param rate Output data rate + * @param scale Full scale selection + * @return `ESP_OK` on success + */ +esp_err_t lsm303_acc_set_config(lsm303_t *dev, lsm303_acc_mode_t mode, lsm303_acc_rate_t rate, lsm303_acc_scale_t scale); + +/** + * @brief Get accelerometer configuration + * + * @param dev Device descriptor + * @param[out] mode Power mode + * @param[out] rate Output data rate + * @param[out] scale Full scale selection + * @return `ESP_OK` on success + */ +esp_err_t lsm303_acc_get_config(lsm303_t *dev, lsm303_acc_mode_t *mode, lsm303_acc_rate_t *rate, lsm303_acc_scale_t *scale); + +/** + * @brief Get accelerometer data state + * + * @param dev Device descriptor + * @param[out] ready Accelerometer data ready to read if true + * @return `ESP_OK` on success + */ +esp_err_t lsm303_acc_data_ready(lsm303_t *dev, bool *ready); + +/** + * @brief Get raw accelerometer data + * + * @param dev Device descriptor + * @param[out] raw Accelerometer data + * @return `ESP_OK` on success + */ +esp_err_t lsm303_acc_get_raw_data(lsm303_t *dev, lsm303_acc_raw_data_t *raw); + +/** + * @brief Convert raw accelerometer data to g + * + * @param dev Device descriptor + * @param raw Raw accelerometer data + * @param[out] data accelerometer data in g + * @return `ESP_OK` on success + */ +esp_err_t lsm303_acc_raw_to_g(lsm303_t *dev, lsm303_acc_raw_data_t *raw, lsm303_acc_data_t *data); + +/** + * @brief Read accelerometer data in g + * + * @param dev Device descriptor + * @param[out] data accelerometer data in g + * @return `ESP_OK` on success + */ +esp_err_t lsm303_acc_get_data(lsm303_t *dev, lsm303_acc_data_t *data); + +/** + * @brief Set magnetometer configuration + * + * @param dev Device descriptor + * @param mode Power mode + * @param rate Output data rate + * @param gain Gain configuration + * @return `ESP_OK` on success + */ +esp_err_t lsm303_mag_set_config(lsm303_t *dev, lsm303_mag_mode_t mode, lsm303_mag_rate_t rate, lsm303_mag_gain_t gain); + +/** + * @brief Get magnetometer configuration + * + * @param dev Device descriptor + * @param[out] mode Power mode + * @param[out] rate Output data rate + * @param[out] gain Gain configuration + * @return `ESP_OK` on success + */ +esp_err_t lsm303_mag_get_config(lsm303_t *dev, lsm303_mag_mode_t *mode, lsm303_mag_rate_t *rate, lsm303_mag_gain_t *gain); + +/** + * @brief Get magnetometer data state + * + * @param dev Device descriptor + * @param[out] ready Magnetometer data ready to read if true + * @return `ESP_OK` on success + */ +esp_err_t lsm303_mag_data_ready(lsm303_t *dev, bool *ready); + +/** + * @brief Get raw magnetometer data + * + * @param dev Device descriptor + * @param[out] raw Magnetometer data + * @return `ESP_OK` on success + */ +esp_err_t lsm303_mag_get_raw_data(lsm303_t *dev, lsm303_mag_raw_data_t *raw); + +/** + * @brief Convert raw magnetometer data to uT + * + * @param dev Device descriptor + * @param raw Raw magnetometer data + * @param[out] data magnetometer data in uT + * @return `ESP_OK` on success + */ +esp_err_t lsm303_mag_raw_to_uT(lsm303_t *dev, lsm303_mag_raw_data_t *raw, lsm303_mag_data_t *data); + +/** + * @brief Read magnetometer data in uT + * + * @param dev Device descriptor + * @param[out] data magnetometer data in uT + * @return `ESP_OK` on success + */ +esp_err_t lsm303_mag_get_data(lsm303_t *dev, lsm303_mag_data_t *data); + +/** + * @brief Read chip temperature + * + * @param dev Device descriptor + * @param[out] temp Chip temperature + * @return `ESP_OK` on success + */ +esp_err_t lsm303_mag_get_temp(lsm303_t *dev, float *temp); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __LSM303_H__ */ diff --git a/components/max1704x/.eil.yml b/components/max1704x/.eil.yml new file mode 100644 index 00000000..1b5d896d --- /dev/null +++ b/components/max1704x/.eil.yml @@ -0,0 +1,20 @@ +name: max1704x +description: Driver for MAX17043/MAX17044/MAX17048/MAX17049 battery fuel gauge +version: 0.9.0 +groups: + - battery +code_owners: shuki25 +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: shuki25 + year: 2022 diff --git a/components/max1704x/CMakeLists.txt b/components/max1704x/CMakeLists.txt new file mode 100644 index 00000000..c29c36c1 --- /dev/null +++ b/components/max1704x/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS max1704x.c + INCLUDE_DIRS . + REQUIRES i2cdev log esp_idf_lib_helpers +) diff --git a/components/max1704x/Kconfig b/components/max1704x/Kconfig new file mode 100644 index 00000000..c9091a55 --- /dev/null +++ b/components/max1704x/Kconfig @@ -0,0 +1,11 @@ +menu "MAX1704X Battery Fuel Gauge" + + choice MAX1704X_MODEL + prompt "Chip type" + config MAX1704X_MODEL_3_4 + bool "MAX17043/MAX17044" + config MAX1704X_MODEL_8_9 + bool "MAX17048/MAX17049" + endchoice + +endmenu \ No newline at end of file diff --git a/components/max1704x/LICENSE b/components/max1704x/LICENSE new file mode 100644 index 00000000..609328c8 --- /dev/null +++ b/components/max1704x/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2022 Joshua Butler + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/max1704x/component.mk b/components/max1704x/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/max1704x/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/max1704x/max1704x.c b/components/max1704x/max1704x.c new file mode 100644 index 00000000..3f0c6ca9 --- /dev/null +++ b/components/max1704x/max1704x.c @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2022 Joshua Butler, MD, MHI + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file max1704x.c + * + * ESP-IDF driver for MAX17043/MAX17044/MAX17048/MAX17049 battery fuel gauge + * + * Copyright (c) 2022 Joshua Butler, MD, MHI + * + * BSD Licensed as described in the file LICENSE + */ + +#include +#include +#include + +#include "max1704x.h" + +#define I2C_FREQ_HZ 400000 + +/** + * MAX1704X registers + */ +#define MAX1704X_REGISTER_VCELL 0x02 +#define MAX1704X_REGISTER_SOC 0x04 +#define MAX1704X_REGISTER_MODE 0x06 +#define MAX1704X_REGISTER_VERSION 0x08 +#define MAX1704X_REGISTER_HIBRT 0x0A +#define MAX1704X_REGISTER_CONFIG 0x0C +#define MAX1704X_REGISTER_VALRT 0x14 // MAX17048/MAX17049 only +#define MAX1704X_REGISTER_CRATE 0x16 // MAX17048/MAX17049 only +#define MAX1704X_REGISTER_VRESET 0x18 // MAX17048/MAX17049 only +#define MAX1704X_REGISTER_STATUS 0x1A // MAX17048/MAX17049 only +#define MAX1704X_REGISTER_COMMAND 0xFE + +/** + * MAX1704X modes + */ + +#define MAX1704X_RESET_COMMAND 0x5400 +#define MAX1704X_QUICKSTART_MODE 0x4000 + +/** + * MAX1704X config register bits + */ + +#define MAX1704X_CONFIG_ALRT_BIT ((1U << 5)) +#define MAX1704X_CONFIG_ALSC_BIT ((1U << 6)) +#define MAX1704X_CONFIG_SLEEP_BIT ((1U << 7)) +#define MAX1704X_CONFIG_ATHD_MASK 0x1F +#define MAX1704X_CONFIG_ATHD_SHIFT 0 +#define MAX1704X_STATUS_RI_BIT ((1U << 0)) +#define MAX1704X_STATUS_VH_BIT ((1U << 1)) +#define MAX1704X_STATUS_VL_BIT ((1U << 2)) +#define MAX1704X_STATUS_VR_BIT ((1U << 3)) +#define MAX1704X_STATUS_SL_BIT ((1U << 4)) +#define MAX1704X_STATUS_SC_BIT ((1U << 5)) +#define MAX1704X_STATUS_VRA_BIT ((1U << 6)) + +/** + * MAX1704X precision constants: + * + * Precision calculated is by per bit, not per LSB. + */ + +#define MAX17043_MV_PRECISION 1.25f // 1.25mV precision +#define MAX17048_MV_PRECISION 0.078125f // 0.078125mV precision +#define MAX1704X_CRATE_PRECISION 0.208f // 0.208% precision +#define MAX1704X_VALRT_PRECISION 20.0f // 20mV precision +#define MAX1704X_HIBRT_VCELL_PRECISION 1.25f // 1.25mV precision +#define MAX1704X_HIBRT_CRATE_PRECISION 0.208f // 0.208% precision + +#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) +#define CHECK_ARG(ARG) do { if (!(ARG)) return ESP_ERR_INVALID_ARG; } while (0) + +static char *TAG = "max1704x"; + +/** + * Private functions + */ + +int16_t be16_to_cpu_signed(const uint8_t data[2]) +{ + int16_t r; + uint16_t u = (unsigned)data[1] | ((unsigned)data[0] << 8); + memcpy(&r, &u, sizeof r); + return r; +} + +int16_t le16_to_cpu_signed(const uint8_t data[2]) +{ + int16_t r; + uint16_t u = (unsigned)data[0] | ((unsigned)data[1] << 8); + memcpy(&r, &u, sizeof r); + return r; +} + +/** + * Public functions + */ + +esp_err_t max1704x_init_desc(max1704x_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + dev->i2c_dev.port = port; + dev->i2c_dev.addr = MAX1704X_I2C_ADDR; + dev->i2c_dev.cfg.sda_io_num = sda_gpio; + dev->i2c_dev.cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->i2c_dev.cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + return i2c_dev_create_mutex(&dev->i2c_dev); +} + +esp_err_t max1704x_free_desc(max1704x_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(&dev->i2c_dev); +} + +esp_err_t max1704x_quickstart(max1704x_t *dev) +{ + CHECK_ARG(dev); + + uint8_t data[2] = { 0x40, 0x00 }; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, MAX1704X_REGISTER_MODE, data, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + ESP_LOGD(TAG, "MAX1704X Quickstart"); + + return ESP_OK; +} + +esp_err_t max1704x_get_voltage(max1704x_t *dev, float *voltage) +{ + CHECK_ARG(dev && voltage); + + uint8_t data[2]; + int value; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MAX1704X_REGISTER_VCELL, data, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + // ESP_LOG_BUFFER_HEXDUMP("voltage", data, 2, ESP_LOG_INFO); + + if (dev->model == MAX17043_4) { + value = (data[0] << 4) | (data[1] >> 4); + *voltage = ((float)value * MAX17043_MV_PRECISION) / 1000; + } else { + *voltage = (((float)data[0] * 256 + (float)data[1]) * MAX17048_MV_PRECISION) / 1000; + } + return ESP_OK; +} + +esp_err_t max1704x_get_soc(max1704x_t *dev, float *soc) +{ + CHECK_ARG(dev && soc); + + uint8_t data[2]; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MAX1704X_REGISTER_SOC, data, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + // ESP_LOG_BUFFER_HEXDUMP("soc", data, 2, ESP_LOG_INFO); + + *soc = (float)data[0] + ((float)data[1]) / 256; + if (*soc > 100) { + *soc = 100.0f; + } + return ESP_OK; +} + +esp_err_t max1704x_get_crate(max1704x_t *dev, float *crate) +{ + CHECK_ARG(dev && crate); + + int16_t crate_value; + uint8_t data[2]; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MAX1704X_REGISTER_CRATE, data, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + if (dev->model == MAX17043_4) { + ESP_LOGE(TAG, "MAX1704X_REGISTER_CRATE is not supported by MAX17043"); + } else { + crate_value = be16_to_cpu_signed(data); + ESP_LOGD(TAG, "crateValue: %d", (int)crate_value); + *crate = ((float)crate_value * MAX1704X_CRATE_PRECISION); + } + return ESP_OK; +} + +esp_err_t max1704x_get_version(max1704x_t *dev, uint16_t *version) +{ + CHECK_ARG(dev && version); + + uint8_t data[2]; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MAX1704X_REGISTER_VERSION, data, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + *version = (data[0] << 8) | data[1]; + return ESP_OK; +} + +esp_err_t max1704x_get_config(max1704x_t *dev) +{ + CHECK_ARG(dev); + + uint8_t data[2]; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MAX1704X_REGISTER_CONFIG, data, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + dev->config.rcomp = data[0]; + dev->config.sleep_mode = (data[1] & MAX1704X_CONFIG_SLEEP_BIT) ? true : false; + dev->config.soc_change_alert = (data[1] & MAX1704X_CONFIG_ALSC_BIT) ? true : false; + dev->config.alert_status = (data[1] & MAX1704X_CONFIG_ALRT_BIT) ? true : false; + dev->config.empty_alert_thresh = 32 - ((data[1] & MAX1704X_CONFIG_ATHD_MASK) >> MAX1704X_CONFIG_ATHD_SHIFT); + + return ESP_OK; +} + +esp_err_t max1704x_set_config(max1704x_t *dev, max1704x_config_t *config) +{ + CHECK_ARG(dev && config); + + uint8_t data[2]; + + if (config->rcomp) { + data[0] = config->rcomp; + dev->config.rcomp = config->rcomp; + } + + data[1] = 0; + dev->config.sleep_mode = config->sleep_mode; + if (config->sleep_mode) { + data[1] |= MAX1704X_CONFIG_SLEEP_BIT; + } + + dev->config.soc_change_alert = config->soc_change_alert; + if (config->soc_change_alert) { + data[1] |= MAX1704X_CONFIG_ALSC_BIT; + } + + dev->config.alert_status = config->alert_status; + if (config->alert_status) { + data[1] |= MAX1704X_CONFIG_ALRT_BIT; + } + + dev->config.empty_alert_thresh = 32 - config->empty_alert_thresh; + data[1] |= (32 - config->empty_alert_thresh) << MAX1704X_CONFIG_ATHD_SHIFT; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, MAX1704X_REGISTER_CONFIG, data, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +esp_err_t max1704x_get_status(max1704x_t *dev) +{ + CHECK_ARG(dev); + + uint8_t data[2]; + + if (dev->model == MAX17043_4) { + ESP_LOGE(TAG, "MAX1704X STATUS is not supported by MAX17043"); + return ESP_ERR_NOT_SUPPORTED; + } + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MAX1704X_REGISTER_STATUS, data, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + dev->status.reset_indicator = (data[0] & MAX1704X_STATUS_RI_BIT) ? true : false; + dev->status.voltage_high = (data[0] & MAX1704X_STATUS_VH_BIT) ? true : false; + dev->status.voltage_low = (data[0] & MAX1704X_STATUS_VL_BIT) ? true : false; + dev->status.voltage_reset = (data[0] & MAX1704X_STATUS_VR_BIT) ? true : false; + dev->status.soc_low = (data[0] & MAX1704X_STATUS_SL_BIT) ? true : false; + dev->status.soc_change = (data[0] & MAX1704X_STATUS_SC_BIT) ? true : false; + dev->status.vreset_alert = (data[0] & MAX1704X_STATUS_VRA_BIT) ? true : false; + + return ESP_OK; +} + +esp_err_t max1704x_set_status(max1704x_t *dev, max1704x_status_t *status) +{ + CHECK_ARG(dev && status); + + uint8_t data[2]; + + if (dev->model == MAX17043_4) { + ESP_LOGE(TAG, "MAX1704X STATUS is not supported by MAX17043"); + return ESP_ERR_NOT_SUPPORTED; + } + + data[0] = 0; + if (status->reset_indicator) { + data[0] |= MAX1704X_STATUS_RI_BIT; + } + if (status->voltage_high) { + data[0] |= MAX1704X_STATUS_VH_BIT; + } + if (status->voltage_low) { + data[0] |= MAX1704X_STATUS_VL_BIT; + } + if (status->voltage_reset) { + data[0] |= MAX1704X_STATUS_VR_BIT; + } + if (status->soc_low) { + data[0] |= MAX1704X_STATUS_SL_BIT; + } + if (status->soc_change) { + data[0] |= MAX1704X_STATUS_SC_BIT; + } + if (status->vreset_alert) { + data[0] |= MAX1704X_STATUS_VRA_BIT; + } + data[1] = 0; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, MAX1704X_REGISTER_STATUS, data, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + return ESP_OK; +} diff --git a/components/max1704x/max1704x.h b/components/max1704x/max1704x.h new file mode 100644 index 00000000..dd6f635d --- /dev/null +++ b/components/max1704x/max1704x.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2022 Joshua Butler, MD, MHI + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file max1704x.h + * @defgroup max1704x max1704x + * @{ + * + * ESP-IDF driver for MAX17043/MAX17044/MAX17048/MAX17049 battery fuel gauge + * + * Copyright (c) 2022 Joshua Butler, MD, MHI + * + * BSD Licensed as described in the file LICENSE + */ + +#ifndef __MAX1704X__H__ +#define __MAX1704X__H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX1704X_I2C_ADDR 0x36 + +/** + * Device model + */ + +typedef enum +{ + MAX17043_4 = 0, + MAX17048_9, +} max1704x_model_t; + +/** + * Alert Status structure + */ +typedef struct +{ + bool reset_indicator; //!< Reset indicator + bool voltage_high; //!< Voltage high alert + bool voltage_low; //!< Voltage low alert + bool voltage_reset; //!< Voltage reset alert + bool soc_low; //!< SOC low alert, set when SOC cross empty_alert_thresh + bool soc_change; //!< SOC change alert, set when SOC change is at least 1% + bool vreset_alert; //!< Set to enable voltage reset alert under conditions specified in the valert register +} max1704x_status_t; + +/** + * MAX1704X configuration structure + */ +typedef struct +{ + uint8_t rcomp; //!< RCOMP register value - default 0x97 + bool sleep_mode; //!< Sleep mode - set to true to enter sleep mode + bool soc_change_alert; //!< SOC change alert - enable/disable SOC change alert + bool alert_status; //!< Alert status - read to check if alert has been triggered + uint8_t empty_alert_thresh; //!< Empty alert threshold - default 0x1C (4%, 32 - ATHD) + uint8_t active_threshold; //!< Exits hibernation when IOCV-CELLI above this threshold + uint8_t hibernate_threshold; //!< Enters Hibernation when CRATE falls below this threshold +} max1704x_config_t; + +/** + * Device descriptor + */ +typedef struct +{ + i2c_dev_t i2c_dev; + max1704x_model_t model; + max1704x_config_t config; + max1704x_status_t status; +} max1704x_t; + +/** + * @brief Initialize device descriptor + * + * @param dev Device descriptor + * @param port I2C port + * @param sda_gpio SDA GPIO + * @param scl_gpio SCL GPIO + * @return `ESP_OK` on success + */ +esp_err_t max1704x_init_desc(max1704x_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t max1704x_free_desc(max1704x_t *dev); + +/** + * @brief Quickstart battery fuel gauge + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t max1704x_quickstart(max1704x_t *dev); + +/** + * @brief Get battery voltage + * + * @param dev Device descriptor + * @param voltage Battery voltage + * @return `ESP_OK` on success + */ +esp_err_t max1704x_get_voltage(max1704x_t *dev, float *voltage); + +/** + * @brief Get state of charge + * + * @param dev Device descriptor + * @param soc State of charge + * @return `ESP_OK` on success + */ +esp_err_t max1704x_get_soc(max1704x_t *dev, float *soc); + +/** + * @brief Get rate of battery charge or discharge + * + * @param dev Device descriptor + * @param crate Rate of charge or discharge + * @return `ESP_OK` on success + */ +esp_err_t max1704x_get_crate(max1704x_t *dev, float *crate); + +/** + * @brief Get the production version of the chip + * + * @param dev Device descriptor + * @param version Production version + * @return `ESP_OK` on success + */ +esp_err_t max1704x_get_version(max1704x_t *dev, uint16_t *version); + +/** + * @brief Get the configuration register + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t max1704x_get_config(max1704x_t *dev); + +/** + * @brief Set the configuration register + * + * @param dev Device descriptor + * @param config Configuration register + * @return `ESP_OK` on success + */ +esp_err_t max1704x_set_config(max1704x_t *dev, max1704x_config_t *config); + +/** + * @brief Get the status register + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t max1704x_get_status(max1704x_t *dev); + +/** + * @brief Set the status register + * + * @param dev Device descriptor + * @param status Status register + * @return `ESP_OK` on success + * + * @note Use this function to clear alert flags after servicing the alert + */ +esp_err_t max1704x_set_status(max1704x_t *dev, max1704x_status_t *status); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif // __MAX1704X__H__ diff --git a/components/max31725/.eil.yml b/components/max31725/.eil.yml index 33037e63..cfc6081f 100644 --- a/components/max31725/.eil.yml +++ b/components/max31725/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: max31725 - description: Driver for MAX31725/MAX31726 temperature sensors - group: temperature - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2020 +name: max31725 +description: Driver for MAX31725/MAX31726 temperature sensors +version: 1.0.0 +groups: + - temperature +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2020 diff --git a/components/max31855/.eil.yml b/components/max31855/.eil.yml index c1f8de14..c235105d 100644 --- a/components/max31855/.eil.yml +++ b/components/max31855/.eil.yml @@ -1,21 +1,18 @@ ---- -components: - - name: max31855 - description: Driver for MAX31855 cold-junction compensated thermocouple-to-digital converter - group: temperature - groups: [] - code_owners: UncleRus - depends: - - driver - - log - thread_safe: yes - targets: - - name: esp32 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2022 +name: max31855 +description: Driver for MAX31855 cold-junction compensated thermocouple-to-digital converter +version: 1.0.0 +groups: + - temperature +code_owners: UncleRus +depends: + - driver + - log +thread_safe: yes +targets: + - esp32 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2022 diff --git a/components/max31865/.eil.yml b/components/max31865/.eil.yml index 5969286e..36993f82 100644 --- a/components/max31865/.eil.yml +++ b/components/max31865/.eil.yml @@ -1,21 +1,18 @@ ---- -components: - - name: max31865 - description: Driver for MAX31865 resistance converter for platinum RTDs - group: temperature - groups: [] - code_owners: UncleRus - depends: - - driver - - log - thread_safe: yes - targets: - - name: esp32 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2021 +name: max31865 +description: Driver for MAX31865 resistance converter for platinum RTDs +version: 1.0.0 +groups: + - temperature +code_owners: UncleRus +depends: + - driver + - log +thread_safe: yes +targets: + - esp32 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2021 diff --git a/components/max7219/.eil.yml b/components/max7219/.eil.yml index 91fbf24e..59e1e6e4 100644 --- a/components/max7219/.eil.yml +++ b/components/max7219/.eil.yml @@ -1,21 +1,18 @@ ---- -components: - - name: max7219 - description: Driver for 8-Digit LED display drivers, MAX7219/MAX7221 - group: led - groups: [] - code_owners: UncleRus - depends: - - driver - - log - thread_safe: yes - targets: - - name: esp32 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2019 +name: max7219 +description: Driver for 8-Digit LED display drivers, MAX7219/MAX7221 +version: 1.0.0 +groups: + - led +code_owners: UncleRus +depends: + - driver + - log +thread_safe: yes +targets: + - esp32 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2019 diff --git a/components/max7219/max7219.c b/components/max7219/max7219.c index fd8939ca..9b554b96 100644 --- a/components/max7219/max7219.c +++ b/components/max7219/max7219.c @@ -236,7 +236,7 @@ esp_err_t max7219_draw_text_7seg(max7219_t *dev, uint8_t pos, const char *s) { CHECK_ARG(dev && s); - while (s && pos < dev->digits) + while (*s && pos < dev->digits) { uint8_t c = get_char(dev, *s); if (*(s + 1) == '.') diff --git a/components/mcp23008/.eil.yml b/components/mcp23008/.eil.yml index b2eceae1..da450141 100644 --- a/components/mcp23008/.eil.yml +++ b/components/mcp23008/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: mcp23008 - description: Driver for 8-bit I2C GPIO expander MCP23008 - group: gpio - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2018 +name: mcp23008 +description: Driver for 8-bit I2C GPIO expander MCP23008 +version: 1.0.0 +groups: + - gpio +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2018 diff --git a/components/mcp23x17/.eil.yml b/components/mcp23x17/.eil.yml index 7aba7c4b..364e5e71 100644 --- a/components/mcp23x17/.eil.yml +++ b/components/mcp23x17/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: mcp23x17 - description: Driver for I2C/SPI 16 bit GPIO expanders MCP23017/MCP23S17 - group: gpio - groups: [] - code_owners: UncleRus - depends: - - driver - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2018 +name: mcp23x17 +description: Driver for I2C/SPI 16 bit GPIO expanders MCP23017/MCP23S17 +version: 1.1.2 +groups: + - gpio +code_owners: UncleRus +depends: + - driver + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2018 diff --git a/components/mcp23x17/mcp23x17.c b/components/mcp23x17/mcp23x17.c index 523f0f8c..099d7322 100644 --- a/components/mcp23x17/mcp23x17.c +++ b/components/mcp23x17/mcp23x17.c @@ -320,7 +320,7 @@ esp_err_t mcp23x17_init_desc_spi(mcp23x17_t *dev, spi_host_device_t host, uint32 return spi_bus_add_device(host, &dev->spi_cfg, &dev->spi_dev); } -esp_err_t mcp23x17_free_desc(mcp23x17_t *dev) +esp_err_t mcp23x17_free_desc_spi(mcp23x17_t *dev) { CHECK_ARG(dev); @@ -359,6 +359,8 @@ esp_err_t mcp23x17_set_int_out_mode(mcp23x17_t *dev, mcp23x17_int_out_mode_t mod if (mode == MCP23X17_OPEN_DRAIN) return write_reg_bit_8(dev, REG_IOCON, true, BIT_IOCON_ODR); + // The INTPOL bit is only functional if the ODR bit is cleared. + write_reg_bit_8(dev, REG_IOCON, false, BIT_IOCON_ODR); return write_reg_bit_8(dev, REG_IOCON, mode == MCP23X17_ACTIVE_HIGH, BIT_IOCON_INTPOL); } diff --git a/components/mcp342x/.eil.yml b/components/mcp342x/.eil.yml index 5bd53d3c..fe581415 100644 --- a/components/mcp342x/.eil.yml +++ b/components/mcp342x/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: mcp342x - description: Driver for 18-Bit, delta-sigma ADC MCP3426/MCP3427/MCP3428 - group: adc-dac - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2019 +name: mcp342x +description: Driver for 18-Bit, delta-sigma ADC MCP3426/MCP3427/MCP3428 +version: 1.0.0 +groups: + - adc-dac +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2019 diff --git a/components/mcp4725/.eil.yml b/components/mcp4725/.eil.yml index ec356bf2..6e9ce829 100644 --- a/components/mcp4725/.eil.yml +++ b/components/mcp4725/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: mcp4725 - description: Driver for 12-bit DAC MCP4725 - group: adc-dac - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2016 +name: mcp4725 +description: Driver for 12-bit DAC MCP4725 +version: 1.0.0 +groups: + - adc-dac +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2016 diff --git a/components/mcp960x/.eil.yml b/components/mcp960x/.eil.yml index c3e25124..7a3e27d0 100644 --- a/components/mcp960x/.eil.yml +++ b/components/mcp960x/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: mcp960x - description: Driver for MCP9600/MCP9601, thermocouple EMF to temperature converter - group: temperature - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2020 +name: mcp960x +description: Driver for MCP9600/MCP9601, thermocouple EMF to temperature converter +version: 1.0.0 +groups: + - temperature +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2020 diff --git a/components/mcp9808/.eil.yml b/components/mcp9808/.eil.yml index 56a50bbf..e2df8bd9 100644 --- a/components/mcp9808/.eil.yml +++ b/components/mcp9808/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: mcp9808 - description: Driver for MCP9808 Digital Temperature Sensor - group: temperature - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2020 +name: mcp9808 +description: Driver for MCP9808 digital temperature sensor +version: 1.0.0 +groups: + - temperature +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2020 diff --git a/components/mhz19b/.eil.yml b/components/mhz19b/.eil.yml index b8e9617b..aede9beb 100644 --- a/components/mhz19b/.eil.yml +++ b/components/mhz19b/.eil.yml @@ -1,26 +1,24 @@ ---- -components: - - name: mhz19b - description: Driver for MH-Z19B NDIR CO₂ sensor - group: air-quality - groups: - - gas - code_owners: UncleRus - depends: - - log - - esp_idf_lib_helpers - thread_safe: no - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: Erriez - year: 2020 - - author: - name: DavidD - year: 2021 +name: mhz19b +description: Driver for MH-Z19B NDIR CO₂ sensor +version: 1.1.0 +groups: + - air-quality + - gas +code_owners: + - Erriez + - DavidD +depends: + - log + - esp_idf_lib_helpers +thread_safe: no +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: Erriez + year: 2020 + - name: DavidD + year: 2021 diff --git a/components/mhz19b/mhz19b.c b/components/mhz19b/mhz19b.c index d12a7388..3aa5c8ce 100644 --- a/components/mhz19b/mhz19b.c +++ b/components/mhz19b/mhz19b.c @@ -60,7 +60,9 @@ esp_err_t mhz19b_init(mhz19b_dev_t *dev, uart_port_t uart_port, gpio_num_t tx_gp .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + .source_clk = UART_SCLK_DEFAULT, +#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) .source_clk = UART_SCLK_APB, #endif }; diff --git a/components/mp2660/.eil.yml b/components/mp2660/.eil.yml new file mode 100644 index 00000000..957bafe1 --- /dev/null +++ b/components/mp2660/.eil.yml @@ -0,0 +1,19 @@ +name: mp2660 +description: Driver for MP2660 5V USB, 500mA, I2C-Controlled Linear Charger with Power Path Management for Single-Cell Li-Ion Battery +version: 1.0.0 +groups: + - battery +code_owners: + - mmarkwort +depends: + - i2cdev + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: mmarkwort + year: 2023 diff --git a/components/mp2660/CMakeLists.txt b/components/mp2660/CMakeLists.txt new file mode 100644 index 00000000..9f824f6c --- /dev/null +++ b/components/mp2660/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS mp2660.c + INCLUDE_DIRS . + REQUIRES i2cdev esp_idf_lib_helpers +) diff --git a/components/mp2660/LICENSE b/components/mp2660/LICENSE new file mode 100644 index 00000000..56e9058a --- /dev/null +++ b/components/mp2660/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2023 Manuel Markwort (https://github.com/mmarkwort) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/components/mp2660/component.mk b/components/mp2660/component.mk new file mode 100644 index 00000000..60e16f4b --- /dev/null +++ b/components/mp2660/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev esp_idf_lib_helpers diff --git a/components/mp2660/mp2660.c b/components/mp2660/mp2660.c new file mode 100644 index 00000000..789eb04e --- /dev/null +++ b/components/mp2660/mp2660.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2023 Manuel Markwort + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file mp2660.c + * + * ESP-IDF driver for Monolithic Systems 5V USB, 500mA, I2C-Controlled Linear Charger with Power Path Management for Single-Cell Li-Ion Battery + * + * Copyright (c) 2023 Manuel Markwort \n + * + * BSD Licensed as described in the file LICENSE + */ + +#include +#include "mp2660.h" + +#define I2C_FREQ_HZ 400000 //!< 400kHz bus speed + +#define MP2660_SOURCE_CONTROL_REG_ADDR 0x00 //!< Address of source control register +#define MP2660_POWER_ON_CONFIG_REG_ADDR 0x01 //!< Address of power on configuration register +#define MP2660_CURENT_CONTROL_REG_ADDR 0x02 //!< Address of current control register +#define MP2660_PRECHARGE_TERMINATION_CURRENT_REG_ADDR 0x03 //!< Address of precharge termination current register +#define MP2660_CHARGE_VOLTAGE_CONTROL_REG_ADDR 0x04 //!< Address of charge voltage control register +#define MP2660_CHARGE_TERMINATION_TIMER_CONTROL_REG_ADDR 0x05 //!< Address of charge termination timer control register +#define MP2660_MISC_OPERATION_CONTROL_REG_ADDR 0x06 //!< Address of misc operation control register +#define MP2660_SYSTEM_STATUS_REG_ADDR 0x07 //!< Address of system status register +#define MP2660_FAULT_REG_ADDR 0x08 //!< Address of fault register + +#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) + +esp_err_t mp2660_init_desc(i2c_dev_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + dev->port = port; + dev->addr = MP2660_I2C_ADDR; + dev->cfg.sda_io_num = sda_gpio; + dev->cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + return i2c_dev_create_mutex(dev); +} + +esp_err_t mp2660_free_desc(i2c_dev_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(dev); +} + +esp_err_t mp2660_read(i2c_dev_t *dev, uint8_t addr, void* data) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, i2c_dev_read_reg(dev, addr, data, 1)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +esp_err_t mp2660_write(i2c_dev_t *dev, uint8_t addr, void* data) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, i2c_dev_write_reg(dev, addr, data, 1)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +esp_err_t mp2660_get_input_source(i2c_dev_t *dev, mp2660_input_source_t* input_source) +{ + return mp2660_read(dev, MP2660_SOURCE_CONTROL_REG_ADDR, input_source); +} + +esp_err_t mp2660_set_input_source(i2c_dev_t *dev, mp2660_input_source_t* input_source) +{ + return mp2660_write(dev, MP2660_SOURCE_CONTROL_REG_ADDR, input_source); +} + +esp_err_t mp2660_get_pwr_on_conf(i2c_dev_t *dev, mp2660_power_on_conf_t* pwr_config) +{ + return mp2660_read(dev, MP2660_POWER_ON_CONFIG_REG_ADDR, pwr_config); +} + +esp_err_t mp2660_set_pwr_on_conf(i2c_dev_t *dev, mp2660_power_on_conf_t* pwr_config) +{ + return mp2660_write(dev, MP2660_POWER_ON_CONFIG_REG_ADDR, pwr_config); +} + +esp_err_t mp2660_get_chrg_current_ctrl(i2c_dev_t *dev, mp2660_charge_current_ctrl_t* chrg_current_ctrl) +{ + return mp2660_read(dev, MP2660_CURENT_CONTROL_REG_ADDR, chrg_current_ctrl); +} + +esp_err_t mp2660_set_chrg_current_ctrl(i2c_dev_t *dev, mp2660_charge_current_ctrl_t* chrg_current_ctrl) +{ + return mp2660_write(dev, MP2660_CURENT_CONTROL_REG_ADDR, chrg_current_ctrl); +} + +esp_err_t mp2660_get_pre_chrg_term_current(i2c_dev_t *dev, mp2660_pre_charge_term_current_t* pre_chrg_term_current) +{ + return mp2660_read(dev, MP2660_PRECHARGE_TERMINATION_CURRENT_REG_ADDR, pre_chrg_term_current); +} + +esp_err_t mp2660_set_pre_chrg_term_current(i2c_dev_t *dev, mp2660_pre_charge_term_current_t* pre_chrg_term_current) +{ + return mp2660_write(dev, MP2660_PRECHARGE_TERMINATION_CURRENT_REG_ADDR, pre_chrg_term_current); +} + +esp_err_t mp2660_get_chrg_voltage_control(i2c_dev_t *dev, mp2660_charge_voltage_ctrl_t* chrg_voltage_ctrl) +{ + return mp2660_read(dev, MP2660_CHARGE_VOLTAGE_CONTROL_REG_ADDR, chrg_voltage_ctrl); +} + +esp_err_t mp2660_set_chrg_voltage_control(i2c_dev_t *dev, mp2660_charge_voltage_ctrl_t* chrg_voltage_ctrl) +{ + return mp2660_write(dev, MP2660_CHARGE_VOLTAGE_CONTROL_REG_ADDR, chrg_voltage_ctrl); +} + +esp_err_t mp2660_get_chrg_term_timer_control(i2c_dev_t *dev, mp2660_charge_term_timer_ctrl_t* charge_term_timer_ctrl) +{ + return mp2660_read(dev, MP2660_CHARGE_TERMINATION_TIMER_CONTROL_REG_ADDR, charge_term_timer_ctrl); +} + +esp_err_t mp2660_set_chrg_term_timer_control(i2c_dev_t *dev, mp2660_charge_term_timer_ctrl_t* charge_term_timer_ctrl) +{ + return mp2660_write(dev, MP2660_CHARGE_TERMINATION_TIMER_CONTROL_REG_ADDR, charge_term_timer_ctrl); +} + +esp_err_t mp2660_get_misc_op_control(i2c_dev_t *dev, mp2660_misc_op_ctrl_t* misc_op_ctrl) +{ + return mp2660_read(dev, MP2660_MISC_OPERATION_CONTROL_REG_ADDR, misc_op_ctrl); +} + +esp_err_t mp2660_set_misc_op_control(i2c_dev_t *dev, mp2660_misc_op_ctrl_t* misc_op_ctrl) +{ + return mp2660_write(dev, MP2660_MISC_OPERATION_CONTROL_REG_ADDR, misc_op_ctrl); +} + +esp_err_t mp2660_get_sys_status(i2c_dev_t *dev, mp2660_sys_status_t* sys_status) +{ + return mp2660_read(dev, MP2660_SYSTEM_STATUS_REG_ADDR, sys_status); +} + +esp_err_t mp2660_get_fault(i2c_dev_t *dev, mp2660_fault_t* fault) +{ + return mp2660_read(dev, MP2660_FAULT_REG_ADDR, fault); +} \ No newline at end of file diff --git a/components/mp2660/mp2660.h b/components/mp2660/mp2660.h new file mode 100644 index 00000000..98e15680 --- /dev/null +++ b/components/mp2660/mp2660.h @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2023 Manuel Markwort + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file mp2660.h + * @defgroup mp2660 mp2660 + * @{ + * + * ESP-IDF driver for Monolithic Systems 5V USB, 500mA, I2C-Controlled Linear Charger with Power Path Management for Single-Cell Li-Ion Battery + * + * Copyright (c) 2023 Manuel Markwort \n + * + * BSD Licensed as described in the file LICENSE + */ + +#ifndef __MP2660_H__ +#define __MP2660_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MP2660_I2C_ADDR 0x09 //!< I2C address + +#define MP2660_SOURCE_CONTROL_REG_DEFAULT 0x4F //!< Default value of source control register after power on +#define MP2660_POWER_ON_CONFIG_REG_DEFAULT 0x4 //!< Default value of power on configuration register after power on +#define MP2660_CURENT_CONTROL_REG_DEFAULT 0xE //!< Default value of current control register after power on +#define MP2660_PRECHARGE_TERMINATION_CURRENT_REG_DEFAULT 0x4A //!< Default value of pre charge termination current register after power on +#define MP2660_CHARGE_VOLTAGE_CONTROL_REG_DEFAULT 0xA3 //!< Default value of voltage control register after power on +#define MP2660_CHARGE_TERMINATION_TIMER_CONTROL_REG_DEFAULT 0x4A //!< Default value of termination timer control register after power on +#define MP2660_MISC_OPERATION_CONTROL_REG_DEFAULT 0xB //!< Default value of misc operation control register after power on +#define MP2660_SYSTEM_STATUS_REG_DEFAULT 0x0 //!< Default value of system status register after power on +#define MP2660_FAULT_REG_DEFAULT 0x0 //!< Default value of fault register after power on + +/** + * Input Source Control Register/Address: 00h (Default: 0100 1111) + */ +typedef struct +{ + union { + struct { + uint8_t i_in_lim_0 : 1; //!< 000: 85mA, 001: 130mA, 010: 175mA, 011: 220mA, 100: 265mA, 101: 310mA, 110: 355mA, 111: 455mA, Default: 455mA (111) + uint8_t i_in_lim_1 : 1; //!< s.a. + uint8_t i_in_lim_2 : 1; //!< s.a. + uint8_t v_in_min_0 : 1; //!< 80mV / RW / VIn Min Offset: 3.88V, Range 3.88V - 5.08V, Default: 4.60V (1001) + uint8_t v_in_min_1 : 1; //!< 160mV / RW + uint8_t v_in_min_2 : 1; //!< 320mV / RW + uint8_t v_in_min_3 : 1; //!< 640mV / RW + uint8_t en_hiz : 1; //!< 0: Disable 1: Enable / RW / This bit only controls the on and off of the LDO FET. + } data_fields; + struct { + uint8_t reg; //!< Register data + } register_data; + }; + +} mp2660_input_source_t; + +/** + * Power-On Configuration Register / Address: 01h (Default: 0000 0100) + */ +typedef struct +{ + union { + struct { + uint8_t v_batt_uvlo_0 : 1; //!< 0.1V / RW / Offset: 2.4V, Range: 2.4V - 3.1V, Default: 2.8V (100) + uint8_t v_batt_uvlo_1 : 1; //!< 0.2V / RW + uint8_t v_batt_uvlo_2 : 1; //!< 0.4V / RW + uint8_t ceb : 1; //!< 0: Charge enable 1: Charge disable / RW / Default: Charge enable (0) + uint8_t reserved_1 : 1; //!< NA + uint8_t reserved_2 : 1; //!< NA + uint8_t i2c_watchdog_timer : 1; //!< 0: Normal 1: Reset / RW / Default: Normal (0) + uint8_t reg_reset : 1; //!< 0: Keep current setting 1: Reset / RW / Default: Keep current register setting (0) + } data_fields; + struct { + uint8_t reg; //!< Register data + } register_data; + }; + +} mp2660_power_on_conf_t; + +/** + * Charge Current Control Register/ Address: 02h (Default: 0000 1110) + */ +typedef struct +{ + union { + struct { + uint8_t icc_4 : 1; //!< 272mA / RW / Offset: 8mA, Range: 8mA - 535mA, Default: 246mA (01110) + uint8_t icc_3 : 1; //!< 136mA / RW + uint8_t icc_2 : 1; //!< 68mA / RW + uint8_t icc_1 : 1; //!< 34mA / RW + uint8_t icc_0 : 1; //!< 17mA / RW + uint8_t reserved_3 : 1; //!< NA + uint8_t reserved_2 : 1; //!< NA + uint8_t reserved_1 : 1; //!< NA + } data_fields; + struct { + uint8_t reg; //!< Register data + } register_data; + }; + +} mp2660_charge_current_ctrl_t; + +/** + * Pre-Charge/ Termination Current/ Address: 03h (Default: 0100 1010) + */ +typedef struct +{ + union { + struct { + uint8_t reserved_1 : 1; //!< NA + uint8_t i_dschg_3 : 1; //!< 800mA / RW / Offset: 200mA, Range: 200mA - 1.6A, Default: 1.0A (1001) + uint8_t i_dschg_2 : 1; //!< 400mA / RW + uint8_t i_dschg_1 : 1; //!< 200mA / RW + uint8_t i_dschg_0 : 1; //!< 100mA / RW + uint8_t reserved_2 : 1; //!< NA + uint8_t i_pre_1 : 1; //!< 14mA / RW / Offset: 6mA, Range: 6mA - 27mA, Default: 20mA (10) + uint8_t i_pre_0 : 1; //!< 7mA / RW + } data_fields; + struct { + uint8_t reg; //!< Register data + } register_data; + }; + +} mp2660_pre_charge_term_current_t; + +/** + * Charge Voltage Control Register/ Address: 04h (Default: 1010 0011) + */ +typedef struct +{ + union { + struct { + uint8_t v_bat_reg_5 : 1; //!< 480mV / RW / Offset: 3.60V, Range: 3.60V - 4.545V, Default: 4.2V (101000) + uint8_t v_bat_reg_4 : 1; //!< 240mV / RW + uint8_t v_bat_reg_3 : 1; //!< 120mV / RW + uint8_t v_bat_reg_2 : 1; //!< 60mV / RW + uint8_t v_bat_reg_1 : 1; //!< 30mV / RW + uint8_t v_bat_reg_0 : 1; //!< 15mV / RW + uint8_t v_batt_pre : 1; //!< 0: 2.8V 1: 3.0V / RW / Default: 3.0V (1) + uint8_t v_batt_rech : 1; //!< 0: 150mV 1: 300mV / RW / Default: 300mV (1) + } data_fields; + struct { + uint8_t reg; //!< Register data + } register_data; + }; + +} mp2660_charge_voltage_ctrl_t; + +/** + * Charge Termination/Timer Control Register / Address: 05h (Default: 0100 1010) + */ +typedef struct +{ + union { + struct { + uint8_t reserved : 1; //!< NA + uint8_t en_term : 1; //!< 0: Disable 1: Enable / RW / Default: Enable (1) + uint8_t watchdog_1 : 1; //!< 00: Disable timer 01: 40s 10: 80s 11: 160s / RW / Default: Disable timer (00) + uint8_t watchdog_0 : 1; //!< s.a. + uint8_t en_timer : 1; //!< 0: Disable 1: Enable / RW / Default: Enable timer (1) + uint8_t chg_timer_1 : 1; //!< 00: 3hrs 01: 5hrs 10: 8hrs 11: 12hrs / RW / Default: 5hrs (01) + uint8_t chg_timer_0 : 1; //!< s.a. + uint8_t term_tmr : 1; //!< 0: Disable 1: Enable / RW / Default: Disable (0) + } data_fields; + struct { + uint8_t reg; //!< Register data + } register_data; + }; + +} mp2660_charge_term_timer_ctrl_t; + +/** + * Miscellaneous Operation Control Register/ Address: 06h (Default: 0000 1011) + */ +typedef struct +{ + union { + struct { + uint8_t reserved_1 : 1; //!< NA + uint8_t tmr2x_en : 1; //!< 0: Disable 2X extended safety timer during PPM 1: Enable 2X extended safety timer during PPM / RW / Default: Disable (0) + uint8_t fet_dis : 1; //!< 0: Enable 1: Turn off / RW / Default: Enable (0) + uint8_t reserved_2 : 1; //!< NA + uint8_t en_ntc : 1; //!< 0: Disable 1: Enable / RW / Default: Enable (1) + uint8_t reserved_3 : 1; //!< NA + uint8_t tj_reg_0 : 1; //!< 00: 60°C 01: 80°C 10: 100°C 11: 120°C / RW / 120°C (11) + uint8_t tj_reg_1 : 1; //!< s.a. + } data_fields; + struct { + uint8_t reg; //!< Register data + } register_data; + }; + +} mp2660_misc_op_ctrl_t; + +/** + * System Status Register/ Address: 07h (Default: 0000 0000) + */ +typedef struct +{ + union { + struct { + uint8_t reserved_1 : 1; //!< NA + uint8_t rev_1 : 1; //!< Revision number / R / Default: (00) + uint8_t rev_0 : 1; //!< s.a. + uint8_t chg_stat_1 : 1; //!< 00: Not charging 01: Pre-charge 10: Charge 11: Charge done / R / Default: Not charging (00) + uint8_t chg_stat_0 : 1; //!< s.a. + uint8_t ppm_stat : 1; //!< 0: No PPM 1: In PPM / R / Default: No PPM (0) (no power-path management happens) + uint8_t pg_stat : 1; //!< 0: Power fail 1: Power good / R / Default: Power fail (0) + uint8_t therm_stat : 1; //!< 0: No thermal regulation 1: In thermal regulation / R / No thermal regulation (0) + } data_fields; + struct { + uint8_t reg; //!< Register data + } register_data; + }; + +} mp2660_sys_status_t; + +/** + * Fault Register/ Address: 08h (Default: 0000 0000) + */ +typedef struct +{ + union { + struct { + uint8_t reserved_1 : 1; //!< NA + uint8_t watchdog_fault : 1; //!< 0: Normal 1: Watchdog timer expiration / R / Default: Normal (0) + uint8_t vin_fault : 1; //!< 0: Normal 1: Input fault (OVP or bad source) / R / Default: Normal (0) + uint8_t them_sd : 1; //!< 0: Normal 1: Thermal shutdown / R / Default: Normal (0) + uint8_t bat_fault : 1; //!< 0: Normal 1: Battery OVP / R / Default: Normal (0) + uint8_t stmr_fault : 1; //!< 0: Normal 1: Safety timer expiration / R / Default: Normal (0) + uint8_t reserved_2 : 1; //!< NA + uint8_t reserved_3 : 1; //!< NA + } data_fields; + struct { + uint8_t reg; //!< Register data + } register_data; + }; + +} mp2660_fault_t; + +/** + * @brief Initialize device descriptor + * + * @param dev Device descriptor + * @param port I2C port + * @param sda_gpio SDA GPIO pin + * @param scl_gpio SCL GPIO pin + * @return `ESP_OK` on success + */ +esp_err_t mp2660_init_desc(i2c_dev_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t mp2660_free_desc(i2c_dev_t *dev); + +/** + * @brief Reads input source register + * @param dev Device descriptor + * @param input_source Target buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_get_input_source(i2c_dev_t *dev, mp2660_input_source_t* input_source); + +/** + * @brief Writes input source register + * @param dev Device descriptor + * @param input_source Source buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_set_input_source(i2c_dev_t *dev, mp2660_input_source_t* input_source); + +/** + * @brief Reads power configuration register + * @param dev Device descriptor + * @param pwr_config Target buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_get_pwr_on_conf(i2c_dev_t *dev, mp2660_power_on_conf_t* pwr_config); + +/** + * @brief Writes power configuration register + * @param dev Device descriptor + * @param pwr_config Source buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_set_pwr_on_conf(i2c_dev_t *dev, mp2660_power_on_conf_t* pwr_config); + +/** + * @brief Reads charge current control register + * @param dev Device descriptor + * @param chrg_current_ctrl Target buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_get_chrg_current_ctrl(i2c_dev_t *dev, mp2660_charge_current_ctrl_t* chrg_current_ctrl); + +/** + * @brief Writes charge current control register + * @param dev Device descriptor + * @param chrg_current_ctrl Source buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_set_chrg_current_ctrl(i2c_dev_t *dev, mp2660_charge_current_ctrl_t* chrg_current_ctrl); + +/** + * @brief Reads pre-charge / termination current register + * @param dev Device descriptor + * @param pre_chrg_term_current Target buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_get_pre_chrg_term_current(i2c_dev_t *dev, mp2660_pre_charge_term_current_t* pre_chrg_term_current); + +/** + * @brief Writes pre-charge / termination current register + * @param dev Device descriptor + * @param pre_chrg_term_current Source buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_set_pre_chrg_term_current(i2c_dev_t *dev, mp2660_pre_charge_term_current_t* pre_chrg_term_current); + +/** + * @brief Reads charge voltage control register + * @param dev Device descriptor + * @param chrg_voltage_ctrl Target buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_get_chrg_voltage_control(i2c_dev_t *dev, mp2660_charge_voltage_ctrl_t* chrg_voltage_ctrl); + +/** + * @brief Writes charge voltage control register + * @param dev Device descriptor + * @param chrg_voltage_ctrl Source buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_set_chrg_voltage_control(i2c_dev_t *dev, mp2660_charge_voltage_ctrl_t* chrg_voltage_ctrl); + +/** + * @brief Reads charge termination / timer control register + * @param dev Device descriptor + * @param charge_term_timer_ctrl Target buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_get_chrg_term_timer_control(i2c_dev_t *dev, mp2660_charge_term_timer_ctrl_t* charge_term_timer_ctrl); + +/** + * @brief Writes charge termination / timer control register + * @param dev Device descriptor + * @param charge_term_timer_ctrl Source buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_set_chrg_term_timer_control(i2c_dev_t *dev, mp2660_charge_term_timer_ctrl_t* charge_term_timer_ctrl); + +/** + * @brief Reads miscellaneous operation control register + * @param dev Device descriptor + * @param misc_op_ctrl Target buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_get_misc_op_control(i2c_dev_t *dev, mp2660_misc_op_ctrl_t* misc_op_ctrl); + +/** + * @brief Writes miscellaneous operation control register + * @param dev Device descriptor + * @param misc_op_ctrl Source buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_set_misc_op_control(i2c_dev_t *dev, mp2660_misc_op_ctrl_t* misc_op_ctrl); + +/** + * @brief Reads system status register + * @param dev Device descriptor + * @param sys_status Target buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_get_sys_status(i2c_dev_t *dev, mp2660_sys_status_t* sys_status); + +/** + * @brief Reads fault register + * @param dev Device descriptor + * @param fault Target buffer + * @return `ESP_OK` on success + */ +esp_err_t mp2660_get_fault(i2c_dev_t *dev, mp2660_fault_t* fault); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif \ No newline at end of file diff --git a/components/mpu6050/.eil.yml b/components/mpu6050/.eil.yml new file mode 100644 index 00000000..38eb9fc8 --- /dev/null +++ b/components/mpu6050/.eil.yml @@ -0,0 +1,32 @@ +name: mpu6050 +description: Driver for MPU6000/MPU6050 6-axis MotionTracking device +version: 2.1.0 +groups: + - imu +code_owners: + - horsemann07 + - UncleRus +depends: + - log + - esp_idf_lib_helpers + - i2cdev +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: jeff_rowberg + year: 2012 + - name: a_e_dalzotto + year: 2019 + - name: g_b_vicari + year: 2019 + - name: gepid + year: 2019 + - name: horsemann07 + year: 2023 + - name: UncleRus + year: 2023 diff --git a/components/mpu6050/CMakeLists.txt b/components/mpu6050/CMakeLists.txt new file mode 100644 index 00000000..def9db66 --- /dev/null +++ b/components/mpu6050/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "mpu6050.c" + INCLUDE_DIRS . + REQUIRES i2cdev log esp_idf_lib_helpers +) diff --git a/components/mpu6050/Kconfig b/components/mpu6050/Kconfig new file mode 100644 index 00000000..44ea1ad0 --- /dev/null +++ b/components/mpu6050/Kconfig @@ -0,0 +1,7 @@ +menu "MPU6050" + +config AUTO_CONFIG_ADDR + bool "configure mpu6050 addr at run time" + default false + +endmenu diff --git a/components/mpu6050/LICENSE b/components/mpu6050/LICENSE new file mode 100644 index 00000000..a77d3ebd --- /dev/null +++ b/components/mpu6050/LICENSE @@ -0,0 +1,27 @@ + +The MIT License (MIT) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/mpu6050/README.md b/components/mpu6050/README.md new file mode 100644 index 00000000..a46a5f34 --- /dev/null +++ b/components/mpu6050/README.md @@ -0,0 +1,27 @@ +# Driver for the MPU6050 + +**NOTES:** +This driver is taken from https://github.com/gabrielbvicari/esp32-mpu6050 + +## Description: + +The [MPU-6050](https://create.arduino.cc/projecthub/CiferTech/what-is-mpu6050-b3b178) +is the world’s first and only 6-axis motion tracking devices designed for the low power, +low cost, and high performance requirements of smartphones, tablets and wearable sensors. + + +## LICENSE + + + The MIT License (MIT) + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/mpu6050/component.mk b/components/mpu6050/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/mpu6050/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/mpu6050/mpu6050.c b/components/mpu6050/mpu6050.c new file mode 100644 index 00000000..5ef145d0 --- /dev/null +++ b/components/mpu6050/mpu6050.c @@ -0,0 +1,1438 @@ +/* + * The MIT License (MIT) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file mpu6050.c + * + * ESP-IDF driver for MPU6050 MEMS Sensor. + * + * 6-axis motion tracking devices designed for the low power, low cost, + * and high performance requirements of smartphones, tablets and wearable sensors. + * + * Copyright (c) 2012 Jeff Rowberg + * Copyright (c) 2019 Angelo Elias Dalzotto <150633@upf.br> + * Copyright (c) 2019 Gabriel Boni Vicari <133192@upf.br> + * Copyright (c) 2019 GEPID - Grupo de Pesquisa em Cultura Digital + * Copyright (c) 2023 Raghav Jha + * Copyright (c) 2023 Ruslan V. Uss + */ + +#include "mpu6050.h" +#include "mpu6050_regs.h" +#include +#include +#include +#include + +// Max 1MHz for esp-idf, but device supports up to 1.7Mhz +#define I2C_FREQ_HZ (1000000) + +#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) +#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) + +static const char *TAG = "mpu6050"; + +static const float accel_res[] = { + [MPU6050_ACCEL_RANGE_2] = 2.0f / 32768.0f, + [MPU6050_ACCEL_RANGE_4] = 4.0f / 32768.0f, + [MPU6050_ACCEL_RANGE_8] = 8.0f / 32768.0f, + [MPU6050_ACCEL_RANGE_16] = 16.0f / 32768.0f, +}; + +static const float gyro_res[] = { + [MPU6050_GYRO_RANGE_250] = 250.0f / 32768.0f, + [MPU6050_GYRO_RANGE_500] = 500.0f / 32768.0f, + [MPU6050_GYRO_RANGE_1000] = 1000.0f / 32768.0f, + [MPU6050_GYRO_RANGE_2000] = 2000.0f / 32768.0f, +}; + +inline static float get_accel_value(mpu6050_dev_t *dev, int16_t raw) +{ + return (float)raw * accel_res[dev->ranges.accel]; +} + +inline static float get_gyro_value(mpu6050_dev_t *dev, int16_t raw) +{ + return (float)raw * gyro_res[dev->ranges.gyro]; +} + +inline static int16_t shuffle(uint16_t word) +{ + return (int16_t)((word >> 8) | (word << 8)); +} + +inline static uint16_t ushuffle(uint16_t word) +{ + return ((word >> 8) | (word << 8)); +} + +static esp_err_t read_reg_bits(mpu6050_dev_t *dev, uint8_t reg_addr, uint8_t offset, uint8_t mask, uint8_t *value) +{ + CHECK_ARG(dev && value && mask); + + uint8_t buf; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, reg_addr, &buf, 1)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + *value = (buf & mask) >> offset; + + return ESP_OK; +} + +static esp_err_t write_reg_bits(mpu6050_dev_t *dev, uint8_t reg_addr, uint8_t offset, uint8_t mask, uint8_t value) +{ + CHECK_ARG(dev && mask); + + uint8_t buf; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, reg_addr, &buf, 1)); + + buf = (buf & ~mask) | ((value << offset) & mask); + + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, reg_addr, &buf, 1)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +static esp_err_t read_reg(mpu6050_dev_t *dev, uint8_t reg_addr, uint8_t *value) +{ + CHECK_ARG(dev && value); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, reg_addr, value, 1)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +static esp_err_t write_reg(mpu6050_dev_t *dev, uint8_t reg_addr, uint8_t data) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, reg_addr, &data, 1)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +static esp_err_t read_reg_word(mpu6050_dev_t *dev, uint8_t reg_addr, int16_t *value) +{ + CHECK_ARG(dev && value); + + uint16_t buf; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, reg_addr, &buf, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + *value = shuffle(buf); + + return ESP_OK; +} + +static esp_err_t write_reg_word(mpu6050_dev_t *dev, uint8_t reg_addr, int16_t value) +{ + CHECK_ARG(dev); + + uint16_t buf = ushuffle(value); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, reg_addr, &buf, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +static esp_err_t read_reg_bool(mpu6050_dev_t *dev, uint8_t reg_addr, uint8_t offset, bool *value) +{ + CHECK_ARG(value); + + uint8_t buf; + CHECK(read_reg_bits(dev, reg_addr, offset, BIT(offset), &buf)); + *value = buf != 0 ? true : false; + + return ESP_OK; +} + +static inline esp_err_t write_reg_bool(mpu6050_dev_t *dev, uint8_t reg_addr, uint8_t offset, bool value) +{ + return write_reg_bits(dev, reg_addr, offset, BIT(offset), value ? 1 : 0); +} + +//////////////////////////////////////////////////////////////////////////////// + +esp_err_t mpu6050_init_desc(mpu6050_dev_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + if (addr != MPU6050_I2C_ADDRESS_LOW && addr != MPU6050_I2C_ADDRESS_HIGH) + { + ESP_LOGE(TAG, "Invalid device address: 0x%02x", addr); + return ESP_ERR_INVALID_ARG; + } + + dev->i2c_dev.port = port; + dev->i2c_dev.addr = addr; + dev->i2c_dev.cfg.sda_io_num = sda_gpio; + dev->i2c_dev.cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->i2c_dev.cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + return i2c_dev_create_mutex(&dev->i2c_dev); +} + +esp_err_t mpu6050_free_desc(mpu6050_dev_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(&dev->i2c_dev); +} + +esp_err_t mpu6050_init(mpu6050_dev_t *dev) +{ + CHECK_ARG(dev); + + CHECK(mpu6050_set_clock_source(dev, MPU6050_CLOCK_PLL_X)); + CHECK(mpu6050_set_full_scale_gyro_range(dev, MPU6050_GYRO_RANGE_250)); + CHECK(mpu6050_set_full_scale_accel_range(dev, MPU6050_ACCEL_RANGE_2)); + CHECK(mpu6050_set_sleep_enabled(dev, false)); + + return ESP_OK; +} + +esp_err_t mpu6050_get_aux_vddio_level(mpu6050_dev_t *dev, mpu6050_vddio_level_t *level) +{ + return read_reg_bits(dev, MPU6050_REGISTER_YG_OFFS_TC, MPU6050_TC_PWR_MODE_BIT, BIT(MPU6050_TC_PWR_MODE_BIT), (uint8_t *)level); +} + +esp_err_t mpu6050_set_aux_vddio_level(mpu6050_dev_t *dev, mpu6050_vddio_level_t level) +{ + return write_reg_bits(dev, MPU6050_REGISTER_YG_OFFS_TC, MPU6050_TC_PWR_MODE_BIT, BIT(MPU6050_TC_PWR_MODE_BIT), level); +} + +esp_err_t mpu6050_get_rate(mpu6050_dev_t *dev, uint8_t *rate) +{ + return read_reg(dev, MPU6050_REGISTER_SMPLRT_DIV, rate); +} + +esp_err_t mpu6050_set_rate(mpu6050_dev_t *dev, uint8_t rate) +{ + return write_reg(dev, MPU6050_REGISTER_SMPLRT_DIV, rate); +} + +esp_err_t mpu6050_get_external_frame_sync(mpu6050_dev_t *dev, mpu6050_ext_sync_t *sync) +{ + return read_reg_bits(dev, MPU6050_REGISTER_CONFIG, MPU6050_CFG_EXT_SYNC_SET_BIT, MPU6050_CFG_EXT_SYNC_SET_MASK, (uint8_t *)sync); +} + +esp_err_t mpu6050_set_external_frame_sync(mpu6050_dev_t *dev, mpu6050_ext_sync_t sync) +{ + return write_reg_bits(dev, MPU6050_REGISTER_CONFIG, MPU6050_CFG_EXT_SYNC_SET_BIT, MPU6050_CFG_EXT_SYNC_SET_MASK, sync); +} + +esp_err_t mpu6050_get_dlpf_mode(mpu6050_dev_t *dev, mpu6050_dlpf_mode_t *mode) +{ + return read_reg_bits(dev, MPU6050_REGISTER_CONFIG, MPU6050_CFG_DLPF_CFG_BIT, MPU6050_CFG_DLPF_CFG_MASK, (uint8_t *)mode); +} + +esp_err_t mpu6050_set_dlpf_mode(mpu6050_dev_t *dev, mpu6050_dlpf_mode_t mode) +{ + return write_reg_bits(dev, MPU6050_REGISTER_CONFIG, MPU6050_CFG_DLPF_CFG_BIT, MPU6050_CFG_DLPF_CFG_MASK, mode); +} + +esp_err_t mpu6050_get_full_scale_gyro_range(mpu6050_dev_t *dev, mpu6050_gyro_range_t *range) +{ + esp_err_t res = read_reg_bits(dev, MPU6050_REGISTER_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_MASK, (uint8_t *)range); + if (res == ESP_OK) + dev->ranges.gyro = *range; + + return res; +} + +esp_err_t mpu6050_set_full_scale_gyro_range(mpu6050_dev_t *dev, mpu6050_gyro_range_t range) +{ + CHECK_ARG(range <= MPU6050_GYRO_RANGE_2000); + + esp_err_t res = write_reg_bits(dev, MPU6050_REGISTER_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_MASK, range); + if (res == ESP_OK) + dev->ranges.gyro = range; + + return res; +} + +esp_err_t mpu6050_get_accel_self_test_factory_trim(mpu6050_dev_t *dev, mpu6050_axis_t axis, uint8_t *trim) +{ + CHECK_ARG(dev && trim && axis <= MPU6050_Z_AXIS); + + static const struct { uint8_t r, s; } regs[] = { + [MPU6050_X_AXIS] = { .r = MPU6050_REGISTER_SELF_TEST_X, .s = 4 }, + [MPU6050_Y_AXIS] = { .r = MPU6050_REGISTER_SELF_TEST_Y, .s = 2 }, + [MPU6050_Z_AXIS] = { .r = MPU6050_REGISTER_SELF_TEST_Z, .s = 0 }, + }; + + uint8_t a = 0; + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, regs[axis].r, trim, 1)); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MPU6050_REGISTER_SELF_TEST_A, &a, 1)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + *trim = ((*trim) >> 3) | ((a >> regs[axis].s) & 0x03); + + return ESP_OK; +} + +esp_err_t mpu6050_get_gyro_self_test_factory_trim(mpu6050_dev_t *dev, mpu6050_axis_t axis, uint8_t *trim) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + static const uint8_t regs[] = { + [MPU6050_X_AXIS] = MPU6050_REGISTER_SELF_TEST_X, + [MPU6050_Y_AXIS] = MPU6050_REGISTER_SELF_TEST_Y, + [MPU6050_Z_AXIS] = MPU6050_REGISTER_SELF_TEST_Z, + }; + + CHECK(read_reg(dev, regs[axis], trim)); + *trim = *trim & 0x1f; + + return ESP_OK; +} + +static const uint8_t accel_sta_bits[] = { + [MPU6050_X_AXIS] = MPU6050_ACONFIG_XA_ST_BIT, + [MPU6050_Y_AXIS] = MPU6050_ACONFIG_YA_ST_BIT, + [MPU6050_Z_AXIS] = MPU6050_ACONFIG_ZA_ST_BIT, +}; + +esp_err_t mpu6050_get_accel_self_test(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool *enabled) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return read_reg_bool(dev, MPU6050_REGISTER_ACCEL_CONFIG, accel_sta_bits[axis], enabled); +} + +esp_err_t mpu6050_set_accel_self_test(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool enabled) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return write_reg_bool(dev, MPU6050_REGISTER_ACCEL_CONFIG, accel_sta_bits[axis], enabled); +} + +esp_err_t mpu6050_get_full_scale_accel_range(mpu6050_dev_t *dev, mpu6050_accel_range_t *range) +{ + esp_err_t res = read_reg_bits(dev, MPU6050_REGISTER_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_MASK, (uint8_t *)range); + if (res == ESP_OK) + dev->ranges.accel = *range; + + return res; +} + +esp_err_t mpu6050_set_full_scale_accel_range(mpu6050_dev_t *dev, mpu6050_accel_range_t range) +{ + CHECK_ARG(range <= MPU6050_ACCEL_RANGE_16); + + esp_err_t res = write_reg_bits(dev, MPU6050_REGISTER_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_MASK, range); + if (res == ESP_OK) + dev->ranges.accel = range; + + return res; +} + +esp_err_t mpu6050_get_dhpf_mode(mpu6050_dev_t *dev, mpu6050_dhpf_mode_t *mode) +{ + return read_reg_bits(dev, MPU6050_REGISTER_ACCEL_CONFIG, MPU6050_ACONFIG_ACCEL_HPF_BIT, MPU6050_ACONFIG_ACCEL_HPF_MASK, (uint8_t *)mode); +} + +esp_err_t mpu6050_set_dhpf_mode(mpu6050_dev_t *dev, mpu6050_dhpf_mode_t mode) +{ + return write_reg_bits(dev, MPU6050_REGISTER_ACCEL_CONFIG, MPU6050_ACONFIG_ACCEL_HPF_BIT, MPU6050_ACONFIG_ACCEL_HPF_MASK, mode); +} + +esp_err_t mpu6050_get_freefall_detection_threshold(mpu6050_dev_t *dev, uint8_t *threshold) +{ + return read_reg(dev, MPU6050_REGISTER_FF_THR, threshold); +} + +esp_err_t mpu6050_set_freefall_detection_threshold(mpu6050_dev_t *dev, uint8_t threshold) +{ + return write_reg(dev, MPU6050_REGISTER_FF_THR, threshold); +} + +esp_err_t mpu6050_get_freefall_detection_duration(mpu6050_dev_t *dev, uint8_t *duration_ms) +{ + return read_reg(dev, MPU6050_REGISTER_FF_DUR, duration_ms); +} + +esp_err_t mpu6050_set_freefall_detection_duration(mpu6050_dev_t *dev, uint8_t duration_ms) +{ + return write_reg(dev, MPU6050_REGISTER_FF_DUR, duration_ms); +} + +esp_err_t mpu6050_get_motion_detection_threshold(mpu6050_dev_t *dev, uint8_t *threshold) +{ + return read_reg(dev, MPU6050_REGISTER_MOT_THR, threshold); +} + +esp_err_t mpu6050_set_motion_detection_threshold(mpu6050_dev_t *dev, uint8_t threshold) +{ + return write_reg(dev, MPU6050_REGISTER_MOT_THR, threshold); +} + +esp_err_t mpu6050_get_motion_detection_duration(mpu6050_dev_t *dev, uint8_t *duration) +{ + return read_reg(dev, MPU6050_REGISTER_MOT_DUR, duration); +} + +esp_err_t mpu6050_set_motion_detection_duration(mpu6050_dev_t *dev, uint8_t duration) +{ + return write_reg(dev, MPU6050_REGISTER_MOT_DUR, duration); +} + +esp_err_t mpu6050_get_zero_motion_detection_threshold(mpu6050_dev_t *dev, uint8_t *threshold) +{ + return read_reg(dev, MPU6050_REGISTER_ZRMOT_THR, threshold); +} + +esp_err_t mpu6050_set_zero_motion_detection_threshold(mpu6050_dev_t *dev, uint8_t threshold) +{ + return write_reg(dev, MPU6050_REGISTER_ZRMOT_THR, threshold); +} + +esp_err_t mpu6050_get_zero_motion_detection_duration(mpu6050_dev_t *dev, uint8_t *duration) +{ + return read_reg(dev, MPU6050_REGISTER_ZRMOT_DUR, duration); +} + +esp_err_t mpu6050_set_zero_motion_detection_duration(mpu6050_dev_t *dev, uint8_t duration) +{ + return write_reg(dev, MPU6050_REGISTER_ZRMOT_DUR, duration); +} + +esp_err_t mpu6050_get_temp_fifo_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_FIFO_EN, MPU6050_TEMP_FIFO_EN_BIT, enabled); +} + +esp_err_t mpu6050_set_temp_fifo_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_FIFO_EN, MPU6050_TEMP_FIFO_EN_BIT, enabled); +} + +static const uint8_t gyro_fifo_bits[] = { + [MPU6050_X_AXIS] = MPU6050_XG_FIFO_EN_BIT, + [MPU6050_Y_AXIS] = MPU6050_YG_FIFO_EN_BIT, + [MPU6050_Z_AXIS] = MPU6050_ZG_FIFO_EN_BIT, +}; + +esp_err_t mpu6050_get_gyro_fifo_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool *enabled) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return read_reg_bool(dev, MPU6050_REGISTER_FIFO_EN, gyro_fifo_bits[axis], enabled); +} + +esp_err_t mpu6050_set_gyro_fifo_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool enabled) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return write_reg_bool(dev, MPU6050_REGISTER_FIFO_EN, gyro_fifo_bits[axis], enabled); +} + +esp_err_t mpu6050_get_accel_fifo_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_FIFO_EN, MPU6050_ACCEL_FIFO_EN_BIT, enabled); +} + +esp_err_t mpu6050_set_accel_fifo_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_FIFO_EN, MPU6050_ACCEL_FIFO_EN_BIT, enabled); +} + +static const struct { uint8_t r, b; } slave_fifo_bits[] = { + [MPU6050_SLAVE_0] = { .r = MPU6050_REGISTER_FIFO_EN, .b = MPU6050_SLV2_FIFO_EN_BIT }, + [MPU6050_SLAVE_1] = { .r = MPU6050_REGISTER_FIFO_EN, .b = MPU6050_SLV1_FIFO_EN_BIT }, + [MPU6050_SLAVE_2] = { .r = MPU6050_REGISTER_FIFO_EN, .b = MPU6050_SLV0_FIFO_EN_BIT }, + [MPU6050_SLAVE_3] = { .r = MPU6050_REGISTER_I2C_MST_CTRL, .b = MPU6050_SLV_3_FIFO_EN_BIT }, +}; + +esp_err_t mpu6050_get_slave_fifo_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *enabled) +{ + CHECK_ARG(num < MPU6050_SLAVE_4); + + return read_reg_bool(dev, slave_fifo_bits[num].r, slave_fifo_bits[num].b, enabled); +} + +esp_err_t mpu6050_set_slave_fifo_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool enabled) +{ + CHECK_ARG(num < MPU6050_SLAVE_4); + + return write_reg_bool(dev, slave_fifo_bits[num].r, slave_fifo_bits[num].b, enabled); +} + +esp_err_t mpu6050_get_multi_master_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_I2C_MST_CTRL, MPU6050_MULT_MST_EN_BIT, enabled); +} + +esp_err_t mpu6050_set_multi_master_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_I2C_MST_CTRL, MPU6050_MULT_MST_EN_BIT, enabled); +} + +esp_err_t mpu6050_get_wait_for_external_sensor_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_I2C_MST_CTRL, MPU6050_WAIT_FOR_ES_BIT, enabled); +} + +esp_err_t mpu6050_set_wait_for_external_sensor_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_I2C_MST_CTRL, MPU6050_WAIT_FOR_ES_BIT, enabled); +} + +esp_err_t mpu6050_get_slave_read_write_transition_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_I2C_MST_CTRL, MPU6050_I2C_MST_P_NSR_BIT, enabled); +} + +esp_err_t mpu6050_set_slave_read_write_transition_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_I2C_MST_CTRL, MPU6050_I2C_MST_P_NSR_BIT, enabled); +} + +esp_err_t mpu6050_get_master_clock_speed(mpu6050_dev_t *dev, mpu6050_i2c_master_clock_t *clk_spd) +{ + return read_reg_bits(dev, MPU6050_REGISTER_I2C_MST_CTRL, MPU6050_I2C_MST_CLK_BIT, MPU6050_I2C_MST_CLK_MASK, (uint8_t *)clk_spd); +} + +esp_err_t mpu6050_set_master_clock_speed(mpu6050_dev_t *dev, mpu6050_i2c_master_clock_t clk_spd) +{ + return write_reg_bits(dev, MPU6050_REGISTER_I2C_MST_CTRL, MPU6050_I2C_MST_CLK_BIT, MPU6050_I2C_MST_CLK_MASK, clk_spd); +} + +esp_err_t mpu6050_get_slave_address(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t *addr) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return read_reg(dev, MPU6050_REGISTER_I2C_SLV0_ADDR + num * 3, addr); +} + +esp_err_t mpu6050_set_slave_address(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t address) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return write_reg(dev, MPU6050_REGISTER_I2C_SLV0_ADDR + num * 3, address); +} + +esp_err_t mpu6050_get_slave_register(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t *reg) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return read_reg(dev, MPU6050_REGISTER_I2C_SLV0_REG + num * 3, reg); +} + +esp_err_t mpu6050_set_slave_register(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t reg) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return write_reg(dev, MPU6050_REGISTER_I2C_SLV0_REG + num * 3, reg); +} + +esp_err_t mpu6050_get_slave_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *enabled) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return read_reg_bool(dev, MPU6050_REGISTER_I2C_SLV0_CTRL + num * 3, MPU6050_I2C_SLV_EN_BIT, enabled); +} + +esp_err_t mpu6050_set_slave_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool enabled) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return write_reg_bool(dev, MPU6050_REGISTER_I2C_SLV0_CTRL + num * 3, MPU6050_I2C_SLV_EN_BIT, enabled); +} + +esp_err_t mpu6050_get_slave_word_byte_swap(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *enabled) +{ + CHECK_ARG(num < MPU6050_SLAVE_4); + + return read_reg_bool(dev, MPU6050_REGISTER_I2C_SLV0_CTRL + num * 3, MPU6050_I2C_SLV_BYTE_SW_BIT, enabled); +} + +esp_err_t mpu6050_set_slave_word_byte_swap(mpu6050_dev_t *dev, mpu6050_slave_t num, bool enabled) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return write_reg_bool(dev, MPU6050_REGISTER_I2C_SLV0_CTRL + num * 3, MPU6050_I2C_SLV_BYTE_SW_BIT, enabled); +} + +esp_err_t mpu6050_get_slave_write_mode(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *mode) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return read_reg_bool(dev, MPU6050_REGISTER_I2C_SLV0_CTRL + num * 3, MPU6050_I2C_SLV_REG_DIS_BIT, mode); +} + +esp_err_t mpu6050_set_slave_write_mode(mpu6050_dev_t *dev, mpu6050_slave_t num, bool mode) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return write_reg_bool(dev, MPU6050_REGISTER_I2C_SLV0_CTRL + num * 3, MPU6050_I2C_SLV_REG_DIS_BIT, mode); +} + +esp_err_t mpu6050_get_slave_word_group_offset(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *enabled) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return read_reg_bool(dev, MPU6050_REGISTER_I2C_SLV0_CTRL + num * 3, MPU6050_I2C_SLV_GRP_BIT, enabled); +} + +esp_err_t mpu6050_set_slave_word_group_offset(mpu6050_dev_t *dev, mpu6050_slave_t num, bool enabled) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return write_reg_bool(dev, MPU6050_REGISTER_I2C_SLV0_CTRL + num * 3, MPU6050_I2C_SLV_GRP_BIT, enabled); +} + +esp_err_t mpu6050_get_slave_data_length(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t *length) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return read_reg_bits(dev, MPU6050_REGISTER_I2C_SLV0_CTRL + num * 3, MPU6050_I2C_SLV_LEN_BIT, MPU6050_I2C_SLV_LEN_MASK, length); +} + +esp_err_t mpu6050_set_slave_data_length(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t length) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return write_reg_bits(dev, MPU6050_REGISTER_I2C_SLV0_CTRL + num * 3, MPU6050_I2C_SLV_LEN_BIT, MPU6050_I2C_SLV_LEN_MASK, length); +} + +esp_err_t mpu6050_set_slave_4_output_byte(mpu6050_dev_t *dev, uint8_t data) +{ + return write_reg(dev, MPU6050_REGISTER_I2C_SLV4_DO, data); +} + +esp_err_t mpu6050_get_slave_4_interrupt_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_INT_EN_BIT, enabled); +} + +esp_err_t mpu6050_set_slave_4_interrupt_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_INT_EN_BIT, enabled); +} + +esp_err_t mpu6050_get_slave_4_master_delay(mpu6050_dev_t *dev, uint8_t *delay) +{ + return read_reg_bits(dev, MPU6050_REGISTER_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_MST_DLY_BIT, MPU6050_I2C_SLV4_MST_DLY_LENGTH, delay); +} + +esp_err_t mpu6050_set_slave_4_master_delay(mpu6050_dev_t *dev, uint8_t delay) +{ + return write_reg_bits(dev, MPU6050_REGISTER_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_MST_DLY_BIT, MPU6050_I2C_SLV4_MST_DLY_LENGTH, delay); +} + +esp_err_t mpu6050_get_slave_4_input_byte(mpu6050_dev_t *dev, uint8_t *byte) +{ + return read_reg(dev, MPU6050_REGISTER_I2C_SLV4_DI, byte); +} + +esp_err_t mpu6050_get_passthrough_status(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_I2C_MST_STATUS, MPU6050_MST_PASS_THROUGH_BIT, enabled); +} + +esp_err_t mpu6050_get_slave_4_is_done(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_I2C_MST_STATUS, MPU6050_MST_I2C_SLV4_DONE_BIT, enabled); +} + +esp_err_t mpu6050_get_lost_arbitration(mpu6050_dev_t *dev, bool *lost) +{ + return read_reg_bool(dev, MPU6050_REGISTER_I2C_MST_STATUS, MPU6050_MST_I2C_LOST_ARB_BIT, lost); +} + +esp_err_t mpu6050_get_slave_nack(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *nack) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + static const uint8_t bits[] = { + [MPU6050_SLAVE_0] = MPU6050_MST_I2C_SLV0_NACK_BIT, + [MPU6050_SLAVE_1] = MPU6050_MST_I2C_SLV1_NACK_BIT, + [MPU6050_SLAVE_2] = MPU6050_MST_I2C_SLV2_NACK_BIT, + [MPU6050_SLAVE_3] = MPU6050_MST_I2C_SLV3_NACK_BIT, + [MPU6050_SLAVE_4] = MPU6050_MST_I2C_SLV4_NACK_BIT, + }; + return read_reg_bool(dev, MPU6050_REGISTER_I2C_MST_STATUS, bits[num], nack); +} + +esp_err_t mpu6050_get_interrupt_mode(mpu6050_dev_t *dev, mpu6050_int_level_t *mode) +{ + return read_reg_bits(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_INT_LEVEL_BIT, BIT(MPU6050_INTCFG_INT_LEVEL_BIT), (uint8_t *)mode); +} + +esp_err_t mpu6050_set_interrupt_mode(mpu6050_dev_t *dev, mpu6050_int_level_t mode) +{ + CHECK_ARG(mode <= MPU6050_INT_LEVEL_LOW); + + return write_reg_bits(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_INT_LEVEL_BIT, BIT(MPU6050_INTCFG_INT_LEVEL_BIT), mode); +} + +esp_err_t mpu6050_get_interrupt_drive(mpu6050_dev_t *dev, mpu6050_int_drive_t *drive) +{ + return read_reg_bits(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_INT_OPEN_BIT, BIT(MPU6050_INTCFG_INT_OPEN_BIT), (uint8_t *)drive); +} + +esp_err_t mpu6050_set_interrupt_drive(mpu6050_dev_t *dev, mpu6050_int_drive_t drive) +{ + CHECK_ARG(drive <= MPU6050_INT_OPEN_DRAIN); + + return write_reg_bits(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_INT_OPEN_BIT, BIT(MPU6050_INTCFG_INT_OPEN_BIT), drive); +} + +esp_err_t mpu6050_get_interrupt_latch(mpu6050_dev_t *dev, mpu6050_int_latch_t *latch) +{ + return read_reg_bits(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_LATCH_INT_EN_BIT, BIT(MPU6050_INTCFG_LATCH_INT_EN_BIT), (uint8_t *)latch); +} + +esp_err_t mpu6050_set_interrupt_latch(mpu6050_dev_t *dev, mpu6050_int_latch_t latch) +{ + CHECK_ARG(latch <= MPU6050_INT_LATCH_CONTINUOUS); + + return write_reg_bits(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_LATCH_INT_EN_BIT, BIT(MPU6050_INTCFG_LATCH_INT_EN_BIT), latch); +} + +esp_err_t mpu6050_get_interrupt_latch_clear(mpu6050_dev_t *dev, bool *clear) +{ + return read_reg_bool(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_INT_RD_CLEAR_BIT, clear); +} + +esp_err_t mpu6050_set_interrupt_latch_clear(mpu6050_dev_t *dev, bool clear) +{ + return write_reg_bool(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_INT_RD_CLEAR_BIT, clear); +} + +esp_err_t mpu6050_get_fsync_interrupt_level(mpu6050_dev_t *dev, mpu6050_int_level_t *level) +{ + return read_reg_bits(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT, BIT(MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT), (uint8_t *)level); +} + +esp_err_t mpu6050_set_fsync_interrupt_level(mpu6050_dev_t *dev, mpu6050_int_level_t level) +{ + CHECK_ARG(level <= MPU6050_INT_LEVEL_LOW); + + return write_reg_bits(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT, BIT(MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT), level); +} + +esp_err_t mpu6050_get_fsync_interrupt_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_EN_BIT, enabled); +} + +esp_err_t mpu6050_set_fsync_interrupt_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_EN_BIT, enabled); +} + +esp_err_t mpu6050_get_i2c_bypass_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_I2C_BYPASS_EN_BIT, enabled); +} + +esp_err_t mpu6050_set_i2c_bypass_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_I2C_BYPASS_EN_BIT, enabled); +} + +esp_err_t mpu6050_get_clock_output_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_CLKOUT_EN_BIT, enabled); +} + +esp_err_t mpu6050_set_clock_output_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_INT_PIN_CFG, MPU6050_INTCFG_CLKOUT_EN_BIT, enabled); +} + +esp_err_t mpu6050_get_int_enabled(mpu6050_dev_t *dev, uint8_t *ints) +{ + return read_reg(dev, MPU6050_REGISTER_INT_ENABLE, ints); +} + +esp_err_t mpu6050_set_int_enabled(mpu6050_dev_t *dev, uint8_t ints) +{ + return write_reg(dev, MPU6050_REGISTER_INT_ENABLE, ints); +} + +esp_err_t mpu6050_get_int_status(mpu6050_dev_t *dev, uint8_t *ints) +{ + return read_reg(dev, MPU6050_REGISTER_INT_ENABLE, ints); +} + +static const uint8_t accel_offs_regs[] = { + [MPU6050_X_AXIS] = MPU6050_REGISTER_XA_OFFS_H, + [MPU6050_Y_AXIS] = MPU6050_REGISTER_YA_OFFS_H, + [MPU6050_Z_AXIS] = MPU6050_REGISTER_ZA_OFFS_H, +}; + +esp_err_t mpu6050_get_accel_offset(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t *offset) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return read_reg_word(dev, accel_offs_regs[axis], offset); +} + +esp_err_t mpu6050_set_accel_offset(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t offset) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return write_reg_word(dev, accel_offs_regs[axis], offset); +} + +static const uint8_t gyro_offs_regs[] = { + [MPU6050_X_AXIS] = MPU6050_REGISTER_XG_OFFS_USRH, + [MPU6050_Y_AXIS] = MPU6050_REGISTER_YG_OFFS_USRH, + [MPU6050_Z_AXIS] = MPU6050_REGISTER_ZG_OFFS_USRH, +}; + +esp_err_t mpu6050_get_gyro_offset(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t *offset) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return read_reg_word(dev, gyro_offs_regs[axis], offset); +} + +esp_err_t mpu6050_set_gyro_offset(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t offset) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return write_reg_word(dev, gyro_offs_regs[axis], offset); +} + +esp_err_t mpu6050_get_raw_acceleration(mpu6050_dev_t *dev, mpu6050_raw_acceleration_t *raw_accel) +{ + CHECK_ARG(dev && raw_accel); + + uint16_t buf[3]; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MPU6050_REGISTER_ACCEL_XOUT_H, buf, sizeof(buf))); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + raw_accel->x = shuffle(buf[0]); + raw_accel->y = shuffle(buf[1]); + raw_accel->z = shuffle(buf[2]); + + return ESP_OK; +} + +esp_err_t mpu6050_get_acceleration(mpu6050_dev_t *dev, mpu6050_acceleration_t *accel) +{ + CHECK_ARG(accel); + + mpu6050_raw_acceleration_t raw; + CHECK(mpu6050_get_raw_acceleration(dev, &raw)); + + accel->x = get_accel_value(dev, raw.x); + accel->y = get_accel_value(dev, raw.y); + accel->z = get_accel_value(dev, raw.z); + + return ESP_OK; +} + +esp_err_t mpu6050_get_raw_acceleration_axis(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t *raw_accel) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return read_reg_word(dev, MPU6050_REGISTER_ACCEL_XOUT_H + axis * 2, raw_accel); +} + +esp_err_t mpu6050_get_acceleration_axis(mpu6050_dev_t *dev, mpu6050_axis_t axis, float *accel) +{ + CHECK_ARG(accel); + + int16_t raw; + CHECK(mpu6050_get_raw_acceleration_axis(dev, axis, &raw)); + + *accel = get_accel_value(dev, raw); + + return ESP_OK; +} + +esp_err_t mpu6050_get_raw_rotation(mpu6050_dev_t *dev, mpu6050_raw_rotation_t *raw_gyro) +{ + CHECK_ARG(dev && raw_gyro); + + uint16_t buf[3]; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MPU6050_REGISTER_GYRO_XOUT_H, buf, sizeof(buf))); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + raw_gyro->x = shuffle(buf[0]); + raw_gyro->y = shuffle(buf[1]); + raw_gyro->z = shuffle(buf[2]); + + return ESP_OK; +} + +esp_err_t mpu6050_get_rotation(mpu6050_dev_t *dev, mpu6050_rotation_t *gyro) +{ + CHECK_ARG(gyro); + + mpu6050_raw_rotation_t raw; + CHECK(mpu6050_get_raw_rotation(dev, &raw)); + + gyro->x = get_gyro_value(dev, raw.x); + gyro->y = get_gyro_value(dev, raw.y); + gyro->z = get_gyro_value(dev, raw.z); + + return ESP_OK; +} + +esp_err_t mpu6050_get_raw_rotation_axis(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t *raw_gyro) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return read_reg_word(dev, MPU6050_REGISTER_ACCEL_XOUT_H + axis * 2, raw_gyro); +} + +esp_err_t mpu6050_get_rotation_axis(mpu6050_dev_t *dev, mpu6050_axis_t axis, float *gyro) +{ + CHECK_ARG(gyro); + + int16_t raw; + CHECK(mpu6050_get_raw_rotation_axis(dev, axis, &raw)); + + *gyro = get_gyro_value(dev, raw); + + return ESP_OK; +} + +esp_err_t mpu6050_get_motion(mpu6050_dev_t *dev, mpu6050_acceleration_t *accel, mpu6050_rotation_t *gyro) +{ + CHECK(mpu6050_get_acceleration(dev, accel)); + CHECK(mpu6050_get_rotation(dev, gyro)); + return ESP_OK; +} + +esp_err_t mpu6050_get_temperature(mpu6050_dev_t *dev, float *temp) +{ + CHECK_ARG(temp); + + int16_t raw = 0; + CHECK(read_reg_word(dev, MPU6050_REGISTER_TEMP_OUT_H, &raw)); + *temp = ((float)raw / 340.0f) + 36.53f; + + return ESP_OK; +} + +esp_err_t mpu6050_get_external_sensor_data(mpu6050_dev_t *dev, int position, void *buf, size_t length) +{ + CHECK_ARG(dev && buf && length && position < 24 && position + length - 1 < 24); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MPU6050_REGISTER_EXT_SENS_DATA_00 + position, buf, length)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +esp_err_t mpu6050_get_motion_status(mpu6050_dev_t *dev, uint8_t *status) +{ + return read_reg(dev, MPU6050_REGISTER_MOT_DETECT_STATUS, status); +} + +esp_err_t mpu6050_set_slave_output_byte(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t data) +{ + CHECK_ARG(num < MPU6050_SLAVE_4); + + return write_reg(dev, MPU6050_REGISTER_I2C_SLV0_DO + num, data); +} + +esp_err_t mpu6050_get_external_shadow_delay_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_I2C_MST_DELAY_CTRL, MPU6050_DLYCTRL_DELAY_ES_SHADOW_BIT, enabled); +} + +esp_err_t mpu6050_set_external_shadow_delay_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_I2C_MST_DELAY_CTRL, MPU6050_DLYCTRL_DELAY_ES_SHADOW_BIT, enabled); +} + +esp_err_t mpu6050_get_slave_delay_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *enabled) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return read_reg_bool(dev, MPU6050_REGISTER_I2C_MST_DELAY_CTRL, num, enabled); +} + +esp_err_t mpu6050_set_slave_delay_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool enabled) +{ + CHECK_ARG(num <= MPU6050_SLAVE_4); + + return write_reg_bool(dev, MPU6050_REGISTER_I2C_MST_DELAY_CTRL, num, enabled); +} + +esp_err_t mpu6050_reset_gyroscope_path(mpu6050_dev_t *dev) +{ + return write_reg_bool(dev, MPU6050_REGISTER_SIGNAL_PATH_RESET, MPU6050_PATHRESET_GYRO_RESET_BIT, 1); +} + +esp_err_t mpu6050_reset_accelerometer_path(mpu6050_dev_t *dev) +{ + return write_reg_bool(dev, MPU6050_REGISTER_SIGNAL_PATH_RESET, MPU6050_PATHRESET_ACCEL_RESET_BIT, 1); +} + +esp_err_t mpu6050_reset_temperature_path(mpu6050_dev_t *dev) +{ + return write_reg_bool(dev, MPU6050_REGISTER_SIGNAL_PATH_RESET, MPU6050_PATHRESET_TEMP_RESET_BIT, 1); +} + +esp_err_t mpu6050_get_accelerometer_power_on_delay(mpu6050_dev_t *dev, uint8_t *delay) +{ + return read_reg_bits(dev, MPU6050_REGISTER_MOT_DETECT_CTRL, MPU6050_DETECT_ACCEL_DELAY_BIT, MPU6050_DETECT_ACCEL_DELAY_MASK, delay); +} + +esp_err_t mpu6050_set_accelerometer_power_on_delay(mpu6050_dev_t *dev, uint8_t delay) +{ + return write_reg_bits(dev, MPU6050_REGISTER_MOT_DETECT_CTRL, MPU6050_DETECT_ACCEL_DELAY_BIT, MPU6050_DETECT_ACCEL_DELAY_MASK, delay); +} + +esp_err_t mpu6050_get_freefall_detection_counter_decrement(mpu6050_dev_t *dev, uint8_t *decrement) +{ + return read_reg_bits(dev, MPU6050_REGISTER_MOT_DETECT_CTRL, MPU6050_DETECT_FF_COUNT_BIT, MPU6050_DETECT_FF_COUNT_MASK, decrement); +} + +esp_err_t mpu6050_set_freefall_detection_counter_decrement(mpu6050_dev_t *dev, uint8_t decrement) +{ + return write_reg_bits(dev, MPU6050_REGISTER_MOT_DETECT_CTRL, MPU6050_DETECT_FF_COUNT_BIT, MPU6050_DETECT_FF_COUNT_MASK, decrement); +} + +esp_err_t mpu6050_get_motion_detection_counter_decrement(mpu6050_dev_t *dev, uint8_t *decrement) +{ + return read_reg_bits(dev, MPU6050_REGISTER_MOT_DETECT_CTRL, MPU6050_DETECT_MOT_COUNT_BIT, MPU6050_DETECT_MOT_COUNT_MASK, decrement); +} + +esp_err_t mpu6050_set_motion_detection_counter_decrement(mpu6050_dev_t *dev, uint8_t decrement) +{ + return write_reg_bits(dev, MPU6050_REGISTER_MOT_DETECT_CTRL, MPU6050_DETECT_MOT_COUNT_BIT, MPU6050_DETECT_MOT_COUNT_MASK, decrement); +} + +esp_err_t mpu6050_get_fifo_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_USER_CTRL, MPU6050_USERCTRL_FIFO_EN_BIT, enabled); +} + +esp_err_t mpu6050_set_fifo_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_USER_CTRL, MPU6050_USERCTRL_FIFO_EN_BIT, enabled); +} + +esp_err_t mpu6050_get_i2c_master_mode_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_USER_CTRL, MPU6050_USERCTRL_I2C_MST_EN_BIT, enabled); +} + +esp_err_t mpu6050_set_i2c_master_mode_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_USER_CTRL, MPU6050_USERCTRL_I2C_MST_EN_BIT, enabled); +} + +esp_err_t mpu6050_switch_spie_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_USER_CTRL, MPU6050_USERCTRL_I2C_IF_DIS_BIT, enabled); +} + +esp_err_t mpu6050_reset_fifo(mpu6050_dev_t *dev) +{ + return write_reg_bool(dev, MPU6050_REGISTER_USER_CTRL, MPU6050_USERCTRL_FIFO_RESET_BIT, 1); +} + +esp_err_t mpu6050_reset_sensors(mpu6050_dev_t *dev) +{ + return write_reg_bool(dev, MPU6050_REGISTER_USER_CTRL, MPU6050_USERCTRL_SIG_COND_RESET_BIT, 1); +} + +esp_err_t mpu6050_reset(mpu6050_dev_t *dev) +{ + return write_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_1, MPU6050_PWR1_DEVICE_RESET_BIT, 1); +} + +esp_err_t mpu6050_get_sleep_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, enabled); +} + +esp_err_t mpu6050_set_sleep_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, enabled); +} + +esp_err_t mpu6050_get_wake_cycle_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + return read_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_1, MPU6050_PWR1_CYCLE_BIT, enabled); +} + +esp_err_t mpu6050_set_wake_cycle_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_1, MPU6050_PWR1_CYCLE_BIT, enabled); +} + +esp_err_t mpu6050_get_temp_sensor_enabled(mpu6050_dev_t *dev, bool *enabled) +{ + CHECK(read_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_1, MPU6050_PWR1_TEMP_DIS_BIT, enabled)); + *enabled = !*enabled; + + return ESP_OK; +} + +esp_err_t mpu6050_set_temp_sensor_enabled(mpu6050_dev_t *dev, bool enabled) +{ + return write_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_1, MPU6050_PWR1_TEMP_DIS_BIT, !enabled); +} + +esp_err_t mpu6050_get_clock_source(mpu6050_dev_t *dev, mpu6050_clock_source_t *source) +{ + return read_reg_bits(dev, MPU6050_REGISTER_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_MASK, (uint8_t *)source); +} + +esp_err_t mpu6050_set_clock_source(mpu6050_dev_t *dev, mpu6050_clock_source_t source) +{ + return write_reg_bits(dev, MPU6050_REGISTER_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_MASK, source); +} + +esp_err_t mpu6050_get_wake_frequency(mpu6050_dev_t *dev, mpu6050_wake_freq_t *frequency) +{ + return read_reg_bits(dev, MPU6050_REGISTER_PWR_MGMT_2, MPU6050_PWR2_LP_WAKE_CTRL_BIT, MPU6050_PWR2_LP_WAKE_CTRL_MASK, (uint8_t *)frequency); +} + +esp_err_t mpu6050_set_wake_frequency(mpu6050_dev_t *dev, mpu6050_wake_freq_t frequency) +{ + return write_reg_bits(dev, MPU6050_REGISTER_PWR_MGMT_2, MPU6050_PWR2_LP_WAKE_CTRL_BIT, MPU6050_PWR2_LP_WAKE_CTRL_MASK, frequency); +} + +static const uint8_t standby_accel_bits[] = { + [MPU6050_X_AXIS] = MPU6050_PWR2_STBY_XA_BIT, + [MPU6050_Y_AXIS] = MPU6050_PWR2_STBY_YA_BIT, + [MPU6050_Z_AXIS] = MPU6050_PWR2_STBY_ZA_BIT, +}; + +esp_err_t mpu6050_get_standby_accel_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool *enabled) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return read_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_2, standby_accel_bits[axis], enabled); +} + +esp_err_t mpu6050_set_standby_accel_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool enabled) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return write_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_2, standby_accel_bits[axis], enabled); +} + +static const uint8_t standby_gyro_bits[] = { + [MPU6050_X_AXIS] = MPU6050_PWR2_STBY_XG_BIT, + [MPU6050_Y_AXIS] = MPU6050_PWR2_STBY_YG_BIT, + [MPU6050_Z_AXIS] = MPU6050_PWR2_STBY_ZG_BIT, +}; + +esp_err_t mpu6050_get_standby_gyro_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool *enabled) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return read_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_2, standby_gyro_bits[axis], enabled); +} + +esp_err_t mpu6050_set_standby_gyro_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool enabled) +{ + CHECK_ARG(axis <= MPU6050_Z_AXIS); + + return write_reg_bool(dev, MPU6050_REGISTER_PWR_MGMT_2, standby_gyro_bits[axis], enabled); +} + +esp_err_t mpu6050_get_fifo_count(mpu6050_dev_t *dev, uint16_t *count) +{ + CHECK_ARG(dev && count); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MPU6050_REGISTER_FIFO_COUNTH, count, 2)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + *count = ushuffle(*count); + return ESP_OK; +} + +esp_err_t mpu6050_get_fifo_bytes(mpu6050_dev_t *dev, uint8_t *data, size_t length) +{ + CHECK_ARG(dev && data && length); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, MPU6050_REGISTER_FIFO_COUNTH, data, length)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +esp_err_t mpu6050_get_fifo_byte(mpu6050_dev_t *dev, uint8_t *data) +{ + return read_reg(dev, MPU6050_REGISTER_FIFO_R_W, data); +} + +esp_err_t mpu6050_set_fifo_byte(mpu6050_dev_t *dev, uint8_t data) +{ + return write_reg(dev, MPU6050_REGISTER_FIFO_R_W, data); +} + +esp_err_t mpu6050_get_device_id(mpu6050_dev_t *dev, uint8_t *id) +{ + return read_reg_bits(dev, MPU6050_REGISTER_WHO_AM_I, MPU6050_WHO_AM_I_BIT, MPU6050_WHO_AM_I_MASK, id); +} + +esp_err_t mpu6050_calibrate(mpu6050_dev_t *dev, float *accel_bias_res, float *gyro_bias_res) +{ + CHECK_ARG(dev && accel_bias_res && gyro_bias_res); + + int16_t temp_offset[3]; + int32_t accel_bias[3] = { 0, 0, 0 }; + int32_t gyro_bias[3] = { 0, 0, 0 }; + int32_t accel_bias_reg[3] = { 0, 0, 0 }; + uint16_t accel_temp[3] = { 0, 0, 0 }; + uint16_t gyro_temp[3] = { 0, 0, 0 }; + uint8_t mask_bit[3] = { 0, 0, 0 }; + uint32_t mask = 1uL; + uint16_t gyro_sensitivity = 131; + uint16_t accel_sensitivity = 16384; + uint8_t tmp_data[12]; + uint16_t packet_count; + + CHECK(mpu6050_reset(dev)); + vTaskDelay(pdMS_TO_TICKS(100)); + + CHECK(mpu6050_set_clock_source(dev, MPU6050_CLOCK_PLL_X)); + vTaskDelay(pdMS_TO_TICKS(200)); + + // Configure device for bias calculation: + CHECK(mpu6050_set_int_enabled(dev, false)); + CHECK(mpu6050_set_fifo_enabled(dev, false)); + CHECK(mpu6050_set_accel_fifo_enabled(dev, false)); + CHECK(mpu6050_set_gyro_fifo_enabled(dev, MPU6050_Z_AXIS, false)); + CHECK(mpu6050_set_gyro_fifo_enabled(dev, MPU6050_X_AXIS, false)); + CHECK(mpu6050_set_gyro_fifo_enabled(dev, MPU6050_Y_AXIS, false)); + CHECK(mpu6050_set_temp_fifo_enabled(dev, false)); + CHECK(mpu6050_set_clock_source(dev, MPU6050_CLOCK_INTERNAL)); + CHECK(mpu6050_set_multi_master_enabled(dev, false)); + CHECK(mpu6050_set_fifo_enabled(dev, false)); + CHECK(mpu6050_set_i2c_master_mode_enabled(dev, false)); + CHECK(mpu6050_reset_sensors(dev)); + vTaskDelay(pdMS_TO_TICKS(15)); + + // Configure MPU6050 gyro and accelerometer for bias calculation: + CHECK(mpu6050_set_rate(dev, 0)); // Set sample rate to 1 kHz. + CHECK(mpu6050_set_dlpf_mode(dev, MPU6050_DLPF_1)); + CHECK(mpu6050_set_full_scale_accel_range(dev, MPU6050_ACCEL_RANGE_2)); + CHECK(mpu6050_set_full_scale_gyro_range(dev, MPU6050_GYRO_RANGE_250)); + + /** + * Configure FIFO to capture data for bias calculation. + */ + + // Enable gyroscope and accelerometer sensors for FIFO: + CHECK(mpu6050_set_fifo_enabled(dev, true)); + CHECK(mpu6050_set_accel_fifo_enabled(dev, true)); + CHECK(mpu6050_set_gyro_fifo_enabled(dev, MPU6050_Z_AXIS, true)); + CHECK(mpu6050_set_gyro_fifo_enabled(dev, MPU6050_X_AXIS, true)); + CHECK(mpu6050_set_gyro_fifo_enabled(dev, MPU6050_Y_AXIS, true)); + vTaskDelay(pdMS_TO_TICKS(80)); // Accumulate 80 samples in 80 ms. + + // At end of sample accumulation, turn off FIFO sensor read: + CHECK(mpu6050_set_fifo_enabled(dev, false)); + CHECK(mpu6050_set_accel_fifo_enabled(dev, false)); + CHECK(mpu6050_set_gyro_fifo_enabled(dev, MPU6050_Z_AXIS, false)); + CHECK(mpu6050_set_gyro_fifo_enabled(dev, MPU6050_X_AXIS, false)); + CHECK(mpu6050_set_gyro_fifo_enabled(dev, MPU6050_Y_AXIS, false)); + CHECK(mpu6050_set_temp_fifo_enabled(dev, false)); + + // Sets of full gyro and accelerometer data for averaging: + CHECK(mpu6050_get_fifo_count(dev, &packet_count)); + packet_count /= 12; + + for (int i = 0; i < packet_count; i++) + { + // Read data for averaging: + CHECK(mpu6050_get_fifo_bytes(dev, &tmp_data[0], 6)); + accel_temp[0] = (int16_t)(((int16_t)tmp_data[0] << 8) | tmp_data[1]); + accel_temp[1] = (int16_t)(((int16_t)tmp_data[2] << 8) | tmp_data[3]); + accel_temp[2] = (int16_t)(((int16_t)tmp_data[4] << 8) | tmp_data[5]); + gyro_temp[0] = (int16_t)(((int16_t)tmp_data[6] << 8) | tmp_data[7]); + gyro_temp[1] = (int16_t)(((int16_t)tmp_data[8] << 8) | tmp_data[9]); + gyro_temp[2] = (int16_t)(((int16_t)tmp_data[10] << 8) | tmp_data[11]); + + // Sum individual 16-bit biases to get accumulated signed 32-bit biases: + accel_bias[0] += (int32_t)accel_temp[0]; + accel_bias[1] += (int32_t)accel_temp[1]; + accel_bias[2] += (int32_t)accel_temp[2]; + gyro_bias[0] += (int32_t)gyro_temp[0]; + gyro_bias[1] += (int32_t)gyro_temp[1]; + gyro_bias[2] += (int32_t)gyro_temp[2]; + } + + // Normalize sums to get average count biases: + accel_bias[0] /= (int32_t)packet_count; + accel_bias[1] /= (int32_t)packet_count; + accel_bias[2] /= (int32_t)packet_count; + gyro_bias[0] /= (int32_t)packet_count; + gyro_bias[1] /= (int32_t)packet_count; + gyro_bias[2] /= (int32_t)packet_count; + + // Remove gravity from the z-axis accelerometer bias calculation: + if (accel_bias[2] > 0L) + accel_bias[2] -= (int32_t)accel_sensitivity; + else + accel_bias[2] += (int32_t)accel_sensitivity; + + /** + * Construct the gyro biases for push to the hardware gyro bias registers, + * which are reset to zero upon device startup: + */ + + // Divide by 4 to get 32.9 LSB per deg/s to expected bias input format. + // Biases are additive, so change sign on calculated average gyro biases. + tmp_data[0] = (-gyro_bias[0] / 4 >> 8) & 0xFF; + tmp_data[1] = (-gyro_bias[0] / 4) & 0xFF; + tmp_data[2] = (-gyro_bias[1] / 4 >> 8) & 0xFF; + tmp_data[3] = (-gyro_bias[1] / 4) & 0xFF; + tmp_data[4] = (-gyro_bias[2] / 4 >> 8) & 0xFF; + tmp_data[5] = (-gyro_bias[2] / 4) & 0xFF; + + // Push gyro biases to hardware registers: + CHECK(mpu6050_set_gyro_offset(dev, MPU6050_X_AXIS, ((int16_t)tmp_data[0]) << 8 | tmp_data[1])); + CHECK(mpu6050_set_gyro_offset(dev, MPU6050_Y_AXIS, ((int16_t)tmp_data[2]) << 8 | tmp_data[3])); + CHECK(mpu6050_set_gyro_offset(dev, MPU6050_Z_AXIS, ((int16_t)tmp_data[4]) << 8 | tmp_data[5])); + + // Construct gyro bias in deg/s for later manual subtraction: + gyro_bias_res[0] = (float)gyro_bias[0] / (float)gyro_sensitivity; + gyro_bias_res[1] = (float)gyro_bias[1] / (float)gyro_sensitivity; + gyro_bias_res[2] = (float)gyro_bias[2] / (float)gyro_sensitivity; + + /** + * Construct the accelerometer biases for push to the hardware accelerometer + * bias registers. These registers contain factory trim values which must be + * added to the calculated accelerometer biases; on boot up these registers + * will hold non-zero values. In addition, bit 0 of the lower byte must be + * preserved since it is used for temperature compensation calculations. + * Accelerometer bias registers expect bias input as 2048 LSB per g, so that + * the accelerometer biases calculated above must be divided by 8. + */ + + // Read factory accelerometer trim values: + CHECK(mpu6050_get_accel_offset(dev, MPU6050_X_AXIS, &(temp_offset[0]))); + CHECK(mpu6050_get_accel_offset(dev, MPU6050_Y_AXIS, &(temp_offset[1]))); + CHECK(mpu6050_get_accel_offset(dev, MPU6050_Z_AXIS, &(temp_offset[2]))); + + for (int i = 0; i < 3; i++) + { + // If temperature compensation bit is set, record that in mask_bit: + if (accel_bias_reg[i] & mask) + mask_bit[i] = 0x01; + } + + /** + * Construct total accelerometer bias, including calculated average + * accelerometer bias from above (Subtract calculated averaged accelerometer + * bias scaled to 2048 LSB/g (16g full scale). + */ + + accel_bias_reg[0] -= (accel_bias[0] / 8); + accel_bias_reg[1] -= (accel_bias[1] / 8); + accel_bias_reg[2] -= (accel_bias[2] / 8); + + tmp_data[0] = (accel_bias_reg[0] >> 8) & 0xFF; + tmp_data[1] = (accel_bias_reg[0]) & 0xFF; + tmp_data[1] = tmp_data[1] | mask_bit[0]; + tmp_data[2] = (accel_bias_reg[1] >> 8) & 0xFF; + tmp_data[3] = (accel_bias_reg[1]) & 0xFF; + tmp_data[3] = tmp_data[3] | mask_bit[1]; + tmp_data[4] = (accel_bias_reg[2] >> 8) & 0xFF; + tmp_data[5] = (accel_bias_reg[2]) & 0xFF; + tmp_data[5] = tmp_data[5] | mask_bit[2]; + + // Push accelerometer biases to hardware registers: + CHECK(mpu6050_set_accel_offset(dev, MPU6050_X_AXIS, ((int16_t)tmp_data[0]) << 8 | tmp_data[1])); + CHECK(mpu6050_set_accel_offset(dev, MPU6050_X_AXIS, ((int16_t)tmp_data[2]) << 8 | tmp_data[3])); + CHECK(mpu6050_set_accel_offset(dev, MPU6050_X_AXIS, ((int16_t)tmp_data[4]) << 8 | tmp_data[5])); + + // Output scaled accelerometer biases for subtraction in the main program: + accel_bias_res[0] = (float)accel_bias[0] / (float)accel_sensitivity; + accel_bias_res[1] = (float)accel_bias[1] / (float)accel_sensitivity; + accel_bias_res[2] = (float)accel_bias[2] / (float)accel_sensitivity; + + return ESP_OK; +} + +esp_err_t mpu6050_self_test(mpu6050_dev_t *dev, float *destination) +{ + uint8_t self_test[6]; + float factory_trim[6]; + + // Configure the accelerometer for self-test: + CHECK(mpu6050_set_accel_self_test(dev, MPU6050_X_AXIS, true)); + CHECK(mpu6050_set_accel_self_test(dev, MPU6050_Y_AXIS, true)); + CHECK(mpu6050_set_accel_self_test(dev, MPU6050_Z_AXIS, true)); + CHECK(mpu6050_set_full_scale_accel_range(dev, MPU6050_ACCEL_RANGE_8)); + CHECK(mpu6050_set_full_scale_gyro_range(dev, MPU6050_GYRO_RANGE_250)); + + CHECK(mpu6050_get_accel_self_test_factory_trim(dev, MPU6050_X_AXIS, &self_test[0])); + CHECK(mpu6050_get_accel_self_test_factory_trim(dev, MPU6050_Y_AXIS, &self_test[1])); + CHECK(mpu6050_get_accel_self_test_factory_trim(dev, MPU6050_Z_AXIS, &self_test[2])); + CHECK(mpu6050_get_gyro_self_test_factory_trim(dev, MPU6050_X_AXIS, &self_test[3])); + CHECK(mpu6050_get_gyro_self_test_factory_trim(dev, MPU6050_Y_AXIS, &self_test[4])); + CHECK(mpu6050_get_gyro_self_test_factory_trim(dev, MPU6050_Z_AXIS, &self_test[5])); + + // Process results to allow final comparison with factory set values: + factory_trim[0] = (4096.0f * 0.34f) * (powf((0.92f / 0.34f), (((float)self_test[0] - 1.0f) / 30.0f))); + factory_trim[1] = (4096.0f * 0.34f) * (powf((0.92f / 0.34f), (((float)self_test[1] - 1.0f) / 30.0f))); + factory_trim[2] = (4096.0f * 0.34f) * (powf((0.92f / 0.34f), (((float)self_test[2] - 1.0f) / 30.0f))); + factory_trim[3] = (25.0f * 131.0f) * (powf(1.046f, ((float)self_test[3] - 1.0f))); + factory_trim[4] = (-25.0f * 131.0f) * (powf(1.046f, ((float)self_test[4] - 1.0f))); + factory_trim[5] = (25.0f * 131.0f) * (powf(1.046f, ((float)self_test[5] - 1.0f))); + + // Report results as a ratio of "(STR - FT) / FT" (The change from Factory Trim of the Self-Test Response). + // To get to percent, must multiply by 100 and subtract result from 100. + for (int i = 0; i < 6; i++) + destination[i] = 100.0f + 100.0f * ((float)self_test[i] - factory_trim[i]) / factory_trim[i]; + return ESP_OK; +} diff --git a/components/mpu6050/mpu6050.h b/components/mpu6050/mpu6050.h new file mode 100644 index 00000000..28a0c3ef --- /dev/null +++ b/components/mpu6050/mpu6050.h @@ -0,0 +1,2412 @@ +/* + * The MIT License (MIT) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file mpu6050.h + * @defgroup mpu6050 mpu6050 + * @{ + * ESP-IDF driver for MPU6050 MEMS Sensor. + * + * 6-axis motion tracking devices designed for the low power, low cost, + * and high performance requirements of smartphones, tablets and wearable sensors. + * + * Copyright (c) 2012 Jeff Rowberg + * Copyright (c) 2019 Angelo Elias Dalzotto <150633@upf.br> + * Copyright (c) 2019 Gabriel Boni Vicari <133192@upf.br> + * Copyright (c) 2019 GEPID - Grupo de Pesquisa em Cultura Digital + * Copyright (c) 2023 Raghav Jha + * Copyright (c) 2023 Ruslan V. Uss + */ +#ifndef __MPU6050_H__ +#define __MPU6050_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Address of MPU6050 (Can be 0x68 or 0x69): +#define MPU6050_I2C_ADDRESS_LOW (0x68) // Address pin low (GND). +#define MPU6050_I2C_ADDRESS_HIGH (0x69) // Address pin high (VCC). + +/** + * Raw acceleration data + */ +typedef struct +{ + int16_t x; //!< raw acceleration axis x + int16_t y; //!< raw acceleration axis y + int16_t z; //!< raw acceleration axis z +} mpu6050_raw_acceleration_t; + +/** + * Raw rotation data + */ +typedef struct +{ + int16_t x; //!< raw rotation axis x + int16_t y; //!< raw rotation axis y + int16_t z; //!< raw rotation axis z +} mpu6050_raw_rotation_t; + +/** + * MPU6050 acceleration data, g + */ +typedef struct +{ + float x; //!< acceleration axis x + float y; //!< acceleration axis y + float z; //!< acceleration axis z +} mpu6050_acceleration_t; + +/** + * MPU6050 rotation data, °/s + */ +typedef struct +{ + float x; //!< rotation axis x + float y; //!< rotation axis y + float z; //!< rotation axis z +} mpu6050_rotation_t; + +/** + * Auxiliary I2C supply voltage levels + */ +typedef enum { + MPU6050_VDDIO_VLOGIC = 0, + MPU6050_VDDIO_VDD, +} mpu6050_vddio_level_t; + +/** + * Axes + */ +typedef enum { + MPU6050_X_AXIS = 0, + MPU6050_Y_AXIS, + MPU6050_Z_AXIS, +} mpu6050_axis_t; + +/** + * Clock sources + */ +typedef enum { + MPU6050_CLOCK_INTERNAL = 0, //!< Internal oscillator + MPU6050_CLOCK_PLL_X, //!< PLL with X Gyro reference + MPU6050_CLOCK_PLL_Y, //!< PLL with Y Gyro reference + MPU6050_CLOCK_PLL_Z, //!< PLL with Z Gyro reference + MPU6050_CLOCK_EXT_32768HZ, //!< PLL with external 32.768kHz reference + MPU6050_CLOCK_EXT_19_2MHZ, //!< PLL with external 19.2MHz reference + MPU6050_CLOCK_RESERVED, + MPU6050_CLOCK_STOP //!< Stops the clock and keeps the timing generator in reset +} mpu6050_clock_source_t; + +/** + * Interrupt sources + */ +typedef enum { + MPU6050_INT_DATA_READY = BIT(0), //!< Data Ready interrupt which occurs each time a write operation to all of the sensor registers has been completed + MPU6050_INT_DMP = BIT(1), //!< Undocumented + MPU6050_INT_PLL_READY = BIT(2), //!< Undocumented + MPU6050_INT_I2C_MASTER = BIT(3), //!< Any of the I2C Master interrupt sources + MPU6050_INT_FIFO_OFLOW = BIT(4), //!< FIFO buffer overflow interrupt + MPU6050_INT_ZERO_MOTION = BIT(5), //!< Zero motion detection interrupt + MPU6050_INT_MOTION = BIT(6), //!< Motion detection interrupt + MPU6050_INT_FREEFALL = BIT(7), //!< Freefall detection interrupt +} mpu6050_int_source_t; + +/** + * INT pin modes + */ +typedef enum { + MPU6050_INT_PUSH_PULL = 0, //!< Push-pull + MPU6050_INT_OPEN_DRAIN, //!< Open drain +} mpu6050_int_drive_t; + +/** + * Location of the frame synchronization sampled bit + */ +typedef enum { + MPU6050_EXT_SYNC_DISABLED = 0, + MPU6050_EXT_SYNC_TEMP_OUT, + MPU6050_EXT_SYNC_GYRO_XOUT, + MPU6050_EXT_SYNC_GYRO_YOUT, + MPU6050_EXT_SYNC_GYRO_ZOUT, + MPU6050_EXT_SYNC_ACCEL_XOUT, + MPU6050_EXT_SYNC_ACCEL_YOUT, + MPU6050_EXT_SYNC_ACCEL_ZOUT, +} mpu6050_ext_sync_t; + +/** + * Gyroscope and accelerometer filter values + */ +typedef enum { + MPU6050_DLPF_0 = 0, //!< Accelerometer: BW = 260Hz, delay = 0, Gyroscope: BW = 256Hz, delay = 0.98ms, Fs = 8kHz + MPU6050_DLPF_1, //!< Accelerometer: BW = 184z, delay = 2ms, Gyroscope: BW = 188Hz, delay = 1.9ms, Fs = 1kHz + MPU6050_DLPF_2, //!< Accelerometer: BW = 94Hz, delay = 3ms, Gyroscope: BW = 98Hz, delay = 2.8ms, Fs = 1kHz + MPU6050_DLPF_3, //!< Accelerometer: BW = 44Hz, delay = 4.9ms, Gyroscope: BW = 42Hz, delay = 4.8ms, Fs = 1kHz + MPU6050_DLPF_4, //!< Accelerometer: BW = 21Hz, delay = 8.5ms, Gyroscope: BW = 20Hz, delay = 8.3ms, Fs = 1kHz + MPU6050_DLPF_5, //!< Accelerometer: BW = 10Hz, delay = 13.8ms, Gyroscope: BW = 10Hz, delay = 13.4ms, Fs = 1kHz + MPU6050_DLPF_6, //!< Accelerometer: BW = 5Hz, delay = 19.0ms, Gyroscope: BW = 5Hz, delay = 18.6ms, Fs = 1kHz +} mpu6050_dlpf_mode_t; + +/** + * Scale ranges for gyroscope + */ +typedef enum { + MPU6050_GYRO_RANGE_250 = 0, //!< ± 250 °/s + MPU6050_GYRO_RANGE_500, //!< ± 500 °/s + MPU6050_GYRO_RANGE_1000, //!< ± 1000 °/s + MPU6050_GYRO_RANGE_2000, //!< ± 2000 °/s +} mpu6050_gyro_range_t; + +/** + * Scale ranges for accelerometer + */ +typedef enum { + MPU6050_ACCEL_RANGE_2 = 0, //!< ± 2g + MPU6050_ACCEL_RANGE_4, //!< ± 4g + MPU6050_ACCEL_RANGE_8, //!< ± 8g + MPU6050_ACCEL_RANGE_16, //!< ± 16g +} mpu6050_accel_range_t; + +/** + * Digital high pass filter modes + */ +typedef enum { + MPU6050_DHPF_RESET = 0, //!< Filter Mode = reset, Cut-off Frequency = None + MPU6050_DHPF_5, //!< Filter Mode = on, Cut-off Frequency = 5Hz + MPU6050_DHPF_2_5, //!< Filter Mode = on, Cut-off Frequency = 2.5Hz + MPU6050_DHPF_1_25, //!< Filter Mode = on, Cut-off Frequency = 1.25Hz + MPU6050_DHPF_0_63, //!< Filter Mode = on, Cut-off Frequency = 0.63Hz + MPU6050_DHPF_HOLD, //!< Filter Mode = hold, Cut-off Frequency = None +} mpu6050_dhpf_mode_t; + +/** + * I2C slave numbers + */ +typedef enum { + MPU6050_SLAVE_0 = 0, + MPU6050_SLAVE_1, + MPU6050_SLAVE_2, + MPU6050_SLAVE_3, + MPU6050_SLAVE_4, +} mpu6050_slave_t; + +/** + * I2C master clock + */ +typedef enum { + MPU6050_I2C_MASTER_CLOCK_348 = 0, //!< 348kHz + MPU6050_I2C_MASTER_CLOCK_333, //!< 333kHz + MPU6050_I2C_MASTER_CLOCK_320, //!< 320kHz + MPU6050_I2C_MASTER_CLOCK_308, //!< 308kHz + MPU6050_I2C_MASTER_CLOCK_296, //!< 296kHz + MPU6050_I2C_MASTER_CLOCK_286, //!< 286kHz + MPU6050_I2C_MASTER_CLOCK_276, //!< 276kHz + MPU6050_I2C_MASTER_CLOCK_267, //!< 267kHz + MPU6050_I2C_MASTER_CLOCK_258, //!< 258kHz + MPU6050_I2C_MASTER_CLOCK_500, //!< 500kHz + MPU6050_I2C_MASTER_CLOCK_471, //!< 471kHz + MPU6050_I2C_MASTER_CLOCK_444, //!< 444kHz + MPU6050_I2C_MASTER_CLOCK_421, //!< 421kHz + MPU6050_I2C_MASTER_CLOCK_400, //!< 400kHz + MPU6050_I2C_MASTER_CLOCK_381, //!< 381kHz + MPU6050_I2C_MASTER_CLOCK_364, //!< 364kHz +} mpu6050_i2c_master_clock_t; + +/** + * Interrupt levels + */ +typedef enum { + MPU6050_INT_LEVEL_HIGH = 0, //!< Active high + MPU6050_INT_LEVEL_LOW, //!< Active low +} mpu6050_int_level_t; + +/** + * Interrupt latch modes + */ +typedef enum { + MPU6050_INT_LATCH_PULSE = 0, //!< 50 us pulse + MPU6050_INT_LATCH_CONTINUOUS, //!< Latch until cleared +} mpu6050_int_latch_t; + +/** + * The frequencies of wake-ups in Accelerometer Only Low Power Mode + */ +typedef enum { + MPU6050_WAKE_FREQ_1_25 = 0, //!< 1.25Hz + MPU6050_WAKE_FREQ_5, //!< 5Hz + MPU6050_WAKE_FREQ_20, //!< 20Hz + MPU6050_WAKE_FREQ_40, //!< 40Hz +} mpu6050_wake_freq_t; + +/** + * Motion detection status flags + */ +typedef enum { + MPU6050_MOTION_ZERO = BIT(0), + MPU6050_MOTION_Z_POS = BIT(2), + MPU6050_MOTION_Z_NEG = BIT(3), + MPU6050_MOTION_Y_POS = BIT(4), + MPU6050_MOTION_Y_NEG = BIT(5), + MPU6050_MOTION_X_POS = BIT(6), + MPU6050_MOTION_X_NEG = BIT(7), +} mpu6050_motion_det_flags_t; + +/** + * Device descriptor + */ +typedef struct { + i2c_dev_t i2c_dev; + struct + { + mpu6050_gyro_range_t gyro; + mpu6050_accel_range_t accel; + } ranges; +} mpu6050_dev_t; + +/** + * @brief Initialize device descriptor. + * + * @param dev Device descriptor + * @param addr Device I2C address + * @param port I2C port + * @param sda_gpio SDA GPIO + * @param scl_gpio SCL GPIO + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_init_desc(mpu6050_dev_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor. + * + * @param dev Device descriptor + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_free_desc(mpu6050_dev_t *dev); + +/** + * @brief Initialize device. + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_init(mpu6050_dev_t *dev); + +/** + * @brief Get the auxiliary I2C supply voltage level. + * + * When set to 1, the auxiliary I2C bus high logic level is VDD. When cleared to + * 0, the auxiliary I2C bus high logic level is VLOGIC. This does not apply to + * the MPU-6000, which does not have a VLOGIC pin. + * + * @param dev Device descriptor + * @param[out] level I2C supply voltage level + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_aux_vddio_level(mpu6050_dev_t *dev, mpu6050_vddio_level_t *level); + +/** + * @brief Set the auxiliary I2C supply voltage level. + * + * When set to 1, the auxiliary I2C bus high logic level is VDD. When cleared to + * 0, the auxiliary I2C bus high logic level is VLOGIC. This does not apply to + * the MPU6000, which does not have a VLOGIC pin. + * + * @param dev Device descriptor + * @param level I2C supply voltage level (0 = VLOGIC, 1 = VDD). + * @return `ESP_OK` on success + * + */ +esp_err_t mpu6050_set_aux_vddio_level(mpu6050_dev_t *dev, mpu6050_vddio_level_t level); + +/** + * @brief Get gyroscope output rate divider. + * + * The sensor register output, FIFO output, DMP sampling, Motion Detection, + * Zero Motion Detection and Free Fall Detection are all based on the Sample Rate. + * The Sample Rate is generated by dividing the gyroscope output rate by + * SMPLRT_DIV: + * + * Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV) + * + * Where Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or + * 7), and 1kHz when the DLPF is enabled. + * + * Note: The accelerometer output rate is 1kHz. This means that for a Sample + * Rate greater than 1kHz, the same accelerometer sample may be output to the + * FIFO, DMP, and sensor registers more than once. + * + * @param dev Device descriptor + * @param[out] rate: accelerometer sample rate + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_rate(mpu6050_dev_t *dev, uint8_t *rate); + +/** + * @brief Set gyroscope output rate divider. + * + * @param dev Device descriptor + * @param rate New sample rate divider. + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_rate(mpu6050_dev_t *dev, uint8_t rate); + +/** + * @brief Get external FSYNC configuration. + * + * Configures the external Frame Synchronization (FSYNC) pin sampling. + * An external signal connected to the FSYNC pin can be sampled by configuring + * EXT_SYNC_SET. Signal changes to the FSYNC pin are latched so that short + * strobes may be captured. The latched FSYNC signal will be sampled at the + * Sampling Rate, as defined in register 25. + * After sampling, the latch will reset to the current FSYNC signal state. + * The sampled value will be reported in place of the least significant bit in + * a sensor data register determined by the value of EXT_SYNC_SET. + * + * @param dev Device descriptor + * @param[out] sync FSYNC configuration value. + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_external_frame_sync(mpu6050_dev_t *dev, mpu6050_ext_sync_t *sync); + +/** + * @brief Set external FSYNC configuration. + * + * @param dev Device descriptor + * @param sync New FSYNC configuration value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_external_frame_sync(mpu6050_dev_t *dev, mpu6050_ext_sync_t sync); + +/** + * @brief Get digital low-pass filter configuration. + * + * The DLPF_CFG parameter sets the digital low pass filter configuration. + * It also determines the internal sampling rate used by the device. + * + * Note: The accelerometer output rate is 1kHz. This means that for a Sample + * Rate greater than 1kHz, the same accelerometer sample may be output to the + * FIFO, DMP, and sensor registers more than once. + * + * @param dev Device descriptor + * @param[out] mode DLFP configuration. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_dlpf_mode(mpu6050_dev_t *dev, mpu6050_dlpf_mode_t *mode); + +/** + * @brief Set digital low-pass filter configuration. + * + * @param dev Device descriptor + * @param mode New DLFP configuration setting. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_dlpf_mode(mpu6050_dev_t *dev, mpu6050_dlpf_mode_t mode); + +/** + * @brief Get full-scale gyroscope range. + * + * The FS_SEL parameter allows setting the full-scale range of the gyro + * sensors. + * + * @param dev Device descriptor + * @param[out] gyro_range full-scale gyroscope range setting. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_full_scale_gyro_range(mpu6050_dev_t *dev, mpu6050_gyro_range_t *gyro_range); + +/** + * @brief Set full-scale gyroscope range. + * + * @param dev Device descriptor + * @param range New full-scale gyroscope range value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_full_scale_gyro_range(mpu6050_dev_t *dev, mpu6050_gyro_range_t range); + +/** + * @brief Get self-test factory trim value for accelerometer axis. + * + * @param dev Device descriptor + * @param axis Accelerometer axis + * @param[out] trim Factory trim value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_accel_self_test_factory_trim(mpu6050_dev_t *dev, mpu6050_axis_t axis, uint8_t *trim); + +/** + * @brief Get self-test factory trim value for gyroscope axis. + * + * @param dev Device descriptor + * @param axis Gyroscope axis + * @param[out] trim Factory trim value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_gyro_self_test_factory_trim(mpu6050_dev_t *dev, mpu6050_axis_t axis, uint8_t *trim); + +/** + * @brief Get self-test enabled for accelerometer axis. + * + * @param dev Device descriptor + * @param axis Accelerometer axis + * @param[out] enabled true if self-test enabled + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_accel_self_test(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool *enabled); + +/** + * @brief Set self-test enabled for accelerometer axis. + * + * @param dev Device descriptor + * @param axis Accelerometer axis + * @param enabled Self test enabled value + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_accel_self_test(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool enabled); + +/** + * @brief Get full-scale accelerometer range. + * + * The FS_SEL parameter allows dev the full-scale range of the accelerometer + * sensors. + * + * @param dev Device descriptor + * @param[out] range Current full-scale accelerometer range setting + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_full_scale_accel_range(mpu6050_dev_t *dev, mpu6050_accel_range_t *range); + +/** + * @brief Set full-scale accelerometer range. + * + * @param dev Device descriptor + * @param range New full-scale accelerometer range setting + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_full_scale_accel_range(mpu6050_dev_t *dev, mpu6050_accel_range_t range); + +/** + * @brief Get the high-pass filter configuration. + * The DHPF is a filter module in the path leading to motion detectors (Free + * Fall, Motion threshold, and Zero Motion). The high pass filter output is not + * available to the data registers. + * + * The high pass filter has three modes: + * + * Reset: The filter output settles to zero within one sample. This + * effectively disables the high pass filter. This mode may be toggled + * to quickly settle the filter. + * + * On: The high pass filter will pass signals above the cut off frequency. + * + * Hold: When triggered, the filter holds the present sample. The filter + * output will be the difference between the input sample and the held + * sample. + * + * @param dev Device descriptor + * @param[out] mode Current high-pass filter configuration + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_dhpf_mode(mpu6050_dev_t *dev, mpu6050_dhpf_mode_t *mode); + +/** + * @brief Set the high-pass filter configuration. + * + * @param dev Device descriptor + * @param mode New high-pass filter configuration + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_dhpf_mode(mpu6050_dev_t *dev, mpu6050_dhpf_mode_t mode); + +/** + * @brief Get free-fall event acceleration threshold. + * + * This register configures the detection threshold for Free Fall event + * detection. The unit of FF_THR is 1LSB = 2mg. Free Fall is detected when the + * absolute value of the accelerometer measurements for the three axes are each + * less than the detection threshold. This condition increments the Free Fall + * duration counter (Register 30). The Free Fall interrupt is triggered when the + * Free Fall duration counter reaches the time specified in FF_DUR. + * + * @param dev Device descriptor + * @param[out] threshold Current free-fall acceleration threshold value (LSB = 2mg) + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_freefall_detection_threshold(mpu6050_dev_t *dev, uint8_t *threshold); + +/** + * @brief Get free-fall event acceleration threshold. + * + * @param dev Device descriptor + * @param threshold New free-fall acceleration threshold value (LSB = 2mg). + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_freefall_detection_threshold(mpu6050_dev_t *dev, uint8_t threshold); + +/** + * @brief Get free-fall event duration threshold. + * + * This register configures the duration_ms counter threshold for Free Fall event + * detection. The duration counter ticks at 1kHz, therefore FF_DUR has a unit + * of 1 LSB = 1 ms. + * + * The Free Fall duration counter increments while the absolute value of the + * accelerometer measurements are each less than the detection threshold + * (Register 29). The Free Fall interrupt is triggered when the Free Fall + * duration_ms counter reaches the time specified in this register. + * + * + * @param dev Device descriptor + * @param[out] duration_ms Current free-fall duration threshold value, ms + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_freefall_detection_duration(mpu6050_dev_t *dev, uint8_t *duration_ms); + +/** + * @brief Set free-fall event duration threshold. + * + * @param dev Device descriptor + * @param duration_ms Free-fall duration threshold value, ms + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_freefall_detection_duration(mpu6050_dev_t *dev, uint8_t duration_ms); + +/** + * @brief Get motion detection event acceleration threshold. + * + * This register configures the detection threshold for Motion interrupt + * generation. The unit of MOT_THR is 1LSB = 2mg. Motion is detected when the + * absolute value of any of the accelerometer measurements exceeds this Motion + * detection threshold. This condition increments the Motion detection duration + * counter (Register 32). The Motion detection interrupt is triggered when the + * Motion Detection counter reaches the time count specified in MOT_DUR + * (Register 32). + * + * The Motion interrupt will indicate the axis and polarity of detected motion + * in MOT_DETECT_STATUS (Register 97). + * + * @param dev Device descriptor + * @param[out] threshold Current motion detection acceleration threshold value (LSB = 2mg) + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_motion_detection_threshold(mpu6050_dev_t *dev, uint8_t *threshold); + +/** + * @brief Set motion detection event acceleration threshold. + * + * @param dev Device descriptor + * @param threshold New motion detection acceleration threshold value (LSB = 2mg) + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_motion_detection_threshold(mpu6050_dev_t *dev, uint8_t threshold); + +/** + * @brief Get motion detection event duration threshold + * + * This register configures the duration counter threshold for Motion interrupt + * generation. The duration counter ticks at 1 kHz, therefore MOT_DUR has a unit + * of 1LSB = 1ms. The Motion detection duration counter increments when the + * absolute value of any of the accelerometer measurements exceeds the Motion + * detection threshold (Register 31). The Motion detection interrupt is + * triggered when the Motion detection counter reaches the time count specified + * in this register. + * + * @param dev Device descriptor + * @param[out] duration Current motion detection duration threshold value, ms + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_motion_detection_duration(mpu6050_dev_t *dev, uint8_t *duration); + +/** + * @brief Set motion detection event duration threshold. + * + * @param dev Device descriptor + * @param duration New motion detection duration threshold value, ms + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_motion_detection_duration(mpu6050_dev_t *dev, uint8_t duration); + +/** + * @brief Get zero motion detection event acceleration threshold. + * + * This register configures the detection threshold for Zero Motion interrupt + * generation. The unit of ZRMOT_THR is 1LSB = 2mg. Zero Motion is detected when + * the absolute value of the accelerometer measurements for the 3 axes are each + * less than the detection threshold. This condition increments the Zero Motion + * duration counter (Register 34). The Zero Motion interrupt is triggered when + * the Zero Motion duration counter reaches the time count specified in + * ZRMOT_DUR (Register 34). + * + * Unlike Free Fall or Motion detection, Zero Motion detection triggers an + * interrupt both when Zero Motion is first detected and when Zero Motion is no + * longer detected. + * + * When a zero motion event is detected, a Zero Motion Status will be indicated + * in the MOT_DETECT_STATUS register (Register 97). When a motion-to-zero-motion + * condition is detected, the status bit is set to 1. When a zero-motion-to- + * motion condition is detected, the status bit is set to 0. + * + * @param dev Device descriptor + * @param[out] threshold Current zero motion detection acceleration threshold + * value (LSB = 2mg) + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_zero_motion_detection_threshold(mpu6050_dev_t *dev, uint8_t *threshold); + +/** + * @brief Set zero motion detection event acceleration threshold. + * + * @param dev Device descriptor + * @param threshold New zero motion detection acceleration threshold + * value (LSB = 2mg) + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_zero_motion_detection_threshold(mpu6050_dev_t *dev, uint8_t threshold); + +/** + * @brief Get zero motion detection event duration threshold. + * + * This register configures the duration counter threshold for Zero Motion + * interrupt generation. The duration counter ticks at 16 Hz, therefore + * ZRMOT_DUR has a unit of 1 LSB = 64 ms. The Zero Motion duration counter + * increments while the absolute value of the accelerometer measurements are + * each less than the detection threshold (Register 33). The Zero Motion + * interrupt is triggered when the Zero Motion duration counter reaches the time + * count specified in this register. + * + * @param dev Device descriptor + * @param[out] duration Current zero motion detection duration threshold value (LSB = 64ms) + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_zero_motion_detection_duration(mpu6050_dev_t *dev, uint8_t *duration); + +/** + * @brief Set zero motion detection event duration threshold. + * + * @param dev Device descriptor + * @param duration New zero motion detection duration threshold value (LSB = 64ms) + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_zero_motion_detection_duration(mpu6050_dev_t *dev, uint8_t duration); + +/** + * @brief Get temperature FIFO enabled value. + * + * When set to 1, this bit enables TEMP_OUT_H and TEMP_OUT_L (Registers 65 and + * 66) to be written into the FIFO buffer. + * + * @param dev Device descriptor + * @param[out] enabled true if temperature FIFO is enabled + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_temp_fifo_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set temperature FIFO enabled value. + * + * @param dev Device descriptor + * @param enabled New temperature FIFO enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_temp_fifo_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get gyroscope axis FIFO enabled value. + * + * @param dev Device descriptor + * @param axis Gyroscope axis + * @param[out] enabled Gyroscope axis FIFO enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_gyro_fifo_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool *enabled); + +/** + * @brief Set gyroscope axis FIFO enabled value. + * + * @param dev Device descriptor + * @param axis Gyroscope axis + * @param enabled New gyroscope axis FIFO enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_gyro_fifo_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool enabled); + +/** + * @brief Get accelerometer FIFO enabled value. + * + * When set to 1, this bit enables ACCEL_XOUT_H, ACCEL_XOUT_L, ACCEL_YOUT_H, + * ACCEL_YOUT_L, ACCEL_ZOUT_H, and ACCEL_ZOUT_L (Registers 59 to 64) to be + * written into the FIFO buffer. + * + * @param dev Device descriptor + * @param[out] enabled Gyroscope axis FIFO enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_accel_fifo_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set accelerometer FIFO enabled value. + * + * @param dev Device descriptor + * @param enabled New accelerometer FIFO enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_accel_fifo_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get Slave FIFO enabled value. + * + * When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) + * associated with Slave to be written into the FIFO buffer. + * + * @param dev Device descriptor + * @param num Slave number (0-3) + * @param[out] enabled Slave FIFO enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_fifo_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *enabled); + +/** + * @brief Set Slave FIFO enabled value. + * + * @param dev Device descriptor + * @param num Slave number (0-3) + * @param enabled New Slave FIFO enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_fifo_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool enabled); + +/** + * @brief Get multi-master enabled value. + * + * Multi-master capability allows multiple I2C masters to operate on the same + * bus. In circuits where multi-master capability is required, set MULT_MST_EN + * to 1. This will increase current drawn by approximately 30uA. + * + * In circuits where multi-master capability is required, the state of the I2C + * bus must always be monitored by each separate I2C Master. Before an I2C + * Master can assume arbitration of the bus, it must first confirm that no other + * I2C Master has arbitration of the bus. When MULT_MST_EN is set to 1, the + * MPU-60X0's bus arbitration detection logic is turned on, enabling it to + * detect when the bus is available. + * + * @param dev Device descriptor + * @param[out] enabled Multi-master enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_multi_master_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set multi-master enabled value + * + * @param dev Device descriptor + * @param enabled New multi-master enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_multi_master_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get wait-for-external-sensor-data enabled value. + * + * When the WAIT_FOR_ES bit is set to 1, the Data Ready interrupt will be + * delayed until External Sensor data from the Slave Devices are loaded into the + * EXT_SENS_DATA registers. This is used to ensure that both the internal sensor + * data (i.e. from gyro and accel) and external sensor data have been loaded to + * their respective data registers (i.e. the data is synced) when the Data Ready + * interrupt is triggered. + * + * @param dev Device descriptor + * @param[out] enabled Wait-for-external-sensor-data enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_wait_for_external_sensor_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set wait-for-external-sensor-data enabled value. + * + * @param dev Device descriptor + * @param enabled New wait-for-external-sensor-data enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_wait_for_external_sensor_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get slave read/write transition enabled value. + * + * The I2C_MST_P_NSR bit configures the I2C Master's transition from one slave + * read to the next slave read. If the bit equals 0, there will be a restart + * between reads. If the bit equals 1, there will be a stop followed by a start + * of the following read. When a write transaction follows a read transaction, + * the stop followed by a start of the successive write will be always used. + * + * @param dev Device descriptor + * @param[out] enabled Slave read/write transition enabled value + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_read_write_transition_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set slave read/write transition enabled value. + * + * @param dev Device descriptor + * @param enabled New slave read/write transition enabled value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_read_write_transition_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get I2C master clock speed. + * + * I2C_MST_CLK is a 4 bit unsigned value which configures a divider on the + * MPU-60X0 internal 8MHz clock. + * + * @param dev Device descriptor + * @param[out] clk_spd Current I2C master clock speed. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_master_clock_speed(mpu6050_dev_t *dev, mpu6050_i2c_master_clock_t *clk_spd); + +/** + * @brief Set I2C master clock speed. + * + * @param dev Device descriptor + * @param clk_spd Current I2C master clock speed. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_master_clock_speed(mpu6050_dev_t *dev, mpu6050_i2c_master_clock_t clk_spd); + +/** + * @brief Get the I2C address of the specified slave. + * + * Note that Bit 7 (MSB) controls read/write mode. If Bit 7 is set, it's a read + * operation, and if it is cleared, then it's a write operation. The remaining + * bits (6-0) are the 7-bit device address of the slave device. + * + * In read mode, the result of the read is placed in the lowest available + * EXT_SENS_DATA register. For further information regarding the allocation of + * read results, please refer to the EXT_SENS_DATA register description + * (Registers 73 - 96). + * + * The MPU-6050 supports a total of five slaves, but Slave 4 has unique + * characteristics, and so it has its own functions (getSlave4* and setSlave4*). + * + * I2C data transactions are performed at the Sample Rate, as defined in + * Register 25. The user is responsible for ensuring that I2C data transactions + * to and from each enabled Slave can be completed within a single period of the + * Sample Rate. + * + * The I2C slave access rate can be reduced relative to the Sample Rate. This + * reduced access rate is determined by I2C_MST_DLY (Register 52). Whether a + * slave's access rate is reduced relative to the Sample Rate is determined by + * I2C_MST_DELAY_CTRL (Register 103). + * + * The processing order for the slaves is fixed. The sequence followed for + * processing the slaves is Slave 0, Slave 1, Slave 2, Slave 3 and Slave 4. If a + * particular Slave is disabled it will be skipped. + * + * Each slave can either be accessed at the sample rate or at a reduced sample + * rate. In a case where some slaves are accessed at the Sample Rate and some + * slaves are accessed at the reduced rate, the sequence of accessing the slaves + * (Slave 0 to Slave 4) is still followed. However, the reduced rate slaves will + * be skipped if their access rate dictates that they should not be accessed + * during that particular cycle. For further information regarding the reduced + * access rate, please refer to Register 52. Whether a slave is accessed at the + * Sample Rate or at the reduced rate is determined by the Delay Enable bits in + * Register 103. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param[out] addr Current address for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_address(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t *addr); + +/** + * @brief Set the I2C address of the specified slave. + * + * @param dev Device descriptor + * @param num Slave number (0-4) + * @param address New address for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_address(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t address); + +/** + * @brief Get the active internal register for the specified slave. + * + * Read/write operations for this slave will be done to whatever internal + * register address is stored in this MPU register. + * + * The MPU-6050 supports a total of five slaves, but Slave 4 has unique + * characteristics, and so it has its own functions. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param[out] reg Current active register for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_register(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t *reg); + +/** + * @brief Set the active internal register for the specified slave. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param reg New active register for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_register(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t reg); + +/** + * @brief Get the enabled value for the specified slave. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param[out] enabled Enabled value for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *enabled); + +/** + * @brief Set the enabled value for the specified slave. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param enabled New enabled value for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool enabled); + +/** + * @brief Get word pair byte-swapping enabled for the specified slave. + * + * When set to 1, this bit enables byte swapping. When byte swapping is enabled, + * the high and low bytes of a word pair are swapped. Please refer to + * I2C_SLV0_GRP for the pairing convention of the word pairs. When cleared to 0, + * bytes transferred to and from Slave will be written to EXT_SENS_DATA + * registers in the order they were transferred. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param[out] enabled Word pair byte-swapping enabled value for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_word_byte_swap(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *enabled); + +/** + * @brief Set word pair byte-swapping enabled for the specified slave. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param enabled New word pair byte-swapping enabled value for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_word_byte_swap(mpu6050_dev_t *dev, mpu6050_slave_t num, bool enabled); + +/** + * @brief Get write mode for the specified slave. + * + * When set to 1, the transaction will read or write data only. When cleared to + * 0, the transaction will write a register address prior to reading or writing + * data. This should equal 0 when specifying the register address within the + * Slave device to/from which the ensuing data transaction will take place. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param[out] mode Write mode: false - register address + data, true - data only + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_write_mode(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *mode); + +/** + * @brief Set write mode for the specified slave. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param mode Write mode: false - register address + data, true - data only + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_write_mode(mpu6050_dev_t *dev, mpu6050_slave_t num, bool mode); + +/** + * @brief Get word pair grouping order offset for the specified slave. + * + * This sets specifies the grouping order of word pairs received from registers. + * When cleared to 0, bytes from register addresses 0 and 1, 2 and 3, etc (even, + * then odd register addresses) are paired to form a word. When set to 1, bytes + * from register addresses are paired 1 and 2, 3 and 4, etc. (odd, then even + * register addresses) are paired to form a word. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param[out] enabled Word pair grouping order offset for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_word_group_offset(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *enabled); + +/** + * @brief Set word pair grouping order offset for the specified slave. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param enabled New word pair grouping order offset for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_word_group_offset(mpu6050_dev_t *dev, mpu6050_slave_t num, bool enabled); + +/** + * @brief Get number of bytes to read for the specified slave. + * + * Specifies the number of bytes transferred to and from Slave 0. Clearing this + * bit to 0 is equivalent to disabling the register by writing 0 to I2C_SLV0_EN. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param[out] length Number of bytes to read for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_data_length(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t *length); + +/** + * @brief Set number of bytes to read for the specified slave. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param length Number of bytes to read for specified slave. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_data_length(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t length); + +/** + * @brief Set new byte to write to Slave 4. + * This register stores the data to be written into the Slave 4. If I2C_SLV4_RW + * is set 1 (set to read), this register has no effect. + * + * @param dev Device descriptor + * @param data New byte to write to Slave 4. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_4_output_byte(mpu6050_dev_t *dev, uint8_t data); + +/** + * @brief Get the enabled value for Slave 4 transaction interrupts. + * + * When set to 1, this bit enables the generation of an interrupt signal upon + * completion of a Slave 4 transaction. When cleared to 0, this bit disables the + * generation of an interrupt signal upon completion of a Slave 4 transaction. + * The interrupt status can be observed in Register 54. + * + * @param dev Device descriptor + * @param[out] enabled Enabled value for Slave 4 transaction interrupts. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_4_interrupt_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set the enabled value for Slave 4 transaction interrupts. + + * @param dev Device descriptor + * @param enabled New enabled value for Slave 4 transaction interrupts. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_4_interrupt_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get Slave 4 master delay value. + * + * This configures the reduced access rate of I2C slaves relative to the Sample + * Rate. When a slave's access rate is decreased relative to the Sample Rate, + * the slave is accessed every: + * + * 1 / (1 + I2C_MST_DLY) samples + * + * This base Sample Rate in turn is determined by SMPLRT_DIV (Register 25) and + * DLPF_CFG (Register 26). Whether a slave's access rate is reduced relative to + * the Sample Rate is determined by I2C_MST_DELAY_CTRL (Register 103). + * + * @param dev Device descriptor + * @param[out] delay Current Slave 4 master delay value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_4_master_delay(mpu6050_dev_t *dev, uint8_t *delay); + +/** + * @brief Set Slave 4 master delay value. + * + * @param dev Device descriptor + * @param delay New Slave 4 master delay value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_4_master_delay(mpu6050_dev_t *dev, uint8_t delay); + +/** + * @brief Get last available byte read from Slave 4. + * + * This register stores the data read from Slave 4. This field is populated + * after a read transaction. + * + * @param dev Device descriptor + * @param[out] byte Last available byte read from to Slave 4. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_4_input_byte(mpu6050_dev_t *dev, uint8_t *byte); + +/** + * @brief Get FSYNC interrupt status. + * + * This bit reflects the status of the FSYNC interrupt from an external device + * into the MPU-60X0. This is used as a way to pass an external interrupt + * through the MPU-60X0 to the host application processor. When set to 1, . + * + * @param dev Device descriptor + * @param[out] enabled FSYNC interrupt status + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_passthrough_status(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Get Slave 4 transaction done status. + * + * Automatically sets to 1 when a Slave 4 transaction has completed. This + * triggers an interrupt if the I2C_MST_INT_EN bit in the INT_ENABLE register + * (Register 56) is asserted and if the SLV_4_DONE_INT bit is asserted in the + * I2C_SLV4_CTRL register (Register 52). + * + * @param dev Device descriptor + * @param[out] enabled Slave 4 transaction done status + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_4_is_done(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Get master arbitration lost status. + * + * This bit automatically sets to 1 when the I2C Master has lost arbitration of + * the auxiliary I2C bus (an error condition). This triggers an interrupt if the + * I2C_MST_INT_EN bit in the INT_ENABLE register (Register 56) is asserted. + * + * @param dev Device descriptor + * @param[out] lost Master arbitration lost status + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_lost_arbitration(mpu6050_dev_t *dev, bool *lost); + +/** + * @brief Get Slave NACK status. + * + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 4. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param[out] nack Slave NACK status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_nack(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *nack); + +/** + * @brief Get interrupt logic level mode. + * + * @param dev Device descriptor + * @param[out] mode Interrupt logic level mode + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_interrupt_mode(mpu6050_dev_t *dev, mpu6050_int_level_t *mode); + +/** + * @brief Set interrupt logic level mode. + * + * @param dev Device descriptor + * @param mode New interrupt mode. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_interrupt_mode(mpu6050_dev_t *dev, mpu6050_int_level_t mode); + +/** + * @brief Get interrupt drive mode. + * + * @param dev Device descriptor + * @param[out] drive Current interrupt drive mode + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_interrupt_drive(mpu6050_dev_t *dev, mpu6050_int_drive_t *drive); + +/** + * @brief Set interrupt drive mode. + * + * @param dev Device descriptor + * @param drive New interrupt drive mode + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_interrupt_drive(mpu6050_dev_t *dev, mpu6050_int_drive_t drive); + +/** + * @brief Get interrupt latch mode. + * + * @param dev Device descriptor + * @param[out] latch Current latch mode + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_interrupt_latch(mpu6050_dev_t *dev, mpu6050_int_latch_t *latch); + +/** + * @brief Set interrupt latch mode. + * + * @param dev Device descriptor + * @param latch New latch mode + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_interrupt_latch(mpu6050_dev_t *dev, mpu6050_int_latch_t latch); + +/** + * @brief Get interrupt latch clear mode. + * + * @param dev Device descriptor + * @param[out] clear Current latch clear mode (false = status-read-only, true = any-register-read). + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_interrupt_latch_clear(mpu6050_dev_t *dev, bool *clear); + +/** + * @brief Set interrupt latch clear mode. + * + * @param dev Device descriptor + * @param clear New latch clear mode (false = status-read-only, true = any-register-read). + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_interrupt_latch_clear(mpu6050_dev_t *dev, bool clear); + +/** + * @brief Get FSYNC interrupt logic level. + * + * @param dev Device descriptor + * @param[out] level Current FSYNC interrupt logic level. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_fsync_interrupt_level(mpu6050_dev_t *dev, mpu6050_int_level_t *level); + +/** + * @brief Set FSYNC interrupt logic level. + * + * @param dev Device descriptor + * @param level New FSYNC interrupt logic level. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_fsync_interrupt_level(mpu6050_dev_t *dev, mpu6050_int_level_t level); + +/** + * @brief Get FSYNC pin interrupt enabled setting. + * + * @param dev Device descriptor + * @param[out] enabled FSYNC pin interrupt enabled setting + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_fsync_interrupt_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set FSYNC pin interrupt enabled setting. + * + * @param dev Device descriptor + * @param enabled New FSYNC pin interrupt enabled setting. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_fsync_interrupt_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get I2C bypass enabled status. + * + * When this bit is equal to 1 and I2C master is disabled, the host + * application processor will be able to directly access the auxiliary I2C + * bus of the MPU-60X0. When this bit is equal to 0, the host application + * processor will not be able to directly access the auxiliary I2C + * bus of the MPU-60X0 regardless of the state of I2C master. + * + * @param dev Device descriptor + * @param[out] enabled Current I2C bypass enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_i2c_bypass_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set I2C bypass enabled status. + * + * @param dev Device descriptor + * @param enabled New I2C bypass enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_i2c_bypass_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get reference clock output enabled status. + * + * When this bit is equal to 1, a reference clock output is provided at the + * CLKOUT pin. When this bit is equal to 0, the clock output is disabled. + * + * @param dev Device descriptor + * @param[out] enabled Current reference clock output enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_clock_output_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set reference clock output enabled status. + * + * @param dev Device descriptor + * @param enabled New reference clock output enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_clock_output_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get full interrupt enabled status. + * + * Full register byte for all interrupts, for quick reading. Each bit will be + * set 0 for disabled, 1 for enabled. + * + * @param dev Device descriptor + * @param[out] ints Combination of mpu6050_int_source_t flags + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_int_enabled(mpu6050_dev_t *dev, uint8_t *ints); + +/** + * @brief Set full interrupt enabled status. + * + * Full register byte for all interrupts, for quick writing. Each bit will be + * set 0 for disabled, 1 for enabled. + * + * @param dev Device descriptor + * @param ints Combination of mpu6050_int_source_t flags + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_int_enabled(mpu6050_dev_t *dev, uint8_t ints); + +/** + * @brief Get full set of interrupt status bits. + * + * These bits clear to 0 after the register has been read. Very useful + * for getting multiple INT statuses, since each single bit read clears + * all of them because it has to read the whole byte. + * + * @param dev Device descriptor + * @param[out] ints Combination of mpu6050_int_source_t flags + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_int_status(mpu6050_dev_t *dev, uint8_t *ints); + +/** + * @brief Get offset for accelerometer axis + * + * Undocumented register/feature + * + * @param dev Device descriptor + * @param axis Accelerometer axis + * @param[out] offset Offset + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_accel_offset(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t *offset); + +/** + * @brief Set offset for accelerometer axis + * + * Undocumented register/feature + * + * @param dev Device descriptor + * @param axis Accelerometer axis + * @param offset Offset + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_accel_offset(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t offset); + +/** + * @brief Get offset for gyroscope axis + * + * Undocumented register/feature + * + * @param dev Device descriptor + * @param axis Gyroscope axis + * @param[out] offset Offset + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_gyro_offset(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t *offset); + +/** + * @brief Get offset for gyroscope axis + * + * Undocumented register/feature + * + * @param dev Device descriptor + * @param axis Gyroscope axis + * @param offset Offset + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_gyro_offset(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t offset); + +/** + * @brief Get 3-axis accelerometer readings. + * + * These registers store the most recent accelerometer measurements. + * Accelerometer measurements are written to these registers at the Sample Rate + * as defined in Register 25. + * + * The accelerometer measurement registers, along with the temperature + * measurement registers, gyroscope measurement registers, and external sensor + * data registers, are composed of two sets of registers: an internal register + * set and a user-facing read register set. + * + * The data within the accelerometer sensors' internal register set is always + * updated at the Sample Rate. Meanwhile, the user-facing read register set + * duplicates the internal register set's data values whenever the serial + * interface is idle. This guarantees that a burst read of sensor registers will + * read measurements from the same sampling instant. Note that if burst reads + * are not used, the user is responsible for ensuring a set of single byte reads + * correspond to a single sampling instant by checking the Data Ready interrupt. + * + * @param dev Device descriptor + * @param[out] accel Three-axis acceleration data, g. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_acceleration(mpu6050_dev_t *dev, mpu6050_acceleration_t *accel); + +/** + * @brief Get raw 3-axis accelerometer readings. + * + * @param dev Device descriptor + * @param[out] raw_accel Raw acceleration data. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_raw_acceleration(mpu6050_dev_t *dev, mpu6050_raw_acceleration_t *raw_accel); + +/** + * @brief Get accelerometer reading on a single axis. + * + * @param dev Device descriptor + * @param axis Accelerometer axis + * @param[out] accel Axis acceleration measurement, g + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_acceleration_axis(mpu6050_dev_t *dev, mpu6050_axis_t axis, float *accel); + +/** + * @brief Get raw accelerometer reading on a single axis. + * + * @param dev Device descriptor + * @param axis Accelerometer axis + * @param[out] raw_accel Raw axis acceleration measurement + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_raw_acceleration_axis(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t *raw_accel); + +/** + * @brief Get current internal temperature. + * + * @param dev Device descriptor + * @param[out] temp Internal temperature, °C + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_temperature(mpu6050_dev_t *dev, float *temp); + +/** + * @brief Get 3-axis gyroscope readings. + * + * These gyroscope measurement registers, along with the accelerometer + * measurement registers, temperature measurement registers, and external sensor + * data registers, are composed of two sets of registers: an internal register + * set and a user-facing read register set. + * The data within the gyroscope sensors' internal register set is always + * updated at the Sample Rate. Meanwhile, the user-facing read register set + * duplicates the internal register set's data values whenever the serial + * interface is idle. This guarantees that a burst read of sensor registers will + * read measurements from the same sampling instant. Note that if burst reads + * are not used, the user is responsible for ensuring a set of single byte reads + * correspond to a single sampling instant by checking the Data Ready interrupt. + * + * @param dev Device descriptor + * @param[out] gyro Rotation data, °/s + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_rotation(mpu6050_dev_t *dev, mpu6050_rotation_t *gyro); + +/** + * @brief Get raw 3-axis gyroscope readings. + * + * @param dev Device descriptor + * @param[out] raw_gyro Raw rotation data. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_raw_rotation(mpu6050_dev_t *dev, mpu6050_raw_rotation_t *raw_gyro); + +/** + * @brief Get gyroscope reading on a single axis. + * + * @param dev Device descriptor + * @param axis Gyroscope axis + * @param[out] gyro Axis rotation measurement, °/s + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_rotation_axis(mpu6050_dev_t *dev, mpu6050_axis_t axis, float *gyro); + +/** + * @brief Get raw gyroscope reading on a single axis. + * + * @param dev Device descriptor + * @param axis Gyroscope axis + * @param[out] raw_gyro Raw axis rotation measurement + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_raw_rotation_axis(mpu6050_dev_t *dev, mpu6050_axis_t axis, int16_t *raw_gyro); + +/** + * @brief Get raw 6-axis motion sensor readings (accel/gyro). + * + * Retrieves all currently available motion sensor values. + * + * @param dev Device descriptor + * @param[out] data_accel acceleration struct. + * @param[out] data_gyro rotation struct. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_motion(mpu6050_dev_t *dev, mpu6050_acceleration_t *data_accel, mpu6050_rotation_t *data_gyro); + +/** + * @brief Read bytes from external sensor data register. + * + * These registers store data read from external sensors by the Slave 0, 1, 2, + * and 3 on the auxiliary I2C interface. Data read by Slave 4 is stored in + * I2C_SLV4_DI (Register 53). + * + * External sensor data is written to these registers at the Sample Rate as + * defined in Register 25. This access rate can be reduced by using the Slave + * Delay Enable registers (Register 103). + * + * External sensor data registers, along with the gyroscope measurement + * registers, accelerometer measurement registers, and temperature measurement + * registers, are composed of two sets of registers: an internal register set + * and a user-facing read register set. + * + * The data within the external sensors' internal register set is always updated + * at the Sample Rate (or the reduced access rate) whenever the serial interface + * is idle. This guarantees that a burst read of sensor registers will read + * measurements from the same sampling instant. Note that if burst reads are not + * used, the user is responsible for ensuring a set of single byte reads + * correspond to a single sampling instant by checking the Data Ready interrupt. + * + * Data is placed in these external sensor data registers according to + * I2C_SLV0_CTRL, I2C_SLV1_CTRL, I2C_SLV2_CTRL, and I2C_SLV3_CTRL (Registers 39, + * 42, 45, and 48). When more than zero bytes are read (I2C_SLVx_LEN > 0) from + * an enabled slave (I2C_SLVx_EN = 1), the slave is read at the Sample Rate (as + * defined in Register 25) or delayed rate (if specified in Register 52 and + * 103). During each Sample cycle, slave reads are performed in order of Slave + * number. If all slaves are enabled with more than zero bytes to be read, the + * order will be Slave 0, followed by Slave 1, Slave 2, and Slave 3. + * + * Each enabled slave will have EXT_SENS_DATA registers associated with it by + * number of bytes read (I2C_SLVx_LEN) in order of slave number, starting from + * EXT_SENS_DATA_00. Note that this means enabling or disabling a slave may + * change the higher numbered slaves' associated registers. Furthermore, if + * fewer total bytes are being read from the external sensors as a result of + * such a change, then the data remaining in the registers which no longer have + * an associated slave device (i.e. high numbered registers) will remain in + * these previously allocated registers unless reset. + * + * If the sum of the read lengths of all SLVx transactions exceed the number of + * available EXT_SENS_DATA registers, the excess bytes will be dropped. There + * are 24 EXT_SENS_DATA registers and hence the total read lengths between all + * the slaves cannot be greater than 24 or some bytes will be lost. + * + * Note: Slave 4's behavior is distinct from that of Slaves 0-3. For further + * information regarding the characteristics of Slave 4, please refer to + * Registers 49 to 53. + * + * Suppose that Slave 0 is enabled with 4 bytes to be read (I2C_SLV0_EN = 1 and + * I2C_SLV0_LEN = 4) while Slave 1 is enabled with 2 bytes to be read so that + * I2C_SLV1_EN = 1 and I2C_SLV1_LEN = 2. In such a situation, EXT_SENS_DATA _00 + * through _03 will be associated with Slave 0, while EXT_SENS_DATA _04 and 05 + * will be associated with Slave 1. If Slave 2 is enabled as well, registers + * starting from EXT_SENS_DATA_06 will be allocated to Slave 2. + * + * If Slave 2 is disabled while Slave 3 is enabled in this same situation, then + * registers starting from EXT_SENS_DATA_06 will be allocated to Slave 3 + * instead. + * + * REGISTER ALLOCATION FOR DYNAMIC DISABLE VS. NORMAL DISABLE: + * If a slave is disabled at any time, the space initially allocated to the + * slave in the EXT_SENS_DATA register, will remain associated with that slave. + * This is to avoid dynamic adjustment of the register allocation. + * + * The allocation of the EXT_SENS_DATA registers is recomputed only when (1) all + * slaves are disabled, or (2) the I2C_MST_RST bit is set (Register 106). + * + * This above is also true if one of the slaves gets NACKed and stops + * functioning. + * + * @param dev Device descriptor + * @param position Starting position (0-23). + * @param[out] buf Buffer to store data + * @param length Bytes to read + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_external_sensor_data(mpu6050_dev_t *dev, int position, void *buf, size_t length); + +/** + * @brief Get full motion detection status register content (all bits). + * + * @param dev Device descriptor + * @param[out] status Motion detection status byte, combination of ::mpu6050_motion_det_flags_t items + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_motion_status(mpu6050_dev_t *dev, uint8_t *status); + +/** + * @brief Write byte to Data Output container for specified slave. + * + * This register holds the output data written into Slave when Slave is set to + * write mode. For further information regarding Slave control, please + * refer to Registers 37 to 39 and immediately following. + * + * @param dev Device descriptor + * @param num Slave number (0-3). + * @param data Byte to write. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_output_byte(mpu6050_dev_t *dev, mpu6050_slave_t num, uint8_t data); + +/** + * @brief Get external data shadow delay enabled status. + * + * This register is used to specify the timing of external sensor data + * shadowing. When DELAY_ES_SHADOW is set to 1, shadowing of external + * sensor data is delayed until all data has been received. + * + * @param dev Device descriptor + * @param[out] enabled External data shadow delay enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_external_shadow_delay_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set external data shadow delay enabled status. + * + * @param dev Device descriptor + * @param enabled New external data shadow delay enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_external_shadow_delay_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get slave delay enabled status. + * + * When a particular slave delay is enabled, the rate of access for the that + * slave device is reduced. When a slave's access rate is decreased relative to + * the Sample Rate, the slave is accessed every: + * + * 1 / (1 + I2C_MST_DLY) Samples + * + * This base Sample Rate in turn is determined by SMPLRT_DIV (Register * 25) + * and DLPF_CFG (Register 26). + * + * For further information regarding I2C_MST_DLY, please refer to register 52. + * For further information regarding the Sample Rate, please refer to + * register 25. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param[out] enabled Slave delay enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_slave_delay_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool *enabled); + +/** + * @brief Set slave delay enabled status. + * + * @param dev Device descriptor + * @param num Slave number (0-4). + * @param enabled New slave delay enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_slave_delay_enabled(mpu6050_dev_t *dev, mpu6050_slave_t num, bool enabled); + +/** + * @brief Reset gyroscope signal path. + * + * The reset will revert the signal path analog to digital converters and + * filters to their power up configurations. + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_reset_gyroscope_path(mpu6050_dev_t *dev); + +/** + * @brief Reset accelerometer signal path. + * + * The reset will revert the signal path analog to digital converters and + * filters to their power up configurations. + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_reset_accelerometer_path(mpu6050_dev_t *dev); + +/** + * @brief Reset temperature sensor signal path. + * + * The reset will revert the signal path analog to digital converters and + * filters to their power up configurations. + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_reset_temperature_path(mpu6050_dev_t *dev); + +/** + * @brief Get accelerometer power-on delay. + * + * The accelerometer data path provides samples to the sensor registers, Motion + * detection, Zero Motion detection, and Free Fall detection modules. The + * signal path contains filters which must be flushed on wake-up with new + * samples before the detection modules begin operations. The default wake-up + * delay, of 4ms can be lengthened by up to 3ms. This additional delay is + * specified in ACCEL_ON_DELAY in units of 1 LSB = 1 ms. The user may select + * any value above zero unless instructed otherwise by InvenSense. Please refer + * to Section 8 of the MPU-6000/MPU-6050 Product Specification document for + * further information regarding the detection modules. + * + * @param dev Device descriptor + * @param[out] delay Current accelerometer power-on delay. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_accelerometer_power_on_delay(mpu6050_dev_t *dev, uint8_t *delay); + +/** + * @brief Set accelerometer power-on delay. + * + * @param dev Device descriptor + * @param delay New accelerometer power-on delay (0-3). + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_accelerometer_power_on_delay(mpu6050_dev_t *dev, uint8_t delay); + +/** + * @brief Get Free Fall detection counter decrement configuration. + * + * Detection is registered by the Free Fall detection module after accelerometer + * measurements meet their respective threshold conditions over a specified + * number of samples. When the threshold conditions are met, the corresponding + * detection counter increments by 1. The user may control the rate at which the + * detection counter decrements when the threshold condition is not met by + * configuring FF_COUNT. The decrement rate can be set according to the + * following table: + * + * |FF_COUNT | Counter Decrement| + * |---------|------------------| + * |0 | Reset | + * |1 | 1 | + * |2 | 2 | + * |3 | 4 | + * + * When FF_COUNT is configured to 0 (reset), any non-qualifying sample will + * reset the counter to 0. For further information on Free Fall detection, + * please refer to Registers 29 to 32. + * + * @param dev Device descriptor + * @param[out] decrement Current decrement configuration. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_freefall_detection_counter_decrement(mpu6050_dev_t *dev, uint8_t *decrement); + +/** + * @brief Set Free Fall detection counter decrement configuration. + * + * @param dev Device descriptor + * @param decrement New decrement configuration value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_freefall_detection_counter_decrement(mpu6050_dev_t *dev, uint8_t decrement); + +/** + * @brief Get Motion detection counter decrement configuration. + * + * Detection is registered by the Motion detection module after accelerometer + * measurements meet their respective threshold conditions over a specified + * number of samples. When the threshold conditions are met, the corresponding + * detection counter increments by 1. The user may control the rate at which the + * detection counter decrements when the threshold condition is not met by + * configuring MOT_COUNT. The decrement rate can be set according to the + * following table: + * + * |MOT_COUNT| Counter Decrement| + * |---------|------------------| + * |0 | Reset | + * |1 | 1 | + * |2 | 2 | + * |3 | 4 | + * + * When MOT_COUNT is configured to 0 (reset), any non-qualifying sample will + * reset the counter to 0. + * + * @param dev Device descriptor + * @param[out] decrement New decrement configuration value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_motion_detection_counter_decrement(mpu6050_dev_t *dev, uint8_t *decrement); + +/** + * @brief Set Motion detection counter decrement configuration. + * + * @param dev Device descriptor + * @param decrement New decrement configuration value. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_motion_detection_counter_decrement(mpu6050_dev_t *dev, uint8_t decrement); + +/** + * @brief Get FIFO enabled status. + * + * When this bit is set to 0, the FIFO buffer is disabled. The FIFO buffer + * cannot be written to or read from while disabled. The FIFO buffer's state + * does not change unless the MPU-60X0 is power cycled. + * + * @param dev Device descriptor + * @param[out] enabled FIFO enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_fifo_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set FIFO enabled status. + * + * @param dev Device descriptor + * @param enabled New FIFO enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_fifo_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get I2C Master Mode enabled status. + * + * When this mode is enabled, the MPU-60X0 acts as the I2C Master to the + * external sensor slave devices on the auxiliary I2C bus. When this bit is + * cleared to 0, the auxiliary I2C bus lines (AUX_DA and AUX_CL) are logically + * driven by the primary I2C bus (SDA and SCL). This is a precondition to + * enabling Bypass Mode. + * + * @param dev Device descriptor + * @param[out] enabled I2C Master Mode enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_i2c_master_mode_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set I2C Master Mode enabled status. + * + * @param dev Device descriptor + * @param enabled New I2C Master Mode enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_i2c_master_mode_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Switch from I2C to SPI mode (MPU-6000 only). + * + * If this is set, the primary SPI interface will be enabled in place of the + * disabled primary I2C interface. + * + * Note: This driver does not support SPI mode! + * + * @param dev Device descriptor + * @param enabled New switch SPIE Mode enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_switch_spie_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Reset the FIFO. + * + * This bit resets the FIFO buffer when set to 1 while FIFO_EN equals 0. This + * bit automatically clears to 0 after the reset has been triggered. + * + * @param dev Device descriptor + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_reset_fifo(mpu6050_dev_t *dev); + +/** + * @brief Reset all sensor registers and signal paths. + * + * When set to 1, this bit resets the signal paths for all sensors (gyroscopes, + * accelerometers, and temperature sensor). This operation will also clear the + * sensor registers. This bit automatically clears to 0 after the reset has been + * triggered. + * + * When resetting only the signal path (and not the sensor registers), please + * use Register 104, SIGNAL_PATH_RESET. + * + * @param dev Device descriptor + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_reset_sensors(mpu6050_dev_t *dev); + +/** + * @brief Trigger a full device reset. + * + * A small delay of ~50ms may be desirable after triggering a reset. + * + * @param dev Device descriptor + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_reset(mpu6050_dev_t *dev); + +/** + * @brief Get sleep mode status. + * + * The SLEEP bit in the register puts the device into very low power + * sleep mode. In this mode, only the serial interface and internal registers + * remain active, allowing for a very low standby current. Clearing this bit + * puts the device back into normal mode. To save power, the individual standby + * selections for each of the gyros should be used if any gyro axis is not used + * by the application. + * + * @param dev Device descriptor + * @param[out] enabled Sleep mode enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_sleep_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set sleep mode status. + * + * @param dev Device descriptor + * @param enabled New sleep mode enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_sleep_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get wake cycle enabled status. + * + * When this bit is set to 1 and SLEEP is disabled, the MPU-60X0 will cycle + * between sleep mode and waking up to take a single sample of data from active + * sensors at a rate determined by LP_WAKE_CTRL (Register 108). + * + * @param dev Device descriptor + * @param[out] enabled Wake cycle enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_wake_cycle_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set wake cycle enabled status. + * + * @param dev Device descriptor + * @param enabled Wake cycle enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_wake_cycle_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get temperature sensor enabled status. + * + * Control the usage of the internal temperature sensor. + * + * Note: this register stores the *disabled* value, but for consistency with the + * rest of the code, the function is named and used with standard true/false + * values to indicate whether the sensor is enabled or disabled, respectively. + * + * @param dev Device descriptor + * @param[out] enabled Temperature sensor enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_temp_sensor_enabled(mpu6050_dev_t *dev, bool *enabled); + +/** + * @brief Set temperature sensor enabled status. + * + * Note: this register stores the *disabled* value, but for consistency with the + * rest of the code, the function is named and used with standard true/false + * values to indicate whether the sensor is enabled or disabled, respectively. + * + * @param dev Device descriptor + * @param enabled New temperature sensor enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_temp_sensor_enabled(mpu6050_dev_t *dev, bool enabled); + +/** + * @brief Get clock source setting. + * + * An internal 8MHz oscillator, gyroscope based clock, or external sources can + * be selected as the MPU-60X0 clock source. When the internal 8 MHz oscillator + * or an external source is chosen as the clock source, the MPU-60X0 can operate + * in low power modes with the gyroscopes disabled. + * + * Upon power up, the MPU-60X0 clock source defaults to the internal oscillator. + * However, it is highly recommended that the device be configured to use one of + * the gyroscopes (or an external clock source) as the clock reference for + * improved stability. + * + * @param dev Device descriptor + * @param[out] source Current clock source setting. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_clock_source(mpu6050_dev_t *dev, mpu6050_clock_source_t *source); + +/** + * @brief Set clock source setting. + * + * @param dev Device descriptor + * @param source New clock source setting. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_clock_source(mpu6050_dev_t *dev, mpu6050_clock_source_t source); + +/** + * @brief Get wake frequency in Accel-Only Low Power Mode. + * + * The MPU-60X0 can be put into Accelerometer Only Low Power Mode by setting + * PWRSEL to 1 in the Power Management 1 register (Register 107). In this mode, + * the device will power off all devices except for the primary I2C interface, + * waking only the accelerometer at fixed intervals to take a single + * measurement. + * + * For further information regarding the MPU-60X0's power modes, please refer to + * Register 107. + * + * @param dev Device descriptor + * @param[out] frequency Current wake frequency. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_wake_frequency(mpu6050_dev_t *dev, mpu6050_wake_freq_t *frequency); + +/** + * @brief Set wake frequency in Accel-Only Low Power Mode. + * + * @param dev Device descriptor + * @param frequency New wake frequency. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_wake_frequency(mpu6050_dev_t *dev, mpu6050_wake_freq_t frequency); + +/** + * @brief Get accelerometer axis standby enabled status. + * + * If enabled, the axis will not gather or report data (or use power). + * + * @param dev Device descriptor + * @param axis Accelerometer axis. + * @param[out] enabled Accelerometer axis standby enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_standby_accel_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool *enabled); + +/** + * @brief Set accelerometer axis standby enabled status. + * + * @param dev Device descriptor + * @param axis Accelerometer axis. + * @param enabled Accelerometer axis standby enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_standby_accel_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool enabled); + +/** + * @brief Get gyroscope axis standby enabled status. + * + * If enabled, the axis will not gather or report data (or use power). + * + * @param dev Device descriptor + * @param axis Gyroscope axis. + * @param[out] enabled Gyroscope axis standby enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_standby_gyro_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool *enabled); + +/** + * @brief Set gyroscope axis standby enabled status. + * + * If enabled, the axis will not gather or report data (or use power). + * + * @param dev Device descriptor + * @param axis Gyroscope axis. + * @param enabled Gyroscope axis standby enabled status. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_standby_gyro_enabled(mpu6050_dev_t *dev, mpu6050_axis_t axis, bool enabled); + +/** + * @brief Get current FIFO buffer size. + * + * This value indicates the number of bytes stored in the FIFO buffer. This + * number is in turn the number of bytes that can be read from the FIFO buffer + * and it is directly proportional to the number of samples available given the + * set of sensor data bound to be stored in the FIFO (Register 35 and 36). + * + * @param dev Device descriptor + * @param[out] count Current FIFO buffer size. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_fifo_count(mpu6050_dev_t *dev, uint16_t *count); + +/** + * @brief Get byte from FIFO buffer. + * + * This register is used to read and write data from the FIFO buffer. Data is + * written to the FIFO in order of register number (from lowest to highest). If + * all the FIFO enable flags (see below) are enabled and all External Sensor + * Data registers (Registers 73 to 96) are associated with a Slave device, the + * contents of registers 59 through 96 will be written in order at the Sample + * Rate. + * + * The contents of the sensor data registers (Registers 59 to 96) are written + * into the FIFO buffer when their corresponding FIFO enable flags are set to 1 + * in FIFO_EN (Register 35). An additional flag for the sensor data registers + * associated with I2C Slave 3 can be found in I2C_MST_CTRL (Register 36). + * + * If the FIFO buffer has overflowed, the status bit FIFO_OFLOW_INT is + * automatically set to 1. This bit is located in INT_STATUS (Register 58). + * When the FIFO buffer has overflowed, the oldest data will be lost and new + * data will be written to the FIFO. + * + * If the FIFO buffer is empty, reading this register will return the last byte + * that was previously read from the FIFO until new data is available. The user + * should check FIFO_COUNT to ensure that the FIFO buffer is not read when + * empty. + * + * @param dev Device descriptor + * @param[out] data Byte from FIFO buffer. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_fifo_byte(mpu6050_dev_t *dev, uint8_t *data); + +/** + * @brief Get bytes from FIFO buffer. + * + * @param dev Device descriptor + * @param[out] data Buffer to store read bytes + * @param length How many bytes to read + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_fifo_bytes(mpu6050_dev_t *dev, uint8_t *data, size_t length); + +/** + * @brief Write byte to FIFO buffer. + * + * @param dev Device descriptor + * @param data New FIFO byte of data. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_set_fifo_byte(mpu6050_dev_t *dev, uint8_t data); + +/** + * @brief Get the ID of the device. + * + * Device identity is stored in the WHO_AM_I register. + * The device ID is 6 bits (Should be 0x34). + * + * @param dev Device descriptor + * @param[out] id Device ID. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_get_device_id(mpu6050_dev_t *dev, uint8_t *id); + +/** + * @brief Function which accumulates gyro and accelerometer data after device initialization. + * + * It calculates the average of the at-rest readings and then loads the + * resulting offsets into accelerometer and gyro bias registers. + * + * @param dev Device descriptor + * @param[out] accel_bias_res Acceleration bias resolution. + * @param[out] gyro_bias_res Rotation bias resolution. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_calibrate(mpu6050_dev_t *dev, float *accel_bias_res, float *gyro_bias_res); + +/** + * @brief Accelerometer and gyroscope self test. + * + * Check calibration WRT factory settings. + * + * @param dev Device descriptor + * @param[out] destination Where the result of the self test will be stored. + * + * @return `ESP_OK` on success + */ +esp_err_t mpu6050_self_test(mpu6050_dev_t *dev, float *destination); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __MPU6050_H__ */ diff --git a/components/mpu6050/mpu6050_regs.h b/components/mpu6050/mpu6050_regs.h new file mode 100644 index 00000000..882420d1 --- /dev/null +++ b/components/mpu6050/mpu6050_regs.h @@ -0,0 +1,353 @@ +#ifndef __MPU6050_REGS_H__ +#define __MPU6050_REGS_H__ + +#define MPU6050_REGISTER_XG_OFFS_TC (0) +#define MPU6050_REGISTER_YG_OFFS_TC (0x01) +#define MPU6050_REGISTER_ZG_OFFS_TC (0x02) +#define MPU6050_REGISTER_X_FINE_GAIN (0x03) +#define MPU6050_REGISTER_Y_FINE_GAIN (0x04) +#define MPU6050_REGISTER_Z_FINE_GAIN (0x05) +#define MPU6050_REGISTER_XA_OFFS_H (0x06) +#define MPU6050_REGISTER_XA_OFFS_L_TC (0x07) +#define MPU6050_REGISTER_YA_OFFS_H (0x08) +#define MPU6050_REGISTER_YA_OFFS_L_TC (0x09) +#define MPU6050_REGISTER_ZA_OFFS_H (0x0A) +#define MPU6050_REGISTER_ZA_OFFS_L_TC (0x0B) +#define MPU6050_REGISTER_SELF_TEST_X (0x0D) +#define MPU6050_REGISTER_SELF_TEST_Y (0x0E) +#define MPU6050_REGISTER_SELF_TEST_Z (0x0F) +#define MPU6050_REGISTER_SELF_TEST_A (0x10) +#define MPU6050_REGISTER_XG_OFFS_USRH (0x13) +#define MPU6050_REGISTER_XG_OFFS_USRL (0x14) +#define MPU6050_REGISTER_YG_OFFS_USRH (0x15) +#define MPU6050_REGISTER_YG_OFFS_USRL (0x16) +#define MPU6050_REGISTER_ZG_OFFS_USRH (0x17) +#define MPU6050_REGISTER_ZG_OFFS_USRL (0x18) +#define MPU6050_REGISTER_SMPLRT_DIV (0x19) +#define MPU6050_REGISTER_CONFIG (0x1A) +#define MPU6050_REGISTER_GYRO_CONFIG (0x1B) +#define MPU6050_REGISTER_ACCEL_CONFIG (0x1C) +#define MPU6050_REGISTER_FF_THR (0x1D) +#define MPU6050_REGISTER_FF_DUR (0x1E) +#define MPU6050_REGISTER_MOT_THR (0x1F) +#define MPU6050_REGISTER_MOT_DUR (0x20) +#define MPU6050_REGISTER_ZRMOT_THR (0x21) +#define MPU6050_REGISTER_ZRMOT_DUR (0x22) +#define MPU6050_REGISTER_FIFO_EN (0x23) +#define MPU6050_REGISTER_I2C_MST_CTRL (0x24) +#define MPU6050_REGISTER_I2C_SLV0_ADDR (0x25) +#define MPU6050_REGISTER_I2C_SLV0_REG (0x26) +#define MPU6050_REGISTER_I2C_SLV0_CTRL (0x27) +#define MPU6050_REGISTER_I2C_SLV1_ADDR (0x28) +#define MPU6050_REGISTER_I2C_SLV1_REG (0x29) +#define MPU6050_REGISTER_I2C_SLV1_CTRL (0x2A) +#define MPU6050_REGISTER_I2C_SLV2_ADDR (0x2B) +#define MPU6050_REGISTER_I2C_SLV2_REG (0x2C) +#define MPU6050_REGISTER_I2C_SLV2_CTRL (0x2D) +#define MPU6050_REGISTER_I2C_SLV3_ADDR (0x2E) +#define MPU6050_REGISTER_I2C_SLV3_REG (0x2F) +#define MPU6050_REGISTER_I2C_SLV3_CTRL (0x30) +#define MPU6050_REGISTER_I2C_SLV4_ADDR (0x31) +#define MPU6050_REGISTER_I2C_SLV4_REG (0x32) +#define MPU6050_REGISTER_I2C_SLV4_DO (0x33) +#define MPU6050_REGISTER_I2C_SLV4_CTRL (0x34) +#define MPU6050_REGISTER_I2C_SLV4_DI (0x35) +#define MPU6050_REGISTER_I2C_MST_STATUS (0x36) +#define MPU6050_REGISTER_INT_PIN_CFG (0x37) +#define MPU6050_REGISTER_INT_ENABLE (0x38) +#define MPU6050_REGISTER_DMP_INT_STATUS (0x39) +#define MPU6050_REGISTER_INT_STATUS (0x3A) +#define MPU6050_REGISTER_ACCEL_XOUT_H (0x3B) +#define MPU6050_REGISTER_ACCEL_XOUT_L (0x3C) +#define MPU6050_REGISTER_ACCEL_YOUT_H (0x3D) +#define MPU6050_REGISTER_ACCEL_YOUT_L (0x3E) +#define MPU6050_REGISTER_ACCEL_ZOUT_H (0x3F) +#define MPU6050_REGISTER_ACCEL_ZOUT_L (0x40) +#define MPU6050_REGISTER_TEMP_OUT_H (0x41) +#define MPU6050_REGISTER_TEMP_OUT_L (0x42) +#define MPU6050_REGISTER_GYRO_XOUT_H (0x43) +#define MPU6050_REGISTER_GYRO_XOUT_L (0x44) +#define MPU6050_REGISTER_GYRO_YOUT_H (0x45) +#define MPU6050_REGISTER_GYRO_YOUT_L (0x46) +#define MPU6050_REGISTER_GYRO_ZOUT_H (0x47) +#define MPU6050_REGISTER_GYRO_ZOUT_L (0x48) +#define MPU6050_REGISTER_EXT_SENS_DATA_00 (0x49) +#define MPU6050_REGISTER_EXT_SENS_DATA_01 (0x4A) +#define MPU6050_REGISTER_EXT_SENS_DATA_02 (0x4B) +#define MPU6050_REGISTER_EXT_SENS_DATA_03 (0x4C) +#define MPU6050_REGISTER_EXT_SENS_DATA_04 (0x4D) +#define MPU6050_REGISTER_EXT_SENS_DATA_05 (0x4E) +#define MPU6050_REGISTER_EXT_SENS_DATA_06 (0x4F) +#define MPU6050_REGISTER_EXT_SENS_DATA_07 (0x50) +#define MPU6050_REGISTER_EXT_SENS_DATA_08 (0x51) +#define MPU6050_REGISTER_EXT_SENS_DATA_09 (0x52) +#define MPU6050_REGISTER_EXT_SENS_DATA_10 (0x53) +#define MPU6050_REGISTER_EXT_SENS_DATA_11 (0x54) +#define MPU6050_REGISTER_EXT_SENS_DATA_12 (0x55) +#define MPU6050_REGISTER_EXT_SENS_DATA_13 (0x56) +#define MPU6050_REGISTER_EXT_SENS_DATA_14 (0x57) +#define MPU6050_REGISTER_EXT_SENS_DATA_15 (0x58) +#define MPU6050_REGISTER_EXT_SENS_DATA_16 (0x59) +#define MPU6050_REGISTER_EXT_SENS_DATA_17 (0x5A) +#define MPU6050_REGISTER_EXT_SENS_DATA_18 (0x5B) +#define MPU6050_REGISTER_EXT_SENS_DATA_19 (0x5C) +#define MPU6050_REGISTER_EXT_SENS_DATA_20 (0x5D) +#define MPU6050_REGISTER_EXT_SENS_DATA_21 (0x5E) +#define MPU6050_REGISTER_EXT_SENS_DATA_22 (0x5F) +#define MPU6050_REGISTER_EXT_SENS_DATA_23 (0x60) +#define MPU6050_REGISTER_MOT_DETECT_STATUS (0x61) +#define MPU6050_REGISTER_I2C_SLV0_DO (0x63) +#define MPU6050_REGISTER_I2C_SLV1_DO (0x64) +#define MPU6050_REGISTER_I2C_SLV2_DO (0x65) +#define MPU6050_REGISTER_I2C_SLV3_DO (0x66) +#define MPU6050_REGISTER_I2C_MST_DELAY_CTRL (0x67) +#define MPU6050_REGISTER_SIGNAL_PATH_RESET (0x68) +#define MPU6050_REGISTER_MOT_DETECT_CTRL (0x69) +#define MPU6050_REGISTER_USER_CTRL (0x6A) +#define MPU6050_REGISTER_PWR_MGMT_1 (0x6B) +#define MPU6050_REGISTER_PWR_MGMT_2 (0x6C) +#define MPU6050_REGISTER_BANK_SEL (0x6D) +#define MPU6050_REGISTER_MEM_START_ADDR (0x6E) +#define MPU6050_REGISTER_MEM_R_W (0x6F) +#define MPU6050_REGISTER_DMP_CFG_1 (0x70) +#define MPU6050_REGISTER_DMP_CFG_2 (0x71) +#define MPU6050_REGISTER_FIFO_COUNTH (0x72) +#define MPU6050_REGISTER_FIFO_COUNTL (0x73) +#define MPU6050_REGISTER_FIFO_R_W (0x74) +#define MPU6050_REGISTER_WHO_AM_I (0x75) + +// DLPF values +#define MPU6050_DLPF_BW_256 (0x00) +#define MPU6050_DLPF_BW_188 (0x01) +#define MPU6050_DLPF_BW_98 (0x02) +#define MPU6050_DLPF_BW_42 (0x03) +#define MPU6050_DLPF_BW_20 (0x04) +#define MPU6050_DLPF_BW_10 (0x05) +#define MPU6050_DLPF_BW_5 (0x06) + +// DHPF values: +#define MPU6050_DHPF_RESET (0x00) +#define MPU6050_DHPF_5 (0x01) +#define MPU6050_DHPF_2P5 (0x02) +#define MPU6050_DHPF_1P25 (0x03) +#define MPU6050_DHPF_0P63 (0x04) +#define MPU6050_DHPF_HOLD (0x07) + + +// Decrement values: +#define MPU6050_DETECT_DECREMENT_RESET (0x0) +#define MPU6050_DETECT_DECREMENT_1 (0x1) +#define MPU6050_DETECT_DECREMENT_2 (0x2) +#define MPU6050_DETECT_DECREMENT_4 (0x3) + +// External sync values: +#define MPU6050_EXT_SYNC_DISABLED (0x0) +#define MPU6050_EXT_SYNC_TEMP_OUT_L (0x1) +#define MPU6050_EXT_SYNC_GYRO_XOUT_L (0x2) +#define MPU6050_EXT_SYNC_GYRO_YOUT_L (0x3) +#define MPU6050_EXT_SYNC_GYRO_ZOUT_L (0x4) +#define MPU6050_EXT_SYNC_ACCEL_XOUT_L (0x5) +#define MPU6050_EXT_SYNC_ACCEL_YOUT_L (0x6) +#define MPU6050_EXT_SYNC_ACCEL_ZOUT_L (0x7) + +// Clock division values: +#define MPU6050_CLOCK_DIV_348 (0x0) +#define MPU6050_CLOCK_DIV_333 (0x1) +#define MPU6050_CLOCK_DIV_320 (0x2) +#define MPU6050_CLOCK_DIV_308 (0x3) +#define MPU6050_CLOCK_DIV_296 (0x4) +#define MPU6050_CLOCK_DIV_286 (0x5) +#define MPU6050_CLOCK_DIV_276 (0x6) +#define MPU6050_CLOCK_DIV_267 (0x7) +#define MPU6050_CLOCK_DIV_258 (0x8) +#define MPU6050_CLOCK_DIV_500 (0x9) +#define MPU6050_CLOCK_DIV_471 (0xA) +#define MPU6050_CLOCK_DIV_444 (0xB) +#define MPU6050_CLOCK_DIV_421 (0xC) +#define MPU6050_CLOCK_DIV_400 (0xD) +#define MPU6050_CLOCK_DIV_381 (0xE) +#define MPU6050_CLOCK_DIV_364 (0xF) + +// Bit and length defines for SELF_TEST register: +#define MPU6050_SELF_TEST_XA_1_BIT (0x07) +#define MPU6050_SELF_TEST_XA_1_LENGTH (0x03) +#define MPU6050_SELF_TEST_XA_2_BIT (0x05) +#define MPU6050_SELF_TEST_XA_2_LENGTH (0x02) +#define MPU6050_SELF_TEST_YA_1_BIT (0x07) +#define MPU6050_SELF_TEST_YA_1_LENGTH (0x03) +#define MPU6050_SELF_TEST_YA_2_BIT (0x03) +#define MPU6050_SELF_TEST_YA_2_LENGTH (0x02) +#define MPU6050_SELF_TEST_ZA_1_BIT (0x07) +#define MPU6050_SELF_TEST_ZA_1_LENGTH (0x03) +#define MPU6050_SELF_TEST_ZA_2_BIT (0x01) +#define MPU6050_SELF_TEST_ZA_2_LENGTH (0x02) +#define MPU6050_SELF_TEST_XG_1_BIT (0x04) +#define MPU6050_SELF_TEST_XG_1_LENGTH (0x05) +#define MPU6050_SELF_TEST_YG_1_BIT (0x04) +#define MPU6050_SELF_TEST_YG_1_LENGTH (0x05) +#define MPU6050_SELF_TEST_ZG_1_BIT (0x04) +#define MPU6050_SELF_TEST_ZG_1_LENGTH (0x05) + +// Bit and length defines for CONFIG register: +#define MPU6050_CFG_EXT_SYNC_SET_BIT (3) +#define MPU6050_CFG_EXT_SYNC_SET_MASK (7 << MPU6050_CFG_EXT_SYNC_SET_BIT) +#define MPU6050_CFG_DLPF_CFG_BIT (0) +#define MPU6050_CFG_DLPF_CFG_MASK (7 << MPU6050_CFG_DLPF_CFG_BIT) + +// Bit and length defines for GYRO_CONFIG register: +#define MPU6050_GCONFIG_FS_SEL_BIT (3) +#define MPU6050_GCONFIG_FS_SEL_MASK (3 << MPU6050_GCONFIG_FS_SEL_BIT) + +// Bit and length defines for ACCEL_CONFIG register: +#define MPU6050_ACONFIG_XA_ST_BIT (7) +#define MPU6050_ACONFIG_YA_ST_BIT (6) +#define MPU6050_ACONFIG_ZA_ST_BIT (5) +#define MPU6050_ACONFIG_AFS_SEL_BIT (3) +#define MPU6050_ACONFIG_AFS_SEL_MASK (3 << MPU6050_ACONFIG_AFS_SEL_BIT) +#define MPU6050_ACONFIG_ACCEL_HPF_BIT (0) +#define MPU6050_ACONFIG_ACCEL_HPF_MASK (7 << MPU6050_ACONFIG_ACCEL_HPF_BIT) + +// Bit and length defines for FIFO_EN register: +#define MPU6050_TEMP_FIFO_EN_BIT (7) +#define MPU6050_XG_FIFO_EN_BIT (6) +#define MPU6050_YG_FIFO_EN_BIT (5) +#define MPU6050_ZG_FIFO_EN_BIT (4) +#define MPU6050_ACCEL_FIFO_EN_BIT (3) +#define MPU6050_SLV2_FIFO_EN_BIT (2) +#define MPU6050_SLV1_FIFO_EN_BIT (1) +#define MPU6050_SLV0_FIFO_EN_BIT (0) + +// Bit and length defines for I2C_MST_CTRL register: +#define MPU6050_MULT_MST_EN_BIT (7) +#define MPU6050_WAIT_FOR_ES_BIT (6) +#define MPU6050_SLV_3_FIFO_EN_BIT (5) +#define MPU6050_I2C_MST_P_NSR_BIT (4) +#define MPU6050_I2C_MST_CLK_BIT (0) +#define MPU6050_I2C_MST_CLK_MASK (7 << MPU6050_I2C_MST_CLK_BIT) + +// Bit and length defines for I2C_SLV* register: +#define MPU6050_I2C_SLV_RW_BIT (7) +#define MPU6050_I2C_SLV_ADDR_BIT (6) +#define MPU6050_I2C_SLV_ADDR_LENGTH (7) +#define MPU6050_I2C_SLV_EN_BIT (7) +#define MPU6050_I2C_SLV_BYTE_SW_BIT (6) +#define MPU6050_I2C_SLV_REG_DIS_BIT (5) +#define MPU6050_I2C_SLV_GRP_BIT (4) +#define MPU6050_I2C_SLV_LEN_BIT (0) +#define MPU6050_I2C_SLV_LEN_MASK (7 << MPU6050_I2C_SLV_LEN_BIT) + +// Bit and length defines for I2C_SLV4 register: +#define MPU6050_I2C_SLV4_RW_BIT (7) +#define MPU6050_I2C_SLV4_ADDR_BIT (6) +#define MPU6050_I2C_SLV4_ADDR_LENGTH (7) +#define MPU6050_I2C_SLV4_EN_BIT (7) +#define MPU6050_I2C_SLV4_INT_EN_BIT (6) +#define MPU6050_I2C_SLV4_REG_DIS_BIT (5) +#define MPU6050_I2C_SLV4_MST_DLY_BIT (4) +#define MPU6050_I2C_SLV4_MST_DLY_LENGTH (5) + +// Bit and length defines for I2C_MST_STATUS register: +#define MPU6050_MST_PASS_THROUGH_BIT (7) +#define MPU6050_MST_I2C_SLV4_DONE_BIT (6) +#define MPU6050_MST_I2C_LOST_ARB_BIT (5) +#define MPU6050_MST_I2C_SLV4_NACK_BIT (4) +#define MPU6050_MST_I2C_SLV3_NACK_BIT (3) +#define MPU6050_MST_I2C_SLV2_NACK_BIT (2) +#define MPU6050_MST_I2C_SLV1_NACK_BIT (1) +#define MPU6050_MST_I2C_SLV0_NACK_BIT (0) + +// Bit and length defines for INT_PIN_CFG register: +#define MPU6050_INTCFG_INT_LEVEL_BIT (7) +#define MPU6050_INTCFG_INT_OPEN_BIT (6) +#define MPU6050_INTCFG_LATCH_INT_EN_BIT (5) +#define MPU6050_INTCFG_INT_RD_CLEAR_BIT (4) +#define MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT (3) +#define MPU6050_INTCFG_FSYNC_INT_EN_BIT (2) +#define MPU6050_INTCFG_I2C_BYPASS_EN_BIT (1) +#define MPU6050_INTCFG_CLKOUT_EN_BIT (0) + +// Bit and length defines for INT_ENABLE and INT_STATUS registers: +#define MPU6050_INTERRUPT_FF_BIT (7) +#define MPU6050_INTERRUPT_MOT_BIT (6) +#define MPU6050_INTERRUPT_ZMOT_BIT (5) +#define MPU6050_INTERRUPT_FIFO_OFLOW_BIT (4) +#define MPU6050_INTERRUPT_I2C_MST_INT_BIT (3) +#define MPU6050_INTERRUPT_PLL_RDY_INT_BIT (2) +#define MPU6050_INTERRUPT_DMP_INT_BIT (1) +#define MPU6050_INTERRUPT_DATA_RDY_BIT (0) + +// Bit and length defines for MOT_DETECT_STATUS register: +#define MPU6050_MOTION_MOT_XNEG_BIT (7) +#define MPU6050_MOTION_MOT_XPOS_BIT (6) +#define MPU6050_MOTION_MOT_YNEG_BIT (5) +#define MPU6050_MOTION_MOT_YPOS_BIT (4) +#define MPU6050_MOTION_MOT_ZNEG_BIT (3) +#define MPU6050_MOTION_MOT_ZPOS_BIT (2) +#define MPU6050_MOTION_MOT_ZRMOT_BIT (0) + +// Bit and length defines for I2C_MST_DELAY_CTRL register: +#define MPU6050_DLYCTRL_DELAY_ES_SHADOW_BIT (7) +#define MPU6050_DLYCTRL_I2C_SLV4_DLY_EN_BIT (4) +#define MPU6050_DLYCTRL_I2C_SLV3_DLY_EN_BIT (3) +#define MPU6050_DLYCTRL_I2C_SLV2_DLY_EN_BIT (2) +#define MPU6050_DLYCTRL_I2C_SLV1_DLY_EN_BIT (1) +#define MPU6050_DLYCTRL_I2C_SLV0_DLY_EN_BIT (0) + +// Bit and length defines for SIGNAL_PATH_RESET register: +#define MPU6050_PATHRESET_GYRO_RESET_BIT (2) +#define MPU6050_PATHRESET_ACCEL_RESET_BIT (1) +#define MPU6050_PATHRESET_TEMP_RESET_BIT (0) + +// Bit and length defines for MOT_DETECT_CTRL register: +#define MPU6050_DETECT_ACCEL_DELAY_BIT (4) +#define MPU6050_DETECT_ACCEL_DELAY_MASK (3 << MPU6050_DETECT_ACCEL_DELAY_BIT) +#define MPU6050_DETECT_FF_COUNT_BIT (2) +#define MPU6050_DETECT_FF_COUNT_MASK (3 << MPU6050_DETECT_FF_COUNT_BIT) +#define MPU6050_DETECT_MOT_COUNT_BIT (0) +#define MPU6050_DETECT_MOT_COUNT_MASK (3 << MPU6050_DETECT_MOT_COUNT_BIT) + +// Bit and length defines for USER_CTRL register: +#define MPU6050_USERCTRL_DMP_EN_BIT (7) +#define MPU6050_USERCTRL_FIFO_EN_BIT (6) +#define MPU6050_USERCTRL_I2C_MST_EN_BIT (5) +#define MPU6050_USERCTRL_I2C_IF_DIS_BIT (4) +#define MPU6050_USERCTRL_DMP_RESET_BIT (3) +#define MPU6050_USERCTRL_FIFO_RESET_BIT (2) +#define MPU6050_USERCTRL_I2C_MST_RESET_BIT (1) +#define MPU6050_USERCTRL_SIG_COND_RESET_BIT (0) + +// Bit and length defines for PWR_MGMT_1 register: +#define MPU6050_PWR1_DEVICE_RESET_BIT (7) +#define MPU6050_PWR1_SLEEP_BIT (6) +#define MPU6050_PWR1_CYCLE_BIT (5) +#define MPU6050_PWR1_TEMP_DIS_BIT (3) +#define MPU6050_PWR1_CLKSEL_BIT (0) +#define MPU6050_PWR1_CLKSEL_MASK (7 << MPU6050_PWR1_CLKSEL_BIT) + +// Bit and length defines for PWR_MGMT_2 register: +#define MPU6050_PWR2_LP_WAKE_CTRL_BIT (6) +#define MPU6050_PWR2_LP_WAKE_CTRL_MASK (3 << MPU6050_PWR2_LP_WAKE_CTRL_BIT) +#define MPU6050_PWR2_STBY_XA_BIT (5) +#define MPU6050_PWR2_STBY_YA_BIT (4) +#define MPU6050_PWR2_STBY_ZA_BIT (3) +#define MPU6050_PWR2_STBY_XG_BIT (2) +#define MPU6050_PWR2_STBY_YG_BIT (1) +#define MPU6050_PWR2_STBY_ZG_BIT (0) + +// Bit and length defines for WHO_AM_I register: +#define MPU6050_WHO_AM_I_BIT (1) +#define MPU6050_WHO_AM_I_MASK (0x3f << MPU6050_WHO_AM_I_BIT) + +// Undocumented bits and lengths: +#define MPU6050_TC_PWR_MODE_BIT (7) +#define MPU6050_TC_OFFSET_BIT (6) +#define MPU6050_TC_OFFSET_LENGTH (6) +#define MPU6050_TC_OTP_BNK_VLD_BIT (0) +#define MPU6050_DMPINT_5_BIT (5) +#define MPU6050_DMPINT_4_BIT (4) +#define MPU6050_DMPINT_3_BIT (3) +#define MPU6050_DMPINT_2_BIT (2) +#define MPU6050_DMPINT_1_BIT (1) +#define MPU6050_DMPINT_0_BIT (0) + +#endif // __MPU6050_REGS_H__ diff --git a/components/ms5611/.eil.yml b/components/ms5611/.eil.yml index 47566d71..718b05b5 100644 --- a/components/ms5611/.eil.yml +++ b/components/ms5611/.eil.yml @@ -1,27 +1,23 @@ ---- -components: - - name: ms5611 - description: Driver for barometic pressure sensor MS5611-01BA03 - group: pressure - groups: - - name: temperature - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2018 - - author: - name: BernhardG - year: 2016 +name: ms5611 +description: Driver for barometic pressure sensor MS5611-01BA03 +version: 1.1.0 +groups: + - pressure + - temperature +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: BernhardG + year: 2016 + - name: UncleRus + year: 2018 diff --git a/components/noise/.eil.yml b/components/noise/.eil.yml index dfba8816..1415446e 100644 --- a/components/noise/.eil.yml +++ b/components/noise/.eil.yml @@ -1,21 +1,18 @@ ---- -components: - - name: noise - description: Noise generation functions - group: common - groups: [] - code_owners: UncleRus - depends: - - lib8tion - thread_safe: N/A - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: FastLED - year: 2013 +name: noise +description: Noise generation functions +version: 1.0.0 +groups: + - common +code_owners: UncleRus +depends: + - lib8tion +thread_safe: n/a +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: FastLED + year: 2013 diff --git a/components/noise/noise.h b/components/noise/noise.h index f74742a5..c463792b 100644 --- a/components/noise/noise.h +++ b/components/noise/noise.h @@ -29,6 +29,10 @@ ///@file noise.h /// Noise functions provided by the library. +#ifdef __cplusplus +extern "C" { +#endif + ///@defgroup Noise Noise functions ///Perlin noise function definitions ///@{ @@ -89,4 +93,8 @@ void fill_raw_noise16into8(uint8_t *pData, uint8_t num_points, uint8_t octaves, ///@} ///@} +#ifdef __cplusplus +} +#endif + #endif /* __NOISE_H__ */ diff --git a/components/onewire/.eil.yml b/components/onewire/.eil.yml index a60d35a0..2782dc17 100644 --- a/components/onewire/.eil.yml +++ b/components/onewire/.eil.yml @@ -1,29 +1,24 @@ ---- -components: - - name: onewire - description: Bit-banging 1-Wire driver - group: common - groups: [] - code_owners: UncleRus - depends: - # XXX conditional depends - - driver - - freertos - - log - - esp_idf_lib_helpers - thread_safe: no - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - # XXX what `MIT *` means? - - name: MIT - copyrights: - - author: - name: UncleRus - year: 2016 - - author: - name: zeroday - year: 2016 +name: onewire +description: Bit-banging 1-Wire driver +version: 1.0.0 +groups: + - common +code_owners: UncleRus +depends: + # XXX conditional depends + - driver + - freertos + - log + - esp_idf_lib_helpers +thread_safe: no +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: zeroday + year: 2016 + - name: UncleRus + year: 2016 diff --git a/components/pca9557/.eil.yml b/components/pca9557/.eil.yml index 352e521c..b1297eba 100644 --- a/components/pca9557/.eil.yml +++ b/components/pca9557/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: pca9557 - description: Driver for PCA9537/PCA9557/TCA9534 remote 4/8-bit I/O expanders for I2C-bus - group: gpio - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2021 +name: pca9557 +description: Driver for PCA9536/PCA9537/PCA9557/TCA9534 remote 4/8-bit I/O expanders for I2C-bus +version: 1.0.0 +groups: + - gpio +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2021 diff --git a/components/pca9557/pca9557.c b/components/pca9557/pca9557.c index 8325e677..6cefcc2b 100644 --- a/components/pca9557/pca9557.c +++ b/components/pca9557/pca9557.c @@ -28,7 +28,7 @@ /** * @file pca9557.c * - * ESP-IDF driver for PCA9537/PCA9557/TCA9534 remote 4/8-bit I/O expanders for I2C-bus + * ESP-IDF driver for PCA9536/PCA9537/PCA9557/TCA9534 remote 4/8-bit I/O expanders for I2C-bus * * Copyright (c) 2021 Ruslan V. Uss * @@ -103,7 +103,7 @@ esp_err_t pca9557_init_desc(i2c_dev_t *dev, uint8_t addr, i2c_port_t port, gpio_ CHECK_ARG(dev && ( (addr & PCA9557_I2C_ADDR_BASE) == PCA9557_I2C_ADDR_BASE || (addr & TCA9534_I2C_ADDR_BASE) == TCA9534_I2C_ADDR_BASE || - addr == PCA9537_I2C_ADDR)); + addr == PCA9537_I2C_ADDR || addr == PCA9536_I2C_ADDR)); dev->port = port; dev->addr = addr; diff --git a/components/pca9557/pca9557.h b/components/pca9557/pca9557.h index e07a87e5..c3110ad8 100644 --- a/components/pca9557/pca9557.h +++ b/components/pca9557/pca9557.h @@ -30,7 +30,7 @@ * @defgroup pca9557 pca9557 * @{ * - * ESP-IDF driver for PCA9537/PCA9557/TCA9534 remote 4/8-bit I/O expanders for I2C-bus + * ESP-IDF driver for PCA9536/PCA9537/PCA9557/TCA9534 remote 4/8-bit I/O expanders for I2C-bus * * Copyright (c) 2021 Ruslan V. Uss * @@ -47,6 +47,7 @@ extern "C" { #endif +#define PCA9536_I2C_ADDR 0x41 ///< I2C address for PCA9536 #define PCA9537_I2C_ADDR 0x49 ///< I2C address for PCA9537 #define PCA9557_I2C_ADDR_BASE 0x18 ///< Base I2C address for PCA9557 #define TCA9534_I2C_ADDR_BASE 0x20 ///< Base I2C address for TCA9534 @@ -129,7 +130,7 @@ esp_err_t pca9557_port_set_polarity(i2c_dev_t *dev, uint8_t pol); * @brief Read I/O port value * * @param dev Pointer to I2C device descriptor - * @param[out] val 8-bit GPIO port value for PCA9557 or 4-bit port value for PCA9537 + * @param[out] val 8-bit GPIO port value for PCA9557 or 4-bit port value for PCA9536/PCA9537 * @return `ESP_OK` on success */ esp_err_t pca9557_port_read(i2c_dev_t *dev, uint8_t *val); @@ -138,7 +139,7 @@ esp_err_t pca9557_port_read(i2c_dev_t *dev, uint8_t *val); * @brief Write value to I/O port * * @param dev Pointer to I2C device descriptor - * @param val 8-bit GPIO port value for PCA9557 or 4-bit port value for PCA9537 + * @param val 8-bit GPIO port value for PCA9557 or 4-bit port value for PCA9536/PCA9537 * @return ESP_OK on success */ esp_err_t pca9557_port_write(i2c_dev_t *dev, uint8_t val); @@ -147,7 +148,7 @@ esp_err_t pca9557_port_write(i2c_dev_t *dev, uint8_t val); * @brief Read I/O pin mode * * @param dev Pointer to device descriptor - * @param pin Pin number, 0..7 for PCA9557, 0..3 for PC9537 + * @param pin Pin number, 0..7 for PCA9557, 0..3 for PCA9536/PC9537 * @param[out] mode Pin mode * @return `ESP_OK` on success */ @@ -157,7 +158,7 @@ esp_err_t pca9557_get_mode(i2c_dev_t *dev, uint8_t pin, pca9557_mode_t *mode); * @brief Set I/O pin mode * * @param dev Pointer to device descriptor - * @param pin Pin number, 0..7 for PCA9557, 0..3 for PC9537 + * @param pin Pin number, 0..7 for PCA9557, 0..3 for PCA9536/PC9537 * @param mode Pin mode * @return `ESP_OK` on success */ @@ -167,7 +168,7 @@ esp_err_t pca9557_set_mode(i2c_dev_t *dev, uint8_t pin, pca9557_mode_t mode); * @brief Read I/O pin level * * @param dev Pointer to device descriptor - * @param pin Pin number, 0..7 for PCA9557, 0..3 for PC9537 + * @param pin Pin number, 0..7 for PCA9557, 0..3 for PCA9536/PC9537 * @param[out] val 1 if pin currently in high state, 0 otherwise * @return `ESP_OK` on success */ @@ -179,7 +180,7 @@ esp_err_t pca9557_get_level(i2c_dev_t *dev, uint8_t pin, uint32_t *val); * Pin must be set up as output * * @param dev Pointer to device descriptor - * @param pin Pin number, 0..7 for PCA9557, 0..3 for PC9537 + * @param pin Pin number, 0..7 for PCA9557, 0..3 for PCA9536/PC9537 * @param val Pin level. 1 - high, 0 - low * @return `ESP_OK` on success */ diff --git a/components/pca9685/.eil.yml b/components/pca9685/.eil.yml index 51e33767..e7633a16 100644 --- a/components/pca9685/.eil.yml +++ b/components/pca9685/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: pca9685 - description: Driver for 16-channel, 12-bit PWM PCA9685 - group: misc - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2016 +name: pca9685 +description: Driver for 16-channel, 12-bit PWM PCA9685 +version: 1.0.0 +groups: + - misc +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2016 diff --git a/components/pcf8563/.eil.yml b/components/pcf8563/.eil.yml index bc0b55c0..b728c741 100644 --- a/components/pcf8563/.eil.yml +++ b/components/pcf8563/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: pcf8563 - description: Driver for PCF8563 real-time clock/calendar - group: rtc - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2020 +name: pcf8563 +description: Driver for PCF8563 (BM8563) real-time clock/calendar +version: 1.0.0 +groups: + - rtc +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2020 diff --git a/components/pcf8563/pcf8563.c b/components/pcf8563/pcf8563.c index 789fc1af..34895561 100644 --- a/components/pcf8563/pcf8563.c +++ b/components/pcf8563/pcf8563.c @@ -28,7 +28,7 @@ /** * @file pcf8563.c * - * ESP-IDF driver for PCF8563 real-time clock/calendar + * ESP-IDF driver for PCF8563 (BM8563) real-time clock/calendar * * Copyright (c) 2020 Ruslan V. Uss * diff --git a/components/pcf8563/pcf8563.h b/components/pcf8563/pcf8563.h index 2d9b5130..5f325ec5 100644 --- a/components/pcf8563/pcf8563.h +++ b/components/pcf8563/pcf8563.h @@ -30,7 +30,7 @@ * @defgroup pcf8563 pcf8563 * @{ * - * ESP-IDF driver for PCF8563 real-time clock/calendar + * ESP-IDF driver for PCF8563 (BM8563) real-time clock/calendar * * Copyright (c) 2020 Ruslan V. Uss * diff --git a/components/pcf8574/.eil.yml b/components/pcf8574/.eil.yml index cb1b777e..3d0dbe0b 100644 --- a/components/pcf8574/.eil.yml +++ b/components/pcf8574/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: pcf8574 - description: Driver for PCF8574 remote 8-bit I/O expander for I2C-bus - group: gpio - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: UncleRus - year: 2018 +name: pcf8574 +description: Driver for PCF8574 remote 8-bit I/O expander for I2C-bus +version: 1.0.0 +groups: + - gpio +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: UncleRus + year: 2018 diff --git a/components/pcf8575/.eil.yml b/components/pcf8575/.eil.yml index af60b6fe..c1344f03 100644 --- a/components/pcf8575/.eil.yml +++ b/components/pcf8575/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: pcf8575 - description: Driver for PCF8575 remote 16-bit I/O expander for I2C-bus - group: gpio - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: UncleRus - year: 2019 +name: pcf8575 +description: Driver for PCF8575 remote 16-bit I/O expander for I2C-bus +version: 1.0.0 +groups: + - gpio +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: UncleRus + year: 2019 diff --git a/components/pcf8591/.eil.yml b/components/pcf8591/.eil.yml index 8630f33c..15e09342 100644 --- a/components/pcf8591/.eil.yml +++ b/components/pcf8591/.eil.yml @@ -1,26 +1,22 @@ ---- -components: - - name: pcf8591 - description: Driver for 8-bit ADC and an 8-bit DAC PCF8591 - group: adc-dac - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2017 - - author: - name: PhamNgocT - year: 2017 +name: pcf8591 +description: Driver for 8-bit ADC and an 8-bit DAC PCF8591 +version: 1.0.0 +groups: + - adc-dac +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: PhamNgocT + year: 2017 + - name: UncleRus + year: 2017 diff --git a/components/qmc5883l/.eil.yml b/components/qmc5883l/.eil.yml index 4a3fde6c..62de474a 100644 --- a/components/qmc5883l/.eil.yml +++ b/components/qmc5883l/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: qmc5883l - description: Driver for QMC5883L 3-axis magnetic sensor - group: magnetic - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2019 +name: qmc5883l +description: Driver for QMC5883L 3-axis magnetic sensor +version: 1.0.0 +groups: + - magnetic +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2019 diff --git a/components/qmp6988/.eil.yml b/components/qmp6988/.eil.yml new file mode 100644 index 00000000..210db5b0 --- /dev/null +++ b/components/qmp6988/.eil.yml @@ -0,0 +1,23 @@ +name: qmp6988 +description: Driver for QMP6988 digital temperature and pressure sensor +version: 0.0.1 +groups: + - temperature + - pressure +code_owners: vonguced +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2016 + - name: vonguced + year: 2024 diff --git a/components/qmp6988/CMakeLists.txt b/components/qmp6988/CMakeLists.txt new file mode 100644 index 00000000..2522582a --- /dev/null +++ b/components/qmp6988/CMakeLists.txt @@ -0,0 +1,11 @@ +if(${IDF_VERSION_MAJOR} STREQUAL 4 AND ${IDF_VERSION_MINOR} STREQUAL 1 AND ${IDF_VERSION_PATCH} STREQUAL 3) + set(req i2cdev log esp_idf_lib_helpers) +else() + set(req i2cdev log esp_idf_lib_helpers esp_timer) +endif() + +idf_component_register( + SRCS qmp6988.c + INCLUDE_DIRS . + REQUIRES ${req} +) \ No newline at end of file diff --git a/components/qmp6988/LICENSE b/components/qmp6988/LICENSE new file mode 100644 index 00000000..cc54db00 --- /dev/null +++ b/components/qmp6988/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2019 Ruslan V. Uss (https://github.com/UncleRus) +Copyright (c) 2022 m5stack (https://github.com/m5stack) +Copyright (c) 2024 vonguced (https://github.com/vonguced) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/components/qmp6988/README.md b/components/qmp6988/README.md new file mode 100644 index 00000000..b9a1aabe --- /dev/null +++ b/components/qmp6988/README.md @@ -0,0 +1,5 @@ +# Driver for QMP6988 digital temperature and pressure sensor + +This driver is based on code of [original QMP6988 driver](https://github.com/m5stack/M5Unit-ENV) +by m5stack, made to be compatible with the i2cdev library (https://github.com/UncleRus/esp-idf-lib). +To convert the code a lot was reused from the sht3x component of the same "esp-idf-lib" library. \ No newline at end of file diff --git a/components/qmp6988/component.mk b/components/qmp6988/component.mk new file mode 100644 index 00000000..4071e351 --- /dev/null +++ b/components/qmp6988/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers \ No newline at end of file diff --git a/components/qmp6988/qmp6988.c b/components/qmp6988/qmp6988.c new file mode 100644 index 00000000..032b34f0 --- /dev/null +++ b/components/qmp6988/qmp6988.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2019 Ruslan V. Uss (https://github.com/UncleRus) + * Copyright (c) 2022 m5stack (https://github.com/m5stack) + * Copyright (c) 2024 vonguced (https://github.com/vonguced) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file qmp6988.c + * + * ESP-IDF driver for QMP6988 digital temperature and pressure sensor + * + * Code based on m5stack \n + * and Ruslan V. Uss + * + * Copyright (c) 2019 Ruslan V. Uss (https://github.com/UncleRus)\n + * Copyright (c) 2022 m5stack (https://github.com/m5stack)\n + * Copyright (c) 2024 vonguced (https://github.com/vonguced)\n + * + * BSD Licensed as described in the file LICENSE + */ + +#include +#include +#include +#include +#include +#include +#include "qmp6988.h" + +#define I2C_FREQ_HZ 1000000 // 1MHz + +typedef uint8_t qmp6988_raw_data_t; + +#define QMP6988_CHIP_ID 0x5C + +#define QMP6988_CHIP_ID_REG 0xD1 +#define QMP6988_RESET_REG 0xE0 /* Device reset register */ +#define QMP6988_DEVICE_STAT_REG 0xF3 /* Device state register */ +#define QMP6988_CTRLMEAS_REG 0xF4 /* Measurement Condition Control Register */ +#define QMP6988_PRESSURE_MSB_REG 0xF7 /* Pressure MSB Register */ +#define QMP6988_TEMPERATURE_MSB_REG 0xFA /* Temperature MSB Reg */ + +#define SUBTRACTOR 8388608 + +/* compensation calculation */ +#define QMP6988_CALIBRATION_DATA_START 0xA0 /* QMP6988 compensation coefficients */ +#define QMP6988_CALIBRATION_DATA_LENGTH 25 + +#define SHIFT_RIGHT_4_POSITION 4 +#define SHIFT_LEFT_2_POSITION 2 +#define SHIFT_LEFT_4_POSITION 4 +#define SHIFT_LEFT_5_POSITION 5 +#define SHIFT_LEFT_8_POSITION 8 +#define SHIFT_LEFT_12_POSITION 12 +#define SHIFT_LEFT_16_POSITION 16 + +#define QMP6988_CTRLMEAS_REG_MODE__POS 0 +#define QMP6988_CTRLMEAS_REG_MODE__MSK 0x03 +#define QMP6988_CTRLMEAS_REG_MODE__LEN 2 + +#define QMP6988_CTRLMEAS_REG_OSRST__POS 5 +#define QMP6988_CTRLMEAS_REG_OSRST__MSK 0xE0 +#define QMP6988_CTRLMEAS_REG_OSRST__LEN 3 + +#define QMP6988_CTRLMEAS_REG_OSRSP__POS 2 +#define QMP6988_CTRLMEAS_REG_OSRSP__MSK 0x1C +#define QMP6988_CTRLMEAS_REG_OSRSP__LEN 3 + +#define QMP6988_CONFIG_REG 0xF1 /*IIR filter co-efficient setting Register*/ +#define QMP6988_CONFIG_REG_FILTER__POS 0 +#define QMP6988_CONFIG_REG_FILTER__MSK 0x07 +#define QMP6988_CONFIG_REG_FILTER__LEN 3 + +/** + * Structure holding raw calibration data for QMP6988. + */ +typedef struct _qmp6988_cali_data +{ + int32_t COE_a0; + int16_t COE_a1; + int16_t COE_a2; + int32_t COE_b00; + int16_t COE_bt1; + int16_t COE_bt2; + int16_t COE_bp1; + int16_t COE_b11; + int16_t COE_bp2; + int16_t COE_b12; + int16_t COE_b21; + int16_t COE_bp3; +} qmp6988_cali_data_t; + +static const char *TAG = "qmp6988"; + +// due to the fact that ticks can be smaller than portTICK_PERIOD_MS, one and +// a half tick period added to the duration to be sure that waiting time for +// the results is long enough +#define TIME_TO_TICKS(ms) (1 + ((ms) + (portTICK_PERIOD_MS - 1) + portTICK_PERIOD_MS / 2) / portTICK_PERIOD_MS) + +#define CHECK(x) \ + do \ + { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) \ + return __; \ + } \ + while (0) +#define CHECK_ARG(VAL) \ + do \ + { \ + if (!(VAL)) \ + return ESP_ERR_INVALID_ARG; \ + } \ + while (0) + +static esp_err_t write_reg(qmp6988_t *dev, uint8_t out_reg, uint8_t cmd) +{ + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write(&dev->i2c_dev, &out_reg, sizeof(out_reg), &cmd, sizeof(cmd))); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +esp_err_t qmp6988_read_reg(qmp6988_t *dev, uint8_t out_reg, qmp6988_raw_data_t *raw_data) +{ + CHECK(i2c_dev_read(&dev->i2c_dev, &out_reg, sizeof(uint8_t), raw_data, sizeof(qmp6988_raw_data_t))); + return ESP_OK; +} + +esp_err_t qmp6988_device_check(qmp6988_t *dev) +{ + qmp6988_raw_data_t chip_id = 0x00; + qmp6988_read_reg(dev, QMP6988_CHIP_ID_REG, &chip_id); + + if (chip_id == QMP6988_CHIP_ID) + { + return ESP_OK; + } + else + { + ESP_LOGE(TAG, "QMP6988 chip id not matching. Expected: 0x%02X got: 0x%02X", QMP6988_CHIP_ID, chip_id); + return ESP_ERR_INVALID_RESPONSE; + } +} + +esp_err_t qmp6988_get_calibration_data(qmp6988_t *dev) +{ + uint8_t a_data_uint8_tr[QMP6988_CALIBRATION_DATA_LENGTH] = { 0 }; + qmp6988_cali_data_t qmp6988_cali; + int len; + + for (len = 0; len < QMP6988_CALIBRATION_DATA_LENGTH; len += 1) + { + CHECK(qmp6988_read_reg(dev, QMP6988_CALIBRATION_DATA_START + len, &a_data_uint8_tr[len])); + } + + qmp6988_cali.COE_a0 = (int32_t)(((int32_t)a_data_uint8_tr[18] << SHIFT_LEFT_12_POSITION) | (a_data_uint8_tr[19] << SHIFT_LEFT_4_POSITION) | (a_data_uint8_tr[24] & 0x0f)) << 12; + qmp6988_cali.COE_a0 = qmp6988_cali.COE_a0 >> 12; + + qmp6988_cali.COE_a1 = (int16_t)((a_data_uint8_tr[20] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[21]); + + qmp6988_cali.COE_a2 = (int16_t)((a_data_uint8_tr[22] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[23]); + + qmp6988_cali.COE_b00 + = (int32_t)((((int32_t)a_data_uint8_tr[0] << SHIFT_LEFT_12_POSITION) | (a_data_uint8_tr[1] << SHIFT_LEFT_4_POSITION) | ((a_data_uint8_tr[24] & 0xf0) >> SHIFT_RIGHT_4_POSITION)) + << 12); + qmp6988_cali.COE_b00 = qmp6988_cali.COE_b00 >> 12; + + qmp6988_cali.COE_bt1 = (int16_t)((a_data_uint8_tr[2] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[3]); + + qmp6988_cali.COE_bt2 = (int16_t)((a_data_uint8_tr[4] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[5]); + + qmp6988_cali.COE_bp1 = (int16_t)((a_data_uint8_tr[6] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[7]); + + qmp6988_cali.COE_b11 = (int16_t)((a_data_uint8_tr[8] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[9]); + + qmp6988_cali.COE_bp2 = (int16_t)((a_data_uint8_tr[10] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[11]); + + qmp6988_cali.COE_b12 = (int16_t)((a_data_uint8_tr[12] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[13]); + + qmp6988_cali.COE_b21 = (int16_t)((a_data_uint8_tr[14] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[15]); + + qmp6988_cali.COE_bp3 = (int16_t)((a_data_uint8_tr[16] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[17]); + + dev->ik.a0 = qmp6988_cali.COE_a0; // 20Q4 + dev->ik.b00 = qmp6988_cali.COE_b00; // 20Q4 + + dev->ik.a1 = 3608L * (int32_t)qmp6988_cali.COE_a1 - 1731677965L; // 31Q23 + dev->ik.a2 = 16889L * (int32_t)qmp6988_cali.COE_a2 - 87619360L; // 30Q47 + + dev->ik.bt1 = 2982L * (int64_t)qmp6988_cali.COE_bt1 + 107370906L; // 28Q15 + dev->ik.bt2 = 329854L * (int64_t)qmp6988_cali.COE_bt2 + 108083093L; // 34Q38 + dev->ik.bp1 = 19923L * (int64_t)qmp6988_cali.COE_bp1 + 1133836764L; // 31Q20 + dev->ik.b11 = 2406L * (int64_t)qmp6988_cali.COE_b11 + 118215883L; // 28Q34 + dev->ik.bp2 = 3079L * (int64_t)qmp6988_cali.COE_bp2 - 181579595L; // 29Q43 + dev->ik.b12 = 6846L * (int64_t)qmp6988_cali.COE_b12 + 85590281L; // 29Q53 + dev->ik.b21 = 13836L * (int64_t)qmp6988_cali.COE_b21 + 79333336L; // 29Q60 + dev->ik.bp3 = 2915L * (int64_t)qmp6988_cali.COE_bp3 + 157155561L; // 28Q65 + return ESP_OK; +} + +int16_t qmp6988_conv_Tx_02e(qmp6988_ik_data_t *ik, int32_t dt) +{ + int16_t ret; + int64_t wk1, wk2; + + // wk1: 60Q4 // bit size + wk1 = ((int64_t)ik->a1 * (int64_t)dt); // 31Q23+24-1=54 (54Q23) + wk2 = ((int64_t)ik->a2 * (int64_t)dt) >> 14; // 30Q47+24-1=53 (39Q33) + wk2 = (wk2 * (int64_t)dt) >> 10; // 39Q33+24-1=62 (52Q23) + wk2 = ((wk1 + wk2) / 32767) >> 19; // 54,52->55Q23 (20Q04) + ret = (int16_t)((ik->a0 + wk2) >> 4); // 21Q4 -> 17Q0 + return ret; +} + +int32_t qmp6988_get_pressure_02e(qmp6988_ik_data_t *ik, int32_t dp, int16_t tx) +{ + int32_t ret; + int64_t wk1, wk2, wk3; + + // wk1 = 48Q16 // bit size + wk1 = ((int64_t)ik->bt1 * (int64_t)tx); // 28Q15+16-1=43 (43Q15) + wk2 = ((int64_t)ik->bp1 * (int64_t)dp) >> 5; // 31Q20+24-1=54 (49Q15) + wk1 += wk2; // 43,49->50Q15 + wk2 = ((int64_t)ik->bt2 * (int64_t)tx) >> 1; // 34Q38+16-1=49 (48Q37) + wk2 = (wk2 * (int64_t)tx) >> 8; // 48Q37+16-1=63 (55Q29) + wk3 = wk2; // 55Q29 + wk2 = ((int64_t)ik->b11 * (int64_t)tx) >> 4; // 28Q34+16-1=43 (39Q30) + wk2 = (wk2 * (int64_t)dp) >> 1; // 39Q30+24-1=62 (61Q29) + wk3 += wk2; // 55,61->62Q29 + wk2 = ((int64_t)ik->bp2 * (int64_t)dp) >> 13; // 29Q43+24-1=52 (39Q30) + wk2 = (wk2 * (int64_t)dp) >> 1; // 39Q30+24-1=62 (61Q29) + wk3 += wk2; // 62,61->63Q29 + wk1 += wk3 >> 14; // Q29 >> 14 -> Q15 + wk2 = ((int64_t)ik->b12 * (int64_t)tx); // 29Q53+16-1=45 (45Q53) + wk2 = (wk2 * (int64_t)tx) >> 22; // 45Q53+16-1=61 (39Q31) + wk2 = (wk2 * (int64_t)dp) >> 1; // 39Q31+24-1=62 (61Q30) + wk3 = wk2; // 61Q30 + wk2 = ((int64_t)ik->b21 * (int64_t)tx) >> 6; // 29Q60+16-1=45 (39Q54) + wk2 = (wk2 * (int64_t)dp) >> 23; // 39Q54+24-1=62 (39Q31) + wk2 = (wk2 * (int64_t)dp) >> 1; // 39Q31+24-1=62 (61Q20) + wk3 += wk2; // 61,61->62Q30 + wk2 = ((int64_t)ik->bp3 * (int64_t)dp) >> 12; // 28Q65+24-1=51 (39Q53) + wk2 = (wk2 * (int64_t)dp) >> 23; // 39Q53+24-1=62 (39Q30) + wk2 = (wk2 * (int64_t)dp); // 39Q30+24-1=62 (62Q30) + wk3 += wk2; // 62,62->63Q30 + wk1 += wk3 >> 15; // Q30 >> 15 = Q15 + wk1 /= 32767L; + wk1 >>= 11; // Q15 >> 7 = Q4 + wk1 += ik->b00; // Q4 + 20Q4 + // wk1 >>= 4; // 28Q4 -> 24Q0 + ret = (int32_t)wk1; + return ret; +} + +/////////////////////////////////////////////////////////////////////////// + +esp_err_t qmp6988_init_desc(qmp6988_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + dev->i2c_dev.port = port; + dev->i2c_dev.addr = addr; + dev->i2c_dev.cfg.sda_io_num = sda_gpio; + dev->i2c_dev.cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->i2c_dev.cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + return i2c_dev_create_mutex(&dev->i2c_dev); +} + +esp_err_t qmp6988_free_desc(qmp6988_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(&dev->i2c_dev); +} + +esp_err_t qmp6988_init(qmp6988_t *dev) +{ + CHECK_ARG(dev); + + dev->power_mode = QMP6988_SLEEP_MODE; + dev->filter_mode = QMP6988_FILTERCOEFF_4; + dev->oversampling_t_mode = QMP6988_OVERSAMPLING_1X; + dev->oversampling_p_mode = QMP6988_OVERSAMPLING_8X; + + CHECK(qmp6988_device_check(dev)); + + CHECK(write_reg(dev, QMP6988_RESET_REG, 0xe6)); + vTaskDelay(TIME_TO_TICKS(20)); // need to wait a short moment after reset + CHECK(qmp6988_get_calibration_data(dev)); + CHECK(qmp6988_setup_powermode(dev, QMP6988_SLEEP_MODE)); + CHECK(qmp6988_set_filter(dev, QMP6988_FILTERCOEFF_4)); + CHECK(qmp6988_set_p_oversampling(dev, QMP6988_OVERSAMPLING_8X)); + CHECK(qmp6988_set_t_oversampling(dev, QMP6988_OVERSAMPLING_1X)); + + return ESP_OK; +} + +esp_err_t qmp6988_setup_powermode(qmp6988_t *dev, qmp6988_power_mode_t power_mode) +{ + CHECK_ARG(dev); + + uint8_t data = 0x00; + + dev->power_mode = power_mode; + CHECK(qmp6988_read_reg(dev, QMP6988_CTRLMEAS_REG, &data)); + data &= 0xfc; + data |= power_mode; + CHECK(write_reg(dev, QMP6988_CTRLMEAS_REG, data)); + + vTaskDelay(TIME_TO_TICKS(20)); + + return ESP_OK; +} + +esp_err_t qmp6988_set_filter(qmp6988_t *dev, qmp6988_filter_t filter_mode) +{ + CHECK_ARG(dev); + + dev->filter_mode = filter_mode; + CHECK(write_reg(dev, QMP6988_CONFIG_REG, filter_mode)); + vTaskDelay(TIME_TO_TICKS(20)); + + return ESP_OK; +} + +esp_err_t qmp6988_set_p_oversampling(qmp6988_t *dev, qmp6988_oversampling_t oversampling_p_mode) +{ + CHECK_ARG(dev); + + qmp6988_raw_data_t data = 0x00; + + dev->oversampling_p_mode = oversampling_p_mode; + CHECK(qmp6988_read_reg(dev, QMP6988_CTRLMEAS_REG, &data)); + data &= 0xe3; + data |= (oversampling_p_mode << 2); + CHECK(write_reg(dev, QMP6988_CTRLMEAS_REG, data)); + vTaskDelay(TIME_TO_TICKS(20)); + + return ESP_OK; +} + +esp_err_t qmp6988_set_t_oversampling(qmp6988_t *dev, qmp6988_oversampling_t oversampling_t_mode) +{ + CHECK_ARG(dev); + + qmp6988_raw_data_t data = 0x00; + + dev->oversampling_t_mode = oversampling_t_mode; + CHECK(qmp6988_read_reg(dev, QMP6988_CTRLMEAS_REG, &data)); + data &= 0x1f; + data |= (oversampling_t_mode << 5); + CHECK(write_reg(dev, QMP6988_CTRLMEAS_REG, data)); + vTaskDelay(TIME_TO_TICKS(20)); + + return ESP_OK; +} + +esp_err_t qmp6988_calc_pressure(qmp6988_t *dev, float *p) +{ + CHECK_ARG(dev && p); + + uint32_t P_read, T_read; + int32_t P_raw, T_raw; + uint8_t a_data_uint8_tr[6] = { 0 }; + int32_t T_int, P_int; + int len; + + // press + for (len = 0; len < 3; len += 1) + { + CHECK(qmp6988_read_reg(dev, QMP6988_PRESSURE_MSB_REG + len, &a_data_uint8_tr[len])); + } + P_read = (uint32_t)((((uint32_t)(a_data_uint8_tr[0])) << SHIFT_LEFT_16_POSITION) | (((uint16_t)(a_data_uint8_tr[1])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[2])); + P_raw = (int32_t)(P_read - SUBTRACTOR); + + // temp + for (len = 3; len < 6; len += 1) + { + CHECK(qmp6988_read_reg(dev, QMP6988_TEMPERATURE_MSB_REG + len - 3, &a_data_uint8_tr[len])); + } + T_read = (uint32_t)((((uint32_t)(a_data_uint8_tr[3])) << SHIFT_LEFT_16_POSITION) | (((uint16_t)(a_data_uint8_tr[4])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[5])); + T_raw = (int32_t)(T_read - SUBTRACTOR); + + T_int = qmp6988_conv_Tx_02e(&(dev->ik), T_raw); + P_int = qmp6988_get_pressure_02e(&(dev->ik), P_raw, T_int); + dev->temperature = (float)T_int / 256.0f; + dev->pressure = (float)P_int / 16.0f; + + *p = dev->pressure; + + return ESP_OK; +} + +esp_err_t qmp6988_calc_temperature(qmp6988_t *dev, float *t) +{ + CHECK_ARG(t); + + float dummy; + CHECK(qmp6988_calc_pressure(dev, &dummy)); + *t = dev->temperature; + + return ESP_OK; +} diff --git a/components/qmp6988/qmp6988.h b/components/qmp6988/qmp6988.h new file mode 100644 index 00000000..639bc407 --- /dev/null +++ b/components/qmp6988/qmp6988.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2019 Ruslan V. Uss (https://github.com/UncleRus) + * Copyright (c) 2022 m5stack (https://github.com/m5stack) + * Copyright (c) 2024 vonguced (https://github.com/vonguced) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file qmp6988.h + * @defgroup qmp6988 qmp6988 + * @{ + * + * ESP-IDF driver for QMP6988 digital temperature and pressure sensor + * + * Code based on m5stack \n + * and Ruslan V. Uss + * + * Copyright (c) 2019 Ruslan V. Uss (https://github.com/UncleRus)\n + * Copyright (c) 2022 m5stack (https://github.com/m5stack)\n + * Copyright (c) 2024 vonguced (https://github.com/vonguced)\n + * + * BSD Licensed as described in the file LICENSE + */ + +#ifndef __QMP6988_H__ +#define __QMP6988_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define QMP6988_I2C_ADDR_GND 0x70 +#define QMP6988_I2C_ADDR_VDD 0x56 + +/** + * Possible measurement modes + */ +typedef enum { + QMP6988_SLEEP_MODE = 0x00, //!< sleep mode + QMP6988_FORCED_MODE = 0x01, //!< one measurement then sleep again + QMP6988_NORMAL_MODE = 0x03 //!< power mode +} qmp6988_power_mode_t; + +/** + * Possible filter modes + */ +typedef enum { + QMP6988_FILTERCOEFF_OFF = 0x00, + QMP6988_FILTERCOEFF_2 = 0x01, + QMP6988_FILTERCOEFF_4 = 0x02, + QMP6988_FILTERCOEFF_8 = 0x03, + QMP6988_FILTERCOEFF_16 = 0x04, + QMP6988_FILTERCOEFF_32 = 0x05 +} qmp6988_filter_t; + +/** + * Possible oversampling modes + */ +typedef enum { + QMP6988_OVERSAMPLING_SKIPPED = 0x00, + QMP6988_OVERSAMPLING_1X = 0x01, + QMP6988_OVERSAMPLING_2X = 0x02, + QMP6988_OVERSAMPLING_4X = 0x03, + QMP6988_OVERSAMPLING_8X = 0x04, + QMP6988_OVERSAMPLING_16X = 0x05, + QMP6988_OVERSAMPLING_32X = 0x06, + QMP6988_OVERSAMPLING_64X = 0x07 +} qmp6988_oversampling_t; + +/** + * Structure holding calibration data for QMP6988. + */ +typedef struct +{ + int32_t a0, b00; + int32_t a1, a2; + int64_t bt1, bt2, bp1, b11, bp2, b12, b21, bp3; +} qmp6988_ik_data_t; + +/** + * Device descriptor + */ +typedef struct +{ + i2c_dev_t i2c_dev; //!< I2C device descriptor + + qmp6988_power_mode_t power_mode; //!< used power mode + qmp6988_filter_t filter_mode; //!< used filter mode + qmp6988_oversampling_t oversampling_t_mode; //!< used oversampling temp mode + qmp6988_oversampling_t oversampling_p_mode; //!< used oversampling pressure mode + + qmp6988_ik_data_t ik; //!< used calibration data + + float temperature; //!< measured temperature + float pressure; //!< measured pressure +} qmp6988_t; + +/** + * @brief Initialize device descriptor. + * + * @param dev Device descriptor. + * @param addr Device address. + * @param port I2C port. + * @param sda_gpio SDA GPIO. + * @param scl_gpio SCL GPIO. + * @return `ESP_OK` on success. + */ +esp_err_t qmp6988_init_desc(qmp6988_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t qmp6988_free_desc(qmp6988_t *dev); + +/** + * @brief Initialize sensor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t qmp6988_init(qmp6988_t *dev); + +/** + * @brief Set up power mode for QMP6988. + * + * @param dev Device descriptor. + * @param power_mode Power mode to be set. + * @return `ESP_OK` on success. + */ +esp_err_t qmp6988_setup_powermode(qmp6988_t *dev, qmp6988_power_mode_t power_mode); + +/** + * @brief Set up filter mode for QMP6988. + * + * @param dev Device descriptor. + * @param filter_mode Filter mode to be set. + * @return `ESP_OK` on success. + */ +esp_err_t qmp6988_set_filter(qmp6988_t *dev, qmp6988_filter_t filter_mode); + +/** + * @brief Set up pressure oversampling mode for QMP6988. + * + * @param dev Device descriptor. + * @param oversampling_p_mode Oversampling mode for pressure to be set. + * @return `ESP_OK` on success. + */ +esp_err_t qmp6988_set_p_oversampling(qmp6988_t *dev, qmp6988_oversampling_t oversampling_p_mode); + +/** + * @brief Set up temperature oversampling mode for QMP6988. + * + * @param dev Device descriptor. + * @param oversampling_t_mode Oversampling mode for temperature to be set. + * @return `ESP_OK` on success. + */ +esp_err_t qmp6988_set_t_oversampling(qmp6988_t *dev, qmp6988_oversampling_t oversampling_t_mode); + +/** + * @brief Calculate pressure based on QMP6988 sensor data. + * + * @param dev Device descriptor. + * @param[out] p Calculated pressure in Pascals (Pa). + * @return `ESP_OK` on success. + */ +esp_err_t qmp6988_calc_pressure(qmp6988_t *dev, float *p); + +/** + * @brief Calculate temperature based on QMP6988 sensor data. + * + * @param dev Device descriptor. + * @param[out] t Calculated temperature in degrees Celsius (°C). + * @return `ESP_OK` on success. + */ +esp_err_t qmp6988_calc_temperature(qmp6988_t *dev, float *t); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __QMP6988_H__ */ diff --git a/components/rda5807m/.eil.yml b/components/rda5807m/.eil.yml index 1816c844..e1283248 100644 --- a/components/rda5807m/.eil.yml +++ b/components/rda5807m/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: rda5807m - description: Driver for single-chip broadcast FM radio tuner RDA5807M - group: misc - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2018 +name: rda5807m +description: Driver for single-chip broadcast FM radio tuner RDA5807M +version: 1.0.0 +groups: + - misc +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2018 diff --git a/components/scd30/.eil.yml b/components/scd30/.eil.yml index ac84edb3..d3024761 100644 --- a/components/scd30/.eil.yml +++ b/components/scd30/.eil.yml @@ -1,30 +1,25 @@ ---- -components: - - name: scd30 - description: Driver for SCD30 CO₂ sensor - group: air-quality - groups: - - name: gas - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2021 - - author: - name: Sensirion - year: 2021 - - author: - name: nated0g - year: 2021 +name: scd30 +description: Driver for SCD30 CO₂ sensor +version: 1.0.5 +groups: + - gas + - air-quality +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: Sensirion + year: 2021 + - name: UncleRus + year: 2021 + - name: nated0g + year: 2021 diff --git a/components/scd30/scd30.c b/components/scd30/scd30.c index c9efcfc0..916d25ae 100644 --- a/components/scd30/scd30.c +++ b/components/scd30/scd30.c @@ -43,19 +43,7 @@ #include #include #include -#if CONFIG_IDF_TARGET_ESP32 -#include -#elif CONFIG_IDF_TARGET_ESP32S2 -#include -#elif CONFIG_IDF_TARGET_ESP32S3 -#include -#elif CONFIG_IDF_TARGET_ESP32C3 -#include -#elif CONFIG_IDF_TARGET_ESP32H2 -#include -#elif CONFIG_IDF_TARGET_ESP32C2 -#include -#endif +#include #include #include "scd30.h" diff --git a/components/scd4x/.eil.yml b/components/scd4x/.eil.yml index 1862bb56..54b7065e 100644 --- a/components/scd4x/.eil.yml +++ b/components/scd4x/.eil.yml @@ -1,27 +1,23 @@ ---- -components: - - name: scd4x - description: Driver for SCD40/SCD41 miniature CO₂ sensor - group: air-quality - groups: - - name: gas - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2021 - - author: - name: Sensirion - year: 2021 +name: scd4x +description: Driver for SCD40/SCD41 miniature CO₂ sensor +version: 1.0.0 +groups: + - gas + - air-quality +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2021 + - name: Sensirion + year: 2021 diff --git a/components/sfa3x/.eil.yml b/components/sfa3x/.eil.yml new file mode 100644 index 00000000..86a02ef7 --- /dev/null +++ b/components/sfa3x/.eil.yml @@ -0,0 +1,26 @@ +name: sfa3x +description: Driver for SFA30 formaldehyde detection module (I2C) +version: 1.0.0 +groups: + - air-quality + - gas + - temperature + - humidity +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: Sensirion + year: 2021 + - name: UncleRus + year: 2021 diff --git a/components/sfa3x/CMakeLists.txt b/components/sfa3x/CMakeLists.txt new file mode 100644 index 00000000..05002af4 --- /dev/null +++ b/components/sfa3x/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS sfa3x.c + INCLUDE_DIRS . + REQUIRES i2cdev log esp_idf_lib_helpers +) diff --git a/components/sfa3x/LICENSE b/components/sfa3x/LICENSE new file mode 100644 index 00000000..1629effa --- /dev/null +++ b/components/sfa3x/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2021, Sensirion AG +Copyright (c) 2021 Ruslan V. Uss + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/components/sfa3x/component.mk b/components/sfa3x/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/sfa3x/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/sfa3x/sfa3x.c b/components/sfa3x/sfa3x.c new file mode 100644 index 00000000..1d1adf38 --- /dev/null +++ b/components/sfa3x/sfa3x.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2021, Sensirion AG + * Copyright (c) 2021 Ruslan V. Uss + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file sfa3x.c + * + * ESP-IDF driver for SFA30 formaldehyde detection module + * + * Ported from https://github.com/Sensirion/embedded-sfa3x + * + * Copyright (c) 2021, Sensirion AG + * Copyright (c) 2021 Ruslan V. Uss + * + * BSD Licensed as described in the file LICENSE + */ +#include +#include +#include +#include +#include +#include +#include "sfa3x.h" + +#define I2C_FREQ_HZ 100000 // 100kHz + +static const char *TAG = "sfa3x"; + +#define CMD_START_CONTINUOUS_MEASUREMENT (0x0006) +#define CMD_STOP_CONTINUOUS_MEASUREMENT (0x0104) +#define CMD_READ_MEASUREMENT (0x0327) +#define CMD_GET_DEVICE_MARKING (0xD060) +#define CMD_DEVICE_RESET (0xD304) + +#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) +#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) + +static uint8_t crc8(const uint8_t *data, size_t count) +{ + uint8_t res = 0xff; + + for (size_t i = 0; i < count; ++i) + { + res ^= data[i]; + for (uint8_t bit = 8; bit > 0; --bit) + { + if (res & 0x80) + res = (res << 1) ^ 0x31; + else + res = (res << 1); + } + } + return res; +} + +static inline uint16_t swap(uint16_t v) +{ + return (v << 8) | (v >> 8); +} + +static esp_err_t send_cmd(i2c_dev_t *dev, uint16_t cmd, uint16_t *data, size_t words) +{ + uint8_t buf[2 + words * 3]; + // add command + *(uint16_t *)buf = swap(cmd); + if (data && words) + // add arguments + for (size_t i = 0; i < words; i++) + { + uint8_t *p = buf + 2 + i * 3; + *(uint16_t *)p = swap(data[i]); + *(p + 2) = crc8(p, 2); + } + + ESP_LOGV(TAG, "Sending buffer:"); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, buf, sizeof(buf), ESP_LOG_VERBOSE); + + return i2c_dev_write(dev, NULL, 0, buf, sizeof(buf)); +} + +static esp_err_t read_resp(i2c_dev_t *dev, uint16_t *data, size_t words) +{ + uint8_t buf[words * 3]; + CHECK(i2c_dev_read(dev, NULL, 0, buf, sizeof(buf))); + + ESP_LOGV(TAG, "Received buffer:"); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, buf, sizeof(buf), ESP_LOG_VERBOSE); + + for (size_t i = 0; i < words; i++) + { + uint8_t *p = buf + i * 3; + uint8_t crc = crc8(p, 2); + if (crc != *(p + 2)) + { + ESP_LOGE(TAG, "Invalid CRC 0x%02x, expected 0x%02x", crc, *(p + 2)); + return ESP_ERR_INVALID_CRC; + } + data[i] = swap(*(uint16_t *)p); + } + return ESP_OK; +} + +static esp_err_t execute_cmd(i2c_dev_t *dev, uint16_t cmd, uint32_t timeout_ms, + uint16_t *out_data, size_t out_words, uint16_t *in_data, size_t in_words) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, send_cmd(dev, cmd, out_data, out_words)); + if (timeout_ms) + { + if (timeout_ms >= portTICK_PERIOD_MS) + vTaskDelay(pdMS_TO_TICKS(timeout_ms)); + else + ets_delay_us(timeout_ms * 1000); + } + if (in_data && in_words) + I2C_DEV_CHECK(dev, read_resp(dev, in_data, in_words)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +esp_err_t sfa3x_init_desc(i2c_dev_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + dev->port = port; + dev->addr = SFA3X_I2C_ADDR; + dev->cfg.sda_io_num = sda_gpio; + dev->cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + return i2c_dev_create_mutex(dev); +} + +esp_err_t sfa3x_free_desc(i2c_dev_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(dev); +} + +esp_err_t sfa3x_reset(i2c_dev_t *dev) +{ + return execute_cmd(dev, CMD_DEVICE_RESET, 100, NULL, 0, NULL, 0); +} + +esp_err_t sfa3x_start_continuous_measurement(i2c_dev_t *dev) +{ + return execute_cmd(dev, CMD_START_CONTINUOUS_MEASUREMENT, 1, NULL, 0, NULL, 0); +} + +esp_err_t sfa3x_stop_continuous_measurement(i2c_dev_t *dev) +{ + return execute_cmd(dev, CMD_STOP_CONTINUOUS_MEASUREMENT, 50, NULL, 0, NULL, 0); +} + +esp_err_t sfa3x_read_measurement(i2c_dev_t *dev, float *hcho, float *humidity, float *temperature) +{ + CHECK_ARG(hcho || temperature || humidity); + + int16_t data[3]; + CHECK(execute_cmd(dev, CMD_READ_MEASUREMENT, 5, NULL, 0, (uint16_t *)data, 3)); + + if (hcho) + *hcho = (float)data[0] / 5.0f; + if (humidity) + *humidity = (float)data[1] / 100.0f; + if (temperature) + *temperature = (float)data[2] / 200.0f; + + return ESP_OK; +} + +esp_err_t sfa3x_get_device_marknig(i2c_dev_t *dev, char *marknig) +{ + CHECK_ARG(marknig); + + uint16_t buf[16]; + CHECK(execute_cmd(dev, CMD_GET_DEVICE_MARKING, 2, NULL, 0, buf, 16)); + + // FIXME swap/unswap + for (size_t i = 0; i < 16; i++) + buf[i] = swap(buf[i]); + + memcpy(marknig, buf, 32); + + return ESP_OK; +} diff --git a/components/sfa3x/sfa3x.h b/components/sfa3x/sfa3x.h new file mode 100644 index 00000000..7971ed27 --- /dev/null +++ b/components/sfa3x/sfa3x.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021, Sensirion AG + * Copyright (c) 2021 Ruslan V. Uss + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file sfa3x.h + * @defgroup sfa3x sfa3x + * @{ + * + * ESP-IDF driver for SFA30 formaldehyde detection module + * + * Ported from https://github.com/Sensirion/embedded-sfa3x + * + * Copyright (c) 2021, Sensirion AG + * Copyright (c) 2021 Ruslan V. Uss + * + * BSD Licensed as described in the file LICENSE + */ +#ifndef __sfa3x_H__ +#define __sfa3x_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SFA3X_I2C_ADDR 0x5d + +/** + * @brief Initialize device descriptor. + * + * @param dev Device descriptor + * @param port I2C port + * @param sda_gpio SDA GPIO + * @param scl_gpio SCL GPIO + * @return `ESP_OK` on success + */ +esp_err_t sfa3x_init_desc(i2c_dev_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor. + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t sfa3x_free_desc(i2c_dev_t *dev); + +/** + * @brief Reset sensor. + * + * This function brings the sensor into the same state as after power-up. + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t sfa3x_reset(i2c_dev_t *dev); + +/** + * @brief Start continuous measurement. + * + * After power up, the module is in Idle-Mode. Before any measurement values can be read, + * the Measurement-Mode needs to be started using this command. + * + * @note This command is only available in idle mode. + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t sfa3x_start_continuous_measurement(i2c_dev_t *dev); + +/** + * @brief Stop continuous measurement. + * + * Stop continuous measurement and return to idle mode for save energy. + * + * @note This command is only available in measurement mode. + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t sfa3x_stop_continuous_measurement(i2c_dev_t *dev); + +/** + * @brief Read sensor output and convert. + * + * @note This command is only available in measurement mode. + * + * @param dev Device descriptor + * @param hcho Formaldehyde concentration in ppb + * @param humidity Relative humidity in percent RH + * @param temperature Temperature in degrees Celsius (°C) + * @return `ESP_OK` on success + */ +esp_err_t sfa3x_read_measurement(i2c_dev_t *dev, float *hcho, float *humidity, float *temperature); + +/** + * @brief Read device marking + * + * To identify individual sensors, the device marking string + * (as printed on the sensor as 2D bar code) can be read with this function. + * + * @param dev Device descriptor + * @param marknig 32 bytes buffer to store marking as NULL-terminated string + * @return `ESP_OK` on success + */ +esp_err_t sfa3x_get_device_marknig(i2c_dev_t *dev, char *marknig); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __sfa3x_H__ */ diff --git a/components/sgm58031/.eil.yml b/components/sgm58031/.eil.yml new file mode 100644 index 00000000..7ab99a9a --- /dev/null +++ b/components/sgm58031/.eil.yml @@ -0,0 +1,21 @@ +name: sgm58031 +description: Driver for SGM58031 16-bit I2C ADC +version: 1.0.0 +groups: + - adc-dac +code_owners: jmpmscorp +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32s3 + - esp32c3 +license: ISC +copyrights: + - name: jmpmscorp + year: 2023 diff --git a/components/sgm58031/CMakeLists.txt b/components/sgm58031/CMakeLists.txt new file mode 100644 index 00000000..f9642e30 --- /dev/null +++ b/components/sgm58031/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS sgm58031.c + INCLUDE_DIRS . + REQUIRES i2cdev log esp_idf_lib_helpers +) diff --git a/components/sgm58031/LICENSE b/components/sgm58031/LICENSE new file mode 100644 index 00000000..545c1d88 --- /dev/null +++ b/components/sgm58031/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2023 Jose Manuel Perez + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/components/sgm58031/component.mk b/components/sgm58031/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/sgm58031/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/sgm58031/sgm58031.c b/components/sgm58031/sgm58031.c new file mode 100644 index 00000000..44865fb3 --- /dev/null +++ b/components/sgm58031/sgm58031.c @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2023 Jose Manuel Perez + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file sgm58031.c + * @defgroup sgm58031 sgm58031 + * @{ + * + * ESP-IDF driver for SGM58031 16-bit I2C ADC + * + * Copyright (c) 2023 Jose Manuel Perez + */ + +#include +#include +#include "sgm58031.h" + +// #define I2C_FREQ_HZ (1000000) // Max 1MHz for esp32 +#define I2C_FREQ_HZ (400000) // 400kHz + +// SGM58031 registers +#define SGM58031_REG_CONVERSION (0x00) +#define SGM58031_REG_CONFIG (0x01) +#define SGM58031_REG_THRESH_L (0x02) +#define SGM58031_REG_THRESH_H (0x03) +#define SGM58031_REG_CONFIG1 (0x04) +#define SGM58031_REG_CHIP_ID (0x05) +#define SGM58031_REG_GN_TRIM1 (0x06) + +/* REG CONFIG */ +#define COMP_QUE_OFFSET (1) +#define COMP_QUE_MASK (0x03) +#define COMP_LAT_OFFSET (2) +#define COMP_LAT_MASK (0x01) +#define COMP_POL_OFFSET (3) +#define COMP_POL_MASK (0x01) +#define COMP_MODE_OFFSET (4) +#define COMP_MODE_MASK (0x01) +#define DR_OFFSET (5) +#define DR_MASK (0x07) +#define MODE_OFFSET (8) +#define MODE_MASK (0x01) +#define PGA_OFFSET (9) +#define PGA_MASK (0x07) +#define MUX_OFFSET (12) +#define MUX_MASK (0x07) +#define OS_OFFSET (15) +#define OS_MASK (0x01) + +/* REG CONFIG1*/ +#define EXT_REF_OFFSET (3) +#define EXT_REF_MASK (0x01) +#define BUS_FLEX_OFFSET (4) +#define BUS_FLEX_MASK (0x01) +#define INT_DIO_OFFSET (5) +#define INT_DIO_MASK (0x01) +#define BURNOUT_OFFSET (6) +#define BURNOUT_MASK (0x01) +#define DR_SEL_OFFSET (7) +#define DR_SEL_MASK (0x01) +#define PD_OFFSET (8) +#define PD_MASK (0x01) + +#define CHECK(x) \ + do \ + { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) \ + return __; \ + } \ + while (0) +#define CHECK_ARG(VAL) \ + do \ + { \ + if (!(VAL)) \ + return ESP_ERR_INVALID_ARG; \ + } \ + while (0) + +static const char *TAG = "sgm58031"; + +const float sgm58031_gain_values[] = { [SGM58031_GAIN_6V144] = 6.144, + [SGM58031_GAIN_4V096] = 4.096, + [SGM58031_GAIN_2V048] = 2.048, + [SGM58031_GAIN_1V024] = 1.024, + [SGM58031_GAIN_0V512] = 0.512, + [SGM58031_GAIN_0V256] = 0.256, + [SGM58031_GAIN_0V256_2] = 0.256, + [SGM58031_GAIN_0V256_3] = 0.256 }; + +static esp_err_t read_reg(i2c_dev_t *dev, uint8_t reg, uint16_t *val) +{ + uint8_t buf[2]; + esp_err_t res; + if ((res = i2c_dev_read_reg(dev, reg, buf, 2)) != ESP_OK) + { + ESP_LOGE(TAG, "Could not read from register 0x%02x", reg); + return res; + } + *val = (buf[0] << 8) | buf[1]; + + return ESP_OK; +} + +static esp_err_t write_reg(i2c_dev_t *dev, uint8_t reg, uint16_t val) +{ + uint8_t buf[2] = { val >> 8, val }; + esp_err_t res; + if ((res = i2c_dev_write_reg(dev, reg, buf, 2)) != ESP_OK) + { + ESP_LOGE(TAG, "Could not write 0x%04x to register 0x%02x", val, reg); + return res; + } + + return ESP_OK; +} + +static esp_err_t read_conf_bits(i2c_dev_t *dev, uint8_t config_reg, uint8_t offs, uint16_t mask, uint16_t *bits) +{ + CHECK_ARG(dev); + CHECK_ARG((config_reg == SGM58031_REG_CONFIG) || (config_reg == SGM58031_REG_CONFIG1)); + + uint16_t val; + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, read_reg(dev, config_reg, &val)); + I2C_DEV_GIVE_MUTEX(dev); + + ESP_LOGD(TAG, "read config reg 0x%02x value: 0x%04x", config_reg, val); + + *bits = (val >> offs) & mask; + + return ESP_OK; +} + +static esp_err_t write_conf_bits(i2c_dev_t *dev, uint8_t config_reg, uint16_t val, uint8_t offs, uint16_t mask) +{ + CHECK_ARG(dev); + CHECK_ARG((config_reg == SGM58031_REG_CONFIG) || (config_reg == SGM58031_REG_CONFIG1)); + + uint16_t old; + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, read_reg(dev, config_reg, &old)); + I2C_DEV_CHECK(dev, write_reg(dev, config_reg, (old & ~(mask << offs)) | (val << offs))); + + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +#define READ_CONFIG(REG, OFFS, MASK, VAR) \ + do \ + { \ + CHECK_ARG(VAR); \ + uint16_t bits; \ + CHECK(read_conf_bits(dev, REG, OFFS, MASK, &bits)); \ + *VAR = bits; \ + return ESP_OK; \ + } \ + while (0) + +/////////////////////////////////////////////////////////////////////////////// + +esp_err_t sgm58031_init_desc(i2c_dev_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + if (addr != SGM58031_ADDR_GND && addr != SGM58031_ADDR_VCC && addr != SGM58031_ADDR_SDA + && addr != SGM58031_ADDR_SCL) + { + ESP_LOGE(TAG, "Invalid I2C address"); + return ESP_ERR_INVALID_ARG; + } + + dev->port = port; + dev->addr = addr; + dev->cfg.sda_io_num = sda_gpio; + dev->cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + return i2c_dev_create_mutex(dev); +} + +esp_err_t sgm58031_free_desc(i2c_dev_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(dev); +} + +esp_err_t sgm58031_is_busy(i2c_dev_t *dev, bool *busy) +{ + CHECK_ARG(dev && busy); + + uint16_t r; + CHECK(read_conf_bits(dev, SGM58031_REG_CONFIG, OS_OFFSET, OS_MASK, &r)); + *busy = !r; + + return ESP_OK; +} + +esp_err_t sgm58031_start_conversion(i2c_dev_t *dev) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG, 1, OS_OFFSET, OS_MASK); +} + +esp_err_t sgm58031_get_value(i2c_dev_t *dev, int16_t *value) +{ + CHECK_ARG(dev && value); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, read_reg(dev, SGM58031_REG_CONVERSION, (uint16_t *)value)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +esp_err_t sgm58031_get_gain(i2c_dev_t *dev, sgm58031_gain_t *gain) +{ + READ_CONFIG(SGM58031_REG_CONFIG, PGA_OFFSET, PGA_MASK, gain); +} + +esp_err_t sgm58031_set_gain(i2c_dev_t *dev, sgm58031_gain_t gain) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG, gain, PGA_OFFSET, PGA_MASK); +} + +esp_err_t sgm58031_get_input_mux(i2c_dev_t *dev, sgm58031_mux_t *mux) +{ + READ_CONFIG(SGM58031_REG_CONFIG, MUX_OFFSET, MUX_MASK, mux); +} + +esp_err_t sgm58031_set_input_mux(i2c_dev_t *dev, sgm58031_mux_t mux) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG, mux, MUX_OFFSET, MUX_MASK); +} + +esp_err_t sgm58031_get_conv_mode(i2c_dev_t *dev, sgm58031_conv_mode_t *mode) +{ + READ_CONFIG(SGM58031_REG_CONFIG, MODE_OFFSET, MODE_MASK, mode); +} + +esp_err_t sgm58031_set_conv_mode(i2c_dev_t *dev, sgm58031_conv_mode_t mode) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG, mode, MODE_OFFSET, MODE_MASK); +} + +esp_err_t sgm58031_get_data_rate(i2c_dev_t *dev, sgm58031_data_rate_t *rate) +{ + CHECK_ARG(dev && rate); + + uint16_t bits; + CHECK(read_conf_bits(dev, SGM58031_REG_CONFIG, DR_OFFSET, DR_MASK, &bits)); + + *rate = bits; + + CHECK(read_conf_bits(dev, SGM58031_REG_CONFIG1, DR_SEL_OFFSET, DR_SEL_MASK, &bits)); + + if (bits == 0x01) // DR SEL is high + { + *rate += 8; + } + + return ESP_OK; +} + +esp_err_t sgm58031_set_data_rate(i2c_dev_t *dev, sgm58031_data_rate_t rate) +{ + esp_err_t ret = ESP_FAIL; + + if (rate >= SGM58031_DATA_RATE_7_5) + { + ret = write_conf_bits(dev, SGM58031_REG_CONFIG1, 1, DR_SEL_OFFSET, DR_SEL_MASK); + + if (ret == ESP_OK) + { + ret = write_conf_bits(dev, SGM58031_REG_CONFIG, rate, DR_OFFSET, DR_MASK); + } + } + else + { + ret = write_conf_bits(dev, SGM58031_REG_CONFIG1, 0, DR_SEL_OFFSET, DR_SEL_MASK); + + if (ret == ESP_OK) + { + ret = write_conf_bits(dev, SGM58031_REG_CONFIG, rate, DR_OFFSET, DR_MASK); + } + } + + return ret; +} + +esp_err_t sgm58031_get_comp_mode(i2c_dev_t *dev, sgm58031_comp_mode_t *mode) +{ + READ_CONFIG(SGM58031_REG_CONFIG, COMP_MODE_OFFSET, COMP_MODE_MASK, mode); +} + +esp_err_t sgm58031_set_comp_mode(i2c_dev_t *dev, sgm58031_comp_mode_t mode) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG, mode, COMP_MODE_OFFSET, COMP_MODE_MASK); +} + +esp_err_t sgm58031_get_comp_polarity(i2c_dev_t *dev, sgm58031_comp_polarity_t *polarity) +{ + READ_CONFIG(SGM58031_REG_CONFIG, COMP_POL_OFFSET, COMP_POL_MASK, polarity); +} + +esp_err_t sgm58031_set_comp_polarity(i2c_dev_t *dev, sgm58031_comp_polarity_t polarity) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG, polarity, COMP_POL_OFFSET, COMP_POL_MASK); +} + +esp_err_t sgm58031_get_comp_latch(i2c_dev_t *dev, sgm58031_comp_latch_t *latch) +{ + READ_CONFIG(SGM58031_REG_CONFIG, COMP_LAT_OFFSET, COMP_LAT_MASK, latch); +} + +esp_err_t sgm58031_set_comp_latch(i2c_dev_t *dev, sgm58031_comp_latch_t latch) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG, latch, COMP_LAT_OFFSET, COMP_LAT_MASK); +} + +esp_err_t sgm58031_get_comp_queue(i2c_dev_t *dev, sgm58031_comp_queue_t *queue) +{ + READ_CONFIG(SGM58031_REG_CONFIG, COMP_QUE_OFFSET, COMP_QUE_MASK, queue); +} + +esp_err_t sgm58031_set_comp_queue(i2c_dev_t *dev, sgm58031_comp_queue_t queue) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG, queue, COMP_QUE_OFFSET, COMP_QUE_MASK); +} + +esp_err_t sgm58031_get_comp_low_thresh(i2c_dev_t *dev, int16_t *th) +{ + CHECK_ARG(dev && th); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, read_reg(dev, SGM58031_REG_THRESH_L, (uint16_t *)th)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +esp_err_t sgm58031_set_comp_low_thresh(i2c_dev_t *dev, int16_t th) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, write_reg(dev, SGM58031_REG_THRESH_L, th)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +esp_err_t sgm58031_get_comp_high_thresh(i2c_dev_t *dev, int16_t *th) +{ + CHECK_ARG(dev && th); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, read_reg(dev, SGM58031_REG_THRESH_H, (uint16_t *)th)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +esp_err_t sgm58031_set_comp_high_thresh(i2c_dev_t *dev, int16_t th) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, write_reg(dev, SGM58031_REG_THRESH_H, th)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +esp_err_t sgm58031_set_ain3_external_reference(i2c_dev_t *dev, bool enable) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG1, enable, EXT_REF_OFFSET, EXT_REF_MASK); +} + +esp_err_t sgm58031_get_ain3_external_reference(i2c_dev_t *dev, bool *enable) +{ + READ_CONFIG(SGM58031_REG_CONFIG1, EXT_REF_OFFSET, EXT_REF_MASK, enable); +} + +esp_err_t sgm58031_set_source_pair(i2c_dev_t *dev, bool enable) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG1, enable, BURNOUT_OFFSET, BURNOUT_MASK); +} + +esp_err_t sgm58031_get_source_pair(i2c_dev_t *dev, bool *enable) +{ + READ_CONFIG(SGM58031_REG_CONFIG1, BURNOUT_OFFSET, BURNOUT_MASK, enable); +} + +esp_err_t sgm58031_set_bus_leakage_circuit(i2c_dev_t *dev, bool enable) +{ + return write_conf_bits(dev, SGM58031_REG_CONFIG1, enable, BUS_FLEX_OFFSET, BUS_FLEX_MASK); +} + +esp_err_t sgm58031_get_bus_leakage_circuit(i2c_dev_t *dev, bool *enable) +{ + READ_CONFIG(SGM58031_REG_CONFIG1, BUS_FLEX_OFFSET, BUS_FLEX_MASK, enable); +} + +esp_err_t sgm58031_get_chip_id(i2c_dev_t *dev, uint8_t *id, uint8_t *version) +{ + CHECK_ARG(dev && id && version); + + I2C_DEV_TAKE_MUTEX(dev); + uint16_t bits; + I2C_DEV_CHECK(dev, read_reg(dev, SGM58031_REG_CHIP_ID, &bits)); + + *version = (bits & 0xFF) >> 5; + *id = (bits >> 8) & 0xFF; + + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +esp_err_t sgm58031_get_gn_trim1(i2c_dev_t *dev, uint16_t *trim_value) +{ + CHECK_ARG(dev && trim_value); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, read_reg(dev, SGM58031_REG_GN_TRIM1, trim_value)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +esp_err_t sgm58031_set_gn_trim1(i2c_dev_t *dev, uint16_t trim_value) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, write_reg(dev, SGM58031_REG_GN_TRIM1, trim_value)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} diff --git a/components/sgm58031/sgm58031.h b/components/sgm58031/sgm58031.h new file mode 100644 index 00000000..846446f7 --- /dev/null +++ b/components/sgm58031/sgm58031.h @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2023 Jose Manuel Perez + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file sgm58031.h + * @defgroup sgm58031 sgm58031 + * @{ + * + * ESP-IDF driver for SGM58031 16-bit I2C ADC + * + * Copyright (c) 2023 Jose Manuel Perez + */ +#ifndef __SGM58031_H__ +#define __SGM58031_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SGM58031_ADDR_GND (0x48) //!< I2C device address with ADDR pin connected to ground +#define SGM58031_ADDR_VCC (0x49) //!< I2C device address with ADDR pin connected to VCC +#define SGM58031_ADDR_SDA (0x4A) //!< I2C device address with ADDR pin connected to SDA +#define SGM58031_ADDR_SCL (0x4B) //!< I2C device address with ADDR pin connected to SCL + +#define SGM58031_MAX_VALUE (0x7fff) //!< Maximum ADC value + +/** + * Gain amplifier + */ +typedef enum { + SGM58031_GAIN_6V144 = 0, //!< +-6.144V + SGM58031_GAIN_4V096, //!< +-4.096V + SGM58031_GAIN_2V048, //!< +-2.048V (default) + SGM58031_GAIN_1V024, //!< +-1.024V + SGM58031_GAIN_0V512, //!< +-0.512V + SGM58031_GAIN_0V256, //!< +-0.256V + SGM58031_GAIN_0V256_2, //!< +-0.256V (same as ADS111X_GAIN_0V256) + SGM58031_GAIN_0V256_3, //!< +-0.256V (same as ADS111X_GAIN_0V256) +} sgm58031_gain_t; + +/** + * Gain amplifier values + */ +extern const float sgm58031_gain_values[]; + +/** + * Input multiplexer configuration + */ +typedef enum { + SGM58031_MUX_AIN0_AIN1 = 0, //!< positive = AIN0, negative = AIN1 (default) + SGM58031_MUX_AIN0_AIN3, //!< positive = AIN0, negative = AIN3 + SGM58031_MUX_AIN1_AIN3, //!< positive = AIN1, negative = AIN3 + SGM58031_MUX_AIN2_AIN3, //!< positive = AIN2, negative = AIN3 + SGM58031_MUX_AIN0_GND, //!< positive = AIN0, negative = GND + SGM58031_MUX_AIN1_GND, //!< positive = AIN1, negative = GND + SGM58031_MUX_AIN2_GND, //!< positive = AIN2, negative = GND + SGM58031_MUX_AIN3_GND, //!< positive = AIN3, negative = GND +} sgm58031_mux_t; + +/** + * Data rate + */ +typedef enum { + SGM58031_DATA_RATE_6_25 = 0x00, //!< 6.25 samples per second (DR_SEL = 0) + SGM58031_DATA_RATE_12_5, //!< 12.5 samples per second (DR_SEL = 0) + SGM58031_DATA_RATE_25, //!< 25 samples per second (DR_SEL = 0) + SGM58031_DATA_RATE_50, //!< 50 samples per second (DR_SEL = 0) + SGM58031_DATA_RATE_100, //!< 100 samples per second (DR_SEL = 0) (default) + SGM58031_DATA_RATE_200, //!< 200 samples per second (DR_SEL = 0) + SGM58031_DATA_RATE_400, //!< 400 samples per second (DR_SEL = 0) + SGM58031_DATA_RATE_800, //!< 800 samples per second (DR_SEL = 0) + SGM58031_DATA_RATE_7_5, //!< 7.5 samples per second (DR_SEL = 1) + SGM58031_DATA_RATE_15, //!< 15 samples per second (DR_SEL = 1) + SGM58031_DATA_RATE_30, //!< 30 samples per second (DR_SEL = 1) + SGM58031_DATA_RATE_60, //!< 60 samples per second (DR_SEL = 1) + SGM58031_DATA_RATE_120, //!< 120 samples per second (DR_SEL = 1) + SGM58031_DATA_RATE_240, //!< 240 samples per second (DR_SEL = 1) + SGM58031_DATA_RATE_480, //!< 480 samples per second (DR_SEL = 1) + SGM58031_DATA_RATE_960 //!< 960 samples per second (DR_SEL = 1) +} sgm58031_data_rate_t; + +/** + * Device operating mode + */ +typedef enum { + SGM58031_CONV_MODE_CONTINUOUS = 0, //!< Continuous conversion mode + SGM58031_CONV_MODE_SINGLE_SHOT //!< Power-down single-shot mode (default) +} sgm58031_conv_mode_t; + +/** + * Comparator mode + */ +typedef enum { + SGM58031_COMP_MODE_NORMAL = 0, //!< Traditional comparator with hysteresis (default) + SGM58031_COMP_MODE_WINDOW //!< Window comparator +} sgm58031_comp_mode_t; + +/** + * Comparator polarity + */ +typedef enum { + SGM58031_COMP_POLARITY_LOW = 0, //!< Active low (default) + SGM58031_COMP_POLARITY_HIGH //!< Active high +} sgm58031_comp_polarity_t; + +/** + * Comparator latch + */ +typedef enum { + SGM58031_COMP_LATCH_DISABLED = 0, //!< Non-latching comparator (default) + SGM58031_COMP_LATCH_ENABLED //!< Latching comparator +} sgm58031_comp_latch_t; + +/** + * Comparator queue + */ +typedef enum { + SGM58031_COMP_QUEUE_1 = 0, //!< Assert ALERT/RDY pin after one conversion + SGM58031_COMP_QUEUE_2, //!< Assert ALERT/RDY pin after two conversions + SGM58031_COMP_QUEUE_4, //!< Assert ALERT/RDY pin after four conversions + SGM58031_COMP_QUEUE_DISABLED //!< Disable comparator (default) +} sgm58031_comp_queue_t; + +/** + * @brief Initialize device descriptor + * + * @param[in] dev Device descriptor + * @param[in] addr Device address + * @param[in] port I2C port number + * @param[in] sda_gpio GPIO pin for SDA + * @param[in] scl_gpio GPIO pin for SCL + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_init_desc(i2c_dev_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param[in] dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t sgm58031_free_desc(i2c_dev_t *dev); + +/** + * @brief Get device operational status + * + * @param[in] dev Device descriptor + * @param[out] busy True when device performing conversion + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise */ +esp_err_t sgm58031_is_busy(i2c_dev_t *dev, bool *busy); + +/** + * @brief Begin a single conversion + * + * Only in single-shot mode. + * + * @param[in] dev Device descriptor + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_start_conversion(i2c_dev_t *dev); + +/** + * @brief Read last conversion result + * + * @param[in] dev Device descriptor + * @param[out] value Last conversion result + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_value(i2c_dev_t *dev, int16_t *value); + +/** + * @brief Read the programmable gain amplifier configuration + * + * Use ::sgm58031_gain_values[] for real voltage. + * + * @param[in] dev Device descriptor + * @param[out] gain Gain value + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or gain are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_gain(i2c_dev_t *dev, sgm58031_gain_t *gain); + +/** + * @brief Configure the programmable gain amplifier + * + * @param[in] dev Device descriptor + * @param[in] gain Gain value + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_gain(i2c_dev_t *dev, sgm58031_gain_t gain); + +/** + * @brief Read the input multiplexer configuration + * + * @param[in] dev Device descriptor + * @param[out] mux Input multiplexer configuration + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or mux are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_input_mux(i2c_dev_t *dev, sgm58031_mux_t *mux); + +/** + * @brief Configure the input multiplexer configuration + * + * @param[in] dev Device descriptor + * @param[in] mux Input multiplexer configuration + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_input_mux(i2c_dev_t *dev, sgm58031_mux_t mux); + +/** + * @brief Read the device operating conversion mode + * + * @param[in] dev Device descriptor + * @param[out] mode Device operating mode + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or mode are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_conv_mode(i2c_dev_t *dev, sgm58031_conv_mode_t *mode); + +/** + * @brief Set the device operating mode + * + * @param[in] dev Device descriptor + * @param[in] mode Device operating mode + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_conv_mode(i2c_dev_t *dev, sgm58031_conv_mode_t mode); + +/** + * @brief Read the data rate + * + * @param[in] dev Device descriptor + * @param[out] rate Data rate + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or rate are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_data_rate(i2c_dev_t *dev, sgm58031_data_rate_t *rate); + +/** + * @brief Configure the data rate + * + * @param[in] dev Device descriptor + * @param[in] rate Data rate + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_data_rate(i2c_dev_t *dev, sgm58031_data_rate_t rate); + +/** + * @brief Get comparator mode + * + * @param[in] dev Device descriptor + * @param[out] mode Comparator mode + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or mode are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_comp_mode(i2c_dev_t *dev, sgm58031_comp_mode_t *mode); + +/** + * @brief Set comparator mode + * + * @param dev Device descriptor + * @param mode Comparator mode + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_comp_mode(i2c_dev_t *dev, sgm58031_comp_mode_t mode); + +/** + * @brief Get polarity of the comparator output pin ALERT/RDY + * + * @param[in] dev Device descriptor + * @param[out] polarity Comparator output pin polarity + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or polarity are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_comp_polarity(i2c_dev_t *dev, sgm58031_comp_polarity_t *polarity); + +/** + * @brief Set polarity of the comparator output pin ALERT/RDY + * + * @param[in] dev Device descriptor + * @param[in] polarity Comparator output pin polarity + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_comp_polarity(i2c_dev_t *dev, sgm58031_comp_polarity_t polarity); + +/** + * @brief Get comparator output latch mode + * + * @param[in] dev Device descriptor + * @param[out] latch Comparator output latch mode + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or latch are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_comp_latch(i2c_dev_t *dev, sgm58031_comp_latch_t *latch); + +/** + * @brief Set comparator output latch mode + * + * @param[in] dev Device descriptor + * @param[in] latch Comparator output latch mode + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_comp_latch(i2c_dev_t *dev, sgm58031_comp_latch_t latch); + +/** + * @brief Get comparator queue size + * + * Get number of the comparator conversions before pin ALERT/RDY + * assertion. + * + * @param[in] dev Device descriptor + * @param[out] queue Number of the comparator conversions + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or queue are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_comp_queue(i2c_dev_t *dev, sgm58031_comp_queue_t *queue); + +/** + * @brief Set comparator queue size + * + * Set number of the comparator conversions before pin ALERT/RDY + * assertion or disable comparator. + * + * @param[in] dev Device descriptor + * @param[in] queue Number of the comparator conversions + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_comp_queue(i2c_dev_t *dev, sgm58031_comp_queue_t queue); + +/** + * @brief Get the lower threshold value used by comparator + * + * @param[in] dev Device descriptor + * @param[out] th Lower threshold value + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or th are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_comp_low_thresh(i2c_dev_t *dev, int16_t *th); + +/** + * @brief Set the lower threshold value used by comparator + * + * @param[in] dev Device descriptor + * @param[in] th Lower threshold value + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_comp_low_thresh(i2c_dev_t *dev, int16_t th); + +/** + * @brief Get the upper threshold value used by comparator + * + * @param[in] dev Device descriptor + * @param[out] th Upper threshold value + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or th are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_comp_high_thresh(i2c_dev_t *dev, int16_t *th); + +/** + * @brief Set the upper threshold value used by comparator + * + * @param[in] dev Device descriptor + * @param[in] th Upper threshold value + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_comp_high_thresh(i2c_dev_t *dev, int16_t th); + +/** + * @brief Enable/disable ain3 as external reference + * + * @param[in] dev Device descriptor + * @param[in] enable True to enable. False to disable + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_ain3_external_reference(i2c_dev_t *dev, bool enable); + +/** + * @brief Get ain3 as external reference enable/disable bit + * + * @param[in] dev Device descriptor + * @param[out] enable True, enable. False, disable + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_ain3_external_reference(i2c_dev_t *dev, bool *enable); + +/** + * @brief Enable/disable source pair of 2uA to selected pair of AINx + * + * @param[in] dev Device descriptor + * @param[in] enable True to enable. False to disable + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_source_pair(i2c_dev_t *dev, bool enable); + +/** + * @brief Get burnout enable/disable bit + * + * @param[in] dev Device descriptor + * @param[out] enable True, enable. False, disable + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_source_pair(i2c_dev_t *dev, bool *enable); + +/** + * @brief Enable/disable I2C bus leakage blocking circuit. + * + * @param[in] dev Device descriptor + * @param[in] enable True to enable. False to disable + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_bus_leakage_circuit(i2c_dev_t *dev, bool enable); + +/** + * @brief Get I2C bus leakage blocking circuit enable/disable bit + * + * @param[in] dev Device descriptor + * @param[out] enable True, enable. False, disable + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_bus_leakage_circuit(i2c_dev_t *dev, bool *enable); + +/** + * @brief Get chip id values + * + * @param[in] dev Device descriptor + * @param[out] id ID value + * @param[out] version Version value + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev, id or version are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_chip_id(i2c_dev_t *dev, uint8_t *id, uint8_t *version); + +/** + * @brief Set GN Trim1 value + * + * @param[in] dev Device descriptor + * @param[in] trim_value Value to be set + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev is NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_set_gn_trim1(i2c_dev_t *dev, uint16_t trim_value); + +/** + * @brief Set GN Trim1 value + * + * @param[in] dev Device descriptor + * @param[in] trim_value Value to be set + * @return + * - `ESP_OK` on success + * - `ESP_INVALID_ARG` if dev or trim_value are NULL + * - `ESP_FAIL` otherwise + */ +esp_err_t sgm58031_get_gn_trim1(i2c_dev_t *dev, uint16_t *trim_value); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __SGM58031_H__ */ diff --git a/components/sgp40/.eil.yml b/components/sgp40/.eil.yml index f89de5dd..b14bc259 100644 --- a/components/sgp40/.eil.yml +++ b/components/sgp40/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: sgp40 - description: Driver for SGP40 Indoor Air Quality Sensor for VOC Measurements - group: air-quality - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2020 +name: sgp40 +description: Driver for SGP40 Indoor Air Quality Sensor for VOC Measurements +version: 1.0.0 +groups: + - air-quality +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2020 diff --git a/components/sht3x/.eil.yml b/components/sht3x/.eil.yml index 42b334dd..8dab1041 100644 --- a/components/sht3x/.eil.yml +++ b/components/sht3x/.eil.yml @@ -1,27 +1,23 @@ ---- -components: - - name: sht3x - description: Driver for Sensirion SHT30/SHT31/SHT35 digital temperature and humidity sensor - group: temperature - groups: - - name: humidity - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2016 - - author: - name: gschorcht - year: 2017 +name: sht3x +description: Driver for Sensirion SHT30/SHT31/SHT35 digital temperature and humidity sensor +version: 1.0.1 +groups: + - temperature + - humidity +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2016 + - name: gschorcht + year: 2017 diff --git a/components/sht4x/.eil.yml b/components/sht4x/.eil.yml index 0e2a0c62..abe4c8c7 100644 --- a/components/sht4x/.eil.yml +++ b/components/sht4x/.eil.yml @@ -1,25 +1,21 @@ ---- -components: - - name: sht4x - description: | - Driver for Sensirion SHT40/SHT41/SHT45 digital temperature and humidity sensor - group: temperature - groups: - - name: humidity - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2021 +name: sht4x +description: Driver for Sensirion SHT40/SHT41/SHT45 digital temperature and humidity sensor +version: 1.0.0 +groups: + - temperature + - humidity +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2021 diff --git a/components/sht4x/sht4x.c b/components/sht4x/sht4x.c index 2ff0e41d..77329695 100644 --- a/components/sht4x/sht4x.c +++ b/components/sht4x/sht4x.c @@ -174,8 +174,7 @@ static esp_err_t exec_cmd(sht4x_t *dev, uint8_t cmd, size_t delay_ticks, sht4x_r { I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); I2C_DEV_CHECK(&dev->i2c_dev, send_cmd_nolock(dev, cmd)); - if (delay_ticks) - vTaskDelay(delay_ticks); + vTaskDelay(delay_ticks + 1); I2C_DEV_CHECK(&dev->i2c_dev, read_res_nolock(dev, res)); I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); diff --git a/components/si7021/.eil.yml b/components/si7021/.eil.yml index ded30139..a16c66c8 100644 --- a/components/si7021/.eil.yml +++ b/components/si7021/.eil.yml @@ -1,27 +1,22 @@ ---- -components: - - name: si7021 - description: | - Driver for Si7013/Si7020/Si7021/HTU2xD/SHT2x and compatible temperature - and humidity sensors - group: temperature - groups: - - name: humidity - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp32c3 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2019 +name: si7021 +description: Driver for Si7013/Si7020/Si7021/HTU2xD/SHT2x and compatible temperature and humidity sensors +version: 1.0.1 +groups: + - temperature + - humidity +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp32c3 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2019 diff --git a/components/si7021/si7021.c b/components/si7021/si7021.c index de8a30d5..1bceb7a7 100644 --- a/components/si7021/si7021.c +++ b/components/si7021/si7021.c @@ -57,9 +57,9 @@ static const char *TAG = "si7021"; #define CMD_READ_USER_REG 0xe7 #define CMD_WRITE_HEATER_REG 0x51 #define CMD_READ_HEATER_REG 0x11 -#define CMD_READ_ID_1 0xfa0f -#define CMD_READ_ID_2 0xfcc9 -#define CMD_READ_FW_REV_1 0x84b8 +#define CMD_READ_ID_1 0x0ffa +#define CMD_READ_ID_2 0xc9fc +#define CMD_READ_FW_REV_1 0xb884 #define BIT_USER_REG_RES0 0 #define BIT_USER_REG_HTRE 2 @@ -316,3 +316,16 @@ esp_err_t si7021_get_device_id(i2c_dev_t *dev, si7021_device_id_t *id) return ESP_OK; } + +esp_err_t si7021_get_device_revision(i2c_dev_t *dev, uint8_t *rev) +{ + CHECK_ARG(dev && rev); + + uint16_t cmd = CMD_READ_FW_REV_1; + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, i2c_dev_read(dev, &cmd, 2, rev, 1)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} diff --git a/components/si7021/si7021.h b/components/si7021/si7021.h index 63d788e1..194ca484 100644 --- a/components/si7021/si7021.h +++ b/components/si7021/si7021.h @@ -229,6 +229,21 @@ esp_err_t si7021_get_serial(i2c_dev_t *dev, uint64_t *serial, bool sht2x_mode); */ esp_err_t si7021_get_device_id(i2c_dev_t *dev, si7021_device_id_t *id); +/** + * @brief Get device revision + * + * This function is supported by: + * + * - SI7013 + * - SI7020 + * - SI7021 + * + * @param dev Device descriptor + * @param[out] rev Device revision + * @return `ESP_OK` on success + */ +esp_err_t si7021_get_device_revision(i2c_dev_t *dev, uint8_t *rev); + #ifdef __cplusplus } #endif diff --git a/components/sts21/.eil.yml b/components/sts21/.eil.yml index 5e7231f3..850fa3bf 100644 --- a/components/sts21/.eil.yml +++ b/components/sts21/.eil.yml @@ -1,24 +1,21 @@ ---- -components: - - name: sts21 - description: | - Driver for STS21 temperature sensor - group: temperature - groups: [] - code_owners: - - name: UncleRus - depends: - - name: i2cdev - - name: log - - name: esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - name: UncleRus - year: 2022 +name: sts21 +description: Driver for STS21 temperature sensor +version: 1.0.0 +groups: + - temperature +code_owners: + - UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2022 diff --git a/components/sts21/sts21.c b/components/sts21/sts21.c index 0a4637e9..ecd2c118 100644 --- a/components/sts21/sts21.c +++ b/components/sts21/sts21.c @@ -28,9 +28,9 @@ /** * @file sts21.c * - * ESP-IDF driver for humidty/temperature sensors sts2110/sts2115/sts2120 + * ESP-IDF driver for humidty/temperature sensors STS2110/STS2115/STS2120 * - * Copyright (c) 2021 Ruslan V. Uss + * Copyright (c) 2022 Ruslan V. Uss * * BSD Licensed as described in the file LICENSE */ diff --git a/components/sts21/sts21.h b/components/sts21/sts21.h index 03361fd9..5f281e0c 100644 --- a/components/sts21/sts21.h +++ b/components/sts21/sts21.h @@ -30,9 +30,9 @@ * @defgroup sts21 sts21 * @{ * - * ESP-IDF driver for humidty/temperature sensors sts2110/sts2115/sts2120 + * ESP-IDF driver for humidty/temperature sensors STS2110/STS2115/STS2120 * - * Copyright (c) 2021 Ruslan V. Uss + * Copyright (c) 2022 Ruslan V. Uss * * BSD Licensed as described in the file LICENSE */ @@ -69,7 +69,6 @@ typedef struct * @brief Initialize device descriptor * * @param dev Device descriptor - * @param addr Device I2C address * @param port I2C port * @param sda_gpio SDA GPIO * @param scl_gpio SCL GPIO diff --git a/components/sts3x/.eil.yml b/components/sts3x/.eil.yml new file mode 100644 index 00000000..8b690050 --- /dev/null +++ b/components/sts3x/.eil.yml @@ -0,0 +1,24 @@ +name: sts3x +description: Driver for Sensirion STS30/STS31/STS35 digital temperature sensor +version: 1.0.0 +groups: + - temperature +code_owners: slimcdk +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: gschorcht + year: 2017 + - name: UncleRus + year: 2019 + - name: slimcdk + year: 2023 diff --git a/components/sts3x/CMakeLists.txt b/components/sts3x/CMakeLists.txt new file mode 100644 index 00000000..05b602fd --- /dev/null +++ b/components/sts3x/CMakeLists.txt @@ -0,0 +1,11 @@ +if(${IDF_VERSION_MAJOR} STREQUAL 4 AND ${IDF_VERSION_MINOR} STREQUAL 1 AND ${IDF_VERSION_PATCH} STREQUAL 3) + set(req i2cdev log esp_idf_lib_helpers) +else() + set(req i2cdev log esp_idf_lib_helpers esp_timer) +endif() + +idf_component_register( + SRCS sts3x.c + INCLUDE_DIRS . + REQUIRES ${req} +) diff --git a/components/sts3x/LICENSE b/components/sts3x/LICENSE new file mode 100644 index 00000000..4b6cd02b --- /dev/null +++ b/components/sts3x/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht) +Copyright (c) 2019 Ruslan V. Uss (https://github.com/UncleRus) +Copyright (c) 2023 Christian Skjerning + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/components/sts3x/README.md b/components/sts3x/README.md new file mode 100644 index 00000000..2e37060b --- /dev/null +++ b/components/sts3x/README.md @@ -0,0 +1,3 @@ +# Driver for STS3x digital temperature sensor + +This driver is fork of SHT3x driver excluding humidity measurements. \ No newline at end of file diff --git a/components/sts3x/component.mk b/components/sts3x/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/sts3x/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/sts3x/sts3x.c b/components/sts3x/sts3x.c new file mode 100644 index 00000000..97d388ca --- /dev/null +++ b/components/sts3x/sts3x.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2017 Gunar Schorcht + * Copyright (c) 2019 Ruslan V. Uss + * Copyright (c) 2023 Christian Skjerning + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file sts3x.c + * + * ESP-IDF driver for Sensirion STS3x digital temperature sensors + * + * Forked from SHT3x driver + * + * Copyright (c) 2017 Gunar Schorcht \n + * Copyright (c) 2019 Ruslan V. Uss \n + * Copyright (c) 2023 Christian Skjerning + * + * BSD-3 Licensed as described in the file LICENSE + */ + +#include +#include +#include +#include +#include +#include "sts3x.h" + +#define I2C_FREQ_HZ 1000000 // 1MHz + +const char *TAG = "sts3x"; + +#define STS3X_STATUS_CMD 0xF32D +#define STS3X_CLEAR_STATUS_CMD 0x3041 +#define STS3X_RESET_CMD 0x30A2 +#define STS3X_FETCH_DATA_CMD 0xE000 +#define STS3X_STOP_PERIODIC_MEAS_CMD 0x3093 +#define STS3X_HEATER_ON_CMD 0x306D +#define STS3X_HEATER_OFF_CMD 0x3066 + +static const uint16_t STS3X_MEASURE_CMD[6][3] = { + {0x2400, 0x240b, 0x2416}, // [SINGLE_SHOT][H,M,L] without clock stretching + {0x2032, 0x2024, 0x202f}, // [PERIODIC_05][H,M,L] + {0x2130, 0x2126, 0x212d}, // [PERIODIC_1 ][H,M,L] + {0x2236, 0x2220, 0x222b}, // [PERIODIC_2 ][H,M,L] + {0x2334, 0x2322, 0x2329}, // [PERIODIC_4 ][H,M,L] + {0x2737, 0x2721, 0x272a} // [PERIODIC_10][H,M,L] +}; + +// due to the fact that ticks can be smaller than portTICK_PERIOD_MS, one and +// a half tick period added to the duration to be sure that waiting time for +// the results is long enough +#define TIME_TO_TICKS(ms) (1 + ((ms) + (portTICK_PERIOD_MS-1) + portTICK_PERIOD_MS/2 ) / portTICK_PERIOD_MS) + +#define STS3X_MEAS_DURATION_REP_HIGH 15 +#define STS3X_MEAS_DURATION_REP_MEDIUM 6 +#define STS3X_MEAS_DURATION_REP_LOW 4 + +// measurement durations in us +static const uint16_t STS3X_MEAS_DURATION_US[3] = { + STS3X_MEAS_DURATION_REP_HIGH * 1000, + STS3X_MEAS_DURATION_REP_MEDIUM * 1000, + STS3X_MEAS_DURATION_REP_LOW * 1000 +}; + +// measurement durations in RTOS ticks +static const uint8_t STS3X_MEAS_DURATION_TICKS[3] = { + TIME_TO_TICKS(STS3X_MEAS_DURATION_REP_HIGH), + TIME_TO_TICKS(STS3X_MEAS_DURATION_REP_MEDIUM), + TIME_TO_TICKS(STS3X_MEAS_DURATION_REP_LOW) +}; + +#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) +#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) + +#define G_POLYNOM 0x31 + +static inline uint16_t shuffle(uint16_t val) +{ + return (val >> 8) | (val << 8); +} + +static uint8_t crc8(uint8_t data[], int len) +{ + // initialization value + uint8_t crc = 0xff; + + // iterate over all bytes + for (int i = 0; i < len; i++) + { + crc ^= data[i]; + for (int i = 0; i < 8; i++) + { + bool xor = crc & 0x80; + crc = crc << 1; + crc = xor ? crc ^ G_POLYNOM : crc; + } + } + return crc; +} + +static esp_err_t send_cmd_nolock(sts3x_t *dev, uint16_t cmd) +{ + cmd = shuffle(cmd); + + return i2c_dev_write(&dev->i2c_dev, NULL, 0, &cmd, 2); +} + +static esp_err_t send_cmd(sts3x_t *dev, uint16_t cmd) +{ + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, send_cmd_nolock(dev, cmd)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +static esp_err_t start_nolock(sts3x_t *dev, sts3x_mode_t mode, sts3x_repeat_t repeat) +{ + dev->mode = mode; + dev->repeatability = repeat; + CHECK(send_cmd_nolock(dev, STS3X_MEASURE_CMD[mode][repeat])); + dev->meas_start_time = esp_timer_get_time(); + dev->meas_started = true; + dev->meas_first = true; + + return ESP_OK; +} + +static inline bool is_measuring(sts3x_t *dev) +{ + // not running if measurement is not started at all or + // it is not the first measurement in periodic mode + if (!dev->meas_started || !dev->meas_first) + return false; + + // not running if time elapsed is greater than duration + uint64_t elapsed = esp_timer_get_time() - dev->meas_start_time; + + return elapsed < STS3X_MEAS_DURATION_US[dev->repeatability]; +} + +static esp_err_t get_raw_data_nolock(sts3x_t *dev, sts3x_raw_data_t raw_data) +{ + if (!dev->meas_started) + { + ESP_LOGE(TAG, "Measurement is not started"); + return ESP_ERR_INVALID_STATE; + } + if (is_measuring(dev)) + { + ESP_LOGE(TAG, "Measurement is still running"); + return ESP_ERR_INVALID_STATE; + } + + // read raw data + uint16_t cmd = shuffle(STS3X_FETCH_DATA_CMD); + CHECK(i2c_dev_read(&dev->i2c_dev, &cmd, 2, raw_data, sizeof(sts3x_raw_data_t))); + + // reset first measurement flag + dev->meas_first = false; + + // reset measurement started flag in single shot mode + if (dev->mode == STS3X_SINGLE_SHOT) + dev->meas_started = false; + + // check temperature crc + if (crc8(raw_data, 2) != raw_data[2]) + { + ESP_LOGE(TAG, "CRC check for temperature data failed"); + return ESP_ERR_INVALID_CRC; + } + + return ESP_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +esp_err_t sts3x_init_desc(sts3x_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + dev->i2c_dev.port = port; + dev->i2c_dev.addr = addr; + dev->i2c_dev.cfg.sda_io_num = sda_gpio; + dev->i2c_dev.cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->i2c_dev.cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + return i2c_dev_create_mutex(&dev->i2c_dev); +} + +esp_err_t sts3x_free_desc(sts3x_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(&dev->i2c_dev); +} + +esp_err_t sts3x_init(sts3x_t *dev) +{ + CHECK_ARG(dev); + + dev->mode = STS3X_SINGLE_SHOT; + dev->meas_start_time = 0; + dev->meas_started = false; + dev->meas_first = false; + + return send_cmd(dev, STS3X_CLEAR_STATUS_CMD); +} + +esp_err_t sts3x_set_heater(sts3x_t *dev, bool enable) +{ + CHECK_ARG(dev); + + return send_cmd(dev, enable ? STS3X_HEATER_ON_CMD : STS3X_HEATER_OFF_CMD); +} + +esp_err_t sts3x_compute_values(sts3x_raw_data_t raw_data, float *temperature) +{ + CHECK_ARG(raw_data && temperature); + + if (temperature) + *temperature = ((((raw_data[0] * 256.0) + raw_data[1]) * 175) / 65535.0) - 45; + + return ESP_OK; +} + +esp_err_t sts3x_measure(sts3x_t *dev, float *temperature) +{ + CHECK_ARG(dev && temperature); + + sts3x_raw_data_t raw_data; + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, start_nolock(dev, STS3X_SINGLE_SHOT, STS3X_HIGH)); + vTaskDelay(STS3X_MEAS_DURATION_TICKS[STS3X_HIGH]); + I2C_DEV_CHECK(&dev->i2c_dev, get_raw_data_nolock(dev, raw_data)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return sts3x_compute_values(raw_data, temperature); +} + +uint8_t sts3x_get_measurement_duration(sts3x_repeat_t repeat) +{ + return STS3X_MEAS_DURATION_TICKS[repeat]; // in RTOS ticks +} + +esp_err_t sts3x_start_measurement(sts3x_t *dev, sts3x_mode_t mode, sts3x_repeat_t repeat) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, start_nolock(dev, mode, repeat)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +esp_err_t sts3x_stop_periodic_measurement(sts3x_t *dev) +{ + CHECK_ARG(dev); + + CHECK(send_cmd(dev, STS3X_STOP_PERIODIC_MEAS_CMD)); + dev->mode = STS3X_SINGLE_SHOT; + dev->meas_start_time = 0; + dev->meas_started = false; + dev->meas_first = false; + + return ESP_OK; +} + +esp_err_t sts3x_get_raw_data(sts3x_t *dev, sts3x_raw_data_t raw_data) +{ + CHECK_ARG(dev && raw_data); + + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, get_raw_data_nolock(dev, raw_data)); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +esp_err_t sts3x_get_results(sts3x_t *dev, float *temperature) +{ + CHECK_ARG(dev && temperature); + + sts3x_raw_data_t raw_data; + + CHECK(sts3x_get_raw_data(dev, raw_data)); + + return sts3x_compute_values(raw_data, temperature); +} diff --git a/components/sts3x/sts3x.h b/components/sts3x/sts3x.h new file mode 100644 index 00000000..d6ffb22d --- /dev/null +++ b/components/sts3x/sts3x.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2017 Gunar Schorcht + * Copyright (c) 2019 Ruslan V. Uss + * Copyright (c) 2023 Christian Skjerning + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file sts3x.h + * @defgroup sts3x sts3x + * @{ + * + * ESP-IDF driver for Sensirion STS3x digital temperature sensors + * + * Forked from + * + * Copyright (c) 2017 Gunar Schorcht \n + * Copyright (c) 2019 Ruslan V. Uss \n + * Copyright (c) 2023 Christian Skjerning + * + * BSD-3 Licensed as described in the file LICENSE + */ +#ifndef __STS3X_H__ +#define __STS3X_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define STS3X_I2C_ADDR_GND 0x44 +#define STS3X_I2C_ADDR_VDD 0x45 + +#define STS3X_RAW_DATA_SIZE 3 + +typedef uint8_t sts3x_raw_data_t[STS3X_RAW_DATA_SIZE]; + +/** + * Possible measurement modes + */ +typedef enum +{ + STS3X_SINGLE_SHOT = 0, //!< one single measurement + STS3X_PERIODIC_05MPS, //!< periodic with 0.5 measurements per second (mps) + STS3X_PERIODIC_1MPS, //!< periodic with 1 measurements per second (mps) + STS3X_PERIODIC_2MPS, //!< periodic with 2 measurements per second (mps) + STS3X_PERIODIC_4MPS, //!< periodic with 4 measurements per second (mps) + STS3X_PERIODIC_10MPS //!< periodic with 10 measurements per second (mps) +} sts3x_mode_t; + +/** + * Possible repeatability modes + */ +typedef enum +{ + STS3X_HIGH = 0, + STS3X_MEDIUM, + STS3X_LOW +} sts3x_repeat_t; + +/** + * Device descriptor + */ +typedef struct +{ + i2c_dev_t i2c_dev; //!< I2C device descriptor + + sts3x_mode_t mode; //!< used measurement mode + sts3x_repeat_t repeatability; //!< used repeatability + + bool meas_started; //!< indicates whether measurement started + uint64_t meas_start_time; //!< measurement start time in us + bool meas_first; //!< first measurement in periodic mode +} sts3x_t; + +/** + * @brief Initialize device descriptor + * + * @param dev Device descriptor + * @param port I2C port + * @param addr Device address + * @param sda_gpio SDA GPIO + * @param scl_gpio SCL GPIO + * @return `ESP_OK` on success + */ +esp_err_t sts3x_init_desc(sts3x_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t sts3x_free_desc(sts3x_t *dev); + +/** + * @brief Initialize sensor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t sts3x_init(sts3x_t *dev); + +/** + * @brief Enable/disable heater + * + * @param dev Device descriptor + * @param enable True to enable, false to disable + * @return `ESP_OK` on success + */ +esp_err_t sts3x_set_heater(sts3x_t *dev, bool enable); + +/** + * @brief High level measurement function + * + * For convenience this function comprises all three steps to perform + * one measurement in only one function: + * + * 1. Starts a measurement in single shot mode with high reliability + * 2. Waits using `vTaskDelay()` until measurement results are available + * 3. Returns the results in kind of floating point sensor values + * + * This function is the easiest way to use the sensor. It is most suitable + * for users that don't want to have the control on sensor details. + * + * @note The function delays the calling task up to 30 ms to wait for + * the measurement results. This might lead to problems when function + * is called from a software timer callback function. + * + * @param dev Device descriptor + * @param temperature Temperature in degree Celsius + * @return `ESP_OK` on success + */ +esp_err_t sts3x_measure(sts3x_t *dev, float *temperature); + +/** + * @brief Get the duration of a measurement in RTOS ticks. + * + * The function returns the duration in RTOS ticks required by the sensor to + * perform a measurement for the given repeatability. Once a measurement is + * started with function ::sts3x_start_measurement() the user task can use this + * duration in RTOS ticks directly to wait with function `vTaskDelay()` until + * the measurement results can be fetched. + * + * @note The duration only depends on repeatability level. Therefore, + * it can be considered as constant for a repeatability. + * + * @param repeat Repeatability, see type ::sts3x_repeat_t + * @return Measurement duration given in RTOS ticks + */ +uint8_t sts3x_get_measurement_duration(sts3x_repeat_t repeat); + +/** + * @brief Start the measurement in single shot or periodic mode + * + * The function starts the measurement either in *single shot mode* + * (exactly one measurement) or *periodic mode* (periodic measurements) + * with given repeatability. + * + * In the *single shot mode*, this function has to be called for each + * measurement. The measurement duration has to be waited every time + * before the results can be fetched. + * + * In the *periodic mode*, this function has to be called only once. Also + * the measurement duration has to be waited only once until the first + * results are available. After this first measurement, the sensor then + * automatically performs all subsequent measurements. The rate of periodic + * measurements can be 10, 4, 2, 1 or 0.5 measurements per second (mps). + * + * @note Due to inaccuracies in timing of the sensor, the user task + * should fetch the results at a lower rate. The rate of the periodic + * measurements is defined by the parameter \p mode. + * + * @param dev Device descriptor + * @param mode Measurement mode, see type ::sts3x_mode_t + * @param repeat Repeatability, see type ::sts3x_repeat_t + * @return `ESP_OK` on success + */ +esp_err_t sts3x_start_measurement(sts3x_t *dev, sts3x_mode_t mode, sts3x_repeat_t repeat); + +/** + * @brief Stop the periodic mode measurements + * + * The function stops the measurements in *periodic mode* + * (periodic measurements) and the sensor returns in *single shot mode* + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t sts3x_stop_periodic_measurement(sts3x_t *dev); + +/** + * @brief Read measurement results from sensor as raw data + * + * The function read measurement results from the sensor, checks the CRC + * checksum and stores them in the byte array as following. + * + * data[0] = Temperature MSB + * data[1] = Temperature LSB + * data[2] = Temperature CRC + * data[3] = Humidity MSB + * data[4] = Humidity LSB + * data[2] = Humidity CRC + * + * In case that there are no new data that can be read, the function fails. + * + * @param dev Device descriptor + * @param raw_data Byte array in which raw data are stored + * @return `ESP_OK` on success + */ +esp_err_t sts3x_get_raw_data(sts3x_t *dev, sts3x_raw_data_t raw_data); + +/** + * @brief Computes sensor values from raw data + * + * @param raw_data Byte array that contains raw data + * @param temperature Temperature in degree Celsius + * @return `ESP_OK` on success + */ +esp_err_t sts3x_compute_values(sts3x_raw_data_t raw_data, float *temperature); + +/** + * @brief Get measurement results in form of sensor values + * + * The function combines function ::sts3x_get_raw_data() and function + * ::sts3x_compute_values() to get the measurement results. + * + * In case that there are no results that can be read, the function fails. + * + * @param dev Device descriptor + * @param temperature Temperature in degree Celsius + * @return `ESP_OK` on success + */ +esp_err_t sts3x_get_results(sts3x_t *dev, float *temperature); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __STS3X_H__ */ diff --git a/components/tca6424a/.eil.yml b/components/tca6424a/.eil.yml new file mode 100644 index 00000000..f8ede34d --- /dev/null +++ b/components/tca6424a/.eil.yml @@ -0,0 +1,20 @@ +name: tca6424a +description: Driver for TCA6424A low-voltage 24-bit I2C I/O expander +version: 1.0.0 +groups: + - gpio +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2023 diff --git a/components/tca6424a/CMakeLists.txt b/components/tca6424a/CMakeLists.txt new file mode 100644 index 00000000..e224f9b7 --- /dev/null +++ b/components/tca6424a/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS tca6424a.c + INCLUDE_DIRS . + REQUIRES i2cdev log esp_idf_lib_helpers +) diff --git a/components/tca6424a/LICENSE b/components/tca6424a/LICENSE new file mode 100644 index 00000000..238462b6 --- /dev/null +++ b/components/tca6424a/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2023 Ruslan V. Uss + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/tca6424a/component.mk b/components/tca6424a/component.mk new file mode 100644 index 00000000..a3610e86 --- /dev/null +++ b/components/tca6424a/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers diff --git a/components/tca6424a/tca6424a.c b/components/tca6424a/tca6424a.c new file mode 100644 index 00000000..6e11af3a --- /dev/null +++ b/components/tca6424a/tca6424a.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2023 Ruslan V. Uss + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file tca6424a.c + * + * ESP-IDF driver for TCA6424A low-voltage 24-bit I2C I/O expander + * + * Copyright (c) 2023 Ruslan V. Uss + * + * BSD Licensed as described in the file LICENSE + */ + +#include "tca6424a.h" +#include +#include + +#define I2C_FREQ_HZ 400000 + +#define REG_AI_PREFIX BIT(7) + +#define REG_INPUT_0 0x00 +#define REG_INPUT_1 0x01 +#define REG_INPUT_2 0x02 +#define REG_OUTPUT_0 0x04 +#define REG_OUTPUT_1 0x05 +#define REG_OUTPUT_2 0x06 +#define REG_POL_0 0x08 +#define REG_POL_1 0x09 +#define REG_POL_2 0x0a +#define REG_CONF_0 0x0c +#define REG_CONF_1 0x0d +#define REG_CONF_2 0x0e + +#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) +#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) + +static const char *TAG = "tca6424a"; + +static esp_err_t read_reg_24(i2c_dev_t *dev, uint8_t reg, uint32_t *val) +{ + CHECK_ARG(dev && val); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, i2c_dev_read_reg(dev, reg, val, 3)); + I2C_DEV_GIVE_MUTEX(dev); + + *val &= 0x00ffffff; + + return ESP_OK; +} + +static esp_err_t write_reg_24(i2c_dev_t *dev, uint8_t reg, uint32_t val) +{ + CHECK_ARG(dev); + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, i2c_dev_write_reg(dev, reg, &val, 3)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +esp_err_t tca6424a_init_desc(i2c_dev_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + if (addr != TCA6424A_I2C_ADDRESS_GND && addr != TCA6424A_I2C_ADDRESS_VCC) + { + ESP_LOGE(TAG, "Invalid I2C address: 0x%02x", addr); + return ESP_ERR_INVALID_ARG; + } + + dev->port = port; + dev->addr = addr; + dev->cfg.sda_io_num = sda_gpio; + dev->cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + return i2c_dev_create_mutex(dev); +} + +esp_err_t tca6424a_free_desc(i2c_dev_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(dev); +} + +esp_err_t tca6424a_port_get_mode(i2c_dev_t *dev, uint32_t *mode) +{ + return read_reg_24(dev, REG_CONF_0 | REG_AI_PREFIX, mode); +} + +esp_err_t tca6424a_port_set_mode(i2c_dev_t *dev, uint32_t mode) +{ + return write_reg_24(dev, REG_CONF_0 | REG_AI_PREFIX, mode); +} + +esp_err_t tca6424a_port_get_polarity_inversion(i2c_dev_t *dev, uint32_t *polarity) +{ + return read_reg_24(dev, REG_POL_0 | REG_AI_PREFIX, polarity); +} + +esp_err_t tca6424a_port_set_polarity_inversion(i2c_dev_t *dev, uint32_t polarity) +{ + return write_reg_24(dev, REG_POL_0 | REG_AI_PREFIX, polarity); +} + +esp_err_t tca6424a_port_read(i2c_dev_t *dev, uint32_t *val) +{ + return read_reg_24(dev, REG_INPUT_0 | REG_AI_PREFIX, val); +} + +esp_err_t tca6424a_port_write(i2c_dev_t *dev, uint32_t val) +{ + return write_reg_24(dev, REG_OUTPUT_0 | REG_AI_PREFIX, val); +} + +esp_err_t tca6424a_get_level(i2c_dev_t *dev, uint8_t pin, uint32_t *val) +{ + CHECK_ARG(dev && val && pin < 24); + + uint8_t raw; + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, i2c_dev_read_reg(dev, REG_INPUT_0 + pin / 8, &raw, 1)); + I2C_DEV_GIVE_MUTEX(dev); + + *val = raw & BIT(pin % 8) ? 1 : 0; + + return ESP_OK; +} + +esp_err_t tca6424a_set_level(i2c_dev_t *dev, uint8_t pin, uint32_t val) +{ + CHECK_ARG(dev && pin < 24); + + uint8_t raw; + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, i2c_dev_read_reg(dev, REG_OUTPUT_0 + pin / 8, &raw, 1)); + raw &= BIT(pin % 8); + if (val) + raw |= BIT(pin % 8); + I2C_DEV_CHECK(dev, i2c_dev_write_reg(dev, REG_OUTPUT_0 + pin / 8, &raw, 1)); + I2C_DEV_GIVE_MUTEX(dev); + + return ESP_OK; +} diff --git a/components/tca6424a/tca6424a.h b/components/tca6424a/tca6424a.h new file mode 100644 index 00000000..7f972d7b --- /dev/null +++ b/components/tca6424a/tca6424a.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2023 Ruslan V. Uss + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file tca6424a.h + * @defgroup tca6424a tca6424a + * @{ + * + * ESP-IDF driver for TCA6424A low-voltage 24-bit I2C I/O expander + * + * Copyright (c) 2023 Ruslan V. Uss + * + * BSD Licensed as described in the file LICENSE + */ +#ifndef __TCA6424A_H__ +#define __TCA6424A_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TCA6424A_I2C_ADDRESS_GND 0x22 +#define TCA6424A_I2C_ADDRESS_VCC 0x23 + +/** + * @brief Initialize device descriptor + * + * Default SCL frequency is 400kHz + * + * @param dev Pointer to I2C device descriptor + * @param addr I2C address (`0b0100`) + * @param port I2C port number + * @param sda_gpio SDA GPIO + * @param scl_gpio SCL GPIO + * @return `ESP_OK` on success + */ +esp_err_t tca6424a_init_desc(i2c_dev_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * @param dev Pointer to I2C device descriptor + * @return `ESP_OK` on success + */ +esp_err_t tca6424a_free_desc(i2c_dev_t *dev); + +/** + * @brief Get GPIO pins mode + * + * 0 - output, 1 - input for lower 24 bits in `val` + * + * @param dev Pointer to device descriptor + * @param[out] mode Buffer to store mode, 0 bit for P00 .. 23 bit for P27 + * @return `ESP_OK` on success + */ +esp_err_t tca6424a_port_get_mode(i2c_dev_t *dev, uint32_t *mode); + +/** + * @brief Set GPIO pins mode + * + * 0 - output, 1 - input for lower 24 bits in `val` + * + * @param dev Pointer to device descriptor + * @param mode Mode, 0 bit for P00 .. 23 bit for P27 + * @return `ESP_OK` on success + */ +esp_err_t tca6424a_port_set_mode(i2c_dev_t *dev, uint32_t mode); + +/** + * @brief Get GPIO pins polarity inversion + * + * 0 - no inversion, 1 - invert polarity for lower 24 bits in `val` + * + * @param dev Pointer to device descriptor + * @param[out] polarity Polarity, 0 bit for P00 .. 23 bit for P27 + * @return `ESP_OK` on success + */ +esp_err_t tca6424a_port_get_polarity_inversion(i2c_dev_t *dev, uint32_t *polarity); + +/** + * @brief Set GPIO pins polarity inversion + * + * 0 - no inversion, 1 - invert polarity for lower 24 bits in `val` + * + * @param dev Pointer to device descriptor + * @param polarity Polarity, 0 bit for P00 .. 23 bit for P27 + * @return `ESP_OK` on success + */ +esp_err_t tca6424a_port_set_polarity_inversion(i2c_dev_t *dev, uint32_t polarity); + +/** + * @brief Read GPIO port value + * + * @param dev Pointer to I2C device descriptor + * @param val 24-bit GPIO port value, 0 bit for P00 .. 23 bit for P27 + * @return `ESP_OK` on success + */ +esp_err_t tca6424a_port_read(i2c_dev_t *dev, uint32_t *val); + +/** + * @brief Write value to GPIO port + * + * @param dev Pointer to I2C device descriptor + * @param val GPIO port value, 0 bit for P00 .. 23 bit for P27 + * @return ESP_OK on success + */ +esp_err_t tca6424a_port_write(i2c_dev_t *dev, uint32_t val); + +/** + * @brief Read GPIO pin level + * + * @param dev Pointer to device descriptor + * @param pin Pin number, 0 bit for P00 .. 23 bit for P27 + * @param[out] val `true` if pin currently in high state + * @return `ESP_OK` on success + */ +esp_err_t tca6424a_get_level(i2c_dev_t *dev, uint8_t pin, uint32_t *val); + +/** + * @brief Set GPIO pin level + * + * Pin must be set up as output + * + * @param dev Pointer to device descriptor + * @param pin Pin number, 0 bit for P00 .. 23 bit for P27 + * @param[out] val `true` if pin currently in high state + * @return `ESP_OK` on success + */ +esp_err_t tca6424a_set_level(i2c_dev_t *dev, uint8_t pin, uint32_t val); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __TCA6424A_H__ */ diff --git a/components/tca9548/.eil.yml b/components/tca9548/.eil.yml index 3ef59bc1..5c2b9908 100644 --- a/components/tca9548/.eil.yml +++ b/components/tca9548/.eil.yml @@ -1,24 +1,20 @@ ---- -components: - - name: tca9548 - description: | - Driver for TCA9548A/PCA9548A low-voltage 8-channel I2C switch - group: misc - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2020 +name: tca9548 +description: Driver for TCA9548A/PCA9548A low-voltage 8-channel I2C switch +version: 1.0.0 +groups: + - misc +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2020 diff --git a/components/tca95x5/.eil.yml b/components/tca95x5/.eil.yml index 0992228d..cc462096 100644 --- a/components/tca95x5/.eil.yml +++ b/components/tca95x5/.eil.yml @@ -1,24 +1,20 @@ ---- -components: - - name: tca95x5 - description: | - Driver for TCA9535/TCA9555 remote 16-bit I/O expanders for I2C-bus - group: gpio - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2019 +name: tca95x5 +description: Driver for TCA9535/TCA9555 remote 16-bit I/O expanders for I2C-bus +version: 1.0.0 +groups: + - gpio +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2019 diff --git a/components/tda74xx/.eil.yml b/components/tda74xx/.eil.yml index 01bb8e91..e4d5874d 100644 --- a/components/tda74xx/.eil.yml +++ b/components/tda74xx/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: tda74xx - description: Driver for TDA7439/TDA7439DS/TDA7440D audioprocessors - group: misc - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: UncleRus - year: 2018 +name: tda74xx +description: Driver for TDA7439/TDA7439DS/TDA7440D audioprocessors +version: 1.0.0 +groups: + - misc +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: UncleRus + year: 2018 diff --git a/components/tsl2561/.eil.yml b/components/tsl2561/.eil.yml index 05066b4d..4baa70a2 100644 --- a/components/tsl2561/.eil.yml +++ b/components/tsl2561/.eil.yml @@ -1,26 +1,22 @@ ---- -components: - - name: tsl2561 - description: Driver for light-to-digital converter TSL2561 - group: light - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2018 - - author: - name: bschwind - year: 2016 +name: tsl2561 +description: Driver for light-to-digital converter TSL2561 +version: 1.0.0 +groups: + - light +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: bschwind + year: 2016 + - name: UncleRus + year: 2018 diff --git a/components/tsl2591/.eil.yml b/components/tsl2591/.eil.yml index 171dda52..50246837 100644 --- a/components/tsl2591/.eil.yml +++ b/components/tsl2591/.eil.yml @@ -1,23 +1,20 @@ ---- -components: - - name: tsl2591 - description: Driver for light-to-digital converter TSL2591 - group: light - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: MIT - copyrights: - - author: - name: juliandoerner - year: 2020 +name: tsl2591 +description: Driver for light-to-digital converter TSL2591 +version: 1.0.0 +groups: + - light +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: MIT +copyrights: + - name: juliandoerner + year: 2020 diff --git a/components/tsl2591/tsl2591.c b/components/tsl2591/tsl2591.c index 911e0d68..5f017b1a 100644 --- a/components/tsl2591/tsl2591.c +++ b/components/tsl2591/tsl2591.c @@ -70,14 +70,6 @@ static const char *TAG = "tsl2591"; #define TSL2591_SPECIAL_CLEAR_BOTH 0x07 // Clear ALS and no persist ALS interrupt #define TSL2591_SPECIAL_CLEAR_NP_INTR 0x0A // Clear no persist ALS interrupt -// TSL2591 integration times in useconds. -#define TSL2591_INTEGRATION_TIME_100MS 110 -#define TSL2591_INTEGRATION_TIME_200MS 210 -#define TSL2591_INTEGRATION_TIME_300MS 310 -#define TSL2591_INTEGRATION_TIME_400MS 410 -#define TSL2591_INTEGRATION_TIME_500MS 510 -#define TSL2591_INTEGRATION_TIME_600MS 610 - // TSL2591 status flags. #define TSL2591_STATUS_ALS_INTR 0x10 #define TSL2591_STATUS_ALS_NP_INTR 0x20 @@ -90,6 +82,15 @@ static const char *TAG = "tsl2591"; #define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) #define SLEEP_MS(x) do { vTaskDelay(pdMS_TO_TICKS(x)); } while (0) +static const uint32_t integration_time_ms[] = { + [TSL2591_INTEGRATION_100MS] = 100, + [TSL2591_INTEGRATION_200MS] = 200, + [TSL2591_INTEGRATION_300MS] = 300, + [TSL2591_INTEGRATION_400MS] = 400, + [TSL2591_INTEGRATION_500MS] = 500, + [TSL2591_INTEGRATION_600MS] = 600, +}; + // Read/write to registers. static inline esp_err_t write_register(tsl2591_t *dev, uint8_t reg, uint8_t value) { @@ -147,7 +148,6 @@ static inline esp_err_t read_register16(tsl2591_t *dev, uint8_t low_register, ui *value = (uint16_t)buf[1] << 8 | buf[0]; return ESP_OK; - } @@ -201,27 +201,12 @@ esp_err_t tsl2591_init(tsl2591_t *dev) // Wait until the first integration cycle is completed. tsl2591_integration_time_t integration_time; ESP_ERROR_CHECK(tsl2591_get_integration_time(dev, &integration_time)); - switch (integration_time) + if (integration_time > TSL2591_INTEGRATION_600MS) { - case TSL2591_INTEGRATION_100MS: - SLEEP_MS(110); - break; - case TSL2591_INTEGRATION_200MS: - SLEEP_MS(210); - break; - case TSL2591_INTEGRATION_300MS: - SLEEP_MS(310); - break; - case TSL2591_INTEGRATION_400MS: - SLEEP_MS(410); - break; - case TSL2591_INTEGRATION_500MS: - SLEEP_MS(510); - break; - case TSL2591_INTEGRATION_600MS: - SLEEP_MS(610); - break; + ESP_LOGE(TAG, "Invalid integration time: %d", integration_time); + return ESP_ERR_INVALID_STATE; } + SLEEP_MS(integration_time_ms[integration_time] + 10); return ESP_OK; } @@ -247,53 +232,33 @@ esp_err_t tsl2591_calculate_lux(tsl2591_t *dev, uint16_t channel0, uint16_t chan { CHECK_ARG(dev && lux); - float atime, again; - switch (dev->settings.control_reg & 0x07) - { - case TSL2591_INTEGRATION_100MS: - atime = 100; - break; - case TSL2591_INTEGRATION_200MS: - atime = 200; - break; - case TSL2591_INTEGRATION_300MS: - atime = 300; - break; - case TSL2591_INTEGRATION_400MS: - atime = 400; - break; - case TSL2591_INTEGRATION_500MS: - atime = 500; - break; - case TSL2591_INTEGRATION_600MS: - atime = 600; - break; - default: - atime = 100; - } + uint8_t itime = dev->settings.control_reg & 0x07; + if (itime > TSL2591_INTEGRATION_600MS) + itime = TSL2591_INTEGRATION_100MS; + float atime = (float)integration_time_ms[itime]; + float again; switch (dev->settings.control_reg & TSL2591_GAIN_MAX) { - case TSL2591_GAIN_LOW: - again = 1; - break; - case TSL2591_GAIN_MEDIUM: - again = 25; - break; - case TSL2591_GAIN_HIGH: - again = 428; - break; - case TSL2591_GAIN_MAX: - again = 9876; - break; - default: - again = 1; + case TSL2591_GAIN_LOW: + again = 1; + break; + case TSL2591_GAIN_MEDIUM: + again = 25; + break; + case TSL2591_GAIN_HIGH: + again = 428; + break; + case TSL2591_GAIN_MAX: + again = 9876; + break; + default: + again = 1; } // See Adafruit Arduino driver. float cpl = (atime * again) / TSL2591_LUX_DF; - *lux = (((float)channel0 - (float)channel1)) * - (1.0F - ((float)channel1 / (float)channel0)) / cpl; + *lux = (((float)channel0 - (float)channel1)) * (1.0F - ((float)channel1 / (float)channel0)) / cpl; return ESP_OK; } @@ -418,7 +383,7 @@ esp_err_t tsl2591_set_integration_time(tsl2591_t *dev, tsl2591_integration_time_ I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); // Last 3 bits represent the integration time. - I2C_DEV_CHECK(&dev->i2c_dev, + I2C_DEV_CHECK(&dev->i2c_dev, write_control_register(dev, (dev->settings.control_reg & ~0x07) | integration_time)); dev->settings.control_reg = (dev->settings.control_reg & ~0x07) | integration_time; diff --git a/components/tsl2591/tsl2591.h b/components/tsl2591/tsl2591.h index 0d64b9da..4f5dce4c 100644 --- a/components/tsl2591/tsl2591.h +++ b/components/tsl2591/tsl2591.h @@ -131,27 +131,20 @@ typedef enum TSL2591_60_CYCLES } tsl2591_persistence_filter_t; -/** - * Device settings. - */ -typedef struct -{ - uint8_t enable_reg; - uint8_t control_reg; - uint8_t persistence_reg; -} tsl2591_settings_t; - /** * Device descriptor. */ typedef struct { i2c_dev_t i2c_dev; - tsl2591_settings_t settings; - + struct + { + uint8_t enable_reg; + uint8_t control_reg; + uint8_t persistence_reg; + } settings; } tsl2591_t; - /** * @brief Initialize device descriptor * diff --git a/components/tsl4531/.eil.yml b/components/tsl4531/.eil.yml index 7d81c32b..4561271a 100644 --- a/components/tsl4531/.eil.yml +++ b/components/tsl4531/.eil.yml @@ -1,26 +1,22 @@ ---- -components: - - name: tsl4531 - description: Driver for digital ambient light sensor TSL4531 - group: light - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2019 - - author: - name: bschwind - year: 2017 +name: tsl4531 +description: Driver for digital ambient light sensor TSL4531 +version: 1.0.1 +groups: + - light +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: bschwind + year: 2017 + - name: UncleRus + year: 2019 diff --git a/components/tsys01/.eil.yml b/components/tsys01/.eil.yml index 28144b7a..710f9c21 100644 --- a/components/tsys01/.eil.yml +++ b/components/tsys01/.eil.yml @@ -1,24 +1,20 @@ ---- -components: - - name: tsys01 - description: | - Driver for precision digital temperature sensor TSYS01 - group: temperature - groups: [] - code_owners: UncleRus - depends: - - i2cdev - - log - - esp_idf_lib_helpers - thread_safe: yes - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2020 +name: tsys01 +description: Driver for precision digital temperature sensor TSYS01 +version: 1.0.0 +groups: + - temperature +code_owners: UncleRus +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2020 diff --git a/components/ultrasonic/.eil.yml b/components/ultrasonic/.eil.yml index 6aba8ac1..c16e9514 100644 --- a/components/ultrasonic/.eil.yml +++ b/components/ultrasonic/.eil.yml @@ -1,24 +1,21 @@ ---- -components: - - name: ultrasonic - description: Driver for ultrasonic range meters, e.g. HC-SR04, HY-SRF05 - group: misc - groups: [] - code_owners: UncleRus - depends: - # XXX conditional depends - - driver - - freertos - - esp_idf_lib_helpers - thread_safe: no - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2016 +name: ultrasonic +description: Driver for ultrasonic range meters, e.g. HC-SR04, HY-SRF05 +version: 1.0.0 +groups: + - misc +code_owners: UncleRus +depends: + # XXX conditional depends + - driver + - freertos + - esp_idf_lib_helpers +thread_safe: no +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2016 diff --git a/components/veml7700/.eil.yml b/components/veml7700/.eil.yml new file mode 100644 index 00000000..8b66f7ea --- /dev/null +++ b/components/veml7700/.eil.yml @@ -0,0 +1,20 @@ +name: veml7700 +description: Driver for VEML7700 ambient light sensor +version: 1.0.0 +groups: + - light +code_owners: Th3Link +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: ISC +copyrights: + - name: Th3Link + year: 2019 diff --git a/components/veml7700/CMakeLists.txt b/components/veml7700/CMakeLists.txt new file mode 100644 index 00000000..f30ec5d9 --- /dev/null +++ b/components/veml7700/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS veml7700.c + INCLUDE_DIRS . + REQUIRES i2cdev log esp_idf_lib_helpers +) diff --git a/components/veml7700/LICENSE b/components/veml7700/LICENSE new file mode 100644 index 00000000..8ff1cf02 --- /dev/null +++ b/components/veml7700/LICENSE @@ -0,0 +1,17 @@ +ISC License + +SPDX-License-Identifier: ISC + +Copyright (c) 2022 Marc Luehr + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/components/veml7700/README.md b/components/veml7700/README.md new file mode 100644 index 00000000..98c587cf --- /dev/null +++ b/components/veml7700/README.md @@ -0,0 +1,104 @@ +# Driver for the VEML7700 ambient light sensor + +This is the driver for the VEML7700 ambiernt light sensor from Vishay. It is not the +cheapest solution but very easy to integrate, with a minimum of further components. +The sensor can be SMD soldered by hand on a custom PCB, but also several breakout boards +are available. + +## About the sensor + +The VEML7700 is a high-accuracy ambient light sensor. All measurements and filtering +is done on the chip which makes it easy to integrate. The sensitivity can be adjusted +by a gain setting and by changing the integration time. The output of the sensor is a +count value which must be converted using the resolution value in the driver. + +The sensor has power saving features which reduces the repetition of measurements. +It has also a power off feature to save even more power. + +The ambient light sensor value is filterd very clone to the caracteristic of the +human eye. Besides, als the white channel with a wider wavelength spectrum. In most +applications, the ambient light value will do just fine. + +## Power consumption + +- 0.5 μA in shut-down mode (@3.3V) +- down to 2 μA in power save mode 4 (@3.3V) +- 45 μA on 100ms integration time (@3.3V) + +## Communication interface + +I2C is used as communication interface without any further interrupt pins. It has six +command codes for the settings and the output values. Read the datasheet or application +note for further information. + +To reduce interactions with the integrated microcontroller, the interrupt feature can +be used. Therefore, one must configure the low and high threshold and enable the interrupt. + +## Interrupt application examples + +If values below a certain threshold is of interest, i.e. to activate lights when its +getting dark outside, the low threshold should be adjusted and setting the high threshold +to maximum (65535). + +Another application could be an automated rollershutter, then both thresholds sould be +set to trigger the up and down movement of rollershutters. + +## Measurement process + +The measurement takes time and the sensor should not be read out faster than the +measurement time. Therefore the application should be adjusted to the sensor configuration +regarting integration time and power save modes. Alternatively, the interrupt feature +can be used by repeatetive reading of the interrupt status. + +## Usage + +This driver uses i2cdev which must be initialized first. Then initialize the device +decriptor, one discriptor per device. The sensor can be used without configuration, +but has a high chance of over-saturation on sunlight. Therefore, change gain and +integration time to your needs and configure the device. + +Then, the veml7700_ambient_light and veml7700_white_channel functions can be used +to read out the brightness. The driver converts the counts from the device to lx using +the configuration. + +### Hardware configurations + +The driver supports multiple VEML7700 sensors at the same time that are +connected to I2C. Following figure show some possible hardware +configurations. + +First figure shows the configuration with one sensor at I2C bus 0. + +```text + +------------------+ +----------+ + | ESP8266 / ESP32 | | VEML7700 | + | | | | + | GPIO 14 (SCL) ----> SCL | + | GPIO 13 (SDA) <---> SDA | + +------------------+ +----------+ +``` + +Next figure shows a possible configuration with two I2C buses. + +```text + +------------------+ +----------+ + | ESP8266 / ESP32 | | VEML7700 | + | | | | + | GPIO 14 (SCL) ----> SCL | + | GPIO 13 (SDA) <---> SDA | + | | +----------+ + | | | VEML7700 | + | | | | + | GPIO 5 (SCL) ----> SCL | + | GPIO 4 (SDA) <---> SDA | + +------------------+ +----------+ +``` + +Only one sensor per I2C bus is possible, since it uses a unconfigurable I2C slave address. +However, one could also use GPIO controller bus buffer to connect the bus to different +devices. + +## References +[Datasheet](https://www.vishay.com/docs/84286/veml7700.pdf) + +[Application Note](https://www.vishay.com/docs/84323/designingveml7700.pdf) diff --git a/components/veml7700/component.mk b/components/veml7700/component.mk new file mode 100644 index 00000000..4071e351 --- /dev/null +++ b/components/veml7700/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers \ No newline at end of file diff --git a/components/veml7700/veml7700.c b/components/veml7700/veml7700.c new file mode 100644 index 00000000..35532bea --- /dev/null +++ b/components/veml7700/veml7700.c @@ -0,0 +1,276 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2022 Marc Luehr + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define VEML7700_COMMAND_CODE_ALS_CONF_0 (0) +#define VEML7700_COMMAND_CODE_ALS_WH (1) +#define VEML7700_COMMAND_CODE_ALS_WL (2) +#define VEML7700_COMMAND_CODE_POWER_SAVING (3) +#define VEML7700_COMMAND_CODE_ALS (4) +#define VEML7700_COMMAND_CODE_WHITE (5) +#define VEML7700_COMMAND_CODE_ALS_INT (6) + +#define VEML7700_GAIN_MASK (0x1800) +#define VEML7700_GAIN_SHIFT (11) + +#define VEML7700_INTEGRATION_TIME_MASK (0x03C0) +#define VEML7700_INTEGRATION_TIME_SHIFT (6) + +#define VEML7700_PERSISTENCE_PROTECTION_MASK (0x0030) +#define VEML7700_PERSISTENCE_PROTECTION_SHIFT (4) + +#define VEML7700_INTERRUPT_ENABLE_MASK (0x0002) +#define VEML7700_INTERRUPT_ENABLE_SHIFT (1) + +#define VEML7700_SHUTDOWN_MASK (0x0001) +#define VEML7700_SHUTDOWN_SHIFT (0) + +#define VEML7700_POWER_SAVING_MODE_MASK (0x0060) +#define VEML7700_POWER_SAVING_MODE_SHIFT (1) + +#define VEML7700_POWER_SAVING_MODE_ENABLE_MASK (0x0001) +#define VEML7700_POWER_SAVING_MODE_ENABLE_SHIFT (0) + +#define VEML7700_INTERRUPT_STATUS_LOW_MASK (0x8000) +#define VEML7700_INTERRUPT_STATUS_LOW_SHIFT (15) +#define VEML7700_INTERRUPT_STATUS_HIGH_MASK (0x4000) +#define VEML7700_INTERRUPT_STATUS_HIGH_SHIFT (14) + +#define VEML7700_RESOLUTION_800MS_IT_GAIN_2 (36) +#define VEML7700_RESOLUTION_800MS_IT_GAIN_2_DIV (10000) + +/** + * @file veml7700.c + * + * ESP-IDF driver for VEML7700 brightness sensors for I2C-bus + * + * Copyright (c) 2022 Marc Luehr + * + * MIT Licensed as described in the file LICENSE + */ + +#include "veml7700.h" + +#define I2C_FREQ_HZ (100000) + +#define CHECK(x) \ + do \ + { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) \ + return __; \ + } \ + while (0) +#define CHECK_ARG(VAL) \ + do \ + { \ + if (!(VAL)) \ + return ESP_ERR_INVALID_ARG; \ + } \ + while (0) + +static esp_err_t read_port(i2c_dev_t *dev, uint8_t command_code, uint16_t *data) +{ + CHECK_ARG(dev); + + I2C_DEV_CHECK(dev, i2c_dev_read(dev, &command_code, 1, data, 2)); + + return ESP_OK; +} + +static esp_err_t write_port(i2c_dev_t *dev, uint8_t command_code, uint16_t data) +{ + CHECK_ARG(dev); + + I2C_DEV_CHECK(dev, i2c_dev_write(dev, &command_code, 1, &data, 2)); + + return ESP_OK; +} + +static uint32_t resolution(veml7700_config_t *config) +{ + CHECK_ARG(config); + + uint32_t resolution = VEML7700_RESOLUTION_800MS_IT_GAIN_2; + switch (config->gain) + { + case VEML7700_GAIN_1: + resolution = resolution * 2; + break; + case VEML7700_GAIN_DIV_4: + resolution = resolution * 8; + break; + case VEML7700_GAIN_DIV_8: + resolution = resolution * 16; + break; + } + switch (config->integration_time) + { + case VEML7700_INTEGRATION_TIME_400MS: + resolution = resolution * 2; + break; + case VEML7700_INTEGRATION_TIME_200MS: + resolution = resolution * 4; + break; + case VEML7700_INTEGRATION_TIME_100MS: + resolution = resolution * 8; + break; + case VEML7700_INTEGRATION_TIME_50MS: + resolution = resolution * 16; + break; + case VEML7700_INTEGRATION_TIME_25MS: + resolution = resolution * 32; + break; + } + return resolution; +} + +/////////////////////////////////////////////////////////////////////////////// + +esp_err_t veml7700_init_desc(i2c_dev_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + dev->port = port; + dev->addr = VEML7700_I2C_ADDR; + dev->cfg.sda_io_num = sda_gpio; + dev->cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + return i2c_dev_create_mutex(dev); +} + +esp_err_t veml7700_free_desc(i2c_dev_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(dev); +} + +esp_err_t veml7700_probe(i2c_dev_t *dev) +{ + CHECK_ARG(dev); + + /* use write request since read request causes a timeout; + * just doing a read is not intended to use by the chip, + * it is waiting for a command code + */ + I2C_DEV_TAKE_MUTEX(dev); + esp_err_t err = i2c_dev_probe(dev, I2C_DEV_WRITE); + I2C_DEV_GIVE_MUTEX(dev); + return err; +} + +esp_err_t veml7700_set_config(i2c_dev_t *dev, veml7700_config_t *config) +{ + CHECK_ARG(dev); + CHECK_ARG(config); + + uint16_t config_data = 0; + config_data |= config->gain << VEML7700_GAIN_SHIFT; + config_data |= config->integration_time << VEML7700_INTEGRATION_TIME_SHIFT; + config_data |= config->persistence_protect << VEML7700_PERSISTENCE_PROTECTION_SHIFT; + config_data |= config->interrupt_enable << VEML7700_INTERRUPT_ENABLE_SHIFT; + config_data |= config->shutdown << VEML7700_SHUTDOWN_SHIFT; + + uint16_t power_saving_data = 0; + power_saving_data |= config->power_saving_mode << VEML7700_POWER_SAVING_MODE_SHIFT; + power_saving_data |= config->power_saving_enable << VEML7700_POWER_SAVING_MODE_ENABLE_SHIFT; + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, write_port(dev, VEML7700_COMMAND_CODE_ALS_CONF_0, config_data)); + I2C_DEV_CHECK(dev, write_port(dev, VEML7700_COMMAND_CODE_ALS_WH, config->threshold_high)); + I2C_DEV_CHECK(dev, write_port(dev, VEML7700_COMMAND_CODE_ALS_WL, config->threshold_low)); + I2C_DEV_CHECK(dev, write_port(dev, VEML7700_COMMAND_CODE_POWER_SAVING, power_saving_data)); + I2C_DEV_GIVE_MUTEX(dev); + return ESP_OK; +} + +esp_err_t veml7700_get_config(i2c_dev_t *dev, veml7700_config_t *config) +{ + CHECK_ARG(dev); + CHECK_ARG(config); + + uint16_t config_data = 0; + uint16_t power_saving_data = 0; + + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, read_port(dev, VEML7700_COMMAND_CODE_ALS_CONF_0, &config_data)); + I2C_DEV_CHECK(dev, read_port(dev, VEML7700_COMMAND_CODE_ALS_WH, &(config->threshold_high))); + I2C_DEV_CHECK(dev, read_port(dev, VEML7700_COMMAND_CODE_ALS_WL, &(config->threshold_low))); + I2C_DEV_CHECK(dev, read_port(dev, VEML7700_COMMAND_CODE_POWER_SAVING, &power_saving_data)); + I2C_DEV_GIVE_MUTEX(dev); + + config->gain = (config_data & VEML7700_GAIN_MASK) >> VEML7700_GAIN_SHIFT; + config->integration_time = (config_data & VEML7700_INTEGRATION_TIME_MASK) + >> VEML7700_INTEGRATION_TIME_SHIFT; + config->integration_time = (config_data & VEML7700_PERSISTENCE_PROTECTION_MASK) + >> VEML7700_PERSISTENCE_PROTECTION_SHIFT; + config->integration_time = (config_data & VEML7700_INTERRUPT_ENABLE_MASK) + >> VEML7700_INTERRUPT_ENABLE_SHIFT; + config->integration_time = (config_data & VEML7700_SHUTDOWN_MASK) + >> VEML7700_SHUTDOWN_SHIFT; + + config->power_saving_mode = (power_saving_data & VEML7700_POWER_SAVING_MODE_MASK) + >> VEML7700_POWER_SAVING_MODE_SHIFT; + config->power_saving_enable = (power_saving_data & VEML7700_POWER_SAVING_MODE_ENABLE_MASK) + >> VEML7700_POWER_SAVING_MODE_ENABLE_SHIFT; + + return ESP_OK; +} + +esp_err_t veml7700_get_ambient_light(i2c_dev_t *dev, veml7700_config_t *config, uint32_t *value) +{ + CHECK_ARG(dev); + CHECK_ARG(config); + + uint16_t raw_value = 0; + CHECK(read_port(dev, VEML7700_COMMAND_CODE_ALS, &raw_value)); + + *value = (raw_value * resolution(config)) / VEML7700_RESOLUTION_800MS_IT_GAIN_2_DIV; + return ESP_OK; +} + +esp_err_t veml7700_get_white_channel(i2c_dev_t *dev, veml7700_config_t *config, uint32_t *value) +{ + CHECK_ARG(dev); + CHECK_ARG(config); + + uint16_t raw_value = 0; + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, read_port(dev, VEML7700_COMMAND_CODE_WHITE, &raw_value)); + I2C_DEV_GIVE_MUTEX(dev); + + *value = (raw_value * resolution(config)) / VEML7700_RESOLUTION_800MS_IT_GAIN_2_DIV; + return ESP_OK; +} + +esp_err_t veml7700_get_interrupt_status(i2c_dev_t *dev, bool *low_threshold, bool *high_threshold) +{ + CHECK_ARG(dev); + + uint16_t interrupt_status = 0; + I2C_DEV_TAKE_MUTEX(dev); + I2C_DEV_CHECK(dev, read_port(dev, VEML7700_COMMAND_CODE_ALS_INT, &interrupt_status)); + I2C_DEV_GIVE_MUTEX(dev); + + *high_threshold = interrupt_status & VEML7700_INTERRUPT_STATUS_HIGH_MASK; + *low_threshold = interrupt_status & VEML7700_INTERRUPT_STATUS_LOW_MASK; + return ESP_OK; +} diff --git a/components/veml7700/veml7700.h b/components/veml7700/veml7700.h new file mode 100644 index 00000000..a498605f --- /dev/null +++ b/components/veml7700/veml7700.h @@ -0,0 +1,164 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2022 Marc Luehr + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file veml7700.h + * @defgroup veml7700 veml7700 + * @{ + * + * ESP-IDF driver for VEML7700 brightness sensors for I2C-bus + * + * Copyright (c) 2022 Marc Luehr + * + * ISC Licensed as described in the file LICENSE + */ +#ifndef __VEML7700_H__ +#define __VEML7700_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define VEML7700_I2C_ADDR (0x10) + +#define VEML7700_INTEGRATION_TIME_25MS (0b1100) +#define VEML7700_INTEGRATION_TIME_50MS (0b1000) +#define VEML7700_INTEGRATION_TIME_100MS (0b0000) +#define VEML7700_INTEGRATION_TIME_200MS (0b0001) +#define VEML7700_INTEGRATION_TIME_400MS (0b0010) +#define VEML7700_INTEGRATION_TIME_800MS (0b0011) + +#define VEML7700_GAIN_1 (0b00) +#define VEML7700_GAIN_2 (0b01) +#define VEML7700_GAIN_DIV_8 (0b10) +#define VEML7700_GAIN_DIV_4 (0b11) + +#define VEML7700_POWER_SAVING_MODE_500MS (0b00) +#define VEML7700_POWER_SAVING_MODE_1000MS (0b01) +#define VEML7700_POWER_SAVING_MODE_2000MS (0b10) +#define VEML7700_POWER_SAVING_MODE_4000MS (0b11) + +#define VEML7700_PERSISTENCE_PROTECTION_1 (0b00) +#define VEML7700_PERSISTENCE_PROTECTION_2 (0b01) +#define VEML7700_PERSISTENCE_PROTECTION_4 (0b10) +#define VEML7700_PERSISTENCE_PROTECTION_8 (0b11) + +/** + * VEML configuration descriptor + */ +typedef struct +{ + uint16_t gain : 2; //!< control the sensitivity + uint16_t integration_time : 4; //!< time to measure + uint16_t persistence_protect : 2; //!< sample count before the interrupt triggers + uint16_t interrupt_enable : 1; //!< enable threshold interrupt + uint16_t shutdown : 1; //!< set to 1 to shutdown the device, set to 0 to wakeup + uint16_t threshold_high; //!< high threshold for the interrupt + uint16_t threshold_low; //!< low threshold for the interrupt + uint16_t power_saving_mode : 2; //!< power saving mode + uint16_t power_saving_enable : 1; //!< enable the pover saving mode +} veml7700_config_t; + +/** + * @brief Initialize device descriptor + * + * Default SCL frequency is 100kHz. The I2C address is fix. + * + * @param dev Pointer to I2C device descriptor + * @param port I2C port number + * @param sda_gpio SDA GPIO + * @param scl_gpio SCL GPIO + * @return `ESP_OK` on success + */ +esp_err_t veml7700_init_desc(i2c_dev_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param dev Pointer to I2C device descriptor + * @return `ESP_OK` on success + */ +esp_err_t veml7700_free_desc(i2c_dev_t *dev); + +/** + * @brief Probe if the device exist on the bus + * + * @param dev Pointer to I2C device descriptor + * @return `ESP_OK` on success + */ +esp_err_t veml7700_probe(i2c_dev_t *dev); + +/** + * @brief Write the config to the device + * + * @param dev Pointer to I2C device descriptor + * @param config Pointer to the config descriptor + * @return `ESP_OK` on success + */ +esp_err_t veml7700_set_config(i2c_dev_t *dev, veml7700_config_t *config); + +/** + * @brief Read the config to the device + * + * @param dev Pointer to I2C device descriptor + * @param config Pointer to the config descriptor + * @return `ESP_OK` on success + */ +esp_err_t veml7700_get_config(i2c_dev_t *dev, veml7700_config_t *config); + +/** + * @brief Read ambient light sensor value from the device + * + * @param dev Pointer to I2C device descriptor + * @param config Pointer to the config descriptor + * @param value_lux Pointer as return value in lux + * @return `ESP_OK` on success + */ +esp_err_t veml7700_get_ambient_light(i2c_dev_t *dev, veml7700_config_t *config, uint32_t *value_lux); + +/** + * @brief Read white channel value from the device + * + * @param dev Pointer to I2C device descriptor + * @param config Pointer to the config descriptor + * @param value_lux Pointer as return value in lux + * @return `ESP_OK` on success + */ +esp_err_t veml7700_get_white_channel(i2c_dev_t *dev, veml7700_config_t *config, uint32_t *value_lux); + +/** + * @brief Read the interrupt status from the device + * + * @param dev Pointer to I2C device descriptor + * @param low_threshold Pointer to return the low threshold passed indicator + * @param high_threshold Pointer to return the high threshold passed indicator + * @return `ESP_OK` on success + */ +esp_err_t veml7700_get_interrupt_status(i2c_dev_t *dev, bool *low_threshold, bool *high_threshold); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* __VEML7700_H__ */ diff --git a/components/wiegand/.eil.yml b/components/wiegand/.eil.yml index 5f643a60..60b757bb 100644 --- a/components/wiegand/.eil.yml +++ b/components/wiegand/.eil.yml @@ -1,24 +1,21 @@ ---- -components: - - name: wiegand - description: Wiegand protocol receiver - group: misc - groups: [] - code_owners: UncleRus - depends: - # XXX conditional depends - - driver - - log - - esp_idf_lib_helpers - thread_safe: no - targets: - - name: esp32 - - name: esp8266 - - name: esp32s2 - - name: esp32c3 - licenses: - - name: BSD-3 - copyrights: - - author: - name: UncleRus - year: 2021 +name: wiegand +description: Wiegand protocol receiver +version: 1.1.0 +groups: + - misc +code_owners: UncleRus +depends: + # XXX conditional depends + - driver + - log + - esp_idf_lib_helpers +thread_safe: no +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2021 diff --git a/devtools/.rubocop.yml b/devtools/.rubocop.yml deleted file mode 100644 index cf698523..00000000 --- a/devtools/.rubocop.yml +++ /dev/null @@ -1,134 +0,0 @@ ---- -# inherit_from: .rubocop_todo.yml - -AllCops: - Exclude: - - "vendor/**/*" - # enable detailed explanations available in cops - # the default output is not enough to understand what is wrong - DisplayCopNames: true - ExtraDetails: true - DisplayStyleGuide: true - - # the default CacheRootDirectory is no longer `/tmp`, but a directory under - # `$HOME` and some Unix platforms use symlink to that path - AllowSymlinksInCacheRootDirectory: true - -Style/StringLiterals: - EnforcedStyle: double_quotes - -Style/SymbolArray: - # perefer brackets for `grep-ability` - EnforcedStyle: brackets - -Metrics/BlockLength: - Exclude: - - Guardfile - IgnoredMethods: - - describe - - context - - namespace - -Layout/LineLength: - Exclude: - # Gemfile is not application code - - "Gemfile" - # ignore heredoc for readability - AllowHeredoc: true - # URLs are almost always long - AllowURI: true - URISchemes: - - http - - https - - git - - ftp - IgnoreCopDirectives: true - -Gemspec/DateAssignment: # new in 1.10 - Enabled: true -Layout/LineEndStringConcatenationIndentation: # new in 1.18 - Enabled: true -Layout/SpaceBeforeBrackets: # new in 1.7 - Enabled: true -Lint/AmbiguousAssignment: # new in 1.7 - Enabled: true -Lint/AmbiguousOperatorPrecedence: # new in 1.21 - Enabled: true -Lint/AmbiguousRange: # new in 1.19 - Enabled: true -Lint/DeprecatedConstants: # new in 1.8 - Enabled: true -Lint/DuplicateBranch: # new in 1.3 - Enabled: true -Lint/DuplicateRegexpCharacterClassElement: # new in 1.1 - Enabled: true -Lint/EmptyBlock: # new in 1.1 - Enabled: true -Lint/EmptyClass: # new in 1.3 - Enabled: true -Lint/EmptyInPattern: # new in 1.16 - Enabled: true -Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21 - Enabled: true -Lint/LambdaWithoutLiteralBlock: # new in 1.8 - Enabled: true -Lint/NoReturnInBeginEndBlocks: # new in 1.2 - Enabled: true -Lint/NumberedParameterAssignment: # new in 1.9 - Enabled: true -Lint/OrAssignmentToConstant: # new in 1.9 - Enabled: true -Lint/RedundantDirGlobSort: # new in 1.8 - Enabled: true -Lint/RequireRelativeSelfPath: # new in 1.22 - Enabled: true -Lint/SymbolConversion: # new in 1.9 - Enabled: true -Lint/ToEnumArguments: # new in 1.1 - Enabled: true -Lint/TripleQuotes: # new in 1.9 - Enabled: true -Lint/UnexpectedBlockArity: # new in 1.5 - Enabled: true -Lint/UnmodifiedReduceAccumulator: # new in 1.1 - Enabled: true -Security/IoMethods: # new in 1.22 - Enabled: true -Style/ArgumentsForwarding: # new in 1.1 - Enabled: true -Style/CollectionCompact: # new in 1.2 - Enabled: true -Style/DocumentDynamicEvalDefinition: # new in 1.1 - Enabled: true -Style/EndlessMethod: # new in 1.8 - Enabled: true -Style/HashConversion: # new in 1.10 - Enabled: true -Style/HashExcept: # new in 1.7 - Enabled: true -Style/IfWithBooleanLiteralBranches: # new in 1.9 - Enabled: true -Style/InPatternThen: # new in 1.16 - Enabled: true -Style/MultilineInPatternThen: # new in 1.16 - Enabled: true -Style/NegatedIfElseCondition: # new in 1.2 - Enabled: true -Style/NilLambda: # new in 1.3 - Enabled: true -Style/NumberedParameters: # new in 1.22 - Enabled: true -Style/NumberedParametersLimit: # new in 1.22 - Enabled: true -Style/QuotedSymbols: # new in 1.16 - Enabled: true -Style/RedundantArgument: # new in 1.4 - Enabled: true -Style/RedundantSelfAssignmentBranch: # new in 1.19 - Enabled: true -Style/SelectByRegexp: # new in 1.22 - Enabled: true -Style/StringChars: # new in 1.12 - Enabled: true -Style/SwapValues: # new in 1.1 - Enabled: true diff --git a/devtools/README.md.erb b/devtools/README.md.erb deleted file mode 100644 index 185480ca..00000000 --- a/devtools/README.md.erb +++ /dev/null @@ -1,147 +0,0 @@ -# ESP-IDF Components library - -[![Build Status](https://github.com/UncleRus/esp-idf-lib/workflows/Build%20examples/badge.svg)](https://github.com/UncleRus/esp-idf-lib/actions?query=workflow%3A%22Build+examples%22) -[![Build the documentation](https://github.com/UncleRus/esp-idf-lib/workflows/Build%20the%20documentation/badge.svg)](https://github.com/UncleRus/esp-idf-lib/actions?query=workflow%3A%22Build+the+documentation%22) -[![Docs Status](https://readthedocs.org/projects/esp-idf-lib/badge/?version=latest&style=flat)](https://esp-idf-lib.readthedocs.io/en/latest/) - -Components for Espressif ESP32 [ESP-IDF framework](https://github.com/espressif/esp-idf) -and [ESP8266 RTOS SDK](https://github.com/espressif/ESP8266_RTOS_SDK). - -Part of them ported from [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos). - -## Supported versions of frameworks and devices - -| Chip | Framework | Versions -|----------------|--------------------|----------------------- -| ESP32 | ESP-IDF | All officially supported versions (see [Support Period Policy](https://github.com/espressif/esp-idf/blob/master/SUPPORT_POLICY.md)) and `master` -| ESP32-S2 *[1]* | ESP-IDF | All officially supported versions and `master` -| ESP32-C3 *[1]* | ESP-IDF | All officially supported versions and `master` -| ESP8266 *[2]* | ESP8266 RTOS SDK | `master`, v3.4 - -[1] *Use "`idf.py set-target esp32s2`" or "`idf.py set-target esp32c3`" before "`idf.py menuconfig`" to change -the chip type.* - -[2] *Due to the incompatibility of ESP8266 drivers and hardware, some -libraries are not* *supported on ESP8266 (see "ESP8266" column in the tables).* - -## How to use - -### ESP32 - -Clone this repository somewhere, e.g.: - -```Shell -cd ~/myprojects/esp -git clone https://github.com/UncleRus/esp-idf-lib.git -``` - -Add path to components in your [project makefile](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system-legacy.html), -e.g: - -```Makefile -PROJECT_NAME := my-esp-project -EXTRA_COMPONENT_DIRS := /home/user/myprojects/esp/esp-idf-lib/components -include $(IDF_PATH)/make/project.mk -``` - -or in [CMakeLists.txt](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html): - -```CMake -cmake_minimum_required(VERSION 3.5) -set(EXTRA_COMPONENT_DIRS /home/user/myprojects/esp/esp-idf-lib/components) -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(my-esp-project) -``` - -or with CMake [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) - -```CMake -cmake_minimum_required(VERSION 3.11) -include(FetchContent) -FetchContent_Declare( - espidflib - GIT_REPOSITORY https://github.com/UncleRus/esp-idf-lib.git -) -FetchContent_MakeAvailable(espidflib) -set(EXTRA_COMPONENT_DIRS ${espidflib_SOURCE_DIR}/components) -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(my-esp-project) -``` - -### ESP8266 RTOS SDK - -Clone this repository somewhere, e.g.: - -```Shell -cd ~/myprojects/esp -git clone https://github.com/UncleRus/esp-idf-lib.git -``` - -Add path to components in your [project makefile](https://docs.espressif.com/projects/esp8266-rtos-sdk/en/latest/api-guides/build-system.html), -e.g: - -```Makefile -PROJECT_NAME := my-esp-project -EXTRA_COMPONENT_DIRS := /home/user/myprojects/esp/esp-idf-lib/components -EXCLUDE_COMPONENTS := max7219 mcp23x17 led_strip max31865 ls7366r max31855 -include $(IDF_PATH)/make/project.mk -``` - -See [GitHub examples](https://github.com/UncleRus/esp-idf-lib/tree/master/examples) -or [GitLab examples](https://gitlab.com/UncleRus/esp-idf-lib/tree/master/examples). - -## Documentation - -- [Documentation](https://esp-idf-lib.readthedocs.io/en/latest/) -- [Frequently asked questions](FAQ.md) - -## Components -<% groups.sort_by!(&:description).each do |g| %> -### <%= g.description %> - -| Component | Description | License | Supported on | Thread safety -|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|-------------- -<% components = all_components.select { |c| c.group_of?(g.name) }.sort_by!(&:name).each do |c| - name = format("%-24s", "**#{c.name}**") - description = format("%-80s", c.description) - licenses = format("%-7s", c.licenses.map(&:name).join(", ")) - supported_on = format("%-18s", c.targets.map { |t| "`#{t.name}`" }.join(", ")) -%> -| <%= name %> | <%= description %> | <%= licenses %> | <%= supported_on %> | <%= c.thread_safe ? "Yes" : "No" %> -<% end -%> -<% end -%> - -## Library maintainers - -- [Ruslan V. Uss](https://github.com/UncleRus) -- [Tomoyuki Sakurai](https://github.com/trombik) - -## Credits - -- [Tomoyuki Sakurai](https://github.com/trombik), developer of the LM75 and - SK9822/APA102 drivers, author of the RTOS SDK ESP82666 support, master CI -- [Gunar Schorcht](https://github.com/gschorcht), developer of SHT3x, BME680 - and CCS811 drivers -- [Brian Schwind](https://github.com/bschwind), developer of TS2561 and - TSL4531 drivers -- [Andrej Krutak](https://github.com/andree182), developer of BH1750 driver -- Frank Bargstedt, developer of BMP180 driver -- [sheinz](https://github.com/sheinz), developer of BMP280 driver -- [Jonathan Hartsuiker](https://github.com/jsuiker), developer of DHT driver -- [Grzegorz Hetman](https://github.com/hetii), developer of DS18B20 driver -- [Alex Stewart](https://github.com/astewart-consensus), developer of DS18B20 driver -- [Richard A Burton](mailto:richardaburton@gmail.com), developer of DS3231 driver -- [Bhuvanchandra DV](https://github.com/bhuvanchandra), developer of DS3231 driver -- [Zaltora](https://github.com/Zaltora), developer of INA3231 driver -- [Bernhard Guillon](https://gitlab.com/mrnice), developer of MS5611-01BA03 driver -- [Pham Ngoc Thanh](https://github.com/panoti), developer of PCF8591 driver -- [Lucio Tarantino](https://github.com/dianlight), developer of ADS111x driver -- [Julian Dörner](https://github.com/juliandoerner), developer of TSL2591 driver -- [FastLED community](https://github.com/FastLED), developers of `lib8tion`, - `color` and `noise` libraries -- [Erriez](https://github.com/Erriez), developer of MH-Z19B driver -- [David Douard](https://github.com/douardda), developer of MH-Z19B driver -- [Nate Usher](https://github.com/nated0g), developer of SCD30 driver -- [Josh Kallus](https://github.com/Jkallus), developer of LS7366R driver -- [saasaa](https://github.com/saasaa), developer of HTS221 driver -- [Timofei Korostelev](https://github.com/chudsaviet), developer of HT16K33 driver -- [Jose Manuel Perez](https://github.com/jmpmscorp), developer of LC709203F driver diff --git a/devtools/Rakefile b/devtools/Rakefile deleted file mode 100644 index 2024a289..00000000 --- a/devtools/Rakefile +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -task default: [:test] - -desc "Run all tests" -task test: [:rubocop, :rspec] - -desc "Run rubocop" -task :rubocop do - sh "rubocop" -end - -desc "Run rspec" -task :rspec do - sh "rspec --format d" -end - -desc "Update README.md" -task :readme do - require "erb" - require_relative "spec/group_list" - require_relative "spec/component" - - template = File.read("README.md.erb") - groups = GroupList.new("groups.yml").all - - # * select if it is a directory - # * make path to metadata file - # * read it - # * parse it as YAML - # * take all components under "components" key - # * flatten the list of components - # * create a Component from the item - all_components = Dir.children("../components") - .select { |f| File.directory?(File.join("../components", f)) } - .map { |c| File.join("../components", c, ".eil.yml") } - .map { |f| File.read(f) } - .map { |f| YAML.safe_load(f) } - .map { |y| y["components"] } - .flatten - .map { |c| Component.new(c) } - markdown = ERB.new(template, trim_mode: "%-").result(binding) - puts markdown -end diff --git a/devtools/cmake-get-requires/.gitignore b/devtools/cmake-get-requires/.gitignore deleted file mode 100644 index cdc8bbb7..00000000 --- a/devtools/cmake-get-requires/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -CMakeCache.txt -CMakeFiles -cmake_install.cmake -Makefile diff --git a/devtools/cmake-get-requires/CMakeLists.txt b/devtools/cmake-get-requires/CMakeLists.txt deleted file mode 100644 index 70b9f116..00000000 --- a/devtools/cmake-get-requires/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# a script to parse CMakeLists.txt of a component, print REQUIRES. -# -# usage: -# cmake -DCOMPONENT_NAME:STRING=framebuffer . >/dev/null -# -# output in stderr: -# REQUIRES:log;color - -cmake_minimum_required(VERSION 3.5) - -# mock idf_component_register() -function(idf_component_register) - set(multiValueArgs REQUIRES) - cmake_parse_arguments(MY "" "" "${multiValueArgs}" ${ARGN}) - - # print REQUIRES argument to stderr. MY_REQUIRES is a semicolon separated - # string, such as `foo;bar` - message(NOTICE "REQUIRES:${MY_REQUIRES}") -endfunction() - -include(${CMAKE_CURRENT_SOURCE_DIR}/../../components/${COMPONENT_NAME}/CMakeLists.txt) - -project(ProjectName) diff --git a/devtools/devtool.py b/devtools/devtool.py new file mode 100755 index 00000000..d0fd1404 --- /dev/null +++ b/devtools/devtool.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 + +import argparse +import devtool +import pathlib as p +import typing as t +import jinja2 +import sys +import os +import subprocess +import pydantic + + +class CI(pydantic.BaseModel): + + meta: bool = False + build: bool = False + docs: bool = False + readme: bool = False + ci: bool = False + + +class Devtool: + + PROGRAM = 'devtool.py' + COMMANDS = { + 'check': 'Check components metadata', + 'render': 'Generate documentation based on components metadata', + 'target': 'Find components by target', + 'depends': 'Show components that depend (directly or indirectly) on a specified component', + 'ci': 'Show list of ci jobs to be done', + } + README = 'README.md' + + def __init__(self): + self.meta: devtool.Metadata | None = None + self.parse_args() + + def parse_args(self): + parser = argparse.ArgumentParser( + prog=self.PROGRAM, + description='esp-idf-lib developer tool', + usage='usage: devtool.py [-h] {%s} [args]' % ','.join(self.COMMANDS.keys()) + ) + parser.add_argument('command', choices=[cmd for cmd in self.COMMANDS], + help='Subcommand to run') + args = parser.parse_args(sys.argv[1:2]) + + repo_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + self.meta = devtool.Metadata(repo_path) + + method = 'cmd_%s' % args.command + if not hasattr(self, method) or not callable(getattr(self, method)): + raise RuntimeError('Invalid command: method not found') + getattr(self, method)() + + def iter_components(self) -> t.Generator[devtool.Component, None, None]: + for d in sorted((str(d.parts[-1]) for d in self.meta.iter_component_dirs() if d.is_dir())): + yield devtool.Component.load(self.meta, d) + + def cmd_check(self): + for c in self.iter_components(): + print('Component %s is OK.' % c.name) + + def cmd_render(self): + # TODO: render docs source + groups = {} + authors = {} + for c in self.iter_components(): + for group in c.groups: + if group.description not in groups: + groups[group.description] = [] + groups[group.description].append(c) + for cr in c.copyrights: + if cr.name.full_name not in authors: + authors[cr.name.full_name] = {'person': cr.name, 'components': []} + authors[cr.name.full_name]['components'].append(c) + + env = jinja2.Environment( + loader=jinja2.PackageLoader('devtool'), + finalize=lambda x: x if x is not None else '', + autoescape=False, + ) + + # render README + tpl = env.get_template(self.README) + readme_fn = p.Path(self.meta.repo_path) / self.README + with readme_fn.open('w') as f: + f.write(tpl.render(groups=groups, authors=authors)) + + @staticmethod + def _find_by_target(target: str, inverse: bool, components: t.Dict[str, devtool.Component]) -> t.List[str]: + res = [] + + for c in components.values(): + if not inverse: + if target in c.targets: + res.append(c.name) + else: + if target not in c.targets: + res.append(c.name) + + return res + + def cmd_target(self): + parser = argparse.ArgumentParser( + prog=self.PROGRAM, + description=self.COMMANDS['target'], + usage='devtool.py target [-h] [-x] {%s}' % ','.join(self.meta.targets) + ) + parser.add_argument('target', choices=self.meta.targets, + help='Target that components must support') + parser.add_argument('-x', action='store_true', + help='Inverse operation: find components that do not support the specified target') + args = parser.parse_args(sys.argv[2:]) + + print(' '.join(self._find_by_target(args.target, args.x, {c.name: c for c in self.iter_components()}))) + + @staticmethod + def _find_dependants(dependency: str, components: t.Dict[str, devtool.Component]) \ + -> t.Dict[str, devtool.Component]: + res = {} + + # direct dependants + for c in components.values(): + if dependency in c.depends and c.name not in res: + res[c.name] = c + + # indirect dependants + while True: + found = False + for d in tuple(res.values()): + for c in components.values(): + if d.name in c.depends and c.name not in res: + res[c.name] = c + found = True + if not found: + break + + return res + + def cmd_depends(self): + parser = argparse.ArgumentParser( + prog=self.PROGRAM, + description=self.COMMANDS['depends'], + usage='devtool.py depends [-h] ' + ) + parser.add_argument('dependency', help='Dependency name') + args = parser.parse_args(sys.argv[2:]) + + print('\n'.join(self._find_dependants(args.dependency, {c.name: c for c in self.iter_components()}).keys())) + + def cmd_ci(self): + diff = subprocess.run( + args=('git', 'diff', '--name-only', 'origin/master..HEAD'), + cwd=self.meta.repo_path, + capture_output=True, + encoding='utf-8', + ) + diff.check_returncode() + + diff = tuple(l.split('/') for l in diff.stdout.strip().replace('\r', '').split('\n')) + dirs = set((l[0] for l in diff)) + changed_components = set((l[1] for l in diff if len(l) > 1 and l[0] in ('components', 'examples'))) + all_components = {c.name: c for c in self.iter_components()} + components_to_build = set() + + res = CI() + if self.README in dirs: + res.readme = True + if changed_components: + res.meta = True + res.build = True + res.readme = True + if 'devtools' in dirs or '.github' in dirs: + res.ci = True + if 'docs' in dirs: + res.docs = True + if res.ci: + res.meta = True + res.build = True + res.readme = True + res.docs = True + components_to_build = set(all_components.keys()) + if res.build and not components_to_build: + for c_name in changed_components: + if c_name in all_components: + components_to_build.add(c_name) + components_to_build = components_to_build.union(self._find_dependants(c_name, all_components).keys()) + + print('_ci_meta=%d' % res.meta) + print('_ci_readme=%d' % res.readme) + print('_ci_docs=%d' % res.docs) + for target in self.meta.targets: + components_by_target = set(self._find_by_target(target, False, all_components)) + print('_ci_build_%s=%s' % (target, ' '.join(components_to_build.intersection(components_by_target)))) + + +if __name__ == '__main__': + Devtool() diff --git a/devtools/devtool/__init__.py b/devtools/devtool/__init__.py new file mode 100644 index 00000000..2ef17e3a --- /dev/null +++ b/devtools/devtool/__init__.py @@ -0,0 +1,2 @@ +from .metadata import * +from .errors import * diff --git a/devtools/devtool/const.py b/devtools/devtool/const.py new file mode 100644 index 00000000..f51afaa3 --- /dev/null +++ b/devtools/devtool/const.py @@ -0,0 +1,8 @@ +ENCODING: str = 'utf-8' + +METADATA_FILENAME: str = '.eil.yml' +GROUPS_FILENAME: str = 'groups.yml' +PERSONS_FILENAME: str = 'persons.yml' +TARGETS_FILENAME: str = 'targets.yml' +DICT_DIR: str = 'devtools' +COMPONENTS_DIR: str = 'components' diff --git a/devtools/devtool/errors.py b/devtools/devtool/errors.py new file mode 100644 index 00000000..f810cfbd --- /dev/null +++ b/devtools/devtool/errors.py @@ -0,0 +1,70 @@ +import os +import typing as t + +__all__ = [ + 'MetadataError', + 'MetadataNotFoundError', + 'StructureError', + 'InvalidFieldsError', + 'InvalidNameError', + 'InvalidItemError', + 'InvalidGroupError', + 'InvalidPersonError', + 'InvalidLicenseError', + 'InvalidTargetError', + 'InvalidThreadSafetyError', +] + + +class MetadataError(RuntimeError): + pass + + +class MetadataNotFoundError(MetadataError): + def __init__(self, path: os.PathLike): + super().__init__('Metadata not found: "%s"' % path) + + +class StructureError(MetadataError): + def __init__(self, ctx: str): + super().__init__('[%s]: Bad metadata structure' % ctx) + + +class InvalidFieldsError(MetadataError): + def __init__(self, ctx: str, expected: t.Set[str], got: t.Set[str]): + super().__init__('[%s]: Bad metadata. Expected fields: %r, got %r' % (ctx, expected, got)) + + +class InvalidNameError(MetadataError): + def __init__(self, dirname: str, metaname: str): + super().__init__('[%s]: Invalid component name: "%s", must be "%s"' % (dirname, metaname, dirname)) + + +class InvalidItemError(MetadataError): + def __init__(self, ctx: str, item_name: str, item_value: str): + super().__init__('[%s]: Invalid %s: "%s"' % (ctx, item_name, item_value)) + + +class InvalidGroupError(InvalidItemError): + def __init__(self, ctx: str, value: str): + super().__init__(ctx, 'group', value) + + +class InvalidPersonError(InvalidItemError): + def __init__(self, ctx: str, value: str): + super().__init__(ctx, 'person', value) + + +class InvalidLicenseError(InvalidItemError): + def __init__(self, ctx: str, value: str): + super().__init__(ctx, 'license', value) + + +class InvalidTargetError(InvalidItemError): + def __init__(self, ctx: str, value: str): + super().__init__(ctx, 'target', value) + + +class InvalidThreadSafetyError(InvalidItemError): + def __init__(self, ctx: str, value: str): + super().__init__(ctx, 'thread_safe', value) diff --git a/devtools/devtool/metadata.py b/devtools/devtool/metadata.py new file mode 100644 index 00000000..2cbb5632 --- /dev/null +++ b/devtools/devtool/metadata.py @@ -0,0 +1,205 @@ +from __future__ import annotations + +import os +import yaml +import pydantic +import typing as t +import pathlib as p +from enum import Enum + +from .errors import * +from . import const + + +__all__ = [ + 'ThreadSafety', + 'Licenses', + 'Group', + 'Person', + 'Copyright', + 'Component', + 'Metadata', +] + + +class ThreadSafety(str, Enum): + NO = 'no' + YES = 'yes' + NA = 'n/a' + + @staticmethod + def from_value(raw: bool | str) -> ThreadSafety: + if isinstance(raw, bool): + return ThreadSafety.YES if raw else ThreadSafety.NO + if raw == 'true': + return ThreadSafety.YES + if raw == 'false': + return ThreadSafety.NO + return ThreadSafety(raw) + + +class Licenses(str, Enum): + ISC = 'ISC' + MIT = 'MIT' + BSD3 = 'BSD-3-Clause' + + @staticmethod + def from_value(raw: str) -> Licenses: + if raw == 'BSD-3': + return Licenses.BSD3 + return Licenses(raw) + + +class Group(pydantic.BaseModel): + name: str + description: str + + +class Person(pydantic.BaseModel): + name: str + full_name: str | None = None + gh_id: str | None = None + email: str | None = None + url: str | None = None + + def __init__(self, **kwargs): + if not kwargs.get('full_name', None): + kwargs['full_name'] = kwargs['name'] + super().__init__(**kwargs) + + +class Copyright(pydantic.BaseModel): + name: Person + year: int + + +class Component(pydantic.BaseModel): + name: str + description: str + version: str + groups: t.List[Group] = [] + code_owners: t.List[Person] = [] + depends: t.List[str] = [] + thread_safe: ThreadSafety = ThreadSafety.NA + targets: t.List[str] = [] + license: Licenses = Licenses.ISC + copyrights: t.List[Copyright] = [] + + @staticmethod + def load(m: Metadata, dirname: os.PathLike) -> Component: + path: p.Path = m.repo_path / const.COMPONENTS_DIR / dirname + meta_fn: p.Path = path / const.METADATA_FILENAME + if not meta_fn.is_file: + raise MetadataNotFoundError(path) + + raw = yaml.safe_load(meta_fn.open('r', encoding=const.ENCODING)) + + if not isinstance(raw, dict): + raise StructureError(str(dirname)) + + if set(Component.__fields__.keys()) != set(raw.keys()): + raise InvalidFieldsError( + str(dirname), set(Component.__fields__.keys()) - set(raw.keys()), + set(raw.keys()) - set(Component.__fields__.keys())) + + ctx = str(dirname) + if ctx != raw['name'].strip(): + raise InvalidNameError(ctx, raw['name']) + + ts = str(raw['thread_safe']) + try: + ts = ThreadSafety.from_value(ts.lower()) + except: + raise InvalidThreadSafetyError(ctx, ts) + + lc = str(raw['license']) + try: + lc = Licenses.from_value(lc.upper()) + except: + raise InvalidLicenseError(ctx, lc) + + res = Component( + name=ctx, + description=raw['description'].strip().strip('.').replace('\n', ' '), + version=raw['version'].strip(), + groups=m.get_groups(ctx, raw), + code_owners=m.get_persons(ctx, raw['code_owners']), + depends=[lib for lib in raw['depends']], + thread_safe=ts, + targets=m.get_targets(ctx, raw['targets']), + license=lc, + copyrights=m.get_copyrights(ctx, raw['copyrights']) + ) + return res + + def save(self, m: Metadata, dirname: os.PathLike) -> None: + meta_fn: p.Path = m.repo_path / const.COMPONENTS_DIR / dirname / const.METADATA_FILENAME + yaml.safe_dump(self.dict(), meta_fn.open('w', encoding=const.ENCODING)) + + +class Metadata: + + GROUPS_FILENAME: str = 'groups.yml' + PERSONS_FILENAME: str = 'persons.yml' + TARGETS_FILENAME: str = 'targets.yml' + + DICT_DIR: str = 'devtools' + COMPONENTS_DIR: str = 'components' + + def __init__(self, repo_path: os.PathLike): + self.repo_path: p.Path = p.Path(repo_path) + + self.groups = {} + self.persons = {} + self.targets = [] + + self.idx_groups = [] + + self.load_dictionaries() + + def load_dictionaries(self) -> None: + path = self.repo_path / p.Path(self.DICT_DIR) + groups_fn = path / self.GROUPS_FILENAME + persons_fn = path / self.PERSONS_FILENAME + targets_fn = path / self.TARGETS_FILENAME + + self.groups = {r['name']: Group(**r) for r in yaml.safe_load(groups_fn.open('r', encoding=const.ENCODING))} + self.persons = {r['name']: Person(**r) for r in yaml.safe_load(persons_fn.open('r', encoding=const.ENCODING))} + self.targets = [r['name'] for r in yaml.safe_load(targets_fn.open('r', encoding=const.ENCODING))] + + self.idx_groups = sorted(self.groups.values(), key=lambda x: x.description) + + def _get_group(self, ctx: str, name: str) -> Group: + if name not in self.groups: + raise InvalidGroupError(ctx, name); + return self.groups[name] + + def _get_person(self, ctx: str, name: str) -> Person: + if name not in self.persons: + raise InvalidPersonError(ctx, name) + return self.persons[name] + + def _get_target(self, ctx: str, name: str) -> str: + if name not in self.targets: + raise InvalidTargetError(ctx, name) + return name + + def get_groups(self, ctx: str, meta: dict) -> t.List[Group]: + return [self._get_group(ctx, g) for g in meta['groups']] + + def get_persons(self, ctx: str, names: str | t.List[str]) -> t.List[Person]: + if isinstance(names, str): + names = [names] + return [self._get_person(ctx, name) for name in names] + + def get_targets(self, ctx: str, names: str | t.List[str]) -> t.List[str]: + if isinstance(names, str): + names = [names] + return [self._get_target(ctx, name) for name in names] + + def get_copyrights(self, ctx: str, meta: list) -> t.List[Copyright]: + return [Copyright(name=self._get_person(ctx, m['name']), year=m['year']) for m in meta] + + def iter_component_dirs(self) -> t.Generator[p.Path]: + components_dir = self.repo_path / const.COMPONENTS_DIR + return components_dir.iterdir() diff --git a/devtools/devtool/templates/README.md b/devtools/devtool/templates/README.md new file mode 100644 index 00000000..cfc80e00 --- /dev/null +++ b/devtools/devtool/templates/README.md @@ -0,0 +1,112 @@ +# ESP-IDF Components library + +[![Main CI process](https://github.com/UncleRus/esp-idf-lib/actions/workflows/ci.yml/badge.svg)](https://github.com/UncleRus/esp-idf-lib/actions/workflows/ci.yml) +[![Docs Status](https://readthedocs.org/projects/esp-idf-lib/badge/?version=latest&style=flat)](https://esp-idf-lib.readthedocs.io/en/latest/) + +Components for Espressif ESP32 [ESP-IDF framework](https://github.com/espressif/esp-idf) +and [ESP8266 RTOS SDK](https://github.com/espressif/ESP8266_RTOS_SDK). + +Part of them ported from [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos). + +## Supported versions of frameworks and devices + +| Chip | Framework | Versions | +|----------|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| ESP32-xx | ESP-IDF | All officially supported versions (see [Support Period Policy](https://github.com/espressif/esp-idf/blob/master/SUPPORT_POLICY.md)) and `master` | +| ESP8266 | ESP8266 RTOS SDK | `master`, v3.4 | + +*See "Supported on" column for each of the components.* + +## How to use + +### ESP32-xx + +Clone this repository somewhere, e.g.: + +```Shell +cd ~/myprojects/esp +git clone https://github.com/UncleRus/esp-idf-lib.git +``` + +Add path to components in your [CMakeLists.txt](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html): +e.g: + +```CMake +cmake_minimum_required(VERSION 3.5) +set(EXTRA_COMPONENT_DIRS /home/user/myprojects/esp/esp-idf-lib/components) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(my-esp-project) +``` + +or with CMake [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) + +```CMake +cmake_minimum_required(VERSION 3.11) +include(FetchContent) +FetchContent_Declare( + espidflib + GIT_REPOSITORY https://github.com/UncleRus/esp-idf-lib.git +) +FetchContent_MakeAvailable(espidflib) +set(EXTRA_COMPONENT_DIRS ${espidflib_SOURCE_DIR}/components) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(my-esp-project) +``` + +### ESP8266 RTOS SDK + +Clone this repository somewhere, e.g.: + +```Shell +cd ~/myprojects/esp +git clone https://github.com/UncleRus/esp-idf-lib.git +``` + +Add path to components in your [project makefile](https://docs.espressif.com/projects/esp8266-rtos-sdk/en/latest/api-guides/build-system.html), +e.g: + +```Makefile +PROJECT_NAME := my-esp-project +EXTRA_COMPONENT_DIRS := /home/user/myprojects/esp/esp-idf-lib/components +EXCLUDE_COMPONENTS := max7219 mcp23x17 led_strip max31865 ls7366r max31855 +include $(IDF_PATH)/make/project.mk +``` + +See [GitHub examples](https://github.com/UncleRus/esp-idf-lib/tree/master/examples) +or [GitLab examples](https://gitlab.com/UncleRus/esp-idf-lib/tree/master/examples). + +## Documentation + +- [Documentation](https://esp-idf-lib.readthedocs.io/en/latest/) +- [Frequently asked questions](FAQ.md) + +## Components +{% for g_name, components in groups|dictsort(False) %} +### {{ g_name }} + +| Component | Description | License | Supported on | Thread safety | +|--------------------------|----------------------------------------------------------------------------------|---------|--------------------|---------------| +{%- for c in components %} +| {{ '%-24s' % ('**%s**' % c.name) }} | {{ '%-80s' % c.description }} | {{ '%-7s' % c.license.value }} | {{ '%-18s' % c.targets|join(', ') }} | {{ '%-13s' % c.thread_safe.value }} | +{%- endfor %} + +{% endfor -%} + +## Library maintainers + +- [Ruslan V. Uss](https://github.com/UncleRus) +- [Tomoyuki Sakurai](https://github.com/trombik) + +## Credits +{% for full_name, copyrights in authors|dictsort() -%} + +{%- if copyrights.person.gh_id -%} + {% set person_name = '[%s](https://github.com/%s)' % (full_name, copyrights.person.gh_id) %} +{%- elif copyrights.person.url -%} + {% set person_name = '[%s](%s)' % (full_name, copyrights.person.url) %} +{%- else -%} + {% set person_name = full_name %} +{%- endif %} +- {{ person_name }}: {% for c in copyrights.components %}`{{ c.name }}` {% endfor %} + +{%- endfor %} diff --git a/devtools/groups.yml b/devtools/groups.yml index 75823564..41de61a0 100644 --- a/devtools/groups.yml +++ b/devtools/groups.yml @@ -1,5 +1,3 @@ ---- - - name: adc-dac description: ADC/DAC libraries @@ -44,3 +42,9 @@ - name: misc description: Other misc libraries + +- name: imu + description: Inertial measurement units + +- name: battery + description: Battery controllers diff --git a/devtools/persons.yml b/devtools/persons.yml index 02958399..ff3c7773 100644 --- a/devtools/persons.yml +++ b/devtools/persons.yml @@ -1,4 +1,3 @@ ---- - name: UncleRus email: unclerus@gmail.com full_name: Ruslan V. Uss @@ -15,6 +14,7 @@ - name: Andrej full_name: Andrej Krutak + gh_id: andree182 email: dev@andree.sk - name: gschorcht @@ -35,15 +35,19 @@ gh_id: jsuiker - name: PavelM + full_name: Pavel Merzlyakov email: merzlyakovpavel@gmail.com - name: AlexS + full_name: Alex Stewart + gh_id: astewart-consensus email: foogod@gmail.com - name: GrzegorzH email: ghetman@gmail.com - name: BhuvanchandraD + gh_id: bhuvanchandra email: bhuvanchandra.dv@gmail.com - name: RichardA @@ -54,16 +58,19 @@ - name: BernhardG email: Bernhard.Guillon@begu.org + url: https://gitlab.com/mrnice - name: zeroday # GH account does not exist email: zeroday@nodemcu.com - name: PhamNgocT - # GH account exists, but not sure that is the correct one + full_name: Thanh Pham + gh_id: panoti email: pnt239@gmail.com - name: Sensirion + full_name: Sensirion AG gh_id: Sensirion - name: nated0g @@ -83,6 +90,7 @@ - name: DavidD full_name: David Douard + gh_id: douardda email: david.douard@sdfa3.org - name: Jkallus @@ -102,3 +110,57 @@ - name: jmpmscorp full_name: Jose Manuel Perez gh_id: jmpmscorp + +- name: weslleymfd + full_name: Weslley Duarte + gh_id: weslleymfd + +- name: janveeh + full_name: Jan Veeh + gh_id: janveeh + +- name: Th3Link + full_name: Marc Luehr + gh_id: th3link + email: marcluehr@gmail.com + +- name: shuki25 + full_name: Joshua Butler + gh_id: shuki25 + email: josh.butler929@gmail.com + +- name: horsemann07 + full_name: Raghav Jha + gh_id: horsemann07 + +- name: jeff_rowberg + full_name: Jeff Rowberg + url: https://www.i2cdevlib.com/ + +- name: a_e_dalzotto + full_name: Angelo Elias Dalzotto + email: 150633@upf.br + +- name: g_b_vicari + full_name: Gabriel Boni Vicari + email: 133192@upf.br + +- name: gepid + full_name: Grupo de Pesquisa em Cultura Digital + url: http://gepid.upf.br/ + +- name: slimcdk + full_name: Christian Skjerning + gh_id: slimcdk + +- name: qb4-dev + full_name: Jakub Turek + gh_id: QB4-dev + +- name: mmarkwort + full_name: Manuel Markwort + gh_id: mmarkwort + +- name: vonguced + full_name: Cedric von Gunten + gh_id: vonguced diff --git a/devtools/requirements.txt b/devtools/requirements.txt new file mode 100644 index 00000000..ffa470fd --- /dev/null +++ b/devtools/requirements.txt @@ -0,0 +1,3 @@ +Jinja2==3.1.3 +pydantic==2.1.1 +PyYAML==6.0 diff --git a/devtools/spec/component.rb b/devtools/spec/component.rb deleted file mode 100644 index b3a22b2a..00000000 --- a/devtools/spec/component.rb +++ /dev/null @@ -1,117 +0,0 @@ -# frozen_string_literal: true - -require_relative "copyright" -require_relative "target" -require_relative "person" -require_relative "license" -require_relative "group" - -class Component - # Component represents a component in metadata. it is not a `component` in - # esp-idf. a metadata may contain multiple Components. usually, an esp-idf - # component has one `component` and its metadata usually contains one - # Component. but a project, such as `esp-idf-lib` repository, may have - # multiple `components`. - - # a list of valid keys. some values of keys are simply String or Integer - # object. others are objects, or resources, defined in the specification. - VALID_KEYS = %w[ - name - description - group - groups - code_owners - depends - thread_safe - targets - licenses - copyrights - ].freeze - - def initialize(hash) - validate_keys(hash) - @metadata = hash - @name = name - end - - attr_reader :metadata - - def validate_keys(hash) - # validate basic constraints only. the classes are for tests in spec - # files, providing readable tests and results. the actual specification is - # in the spec files, not in classes. rspec is more readable and maintainable - # than ruby code. - # - # maybe, if these classes are found to be useful, create a gem of the - # classes and specification tests in the gem. until then, keep the classes - # simple so that others can maintain the specification. - raise ArgumentError, "missing name" unless hash.key?("name") - raise ArgumentError, "empty name" if hash["name"].empty? - - hash.each_key do |k| - raise ArgumentError, "unknown key: `#{k}`" unless VALID_KEYS.include?(k) - end - end - - def to_s - metadata["name"] - end - - # special keys that return instances of classes. - def group - Group.new(metadata["group"]) - end - - def groups - metadata["groups"].map { |g| Group.new(g) } - end - - def code_owners - metadata["code_owners"].map { |p| Person.new(p) } - end - - def targets - metadata["targets"].map { |t| Target.new(t) } - end - - def licenses - metadata["licenses"].map { |l| License.new(l) } - end - - def copyrights - metadata["copyrights"].map { |c| Copyright.new(c) } - end - - def valid_key_with_question?(name) - name.to_s.end_with?("?") && VALID_KEYS.include?(name.to_s.chop) - end - - def valid_key?(name) - VALID_KEYS.include?(name.to_s) - end - - def description - # if description contains newline, remove it - metadata["description"].split("\n").join(" ") - end - - def method_missing(name, *args, &block) - # name?, description?, etc - return metadata.key?(name.to_s.chop) if valid_key_with_question?(name) - # name, etc - return metadata[name.to_s] if valid_key?(name) - - super - end - - def respond_to_missing?(name, include_private = false) - # when name is not something we don't know, do `super`, i.e. raising - # unknown methods error. - super unless valid_key_with_question?(name) || valid_key?(name) - end - - def group_of?(arg) - group_name = arg.respond_to?(:name) ? arg.name : arg - group.name == group_name || groups.map(&:name).include?(group_name) - end -end diff --git a/devtools/spec/component_spec.rb b/devtools/spec/component_spec.rb deleted file mode 100644 index 5813f7bf..00000000 --- a/devtools/spec/component_spec.rb +++ /dev/null @@ -1,169 +0,0 @@ -# frozen_string_literal: true - -require_relative "spec_helper" - -VALID_THREAD_SAFE_VALUES = [true, false, "N/A"].freeze - -# rubocop:disable Metrics/BlockLength -metadata_array.each do |m| - RSpec.describe "metadata #{m}" do - it "has components" do - expect(m.components?).to be true - end - - it "has one or more of components" do - expect(m.components.length).to be >= 1 - end - - m.components.each do |c| - describe "component #{c}" do - subject { c } - it "does not raise error" do - expect { subject }.not_to raise_error - end - - describe "name" do - it "has name" do - expect(subject.name?).to be true - end - - it "has String name" do - expect(subject.name).to be_kind_of(String) - end - - it "has non-empty name" do - expect(subject.name).not_to be_empty - end - end - - describe "description" do - it "has description" do - expect(subject.description?).to be true - end - - it "has String description" do - expect(subject.description).to be_kind_of(String) - end - end - - describe "group" do - it "has a primary group" do - expect(subject.group?).to be true - end - - it "has a valid primary group" do - expect { subject.group }.not_to raise_error - end - end - - describe "groups" do - context "when it has one or more groups" do - it "has zero or more of groups" do - skip "it has no groups" unless subject.groups? - expect(subject.groups.length).to be >= 0 - end - - it "has valid groups" do - skip "it has no groups" unless subject.groups? - skip "it has zero group" if subject.groups? && subject.groups.empty? - expect { subject.groups }.not_to raise_error - end - end - end - - describe "depends" do - # XXX `depends` needs better tests because `depends` has zero or - # more of components, and some components are one in our components, - # others are one in esp-idf. we need to know one in `depends` - # actually exists. other resources, such as `People` resolves the - # issue by having a list of `People`. - context "when it has depends" do - it "has zero or more of depends" do - skip "it has no depends" unless subject.depends? - expect(subject.depends.length).to be >= 0 - end - - it "has valid depends" do - skip "it has no depends" unless subject.depends? - skip "it has zero depends" if subject.depends? && subject.depends.empty? - expect { subject.depends }.not_to raise_error - end - end - end - - describe "thread_safe" do - it "has thread_safe" do - expect(subject.thread_safe?).to be true - end - - it "has valid values of thread_safe" do - expect(VALID_THREAD_SAFE_VALUES).to include subject.thread_safe - end - end - - describe "targets" do - it "has targets" do - expect(subject.targets?).to be true - end - - it "has valid targets" do - expect { subject.targets }.not_to raise_error - end - end - - describe "licenses" do - it "has licenses" do - expect(subject.licenses?).to be true - end - - it "has valid licenses" do - expect { subject.licenses }.not_to raise_error - end - - it "has one or more of licenses" do - expect(subject.licenses.length).to be >= 1 - end - end - - describe "copyrights" do - it "has copyrights" do - expect(subject.copyrights?).to be true - end - - it "has valid copyrights" do - expect { subject.copyrights }.not_to raise_error - end - - it "has one or more of copyrights" do - expect(subject.copyrights.length).to be >= 1 - end - end - - describe "each copyright" do - it "has only one of name or author in copyrights" do - subject.copyrights.each do |copyright| - expect(copyright.name? && copyright.author?).to be false - end - end - - context "when a copyright has author" do - it "has valid Person as copyright author" do - subject.copyrights.select(&:author?).each do |copyright| - expect(copyright.author).to be_a Person - end - end - end - - context "when a copyright has name" do - it "has valid Person as copyright author" do - subject.copyrights.select(&:name?).each do |copyright| - expect(copyright.name).to be_a Person - end - end - end - end - end - end - end -end -# rubocop:enable Metrics/BlockLength diff --git a/devtools/spec/copyright.rb b/devtools/spec/copyright.rb deleted file mode 100644 index 4211bc97..00000000 --- a/devtools/spec/copyright.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -require_relative "person" - -class Copyright - VALID_KEYS = %w[ - author - name - year - ].freeze - - def initialize(hash) - raise ArgumentError, "expect Hash, got `#{hash.class}`" unless hash.is_a?(Hash) - - validate_keys(hash) - @metadata = hash - end - - attr_reader :metadata - - def validate_keys(hash) - hash.each_key do |k| - raise ArgumentError, "unknown key: `#{k}`. valid keys are: #{VALID_KEYS.join(' ')}" unless VALID_KEYS.include? k - end - end - - def author? - metadata.key?("author") - end - - def author - Person.new(metadata["author"]) - end - - def name - Person.new("name" => metadata["name"]) - end - - def name? - metadata.key?("name") - end - - def year - metadata["year"] - end - - def year? - metadata.key?("year") - end -end diff --git a/devtools/spec/group.rb b/devtools/spec/group.rb deleted file mode 100644 index 14bde483..00000000 --- a/devtools/spec/group.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -class Group - VALID_KEYS = %w[name description].freeze - - def initialize(arg) - validate_arg(arg) - validate_keys(arg) if arg.is_a? Hash - - @metadata = if arg.is_a? String - { "name" => arg } - else - arg - end - end - - def validate_keys(arg) - arg.each_key do |k| - raise ArgumentError, "unknown key: `#{k}`. valid keys are: #{VALID_KEYS.join(' ')}" unless VALID_KEYS.include? k - end - raise ArgumentError, "a key, `name` is required, but missing" unless arg.key?("name") - end - - def validate_arg(arg) - raise ArgumentError, "argument must be String or Hash" unless arg.is_a?(String) || arg.is_a?(Hash) - end - - def name? - @metadata.key?("name") - end - - def name - @metadata["name"] - end - - def description? - @metadata.key?("description") - end - - def description - @metadata["description"] - end - - def to_s - @metadata["name"] - end -end diff --git a/devtools/spec/group_list.rb b/devtools/spec/group_list.rb deleted file mode 100644 index faa16f0e..00000000 --- a/devtools/spec/group_list.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -require "yaml" -require_relative "group" - -class GroupList - # path to `groups.yml` - def initialize(arg) - @path = File.expand_path(arg) - end - - attr_reader :path - - def load_file - File.read(path) - end - - def parse - YAML.safe_load(load_file) - end - - def metadata - return @metadata if @metadata - - @metadata = parse - end - - def all - metadata.map { |g| Group.new(g) } - end - - def lookup(name) - metadata.select { |g| g["name"] == name }.map { |g| Group.new(g) } - end -end diff --git a/devtools/spec/groups_spec.rb b/devtools/spec/groups_spec.rb deleted file mode 100644 index 1101e6a8..00000000 --- a/devtools/spec/groups_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -require_relative "spec_helper" -require_relative "group_list" - -file = File.join(File.dirname(__FILE__), "..", "groups.yml") - -RSpec.describe GroupList do - subject { GroupList.new(file) } - - it "creates new instance" do - expect { subject }.not_to raise_error - end - - describe "#load_file" do - it "does not raise" do - expect { subject.load_file }.not_to raise_error - end - end - - describe "#parse" do - it "does not raise" do - expect { subject.parse }.not_to raise_error - end - end - - describe "#metadata" do - it "returns Array" do - expect(subject.metadata).to be_a Array - end - end -end - -RSpec.describe "Group list metadata #{file}" do - groups = GroupList.new(file) - - groups.all.each do |group| - describe "Group #{group}" do - subject { group } - - it "is a Group" do - expect(group).to be_a Group - end - - it "has name as a key" do - expect(group.name?).to be true - end - - it "has non-empty name" do - expect(group.name).not_to be_empty - end - - it "has description as a key" do - expect(group.description?).to be true - end - - it "has non-empty description" do - expect(group.description).not_to be_empty - end - - it "is a unique group" do - expect(groups.lookup(group.name).length).to be 1 - end - end - end -end diff --git a/devtools/spec/license.rb b/devtools/spec/license.rb deleted file mode 100644 index 6b1e104a..00000000 --- a/devtools/spec/license.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -class License - def initialize(hash) - raise ArgumentError, "missing key `name`" unless hash.key?("name") - - @metadata = hash - end - - def name - @metadata["name"] - end - - def to_s - name - end -end diff --git a/devtools/spec/metadata.rb b/devtools/spec/metadata.rb deleted file mode 100644 index 2dc6bfd1..00000000 --- a/devtools/spec/metadata.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -require_relative "component" - -# A class that represents metadaata, `.eil.yml` -class Metadata - # path: path to component root directory - def initialize(path) - raise ArgumentError, "path is missing" unless path - - @path = path - @name = File.basename(path) - raise ArgumentError, "path `#{path}` does not have basename" if @name.empty? - - metadata - end - - attr_reader :path, :name - - def metadata - return @metadata if @metadata - - file = File.join(path, ".eil.yml") - @metadata = YAML.safe_load(File.read(file)) - rescue StandardError => e - warn "failed to open `#{file}`. does component `#{File.basename(path)}` have `.eil.yml` file?" - raise e - end - - def components? - metadata.key?("components") - end - - def components - metadata["components"].map { |c| Component.new(c) } - end - - def to_s - name - end -end diff --git a/devtools/spec/person.rb b/devtools/spec/person.rb deleted file mode 100644 index 8fc7fd49..00000000 --- a/devtools/spec/person.rb +++ /dev/null @@ -1,100 +0,0 @@ -# frozen_string_literal: true - -require "yaml" - -# A class that repesents Peson -class Person - PERSON_FILE = File.expand_path(File.join(File.dirname("__FILE__"), "persons.yml")).freeze - - def initialize(arg) - validate_arg(arg) - validate_keys(arg) if arg.is_a? Hash - - name = if arg.is_a? String - arg - else - arg["name"] - end - @metadata = lookup_person(name) - end - - attr_reader :metadata, :persons - - def validate_keys(hash) - raise ArgumentError, "missing key: `name`" unless hash.key?("name") - end - - def valid_arg_class?(arg) - arg.is_a?(String) || arg.is_a?(Hash) - end - - def validate_arg(arg) - raise ArgumentError, "String or dict is ecpected, but got `#{arg.class}`" unless valid_arg_class?(arg) - end - - def load_person_file - File.read(PERSON_FILE) - end - - def parse(string) - return @persons if @persons - - @persons = YAML.safe_load(string) - rescue StandardError => e - warn "failed to parse #{PERSON_FILE} as YAML" - raise e - end - - def lookup_person(name) - parse(load_person_file) - person = persons.select { |p| p["name"] == name } - raise ArgumentError, "cannot find Person with name `#{name}` in #{PERSON_FILE}" unless person - raise ArgumentError, "Person with name `#{name}` has duplicated entry in #{PERSON_FILE}" if person.length > 1 - - person.first - end - - def name? - metadata.key?("name") - end - - def name - metadata["name"] - end - - def full_name? - metadata.key?("full_name") - end - - def full_name - metadata["full_name"] - end - - def gh_id? - metadata.key?("gh_id") - end - - def gh_id - metadata["gh_id"] - end - - def email? - metadata.key?("email") - end - - def email - metadata["email"] - end - - def website? - metadata.key?("website") - end - - def website - metadata["website"] - end - - def to_s - metadata["name"] - end -end diff --git a/devtools/spec/person_list.rb b/devtools/spec/person_list.rb deleted file mode 100644 index 3c9acb3f..00000000 --- a/devtools/spec/person_list.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -require "yaml" - -class PersonList - # path to `persons.yml` - def initialize(arg) - @path = File.expand_path(arg) - end - - attr_reader :path - - def load_file - File.read(path) - end - - def parse - YAML.safe_load(load_file) - end - - def metadata - return @metadata if @metadata - - @metadata = parse - end - - def all - metadata - end - - def lookup(name) - metadata.select { |g| g["name"] == name } - end -end diff --git a/devtools/spec/persons_spec.rb b/devtools/spec/persons_spec.rb deleted file mode 100644 index f287531d..00000000 --- a/devtools/spec/persons_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -require_relative "spec_helper" -require_relative "person_list" -require_relative "person" - -file = File.join(File.dirname(__FILE__), "..", "persons.yml") - -RSpec.describe "Person list metadata #{file}" do - persons = PersonList.new(file) - - persons.all.each do |person| - describe "Person #{person}" do - subject { Person.new(person) } - - it "has a name" do - expect(subject.name?).to be true - end - - it "has non-empty name" do - expect(subject.name).not_to be_empty - end - - it "has one or more of contact information" do - has_contact = subject.email? || subject.gh_id? || subject.full_name? || subject.website? - expect(has_contact).to be true - end - - it "is unique in persons.yml" do - expect(persons.lookup(subject.name).length).to be 1 - end - end - end -end diff --git a/devtools/spec/spec_helper.rb b/devtools/spec/spec_helper.rb deleted file mode 100644 index 4e894c35..00000000 --- a/devtools/spec/spec_helper.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -require "rspec" -require "yaml" -require_relative "metadata" - -@component_dir = File.expand_path(File.join(File.dirname(__FILE__), "../../components")) - -def metadata_array - directories = Dir.children(Dir.new(@component_dir)).map { |c| File.join(@component_dir, c) } - directories = directories.select { |d| File.directory?(d) } - directories.map { |path| Metadata.new(path) } -end diff --git a/devtools/spec/target.rb b/devtools/spec/target.rb deleted file mode 100644 index 353e6984..00000000 --- a/devtools/spec/target.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true - -require "yaml" - -# A class that represents build target - -class Target - VALID_KEYS = %w[name].freeze - TARGETS_FILE = File.expand_path(File.join(File.dirname("__FILE__"), "..", "targets.yml")).freeze - - def initialize(arg) - validate_arg(arg) - validate_keys(arg) if arg.is_a?(Hash) - @metadata = if arg.is_a?(String) - { "name" => arg } - else - arg - end - end - - attr_reader :metadata - - def valid_arg_class?(arg) - arg.is_a?(String) || arg.is_a?(Hash) - end - - def validate_arg(arg) - raise ArgumentError, "String or dict is ecpected, but got `#{arg.class}`" unless valid_arg_class?(arg) - end - - def validate_keys(hash) - hash.each_key do |k| - raise ArgumentError, "unknown key: `#{k}`. valid keys are: #{VALID_KEYS.join(' ')}" unless VALID_KEYS.include?(k) - end - end - - def name? - metadata.key?("name") - end - - def name - metadata["name"] - end - - def load_file - File.read(TARGETS_FILE) - end - - def parse(string) - YAML.safe_load(string) - rescue StandardError => e - warn "failed to parse #{TARGETS_FILE} as YAML" - raise e - end - - def targets - return @targets if @targets - - @targets = parse(load_file) - end - - def lookup(name) - targets.select { |t| t.name == name } - end -end diff --git a/devtools/spec/target_list.rb b/devtools/spec/target_list.rb deleted file mode 100644 index 987f2bf2..00000000 --- a/devtools/spec/target_list.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -require "person_list" - -class TargetList < PersonList -end diff --git a/devtools/spec/targets_spec.rb b/devtools/spec/targets_spec.rb deleted file mode 100644 index 05147729..00000000 --- a/devtools/spec/targets_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -require_relative "spec_helper" -require_relative "target_list" - -file = File.join(File.dirname(__FILE__), "..", "targets.yml") - -RSpec.describe "Target list metadata #{file}" do - targets = TargetList.new(file) - - targets.all.each do |target| - describe "Target #{target}" do - subject { Target.new(target) } - - it "has a name" do - expect(subject.name?).to be true - end - - it "has non-empty name" do - expect(subject.name).not_to be_empty - end - - it "is a unique target in the list" do - expect(targets.lookup(subject.name).length).to be 1 - end - end - end -end diff --git a/devtools/targets.yml b/devtools/targets.yml index 3555112e..47ba52f0 100644 --- a/devtools/targets.yml +++ b/devtools/targets.yml @@ -1,5 +1,11 @@ --- - name: esp32 - +- name: esp32c2 +- name: esp32c3 +- name: esp32c6 +- name: esp32h2 +- name: esp32h4 +- name: esp32s2 +- name: esp32s3 - name: esp8266 diff --git a/docs/.gitignore b/docs/.gitignore index 62e57df1..cd3088f2 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -2,3 +2,4 @@ /latex/ /build/ /xml/ +doxygen.log diff --git a/docs/Makefile b/docs/Makefile index f3f6f918..cf8d6bd7 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -22,7 +22,10 @@ help: @echo " man to make manual pages" @echo " all to make all of the above" -xml/index.xml: +$(BUILDDIR): + mkdir $(BUILDDIR) + +xml/index.xml: $(BUILDDIR) doxygen doxygen.conf sed -i 's/LIB8STATIC_ALWAYS_INLINE/inline static/g' xml/*.xml sed -i 's/LIB8STATIC/inline static/g' xml/*.xml diff --git a/docs/doxygen.conf b/docs/doxygen.conf index 1161bead..b0cf1553 100644 --- a/docs/doxygen.conf +++ b/docs/doxygen.conf @@ -1,4 +1,4 @@ -# Doxyfile 1.8.20 +# Doxyfile 1.9.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,6 +12,15 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -60,16 +69,28 @@ PROJECT_LOGO = OUTPUT_DIRECTORY = -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# numer of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -81,26 +102,18 @@ ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -258,16 +271,16 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = @@ -312,8 +325,8 @@ OPTIMIZE_OUTPUT_SLICE = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files @@ -323,7 +336,10 @@ OPTIMIZE_OUTPUT_SLICE = NO # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = @@ -457,13 +473,13 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, -# which efficively disables parallel processing. Please report any issues you +# which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. @@ -533,6 +549,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -570,11 +593,18 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) and Mac users are advised to set this option to NO. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. # The default value is: system dependent. CASE_SENSE_NAMES = YES @@ -593,6 +623,12 @@ HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -750,7 +786,8 @@ FILE_VERSION_FILTER = # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -796,24 +833,35 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO @@ -824,15 +872,29 @@ WARN_AS_ERROR = NO # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). -WARN_LOGFILE = +WARN_LOGFILE = doxygen.log #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -849,8 +911,8 @@ INPUT = ../components # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 @@ -863,12 +925,14 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen -# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, # *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.h \ @@ -912,7 +976,7 @@ EXCLUDE_PATTERNS = # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test +# ANamespace::AClass, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* @@ -1088,16 +1152,24 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories @@ -1111,7 +1183,7 @@ CLANG_OPTIONS = # file is the compilation database (see: # http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the # options used when the source files were built. This is equivalent to -# specifying the "-p" option to a clang tool, such as clang-check. These options +# specifying the -p option to a clang tool, such as clang-check. These options # will then be passed to the parser. Any options specified with CLANG_OPTIONS # will be added as well. # Note: The availability of this option depends on whether or not doxygen was @@ -1130,13 +1202,6 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored @@ -1236,7 +1301,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. @@ -1246,7 +1311,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1307,10 +1372,11 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. @@ -1327,6 +1393,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1352,8 +1425,12 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1428,7 +1505,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1436,8 +1514,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1445,16 +1523,16 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = @@ -1466,9 +1544,9 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1511,16 +1589,28 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1545,6 +1635,13 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for @@ -1593,11 +1690,29 @@ FORMULA_MACROFILE = USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1610,22 +1725,29 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1672,7 +1794,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1685,8 +1808,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1795,29 +1919,31 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = @@ -1862,8 +1988,7 @@ USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode # command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# if errors occur, instead of asking the user for help. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1876,16 +2001,6 @@ LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. @@ -1966,16 +2081,6 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_SOURCE_CODE = NO - #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -2072,15 +2177,6 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- @@ -2167,7 +2263,8 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2259,15 +2356,6 @@ EXTERNAL_PAGES = YES # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2324,11 +2412,14 @@ DOT_FONTSIZE = 10 DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a +# graph for each documented class showing the direct and indirect inheritance +# relations. In case HAVE_DOT is set as well dot will be used to draw the graph, +# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set +# to TEXT the direct and indirect inheritance relations will be shown as texts / +# links. +# Possible values are: NO, YES, TEXT and GRAPH. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES @@ -2342,7 +2433,8 @@ CLASS_GRAPH = YES COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. See also the chapter Grouping +# in the manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2365,10 +2457,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2435,6 +2549,13 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: @@ -2442,10 +2563,9 @@ DIRECTORY_GRAPH = YES # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, -# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, -# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, -# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# Possible values are: png, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, +# gif, gif:cairo, gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, +# png:cairo, png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and # png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2490,10 +2610,10 @@ MSCFILE_DIRS = DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = @@ -2517,7 +2637,7 @@ PLANTUML_INCLUDE_PATH = # Minimum value: 0, maximum value: 10000, default value: 50. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_GRAPH_MAX_NODES = 50 +DOT_GRAPH_MAX_NODES = 1000 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs # generated by dot. A depth value of 3 means that only nodes reachable from the @@ -2555,14 +2675,18 @@ DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES diff --git a/docs/source/chips.rst b/docs/source/chips.rst index 6213d2e5..4248535e 100644 --- a/docs/source/chips.rst +++ b/docs/source/chips.rst @@ -23,6 +23,10 @@ Supported devices +---------------+----------------------------------------+----------------------+ | DS18S20 | Maxim | :ref:`ds18x20` | +---------------+----------------------------------------+----------------------+ +| DS1822 | Maxim | :ref:`ds18x20` | ++---------------+----------------------------------------+----------------------+ +| MAX31850 | Maxim | :ref:`ds18x20` | ++---------------+----------------------------------------+----------------------+ | ADS1013 | TI | :ref:`ads111x` | +---------------+----------------------------------------+----------------------+ | ADS1014 | TI | :ref:`ads111x` | @@ -55,6 +59,10 @@ Supported devices +---------------+----------------------------------------+----------------------+ | AM2302 | AOSONG | :ref:`dht` | +---------------+----------------------------------------+----------------------+ +| AM2320 | AOSONG | :ref:`dht` | ++---------------+----------------------------------------+----------------------+ +| AM2320 | AOSONG | :ref:`am2320` | ++---------------+----------------------------------------+----------------------+ | AM2321 | AOSONG | :ref:`dht` | +---------------+----------------------------------------+----------------------+ | HMC5883L | Farnell | :ref:`hmc5883l` | @@ -101,12 +109,16 @@ Supported devices +---------------+----------------------------------------+----------------------+ | MCP9808 | Microchip | :ref:`mcp9808` | +---------------+----------------------------------------+----------------------+ +| MP2660 | Monolithic Systems | :ref:`mp2660` | ++---------------+----------------------------------------+----------------------+ | MS5611-01BA03 | Measurement Specialties | :ref:`ms5611` | +---------------+----------------------------------------+----------------------+ | PCA9685 | NXP | :ref:`pca9685` | +---------------+----------------------------------------+----------------------+ | PCF8563 | NXP | :ref:`pcf8563` | +---------------+----------------------------------------+----------------------+ +| BM8563 | GATEMODE | :ref:`pcf8563` | ++---------------+----------------------------------------+----------------------+ | PCF8574 | NXP | :ref:`pcf8574` | +---------------+----------------------------------------+----------------------+ | PCF8575 | NXP | :ref:`pcf8575` | @@ -207,3 +219,47 @@ Supported devices +---------------+----------------------------------------+----------------------+ | LC709203F | ON Semiconductor | :ref:`lc709203f` | +---------------+----------------------------------------+----------------------+ +| ADS130E08 | TI | :ref:`ads130e08` | ++---------------+----------------------------------------+----------------------+ +| DPS310 | Infineon Technologies | :ref:`dps310` | ++---------------+----------------------------------------+----------------------+ +| ICM-42670-P | TDK | :ref:`icm42670` | ++---------------+----------------------------------------+----------------------+ +| VEML7700 | Vishay Intertechnology | :ref:`veml7700` | ++---------------+----------------------------------------+----------------------+ +| MPU-6000 | TDK/InvenSense | :ref:`mpu6050` | ++---------------+----------------------------------------+----------------------+ +| MPU-6050 | TDK/InvenSense | :ref:`mpu6050` | ++---------------+----------------------------------------+----------------------+ +| SGM58031 | SG Micro | :ref:`sgm58031` | ++---------------+----------------------------------------+----------------------+ +| STS30 | Sensirion | :ref:`sts3x` | ++---------------+----------------------------------------+----------------------+ +| STS31 | Sensirion | :ref:`sts3x` | ++---------------+----------------------------------------+----------------------+ +| STS32 | Sensirion | :ref:`sts3x` | ++---------------+----------------------------------------+----------------------+ +| STS33 | Sensirion | :ref:`sts3x` | ++---------------+----------------------------------------+----------------------+ +| STS34 | Sensirion | :ref:`sts3x` | ++---------------+----------------------------------------+----------------------+ +| STS35 | Sensirion | :ref:`sts3x` | ++---------------+----------------------------------------+----------------------+ +| MAX17043 | Maxim | :ref:`max1704x` | ++---------------+----------------------------------------+----------------------+ +| MAX17044 | Maxim | :ref:`max1704x` | ++---------------+----------------------------------------+----------------------+ +| MAX17048 | Maxim | :ref:`max1704x` | ++---------------+----------------------------------------+----------------------+ +| MAX17049 | Maxim | :ref:`max1704x` | ++---------------+----------------------------------------+----------------------+ +| TCA6424A | TI | :ref:`tca6424a` | ++---------------+----------------------------------------+----------------------+ +| SFA30 | Sensirion | :ref:`sfa3x` | ++---------------+----------------------------------------+----------------------+ +| L3GD20 | STMicroelectronics | :ref:`l3gx` | ++---------------+----------------------------------------+----------------------+ +| L3G4200D | STMicroelectronics | :ref:`l3gx` | ++---------------+----------------------------------------+----------------------+ +| LSM303 | STMicroelectronics | :ref:`lsm303` | ++---------------+----------------------------------------+----------------------+ diff --git a/docs/source/groups/ads130e08.rst b/docs/source/groups/ads130e08.rst new file mode 100644 index 00000000..ab016874 --- /dev/null +++ b/docs/source/groups/ads130e08.rst @@ -0,0 +1,8 @@ +.. _ads130e08: + +ads130e08 - Driver for ADS130E08 SPI ADC +======================================== + +.. doxygengroup:: ads130e08 + :members: + diff --git a/docs/source/groups/am2320.rst b/docs/source/groups/am2320.rst new file mode 100644 index 00000000..7c708da3 --- /dev/null +++ b/docs/source/groups/am2320.rst @@ -0,0 +1,7 @@ +.. _am2320: + +am2320 - Driver for AM2320 temperature and humidity sensor (I2C) +================================================================ + +.. doxygengroup:: am2320 + :members: diff --git a/docs/source/groups/calibration.rst b/docs/source/groups/calibration.rst new file mode 100644 index 00000000..0f758611 --- /dev/null +++ b/docs/source/groups/calibration.rst @@ -0,0 +1,7 @@ +.. _calibration: + +calibration - Multi-point calibration library +============================================= + +.. doxygengroup:: calibration + :members: diff --git a/docs/source/groups/dps310.rst b/docs/source/groups/dps310.rst new file mode 100644 index 00000000..97f75a38 --- /dev/null +++ b/docs/source/groups/dps310.rst @@ -0,0 +1,8 @@ +.. _dps310: + +dps310 - Driver for for DPS310, barometric pressure sensor +========================================================== + +.. doxygengroup:: dps310 + :members: + diff --git a/docs/source/groups/hdc1000.rst b/docs/source/groups/hdc1000.rst index aeb491cc..dec86c09 100644 --- a/docs/source/groups/hdc1000.rst +++ b/docs/source/groups/hdc1000.rst @@ -1,4 +1,4 @@ -.. hdc1000: +.. _hdc1000: hdc1000 - Driver for HDC1000 temperature and humidity sensor ============================================================ diff --git a/docs/source/groups/icm42670.rst b/docs/source/groups/icm42670.rst new file mode 100644 index 00000000..53b9f399 --- /dev/null +++ b/docs/source/groups/icm42670.rst @@ -0,0 +1,8 @@ +.. _icm42670: + +icm42670 - Driver for TDK ICM-42670-P 6-Axis IMU +================================================ + +.. doxygengroup:: icm42670 + :members: + diff --git a/docs/source/groups/l3gx.rst b/docs/source/groups/l3gx.rst new file mode 100644 index 00000000..3852a858 --- /dev/null +++ b/docs/source/groups/l3gx.rst @@ -0,0 +1,8 @@ +.. _l3gx: + +l3gx - Driver for L3Gx(L3GD20/L3G4200D) 3-axis gyroscope sensors +================================================================ + +.. doxygengroup:: l3gx + :members: + diff --git a/docs/source/groups/lc709203f.rst b/docs/source/groups/lc709203f.rst index 6c769ec1..5225c376 100644 --- a/docs/source/groups/lc709203f.rst +++ b/docs/source/groups/lc709203f.rst @@ -1,7 +1,7 @@ .. _lc709203f: lc709203f - Driver for LC709203F battery fuel gauge -===================================== +=================================================== .. doxygengroup:: lc709203f :members: \ No newline at end of file diff --git a/docs/source/groups/lsm303.rst b/docs/source/groups/lsm303.rst new file mode 100644 index 00000000..9899d7bf --- /dev/null +++ b/docs/source/groups/lsm303.rst @@ -0,0 +1,7 @@ +.. _lsm303: + +lsm303 - Driver for LSM303 3-axis accelerometer and magnetometer sensor +======================================================================= + +.. doxygengroup:: lsm303 + :members: diff --git a/docs/source/groups/max1704x.rst b/docs/source/groups/max1704x.rst new file mode 100644 index 00000000..6e1d0fa8 --- /dev/null +++ b/docs/source/groups/max1704x.rst @@ -0,0 +1,7 @@ +.. _max1704x: + +max1704x - Driver for MAX17043/MAX17044/MAX17048/MAX17049 battery fuel gauge +============================================================================ + +.. doxygengroup:: max1704x + :members: \ No newline at end of file diff --git a/docs/source/groups/mp2660.rst b/docs/source/groups/mp2660.rst new file mode 100644 index 00000000..76a7f6f0 --- /dev/null +++ b/docs/source/groups/mp2660.rst @@ -0,0 +1,8 @@ +.. _mp2660: + +mp2660 - 5V USB, 500mA, I2C-Controlled Linear Charger with Power Path Management for Single-Cell Li-Ion Battery +=============================================================================================================== + +.. doxygengroup:: mp2660 + :members: + diff --git a/docs/source/groups/mpu6050.rst b/docs/source/groups/mpu6050.rst new file mode 100644 index 00000000..34ca7a32 --- /dev/null +++ b/docs/source/groups/mpu6050.rst @@ -0,0 +1,8 @@ +.. _mpu6050: + +mpu6050 - Driver for MPU6000/MPU6050 6-axis MotionTracking device +================================================================= + +.. doxygengroup:: mpu6050 + :members: + diff --git a/docs/source/groups/pcf8563.rst b/docs/source/groups/pcf8563.rst index 600151ee..2205c8a3 100644 --- a/docs/source/groups/pcf8563.rst +++ b/docs/source/groups/pcf8563.rst @@ -1,8 +1,7 @@ .. _pcf8563: -pcf8563 - Driver for PCF8563 real-time clock/calendar -===================================================== +pcf8563 - Driver for PCF8563 (BM8563) real-time clock/calendar +============================================================== .. doxygengroup:: pcf8563 :members: - diff --git a/docs/source/groups/qmp6988.rst b/docs/source/groups/qmp6988.rst new file mode 100644 index 00000000..42a42ec7 --- /dev/null +++ b/docs/source/groups/qmp6988.rst @@ -0,0 +1,8 @@ +.. _qmp6988: + +qmp6988 - Driver for QMP6988 digital temperature and pressure sensor +==================================================================== + +.. doxygengroup:: qmp6988 + :members: + diff --git a/docs/source/groups/sfa3x.rst b/docs/source/groups/sfa3x.rst new file mode 100644 index 00000000..2e778edd --- /dev/null +++ b/docs/source/groups/sfa3x.rst @@ -0,0 +1,7 @@ +.. _sfa3x: + +sfa3x - Driver for SFA30 formaldehyde detection module (I2C) +============================================================ + +.. doxygengroup:: sfa3x + :members: diff --git a/docs/source/groups/sgm58031.rst b/docs/source/groups/sgm58031.rst new file mode 100644 index 00000000..c67d1265 --- /dev/null +++ b/docs/source/groups/sgm58031.rst @@ -0,0 +1,7 @@ +.. _sgm58031: + +sgm58031 - Driver for SGM58031 16-bit ADC +======================================================================= + +.. doxygengroup:: sgm58031 + :members: \ No newline at end of file diff --git a/docs/source/groups/sts3x.rst b/docs/source/groups/sts3x.rst new file mode 100644 index 00000000..fcd0392c --- /dev/null +++ b/docs/source/groups/sts3x.rst @@ -0,0 +1,7 @@ +.. _sts3x: + +sts3x - Driver for Sensirion STS3x digital temperature sensor +========================================================================== + +.. doxygengroup:: sts3x + :members: diff --git a/docs/source/groups/tca6424a.rst b/docs/source/groups/tca6424a.rst new file mode 100644 index 00000000..900cfdc9 --- /dev/null +++ b/docs/source/groups/tca6424a.rst @@ -0,0 +1,8 @@ +.. _tca6424a: + +tca6424a - Driver for TCA6424A low-voltage 24-bit I2C I/O expander +================================================================== + +.. doxygengroup:: tca6424a + :members: + diff --git a/docs/source/groups/veml7700.rst b/docs/source/groups/veml7700.rst new file mode 100644 index 00000000..afc952a3 --- /dev/null +++ b/docs/source/groups/veml7700.rst @@ -0,0 +1,111 @@ +.. _veml7700: + +veml7700 - Driver for ambient light sensor VEML7700 +==================================================== + +The VEML7700 is a high-accuracy ambient light sensor. All measurements and filtering +is done on the chip which makes it easy to integrate. The sensitivity can be adjusted +by a gain setting and by changing the integration time. The output of the sensor is a +count value which must be converted using the resolution value in the driver. + +The sensor has power saving features which reduces the repetition of measurements. +It has also a power off feature to save even more power. + +The ambient light sensor value is filterd very clone to the caracteristic of the +human eye. Besides, als the white channel with a wider wavelength spectrum. In most +applications, the ambient light value will do just fine. + +Power consumption +----------------- + +* 0.5 μA in shut-down mode (@3.3V) +* down to 2 μA in power save mode 4 (@3.3V) +* 45 μA on 100ms integration time (@3.3V) + +Communication interface +----------------------- + +I2C is used as communication interface without any further interrupt pins. It has six +command codes for the settings and the output values. Read the datasheet or application +note for further information. + +To reduce interactions with the integrated microcontroller, the interrupt feature can +be used. Therefore, one must configure the low and high threshold and enable the interrupt. + +Interrupt application examples +------------------------------ + +If values below a certain threshold is of interest, i.e. to activate lights when its +getting dark outside, the low threshold should be adjusted and setting the high threshold +to maximum (65535). + +Another application could be an automated rollershutter, then both thresholds sould be +set to trigger the up and down movement of rollershutters. + +Measurement process +------------------- + +The measurement takes time and the sensor should not be read out faster than the +measurement time. Therefore the application should be adjusted to the sensor configuration +regarting integration time and power save modes. Alternatively, the interrupt feature +can be used by repeatetive reading of the interrupt status. + +Usage +----- + +This driver uses i2cdev which must be initialized first. Then initialize the device +decriptor, one discriptor per device. The sensor can be used without configuration, +but has a high chance of over-saturation on sunlight. Therefore, change gain and +integration time to your needs and configure the device. + +Then, the veml7700_ambient_light and veml7700_white_channel functions can be used +to read out the brightness. The driver converts the counts from the device to lx using +the configuration. + +Hardware configurations +^^^^^^^^^^^^^^^^^^^^^^^ + +The driver supports multiple VEML7700 sensors at the same time that are +connected to I2C. Following figure show some possible hardware +configurations. + +First figure shows the configuration with one sensor at I2C bus 0. + +.. code-block:: text + + +------------------+ +----------+ + | ESP8266 / ESP32 | | VEML7700 | + | | | | + | GPIO 14 (SCL) ----> SCL | + | GPIO 13 (SDA) <---> SDA | + +------------------+ +----------+ + +Next figure shows a possible configuration with two I2C buses. + +.. code-block:: text + + +------------------+ +----------+ + | ESP8266 / ESP32 | | VEML7700 | + | | | | + | GPIO 14 (SCL) ----> SCL | + | GPIO 13 (SDA) <---> SDA | + | | +----------+ + | | | VEML7700 | + | | | | + | GPIO 5 (SCL) ----> SCL | + | GPIO 4 (SDA) <---> SDA | + +------------------+ +----------+ + +Only one sensor per I2C bus is possible, since it uses a unconfigurable I2C slave address. +However, one could also use GPIO controller bus buffer to connect the bus to different +devices. + +References +---------- + +`Datasheet `_ + +`Application Note `_ + +.. doxygengroup:: veml7700 + :members: diff --git a/docs/source/index.rst b/docs/source/index.rst index 2e6b755a..b135cd4b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -34,15 +34,7 @@ or cd ~/my/work/path git clone git@gitlab.com:UncleRus/esp-idf-lib.git -Add path to components in your project makefile, e.g: - -.. code-block:: make - - PROJECT_NAME := my-esp-project - EXTRA_COMPONENT_DIRS := $(HOME)/my/work/path/esp-idf-lib/components - include $(IDF_PATH)/make/project.mk - -or in CMakeLists.txt: +Add path to components in your project CMakeLists.txt: .. code-block:: cmake @@ -92,6 +84,7 @@ Common libraries groups/color groups/noise groups/framebuffer + groups/calibration Real-time clocks ================ @@ -126,7 +119,9 @@ Humidity & temperature sensors groups/hdc1000 groups/max31855 groups/sts21 - + groups/sts3x + groups/am2320 + Pressure sensors ================ .. toctree:: @@ -135,7 +130,9 @@ Pressure sensors groups/bmp180 groups/bmp280 groups/bme680 + groups/dps310 groups/ms5611 + groups/qmp6988 Air quality/Gas sensors ======================= @@ -147,6 +144,7 @@ Air quality/Gas sensors groups/mhz19b groups/scd4x groups/scd30 + groups/sfa3x ADC/DAC ======= @@ -158,6 +156,8 @@ ADC/DAC groups/pcf8591 groups/mcp4725 groups/mcp342x + groups/ads130e08 + groups/sgm58031 Power/Current monitors ====================== @@ -175,7 +175,8 @@ Magnetic sensors groups/hmc5883l groups/qmc5883l - + groups/lsm303 + Light sensors ============= .. toctree:: @@ -185,6 +186,7 @@ Light sensors groups/tsl2561 groups/tsl4531 groups/tsl2591 + groups/veml7700 GPIO expanders ============== @@ -197,7 +199,8 @@ GPIO expanders groups/mcp23008 groups/mcp23x17 groups/pca9557 - + groups/tca6424a + LED drivers =========== .. toctree:: @@ -206,6 +209,7 @@ LED drivers groups/led_strip groups/led_strip_spi groups/ht16k33 + groups/max7219 Input controls ============== @@ -216,13 +220,31 @@ Input controls groups/encoder groups/ls7366r +Inertial measurement units +========================== +.. toctree:: + :maxdepth: 1 + + groups/icm42670 + groups/mpu6050 + groups/l3gx + groups/lsm303 + +Battery controllers +=================== +.. toctree:: + :maxdepth: 1 + + groups/lc709203f + groups/max1704x + groups/mp2660 + Other ===== .. toctree:: :maxdepth: 1 groups/hd44780 - groups/max7219 groups/pca9685 groups/ultrasonic groups/tda74xx @@ -230,8 +252,7 @@ Other groups/tca9548 groups/ds3502 groups/wiegand - groups/lc709203f - + =========== Information diff --git a/examples/ads111x/default/main/main.c b/examples/ads111x/default/main/main.c index 8277c4f4..0b873864 100644 --- a/examples/ads111x/default/main/main.c +++ b/examples/ads111x/default/main/main.c @@ -28,11 +28,11 @@ static void measure(size_t n) { // wait for conversion end bool busy; - do - { - ads111x_is_busy(&devices[n], &busy); - } - while (busy); + // do + // { + // ads111x_is_busy(&devices[n], &busy); + // } + // while (busy); // Read result int16_t raw = 0; diff --git a/examples/ads130e08/default/CMakeLists.txt b/examples/ads130e08/default/CMakeLists.txt new file mode 100644 index 00000000..f7f2e3b9 --- /dev/null +++ b/examples/ads130e08/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-ads130e08) diff --git a/examples/ads130e08/default/Makefile b/examples/ads130e08/default/Makefile new file mode 100644 index 00000000..2f8508ea --- /dev/null +++ b/examples/ads130e08/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-ads130e08 + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/ads130e08/default/README.md b/examples/ads130e08/default/README.md new file mode 100644 index 00000000..7428a660 --- /dev/null +++ b/examples/ads130e08/default/README.md @@ -0,0 +1,34 @@ +# Example for `ADS130E08` driver + +The datasheet can be found [here](https://www.ti.com/lit/ds/symlink/ads130e08.pdf?ts=1666137782883). + +## What it does + +The example configures one `ADS130E08` device on a `SPI` bus, enabling it to be read via ISR or polling. + +## Wiring + +Connect `MOSI`, `MISO`, `SCLK`, `CS` and `INT` pins to the following pins: + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_MOSI_GPIO` | GPIO number for `MOSI` | "23" for `esp32` | +| `CONFIG_EXAMPLE_MISO_GPIO` | GPIO number for `MISO` | "19" for `esp32` | +| `CONFIG_EXAMPLE_SCLK_GPIO` | GPIO number for `SCLK` | "18" for `esp32` | +| `CONFIG_EXAMPLE_CS_GPIO` | GPIO number for `CS` | "5" for `esp32` | +| `CONFIG_EXAMPLE_INT_GPIO` | GPIO number for `INT` | "26" for `esp32` | + +The driver was tested in ESP32 and ESP32S3, but should also work on other ESP boards. + +## Notes + +1. Please configure the SPI connections to ADS130E08 under `Example configuration` in `menuconfig`. +2. In order to read data via ISR, is necessary to enable SPI master and slave functions into IRAM under `Component config` -> `Driver configurations` -> `SPI configuration`. + +## Output + +``` +I (1010) ads130e08: Fault status -> Positive 00, Negative f0 +I (1010) ads130e08: Gpios level -> 0f +I (1010) ads130e08: Raw data -> CH1: -1 CH2: -1 CH3: -1 CH4: -1 CH5: -1 CH6: -1 CH7: -1 CH8: -1 +``` \ No newline at end of file diff --git a/examples/ads130e08/default/main/CMakeLists.txt b/examples/ads130e08/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/ads130e08/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/ads130e08/default/main/Kconfig.projbuild b/examples/ads130e08/default/main/Kconfig.projbuild new file mode 100644 index 00000000..9ce60d83 --- /dev/null +++ b/examples/ads130e08/default/main/Kconfig.projbuild @@ -0,0 +1,36 @@ +menu "Example configuration" + config EXAMPLE_MOSI_GPIO + int "SPI MOSI GPIO Number" + default 23 if IDF_TARGET_ESP32 + default 11 if IDF_TARGET_ESP32S3 + help + GPIO number for SPI MOSI (Master Output / Slave Input) line. + + config EXAMPLE_MISO_GPIO + int "SPI MISO GPIO Number" + default 19 if IDF_TARGET_ESP32 + default 13 if IDF_TARGET_ESP32S3 + help + GPIO number for SPI MISO (Master Input / Slave Output) line. + + config EXAMPLE_SCLK_GPIO + int "SPI SCLK GPIO Number" + default 18 if IDF_TARGET_ESP32 + default 12 if IDF_TARGET_ESP32S3 + help + GPIO number for SPI SCLK (Clock) line. + + config EXAMPLE_CS_GPIO + int "SPI CS GPIO Number" + default 5 if IDF_TARGET_ESP32 + default 10 if IDF_TARGET_ESP32S3 + help + GPIO number for SPI CS (Chip Select) line. + + config EXAMPLE_INT_GPIO + int "SPI INT GPIO Number" + default 26 if IDF_TARGET_ESP32 + default 2 if IDF_TARGET_ESP32S3 + help + GPIO number for SPI INT (Interrupt) line. +endmenu diff --git a/examples/ads130e08/default/main/component.mk b/examples/ads130e08/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/ads130e08/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/ads130e08/default/main/main.c b/examples/ads130e08/default/main/main.c new file mode 100644 index 00000000..3ff98429 --- /dev/null +++ b/examples/ads130e08/default/main/main.c @@ -0,0 +1,102 @@ +#include +#include +#include "freertos/semphr.h" +#include +#include +#include +#include +#include +#include +#include + +#if CONFIG_IDF_TARGET_ESP32 +#define HOST HSPI_HOST +#elif CONFIG_IDF_TARGET_ESP32S3 +#define HOST SPI2_HOST +#endif + +static const char *TAG_ADS130E08 = "ads130e08"; + +ads130e08_t adc_dev; +ads130e08_raw_data_t raw_data; + +SemaphoreHandle_t ads130e08_drdy_semaphore = NULL; + +void IRAM_ATTR gpio_isr_handler(void *arg) +{ + /* data ready */ + xSemaphoreGiveFromISR(ads130e08_drdy_semaphore, NULL); + portYIELD_FROM_ISR(); +} + +// Main task +void ads130e08_test(void *pvParameters) +{ + // Initialize semaphore + ads130e08_drdy_semaphore = xSemaphoreCreateBinary(); + + // Configure SPI bus + spi_bus_config_t spi_cfg = { + .mosi_io_num = CONFIG_EXAMPLE_MOSI_GPIO, + .miso_io_num = CONFIG_EXAMPLE_MISO_GPIO, + .sclk_io_num = CONFIG_EXAMPLE_SCLK_GPIO, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 0, + .flags = 0, + }; + ESP_ERROR_CHECK(spi_bus_initialize(HOST, &spi_cfg, 1)); + + // Init device + static ads130e08_dev_config_t dev_config = { + .clk_en = ADS130E08_CLK_OUT_DISABLED, + .int_test = ADS130E08_INT_TEST_EXTERNAL, + .test_amp = ADS130E08_TEST_AMP_CALIB_1X, + .test_freq = ADS130E08_TEST_FREQ_EXP_21, + .pd_refbuf = ADS130E08_INTERNAL_REF_BUFFER_ENABLED, + .vref_4v = ADS130E08_REF_VOLTAGE_2_4V, + .opamp_ref = ADS130E08_NON_INVERTING_CONNECT_OPAMP, + .pd_opamp = ADS130E08_OPAMP_DISABLED, + }; + + ESP_ERROR_CHECK(ads130e08_init_desc(&adc_dev, HOST, CONFIG_EXAMPLE_CS_GPIO)); + ESP_ERROR_CHECK(ads130e08_send_system_cmd(&adc_dev, ADS130E08_CMD_STOP)); + ESP_ERROR_CHECK(ads130e08_send_system_cmd(&adc_dev, ADS130E08_CMD_RESET)); + ESP_ERROR_CHECK(ads130e08_send_data_read_cmd(&adc_dev, ADS130E08_CMD_SDATAC)); + ESP_ERROR_CHECK(ads130e08_set_device_config(&adc_dev, dev_config)); + + // Configure ISR + gpio_config_t int_ads130e08 = { + .intr_type = GPIO_INTR_NEGEDGE, + .mode = GPIO_MODE_INPUT, + .pin_bit_mask = (1ULL << CONFIG_EXAMPLE_INT_GPIO), + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + }; + + ESP_ERROR_CHECK(gpio_config(&int_ads130e08)); + ESP_ERROR_CHECK(gpio_install_isr_service(0)); + ESP_ERROR_CHECK(gpio_isr_handler_add(CONFIG_EXAMPLE_INT_GPIO, gpio_isr_handler, (void *)CONFIG_EXAMPLE_INT_GPIO)); + + // Start conversion + ESP_ERROR_CHECK(ads130e08_send_system_cmd(&adc_dev, ADS130E08_CMD_START)); + + for (;;) + { + xSemaphoreTake(ads130e08_drdy_semaphore, portMAX_DELAY); + + ESP_ERROR_CHECK(ads130e08_get_rdata(&adc_dev, &raw_data)); + + ESP_LOGI(TAG_ADS130E08, "Fault status -> Positive %02x, Negative %02x", raw_data.fault_statp, + raw_data.fault_statn); + ESP_LOGI(TAG_ADS130E08, "Gpios level -> %02x", raw_data.gpios_level); + ESP_LOGI(TAG_ADS130E08, "Raw data -> CH1: %d CH2: %d CH3: %d CH4: %d CH5: %d CH6: %d CH7: %d CH8: %d", + raw_data.channels_raw[0], raw_data.channels_raw[1], raw_data.channels_raw[2], raw_data.channels_raw[3], + raw_data.channels_raw[4], raw_data.channels_raw[5], raw_data.channels_raw[6], raw_data.channels_raw[7]); + } +} + +void app_main() +{ + xTaskCreatePinnedToCore(ads130e08_test, "ads130e08_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/am2320/default/CMakeLists.txt b/examples/am2320/default/CMakeLists.txt new file mode 100644 index 00000000..216240e4 --- /dev/null +++ b/examples/am2320/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-am2320) diff --git a/examples/am2320/default/Makefile b/examples/am2320/default/Makefile new file mode 100644 index 00000000..2b9b3726 --- /dev/null +++ b/examples/am2320/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-am2320 + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/am2320/default/README.md b/examples/am2320/default/README.md new file mode 100644 index 00000000..da1bb7a6 --- /dev/null +++ b/examples/am2320/default/README.md @@ -0,0 +1,21 @@ +# Example for `am2320` driver + +## What it does + +It displays sensor info (device ID, model ID, version) and then shows +temperature and humidity in loop. + +## Wiring + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | + + +## Note + +This sensor also supports single-wire DHT protocol. See `dht` component. diff --git a/examples/am2320/default/main/CMakeLists.txt b/examples/am2320/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/am2320/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/am2320/default/main/Kconfig.projbuild b/examples/am2320/default/main/Kconfig.projbuild new file mode 100644 index 00000000..cebd2a2d --- /dev/null +++ b/examples/am2320/default/main/Kconfig.projbuild @@ -0,0 +1,19 @@ +menu "Example configuration" + + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. + +endmenu diff --git a/examples/am2320/default/main/component.mk b/examples/am2320/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/am2320/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/am2320/default/main/main.c b/examples/am2320/default/main/main.c new file mode 100644 index 00000000..c854c8df --- /dev/null +++ b/examples/am2320/default/main/main.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +static const char *TAG = "am2320-example"; + +void task(void *pvParameters) +{ + i2c_dev_t dev = {0}; + + ESP_ERROR_CHECK(am2320_init_desc(&dev, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + + float temperature, humidity; + + while (1) + { + esp_err_t res = am2320_get_rht(&dev, &temperature, &humidity); + if (res == ESP_OK) + ESP_LOGI(TAG, "Temperature: %.1f°C, Humidity: %.1f%%", temperature, humidity); + else + ESP_LOGE(TAG, "Error reading data: %d (%s)", res, esp_err_to_name(res)); + + vTaskDelay(pdMS_TO_TICKS(500)); + } +} + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + xTaskCreatePinnedToCore(task, TAG, configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/am2320/default/sdkconfig.defaults.esp8266 b/examples/am2320/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/am2320/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/calibration/default/CMakeLists.txt b/examples/calibration/default/CMakeLists.txt new file mode 100644 index 00000000..96c4893d --- /dev/null +++ b/examples/calibration/default/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(calibration_example) diff --git a/examples/calibration/default/Makefile b/examples/calibration/default/Makefile new file mode 100644 index 00000000..044b6820 --- /dev/null +++ b/examples/calibration/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := calibration_example + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/calibration/default/main/CMakeLists.txt b/examples/calibration/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/calibration/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/calibration/default/main/component.mk b/examples/calibration/default/main/component.mk new file mode 100644 index 00000000..004b18e6 --- /dev/null +++ b/examples/calibration/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . diff --git a/examples/calibration/default/main/main.c b/examples/calibration/default/main/main.c new file mode 100644 index 00000000..36dc3b39 --- /dev/null +++ b/examples/calibration/default/main/main.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) YYYY YOUR NAME HERE + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +static char *TAG = "calibration_example"; + +static const calibration_point_t points[] = { + { .code = 20, .value = 40 }, + { .code = 10, .value = 25 }, + { .code = 15, .value = 31 }, + { .code = 30, .value = 55 }, + { .code = 100, .value = 250 }, + { .code = 50, .value = 80 }, +}; + +static const size_t num_points = sizeof(points) / sizeof(calibration_point_t); + +static const float test_points[] = { + 0, -40, 15, 16, 30, 300, 31, 35, 40 +}; + +void app_main() +{ + calibration_handle_t c = {0 }; + ESP_ERROR_CHECK(calibration_init(&c, num_points, CALIBRATION_LINEAR)); + ESP_ERROR_CHECK(calibration_add_points(&c, points, num_points)); + + ESP_LOGI(TAG, "Calibration points:"); + for (int i = 0; i < c.filled; i++) + ESP_LOGI(TAG, "Point %d: %.2f ~ %.2f", i, c.points[i].code, c.points[i].value); + + printf("\n"); + + ESP_LOGI(TAG, "Test points:"); + for (int i = 0; i < sizeof(test_points) / sizeof(float); i++) + { + float v; + ESP_ERROR_CHECK(calibration_get_value(&c, test_points[i], &v)); + ESP_LOGI(TAG, "Test %d: %.2f ~ %.2f", i, test_points[i], v); + } +} diff --git a/examples/calibration/default/sdkconfig.defaults.esp8266 b/examples/calibration/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/calibration/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/dps310/altitude/CMakeLists.txt b/examples/dps310/altitude/CMakeLists.txt new file mode 100644 index 00000000..6a7ac657 --- /dev/null +++ b/examples/dps310/altitude/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-dps310-altitude) diff --git a/examples/dps310/altitude/Makefile b/examples/dps310/altitude/Makefile new file mode 100644 index 00000000..2a8ba34d --- /dev/null +++ b/examples/dps310/altitude/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-dps310-altitude + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/dps310/altitude/README.md b/examples/dps310/altitude/README.md new file mode 100644 index 00000000..9eff3ca9 --- /dev/null +++ b/examples/dps310/altitude/README.md @@ -0,0 +1,83 @@ +# Example for `dps310` driver (background mode) + +## What it does + +The example application initializes `DPS310` device, calibrates the sensor by +the real altitude. + +It shows altitude in the loop. + +## Wiring + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | + +## Notes + +The default I2C address, which is used in this example, is `0x77`. Change the +address under `Example configuration` by `idf.py menuconfig`. + +Change `EXAMPLE_REAL_ALTITUDE` to the altitude of the device by `idf.py +menuconfig` (use a map, or Google Earth to find out). + +## Example output + +```console +I (0) cpu_start: App cpu up. +I (228) cpu_start: Pro cpu start user code +I (228) cpu_start: cpu freq: 160000000 +I (228) cpu_start: Application information: +I (233) cpu_start: Project name: example-dps310-background +I (239) cpu_start: App version: 0.9.1-73-g4d01ef1-dirty +I (246) cpu_start: Compile time: Nov 13 2022 04:20:54 +I (252) cpu_start: ELF file SHA256: e0f948e27e257dd5... +I (258) cpu_start: ESP-IDF: v4.4.2-dirty +I (263) heap_init: Initializing. RAM available for dynamic allocation: +I (270) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM +I (277) heap_init: At 3FFB2CF0 len 0002D310 (180 KiB): DRAM +I (283) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM +I (289) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM +I (296) heap_init: At 4008C9D0 len 00013630 (77 KiB): IRAM +I (303) spi_flash: detected chip: generic +I (306) spi_flash: flash io: dio +W (310) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. +I (325) cpu_start: Starting scheduler on PRO CPU. +I (0) cpu_start: Starting scheduler on APP CPU. +I (0) dps310_example_default: Initializing I2C +I (10) dps310_example_default: Initializing the device descriptor +I (10) dps310_example_default: Initializing the device +I (140) dps310_example_default: Waiting for the sensor to be ready for measurement +I (1260) dps310: Calibration result: +I (1260) dps310: Pressure: 100799.67 (Pa) +I (1260) dps310: Calculated Pressure at sea level: 100991.00 (Pa) +I (1270) dps310: Calculated altitude: 15.99 (m) +I (1270) dps310: Real altitude: 16.00 (m) +I (1280) dps310: Offset: 0.01 (m) +I (1280) dps310_example_default: Setting background measurement mode +I (1290) dps310_example_default: Starting the loop +I (1340) dps310_example_default: altitude: 16.00 (m) +I (1390) dps310_example_default: altitude: 16.00 (m) +I (1440) dps310_example_default: altitude: 16.03 (m) +I (1490) dps310_example_default: altitude: 16.03 (m) +I (1540) dps310_example_default: altitude: 16.07 (m) +I (1590) dps310_example_default: altitude: 16.07 (m) +I (1640) dps310_example_default: altitude: 16.17 (m) +I (1690) dps310_example_default: altitude: 16.17 (m) +I (1740) dps310_example_default: altitude: 16.18 (m) +I (1790) dps310_example_default: altitude: 16.18 (m) +I (1840) dps310_example_default: altitude: 16.21 (m) +I (1890) dps310_example_default: altitude: 16.21 (m) +I (1940) dps310_example_default: altitude: 16.21 (m) +I (1990) dps310_example_default: altitude: 16.21 (m) +I (2040) dps310_example_default: altitude: 16.18 (m) +I (2090) dps310_example_default: altitude: 16.18 (m) +I (2140) dps310_example_default: altitude: 16.18 (m) +I (2190) dps310_example_default: altitude: 16.14 (m) +I (2240) dps310_example_default: altitude: 16.14 (m) +... +``` diff --git a/examples/dps310/altitude/main/CMakeLists.txt b/examples/dps310/altitude/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/dps310/altitude/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/dps310/altitude/main/Kconfig.projbuild b/examples/dps310/altitude/main/Kconfig.projbuild new file mode 100644 index 00000000..d0e4b4d4 --- /dev/null +++ b/examples/dps310/altitude/main/Kconfig.projbuild @@ -0,0 +1,27 @@ +menu "Example configuration" + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. + config EXAMPLE_I2C_ADDRESS + hex "I2C address" + default 0x77 + help + I2C address of DPS310. When SDO is pulled low, 0x76. When pulled high, 0x77. + config EXAMPLE_REAL_ALTITUDE + int "Real altitude of the device in meter" + default 16 + help + Change this to the altitude of the device. +endmenu diff --git a/examples/dps310/altitude/main/component.mk b/examples/dps310/altitude/main/component.mk new file mode 100644 index 00000000..004b18e6 --- /dev/null +++ b/examples/dps310/altitude/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . diff --git a/examples/dps310/altitude/main/main.c b/examples/dps310/altitude/main/main.c new file mode 100644 index 00000000..97f24ac2 --- /dev/null +++ b/examples/dps310/altitude/main/main.c @@ -0,0 +1,157 @@ +/* + * This example code is in the Public Domain. + */ + +/* standard headers */ +#include +#include +#include + +/* esp-idf headers */ +#include +#include +#include +#include +#include + +#define I2C_PORT 0 + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +static const char *TAG = "dps310_example_default"; + +void dps310_task(void *pvParameters) +{ + bool sensor_ready = false; + bool coef_ready = false; + float altitude = 0; + esp_err_t err = ESP_FAIL; + dps310_t dev; + + /* Initialize dps310_config_t with DPS310_CONFIG_DEFAULT() macro, which + * sets default values. + */ + dps310_config_t config = DPS310_CONFIG_DEFAULT(); + + /* Create and initialize the device descriptor with zero */ + memset(&dev, 0, sizeof(dps310_t)); + + config.tmp_oversampling = DPS310_TMP_PRC_64; + config.pm_oversampling = DPS310_PM_PRC_64; + config.tmp_rate = DPS310_TMP_RATE_1; + config.pm_rate = DPS310_PM_RATE_64; + + /* Initialize the I2C */ + ESP_LOGI(TAG, "Initializing I2C"); + err = i2cdev_init(); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "i2cdev_init(): %s", esp_err_to_name(err)); + goto init_fail; + } + + /* Initialize the device descriptor */ + ESP_LOGI(TAG, "Initializing the device descriptor"); + err = dps310_init_desc(&dev, CONFIG_EXAMPLE_I2C_ADDRESS, I2C_PORT, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_init_desc(): %s", esp_err_to_name(err)); + goto init_fail; + } + + /* Initialize the device. */ + ESP_LOGI(TAG, "Initializing the device"); + err = dps310_init(&dev, &config); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_init(): %s", esp_err_to_name(err)); + goto fail; + } + + /* ensure the sensor is ready and coefficients, or COEF, are also ready. + * */ + ESP_LOGI(TAG, "Waiting for the sensor to be ready for measurement"); + do + { + vTaskDelay(pdMS_TO_TICKS(10)); + if (!sensor_ready) + { + err = dps310_is_ready_for_sensor(&dev, &sensor_ready); + if (err != ESP_OK) + { + goto fail; + } + } + + if (!coef_ready) + { + err = dps310_is_ready_for_coef(&dev, &coef_ready); + if (err != ESP_OK) + { + goto fail; + } + } + } while (!sensor_ready || !coef_ready); + + /* read COEF once, which is used to compensate the raw value. The COEF + * values are kept in the device descriptor. + */ + err = dps310_get_coef(&dev); + if (err != ESP_OK) + { + goto fail; + } + + /* Calibrate altitude */ + err = dps310_calibrate_altitude(&dev, CONFIG_EXAMPLE_REAL_ALTITUDE); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_calibrate_altitude(): %s", esp_err_to_name(err)); + goto fail; + } + + /* Temperature and pressure background mode + * + * The sensor continuously measures temperature and pressure. + */ + ESP_LOGI(TAG, "Setting background measurement mode"); + err = dps310_backgorund_start(&dev, DPS310_MODE_BACKGROUND_ALL); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_set_mode(): %s", esp_err_to_name(err)); + goto fail; + } + + ESP_LOGI(TAG, "Starting the loop"); + while (1) { + + vTaskDelay(pdMS_TO_TICKS(50)); + err = dps310_read_altitude(&dev, &altitude); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_altitude(): %s", esp_err_to_name(err)); + } + ESP_LOGI(TAG, "altitude: %0.2f (m)", altitude); + } + +fail: + + /* free the resources. + * + * ignore returned value here as well. + */ + (void)dps310_free_desc(&dev); + +init_fail: + ESP_LOGE(TAG, "Halting due to error"); + while (1) { + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +void app_main() +{ + xTaskCreatePinnedToCore(dps310_task, TAG, configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/dps310/altitude/sdkconfig.defaults.esp8266 b/examples/dps310/altitude/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/dps310/altitude/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/dps310/background/CMakeLists.txt b/examples/dps310/background/CMakeLists.txt new file mode 100644 index 00000000..232f9643 --- /dev/null +++ b/examples/dps310/background/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-dps310-background) diff --git a/examples/dps310/background/Makefile b/examples/dps310/background/Makefile new file mode 100644 index 00000000..bd7cf0b3 --- /dev/null +++ b/examples/dps310/background/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-dps310-background + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/dps310/background/README.md b/examples/dps310/background/README.md new file mode 100644 index 00000000..878e33e6 --- /dev/null +++ b/examples/dps310/background/README.md @@ -0,0 +1,84 @@ +# Example for `dps310` driver (background mode) + +## What it does + +The example application initializes `DPS310` device. It enables the FIFO, and +set operation mode to background mode (one measurement per sec for temperature +and 2 measurements per sec for pressure). + +In the loop, the sensor is polled by querying if FIFO is empty. Continue the +next loop if FIFO is empty, read one measurement from FIFO, otherwise. + +## Wiring + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | + +## Notes + +The default I2C address, which is used in this example, is `0x77`. Change the +address under `Example configuration` by `idf.py menuconfig`. + +## Example output + +```console +I (0) cpu_start: App cpu up. +I (226) cpu_start: Pro cpu start user code +I (226) cpu_start: cpu freq: 160000000 +I (226) cpu_start: Application information: +I (230) cpu_start: Project name: example-dps310-background +I (237) cpu_start: App version: 0.9.1-64-gd7ad7d9-dirty +I (243) cpu_start: Compile time: Nov 3 2022 19:18:58 +I (249) cpu_start: ELF file SHA256: d6ee323571e3c6e3... +I (255) cpu_start: ESP-IDF: v4.4.2-dirty +I (261) heap_init: Initializing. RAM available for dynamic allocation: +I (268) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM +I (274) heap_init: At 3FFB2CE8 len 0002D318 (180 KiB): DRAM +I (280) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM +I (286) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM +I (293) heap_init: At 4008C9D0 len 00013630 (77 KiB): IRAM +I (300) spi_flash: detected chip: generic +I (304) spi_flash: flash io: dio +W (308) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. +I (322) cpu_start: Starting scheduler on PRO CPU. +I (0) cpu_start: Starting scheduler on APP CPU. +I (0) dps310_example_default: Initializing I2C +I (10) dps310_example_default: Initializing the device descriptor +I (10) dps310_example_default: Initializing the device +I (150) dps310_example_default: Waiting for the sensor to be ready for measurement +I (160) dps310_example_default: Flushing FIFO +I (160) dps310_example_default: Setting background measurement mode +I (160) dps310_example_default: Starting the loop +I (370) dps310_example_default: pressure: 101807.75 Pa +I (580) dps310_example_default: temperature: 27.93 °C +I (870) dps310_example_default: pressure: 101045.64 Pa +I (1380) dps310_example_default: pressure: 101045.03 Pa +I (1580) dps310_example_default: temperature: 27.93 °C +I (1880) dps310_example_default: pressure: 101043.59 Pa +I (2380) dps310_example_default: pressure: 101043.88 Pa +I (2590) dps310_example_default: temperature: 27.91 °C +I (2890) dps310_example_default: pressure: 101043.05 Pa +I (3390) dps310_example_default: pressure: 101043.48 Pa +I (3600) dps310_example_default: temperature: 27.91 °C +I (3890) dps310_example_default: pressure: 101042.98 Pa +I (4390) dps310_example_default: pressure: 101043.41 Pa +I (4600) dps310_example_default: temperature: 27.91 °C +I (4900) dps310_example_default: pressure: 101042.81 Pa +I (5400) dps310_example_default: pressure: 101043.41 Pa +I (5610) dps310_example_default: temperature: 27.91 °C +I (5900) dps310_example_default: pressure: 101042.90 Pa +I (6410) dps310_example_default: pressure: 101043.49 Pa +I (6610) dps310_example_default: temperature: 27.91 °C +I (6910) dps310_example_default: pressure: 101042.85 Pa +I (7410) dps310_example_default: pressure: 101043.41 Pa +I (7620) dps310_example_default: temperature: 27.90 °C +I (7910) dps310_example_default: pressure: 101042.59 Pa +I (8420) dps310_example_default: pressure: 101043.34 Pa +I (8630) dps310_example_default: temperature: 27.90 °C +... +``` diff --git a/examples/dps310/background/main/CMakeLists.txt b/examples/dps310/background/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/dps310/background/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/dps310/background/main/Kconfig.projbuild b/examples/dps310/background/main/Kconfig.projbuild new file mode 100644 index 00000000..eb6ce643 --- /dev/null +++ b/examples/dps310/background/main/Kconfig.projbuild @@ -0,0 +1,22 @@ +menu "Example configuration" + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. + config EXAMPLE_I2C_ADDRESS + hex "I2C address" + default 0x77 + help + I2C address of DPS310. When SDO is pulled low, 0x76. When pulled high, 0x77. +endmenu diff --git a/examples/dps310/background/main/component.mk b/examples/dps310/background/main/component.mk new file mode 100644 index 00000000..004b18e6 --- /dev/null +++ b/examples/dps310/background/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . diff --git a/examples/dps310/background/main/main.c b/examples/dps310/background/main/main.c new file mode 100644 index 00000000..30dd0ae1 --- /dev/null +++ b/examples/dps310/background/main/main.c @@ -0,0 +1,247 @@ +/* + * This example code is in the Public Domain. + */ + +/* standard headers */ +#include +#include +#include + +/* esp-idf headers */ +#include +#include +#include +#include +#include + +#define I2C_PORT 0 + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +static const char *TAG = "dps310_example_default"; + +void dps310_task(void *pvParameters) +{ + bool sensor_ready = false; + bool coef_ready = false; + bool fifo_is_empty = true; + dps310_fifo_en_mode_t fifo_en_mode = 0; + dps310_mode_t mode = 0; + esp_err_t err = ESP_FAIL; + dps310_t dev; + dps310_fifo_measurement_t measurement; + + /* Initialize dps310_config_t with DPS310_CONFIG_DEFAULT() macro, which + * sets default values. + */ + dps310_config_t config = DPS310_CONFIG_DEFAULT(); + + /* Create and initialize the device descriptor with zero */ + memset(&dev, 0, sizeof(dps310_t)); + memset(&measurement, 0, sizeof(dps310_fifo_measurement_t)); + + /* Modify dps310_config_t. Use 1 measurements per sec for temperature, 2 + * measurements per sec for pressure. + * */ + config.tmp_oversampling = DPS310_TMP_PRC_128; + config.pm_oversampling = DPS310_PM_PRC_128; + config.tmp_rate = DPS310_TMP_RATE_1; + config.pm_rate = DPS310_PM_RATE_2; + + /* Enable FIFO */ + config.fifo_en_mode = DPS310_FIFO_ENABLE; + + /* Initialize the I2C */ + ESP_LOGI(TAG, "Initializing I2C"); + err = i2cdev_init(); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "i2cdev_init(): %s", esp_err_to_name(err)); + goto init_fail; + } + + /* Initialize the device descriptor */ + ESP_LOGI(TAG, "Initializing the device descriptor"); + err = dps310_init_desc(&dev, CONFIG_EXAMPLE_I2C_ADDRESS, I2C_PORT, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_init_desc(): %s", esp_err_to_name(err)); + goto init_fail; + } + + /* Initialize the device. */ + ESP_LOGI(TAG, "Initializing the device"); + err = dps310_init(&dev, &config); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_init(): %s", esp_err_to_name(err)); + goto fail; + } + + err = dps310_get_fifo_en(&dev, &fifo_en_mode); + if (err != ESP_OK) + { + goto fail; + } + if (fifo_en_mode != DPS310_FIFO_ENABLE) + { + ESP_LOGE(TAG, "fifo_en is not enabled"); + goto fail; + } + + /* ensure the sensor is ready and coefficients, or COEF, are also ready. + * */ + ESP_LOGI(TAG, "Waiting for the sensor to be ready for measurement"); + do + { + vTaskDelay(pdMS_TO_TICKS(10)); + if (!sensor_ready) + { + err = dps310_is_ready_for_sensor(&dev, &sensor_ready); + if (err != ESP_OK) + { + goto fail; + } + } + + if (!coef_ready) + { + err = dps310_is_ready_for_coef(&dev, &coef_ready); + if (err != ESP_OK) + { + goto fail; + } + } + } while (!sensor_ready || !coef_ready); + + /* read COEF once, which is used to compensate the raw value. The COEF + * values are kept in the device descriptor. + */ + err = dps310_get_coef(&dev); + if (err != ESP_OK) + { + goto fail; + } + + /* Flush FIFO */ + ESP_LOGI(TAG, "Flushing FIFO"); + err = dps310_flush_fifo(&dev); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_flush_fifo(): %s", esp_err_to_name(err)); + goto fail; + } + + /* Temperature and pressure background mode + * + * The sensor continuously measures temperature and pressure. The results + * are kept in FIFO. The FIFO has 32 slots for measurement results. + * + * `dps310_set_mode(&dev, DPS310_MODE_BACKGROUND_ALL)` may be used if you + * prefer. + */ + ESP_LOGI(TAG, "Setting background measurement mode"); + err = dps310_backgorund_start(&dev, DPS310_MODE_BACKGROUND_ALL); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_set_mode(): %s", esp_err_to_name(err)); + goto fail; + } + err = dps310_get_mode(&dev, &mode); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_get_mode(): %s", esp_err_to_name(err)); + goto fail; + } + + if (mode != DPS310_MODE_BACKGROUND_ALL) + { + ESP_LOGE(TAG, "mode is not DPS310_MODE_BACKGROUND_ALL"); + goto fail; + } + + ESP_LOGI(TAG, "Starting the loop"); + while (1) { + + vTaskDelay(pdMS_TO_TICKS(1)); + + /* Skip when FIFO is empty, read FIFO when FIFO is not empty */ + err = dps310_is_fifo_empty(&dev, &fifo_is_empty); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_is_fifo_empty(): %s", esp_err_to_name(err)); + goto fail; + } + if (fifo_is_empty) + { + continue; + } + + err = dps310_read_fifo(&dev, &measurement); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_fifo(): %s", esp_err_to_name(err)); + goto fail; + + } + + /* In the log output, there should be two pressure measurements, + * following one temperature measurement. + */ + switch (measurement.type) + { + case DPS310_MEASUREMENT_TEMPERATURE: + ESP_LOGI(TAG, "temperature: %0.2f °C", measurement.result); + break; + ;; + case DPS310_MEASUREMENT_PRESSURE: + ESP_LOGI(TAG, "pressure: %0.2f Pa", measurement.result); + break; + ;; + + /* we are sure there should be a measurement result in FIFO + * because dps310_is_fifo_empty() returned false. but when FIFO is + * empty, the result is DPS310_MEASUREMENT_EMPTY type. + */ + case DPS310_MEASUREMENT_EMPTY: + ESP_LOGI(TAG, "FIFO is empty"); + break; + ;; + + /* should not happen */ + default: + ESP_LOGE(TAG, "Unknown dps310_fifo_measurement_type_t: %i", measurement.type); + goto fail; + ;; + } + } + +fail: + + /* stop background measurement. + * + * ignore returned value here as it would probably fail depending on the + * nature of the previous error, and there is nothing we can do. + */ + ESP_LOGI(TAG, "Stopping background measurement"); + (void)dps310_backgorund_stop(&dev); + + /* free the resources. + * + * ignore returned value here as well. + */ + (void)dps310_free_desc(&dev); + +init_fail: + ESP_LOGE(TAG, "Halting due to error"); + while (1) { + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +void app_main() +{ + xTaskCreatePinnedToCore(dps310_task, TAG, configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/dps310/background/sdkconfig.defaults.esp8266 b/examples/dps310/background/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/dps310/background/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/dps310/default/CMakeLists.txt b/examples/dps310/default/CMakeLists.txt new file mode 100644 index 00000000..a61287c1 --- /dev/null +++ b/examples/dps310/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-dps310-default) diff --git a/examples/dps310/default/Makefile b/examples/dps310/default/Makefile new file mode 100644 index 00000000..7fcd3c6c --- /dev/null +++ b/examples/dps310/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-dps310-default + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/dps310/default/README.md b/examples/dps310/default/README.md new file mode 100644 index 00000000..dc50e388 --- /dev/null +++ b/examples/dps310/default/README.md @@ -0,0 +1,106 @@ +# Example for `dps310` driver + +## What it does + +The example application initializes `DPS310` device. It reads temperature and +pressure in a loop. + +## Wiring + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | + +## Notes + +The default I2C address, which is used in this example, is `0x77`. Change the +address under `Example configuration` by `idf.py menuconfig`. + +## Example output + +```console +ets Jun 8 2016 00:22:57 + +rst:0x1 (POWERON_RESET),boot:0x12 (SPI_FAST_FLASH_BOOT) +configsip: 0, SPIWP:0xee +clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 +mode:DIO, clock div:2 +load:0x3fff0030,len:6664 +load:0x40078000,len:14848 +load:0x40080400,len:3792 +0x40080400: _init at ??:? + +entry 0x40080694 +I (27) boot: ESP-IDF v4.4.2-dirty 2nd stage bootloader +I (27) boot: compile time 18:31:58 +I (27) boot: chip revision: 1 +I (30) boot_comm: chip revision: 1, min. bootloader chip revision: 0 +I (37) boot.esp32: SPI Speed : 40MHz +I (42) boot.esp32: SPI Mode : DIO +I (46) boot.esp32: SPI Flash Size : 2MB +I (51) boot: Enabling RNG early entropy source... +I (56) boot: Partition Table: +I (60) boot: ## Label Usage Type ST Offset Length +I (67) boot: 0 nvs WiFi data 01 02 00009000 00006000 +I (75) boot: 1 phy_init RF data 01 01 0000f000 00001000 +I (82) boot: 2 factory factory app 00 00 00010000 00100000 +I (90) boot: End of partition table +I (94) boot_comm: chip revision: 1, min. application chip revision: 0 +I (101) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=0a12ch ( 41260) map +I (124) esp_image: segment 1: paddr=0001a154 vaddr=3ffb0000 size=023a0h ( 9120) load +I (128) esp_image: segment 2: paddr=0001c4fc vaddr=40080000 size=03b1ch ( 15132) load +I (137) esp_image: segment 3: paddr=00020020 vaddr=400d0020 size=18ac8h (101064) map +I (175) esp_image: segment 4: paddr=00038af0 vaddr=40083b1c size=08eb4h ( 36532) load +I (191) esp_image: segment 5: paddr=000419ac vaddr=50000000 size=00010h ( 16) load +I (197) boot: Loaded app from partition at offset 0x10000 +I (197) boot: Disabling RNG early entropy source... +I (211) cpu_start: Pro cpu up. +I (211) cpu_start: Starting app cpu, entry point is 0x40081108 +0x40081108: call_start_cpu1 at /usr/home/trombik/github/trombik/esp-idf/components/esp_system/port/cpu_start.c:160 + +I (0) cpu_start: App cpu up. +I (225) cpu_start: Pro cpu start user code +I (225) cpu_start: cpu freq: 160000000 +I (225) cpu_start: Application information: +I (230) cpu_start: Project name: example-dps310-default +I (236) cpu_start: App version: 0.9.1-61-g6ff7b98 +I (242) cpu_start: Compile time: Oct 30 2022 18:32:16 +I (248) cpu_start: ELF file SHA256: 6fd6da770ef72528... +I (254) cpu_start: ESP-IDF: v4.4.2-dirty +I (259) heap_init: Initializing. RAM available for dynamic allocation: +I (266) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM +I (272) heap_init: At 3FFB2CE8 len 0002D318 (180 KiB): DRAM +I (279) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM +I (285) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM +I (291) heap_init: At 4008C9D0 len 00013630 (77 KiB): IRAM +I (299) spi_flash: detected chip: generic +I (302) spi_flash: flash io: dio +W (306) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. +I (320) cpu_start: Starting scheduler on PRO CPU. +I (0) cpu_start: Starting scheduler on APP CPU. +I (0) dps310_example_default: Initializing I2C +I (10) dps310_example_default: Initializing the device descriptor +I (10) dps310_example_default: Initializing the device +I (80) dps310_example_default: Waiting for the sensor to be ready for measurement +I (90) dps310_example_default: Starting the loop +I (90) dps310_example_default: Setting manual temperature measurement mode +I (90) dps310_example_default: Waiting for the temperature value to be ready +I (310) dps310_example_default: Temperature: 31.32 °C +I (310) dps310_example_default: Setting manual pressure measurement mode +I (310) dps310_example_default: Waiting for the pressure value to be ready +I (530) dps310_example_default: Pressure: 100742.27 Pa +I (1530) dps310_example_default: Setting manual temperature measurement mode +I (1530) dps310_example_default: Waiting for the temperature value to be ready +I (1740) dps310_example_default: Temperature: 31.29 °C +I (1740) dps310_example_default: Setting manual pressure measurement mode +I (1740) dps310_example_default: Waiting for the pressure value to be ready +I (1960) dps310_example_default: Pressure: 100742.00 Pa +I (2960) dps310_example_default: Setting manual temperature measurement mode +I (2960) dps310_example_default: Waiting for the temperature value to be ready +I (3170) dps310_example_default: Temperature: 31.29 °C +... +``` diff --git a/examples/dps310/default/main/CMakeLists.txt b/examples/dps310/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/dps310/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/dps310/default/main/Kconfig.projbuild b/examples/dps310/default/main/Kconfig.projbuild new file mode 100644 index 00000000..eb6ce643 --- /dev/null +++ b/examples/dps310/default/main/Kconfig.projbuild @@ -0,0 +1,22 @@ +menu "Example configuration" + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. + config EXAMPLE_I2C_ADDRESS + hex "I2C address" + default 0x77 + help + I2C address of DPS310. When SDO is pulled low, 0x76. When pulled high, 0x77. +endmenu diff --git a/examples/dps310/default/main/component.mk b/examples/dps310/default/main/component.mk new file mode 100644 index 00000000..004b18e6 --- /dev/null +++ b/examples/dps310/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . diff --git a/examples/dps310/default/main/main.c b/examples/dps310/default/main/main.c new file mode 100644 index 00000000..28a66302 --- /dev/null +++ b/examples/dps310/default/main/main.c @@ -0,0 +1,203 @@ +/* + * This example code is in the Public Domain. + */ + +/* standard headers */ +#include +#include +#include + +/* esp-idf headers */ +#include +#include +#include +#include +#include + +#define I2C_PORT 0 + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +static const char *TAG = "dps310_example_default"; + +void dps310_task(void *pvParameters) +{ + bool sensor_ready = false; + bool temperature_ready = false; + bool pressure_ready = false; + bool coef_ready = false; + float temperature = 0; + float pressure = 0; + esp_err_t err = ESP_FAIL; + + /* Initialize dps310_config_t with DPS310_CONFIG_DEFAULT() macro, which + * sets default values. + */ + dps310_config_t config = DPS310_CONFIG_DEFAULT(); + + /* Create and initialize the device descriptor with zero */ + dps310_t dev; + memset(&dev, 0, sizeof(dps310_t)); + + /* Modify dps310_config_t */ + config.tmp_oversampling = DPS310_TMP_PRC_128; + config.pm_oversampling = DPS310_PM_PRC_128; + + /* Initialize the I2C */ + ESP_LOGI(TAG, "Initializing I2C"); + err = i2cdev_init(); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "i2cdev_init(): %s", esp_err_to_name(err)); + goto fail; + } + + /* Initialize the device descriptor */ + ESP_LOGI(TAG, "Initializing the device descriptor"); + err = dps310_init_desc(&dev, CONFIG_EXAMPLE_I2C_ADDRESS, I2C_PORT, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_init_desc(): %s", esp_err_to_name(err)); + goto fail; + } + + /* Initialize the device. */ + ESP_LOGI(TAG, "Initializing the device"); + err = dps310_init(&dev, &config); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_init(): %s", esp_err_to_name(err)); + goto fail; + } + + /* ensure the sensor is ready and coefficients, or COEF, are also ready. + * */ + ESP_LOGI(TAG, "Waiting for the sensor to be ready for measurement"); + do + { + vTaskDelay(pdMS_TO_TICKS(10)); + if (!sensor_ready) + { + err = dps310_is_ready_for_sensor(&dev, &sensor_ready); + if (err != ESP_OK) + { + goto fail; + } + } + + if (!coef_ready) + { + err = dps310_is_ready_for_coef(&dev, &coef_ready); + if (err != ESP_OK) + { + goto fail; + } + } + } while (!sensor_ready || !coef_ready); + + /* read COEF once, which is used to compensate the raw value. The COEF + * values are kept in the device descriptor. + */ + err = dps310_get_coef(&dev); + if (err != ESP_OK) + { + goto fail; + } + + ESP_LOGI(TAG, "Starting the loop"); + while (1) { + + /* Temperature command mode. + * + * The sensor measures temperature once, stores the raw value in a + * resister, and sets TMP_RDY bit. The sensor goes back to standby mode + * after measurement. + */ + ESP_LOGI(TAG, "Setting manual temperature measurement mode"); + err = dps310_set_mode(&dev, DPS310_MODE_COMMAND_TEMPERATURE); + if (err != ESP_OK) + { + goto fail; + } + + /* wait for the result by polling TMP_RDY bit */ + ESP_LOGI(TAG, "Waiting for the temperature value to be ready"); + do + { + vTaskDelay(pdMS_TO_TICKS(10)); + err = dps310_is_ready_for_temp(&dev, &temperature_ready); + if (err != ESP_OK) + { + goto fail; + } + } while (!temperature_ready); + + /* Read the result of temperature measurement */ + err = dps310_read_temp(&dev, &temperature); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_temp(): %s", esp_err_to_name(err)); + goto fail; + } + ESP_LOGI(TAG, "Temperature: %0.2f °C", temperature); + + /* Pressure command mode + * + * The sensor measures pressure once, stores the raw value in a resister, + * and sets PRS_RDY bit. The sensor goes back to standby mode after + * measurement. + */ + ESP_LOGI(TAG, "Setting manual pressure measurement mode"); + err = dps310_set_mode(&dev, DPS310_MODE_COMMAND_PRESSURE); + if (err != ESP_OK) + { + goto fail; + } + + /* wait for the result by polling PRS_RDY bit */ + ESP_LOGI(TAG, "Waiting for the pressure value to be ready"); + do + { + vTaskDelay(pdMS_TO_TICKS(10)); + err = dps310_is_ready_for_pressure(&dev, &pressure_ready); + if (err != ESP_OK) + { + goto fail; + } + } while (!pressure_ready); + + /* Read the result of pressure measurement, and compensate the result with + * temperature and COEF. + * + * Note that dps310_read_pressure() reads temperature *and* pressure + * values for compensation, including oversampling rates. + * + * This implies: + * + * * temperature measurement must be done brefore dps310_read_pressure() + * at least once. + * * the function is slow. + */ + err = dps310_read_pressure(&dev, &pressure); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "dps310_read_pressure(): %s", esp_err_to_name(err)); + goto fail; + } + ESP_LOGI(TAG, "Pressure: %0.2f Pa", pressure); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + +fail: + ESP_LOGE(TAG, "Halting due to error"); + while (1) { + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +void app_main() +{ + xTaskCreatePinnedToCore(dps310_task, TAG, configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/dps310/default/sdkconfig.defaults b/examples/dps310/default/sdkconfig.defaults new file mode 100644 index 00000000..757c0adc --- /dev/null +++ b/examples/dps310/default/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y diff --git a/examples/dps310/default/sdkconfig.defaults.esp8266 b/examples/dps310/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/dps310/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/ds18x20/multi/README.md b/examples/ds18x20/multi/README.md index 251d7c79..10352313 100644 --- a/examples/ds18x20/multi/README.md +++ b/examples/ds18x20/multi/README.md @@ -1,9 +1,5 @@ # Example for `ds18x20` driver -## Datasheet - -* [DS18S20 High-Precision 1-Wire Digital Thermometer](https://datasheets.maximintegrated.com/en/ds/DS18S20.pdf) - ## What it does The example configures one or `CONFIG_EXAMPLE_DS18X20_MAX_SENSORS` of @@ -24,6 +20,6 @@ recommended instead! Connect `DQ` pin to `CONFIG_EXAMPLE_ONEWIRE_GPIO`. -| Name | Description | Defaults | -|------|-------------|----------| -| `CONFIG_EXAMPLE_ONEWIRE_GPIO` | GPIO Number of 1-Wire bus, or `DQ` | "4" for `esp8266`, "18" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| Name | Description | Defaults | +|------|-------------|-----------------------------------------------------------------------------------| +| `CONFIG_EXAMPLE_ONEWIRE_GPIO` | GPIO Number of 1-Wire bus, or `DQ` | "4" for `esp8266`, "18" for `esp32c3`, "17" for `esp32`, `esp32s2`, and `esp32s3` | diff --git a/examples/ds18x20/multi/main/main.c b/examples/ds18x20/multi/main/main.c index 6e598b6e..34b1c526 100644 --- a/examples/ds18x20/multi/main/main.c +++ b/examples/ds18x20/multi/main/main.c @@ -13,9 +13,25 @@ static const uint32_t LOOP_DELAY_MS = 500; static const char *TAG = "ds18x20_test"; +static const char *sensor_type(uint8_t family_id) +{ + switch (family_id) + { + case DS18X20_FAMILY_DS18S20: + return "DS18S20"; + case DS18X20_FAMILY_DS1822: + return "DS1822"; + case DS18X20_FAMILY_DS18B20: + return "DS18B20"; + case DS18X20_FAMILY_MAX31850: + return "MAX31850"; + } + return "Unknown"; +} + void ds18x20_test(void *pvParameter) { - ds18x20_addr_t addrs[MAX_SENSORS]; + onewire_addr_t addrs[MAX_SENSORS]; float temps[MAX_SENSORS]; size_t sensor_count = 0; @@ -76,7 +92,7 @@ void ds18x20_test(void *pvParameter) // example. See sdkconfig.defaults.esp8266 ESP_LOGI(TAG, "Sensor %08" PRIx32 "%08" PRIx32 " (%s) reports %.3f°C (%.3f°F)", (uint32_t)(addrs[j] >> 32), (uint32_t)addrs[j], - (addrs[j] & 0xff) == DS18B20_FAMILY_ID ? "DS18B20" : "DS18S20", + sensor_type(addrs[j]), temp_c, temp_f); } diff --git a/examples/ds18x20/single/README.md b/examples/ds18x20/single/README.md index f8b803b4..22651e2b 100644 --- a/examples/ds18x20/single/README.md +++ b/examples/ds18x20/single/README.md @@ -1,12 +1,8 @@ # Example for `ds18x20` driver -## Datasheet - -* [DS18S20 High-Precision 1-Wire Digital Thermometer](https://datasheets.maximintegrated.com/en/ds/DS18S20.pdf) - ## What it does -The example configures a single `ds18x20` device with an internal pullup on +The example configures a single `ds18x20` device with an internal pull-up on `CONFIG_EXAMPLE_ONEWIRE_GPIO` (see below for defaults). You need to set `CONFIG_EXAMPLE_DS18X20_ADDR` to your own sensors' address. Use `menuconfig` to change the default values under `Example configuration`. @@ -26,6 +22,6 @@ recommended instead! Connect `DQ` pin to `CONFIG_EXAMPLE_ONEWIRE_GPIO`. -| Name | Description | Defaults | -|------|-------------|----------| -| `CONFIG_EXAMPLE_ONEWIRE_GPIO` | GPIO Number of 1-Wire bus, or `DQ` | "4" for `esp8266`, "18" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| Name | Description | Defaults | +|------|-------------|------------------------------------------------------------------------------------| +| `CONFIG_EXAMPLE_ONEWIRE_GPIO` | GPIO Number of 1-Wire bus, or `DQ` | "4" for `esp8266`, "18" for `esp32c3`, "17" for `esp32`, `esp32s2`, and `esp32s3` | diff --git a/examples/ds18x20/single/main/main.c b/examples/ds18x20/single/main/main.c index d9b0e41c..c50e2f28 100644 --- a/examples/ds18x20/single/main/main.c +++ b/examples/ds18x20/single/main/main.c @@ -9,7 +9,7 @@ static const gpio_num_t SENSOR_GPIO = CONFIG_EXAMPLE_ONEWIRE_GPIO; // Use address of your own sensor here! // You can find out the address of your sensor by running ds18x20_multi example -static const ds18x20_addr_t SENSOR_ADDR = CONFIG_EXAMPLE_DS18X20_ADDR; +static const onewire_addr_t SENSOR_ADDR = CONFIG_EXAMPLE_DS18X20_ADDR; static const char *TAG = "ds18x20_test"; diff --git a/examples/icm42670/default/CMakeLists.txt b/examples/icm42670/default/CMakeLists.txt new file mode 100644 index 00000000..7f1be0b6 --- /dev/null +++ b/examples/icm42670/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-icm42670-simple) diff --git a/examples/icm42670/default/Makefile b/examples/icm42670/default/Makefile new file mode 100644 index 00000000..0524e3b1 --- /dev/null +++ b/examples/icm42670/default/Makefile @@ -0,0 +1,7 @@ +#V := 1 +PROJECT_NAME := example-icm42670-simple + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/icm42670/default/README.md b/examples/icm42670/default/README.md new file mode 100644 index 00000000..0ec0b02b --- /dev/null +++ b/examples/icm42670/default/README.md @@ -0,0 +1,23 @@ +# Example for `icm42670` driver + +## What it does + +This example shows the basic configuration and initialization of the ICM42670 IMU and prints raw accelerometer and gyro values. + +Optional configuration of output data rate (ODR), full scale range (FSR) and low pass filters (LPF) is shown. + + +## Wiring + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "8" (`esp32c3`-based ESP-RS board), "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "10" (`esp32c3`-based ESP-RS board), "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | + +## Notes + +Choose I2C address under `Example configuration` in `menuconfig`. The default is +`ICM42670_I2C_ADDR_GND` (0x68). diff --git a/examples/icm42670/default/main/CMakeLists.txt b/examples/icm42670/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/icm42670/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/icm42670/default/main/Kconfig.projbuild b/examples/icm42670/default/main/Kconfig.projbuild new file mode 100644 index 00000000..8db6a14f --- /dev/null +++ b/examples/icm42670/default/main/Kconfig.projbuild @@ -0,0 +1,36 @@ +menu "Example configuration" + choice EXAMPLE_I2C_ADDRESS + prompt "Select I2C address" + default EXAMPLE_I2C_ADDRESS_GND + help + Select I2C address. + + config EXAMPLE_I2C_ADDRESS_GND + bool "ICM42670_I2C_ADDR_GND (0x68)" + config EXAMPLE_I2C_ADDRESS_VCC + bool "ICM42670_I2C_ADDR_VCC (0x69)" + endchoice + + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. + + config EXAMPLE_INT_INPUT_PIN + int "Interrupt Input Pin GPIO Number" + default 0 + help + GPIO number for Interrupt Input Pin + +endmenu diff --git a/examples/icm42670/default/main/component.mk b/examples/icm42670/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/icm42670/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/icm42670/default/main/main.c b/examples/icm42670/default/main/main.c new file mode 100644 index 00000000..b79f6fc3 --- /dev/null +++ b/examples/icm42670/default/main/main.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include +#include + +static const char *TAG = "icm42670"; + +#define PORT 0 +#if defined(CONFIG_EXAMPLE_I2C_ADDRESS_GND) +#define I2C_ADDR ICM42670_I2C_ADDR_GND +#endif +#if defined(CONFIG_EXAMPLE_I2C_ADDRESS_VCC) +#define I2C_ADDR ICM42670_I2C_ADDR_VCC +#endif + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +/* Find gpio definitions in sdkconfig */ + +void icm42670_test(void *pvParameters) +{ + // init device descriptor and device + icm42670_t dev = { 0 }; + ESP_ERROR_CHECK( + icm42670_init_desc(&dev, I2C_ADDR, PORT, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(icm42670_init(&dev)); + + // enable accelerometer and gyro in low-noise (LN) mode + ESP_ERROR_CHECK(icm42670_set_gyro_pwr_mode(&dev, ICM42670_GYRO_ENABLE_LN_MODE)); + ESP_ERROR_CHECK(icm42670_set_accel_pwr_mode(&dev, ICM42670_ACCEL_ENABLE_LN_MODE)); + + /* OPTIONAL */ + // enable low-pass-filters on accelerometer and gyro + ESP_ERROR_CHECK(icm42670_set_accel_lpf(&dev, ICM42670_ACCEL_LFP_53HZ)); + ESP_ERROR_CHECK(icm42670_set_gyro_lpf(&dev, ICM42670_GYRO_LFP_53HZ)); + // set output data rate (ODR) + ESP_ERROR_CHECK(icm42670_set_accel_odr(&dev, ICM42670_ACCEL_ODR_200HZ)); + ESP_ERROR_CHECK(icm42670_set_gyro_odr(&dev, ICM42670_GYRO_ODR_200HZ)); + // set full scale range (FSR) + ESP_ERROR_CHECK(icm42670_set_accel_fsr(&dev, ICM42670_ACCEL_RANGE_16G)); + ESP_ERROR_CHECK(icm42670_set_gyro_fsr(&dev, ICM42670_GYRO_RANGE_2000DPS)); + + // read temperature sensor value once + float temperature; + ESP_ERROR_CHECK(icm42670_read_temperature(&dev, &temperature)); + ESP_LOGI(TAG, "Temperature reading: %f", temperature); + + int16_t raw_reading; + uint8_t data_register; + + /* select which acceleration or gyro value should be read: */ + // data_register = ICM42670_REG_ACCEL_DATA_X1; + // data_register = ICM42670_REG_ACCEL_DATA_Y1; + // data_register = ICM42670_REG_ACCEL_DATA_Z1; + data_register = ICM42670_REG_GYRO_DATA_X1; + // data_register = ICM42670_REG_GYRO_DATA_Y1; + // data_register = ICM42670_REG_GYRO_DATA_Z1; + + // now poll selected accelerometer or gyro raw value directly from registers + while (1) + { + ESP_ERROR_CHECK(icm42670_read_raw_data(&dev, data_register, &raw_reading)); + + ESP_LOGI(TAG, "Raw accelerometer / gyro reading: %d", raw_reading); + + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + + xTaskCreatePinnedToCore(icm42670_test, "icm42670_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/icm42670/wake-on-motion/CMakeLists.txt b/examples/icm42670/wake-on-motion/CMakeLists.txt new file mode 100644 index 00000000..5a6cf2d0 --- /dev/null +++ b/examples/icm42670/wake-on-motion/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-icm42670-wom) diff --git a/examples/icm42670/wake-on-motion/Makefile b/examples/icm42670/wake-on-motion/Makefile new file mode 100644 index 00000000..35475759 --- /dev/null +++ b/examples/icm42670/wake-on-motion/Makefile @@ -0,0 +1,7 @@ +#V := 1 +PROJECT_NAME := example-icm42670-wom + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/icm42670/wake-on-motion/README.md b/examples/icm42670/wake-on-motion/README.md new file mode 100644 index 00000000..0b1a3a4f --- /dev/null +++ b/examples/icm42670/wake-on-motion/README.md @@ -0,0 +1,25 @@ +# Wake-on-Motion (WoM) Example for `icm42670` driver + +## What it does + +This example configures the ICM42670 IMU to send an interrupt signal via a defined pin to the ESP if motion above a certain threshold is detected. This threshold (0.39*g by default) as well as the axes along movements are detected (all three axes by default) are configurable. Whether a WoM event was triggered or not is continuously checked in the main loop. + +If the user moves the device, the "WoM event detected" state changes from false to true. + +This Wake-on-Motion (WoM) feature can be used to wake the MCU from deep-sleep or perform any other operation if motion is detected. The WoM detection is performed in a low-power mode of the IMU. + +## Wiring + +Connect `SCL`, `SDA` and `INT` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "8" (`esp32c3`-based ESP-RS board), "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "10" (`esp32c3`-based ESP-RS board), "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_INT_INPUT_PIN` | GPIO number for `INT` | "0"| + +## Notes + +Choose I2C address under `Example configuration` in `menuconfig`. The default is +`ICM42670_I2C_ADDR_GND` (0x68). diff --git a/examples/icm42670/wake-on-motion/main/CMakeLists.txt b/examples/icm42670/wake-on-motion/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/icm42670/wake-on-motion/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/icm42670/wake-on-motion/main/Kconfig.projbuild b/examples/icm42670/wake-on-motion/main/Kconfig.projbuild new file mode 100644 index 00000000..8db6a14f --- /dev/null +++ b/examples/icm42670/wake-on-motion/main/Kconfig.projbuild @@ -0,0 +1,36 @@ +menu "Example configuration" + choice EXAMPLE_I2C_ADDRESS + prompt "Select I2C address" + default EXAMPLE_I2C_ADDRESS_GND + help + Select I2C address. + + config EXAMPLE_I2C_ADDRESS_GND + bool "ICM42670_I2C_ADDR_GND (0x68)" + config EXAMPLE_I2C_ADDRESS_VCC + bool "ICM42670_I2C_ADDR_VCC (0x69)" + endchoice + + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. + + config EXAMPLE_INT_INPUT_PIN + int "Interrupt Input Pin GPIO Number" + default 0 + help + GPIO number for Interrupt Input Pin + +endmenu diff --git a/examples/icm42670/wake-on-motion/main/component.mk b/examples/icm42670/wake-on-motion/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/icm42670/wake-on-motion/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/icm42670/wake-on-motion/main/main.c b/examples/icm42670/wake-on-motion/main/main.c new file mode 100644 index 00000000..00755f56 --- /dev/null +++ b/examples/icm42670/wake-on-motion/main/main.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include +#include + +static const char *TAG = "icm42670"; + +#define PORT 0 +#if defined(CONFIG_EXAMPLE_I2C_ADDRESS_GND) +#define I2C_ADDR ICM42670_I2C_ADDR_GND +#endif +#if defined(CONFIG_EXAMPLE_I2C_ADDRESS_VCC) +#define I2C_ADDR ICM42670_I2C_ADDR_VCC +#endif + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +/* Find gpio definitions in sdkconfig */ + +void icm42670_wom_test(void *pvParameters) +{ + // config IO0 as input, pull-up enabled + const gpio_config_t io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_INPUT, + .pin_bit_mask = BIT(CONFIG_EXAMPLE_INT_INPUT_PIN), + .pull_down_en = 0, + .pull_up_en = 0, + }; + gpio_config(&io_conf); + + // init device descriptor and device + icm42670_t dev = { 0 }; + ESP_ERROR_CHECK(icm42670_init_desc(&dev, I2C_ADDR, PORT, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(icm42670_init(&dev)); + + /* config a Wake-On-Motion (WoM) interrupt on ICM42670-pin 2 + * - interrupt pin on ICM42670 = 2 + * - signal level is latched + * - signal is fully driven (push/pull) + * - polarity is active high + */ + const uint8_t int_pin = 2; + const icm42670_int_config_t int_config = { + .mode = ICM42670_INT_MODE_LATCHED, + .drive = ICM42670_INT_DRIVE_PUSH_PULL, + .polarity = ICM42670_INT_POLARITY_ACTIVE_HIGH, + }; + ESP_ERROR_CHECK(icm42670_config_int_pin(&dev, int_pin, int_config)); + + // enable interrupt sources (in this case all three axes) + icm42670_int_source_t sources = {false}; + sources.wom_z = true; + sources.wom_y = true; + sources.wom_z = true; + ESP_ERROR_CHECK(icm42670_set_int_sources(&dev, int_pin, sources)); + + /* configure Wake-On-Motion (WoM): + * - first exceeding of the threshold is considered as WoM event + * - the WoM event sources are logically linked by a OR + * - the reference measurement for the threshold is taken at startup + * - the threshold is set to 100, which corresponds to 0.39*g + * (WoM thresholds are expressed in fixed “mg” independent of the selected Range [0g : 1g] + * Resolution 1g/256=~3.9 mg) + */ + const icm42670_wom_config_t wom_config = { + .trigger = ICM42670_WOM_INT_DUR_FIRST, + .logical_mode = ICM42670_WOM_INT_MODE_ALL_OR, + .reference = ICM42670_WOM_MODE_REF_INITIAL, + .wom_y_threshold = 100, + .wom_z_threshold = 100, + .wom_x_threshold = 100, + }; + ESP_ERROR_CHECK(icm42670_config_wom(&dev, wom_config)); + + // set output-data-rate (ODR) and averaging (AVG) on accelerometer + ESP_ERROR_CHECK(icm42670_set_accel_odr(&dev, ICM42670_ACCEL_ODR_200HZ)); + ESP_ERROR_CHECK(icm42670_set_accel_avg(&dev, ICM42670_ACCEL_AVG_8X)); + + // disable gyro and enable accelerometer in low-power (LP) mode + ESP_ERROR_CHECK(icm42670_set_gyro_pwr_mode(&dev, ICM42670_GYRO_DISABLE)); + ESP_ERROR_CHECK(icm42670_set_low_power_clock(&dev, ICM42670_LP_CLK_WUO)); + ESP_ERROR_CHECK_WITHOUT_ABORT(icm42670_set_accel_pwr_mode(&dev, ICM42670_ACCEL_ENABLE_LP_MODE)); + + //enable WoM + ESP_ERROR_CHECK(icm42670_enable_wom(&dev, true)); + + // now poll intterupt pin for changes + while(1) + { + ESP_LOGI(TAG, "WoM event detected: %s", gpio_get_level(CONFIG_EXAMPLE_INT_INPUT_PIN) ? "true" : "false"); + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + + xTaskCreatePinnedToCore(icm42670_wom_test, "icm42670_wom_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} + diff --git a/examples/ina219/default/main/Kconfig.projbuild b/examples/ina219/default/main/Kconfig.projbuild index 5c48b995..da17df98 100644 --- a/examples/ina219/default/main/Kconfig.projbuild +++ b/examples/ina219/default/main/Kconfig.projbuild @@ -1,10 +1,4 @@ menu "Example configuration" - config EXAMPLE_MAX_CURRENT - int "Expected max current in Ampare to measure" - default 5 - help - Expected max current in Ampare. Note that the value must be int, not float. - config EXAMPLE_SHUNT_RESISTOR_MILLI_OHM int "Shunt resistor value in milliohm)" default 100 diff --git a/examples/ina219/default/main/main.c b/examples/ina219/default/main/main.c index eee127c2..fe526420 100644 --- a/examples/ina219/default/main/main.c +++ b/examples/ina219/default/main/main.c @@ -27,7 +27,7 @@ void task(void *pvParameters) ESP_LOGI(TAG, "Calibrating INA219"); - ESP_ERROR_CHECK(ina219_calibrate(&dev, (float)CONFIG_EXAMPLE_MAX_CURRENT, (float)CONFIG_EXAMPLE_SHUNT_RESISTOR_MILLI_OHM / 1000.0f)); + ESP_ERROR_CHECK(ina219_calibrate(&dev, (float)CONFIG_EXAMPLE_SHUNT_RESISTOR_MILLI_OHM / 1000.0f)); float bus_voltage, shunt_voltage, current, power; diff --git a/examples/l3gx/default/CMakeLists.txt b/examples/l3gx/default/CMakeLists.txt new file mode 100644 index 00000000..1e8da112 --- /dev/null +++ b/examples/l3gx/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-l3gx) diff --git a/examples/l3gx/default/Makefile b/examples/l3gx/default/Makefile new file mode 100644 index 00000000..30bf4aaf --- /dev/null +++ b/examples/l3gx/default/Makefile @@ -0,0 +1,10 @@ +#V := 1 +PROJECT_NAME := example-l3gx + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +#ifdef CONFIG_IDF_TARGET_ESP8266 +EXCLUDE_COMPONENTS := max7219 mcp23x17 led_strip max31865 ls7366r max31855 ads130e08 +#endif + +include $(IDF_PATH)/make/project.mk diff --git a/examples/l3gx/default/README.md b/examples/l3gx/default/README.md new file mode 100644 index 00000000..8845be9e --- /dev/null +++ b/examples/l3gx/default/README.md @@ -0,0 +1,19 @@ +# Example for `l3gx` driver + +## What it does + +It shows L3Gx gyroscope(L3GD20/L3G4200D) sensor data in a loop. + +## Wiring + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | + +## Notes + +`CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL` must be `y` on `esp8266`. diff --git a/examples/l3gx/default/main/CMakeLists.txt b/examples/l3gx/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/l3gx/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/l3gx/default/main/Kconfig.projbuild b/examples/l3gx/default/main/Kconfig.projbuild new file mode 100644 index 00000000..3472199a --- /dev/null +++ b/examples/l3gx/default/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example configuration" + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. +endmenu diff --git a/examples/l3gx/default/main/component.mk b/examples/l3gx/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/l3gx/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/l3gx/default/main/main.c b/examples/l3gx/default/main/main.c new file mode 100644 index 00000000..19ec06b7 --- /dev/null +++ b/examples/l3gx/default/main/main.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include + +#define L3Gx_GYRO_I2C_ADDR 0x69 + +void gyro_test(void *pvParameters) +{ + l3gx_t l3gx_gyro; + l3gx_raw_data_t raw_data; + l3gx_data_t gyro_data; + bool gyro_ready; + + ESP_ERROR_CHECK(l3gx_init_desc(&l3gx_gyro,L3Gx_GYRO_I2C_ADDR,0,CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(l3gx_init(&l3gx_gyro)); + + /* OPTIONAL */ + ESP_ERROR_CHECK(l3gd20_set_scale(&l3gx_gyro,L3GX_SCALE_500)); + ESP_ERROR_CHECK(l3gd20_set_datarate_and_bandwith(&l3gx_gyro,L3GX_DRBW_800_30)); + + while (1) + { + if (l3gx_data_ready(&l3gx_gyro,&gyro_ready) == ESP_OK){ + if (l3gx_get_raw_data(&l3gx_gyro, &raw_data) == ESP_OK){ + l3gd20_raw_to_dps(&l3gx_gyro, &raw_data, &gyro_data); + + printf("Gyro data: raw[x=%6d y=%6d z=%6d] x=%.2fdps y=%.2fdps z=%.2fdps\n", + raw_data.x, raw_data.y, raw_data.z, + gyro_data.x, gyro_data.y, gyro_data.z); + } else { + printf("Could not read data from sensor\n"); + } + } + vTaskDelay(pdMS_TO_TICKS(500)); + } +} + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + xTaskCreate(gyro_test, "gyro_test", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); +} + diff --git a/examples/l3gx/default/sdkconfig.defaults.esp8266 b/examples/l3gx/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/l3gx/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/ls7366r/default/main/main.c b/examples/ls7366r/default/main/main.c index 3fc3037b..8bcc0bcc 100644 --- a/examples/ls7366r/default/main/main.c +++ b/examples/ls7366r/default/main/main.c @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -9,18 +8,16 @@ #include #include #include -#include - #define TEST_PIN CONFIG_EXAMPLE_PIN_NUM_TEST #define INTR_PIN CONFIG_EXAMPLE_PIN_NUM_INTR #define GPIO_OUTPUT_PIN_SEL (1ULL << TEST_PIN) #define GPIO_INPUT_PIN_SEL (1ULL << INTR_PIN) -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) -#define LS7366R_HOST SPI3_HOST +#ifdef HSPI_HOST +#define LS7366R_HOST HSPI_HOST #else -#define LS7366R_HOST VSPI_HOST +#define LS7366R_HOST SPI2_HOST #endif #define PIN_NUM_MISO CONFIG_EXAMPLE_PIN_NUM_MISO diff --git a/examples/lsm303/default/CMakeLists.txt b/examples/lsm303/default/CMakeLists.txt new file mode 100644 index 00000000..fff6ced4 --- /dev/null +++ b/examples/lsm303/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-lsm303) diff --git a/examples/lsm303/default/Makefile b/examples/lsm303/default/Makefile new file mode 100644 index 00000000..913895de --- /dev/null +++ b/examples/lsm303/default/Makefile @@ -0,0 +1,10 @@ +#V := 1 +PROJECT_NAME := example-lsm303 + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +#ifdef CONFIG_IDF_TARGET_ESP8266 +EXCLUDE_COMPONENTS := max7219 mcp23x17 led_strip max31865 ls7366r max31855 ads130e08 +#endif + +include $(IDF_PATH)/make/project.mk diff --git a/examples/lsm303/default/README.md b/examples/lsm303/default/README.md new file mode 100644 index 00000000..717bd140 --- /dev/null +++ b/examples/lsm303/default/README.md @@ -0,0 +1,19 @@ +# Example for `lsm303` driver + +## What it does + +It shows LSM303: 3-axis accelerometer and magnetometer sensor data in a loop. + +## Wiring + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | + +## Notes + +`CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL` must be `y` on `esp8266`. diff --git a/examples/lsm303/default/main/CMakeLists.txt b/examples/lsm303/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/lsm303/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/lsm303/default/main/Kconfig.projbuild b/examples/lsm303/default/main/Kconfig.projbuild new file mode 100644 index 00000000..3472199a --- /dev/null +++ b/examples/lsm303/default/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example configuration" + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. +endmenu diff --git a/examples/lsm303/default/main/component.mk b/examples/lsm303/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/lsm303/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/lsm303/default/main/main.c b/examples/lsm303/default/main/main.c new file mode 100644 index 00000000..e8f9c3e6 --- /dev/null +++ b/examples/lsm303/default/main/main.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +static const char *TAG = "IMU"; + +void lsm303_test(void *pvParameters) +{ + lsm303_t lsm303; + + lsm303_acc_raw_data_t acc_raw; + lsm303_acc_data_t acc; + + lsm303_mag_raw_data_t mag_raw; + lsm303_mag_data_t mag; + + bool acc_ready, mag_ready; + + ESP_ERROR_CHECK(lsm303_init_desc(&lsm303, LSM303_ADDR_ACC, LSM303_ADDR_MAG, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(lsm303_init(&lsm303)); + + /* OPTIONAL */ + ESP_ERROR_CHECK(lsm303_acc_set_config(&lsm303, LSM303_ACC_MODE_NORMAL, LSM303_ODR_100_HZ, LSM303_ACC_SCALE_2G)); + ESP_ERROR_CHECK(lsm303_mag_set_config(&lsm303, LSM303_MAG_MODE_CONT, LSM303_MAG_RATE_15, LSM303_MAG_GAIN_1_3)); + + while (1) + { + if (lsm303_acc_data_ready(&lsm303, &acc_ready) != ESP_OK) + { + ESP_LOGE(TAG, "Could not read acc"); + vTaskDelay(pdMS_TO_TICKS(1000)); + continue; + } + + if (lsm303_mag_data_ready(&lsm303, &mag_ready) != ESP_OK) + { + ESP_LOGE(TAG, "Could not read mag"); + vTaskDelay(pdMS_TO_TICKS(1000)); + continue; + } + + if (acc_ready) + { + if (lsm303_acc_get_raw_data(&lsm303, &acc_raw) == ESP_OK) + { + lsm303_acc_raw_to_g(&lsm303, &acc_raw, &acc); + + printf("Acc data: raw[x=%6d y=%6d z=%6d] x=%.2fg y=%.2fg z=%.2fg\n", acc_raw.x, acc_raw.y, acc_raw.z, acc.x, acc.y, acc.z); + } + else + { + printf("Could not read data from sensor\n"); + } + } + + if (acc_ready) + { + if (lsm303_mag_get_raw_data(&lsm303, &mag_raw) == ESP_OK) + { + lsm303_mag_raw_to_uT(&lsm303, &mag_raw, &mag); + + printf("Mag data: raw[x=%6d y=%6d z=%6d] x=%.2fuT y=%.2fuT z=%.2fuT\n", mag_raw.x, mag_raw.y, mag_raw.z, mag.x, mag.y, mag.z); + } + else + { + printf("Could not read data from sensor\n"); + } + } + + vTaskDelay(pdMS_TO_TICKS(500)); + } +} + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + xTaskCreate(lsm303_test, "lsm303_test", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); +} diff --git a/examples/lsm303/default/sdkconfig.defaults.esp8266 b/examples/lsm303/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/lsm303/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/max1704x/default/CMakeLists.txt b/examples/max1704x/default/CMakeLists.txt new file mode 100644 index 00000000..d2a8b06c --- /dev/null +++ b/examples/max1704x/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-max1704x) diff --git a/examples/max1704x/default/Makefile b/examples/max1704x/default/Makefile new file mode 100644 index 00000000..d561801c --- /dev/null +++ b/examples/max1704x/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-max1704x + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/max1704x/default/README.md b/examples/max1704x/default/README.md new file mode 100644 index 00000000..4857f64b --- /dev/null +++ b/examples/max1704x/default/README.md @@ -0,0 +1,16 @@ +# Example for `max1704x` driver + +## What it does + +The example configures a `max1704x` device to get battry voltage, state of charge, +and rate of change (MAX17048/9) in a loop. + +## Wiring + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | diff --git a/examples/max1704x/default/main/CMakeLists.txt b/examples/max1704x/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/max1704x/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/max1704x/default/main/Kconfig.projbuild b/examples/max1704x/default/main/Kconfig.projbuild new file mode 100644 index 00000000..3472199a --- /dev/null +++ b/examples/max1704x/default/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example configuration" + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. +endmenu diff --git a/examples/max1704x/default/main/component.mk b/examples/max1704x/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/max1704x/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/max1704x/default/main/main.c b/examples/max1704x/default/main/main.c new file mode 100644 index 00000000..bea3d33b --- /dev/null +++ b/examples/max1704x/default/main/main.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include + +static char *TAG = "main"; + +void test(void *pvParameters) +{ + esp_err_t r; + max1704x_t dev = {0 }; + max1704x_config_t config = { 0 }; + max1704x_status_t status = { 0 }; + uint16_t version = 0; + float voltage = 0; + float soc_percent = 0; + float rate_change = 0; + + /** + * Set up I2C bus to communicate with MAX1704X + */ + + dev.model = MAX17043_4; + + ESP_ERROR_CHECK(max1704x_init_desc(&dev, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(max1704x_quickstart(&dev)); + ESP_ERROR_CHECK(max1704x_get_version(&dev, &version)); + ESP_LOGI(TAG, "Version: %d\n", version); + + /** + * Get MAX1704X configuration + */ + ESP_LOGI(TAG, "--- MAX1704X config register ---"); + ESP_ERROR_CHECK(max1704x_get_config(&dev)); + ESP_LOGI(TAG, "Alert Status: %d", dev.config.alert_status); + ESP_LOGI(TAG, "Sleep Mode: %d", dev.config.sleep_mode); + ESP_LOGI(TAG, "SOC Change Alert Mode: %d", dev.config.soc_change_alert); + ESP_LOGI(TAG, "Empty Alert Threshold: %d%%", dev.config.empty_alert_thresh); + ESP_LOGI(TAG, "RCOMP Value: %d (%x)", dev.config.rcomp, dev.config.rcomp); + ESP_LOGI(TAG, "--- End Configuration ---\n"); + + // Change configuration settings + config.soc_change_alert = true; + config.empty_alert_thresh = 10; + ESP_LOGI(TAG, "Setting new MAX1704X configuration"); + ESP_ERROR_CHECK(max1704x_set_config(&dev, &config)); + + ESP_LOGI(TAG, "--- MAX1704X config register after updating configurations ---"); + ESP_ERROR_CHECK(max1704x_get_config(&dev)); + ESP_LOGI(TAG, "Alert Status: %d", dev.config.alert_status); + ESP_LOGI(TAG, "Sleep Mode: %d", dev.config.sleep_mode); + ESP_LOGI(TAG, "SOC Change Alert Mode: %d", dev.config.soc_change_alert); + ESP_LOGI(TAG, "Empty Alert Threshold: %d%%", dev.config.empty_alert_thresh); + ESP_LOGI(TAG, "RCOMP Value: %d (%x)", dev.config.rcomp, dev.config.rcomp); + ESP_LOGI(TAG, "--- End Configuration ---\n"); + + /** + * Get current MAX1704X status + */ + ESP_LOGI(TAG, "--- MAX1704X status register ---"); + ESP_ERROR_CHECK(max1704x_get_status(&dev)); + ESP_LOGI(TAG, "Reset Indicator: %d", dev.status.reset_indicator); + ESP_LOGI(TAG, "Voltage High Alert: %d", dev.status.voltage_high); + ESP_LOGI(TAG, "Voltage Low Alert: %d", dev.status.voltage_low); + ESP_LOGI(TAG, "Voltage Reset Alert: %d", dev.status.voltage_reset); + ESP_LOGI(TAG, "SOC Low Alert: %d", dev.status.soc_low); + ESP_LOGI(TAG, "SOC Change Alert: %d", dev.status.soc_change); + ESP_LOGI(TAG, "Voltage Reset Alert Enabled: %d", dev.status.vreset_alert); + ESP_LOGI(TAG, "--- End Status ---\n"); + + // Update MAX1704X status register to clear the reset indicator + ESP_LOGI(TAG, "Setting the status register to clear reset indicator"); + status.reset_indicator = false; + + ESP_ERROR_CHECK(max1704x_set_status(&dev, &status)); + ESP_LOGI(TAG, "--- MAX1704X status register after updating status register ---"); + ESP_ERROR_CHECK(max1704x_get_status(&dev)); + ESP_LOGI(TAG, "Reset Indicator: %d", dev.status.reset_indicator); + ESP_LOGI(TAG, "Voltage High Alert: %d", dev.status.voltage_high); + ESP_LOGI(TAG, "Voltage Low Alert: %d", dev.status.voltage_low); + ESP_LOGI(TAG, "Voltage Reset Alert: %d", dev.status.voltage_reset); + ESP_LOGI(TAG, "SOC Low Alert: %d", dev.status.soc_low); + ESP_LOGI(TAG, "SOC Change Alert: %d", dev.status.soc_change); + ESP_LOGI(TAG, "Voltage Reset Alert Enabled: %d", dev.status.vreset_alert); + ESP_LOGI(TAG, "--- End Status ---\n"); + + /** + * Get current MAX1704X voltage, SOC, and rate of change every 5 seconds + */ + + while (1) + { + r = max1704x_get_voltage(&dev, &voltage); + + if (r == ESP_OK) { + ESP_LOGI(TAG, "Voltage: %.2fV", voltage); + } + else + ESP_LOGI(TAG, "Error %d: %s", r, esp_err_to_name(r)); + + r = max1704x_get_soc(&dev, &soc_percent); + if (r == ESP_OK) { + ESP_LOGI(TAG, "SOC: %.2f%%", soc_percent); + } + else + ESP_LOGI(TAG, "Error %d: %s", r, esp_err_to_name(r)); + + r = max1704x_get_crate(&dev, &rate_change); + if (r == ESP_OK) { + ESP_LOGI(TAG, "SOC rate of change: %.2f%%", rate_change); + } + else + ESP_LOGI(TAG, "Error %d: %s", r, esp_err_to_name(r)); + + printf("\n"); + vTaskDelay(pdMS_TO_TICKS(5000)); + } +} + +void app_main() +{ + /** + * Initialize I2C bus + */ + ESP_ERROR_CHECK(i2cdev_init()); + + xTaskCreate(test, "test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL); +} diff --git a/examples/max1704x/default/sdkconfig.defaults.esp8266 b/examples/max1704x/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/max1704x/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/mcp23x17/mcp23017/main/main.c b/examples/mcp23x17/mcp23017/main/main.c index efdbd6d0..a10ac16a 100644 --- a/examples/mcp23x17/mcp23017/main/main.c +++ b/examples/mcp23x17/mcp23017/main/main.c @@ -1,19 +1,44 @@ -#include +#include #include #include +#include #include #include -#include + +static const char *TAG = "mcp23017_example"; + +static EventGroupHandle_t eg = NULL; +static mcp23x17_t dev = { 0 }; + +#define BIT_BUTTON_CHANGED BIT(0) static void IRAM_ATTR intr_handler(void *arg) { - printf("Interrupt!\n"); + // On interrupt set bit in event group + BaseType_t hp_task; + if (xEventGroupSetBitsFromISR(eg, BIT_BUTTON_CHANGED, &hp_task) != pdFAIL) +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + portYIELD_FROM_ISR(hp_task); +#else + portYIELD_FROM_ISR(); +#endif +} + +void button_handler(void *pvParameters) +{ + while (1) + { + // wait for BIT_BUTTON_CHANGED, clear it on exit + if (xEventGroupWaitBits(eg, BIT_BUTTON_CHANGED, pdTRUE, pdTRUE, portMAX_DELAY) != BIT_BUTTON_CHANGED) + continue; + // OK, we got this bit set + ESP_LOGI(TAG, "Button was pressed!"); + } } void test(void *pvParameters) { - mcp23x17_t dev; - memset(&dev, 0, sizeof(mcp23x17_t)); + eg = xEventGroupCreate(); ESP_ERROR_CHECK(mcp23x17_init_desc(&dev, CONFIG_EXAMPLE_I2C_ADDR, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); @@ -22,6 +47,10 @@ void test(void *pvParameters) // Setup interrupt on it mcp23x17_set_interrupt(&dev, 0, MCP23X17_INT_ANY_EDGE); + // Run button handler + xTaskCreate(button_handler, "button_handler", 4096, NULL, 5, NULL); + + // Setup GPIO interrupt gpio_set_direction(CONFIG_EXAMPLE_INTA_GPIO, GPIO_MODE_INPUT); gpio_set_intr_type(CONFIG_EXAMPLE_INTA_GPIO, GPIO_INTR_ANYEDGE); gpio_install_isr_service(0); @@ -44,4 +73,3 @@ void app_main() ESP_ERROR_CHECK(i2cdev_init()); xTaskCreate(test, "test", configMINIMAL_STACK_SIZE * 6, NULL, 5, NULL); } - diff --git a/examples/mp2660/default/CMakeLists.txt b/examples/mp2660/default/CMakeLists.txt new file mode 100644 index 00000000..998dc994 --- /dev/null +++ b/examples/mp2660/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-mp2660) diff --git a/examples/mp2660/default/Makefile b/examples/mp2660/default/Makefile new file mode 100644 index 00000000..edc9ef30 --- /dev/null +++ b/examples/mp2660/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-mp2660 + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/mp2660/default/README.md b/examples/mp2660/default/README.md new file mode 100644 index 00000000..17297b4a --- /dev/null +++ b/examples/mp2660/default/README.md @@ -0,0 +1,57 @@ +# Example for `mp2660` driver + +## What it does + +It reads all registers from IC + +## Wiring + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "22" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "21" for `esp32`, `esp32s2`, and `esp32s3` | + +## Example log + +```console +I (0) cpu_start: App cpu up. +I (137) cpu_start: Pro cpu start user code +I (137) cpu_start: cpu freq: 80000000 Hz +I (137) cpu_start: Application information: +I (138) cpu_start: Project name: Esp32.MP2660.I2C +I (143) cpu_start: App version: 0.0.1 +I (147) cpu_start: Compile time: Oct 9 2023 22:57:10 +I (152) cpu_start: ELF file SHA256: 8231edda675415ce... +I (157) cpu_start: ESP-IDF: v5.0.1 +I (161) cpu_start: Min chip rev: v0.0 +I (165) cpu_start: Max chip rev: v3.99 +I (169) cpu_start: Chip rev: v3.0 +I (173) heap_init: Initializing. RAM available for dynamic allocation: +I (179) heap_init: At 3FFAE6E0 len 0000F480 (61 KiB): DRAM +I (184) heap_init: At 3FFC16B0 len 0001E950 (122 KiB): DRAM +I (189) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM +I (195) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM +I (200) heap_init: At 4008BB98 len 00014468 (81 KiB): IRAM +I (207) spi_flash: detected chip: generic +I (209) spi_flash: flash io: dio +I (214) cpu_start: Starting scheduler on PRO CPU. +I (0) cpu_start: Starting scheduler on APP CPU. +E (00:00:03.084) i2cdev: Could not read from device [0x09 at 0]: 263 (ESP_ERR_TIMEOUT) +I (00:00:03.085) Main: Result reading fault register: ESP_ERR_TIMEOUT +E (00:00:03.088) i2cdev: Could not read from device [0x09 at 0]: -1 (ESP_FAIL) +I (00:00:03.095) Main: Result reading fault register: ESP_FAIL +I (00:00:03.101) Main: Result reading fault register: ESP_OK +I (00:00:03.106) Main: Result: 0000004f, EnHIZ: 0, VInMin3: 1, VInMin2: 0, VInMin1: 0, VInMin0: 1, IInLim_2: 1, IInLim_1: 1, IInLim_0: 1 +I (00:00:03.118) Main: Result: 00000004, UVLO 0: 0, UVLO 1: 0, UVLO 2: 1, CEB: 0, WatchdogTimer: 0, RegisterReset: 0 +I (00:00:03.128) Main: Result: 0000000e, ICC 0: 0, ICC 1: 1, ICC 2: 1, ICC 3: 1, ICC 4: 0 +I (00:00:03.136) Main: Result: 0000004a, DSCHG 0: 0, DSCHG 1: 1, DSCHG 2: 0, DSCHG 3: 1, IPre 0: 0, IPre 1: 1 +I (00:00:03.146) Main: Result: 000000a3, VBattReg 0: 1, VBattReg 1: 0, VBattReg 2: 0, VBattReg 3: 0, VBattReg 4: 1, VBattReg 5: 1, VBatt pre: 0, VBatt rech: 1 +I (00:00:03.160) Main: Result: 0000004a, EnTerm: 1, WatchDog 0: 1, WatchDog 1: 0, EnTimer: 0, ChgTimer 0: 1, ChgTimer 1: 0, TermTmr: 0 +I (00:00:03.172) Main: Result: 0000000b, TMR2XEn: 1, FetDis: 0, EnNtc: 0, TJReg 0: 0, TJReg 1: 0 +I (00:00:03.180) Main: Result: 00000080, Rev 1: 0, Rev 0: 0, ChgStat 1: 0, ChgStat 0: 0, PPMStat: 0, PGStat: 0, ThermStat: 1 +I (00:00:03.191) Main: Result: 00000080, WatchdogFault: 0, VinFault: 0, ThemSd: 0, BatFault 0: 0, StmrFault: 0 +I (00:00:03.200) Main: Done. +``` diff --git a/examples/mp2660/default/main/CMakeLists.txt b/examples/mp2660/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/mp2660/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/mp2660/default/main/Kconfig.projbuild b/examples/mp2660/default/main/Kconfig.projbuild new file mode 100644 index 00000000..bc7cc696 --- /dev/null +++ b/examples/mp2660/default/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example configuration" + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 22 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 21 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. +endmenu diff --git a/examples/mp2660/default/main/component.mk b/examples/mp2660/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/mp2660/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/mp2660/default/main/main.c b/examples/mp2660/default/main/main.c new file mode 100644 index 00000000..f6eaf6ed --- /dev/null +++ b/examples/mp2660/default/main/main.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +static const char *MAIN_LOG_TAG = "Main"; + +void task(void *pvParameters) +{ + i2c_dev_t charger; + memset(&charger, 0, sizeof(i2c_dev_t)); + + i2cdev_init(); + + esp_err_t err = mp2660_init_desc(&charger, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL); + if (err != ESP_OK) + { + ESP_LOGI(MAIN_LOG_TAG, "Cannot init MP2660: %s", esp_err_to_name(err)); + return; + } + + vTaskDelay(pdMS_TO_TICKS(2000)); + + do + { + mp2660_fault_t fault; + err = mp2660_get_fault(&charger, &fault); + + ESP_LOGI(MAIN_LOG_TAG, "Result reading fault register: %s", esp_err_to_name(err)); + } + while (err != ESP_OK); + + for (;;) + { + mp2660_input_source_t input_source; + err = mp2660_get_input_source(&charger, &input_source); + if (err != ESP_OK) + { + ESP_LOGI(MAIN_LOG_TAG, "Cannot read MP2660 input source register: %s", esp_err_to_name(err)); + } + else + { + ESP_LOGI(MAIN_LOG_TAG, "Result: %08x, EnHIZ: %d, VInMin3: %d, VInMin2: %d, VInMin1: %d, VInMin0: %d, IInLim_2: %d, IInLim_1: %d, IInLim_0: %d", input_source.register_data.reg, + input_source.data_fields.en_hiz, input_source.data_fields.v_in_min_3, input_source.data_fields.v_in_min_2, input_source.data_fields.v_in_min_1, input_source.data_fields.v_in_min_0, + input_source.data_fields.i_in_lim_2, input_source.data_fields.i_in_lim_1, input_source.data_fields.i_in_lim_0); + } + + mp2660_power_on_conf_t pwr_conf; + err = mp2660_get_pwr_on_conf(&charger, &pwr_conf); + + if (err != ESP_OK) + { + ESP_LOGI(MAIN_LOG_TAG, "Cannot read MP2660 power configuration register: %s", esp_err_to_name(err)); + } + else + { + ESP_LOGI(MAIN_LOG_TAG, "Result: %08x, UVLO 0: %d, UVLO 1: %d, UVLO 2: %d, CEB: %d, WatchdogTimer: %d, RegisterReset: %d", pwr_conf.register_data.reg, pwr_conf.data_fields.v_batt_uvlo_0, + pwr_conf.data_fields.v_batt_uvlo_1, pwr_conf.data_fields.v_batt_uvlo_2, pwr_conf.data_fields.ceb, pwr_conf.data_fields.i2c_watchdog_timer, pwr_conf.data_fields.reg_reset); + } + + mp2660_charge_current_ctrl_t charge_current_ctrl; + err = mp2660_get_chrg_current_ctrl(&charger, &charge_current_ctrl); + + if (err != ESP_OK) + { + ESP_LOGI(MAIN_LOG_TAG, "Cannot read MP2660 charge current control register: %s", esp_err_to_name(err)); + } + else + { + ESP_LOGI(MAIN_LOG_TAG, "Result: %08x, ICC 0: %d, ICC 1: %d, ICC 2: %d, ICC 3: %d, ICC 4: %d", charge_current_ctrl.register_data.reg, charge_current_ctrl.data_fields.icc_0, + charge_current_ctrl.data_fields.icc_1, charge_current_ctrl.data_fields.icc_2, charge_current_ctrl.data_fields.icc_3, charge_current_ctrl.data_fields.icc_4); + } + + mp2660_pre_charge_term_current_t pre_chrg_term_current; + err = mp2660_get_pre_chrg_term_current(&charger, &pre_chrg_term_current); + + if (err != ESP_OK) + { + ESP_LOGI(MAIN_LOG_TAG, "Cannot read MP2660 pre charge/termination current control register: %s", esp_err_to_name(err)); + } + else + { + ESP_LOGI(MAIN_LOG_TAG, "Result: %08x, DSCHG 0: %d, DSCHG 1: %d, DSCHG 2: %d, DSCHG 3: %d, IPre 0: %d, IPre 1: %d", pre_chrg_term_current.register_data.reg, + pre_chrg_term_current.data_fields.i_dschg_0, pre_chrg_term_current.data_fields.i_dschg_1, pre_chrg_term_current.data_fields.i_dschg_2, pre_chrg_term_current.data_fields.i_dschg_3, + pre_chrg_term_current.data_fields.i_pre_0, pre_chrg_term_current.data_fields.i_pre_1); + } + + mp2660_charge_voltage_ctrl_t chrg_voltage_ctrl; + err = mp2660_get_chrg_voltage_control(&charger, &chrg_voltage_ctrl); + + if (err != ESP_OK) + { + ESP_LOGI(MAIN_LOG_TAG, "Cannot read MP2660 charge voltage control register: %s", esp_err_to_name(err)); + } + else + { + ESP_LOGI(MAIN_LOG_TAG, "Result: %08x, VBattReg 0: %d, VBattReg 1: %d, VBattReg 2: %d, VBattReg 3: %d, VBattReg 4: %d, VBattReg 5: %d, VBatt pre: %d, VBatt rech: %d", + chrg_voltage_ctrl.register_data.reg, chrg_voltage_ctrl.data_fields.v_bat_reg_0, chrg_voltage_ctrl.data_fields.v_bat_reg_1, chrg_voltage_ctrl.data_fields.v_bat_reg_2, + chrg_voltage_ctrl.data_fields.v_bat_reg_3, chrg_voltage_ctrl.data_fields.v_bat_reg_4, chrg_voltage_ctrl.data_fields.v_bat_reg_5, chrg_voltage_ctrl.data_fields.v_batt_pre, + chrg_voltage_ctrl.data_fields.v_batt_rech); + } + + mp2660_charge_term_timer_ctrl_t charge_term_timer_ctrl; + err = mp2660_get_chrg_term_timer_control(&charger, &charge_term_timer_ctrl); + + if (err != ESP_OK) + { + ESP_LOGI(MAIN_LOG_TAG, "Cannot read MP2660 charge termination / timer control register: %s", esp_err_to_name(err)); + } + else + { + ESP_LOGI(MAIN_LOG_TAG, "Result: %08x, EnTerm: %d, WatchDog 0: %d, WatchDog 1: %d, EnTimer: %d, ChgTimer 0: %d, ChgTimer 1: %d, TermTmr: %d", charge_term_timer_ctrl.register_data.reg, + charge_term_timer_ctrl.data_fields.en_term, charge_term_timer_ctrl.data_fields.watchdog_0, charge_term_timer_ctrl.data_fields.watchdog_1, charge_term_timer_ctrl.data_fields.en_timer, + charge_term_timer_ctrl.data_fields.chg_timer_0, charge_term_timer_ctrl.data_fields.chg_timer_1, charge_term_timer_ctrl.data_fields.term_tmr); + } + + mp2660_misc_op_ctrl_t misc_op_ctrl; + err = mp2660_get_misc_op_control(&charger, &misc_op_ctrl); + + if (err != ESP_OK) + { + ESP_LOGI(MAIN_LOG_TAG, "Cannot read MP2660 misc operation control register: %s", esp_err_to_name(err)); + } + else + { + ESP_LOGI(MAIN_LOG_TAG, "Result: %08x, TMR2XEn: %d, FetDis: %d, EnNtc: %d, TJReg 0: %d, TJReg 1: %d", misc_op_ctrl.register_data.reg, misc_op_ctrl.data_fields.tmr2x_en, + misc_op_ctrl.data_fields.fet_dis, misc_op_ctrl.data_fields.en_ntc, misc_op_ctrl.data_fields.tj_reg_0, misc_op_ctrl.data_fields.tj_reg_1); + } + + mp2660_sys_status_t sys_status; + err = mp2660_get_sys_status(&charger, &sys_status); + + if (err != ESP_OK) + { + ESP_LOGI(MAIN_LOG_TAG, "Cannot read MP2660 system status register: %s", esp_err_to_name(err)); + } + else + { + ESP_LOGI(MAIN_LOG_TAG, "Result: %08x, Rev 1: %d, Rev 0: %d, ChgStat 1: %d, ChgStat 0: %d, PPMStat: %d, PGStat: %d, ThermStat: %d", sys_status.register_data.reg, + sys_status.data_fields.rev_1, sys_status.data_fields.rev_0, sys_status.data_fields.chg_stat_1, sys_status.data_fields.chg_stat_0, sys_status.data_fields.ppm_stat, + sys_status.data_fields.pg_stat, sys_status.data_fields.therm_stat); + } + + mp2660_fault_t fault; + err = mp2660_get_fault(&charger, &fault); + + if (err != ESP_OK) + { + ESP_LOGI(MAIN_LOG_TAG, "Cannot read MP2660 fault register: %s", esp_err_to_name(err)); + } + else + { + ESP_LOGI(MAIN_LOG_TAG, "Result: %08x, WatchdogFault: %d, VinFault: %d, ThemSd: %d, BatFault 0: %d, StmrFault: %d", fault.register_data.reg, fault.data_fields.watchdog_fault, + fault.data_fields.vin_fault, fault.data_fields.them_sd, fault.data_fields.bat_fault, fault.data_fields.stmr_fault); + } + + ESP_LOGI(MAIN_LOG_TAG, "Done."); + + vTaskDelay(pdMS_TO_TICKS(3000)); + } +} + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + + xTaskCreatePinnedToCore(task, "test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/mp2660/default/sdkconfig.defaults.esp8266 b/examples/mp2660/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/mp2660/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/mpu6050/default/CMakeLists.txt b/examples/mpu6050/default/CMakeLists.txt new file mode 100644 index 00000000..a9dbb78b --- /dev/null +++ b/examples/mpu6050/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-mpu6050) diff --git a/examples/mpu6050/default/Makefile b/examples/mpu6050/default/Makefile new file mode 100644 index 00000000..69b05e8c --- /dev/null +++ b/examples/mpu6050/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-mpu6050 + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/mpu6050/default/README.md b/examples/mpu6050/default/README.md new file mode 100644 index 00000000..1a35e504 --- /dev/null +++ b/examples/mpu6050/default/README.md @@ -0,0 +1,74 @@ +# Example for `mpu6050` driver + +## What it does + +This example initializes an MPU-6050 connected on the I2C bus and then displays the accelerometer and gyroscope data in gravity and degrees per second in an endless loop. + +**NOTE:** +Example is tested on ESP-IDF v4.4, v5.1 and ESP32 Wrover Module + +## Wiring + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +| --------------------------------------- | --------------------- | ---------- | +| `CONFIG_EXAMPLE_MPU6050_I2C_MASTER_SCL` | GPIO number for `SCL` | `esp32` 22 | +| `CONFIG_EXAMPLE_MPU6050_I2C_MASTER_SDA` | GPIO number for `SDA` | `esp32` 21 | +| `CONFIG_EXAMPLE_MPU6050_I2C_CLOCK_HZ` | I2C Clock Freq | 100000 | + + +## Logs + +``` +I (316) cpu_start: Starting scheduler on PRO CPU. +I (0) cpu_start: Starting scheduler on APP CPU. +I (0) mpu6050_test: mpu6050 config: addr 0x68, sda 21, scl 22, clk, 100000 port 1 +I (4341) mpu6050_test: mpu6050 connection successfull. +I (4341) mpu6050_test: ********************************************************************** +I (4341) mpu6050_test: Rotation: x=-246 y=-356 z=-52 +I (4351) mpu6050_test: Acceleration: x=-16016 y=-644 z=-784 +I (4351) mpu6050_test: Temperature: 33 +I (5361) mpu6050_test: ********************************************************************** +I (5361) mpu6050_test: Rotation: x=-173 y=-223 z=-41 +I (5361) mpu6050_test: Acceleration: x=-15872 y=-520 z=-148 +I (5371) mpu6050_test: Temperature: 33 +I (6371) mpu6050_test: ********************************************************************** +I (6371) mpu6050_test: Rotation: x=-318 y=268 z=-60 +I (6371) mpu6050_test: Acceleration: x=-15860 y=-672 z=-172 +I (6381) mpu6050_test: Temperature: 33 +I (7381) mpu6050_test: ********************************************************************** +I (7381) mpu6050_test: Rotation: x=-606 y=-209 z=183 +I (7381) mpu6050_test: Acceleration: x=-15616 y=-280 z=320 +I (7391) mpu6050_test: Temperature: 33 +I (8391) mpu6050_test: ********************************************************************** +I (8391) mpu6050_test: Rotation: x=-714 y=1262 z=-98 +I (8391) mpu6050_test: Acceleration: x=-15948 y=-260 z=-60 +I (8401) mpu6050_test: Temperature: 33 +I (9401) mpu6050_test: ********************************************************************** +I (9401) mpu6050_test: Rotation: x=-387 y=70 z=-370 +I (9401) mpu6050_test: Acceleration: x=-15972 y=-440 z=-152 +I (9411) mpu6050_test: Temperature: 33 +I (10411) mpu6050_test: ********************************************************************** +I (10411) mpu6050_test: Rotation: x=120 y=-288 z=-56 +I (10411) mpu6050_test: Acceleration: x=-15848 y=-112 z=72 +I (10421) mpu6050_test: Temperature: 33 +I (11421) mpu6050_test: ********************************************************************** +I (11421) mpu6050_test: Rotation: x=-186 y=-129 z=-42 +I (11421) mpu6050_test: Acceleration: x=-15844 y=-140 z=-52 +I (11431) mpu6050_test: Temperature: 33 +I (12431) mpu6050_test: ********************************************************************** +I (12431) mpu6050_test: Rotation: x=-184 y=-7 z=-70 +I (12431) mpu6050_test: Acceleration: x=-15780 y=-128 z=16 +I (12441) mpu6050_test: Temperature: 33 +I (13441) mpu6050_test: ********************************************************************** +I (13441) mpu6050_test: Rotation: x=-142 y=-142 z=-49 +I (13441) mpu6050_test: Acceleration: x=-15980 y=-96 z=-112 +I (13451) mpu6050_test: Temperature: 33 +I (14451) mpu6050_test: ********************************************************************** +I (14451) mpu6050_test: Rotation: x=-218 y=-264 z=-45 +I (14451) mpu6050_test: Acceleration: x=-15864 y=-264 z=188 +I (14461) mpu6050_test: Temperature: 33 +I (15461) mpu6050_test: ********************************************************************** +``` diff --git a/examples/mpu6050/default/main/CMakeLists.txt b/examples/mpu6050/default/main/CMakeLists.txt new file mode 100644 index 00000000..06502277 --- /dev/null +++ b/examples/mpu6050/default/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS "." + REQUIRES mpu6050) diff --git a/examples/mpu6050/default/main/Kconfig.projbuild b/examples/mpu6050/default/main/Kconfig.projbuild new file mode 100644 index 00000000..1233e359 --- /dev/null +++ b/examples/mpu6050/default/main/Kconfig.projbuild @@ -0,0 +1,35 @@ +menu "MPU6050 Example Configuration" + + choice EXAMPLE_I2C_ADDRESS + prompt "Select I2C address" + default EXAMPLE_I2C_ADDRESS_LOW + help + Select I2C address + + config EXAMPLE_I2C_ADDRESS_LOW + bool "MPU6050_I2C_ADDRESS_LOW" + help + Choose this when ADDR pin is connected to ground + config EXAMPLE_I2C_ADDRESS_HIGH + bool "MPU6050_I2C_ADDRESS_HIGH" + help + Choose this when ADDR pin is connected to VCC + endchoice + + config EXAMPLE_SCL_GPIO + int "MPU6050 SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_SDA_GPIO + int "MPU6050 SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. + +endmenu diff --git a/examples/mpu6050/default/main/component.mk b/examples/mpu6050/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/mpu6050/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/mpu6050/default/main/main.c b/examples/mpu6050/default/main/main.c new file mode 100644 index 00000000..09a0149f --- /dev/null +++ b/examples/mpu6050/default/main/main.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_EXAMPLE_I2C_ADDRESS_LOW +#define ADDR MPU6050_I2C_ADDRESS_LOW +#else +#define ADDR MPU6050_I2C_ADDRESS_HIGH +#endif + +static const char *TAG = "mpu6050_test"; + +void mpu6050_test(void *pvParameters) +{ + mpu6050_dev_t dev = { 0 }; + + ESP_ERROR_CHECK(mpu6050_init_desc(&dev, ADDR, 0, CONFIG_EXAMPLE_SDA_GPIO, CONFIG_EXAMPLE_SCL_GPIO)); + + while (1) + { + esp_err_t res = i2c_dev_probe(&dev.i2c_dev, I2C_DEV_WRITE); + if (res == ESP_OK) + { + ESP_LOGI(TAG, "Found MPU60x0 device"); + break; + } + ESP_LOGE(TAG, "MPU60x0 not found"); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_ERROR_CHECK(mpu6050_init(&dev)); + + ESP_LOGI(TAG, "Accel range: %d", dev.ranges.accel); + ESP_LOGI(TAG, "Gyro range: %d", dev.ranges.gyro); + + while (1) + { + float temp; + mpu6050_acceleration_t accel = { 0 }; + mpu6050_rotation_t rotation = { 0 }; + + ESP_ERROR_CHECK(mpu6050_get_temperature(&dev, &temp)); + ESP_ERROR_CHECK(mpu6050_get_motion(&dev, &accel, &rotation)); + + ESP_LOGI(TAG, "**********************************************************************"); + ESP_LOGI(TAG, "Acceleration: x=%.4f y=%.4f z=%.4f", accel.x, accel.y, accel.z); + ESP_LOGI(TAG, "Rotation: x=%.4f y=%.4f z=%.4f", rotation.x, rotation.y, rotation.z); + ESP_LOGI(TAG, "Temperature: %.1f", temp); + + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +void app_main() +{ + // task + ESP_ERROR_CHECK(i2cdev_init()); + + xTaskCreate(mpu6050_test, "mpu6050_test", configMINIMAL_STACK_SIZE * 6, NULL, 5, NULL); +} diff --git a/examples/mpu6050/default/sdkconfig.defaults.esp8266 b/examples/mpu6050/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/mpu6050/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/qmp6988/default/CMakeLists.txt b/examples/qmp6988/default/CMakeLists.txt new file mode 100644 index 00000000..cc8d7c9d --- /dev/null +++ b/examples/qmp6988/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-qmp6988) diff --git a/examples/qmp6988/default/Makefile b/examples/qmp6988/default/Makefile new file mode 100644 index 00000000..13be8b39 --- /dev/null +++ b/examples/qmp6988/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-qmp6988 + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/qmp6988/default/README.md b/examples/qmp6988/default/README.md new file mode 100644 index 00000000..50eaa10c --- /dev/null +++ b/examples/qmp6988/default/README.md @@ -0,0 +1,16 @@ +# Example for `qmp6988` driver + +## What it does + +The example configures a `qmp6988` device. + +## Wiring + + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | diff --git a/examples/qmp6988/default/main/CMakeLists.txt b/examples/qmp6988/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/qmp6988/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/qmp6988/default/main/Kconfig.projbuild b/examples/qmp6988/default/main/Kconfig.projbuild new file mode 100644 index 00000000..35646959 --- /dev/null +++ b/examples/qmp6988/default/main/Kconfig.projbuild @@ -0,0 +1,43 @@ +menu "Example configuration" + choice EXAMPLE_QMP6988_DEMO + prompt "Demo mode" + default EXAMPLE_QMP6988_DEMO_NORMAL + help + Choose how to masure values from the sensor. See the main.c for + details. + + config EXAMPLE_QMP6988_DEMO_FORCED + bool "Forced mode" + help + In this example the qmp6988 makes a single measurement before entering + sleep mode again. + + config EXAMPLE_QMP6988_DEMO_NORMAL + bool "Normal mode" + help + In this example the qmp6988 makes continuous measurements. + + endchoice + + config EXAMPLE_QMP6988_ADDR + hex "I2C address of QMP6988" + default 0x70 + help + I2C address of QMP6988, default 0x70. + + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. +endmenu diff --git a/examples/qmp6988/default/main/component.mk b/examples/qmp6988/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/qmp6988/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/qmp6988/default/main/main.c b/examples/qmp6988/default/main/main.c new file mode 100644 index 00000000..b380d246 --- /dev/null +++ b/examples/qmp6988/default/main/main.c @@ -0,0 +1,116 @@ +/** + * Simple example with QMP6988 sensor. + * + * It shows different user task implementations in *forced mode* and + * *normal mode*. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* float is used in printf(). you need non-default configuration in + * sdkconfig for ESP8266, which is enabled by default for this + * example. see sdkconfig.defaults.esp8266 + */ + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +static qmp6988_t dev; + +#if defined(CONFIG_EXAMPLE_QMP6988_DEMO_FORCED) + +/* + * User task that triggers a measurement every 5 seconds. Due to power + * efficiency reasons it uses *Forced* mode. + */ +void task(void *pvParameters) +{ + float temperature; + float pressure; + + TickType_t last_wakeup = xTaskGetTickCount(); + + /* We can lower the standard filter and oversampling to use less power + and in low accuracy examples e.g. a weather station */ + + ESP_ERROR_CHECK(qmp6988_set_filter(&dev, QMP6988_FILTERCOEFF_OFF)); + ESP_ERROR_CHECK(qmp6988_set_p_oversampling(&dev, QMP6988_OVERSAMPLING_2X)); + ESP_ERROR_CHECK(qmp6988_set_t_oversampling(&dev, QMP6988_OVERSAMPLING_1X)); + + while (1) + { + /*Set forced mode for qmp6988 (need to set this each time as it falls + back to sleep mode automatically after a single measurement). */ + if (qmp6988_setup_powermode(&dev, QMP6988_FORCED_MODE) == ESP_OK) + printf("Power mode set to forced mode\n"); + else + printf("Sensor error\n"); + + /* Wait until forced measurement is ready (constant time of at least 5.5 ms + according to the modified oversampling and filter modes.*/ + vTaskDelay(1); + + /*Calculate pressure values (this includes temperature as well, + as temp is needed to calc pressure)*/ + ESP_ERROR_CHECK(qmp6988_calc_pressure(&dev, &pressure)); + temperature = dev.temperature; + printf("QMP6988 Sensor: %.2f °C, %.2f Pa\n", temperature, pressure); + + // Wait until 5 seconds (cycle time) are over. + vTaskDelayUntil(&last_wakeup, pdMS_TO_TICKS(5000)); + } +} + +#else // CONFIG_QMP6988_DEMO_NORMAL +/* + * User task that fetches latest measurement results of sensor every 2 + * seconds. It starts the QMP6988 in normal (periodic) mode. + */ +void task(void *pvParameters) +{ + float temperature; + float pressure; + + // Set normal mode for qmp6988. + if (qmp6988_setup_powermode(&dev, QMP6988_NORMAL_MODE) == ESP_OK) + printf("Power mode set to normal mode\n"); + else + printf("Sensor error\n"); + + /* Wait until first measurement is ready (constant time of at least 10.6 ms + according to the default oversampling and filter modes.*/ + vTaskDelay(2); + + TickType_t last_wakeup = xTaskGetTickCount(); + + while (1) + { + /*Calculate pressure values (this includes temperature as well, + as temp is needed to calc pressure)*/ + ESP_ERROR_CHECK(qmp6988_calc_pressure(&dev, &pressure)); + temperature = dev.temperature; + printf("QMP6988 Sensor: %.2f °C, %.2f Pa\n", temperature, pressure); + + // Wait until 2 seconds (cycle time) are over. + vTaskDelayUntil(&last_wakeup, pdMS_TO_TICKS(2000)); + } +} + +#endif + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + + ESP_ERROR_CHECK(qmp6988_init_desc(&dev, CONFIG_EXAMPLE_QMP6988_ADDR, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(qmp6988_init(&dev)); + + xTaskCreatePinnedToCore(task, "qmp6988_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/qmp6988/default/sdkconfig.defaults.esp8266 b/examples/qmp6988/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/qmp6988/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/sfa3x/default/CMakeLists.txt b/examples/sfa3x/default/CMakeLists.txt new file mode 100644 index 00000000..97ab835b --- /dev/null +++ b/examples/sfa3x/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-sfa3x) diff --git a/examples/sfa3x/default/Makefile b/examples/sfa3x/default/Makefile new file mode 100644 index 00000000..bfea37bf --- /dev/null +++ b/examples/sfa3x/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-sfa3x + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/sfa3x/default/main/CMakeLists.txt b/examples/sfa3x/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/sfa3x/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/sfa3x/default/main/component.mk b/examples/sfa3x/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/sfa3x/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/sfa3x/default/main/main.c b/examples/sfa3x/default/main/main.c new file mode 100644 index 00000000..4989feeb --- /dev/null +++ b/examples/sfa3x/default/main/main.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include + +static const char *TAG = "sfa3x-example"; + +#if defined(CONFIG_IDF_TARGET_ESP8266) +#define SDA_GPIO 4 +#define SCL_GPIO 5 +#else +#define SDA_GPIO 16 +#define SCL_GPIO 17 +#endif + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +void task(void *pvParameters) +{ + i2c_dev_t dev = { 0 }; + + ESP_ERROR_CHECK(sfa3x_init_desc(&dev, 0, SDA_GPIO, SCL_GPIO)); + + char marking[32]; + ESP_ERROR_CHECK(sfa3x_get_device_marknig(&dev, marking)); + ESP_LOGI(TAG, "Sensor marking: %s", marking); + + ESP_ERROR_CHECK(sfa3x_start_continuous_measurement(&dev)); + ESP_LOGI(TAG, "Continuous measurement started"); + + float hcho, temperature, humidity; + while (1) + { + vTaskDelay(pdMS_TO_TICKS(500)); + + esp_err_t res = sfa3x_read_measurement(&dev, &hcho, &humidity, &temperature); + if (res != ESP_OK) + { + ESP_LOGE(TAG, "Error reading results %d (%s)", res, esp_err_to_name(res)); + continue; + } + + ESP_LOGI(TAG, "Formaldehyde: %.2f bpm", hcho); + ESP_LOGI(TAG, "Temperature: %.2f °C", temperature); + ESP_LOGI(TAG, "Humidity: %.2f %%", humidity); + } +} + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + + xTaskCreatePinnedToCore(task, TAG, configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/sfa3x/default/sdkconfig.defaults.esp8266 b/examples/sfa3x/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/sfa3x/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/sgm58031/default/CMakeLists.txt b/examples/sgm58031/default/CMakeLists.txt new file mode 100644 index 00000000..b4f2b911 --- /dev/null +++ b/examples/sgm58031/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(sgm58031-continuos) diff --git a/examples/sgm58031/default/Makefile b/examples/sgm58031/default/Makefile new file mode 100644 index 00000000..832cc8f0 --- /dev/null +++ b/examples/sgm58031/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := sgm58031-continuos + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/sgm58031/default/README.md b/examples/sgm58031/default/README.md new file mode 100644 index 00000000..5321b134 --- /dev/null +++ b/examples/sgm58031/default/README.md @@ -0,0 +1,35 @@ +# Example for `SGM58031` driver + +The datasheet can be found [here](https://www.sg-micro.com/uploads/soft/20220609/1654744052.pdf). + +## What it does + +The example configures two `SMG58031` devices on an `I2C` bus. One has pin +`ADDR` connected to `GND`, and another has pin `ADDR` connected to `VCC`. The +example initializes two devices, and shows raw ADC values and voltages in a +loop. + +## Wiring + +Make the connections as below: + +| S. No. | `SGM58031` | `ESP32` | +|--------|-----------|---------------------| +| 1. | `V_DD` | `V_in` or 5V source | +| 2. | `GND` | `GND` | +| 3. | `SCL` | See below | +| 4. | `SDA` | See below | +| 5. | `A0`-`A3` | analog inputs | + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | + +This example specifically demonstrates the simultaneous use of multiple +devices, which is why the default `CONFIG_EXAMPLE_DEV_COUNT` is 2. If you are +using only one IC then please change the value of `CONFIG_EXAMPLE_DEV_COUNT` +to 1 under `Example configuration` in `menuconfig`. diff --git a/examples/sgm58031/default/main/CMakeLists.txt b/examples/sgm58031/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/sgm58031/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/sgm58031/default/main/Kconfig.projbuild b/examples/sgm58031/default/main/Kconfig.projbuild new file mode 100644 index 00000000..0b97b09a --- /dev/null +++ b/examples/sgm58031/default/main/Kconfig.projbuild @@ -0,0 +1,23 @@ +menu "Example configuration" + config EXAMPLE_DEV_COUNT + int "Number of SGM58031" + default 2 + help + Number of SGM58031 devices on the bus. + + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. +endmenu diff --git a/examples/sgm58031/default/main/component.mk b/examples/sgm58031/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/sgm58031/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/sgm58031/default/main/main.c b/examples/sgm58031/default/main/main.c new file mode 100644 index 00000000..5f680ee4 --- /dev/null +++ b/examples/sgm58031/default/main/main.c @@ -0,0 +1,76 @@ +#include +#include + +#include +#include + +#include + +#include + +#define I2C_PORT 0 + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +#define GAIN SGM58031_GAIN_2V048 // +-2.048V + +static const char *TAG = "example"; +// I2C addresses +static const uint8_t addr = SGM58031_ADDR_GND; + +// Descriptors +static i2c_dev_t device; + +// Gain value +static float gain_val; + +// Main task +void sgm58031_test(void *pvParameters) +{ + gain_val = sgm58031_gain_values[GAIN]; + + // Setup ICs + + ESP_ERROR_CHECK( + sgm58031_init_desc(&device, addr, I2C_PORT, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + + uint8_t id, version; + ESP_ERROR_CHECK(sgm58031_get_chip_id(&device, &id, &version)); + ESP_LOGI(TAG, "Device - Addr 0x%02x\tID: %d\tVersion: %d", addr, id, version); + + ESP_ERROR_CHECK(sgm58031_set_data_rate(&device, SGM58031_DATA_RATE_240)); // 25 samples per second + ESP_ERROR_CHECK(sgm58031_set_input_mux(&device, SGM58031_MUX_AIN0_GND)); // positive = AIN0, negative = GND + ESP_ERROR_CHECK(sgm58031_set_gain(&device, GAIN)); + ESP_ERROR_CHECK(sgm58031_set_conv_mode(&device, SGM58031_CONV_MODE_CONTINUOUS)); // Continuous conversion mode + + while (1) + { + // Read result + int16_t raw = 0; + if (sgm58031_get_value(&device, &raw) == ESP_OK) + { + float voltage = gain_val / SGM58031_MAX_VALUE * raw; + ESP_LOGI(TAG, "[0x%02x] Raw ADC value: %d, voltage: %.04f volts", addr, raw, voltage); + } + else + { + ESP_LOGE(TAG, "[0x%02x] Cannot read ADC value", addr); + } + + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +void app_main() +{ + // Init library + ESP_ERROR_CHECK(i2cdev_init()); + + // Clear device descriptors + memset(&device, 0, sizeof(device)); + + // Start task + xTaskCreatePinnedToCore(sgm58031_test, "sgm58031_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/sgm58031/single-shot/CMakeLists.txt b/examples/sgm58031/single-shot/CMakeLists.txt new file mode 100644 index 00000000..442316f2 --- /dev/null +++ b/examples/sgm58031/single-shot/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(sgm58031-single-shot) diff --git a/examples/sgm58031/single-shot/Makefile b/examples/sgm58031/single-shot/Makefile new file mode 100644 index 00000000..87796c53 --- /dev/null +++ b/examples/sgm58031/single-shot/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := sgm58031-single-shot + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/sgm58031/single-shot/README.md b/examples/sgm58031/single-shot/README.md new file mode 100644 index 00000000..e5f59d08 --- /dev/null +++ b/examples/sgm58031/single-shot/README.md @@ -0,0 +1,29 @@ +# Single-Shot example for `SGM58031` driver + +The datasheet can be found [here](https://www.sg-micro.com/uploads/soft/20220609/1654744052.pdf). + +## What it does + +The example configures a `SMG58031` device on an `I2C` bus with address selected +from config value `CONFIG_EXAMPLE_I2C_ADDRESS` (default to 0x48). +The example initializes the device and shows raw ADC values and voltages in a loop from each device input working in single-shot mode. + +## Wiring + +Make the connections as below: + +| S. No. | `SGM58031` | `ESP32` | +| ------ | ---------- | ------------------ | +| 1. | `VDD` | `Vin` or 5V source | +| 2. | `GND` | `GND` | +| 3. | `SCL` | See below | +| 4. | `SDA` | See below | +| 5. | `A0`-`A3` | analog inputs | + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +| ------------------------------- | --------------------- | -------------------------------------------------------------------------------- | +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | diff --git a/examples/sgm58031/single-shot/main/CMakeLists.txt b/examples/sgm58031/single-shot/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/sgm58031/single-shot/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/sgm58031/single-shot/main/Kconfig.projbuild b/examples/sgm58031/single-shot/main/Kconfig.projbuild new file mode 100644 index 00000000..e1a708c4 --- /dev/null +++ b/examples/sgm58031/single-shot/main/Kconfig.projbuild @@ -0,0 +1,40 @@ +menu "Example configuration" + choice EXAMPLE_I2C_DEVICE_ADDRESS + bool "SGM58031 I2C address" + default SGM58031_0x48_ADDR + help + Select SMG58031 I2C address + + config SGM58031_0x48_ADDR + bool "0x49 (ADDR <-> GND)" + config SGM58031_0x49_ADDR + bool "0x49 (ADDR <-> VDD)" + config SGM58031_0x4A_ADDR + bool "0x4A (ADDR <-> SDA)" + config SGM58031_0x48_ADDR + bool "0x4B (ADDR <-> SCL)" + endchoice + + config EXAMPLE_I2C_DEVICE_ADDRESS + hex + default 0x48 if SGM58031_0x48_ADDR + default 0x49 if SGM58031_0x49_ADDR + default 0x4A if SGM58031_0x4A_ADDR + default 0x4B if SGM58031_0x4B_ADDR + + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. +endmenu diff --git a/examples/sgm58031/single-shot/main/component.mk b/examples/sgm58031/single-shot/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/sgm58031/single-shot/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/sgm58031/single-shot/main/main.c b/examples/sgm58031/single-shot/main/main.c new file mode 100644 index 00000000..fd477c03 --- /dev/null +++ b/examples/sgm58031/single-shot/main/main.c @@ -0,0 +1,90 @@ +#include +#include + +#include +#include + +#include + +#include + +#define I2C_PORT 0 + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +#define GAIN SGM58031_GAIN_2V048 // +-2.048V + +static const char *TAG = "single-shot-example"; +// I2C addresses +static const uint8_t addr = CONFIG_EXAMPLE_I2C_DEVICE_ADDRESS; + +// Descriptors +static i2c_dev_t device; + +// Gain value +static float gain_val; + +// Main task +void sgm58031_test(void *pvParameters) +{ + gain_val = sgm58031_gain_values[GAIN]; + + // Setup ICs + + ESP_ERROR_CHECK( + sgm58031_init_desc(&device, addr, I2C_PORT, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + + uint8_t id, version; + ESP_ERROR_CHECK(sgm58031_get_chip_id(&device, &id, &version)); + ESP_LOGI(TAG, "Device - Addr 0x%02x\tID: %d\tVersion: %d", addr, id, version); + + ESP_ERROR_CHECK(sgm58031_set_data_rate(&device, SGM58031_DATA_RATE_800)); // 25 samples per second + ESP_ERROR_CHECK(sgm58031_set_gain(&device, GAIN)); + ESP_ERROR_CHECK(sgm58031_set_conv_mode(&device, SGM58031_CONV_MODE_SINGLE_SHOT)); // Single-shot conversion mode + + while (1) + { + for (uint8_t i = 0; i < 4; ++i) + { + ESP_ERROR_CHECK(sgm58031_set_input_mux(&device, SGM58031_MUX_AIN0_GND + i)); // positive = AINx, negative = GND + + if (sgm58031_start_conversion(&device) == ESP_OK) + { + bool busy = true; + + while (busy) + { + sgm58031_is_busy(&device, &busy); + } + + // Read result + int16_t raw = 0; + if (sgm58031_get_value(&device, &raw) == ESP_OK) + { + float voltage = gain_val / SGM58031_MAX_VALUE * raw; + ESP_LOGI(TAG, "[0x%02x] AIN%d Raw ADC value: %d, voltage: %.04f volts", addr, i, raw, voltage); + } + else + { + ESP_LOGE(TAG, "[0x%02x] Cannot read ADC value on AIN%d", addr, i); + } + } + } + + vTaskDelay(pdMS_TO_TICKS(2000)); + } +} + +void app_main() +{ + // Init library + ESP_ERROR_CHECK(i2cdev_init()); + + // Clear device descriptors + memset(&device, 0, sizeof(device)); + + // Start task + xTaskCreatePinnedToCore(sgm58031_test, "sgm58031_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/sts3x/default/CMakeLists.txt b/examples/sts3x/default/CMakeLists.txt new file mode 100644 index 00000000..9e63b679 --- /dev/null +++ b/examples/sts3x/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-sts3x) diff --git a/examples/sts3x/default/Makefile b/examples/sts3x/default/Makefile new file mode 100644 index 00000000..fdd891a4 --- /dev/null +++ b/examples/sts3x/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-sts3x + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/sts3x/default/README.md b/examples/sts3x/default/README.md new file mode 100644 index 00000000..6c15a6b1 --- /dev/null +++ b/examples/sts3x/default/README.md @@ -0,0 +1,30 @@ +# Example for `sts3x` driver + +## What it does + +The example configures a `sts3x` device. + +When `CONFIG_EXAMPLE_STS3X_DEMO_HL` is defined, the task that triggers a +measurement every 5 seconds. Due to power efficiency reasons it uses *single +shot* mode. In this example it uses the high level function *sts3x_measure()* to +perform one measurement in each cycle. + +When `CONFIG_EXAMPLE_STS3X_DEMO_LL` is defined, the task that triggers a +measurement every 5 seconds. Due to power efficiency reasons it uses *single +shot* mode. In this example it starts the measurement, waits for the results +and fetches the results using separate functions. + +Choose either `CONFIG_EXAMPLE_STS3X_DEMO_HL` or `CONFIG_EXAMPLE_STS3X_DEMO_LL` +in `make menuconfig` under `Example configuration`. The default is +`CONFIG_EXAMPLE_STS3X_DEMO_HL`. + +## Wiring + + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | diff --git a/examples/sts3x/default/main/CMakeLists.txt b/examples/sts3x/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/sts3x/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/sts3x/default/main/Kconfig.projbuild b/examples/sts3x/default/main/Kconfig.projbuild new file mode 100644 index 00000000..3f372396 --- /dev/null +++ b/examples/sts3x/default/main/Kconfig.projbuild @@ -0,0 +1,46 @@ +menu "Example configuration" + choice EXAMPLE_STS3X_DEMO + prompt "Demo mode" + default EXAMPLE_STS3X_DEMO_HL + help + Choose how to masure values from the sensor. See the main.c for + details. + + config EXAMPLE_STS3X_DEMO_HL + bool "High level" + help + In this example it uses the high level function + sts3x_measure() to perform one measurement in each cycle. + + config EXAMPLE_STS3X_DEMO_LL + bool "Low level" + help + In this example it starts the measurement, waits for the + results and fetches the results using separate functions + + endchoice + + config EXAMPLE_STS3X_ADDR + hex "I2C address of STS3x" + default 0x44 + help + I2C address of STS3x, either 0x44 or 0x45. When ADDR pin is + grounded, choose 0x44. When ADDR pin is pulled up to VDD, choose + 0x45. + + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. +endmenu diff --git a/examples/sts3x/default/main/component.mk b/examples/sts3x/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/sts3x/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/sts3x/default/main/main.c b/examples/sts3x/default/main/main.c new file mode 100644 index 00000000..f1539b22 --- /dev/null +++ b/examples/sts3x/default/main/main.c @@ -0,0 +1,131 @@ +/** + * Simple example with STS3x sensor. + * + * It shows different user task implementations in *single shot mode* and + * *periodic mode*. In *single shot* mode either low level or high level + * functions are used. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* float is used in printf(). you need non-default configuration in + * sdkconfig for ESP8266, which is enabled by default for this + * example. see sdkconfig.defaults.esp8266 + */ + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +static sts3x_t dev; + +#if defined(CONFIG_EXAMPLE_STS3X_DEMO_HL) + +/* + * User task that triggers a measurement every 5 seconds. Due to power + * efficiency reasons it uses *single shot* mode. In this example it uses the + * high level function *sts3x_measure* to perform one measurement in each cycle. + */ +void task(void *pvParameters) +{ + float temperature; + + TickType_t last_wakeup = xTaskGetTickCount(); + + while (1) + { + // perform one measurement and do something with the results + ESP_ERROR_CHECK(sts3x_measure(&dev, &temperature)); + printf("STS3x Sensor: %.2f °C\n", temperature); + + // wait until 5 seconds are over + vTaskDelayUntil(&last_wakeup, pdMS_TO_TICKS(5000)); + } +} + +#elif defined(CONFIG_EXAMPLE_STS3X_DEMO_LL) + +/* + * User task that triggers a measurement every 5 seconds. Due to power + * efficiency reasons it uses *single shot* mode. In this example it starts the + * measurement, waits for the results and fetches the results using separate + * functions + */ +void task(void *pvParameters) +{ + float temperature; + + TickType_t last_wakeup = xTaskGetTickCount(); + + // get the measurement duration for high repeatability; + uint8_t duration = sts3x_get_measurement_duration(STS3X_HIGH); + + while (1) + { + // Trigger one measurement in single shot mode with high repeatability. + ESP_ERROR_CHECK(sts3x_start_measurement(&dev, STS3X_SINGLE_SHOT, STS3X_HIGH)); + + // Wait until measurement is ready (constant time of at least 30 ms + // or the duration returned from *sts3x_get_measurement_duration*). + vTaskDelay(duration); + + // retrieve the values and do something with them + ESP_ERROR_CHECK(sts3x_get_results(&dev, &temperature)); + printf("STS3x Sensor: %.2f °C\n", temperature); + + // wait until 5 seconds are over + vTaskDelayUntil(&last_wakeup, pdMS_TO_TICKS(5000)); + } +} + +#else // CONFIG_STS3X_DEMO_PERIODIC +/* + * User task that fetches latest measurement results of sensor every 2 + * seconds. It starts the STS3x in periodic mode with 1 measurements per + * second (*STS3X_PERIODIC_1MPS*). + */ +void task(void *pvParameters) +{ + float temperature; + esp_err_t res; + + // Start periodic measurements with 1 measurement per second. + ESP_ERROR_CHECK(sts3x_start_measurement(&dev, STS3X_PERIODIC_1MPS, STS3X_HIGH)); + + // Wait until first measurement is ready (constant time of at least 30 ms + // or the duration returned from *sts3x_get_measurement_duration*). + vTaskDelay(sts3x_get_measurement_duration(STS3X_HIGH)); + + TickType_t last_wakeup = xTaskGetTickCount(); + + while (1) + { + // Get the values and do something with them. + if ((res = sts3x_get_results(&dev, &temperature)) == ESP_OK) + printf("STS3x Sensor: %.2f °C\n", temperature); + else + printf("Could not get results: %d (%s)", res, esp_err_to_name(res)); + + // Wait until 2 seconds (cycle time) are over. + vTaskDelayUntil(&last_wakeup, pdMS_TO_TICKS(2000)); + } +} + +#endif + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + memset(&dev, 0, sizeof(sts3x_t)); + + ESP_ERROR_CHECK(sts3x_init_desc(&dev, CONFIG_EXAMPLE_STS3X_ADDR, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(sts3x_init(&dev)); + + xTaskCreatePinnedToCore(task, "sts31_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/sts3x/default/sdkconfig.defaults.esp8266 b/examples/sts3x/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/sts3x/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y diff --git a/examples/tca6424a/default/CMakeLists.txt b/examples/tca6424a/default/CMakeLists.txt new file mode 100644 index 00000000..6881e06f --- /dev/null +++ b/examples/tca6424a/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-tca95x5) diff --git a/examples/tca6424a/default/Makefile b/examples/tca6424a/default/Makefile new file mode 100644 index 00000000..ce29ecca --- /dev/null +++ b/examples/tca6424a/default/Makefile @@ -0,0 +1,7 @@ +#V := 1 +PROJECT_NAME := example-tca95x5 + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/tca6424a/default/main/CMakeLists.txt b/examples/tca6424a/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/tca6424a/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/tca6424a/default/main/Kconfig.projbuild b/examples/tca6424a/default/main/Kconfig.projbuild new file mode 100644 index 00000000..0a9e6cd7 --- /dev/null +++ b/examples/tca6424a/default/main/Kconfig.projbuild @@ -0,0 +1,41 @@ +menu "Example configuration" + choice EXAMPLE_I2C_ADDRESS + prompt "Select I2C address" + default EXAMPLE_I2C_ADDRESS_GND + help + Select I2C address + + config EXAMPLE_I2C_ADDRESS_GND + bool "TCA6424A_I2C_ADDRESS_GND" + help + Choose this when ADDR pin is connected to ground + config EXAMPLE_I2C_ADDRESS_VCC + bool "TCA6424A_I2C_ADDRESS_VCC" + help + Choose this when ADDR pin is connected to VCC + endchoice + + config EXAMPLE_INT_GPIO + int "INT GPIO Number" + default 14 if IDF_TARGET_ESP8266 + default 7 if IDF_TARGET_ESP32C3 + default 5 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number connected to INT pin. + + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. +endmenu diff --git a/examples/tca6424a/default/main/component.mk b/examples/tca6424a/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/tca6424a/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/tca6424a/default/main/main.c b/examples/tca6424a/default/main/main.c new file mode 100644 index 00000000..9c46d835 --- /dev/null +++ b/examples/tca6424a/default/main/main.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static i2c_dev_t tca6424a = {0 }; + +static QueueHandle_t gpio_queue = NULL; + +static const char *TAG = "tca6424a-example"; + +#ifdef CONFIG_EXAMPLE_I2C_ADDRESS_GND +#define ADDR TCA6424A_I2C_ADDRESS_GND +#endif +#ifdef CONFIG_EXAMPLE_I2C_ADDRESS_VCC +#define ADDR TCA6424A_I2C_ADDRESS_VCC +#endif + +// Interrupt handler +static void IRAM_ATTR intr_handler(void *arg) +{ + gpio_num_t gpio = (gpio_num_t)arg; + xQueueSendFromISR(gpio_queue, &gpio, NULL); +} + +// Interrupt event receiver +static void gpio_recv_task(void *arg) +{ + gpio_num_t gpio; + while (1) + { + if (xQueueReceive(gpio_queue, &gpio, portMAX_DELAY)) + { + ESP_LOGI(TAG, "GPIO interrupt on pin %d", gpio); + if (gpio != CONFIG_EXAMPLE_INT_GPIO) continue; + + uint32_t val; + esp_err_t res = tca6424a_port_read(&tca6424a, &val); + if (res != ESP_OK) + { + ESP_LOGE(TAG, "Error reading TCA6424A: %d (%s)", res, esp_err_to_name(res)); + continue; + } + + ESP_LOGI(TAG, "TCA6424A port value: 0x%" PRIX32, val); + } + } +} + +void main_task(void *pvParameters) +{ + // Init descriptor + ESP_ERROR_CHECK(tca6424a_init_desc(&tca6424a, ADDR, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + + // Setup P00, P01 and P02 as input, others as output + ESP_ERROR_CHECK(tca6424a_port_set_mode(&tca6424a, 0x000007)); // 0b000000000000000000000111 + + // Create queue + gpio_queue = xQueueCreate(5, sizeof(gpio_num_t)); + + // Run event receiver + xTaskCreate(gpio_recv_task, "gpio_recv_task", 4096, NULL, 5, NULL); + + // Setup GPIO interrupt + gpio_set_direction(CONFIG_EXAMPLE_INT_GPIO, GPIO_MODE_INPUT); + gpio_set_intr_type(CONFIG_EXAMPLE_INT_GPIO, GPIO_INTR_NEGEDGE); + gpio_install_isr_service(0); + gpio_isr_handler_add(CONFIG_EXAMPLE_INT_GPIO, intr_handler, (void *)CONFIG_EXAMPLE_INT_GPIO); + + // blink on P10 + bool on = true; + while (1) + { + ESP_ERROR_CHECK(tca6424a_set_level(&tca6424a, 8, on)); + on = !on; + vTaskDelay(pdMS_TO_TICKS(500)); + } +} + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + xTaskCreate(main_task, "main_task", configMINIMAL_STACK_SIZE * 6, NULL, 5, NULL); +} diff --git a/examples/tsl2591/default/main/main.c b/examples/tsl2591/default/main/main.c index 5e87902d..1b498c45 100644 --- a/examples/tsl2591/default/main/main.c +++ b/examples/tsl2591/default/main/main.c @@ -15,11 +15,20 @@ void tsl2591_test(void *pvParameters) ESP_ERROR_CHECK(tsl2591_init_desc(&dev, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); ESP_ERROR_CHECK(tsl2591_init(&dev)); + // Turn TSL2591 on + ESP_ERROR_CHECK(tsl2591_set_power_status(&dev, TSL2591_POWER_ON)); + // Turn ALS on + ESP_ERROR_CHECK(tsl2591_set_als_status(&dev, TSL2591_ALS_ON)); + // Set gain + ESP_ERROR_CHECK(tsl2591_set_gain(&dev, TSL2591_GAIN_MEDIUM)); + // Set integration time = 300ms + ESP_ERROR_CHECK(tsl2591_set_integration_time(&dev, TSL2591_INTEGRATION_300MS)); + float lux; esp_err_t res; while (1) { - vTaskDelay(pdMS_TO_TICKS(100)); + vTaskDelay(pdMS_TO_TICKS(500)); if ((res = tsl2591_get_lux(&dev, &lux)) != ESP_OK) printf("Could not read lux value: %d\n", res); @@ -34,4 +43,3 @@ void app_main() xTaskCreatePinnedToCore(tsl2591_test, "tsl2591_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); } - diff --git a/examples/veml7700/default/CMakeLists.txt b/examples/veml7700/default/CMakeLists.txt new file mode 100644 index 00000000..cf9c4e55 --- /dev/null +++ b/examples/veml7700/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-veml7700-i2c) diff --git a/examples/veml7700/default/Makefile b/examples/veml7700/default/Makefile new file mode 100644 index 00000000..da698c24 --- /dev/null +++ b/examples/veml7700/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-veml7700-i2c + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/veml7700/default/README.md b/examples/veml7700/default/README.md new file mode 100644 index 00000000..9d28dc3d --- /dev/null +++ b/examples/veml7700/default/README.md @@ -0,0 +1,48 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | + +# VEML7700 Example + +Configures an I2C attached VEML7700 ambient light sensor and then reads the values +every 1.2 seconds. + +## How to use example + +Follow detailed instructions provided specifically for this example. + +Select the instructions depending on Espressif chip installed on your development board: + +- [ESP32 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/stable/get-started/index.html) +- [ESP32-S2 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html) + +Use `idf.py menuconfig` to adapt the pin configuration to your setup. + +## Example folder contents + +The project **example-veml7700-i2c** contains one source file in C language [main.c](main/main.c). The file is located in folder [main](main). + +ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both). + +Besides, using make is also supported up to esp-idf 4.x and will opt out in 5.x + +Below is short explanation of remaining files in the project folder. + +``` +├── CMakeLists.txt +├── Makefile +├── main +│ ├── CMakeLists.txt +| ├── components.mk +| ├── Kconfig.projbuild This is the project configuration for menuconfig +│ └── main.c +└── README.md This is the file you are currently reading +``` + +For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html) of the ESP-IDF Programming Guide. + +## Troubleshooting + +* Program upload failure + + * Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs. + * The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again. diff --git a/examples/veml7700/default/main/CMakeLists.txt b/examples/veml7700/default/main/CMakeLists.txt new file mode 100644 index 00000000..ae4308a1 --- /dev/null +++ b/examples/veml7700/default/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS "") + +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/examples/veml7700/default/main/Kconfig.projbuild b/examples/veml7700/default/main/Kconfig.projbuild new file mode 100644 index 00000000..899679e6 --- /dev/null +++ b/examples/veml7700/default/main/Kconfig.projbuild @@ -0,0 +1,31 @@ +menu "Example Configuration" + + config EXAMPLE_SCL_GPIO_NUM + int "SCL GPIO Num" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_SDA_GPIO_NUM + int "SDA GPIO Num" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. + + config EXAMPLE_SCL_PULLUP_ENABLE + bool "SCL pullup enable" + default n + help + Enable internal pullup resistor for SCL. + + config EXAMPLE_SDA_PULLUP_ENABLE + bool "SDA pullup enable" + default n + help + Enable internal pullup resistor for SDA. + +endmenu diff --git a/examples/veml7700/default/main/component.mk b/examples/veml7700/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/veml7700/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/veml7700/default/main/main.c b/examples/veml7700/default/main/main.c new file mode 100644 index 00000000..bcd39ab2 --- /dev/null +++ b/examples/veml7700/default/main/main.c @@ -0,0 +1,134 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2022 Marc Luehr + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include + +#include +#include + +static const char *TAG = "VEML7700main"; + +#define SDA_GPIO_NUM CONFIG_EXAMPLE_SDA_GPIO_NUM +#define SCL_GPIO_NUM CONFIG_EXAMPLE_SCL_GPIO_NUM + +static bool compare_configuration(veml7700_config_t* config_a, veml7700_config_t* config_b) +{ + return (config_a->gain == config_b->gain) && + (config_a->gain == config_b->gain) && + (config_a->integration_time == config_b->integration_time) && + (config_a->persistence_protect == config_b->persistence_protect) && + (config_a->interrupt_enable == config_b->interrupt_enable) && + (config_a->shutdown == config_b->shutdown) && + (config_a->threshold_high == config_b->threshold_high) && + (config_a->threshold_low == config_b->threshold_low) && + (config_a->power_saving_mode == config_b->power_saving_mode) && + (config_a->power_saving_enable == config_b->power_saving_enable); +} + +void app_main(void) +{ + i2c_dev_t veml7700_device; + veml7700_config_t veml7700_configuration; + + memset(&veml7700_device, 0, sizeof(i2c_dev_t)); + memset(&veml7700_configuration, 0, sizeof(veml7700_config_t)); + + ESP_LOGI(TAG, "initializing hardware"); + + // remember to always init i2cdev first before using it + ESP_ERROR_CHECK(i2cdev_init()); + + // initialize the device struct + ESP_ERROR_CHECK(veml7700_init_desc(&veml7700_device, I2C_NUM_0, SDA_GPIO_NUM, SCL_GPIO_NUM)); + +#ifdef CONFIG_EXAMPLE_I2C_MASTER_SDA + veml7700_device.cfg.sda_pullup_en = true; +#endif + +#ifdef CONFIG_EXAMPLE_I2C_MASTER_SCL + veml7700_device.cfg.scl_pullup_en = true; +#endif + // check if the device is available + ESP_ERROR_CHECK(veml7700_probe(&veml7700_device)); + + /* set configuration parameters + * select gain 1/8 coastest resolution but the sensor will most likely not + * over-saturated + */ + veml7700_configuration.gain = VEML7700_GAIN_DIV_8; + + /* set the time to integrate the light values. more time will lead to a finer + * resolution but will be over-saturated earlier + */ + veml7700_configuration.integration_time = VEML7700_INTEGRATION_TIME_100MS; + + // interrupt is not used + veml7700_configuration.persistence_protect = VEML7700_PERSISTENCE_PROTECTION_4; + veml7700_configuration.interrupt_enable = 1; + veml7700_configuration.shutdown = 0; + + /* set power saving modes. This reduces the repeated measurement. When disabled + * the sensors performs one measurement cycle after the other (here: 100ms + * intergration time) setting the power save mode add a 1000ms sleep during + * measurements. + */ + veml7700_configuration.power_saving_mode = VEML7700_POWER_SAVING_MODE_1000MS; + veml7700_configuration.power_saving_enable = 1; + + // write the configuration to the device + ESP_ERROR_CHECK(veml7700_set_config(&veml7700_device, &veml7700_configuration)); + + /* read back the configuration for testing purposes. This step is not needed + * for a normal application + */ + veml7700_config_t veml7700_configuration_readback; + ESP_ERROR_CHECK(veml7700_get_config(&veml7700_device, &veml7700_configuration_readback)); + + if (compare_configuration(&veml7700_configuration_readback, + &veml7700_configuration_readback)) + { + ESP_LOGI(TAG, "Configuration read back matches"); + } + else + { + ESP_LOGE(TAG, "Configuration read back does not match"); + } + + while (1) + { + uint32_t als = 0; + uint32_t white = 0; + bool low_threshold = false; + bool high_threshold = false; + + veml7700_get_ambient_light(&veml7700_device, &veml7700_configuration, &als); + veml7700_get_white_channel(&veml7700_device, &veml7700_configuration, &white); + veml7700_get_white_channel(&veml7700_device, &veml7700_configuration, &white); + veml7700_get_interrupt_status(&veml7700_device, &low_threshold, + &high_threshold); + + ESP_LOGI(TAG, "ALS: %u lx", als); + ESP_LOGI(TAG, "WHITE: %u lx", white); + ESP_LOGI(TAG, "Interrupt status: low: %d high: %d", low_threshold, + high_threshold); + vTaskDelay(pdMS_TO_TICKS(1200)); + } +}