esp32 temperature node

This commit is contained in:
2025-10-04 01:37:30 -04:00
parent d7a3e891f1
commit 977e74b2e4
83 changed files with 4220 additions and 214 deletions

View File

@@ -1,213 +0,0 @@
components:
- name: "app_trace"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/app_trace"
- name: "app_update"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/app_update"
- name: "bootloader"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/bootloader"
- name: "bootloader_support"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/bootloader_support"
- name: "bt"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/bt"
- name: "cmock"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/cmock"
- name: "console"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/console"
- name: "cxx"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/cxx"
- name: "driver"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/driver"
- name: "efuse"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/efuse"
- name: "esp-tls"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp-tls"
- name: "esp_adc"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_adc"
- name: "esp_app_format"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_app_format"
- name: "esp_bootloader_format"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_bootloader_format"
- name: "esp_coex"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_coex"
- name: "esp_common"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_common"
- name: "esp_driver_ana_cmpr"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_ana_cmpr"
- name: "esp_driver_cam"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_cam"
- name: "esp_driver_dac"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_dac"
- name: "esp_driver_gpio"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_gpio"
- name: "esp_driver_gptimer"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_gptimer"
- name: "esp_driver_i2c"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_i2c"
- name: "esp_driver_i2s"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_i2s"
- name: "esp_driver_isp"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_isp"
- name: "esp_driver_jpeg"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_jpeg"
- name: "esp_driver_ledc"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_ledc"
- name: "esp_driver_mcpwm"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_mcpwm"
- name: "esp_driver_parlio"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_parlio"
- name: "esp_driver_pcnt"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_pcnt"
- name: "esp_driver_ppa"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_ppa"
- name: "esp_driver_rmt"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_rmt"
- name: "esp_driver_sdio"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_sdio"
- name: "esp_driver_sdm"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_sdm"
- name: "esp_driver_sdmmc"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_sdmmc"
- name: "esp_driver_sdspi"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_sdspi"
- name: "esp_driver_spi"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_spi"
- name: "esp_driver_touch_sens"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_touch_sens"
- name: "esp_driver_tsens"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_tsens"
- name: "esp_driver_uart"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_uart"
- name: "esp_driver_usb_serial_jtag"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_usb_serial_jtag"
- name: "esp_eth"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_eth"
- name: "esp_event"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_event"
- name: "esp_gdbstub"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_gdbstub"
- name: "esp_hid"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_hid"
- name: "esp_http_client"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_http_client"
- name: "esp_http_server"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_http_server"
- name: "esp_https_ota"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_https_ota"
- name: "esp_https_server"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_https_server"
- name: "esp_hw_support"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_hw_support"
- name: "esp_lcd"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_lcd"
- name: "esp_local_ctrl"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_local_ctrl"
- name: "esp_mm"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_mm"
- name: "esp_netif"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_netif"
- name: "esp_netif_stack"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_netif_stack"
- name: "esp_partition"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_partition"
- name: "esp_phy"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_phy"
- name: "esp_pm"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_pm"
- name: "esp_psram"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_psram"
- name: "esp_ringbuf"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_ringbuf"
- name: "esp_rom"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_rom"
- name: "esp_security"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_security"
- name: "esp_system"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_system"
- name: "esp_timer"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_timer"
- name: "esp_vfs_console"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_vfs_console"
- name: "esp_wifi"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_wifi"
- name: "espcoredump"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/espcoredump"
- name: "esptool_py"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esptool_py"
- name: "fatfs"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/fatfs"
- name: "freertos"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/freertos"
- name: "hal"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/hal"
- name: "heap"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/heap"
- name: "http_parser"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/http_parser"
- name: "idf_test"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/idf_test"
- name: "ieee802154"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/ieee802154"
- name: "json"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/json"
- name: "linux"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/linux"
- name: "log"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/log"
- name: "lwip"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/lwip"
- name: "mbedtls"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/mbedtls"
- name: "mqtt"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/mqtt"
- name: "newlib"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/newlib"
- name: "nvs_flash"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/nvs_flash"
- name: "nvs_sec_provider"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/nvs_sec_provider"
- name: "openthread"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/openthread"
- name: "partition_table"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/partition_table"
- name: "perfmon"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/perfmon"
- name: "protobuf-c"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/protobuf-c"
- name: "protocomm"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/protocomm"
- name: "pthread"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/pthread"
- name: "riscv"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/riscv"
- name: "rt"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/rt"
- name: "sdmmc"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/sdmmc"
- name: "soc"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/soc"
- name: "spi_flash"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/spi_flash"
- name: "spiffs"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/spiffs"
- name: "tcp_transport"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/tcp_transport"
- name: "touch_element"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/touch_element"
- name: "ulp"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/ulp"
- name: "unity"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/unity"
- name: "usb"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/usb"
- name: "vfs"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/vfs"
- name: "wear_levelling"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/wear_levelling"
- name: "wifi_provisioning"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/wifi_provisioning"
- name: "wpa_supplicant"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/wpa_supplicant"
- name: "xtensa"
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/xtensa"
- name: "main"
path: "/home/abobkov/MyProjects/ESP-Nodes/ESP32-IDF_Temperture-Node-v2/main"

View File

@@ -1,4 +1,70 @@
dependencies:
esp-idf-lib/bmp280:
component_hash: 82b01a37c200ed66a6c451026e2afdbe8046c6f702d41085e5a1d83aec3a90d8
dependencies:
- name: esp-idf-lib/esp_idf_lib_helpers
registry_url: https://components.espressif.com
require: private
version: '*'
- name: esp-idf-lib/i2cdev
registry_url: https://components.espressif.com
require: private
version: '*'
source:
registry_url: https://components.espressif.com/
type: service
targets:
- esp32
- esp32c2
- esp32c3
- esp32c5
- esp32c6
- esp32c61
- esp32h2
- esp32p4
- esp32s2
- esp32s3
version: 1.0.7
esp-idf-lib/esp_idf_lib_helpers:
component_hash: a8049b1e609679fb54b2d57b0399dd29c4d1fda09a797edac9926f7810aa5703
dependencies: []
source:
registry_url: https://components.espressif.com
type: service
targets:
- esp32
- esp32c2
- esp32c3
- esp32c5
- esp32c6
- esp32c61
- esp32h2
- esp32p4
- esp32s2
- esp32s3
version: 1.3.10
esp-idf-lib/i2cdev:
component_hash: 11c08f9e1a7d346b5dd763196dc2567cf2209ae49042402c2c2d296624601c14
dependencies:
- name: esp-idf-lib/esp_idf_lib_helpers
registry_url: https://components.espressif.com
require: private
version: '*'
source:
registry_url: https://components.espressif.com
type: service
targets:
- esp32
- esp32c2
- esp32c3
- esp32c5
- esp32c6
- esp32c61
- esp32h2
- esp32p4
- esp32s2
- esp32s3
version: 2.0.8
espressif/bme280:
component_hash: 873d97d0bd30004f45d1653f078a4bafe39c1767e57d4bae0f0a13bc3a4d5e3d
dependencies:
@@ -46,8 +112,9 @@ dependencies:
type: idf
version: 5.4.1
direct_dependencies:
- esp-idf-lib/bmp280
- espressif/bme280
- idf
manifest_hash: 56655df8035b9ce03ff888e9decae7c9c07a8c31eb7c6c080e638f76e687b00e
manifest_hash: 6a0a2f31ea59f281811a0fd0eb315dfa46b1a0f45d9af8d29ab20fcc8152290c
target: esp32
version: 2.0.0

View File

@@ -0,0 +1 @@
--ignore-dir=build

View File

@@ -0,0 +1,17 @@
--align-reference=name
--attach-classes
--attach-classes
--attach-namespaces
--convert-tabs
--exclude=build
--exclude=common
--exclude=managed_components
--ignore-exclude-errors
--indent-switches
--indent=spaces=4
--keep-one-line-statements
--max-continuation-indent=120
--pad-header
--pad-oper
--style=allman
--unpad-paren

View File

@@ -0,0 +1,66 @@
---
Language: Cpp
BasedOnStyle: WebKit
AlignConsecutiveMacros: true
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Always
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
ColumnLimit: 200
CompactNamespaces: true
Cpp11BracedListStyle: false
FixNamespaceComments: true
IndentCaseLabels: true
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 4
UseTab: Never

View File

@@ -0,0 +1 @@
82b01a37c200ed66a6c451026e2afdbe8046c6f702d41085e5a1d83aec3a90d8

View File

@@ -0,0 +1,32 @@
---
name: bmp280
description: Driver for BMP280/BME280 digital pressure sensor
version: 1.0.7
groups:
- pressure
- temperature
code_owners:
- UncleRus
depends:
- i2cdev
- log
- esp_idf_lib_helpers
thread_safe: yes
targets:
- esp32
- esp8266
- esp32s2
- esp32c3
- esp32s3
- esp32c2
- esp32c6
- esp32h2
- esp32p4
- esp32c5
- esp32c61
license: MIT
copyrights:
- name: sheinz
year: 2016
- name: UncleRus
year: 2018

View File

@@ -0,0 +1,7 @@
examples/**/sdkconfig
examples/**/sdkconfig.old
examples/**/build/
examples/**/dependencies.lock
docs/_*/
docs/doxygen.log
*.swp

View File

@@ -0,0 +1,3 @@
[submodule "common"]
path = common
url = https://github.com/esp-idf-lib/common.git

View File

@@ -0,0 +1 @@
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-07-31T11:56:00.065336+00:00", "files": [{"path": ".ackrc", "size": 19, "hash": "ad251fb2384460920327843ab53f696de85a385a179e9ff06f2dd496607a2f31"}, {"path": ".astylerc", "size": 324, "hash": "07a888d14b14e7d39e3e645c7f75addb9620ec9026248a5393baa63ef207556f"}, {"path": ".clang-format", "size": 1814, "hash": "b23e699f74909593d399c31f56d38e02c8a71c7dae0102813429e73520783101"}, {"path": ".eil.yml", "size": 457, "hash": "64746c5ae60a16c7b54aa010139564c531a855e8bf29c232914d73ed9a236875"}, {"path": ".gitignore", "size": 129, "hash": "44cc4e80bc8c455013cbdbd2338b690b62da9ded5368d66f4a576401e0aaa189"}, {"path": ".gitmodules", "size": 85, "hash": "bdbdfe664e912bb32da537de8e7137eccce5869bbe030b254bd96d31891295e5"}, {"path": "CMakeLists.txt", "size": 366, "hash": "ce941164a14cc769e45f931eb00cf84140df0464462d9c686ff41369d38d6c96"}, {"path": "Kconfig.i2c", "size": 848, "hash": "541dae0eb78e6309736d6e6f0d1c415c0881d65e7a6668e9135d4e0b55f56a89"}, {"path": "LICENSE", "size": 1164, "hash": "759ac97707f5e9ba2aa49c1672e47c560a54fa20cac1c51aac326c6f18379dfd"}, {"path": "README.md", "size": 1398, "hash": "e106e6a77e5b753cc23f8947340a16ff9b713402dc21219a8dcb728f5558ea0e"}, {"path": "bmp280.c", "size": 14401, "hash": "0c30035adbe635f5a48222b2a8bb22ff3a4cbbb763f0d9a3f9da6efea8c8243c"}, {"path": "bmp280.h", "size": 7280, "hash": "a9ad0b7e0cc7d31dfddfb2e15b4b981df0bac84c5c4b6c9216ed880737d61374"}, {"path": "component.mk", "size": 81, "hash": "5a79f4b9750e2189b11141694f761f5c13f9b38eee3dfd860bedd443fad81356"}, {"path": "idf_component.yml", "size": 738, "hash": "45e19c437f58d192ceb44792d475fd946f2f2baeba49c2ebb7ef7e52d6c92645"}, {"path": "bin/eil-q", "size": 399, "hash": "52bfaca6bfc34bba78c5707ed4013bac1839ef67ab4fa6feb8885b6face2c86a"}, {"path": "common/.git", "size": 31, "hash": "3c08a82d0d8b9fc7bb41dbf0d0836636c5b5e89a73243f77817467f7d1ffccbc"}, {"path": "examples/default/CMakeLists.txt", "size": 244, "hash": "b4b2f70858d4efc7b66f0544d91e338459483af7aa623af4ddb77411ba610a36"}, {"path": "examples/default/Makefile", "size": 77, "hash": "39df247069fd61b8a148bfa06d06c0fa1c11dff812bccd7b4b2c89882b01886f"}, {"path": "examples/default/README.md", "size": 684, "hash": "a0590f4cd58f0abae7c321d6a6969c9bd1626104ebbe1940b6f88ec27d9d130e"}, {"path": "examples/default/sdkconfig.defaults.esp8266", "size": 37, "hash": "91b9d5c57c163b5914b4e07c446b7cfa7a93dc754644675951452d934686b4ca"}, {"path": "examples/default/main/CMakeLists.txt", "size": 75, "hash": "27d6d39ef0cb6a9097984b41e080b09f7ff972339a0bb4ddf771db4001cb9782"}, {"path": "examples/default/main/Kconfig.projbuild", "size": 72, "hash": "8ca9b29c2c6f3253ffbe9307c8a5a960f608ea05241bce86e549b210c2ea7dd1"}, {"path": "examples/default/main/component.mk", "size": 39, "hash": "532e7836f005fd3b10f1955409e4956ad69fd386e80b12de11d96f8e84977843"}, {"path": "examples/default/main/idf_component.yml", "size": 180, "hash": "c64c9a1fb13f38ec33cb37ee91a058c922e43e37164884532223521f579d6899"}, {"path": "examples/default/main/main.c", "size": 1536, "hash": "e959afe2bc5b756ac04f16feeed24612c69b0113c0292fb66a0df47a26ca5665"}, {"path": "common/cmake/esp-idf-lib.cmake", "size": 1119, "hash": "ea0f8a819ac721dfbf5ab5f109c75c32c90c8060e6fcc9fbad1ce9de29451405"}]}

View File

@@ -0,0 +1,13 @@
idf_component_register(
SRCS bmp280.c
INCLUDE_DIRS .
REQUIRES i2cdev log esp_idf_lib_helpers
)
# include common cmake file for components
set(ESP_IDF_LIB_CMAKE ${CMAKE_CURRENT_LIST_DIR}/common/cmake/esp-idf-lib.cmake)
if(EXISTS ${ESP_IDF_LIB_CMAKE})
include(${ESP_IDF_LIB_CMAKE})
else()
message(WARNING "${ESP_IDF_LIB_CMAKE} not found")
endif()

View File

@@ -0,0 +1,19 @@
config EXAMPLE_I2C_MASTER_SCL
int "SCL GPIO Number"
default 5 if IDF_TARGET_ESP8266
default 6 if IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C61
default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 4 if IDF_TARGET_ESP32H2
default 4 if IDF_TARGET_ESP32P4
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_ESP32C2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C61
default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 3 if IDF_TARGET_ESP32H2
default 3 if IDF_TARGET_ESP32P4
help
GPIO number for I2C Master data line.

View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2016 sheinz (https://github.com/sheinz)
Copyright (c) 2018 Ruslan V. Uss (https://github.com/UncleRus)
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.

View File

@@ -0,0 +1,30 @@
# esp-idf-lib/bmp280
[![Build examples](https://github.com/esp-idf-lib/bmp280/actions/workflows//build.yml/badge.svg)](https://github.com/esp-idf-lib/bmp280/actions/workflows//build.yml)
[![Build docs](https://github.com/esp-idf-lib/bmp280/actions/workflows//build-docs.yml/badge.svg)](https://github.com/esp-idf-lib/bmp280/actions/workflows//build-docs.yml)
[![Validation](https://github.com/esp-idf-lib/bmp280/actions/workflows//validate-component.yml/badge.svg)](https://github.com/esp-idf-lib/bmp280/actions/workflows//validate-component.yml)
Driver for BMP280/BME280 digital pressure sensor.
* [Documentation](https://esp-idf-lib.github.io/bmp280/)
* [Repository](https://github.com/esp-idf-lib/bmp280)
* [Issues](https://github.com/esp-idf-lib/bmp280/issues)
* [Discussions and questions](https://github.com/esp-idf-lib/core/discussions)
* [Component page at the ESP Component Registry](https://components.espressif.com/components/esp-idf-lib/bmp280)
## Installation
```sh
idf.py add-dependency esp-idf-lib/bmp280
```
## Support
For questions and discussions about the component, please use
[Discussions](https://github.com/esp-idf-lib/core/discussions)
at [esp-idf-lib/core](https://github.com/esp-idf-lib/core).
## Contributing
Please read [CONTRIBUTING.md](https://github.com/esp-idf-lib/core/blob/main/CONTRIBUTING.md)
at [esp-idf-lib/core](https://github.com/esp-idf-lib/core).

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env ruby
require "pathname"
require "yaml"
# A simple CLI to get values in .eil.yml
key = ARGV.shift
project_root = Pathname.new(File.expand_path(__FILE__)).parent.parent
eil_file = project_root / ".eil.yml"
doc = YAML.safe_load(File.read eil_file)
case key
when "copyright_string"
puts doc["copyrights"].map { |e| "#{e['year']}, #{e['name']}" }.join(", ")
else
puts doc[key]
end

View File

@@ -0,0 +1,411 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 sheinz <https://github.com/sheinz>
* Copyright (c) 2018 Ruslan V. Uss <unclerus@gmail.com>
*
* 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 bmp280.c
*
* ESP-IDF driver for BMP280/BME280 digital pressure sensor
*
* Ported from esp-open-rtos
*
* Copyright (c) 2016 sheinz <https://github.com/sheinz>\n
* Copyright (c) 2018 Ruslan V. Uss <unclerus@gmail.com>
*
* MIT Licensed as described in the file LICENSE
*/
#include "bmp280.h"
#include <inttypes.h>
#include <esp_log.h>
#include <esp_idf_lib_helpers.h>
#define I2C_FREQ_HZ 1000000 // Max 1MHz for esp-idf
static const char *TAG = "bmp280";
/**
* BMP280 registers
*/
#define BMP280_REG_TEMP_XLSB 0xFC /* bits: 7-4 */
#define BMP280_REG_TEMP_LSB 0xFB
#define BMP280_REG_TEMP_MSB 0xFA
#define BMP280_REG_TEMP (BMP280_REG_TEMP_MSB)
#define BMP280_REG_PRESS_XLSB 0xF9 /* bits: 7-4 */
#define BMP280_REG_PRESS_LSB 0xF8
#define BMP280_REG_PRESS_MSB 0xF7
#define BMP280_REG_PRESSURE (BMP280_REG_PRESS_MSB)
#define BMP280_REG_CONFIG 0xF5 /* bits: 7-5 t_sb; 4-2 filter; 0 spi3w_en */
#define BMP280_REG_CTRL 0xF4 /* bits: 7-5 osrs_t; 4-2 osrs_p; 1-0 mode */
#define BMP280_REG_STATUS 0xF3 /* bits: 3 measuring; 0 im_update */
#define BMP280_REG_CTRL_HUM 0xF2 /* bits: 2-0 osrs_h; */
#define BMP280_REG_RESET 0xE0
#define BMP280_REG_ID 0xD0
#define BMP280_REG_CALIB 0x88
#define BMP280_REG_HUM_CALIB 0x88
#define BMP280_RESET_VALUE 0xB6
#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)
static esp_err_t read_register16(i2c_dev_t *dev, uint8_t reg, uint16_t *r)
{
uint8_t d[] = { 0, 0 };
CHECK(i2c_dev_read_reg(dev, reg, d, 2));
*r = d[0] | (d[1] << 8);
return ESP_OK;
}
inline static esp_err_t write_register8(i2c_dev_t *dev, uint8_t addr, uint8_t value)
{
return i2c_dev_write_reg(dev, addr, &value, 1);
}
static esp_err_t read_calibration_data(bmp280_t *dev)
{
CHECK(read_register16(&dev->i2c_dev, 0x88, &dev->dig_T1));
CHECK(read_register16(&dev->i2c_dev, 0x8a, (uint16_t *)&dev->dig_T2));
CHECK(read_register16(&dev->i2c_dev, 0x8c, (uint16_t *)&dev->dig_T3));
CHECK(read_register16(&dev->i2c_dev, 0x8e, &dev->dig_P1));
CHECK(read_register16(&dev->i2c_dev, 0x90, (uint16_t *)&dev->dig_P2));
CHECK(read_register16(&dev->i2c_dev, 0x92, (uint16_t *)&dev->dig_P3));
CHECK(read_register16(&dev->i2c_dev, 0x94, (uint16_t *)&dev->dig_P4));
CHECK(read_register16(&dev->i2c_dev, 0x96, (uint16_t *)&dev->dig_P5));
CHECK(read_register16(&dev->i2c_dev, 0x98, (uint16_t *)&dev->dig_P6));
CHECK(read_register16(&dev->i2c_dev, 0x9a, (uint16_t *)&dev->dig_P7));
CHECK(read_register16(&dev->i2c_dev, 0x9c, (uint16_t *)&dev->dig_P8));
CHECK(read_register16(&dev->i2c_dev, 0x9e, (uint16_t *)&dev->dig_P9));
ESP_LOGD(TAG, "Calibration data received:");
ESP_LOGD(TAG, "dig_T1=%d", dev->dig_T1);
ESP_LOGD(TAG, "dig_T2=%d", dev->dig_T2);
ESP_LOGD(TAG, "dig_T3=%d", dev->dig_T3);
ESP_LOGD(TAG, "dig_P1=%d", dev->dig_P1);
ESP_LOGD(TAG, "dig_P2=%d", dev->dig_P2);
ESP_LOGD(TAG, "dig_P3=%d", dev->dig_P3);
ESP_LOGD(TAG, "dig_P4=%d", dev->dig_P4);
ESP_LOGD(TAG, "dig_P5=%d", dev->dig_P5);
ESP_LOGD(TAG, "dig_P6=%d", dev->dig_P6);
ESP_LOGD(TAG, "dig_P7=%d", dev->dig_P7);
ESP_LOGD(TAG, "dig_P8=%d", dev->dig_P8);
ESP_LOGD(TAG, "dig_P9=%d", dev->dig_P9);
return ESP_OK;
}
static esp_err_t read_hum_calibration_data(bmp280_t *dev)
{
uint16_t h4, h5;
CHECK(i2c_dev_read_reg(&dev->i2c_dev, 0xa1, &dev->dig_H1, 1));
CHECK(read_register16(&dev->i2c_dev, 0xe1, (uint16_t *)&dev->dig_H2));
CHECK(i2c_dev_read_reg(&dev->i2c_dev, 0xe3, &dev->dig_H3, 1));
CHECK(read_register16(&dev->i2c_dev, 0xe4, &h4));
CHECK(read_register16(&dev->i2c_dev, 0xe5, &h5));
CHECK(i2c_dev_read_reg(&dev->i2c_dev, 0xe7, (uint8_t *)&dev->dig_H6, 1));
dev->dig_H4 = (h4 & 0x00ff) << 4 | (h4 & 0x0f00) >> 8;
dev->dig_H5 = h5 >> 4;
ESP_LOGD(TAG, "Calibration data received:");
ESP_LOGD(TAG, "dig_H1=%d", dev->dig_H1);
ESP_LOGD(TAG, "dig_H2=%d", dev->dig_H2);
ESP_LOGD(TAG, "dig_H3=%d", dev->dig_H3);
ESP_LOGD(TAG, "dig_H4=%d", dev->dig_H4);
ESP_LOGD(TAG, "dig_H5=%d", dev->dig_H5);
ESP_LOGD(TAG, "dig_H6=%d", dev->dig_H6);
return ESP_OK;
}
esp_err_t bmp280_init_desc(bmp280_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio)
{
CHECK_ARG(dev);
if (addr != BMP280_I2C_ADDRESS_0 && addr != BMP280_I2C_ADDRESS_1)
{
ESP_LOGE(TAG, "Invalid I2C address");
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 bmp280_free_desc(bmp280_t *dev)
{
CHECK_ARG(dev);
return i2c_dev_delete_mutex(&dev->i2c_dev);
}
esp_err_t bmp280_init_default_params(bmp280_params_t *params)
{
CHECK_ARG(params);
params->mode = BMP280_MODE_NORMAL;
params->filter = BMP280_FILTER_OFF;
params->oversampling_pressure = BMP280_STANDARD;
params->oversampling_temperature = BMP280_STANDARD;
params->oversampling_humidity = BMP280_STANDARD;
params->standby = BMP280_STANDBY_250;
return ESP_OK;
}
esp_err_t bmp280_init(bmp280_t *dev, bmp280_params_t *params)
{
CHECK_ARG(dev && params);
I2C_DEV_TAKE_MUTEX(&dev->i2c_dev);
CHECK_LOGE(dev, i2c_dev_read_reg(&dev->i2c_dev, BMP280_REG_ID, &dev->id, 1), "Sensor not found");
if (dev->id != BMP280_CHIP_ID && dev->id != BME280_CHIP_ID)
{
CHECK_LOGE(dev, ESP_ERR_INVALID_VERSION,
"Invalid chip ID: expected: 0x%x (BME280) or 0x%x (BMP280) got: 0x%x",
BME280_CHIP_ID, BMP280_CHIP_ID, dev->id);
}
// Soft reset.
CHECK_LOGE(dev, write_register8(&dev->i2c_dev, BMP280_REG_RESET, BMP280_RESET_VALUE), "Failed to reset sensor");
// Wait until finished copying over the NVP data.
while (1)
{
uint8_t status;
if (!i2c_dev_read_reg(&dev->i2c_dev, BMP280_REG_STATUS, &status, 1) && (status & 1) == 0)
break;
}
CHECK_LOGE(dev, read_calibration_data(dev), "Failed to read calibration data");
if (dev->id == BME280_CHIP_ID)
{
CHECK_LOGE(dev, read_hum_calibration_data(dev), "Failed to read humidity calibration data");
}
uint8_t config = (params->standby << 5) | (params->filter << 2);
ESP_LOGD(TAG, "Writing config reg=%x", config);
CHECK_LOGE(dev, write_register8(&dev->i2c_dev, BMP280_REG_CONFIG, config), "Failed to configure sensor");
if (params->mode == BMP280_MODE_FORCED)
{
params->mode = BMP280_MODE_SLEEP; // initial mode for forced is sleep
}
uint8_t ctrl = (params->oversampling_temperature << 5) | (params->oversampling_pressure << 2) | (params->mode);
if (dev->id == BME280_CHIP_ID)
{
// Write crtl hum reg first, only active after write to BMP280_REG_CTRL.
uint8_t ctrl_hum = params->oversampling_humidity;
ESP_LOGD(TAG, "Writing ctrl hum reg=%x", ctrl_hum);
CHECK_LOGE(dev, write_register8(&dev->i2c_dev, BMP280_REG_CTRL_HUM, ctrl_hum), "Failed to control sensor");
}
ESP_LOGD(TAG, "Writing ctrl reg=%x", ctrl);
CHECK_LOGE(dev, write_register8(&dev->i2c_dev, BMP280_REG_CTRL, ctrl), "Failed to control sensor");
I2C_DEV_GIVE_MUTEX(&dev->i2c_dev);
return ESP_OK;
}
esp_err_t bmp280_force_measurement(bmp280_t *dev)
{
CHECK_ARG(dev);
I2C_DEV_TAKE_MUTEX(&dev->i2c_dev);
uint8_t ctrl;
I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, BMP280_REG_CTRL, &ctrl, 1));
ctrl &= ~0b11; // clear two lower bits
ctrl |= BMP280_MODE_FORCED;
ESP_LOGD(TAG, "Writing ctrl reg=%x", ctrl);
CHECK_LOGE(dev, write_register8(&dev->i2c_dev, BMP280_REG_CTRL, ctrl), "Failed to start forced mode");
I2C_DEV_GIVE_MUTEX(&dev->i2c_dev);
return ESP_OK;
}
esp_err_t bmp280_is_measuring(bmp280_t *dev, bool *busy)
{
CHECK_ARG(dev && busy);
I2C_DEV_TAKE_MUTEX(&dev->i2c_dev);
const uint8_t regs[2] = { BMP280_REG_STATUS, BMP280_REG_CTRL };
uint8_t status[2];
CHECK_LOGE(dev, i2c_dev_read(&dev->i2c_dev, regs, 2, status, 2), "Failed to read status registers");
// Check mode - FORCED means BM280 is busy (it switches to SLEEP mode when finished)
// Additionally, check 'measuring' bit in status register
*busy = ((status[1] & 0b11) == BMP280_MODE_FORCED) || (status[0] & (1 << 3));
I2C_DEV_GIVE_MUTEX(&dev->i2c_dev);
return ESP_OK;
}
/**
* Compensation algorithm is taken from BMP280 datasheet.
*
* Return value is in degrees Celsius.
*/
static inline int32_t compensate_temperature(bmp280_t *dev, int32_t adc_temp, int32_t *fine_temp)
{
int32_t var1, var2;
var1 = ((((adc_temp >> 3) - ((int32_t)dev->dig_T1 << 1))) * (int32_t)dev->dig_T2) >> 11;
var2 = (((((adc_temp >> 4) - (int32_t)dev->dig_T1) * ((adc_temp >> 4) - (int32_t)dev->dig_T1)) >> 12) * (int32_t)dev->dig_T3) >> 14;
*fine_temp = var1 + var2;
return (*fine_temp * 5 + 128) >> 8;
}
/**
* Compensation algorithm is taken from BMP280 datasheet.
*
* Return value is in Pa, 24 integer bits and 8 fractional bits.
*/
static inline uint32_t compensate_pressure(bmp280_t *dev, int32_t adc_press, int32_t fine_temp)
{
int64_t var1, var2, p;
var1 = (int64_t)fine_temp - 128000;
var2 = var1 * var1 * (int64_t)dev->dig_P6;
var2 = var2 + ((var1 * (int64_t)dev->dig_P5) << 17);
var2 = var2 + (((int64_t)dev->dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)dev->dig_P3) >> 8) + ((var1 * (int64_t)dev->dig_P2) << 12);
var1 = (((int64_t)1 << 47) + var1) * ((int64_t)dev->dig_P1) >> 33;
if (var1 == 0)
{
return 0; // avoid exception caused by division by zero
}
p = 1048576 - adc_press;
p = (((p << 31) - var2) * 3125) / var1;
var1 = ((int64_t)dev->dig_P9 * (p >> 13) * (p >> 13)) >> 25;
var2 = ((int64_t)dev->dig_P8 * p) >> 19;
p = ((p + var1 + var2) >> 8) + ((int64_t)dev->dig_P7 << 4);
return p;
}
/**
* Compensation algorithm is taken from BME280 datasheet.
*
* Return value is in Pa, 24 integer bits and 8 fractional bits.
*/
static inline uint32_t compensate_humidity(bmp280_t *dev, int32_t adc_hum, int32_t fine_temp)
{
int32_t v_x1_u32r;
v_x1_u32r = fine_temp - (int32_t)76800;
v_x1_u32r = ((((adc_hum << 14) - ((int32_t)dev->dig_H4 << 20) - ((int32_t)dev->dig_H5 * v_x1_u32r)) + (int32_t)16384) >> 15)
* (((((((v_x1_u32r * (int32_t)dev->dig_H6) >> 10) * (((v_x1_u32r * (int32_t)dev->dig_H3) >> 11) + (int32_t)32768)) >> 10)
+ (int32_t)2097152) * (int32_t)dev->dig_H2 + 8192) >> 14);
v_x1_u32r = v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * (int32_t)dev->dig_H1) >> 4);
v_x1_u32r = v_x1_u32r < 0 ? 0 : v_x1_u32r;
v_x1_u32r = v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r;
return v_x1_u32r >> 12;
}
esp_err_t bmp280_read_fixed(bmp280_t *dev, int32_t *temperature, uint32_t *pressure, uint32_t *humidity)
{
CHECK_ARG(dev && temperature && pressure);
int32_t adc_pressure;
int32_t adc_temp;
uint8_t data[8];
// Only the BME280 supports reading the humidity.
if (dev->id != BME280_CHIP_ID)
{
if (humidity)
*humidity = 0;
humidity = NULL;
}
I2C_DEV_TAKE_MUTEX(&dev->i2c_dev);
// Need to read in one sequence to ensure they match.
size_t size = humidity ? 8 : 6;
CHECK_LOGE(dev, i2c_dev_read_reg(&dev->i2c_dev, 0xf7, data, size), "Failed to read data");
adc_pressure = data[0] << 12 | data[1] << 4 | data[2] >> 4;
adc_temp = data[3] << 12 | data[4] << 4 | data[5] >> 4;
ESP_LOGD(TAG, "ADC temperature: %" PRIi32, adc_temp);
ESP_LOGD(TAG, "ADC pressure: %" PRIi32, adc_pressure);
int32_t fine_temp;
*temperature = compensate_temperature(dev, adc_temp, &fine_temp);
*pressure = compensate_pressure(dev, adc_pressure, fine_temp);
if (humidity)
{
int32_t adc_humidity = data[6] << 8 | data[7];
ESP_LOGD(TAG, "ADC humidity: %" PRIi32, adc_humidity);
*humidity = compensate_humidity(dev, adc_humidity, fine_temp);
}
I2C_DEV_GIVE_MUTEX(&dev->i2c_dev);
return ESP_OK;
}
esp_err_t bmp280_read_float(bmp280_t *dev, float *temperature, float *pressure, float *humidity)
{
int32_t fixed_temperature;
uint32_t fixed_pressure;
uint32_t fixed_humidity;
CHECK(bmp280_read_fixed(dev, &fixed_temperature, &fixed_pressure, humidity ? &fixed_humidity : NULL));
*temperature = (float)fixed_temperature / 100;
*pressure = (float)fixed_pressure / 256;
if (humidity)
*humidity = (float)fixed_humidity / 1024;
return ESP_OK;
}

View File

@@ -0,0 +1,256 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 sheinz <https://github.com/sheinz>
* Copyright (c) 2018 Ruslan V. Uss <unclerus@gmail.com>
*
* 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 bmp280.h
* @defgroup bmp280 bmp280
* @{
*
* ESP-IDF driver for BMP280/BME280 digital pressure sensor
*
* Ported from esp-open-rtos
*
* Copyright (c) 2016 sheinz <https://github.com/sheinz>\n
* Copyright (c) 2018 Ruslan V. Uss <unclerus@gmail.com>
*
* MIT Licensed as described in the file LICENSE
*/
#ifndef __BMP280_H__
#define __BMP280_H__
#include <stdint.h>
#include <stdbool.h>
#include <esp_err.h>
#include <i2cdev.h>
#ifdef __cplusplus
extern "C" {
#endif
#define BMP280_I2C_ADDRESS_0 0x76 //!< I2C address when SDO pin is low
#define BMP280_I2C_ADDRESS_1 0x77 //!< I2C address when SDO pin is high
#define BMP280_CHIP_ID 0x58 //!< BMP280 has chip-id 0x58
#define BME280_CHIP_ID 0x60 //!< BME280 has chip-id 0x60
/**
* Mode of BMP280 module operation.
*/
typedef enum
{
BMP280_MODE_SLEEP = 0, //!< Sleep mode
BMP280_MODE_FORCED = 1, //!< Measurement is initiated by user
BMP280_MODE_NORMAL = 3 //!< Continues measurement
} BMP280_Mode;
typedef enum
{
BMP280_FILTER_OFF = 0,
BMP280_FILTER_2 = 1,
BMP280_FILTER_4 = 2,
BMP280_FILTER_8 = 3,
BMP280_FILTER_16 = 4
} BMP280_Filter;
/**
* Pressure oversampling settings
*/
typedef enum
{
BMP280_SKIPPED = 0, //!< no measurement
BMP280_ULTRA_LOW_POWER = 1, //!< oversampling x1
BMP280_LOW_POWER = 2, //!< oversampling x2
BMP280_STANDARD = 3, //!< oversampling x4
BMP280_HIGH_RES = 4, //!< oversampling x8
BMP280_ULTRA_HIGH_RES = 5 //!< oversampling x16
} BMP280_Oversampling;
/**
* Stand by time between measurements in normal mode
*/
typedef enum
{
BMP280_STANDBY_05 = 0, //!< stand by time 0.5ms
BMP280_STANDBY_62 = 1, //!< stand by time 62.5ms
BMP280_STANDBY_125 = 2, //!< stand by time 125ms
BMP280_STANDBY_250 = 3, //!< stand by time 250ms
BMP280_STANDBY_500 = 4, //!< stand by time 500ms
BMP280_STANDBY_1000 = 5, //!< stand by time 1s
BMP280_STANDBY_2000 = 6, //!< stand by time 2s BMP280, 10ms BME280
BMP280_STANDBY_4000 = 7, //!< stand by time 4s BMP280, 20ms BME280
} BMP280_StandbyTime;
/**
* Configuration parameters for BMP280 module.
* Use function ::bmp280_init_default_params() to use default configuration.
*/
typedef struct
{
BMP280_Mode mode;
BMP280_Filter filter;
BMP280_Oversampling oversampling_pressure;
BMP280_Oversampling oversampling_temperature;
BMP280_Oversampling oversampling_humidity;
BMP280_StandbyTime standby;
} bmp280_params_t;
/**
* Device descriptor
*/
typedef struct
{
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
/* Humidity compensation for BME280 */
uint8_t dig_H1;
int16_t dig_H2;
uint8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;
i2c_dev_t i2c_dev; //!< I2C device descriptor
uint8_t id; //!< Chip ID
} bmp280_t;
/**
* @brief Initialize device descriptor
*
* @param dev Device descriptor
* @param addr BMP280 address
* @param port I2C port number
* @param sda_gpio GPIO pin for SDA
* @param scl_gpio GPIO pin for SCL
* @return `ESP_OK` on success
*/
esp_err_t bmp280_init_desc(bmp280_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 bmp280_free_desc(bmp280_t *dev);
/**
* @brief Initialize default parameters
*
* Default configuration:
*
* - mode: NORMAL
* - filter: OFF
* - oversampling: x4
* - standby time: 250ms
*
* @param[out] params Default parameters
* @return `ESP_OK` on success
*/
esp_err_t bmp280_init_default_params(bmp280_params_t *params);
/**
* @brief Initialize BMP280 module
*
* Probes for the device, soft resets the device, reads the calibration
* constants, and configures the device using the supplied parameters.
*
* This may be called again to soft reset the device and initialize it again.
*
* @param dev Device descriptor
* @param params Parameters
* @return `ESP_OK` on success
*/
esp_err_t bmp280_init(bmp280_t *dev, bmp280_params_t *params);
/**
* @brief Start measurement in forced mode
*
* The module remains in forced mode after this call.
* Do not call this method in normal mode.
*
* @param dev Device descriptor
* @return `ESP_OK` on success
*/
esp_err_t bmp280_force_measurement(bmp280_t *dev);
/**
* @brief Check if BMP280 is busy
*
* @param dev Device descriptor
* @param[out] busy true if BMP280 measures temperature/pressure
* @return `ESP_OK` on success
*/
esp_err_t bmp280_is_measuring(bmp280_t *dev, bool *busy);
/**
* @brief Read raw compensated temperature and pressure data
*
* Temperature in degrees Celsius times 100.
*
* Pressure in Pascals in fixed point 24 bit integer 8 bit fraction format.
*
* Humidity is optional and only read for the BME280, in percent relative
* humidity as a fixed point 22 bit integer and 10 bit fraction format.
*
* @param dev Device descriptor
* @param[out] temperature Temperature, deg.C * 100
* @param[out] pressure Pressure
* @param[out] humidity Humidity, optional
* @return `ESP_OK` on success
*/
esp_err_t bmp280_read_fixed(bmp280_t *dev, int32_t *temperature,
uint32_t *pressure, uint32_t *humidity);
/**
* @brief Read compensated temperature and pressure data
*
* Humidity is optional and only read for the BME280.
*
* @param dev Device descriptor
* @param[out] temperature Temperature, deg.C
* @param[out] pressure Pressure, Pascal
* @param[out] humidity Relative humidity, percents (optional)
* @return `ESP_OK` on success
*/
esp_err_t bmp280_read_float(bmp280_t *dev, float *temperature,
float *pressure, float *humidity);
#ifdef __cplusplus
}
#endif
/**@}*/
#endif // __BMP280_H__

View File

@@ -0,0 +1,36 @@
# Set common build flags but enable them only when the build is in our CI,
# making them optional. The idea is, fail when compiled in our CI but just
# warn in any other cases. Code that compiles with one toolchain warning-free
# may not do so with another toolchain, which creates a project dependency on
# specific toolchain vendors and versions.
#
# Define flags that may cause failures here.
if (DEFINED ENV{ESP_IDF_LIB_CI})
set(ESP_IDF_LIB_CI_FLAGS
-Werror=unused-variable
-Werror=unused-function
-Werror=write-strings
-Werror
)
endif()
# Set common build flags. Mandatory.
#
# Define flags that do not cause failures here.
set(ESP_IDF_LIB_FLAGS
-Wextra
-Wwrite-strings
-Wunused-variable
-Wunused-function
-Wreturn-type
)
# When COMPONENT_LIB is INTERFACE_LIBRARY, or a header-only library, do not
# set the flags.
get_target_property(COMPONENT_TYPE ${COMPONENT_LIB} TYPE)
if(NOT COMPONENT_TYPE STREQUAL "INTERFACE_LIBRARY")
target_compile_options(${COMPONENT_LIB} PRIVATE
${ESP_IDF_LIB_FLAGS}
${ESP_IDF_LIB_CI_FLAGS}
)
endif()

View File

@@ -0,0 +1,2 @@
COMPONENT_ADD_INCLUDEDIRS = .
COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers

View File

@@ -0,0 +1,7 @@
# 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)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(example-bmp280)

View File

@@ -0,0 +1,5 @@
#V := 1
PROJECT_NAME := example-bmp280
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,21 @@
# Example for `bmp280` driver
## What it does
It shows device name, `BME280` or `BMP280`, found on I2C bus.
It shows 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
`CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL` must be `y` on `esp8266`.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
menu "Example configuration"
rsource "../../../Kconfig.i2c"
endmenu

View File

@@ -0,0 +1 @@
COMPONENT_ADD_INCLUDEDIRS = . include/

View File

@@ -0,0 +1,9 @@
dependencies:
esp-idf-lib/bmp280:
version: '*'
esp-idf-lib/esp_idf_lib_helpers:
version: '*'
esp-idf-lib/i2cdev:
version: '*'
description: default
version: 1.0.0

View File

@@ -0,0 +1,53 @@
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_system.h>
#include <bmp280.h>
#include <string.h>
#ifndef APP_CPU_NUM
#define APP_CPU_NUM PRO_CPU_NUM
#endif
void bmp280_test(void *pvParameters)
{
bmp280_params_t params;
bmp280_init_default_params(&params);
bmp280_t dev;
memset(&dev, 0, sizeof(bmp280_t));
ESP_ERROR_CHECK(bmp280_init_desc(&dev, BMP280_I2C_ADDRESS_0, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL));
ESP_ERROR_CHECK(bmp280_init(&dev, &params));
bool bme280p = dev.id == BME280_CHIP_ID;
printf("BMP280: found %s\n", bme280p ? "BME280" : "BMP280");
float pressure, temperature, humidity;
while (1)
{
vTaskDelay(pdMS_TO_TICKS(500));
if (bmp280_read_float(&dev, &temperature, &pressure, &humidity) != ESP_OK)
{
printf("Temperature/pressure reading failed\n");
continue;
}
/* 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
*/
printf("Pressure: %.2f Pa, Temperature: %.2f C", pressure, temperature);
if (bme280p)
printf(", Humidity: %.2f\n", humidity);
else
printf("\n");
}
}
void app_main()
{
ESP_ERROR_CHECK(i2cdev_init());
xTaskCreatePinnedToCore(bmp280_test, "bmp280_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM);
}

View File

@@ -0,0 +1 @@
CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y

View File

@@ -0,0 +1,32 @@
dependencies:
esp-idf-lib/esp_idf_lib_helpers:
version: '*'
esp-idf-lib/i2cdev:
version: '*'
description: Driver for BMP280/BME280 digital pressure sensor
discussion: https://github.com/esp-idf-lib/core/discussions
documentation: https://esp-idf-lib.github.io/bmp280/
files:
exclude:
- docs/**/*
issues: https://github.com/esp-idf-lib/bmp280/issues
license: MIT
maintainers:
- Ruslan V. Uss (@UncleRus) <unclerus@gmail.com>
repository: git://github.com/esp-idf-lib/bmp280.git
repository_info:
commit_sha: ba9aaa92d7416740703b044a53462c0e9b8ee4e8
path: .
targets:
- esp32
- esp32c2
- esp32c3
- esp32c5
- esp32c6
- esp32c61
- esp32h2
- esp32p4
- esp32s2
- esp32s3
url: https://github.com/esp-idf-lib/core
version: 1.0.7

View File

@@ -0,0 +1 @@
--ignore-dir=build

View File

@@ -0,0 +1,17 @@
--align-reference=name
--attach-classes
--attach-classes
--attach-namespaces
--convert-tabs
--exclude=build
--exclude=common
--exclude=managed_components
--ignore-exclude-errors
--indent-switches
--indent=spaces=4
--keep-one-line-statements
--max-continuation-indent=120
--pad-header
--pad-oper
--style=allman
--unpad-paren

View File

@@ -0,0 +1,66 @@
---
Language: Cpp
BasedOnStyle: WebKit
AlignConsecutiveMacros: true
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Always
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
ColumnLimit: 200
CompactNamespaces: true
Cpp11BracedListStyle: false
FixNamespaceComments: true
IndentCaseLabels: true
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 4
UseTab: Never

View File

@@ -0,0 +1 @@
a8049b1e609679fb54b2d57b0399dd29c4d1fda09a797edac9926f7810aa5703

View File

@@ -0,0 +1,28 @@
---
name: esp_idf_lib_helpers
description: Common support library for esp-idf-lib
version: 1.3.10
groups:
- common
code_owners:
- trombik
- UncleRus
depends:
- freertos
thread_safe: n/a
targets:
- esp32
- esp8266
- esp32s2
- esp32c3
- esp32s3
- esp32c2
- esp32c6
- esp32h2
- esp32p4
- esp32c5
- esp32c61
license: ISC
copyrights:
- name: trombik
year: 2019

View File

@@ -0,0 +1,7 @@
examples/**/sdkconfig
examples/**/sdkconfig.old
examples/**/build/
examples/**/dependencies.lock
docs/_*/
docs/doxygen.log
*.swp

View File

@@ -0,0 +1,3 @@
[submodule "common"]
path = common
url = https://github.com/esp-idf-lib/common.git

View File

@@ -0,0 +1 @@
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-07-31T11:58:03.784223+00:00", "files": [{"path": ".ackrc", "size": 19, "hash": "ad251fb2384460920327843ab53f696de85a385a179e9ff06f2dd496607a2f31"}, {"path": ".astylerc", "size": 324, "hash": "07a888d14b14e7d39e3e645c7f75addb9620ec9026248a5393baa63ef207556f"}, {"path": ".clang-format", "size": 1814, "hash": "b23e699f74909593d399c31f56d38e02c8a71c7dae0102813429e73520783101"}, {"path": ".eil.yml", "size": 392, "hash": "bca7818e8535faa07a803f8128f11a27dcad7a2b3d39f9f613de2234489eade3"}, {"path": ".gitignore", "size": 129, "hash": "44cc4e80bc8c455013cbdbd2338b690b62da9ded5368d66f4a576401e0aaa189"}, {"path": ".gitmodules", "size": 85, "hash": "bdbdfe664e912bb32da537de8e7137eccce5869bbe030b254bd96d31891295e5"}, {"path": "CMakeLists.txt", "size": 326, "hash": "bf2befdeb8c2cb5e647057b4838d3020b7f4e5848500ab53498c1356b30722a2"}, {"path": "LICENSE", "size": 747, "hash": "8af15098bd9750efab7c1fce707e3826e269de336a0186ee3d87c29756f2e48d"}, {"path": "README.md", "size": 1544, "hash": "b0877176e744b8f63555cee4ea5af15f7c352eb31b6fa60babe6f51a75819f8c"}, {"path": "component.mk", "size": 141, "hash": "17a9935132097f4e1e39cf5c462434049cdcf2a605ea626d01588902a15e9ee6"}, {"path": "esp_idf_lib_helpers.h", "size": 3893, "hash": "2000c263dbe8de1e85d667ff255fed76f8640b218f6ac743bcbf82c6f716a605"}, {"path": "ets_sys.h", "size": 814, "hash": "d3d72ec5eee58d270843a944905b9f6b2855316a68dfa62a9cdb9d3a2f67a371"}, {"path": "idf_component.yml", "size": 726, "hash": "2173955049ce47e8a25072670af1845ca72ae2bc2340554efcc5211b324ebab9"}, {"path": "bin/eil-q", "size": 399, "hash": "52bfaca6bfc34bba78c5707ed4013bac1839ef67ab4fa6feb8885b6face2c86a"}, {"path": "common/.git", "size": 31, "hash": "3c08a82d0d8b9fc7bb41dbf0d0836636c5b5e89a73243f77817467f7d1ffccbc"}, {"path": "examples/default/CMakeLists.txt", "size": 113, "hash": "14df6860fe3c63b899ed7513c11c1d03089cd759651472fd49a8856527822618"}, {"path": "examples/default/Makefile", "size": 78, "hash": "62afae4841ff92184346fd8fabb18747d2593af2acb6dd3b97b8e98cbf1d7620"}, {"path": "examples/default/README.md", "size": 339, "hash": "208bcdb94e248ee8d5addcca246342b31894973703b0bef017dedc5a19c7cf1f"}, {"path": "examples/default/sdkconfig.defaults", "size": 57, "hash": "d7bbc11a66b1ad3722ec594540a4463f613a42674bd7c844a0236031d8b27ac9"}, {"path": "examples/default/main/CMakeLists.txt", "size": 75, "hash": "27d6d39ef0cb6a9097984b41e080b09f7ff972339a0bb4ddf771db4001cb9782"}, {"path": "examples/default/main/component.mk", "size": 30, "hash": "d2059eace95f81f03ac0e38a92f119418d56d44efa3869f373639a91c9e7da21"}, {"path": "examples/default/main/idf_component.yml", "size": 102, "hash": "b436b739c8de5770c3432665e86ea5c6fde3b8c9282c3d7092703ac2fd5d8b3f"}, {"path": "examples/default/main/main.c", "size": 1218, "hash": "675eb4d5afd116ba05f06857e29b28a2f099d7f6c1affb509d9949422eb5104b"}, {"path": "examples/default/main/my_local_header.h", "size": 870, "hash": "60f0ca3babd18d86129b28088b0af7927f8a906839b44cac7258dfa5c4497d5b"}, {"path": "common/cmake/esp-idf-lib.cmake", "size": 1119, "hash": "ea0f8a819ac721dfbf5ab5f109c75c32c90c8060e6fcc9fbad1ce9de29451405"}]}

View File

@@ -0,0 +1,12 @@
idf_component_register(
INCLUDE_DIRS .
REQUIRES freertos
)
# include common cmake file for components
set(ESP_IDF_LIB_CMAKE ${CMAKE_CURRENT_LIST_DIR}/common/cmake/esp-idf-lib.cmake)
if(EXISTS ${ESP_IDF_LIB_CMAKE})
include(${ESP_IDF_LIB_CMAKE})
else()
message(WARNING "${ESP_IDF_LIB_CMAKE} not found")
endif()

View File

@@ -0,0 +1,13 @@
Copyright (c) 2019 Tomoyuki Sakurai <y@trombik.org>
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.

View File

@@ -0,0 +1,30 @@
# esp-idf-lib/esp_idf_lib_helpers
[![Build examples](https://github.com/esp-idf-lib/esp_idf_lib_helpers/actions/workflows//build.yml/badge.svg)](https://github.com/esp-idf-lib/esp_idf_lib_helpers/actions/workflows//build.yml)
[![Build docs](https://github.com/esp-idf-lib/esp_idf_lib_helpers/actions/workflows//build-docs.yml/badge.svg)](https://github.com/esp-idf-lib/esp_idf_lib_helpers/actions/workflows//build-docs.yml)
[![Validation](https://github.com/esp-idf-lib/esp_idf_lib_helpers/actions/workflows//validate-component.yml/badge.svg)](https://github.com/esp-idf-lib/esp_idf_lib_helpers/actions/workflows//validate-component.yml)
Common support library for esp-idf-lib.
* [Documentation](https://esp-idf-lib.github.io/esp_idf_lib_helpers/)
* [Repository](https://github.com/esp-idf-lib/esp_idf_lib_helpers)
* [Issues](https://github.com/esp-idf-lib/esp_idf_lib_helpers/issues)
* [Discussions and questions](https://github.com/esp-idf-lib/core/discussions)
* [Component page at the ESP Component Registry](https://components.espressif.com/components/esp-idf-lib/esp_idf_lib_helpers)
## Installation
```sh
idf.py add-dependency esp-idf-lib/esp_idf_lib_helpers
```
## Support
For questions and discussions about the component, please use
[Discussions](https://github.com/esp-idf-lib/core/discussions)
at [esp-idf-lib/core](https://github.com/esp-idf-lib/core).
## Contributing
Please read [CONTRIBUTING.md](https://github.com/esp-idf-lib/core/blob/main/CONTRIBUTING.md)
at [esp-idf-lib/core](https://github.com/esp-idf-lib/core).

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env ruby
require "pathname"
require "yaml"
# A simple CLI to get values in .eil.yml
key = ARGV.shift
project_root = Pathname.new(File.expand_path(__FILE__)).parent.parent
eil_file = project_root / ".eil.yml"
doc = YAML.safe_load(File.read eil_file)
case key
when "copyright_string"
puts doc["copyrights"].map { |e| "#{e['year']}, #{e['name']}" }.join(", ")
else
puts doc[key]
end

View File

@@ -0,0 +1,36 @@
# Set common build flags but enable them only when the build is in our CI,
# making them optional. The idea is, fail when compiled in our CI but just
# warn in any other cases. Code that compiles with one toolchain warning-free
# may not do so with another toolchain, which creates a project dependency on
# specific toolchain vendors and versions.
#
# Define flags that may cause failures here.
if (DEFINED ENV{ESP_IDF_LIB_CI})
set(ESP_IDF_LIB_CI_FLAGS
-Werror=unused-variable
-Werror=unused-function
-Werror=write-strings
-Werror
)
endif()
# Set common build flags. Mandatory.
#
# Define flags that do not cause failures here.
set(ESP_IDF_LIB_FLAGS
-Wextra
-Wwrite-strings
-Wunused-variable
-Wunused-function
-Wreturn-type
)
# When COMPONENT_LIB is INTERFACE_LIBRARY, or a header-only library, do not
# set the flags.
get_target_property(COMPONENT_TYPE ${COMPONENT_LIB} TYPE)
if(NOT COMPONENT_TYPE STREQUAL "INTERFACE_LIBRARY")
target_compile_options(${COMPONENT_LIB} PRIVATE
${ESP_IDF_LIB_FLAGS}
${ESP_IDF_LIB_CI_FLAGS}
)
endif()

View File

@@ -0,0 +1,8 @@
COMPONENT_ADD_INCLUDEDIRS = .
ifdef CONFIG_IDF_TARGET_ESP8266
COMPONENT_DEPENDS = esp8266 freertos
else
COMPONENT_DEPENDS = freertos
endif

View File

@@ -0,0 +1,101 @@
/*
* Copyright (c) 2019 Tomoyuki Sakurai <y@trombik.org>
*
* 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(__ESP_IDF_LIB_HELPERS__H__)
#define __ESP_IDF_LIB_HELPERS__H__
/* XXX this header file does not need to include freertos/FreeRTOS.h.
* but without it, ESP8266 RTOS SDK does not include `sdkconfig.h` in correct
* order. as this header depends on sdkconfig.h, sdkconfig.h must be included
* first. however, the SDK includes this header first, then includes
* `sdkconfig.h` when freertos/FreeRTOS.h is not explicitly included. an
* evidence can be found in `build/${COMPONENT}/${COMPONENT}.d` in a failed
* build.
*/
#include <freertos/FreeRTOS.h>
#include <esp_idf_version.h>
#if !defined(ESP_IDF_VERSION) || !defined(ESP_IDF_VERSION_VAL)
#error Unknown ESP-IDF/ESP8266 RTOS SDK version
#endif
/* Minimal supported version for ESP32, ESP32S2 */
#define HELPER_ESP32_MIN_VER ESP_IDF_VERSION_VAL(3, 3, 5)
/* Minimal supported version for ESP8266 */
#define HELPER_ESP8266_MIN_VER ESP_IDF_VERSION_VAL(3, 3, 0)
/* HELPER_TARGET_IS_ESP32
* 1 when the target is esp32
*/
#if defined(CONFIG_IDF_TARGET_ESP32) \
|| defined(CONFIG_IDF_TARGET_ESP32S2) \
|| defined(CONFIG_IDF_TARGET_ESP32S3) \
|| defined(CONFIG_IDF_TARGET_ESP32C2) \
|| defined(CONFIG_IDF_TARGET_ESP32C3) \
|| defined(CONFIG_IDF_TARGET_ESP32C5) \
|| defined(CONFIG_IDF_TARGET_ESP32C6) \
|| defined(CONFIG_IDF_TARGET_ESP32P4) \
|| defined(CONFIG_IDF_TARGET_ESP32C61) \
|| defined(CONFIG_IDF_TARGET_ESP32H2)
#define HELPER_TARGET_IS_ESP32 (1)
/* HELPER_TARGET_IS_ESP8266
* 1 when the target is esp8266
*/
#elif defined(CONFIG_IDF_TARGET_ESP8266)
#define HELPER_TARGET_IS_ESP8266 (1)
#else
#error BUG: cannot determine the target
#endif
#if HELPER_TARGET_IS_ESP32 && ESP_IDF_VERSION < HELPER_ESP32_MIN_VER
#error Unsupported ESP-IDF version. Please update!
#endif
#if HELPER_TARGET_IS_ESP8266 && ESP_IDF_VERSION < HELPER_ESP8266_MIN_VER
#error Unsupported ESP8266 RTOS SDK version. Please update!
#endif
/* HELPER_SPI_HOST_DEFAULT
*
* The default SPI_HOST for spi_host_device_t
*/
#if CONFIG_IDF_TARGET_ESP32
#define HELPER_SPI_HOST_DEFAULT HSPI_HOST
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#define HELPER_SPI_HOST_DEFAULT SPI2_HOST
#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61
#define HELPER_SPI_HOST_DEFAULT SPI1_HOST
#elif CONFIG_IDF_TARGET_ESP32H2
#define HELPER_SPI_HOST_DEFAULT SPI1_HOST
#elif CONFIG_IDF_TARGET_ESP32P4
#define HELPER_SPI_HOST_DEFAULT SPI1_HOST
#endif
/* show the actual values for debugging */
#if DEBUG
#define VALUE_TO_STRING(x) #x
#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))
#pragma message(VAR_NAME_VALUE(ESP_IDF_VERSION_MAJOR))
#endif
#endif

View File

@@ -0,0 +1,27 @@
#if CONFIG_IDF_TARGET_ESP32
#include <esp32/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32C2
#include <esp32c2/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32C3
#include <esp32c3/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32C5
#include <esp32c5/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32C6
#include <esp32c6/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32C61
#include <esp32c61/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32H2
#include <esp32h2/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32H4
#include <esp32h4/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32S2
#include <esp32s2/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32S3
#include <esp32s3/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32P4
#include <esp32p4/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP8266
#include <rom/ets_sys.h>
#else
#error "ets_sys: Unknown target"
#endif

View File

@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(example_example)

View File

@@ -0,0 +1,5 @@
#V := 1
PROJECT_NAME := example_example
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,16 @@
# Example application for `example` component
## What the example does
The example does nothing but waits in a loop.
## Configuration
No configuration is available.
## Notes
This is an example application of `example`. It is intended as an example
application for new component.
The code under `main` should conform the code style.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,5 @@
dependencies:
esp-idf-lib/esp_idf_lib_helpers:
version: '*'
description: default
version: 1.0.0

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) YYYY YOUR NAME HERE <user@your.dom.ain>
*
* 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 <esp_log.h>
#include <esp_idf_lib_helpers.h>
#include "my_local_header.h"
static char *tag = "main";
void app_main()
{
ESP_LOGI(tag, "An example log");
#if HELPER_TARGET_IS_ESP32
ESP_LOGI(tag, "the target is ESP32");
#else
ESP_LOGI(tag, "the target is not ESP32");
#endif
while (1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) YYYY YOUR NAME HERE <user@your.dom.ain>
*
* 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(__MY_LOCAL_HEADER__H__)
#define __MY_LOCAL_HEADER__H__
#endif

View File

@@ -0,0 +1 @@
# add required non-default option for the example if any

View File

@@ -0,0 +1,29 @@
dependencies: {}
description: Common support library for esp-idf-lib
discussion: https://github.com/esp-idf-lib/core/discussions
documentation: https://esp-idf-lib.github.io/esp_idf_lib_helpers/
files:
exclude:
- docs/**/*
issues: https://github.com/esp-idf-lib/esp_idf_lib_helpers/issues
license: ISC
maintainers:
- Tomoyuki Sakurai (@trombik) <y@trombik.org>
- Ruslan V. Uss (@UncleRus) <unclerus@gmail.com>
repository: git://github.com/esp-idf-lib/esp_idf_lib_helpers.git
repository_info:
commit_sha: 57bbd8f3cda9c5ad390fc3ea5585e0ad80672584
path: .
targets:
- esp32
- esp32c2
- esp32c3
- esp32c5
- esp32c6
- esp32c61
- esp32h2
- esp32p4
- esp32s2
- esp32s3
url: https://github.com/esp-idf-lib/core
version: 1.3.10

View File

@@ -0,0 +1 @@
--ignore-dir=build

View File

@@ -0,0 +1,17 @@
--align-reference=name
--attach-classes
--attach-classes
--attach-namespaces
--convert-tabs
--exclude=build
--exclude=common
--exclude=managed_components
--ignore-exclude-errors
--indent-switches
--indent=spaces=4
--keep-one-line-statements
--max-continuation-indent=120
--pad-header
--pad-oper
--style=allman
--unpad-paren

View File

@@ -0,0 +1,66 @@
---
Language: Cpp
BasedOnStyle: WebKit
AlignConsecutiveMacros: true
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Always
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
ColumnLimit: 200
CompactNamespaces: true
Cpp11BracedListStyle: false
FixNamespaceComments: true
IndentCaseLabels: true
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 4
UseTab: Never

View File

@@ -0,0 +1 @@
11c08f9e1a7d346b5dd763196dc2567cf2209ae49042402c2c2d296624601c14

View File

@@ -0,0 +1,28 @@
name: i2cdev
description: ESP-IDF I2C master thread-safe utilities
version: 2.0.8
groups:
- common
code_owners:
- UncleRus
depends:
- driver
- freertos
- esp_idf_lib_helpers
thread_safe: yes
targets:
- esp32
- esp8266
- esp32s2
- esp32c3
- esp32s3
- esp32c2
- esp32c6
- esp32h2
- esp32p4
- esp32c5
- esp32c61
license: MIT
copyrights:
- name: UncleRus
year: 2018

View File

@@ -0,0 +1,7 @@
examples/**/sdkconfig
examples/**/sdkconfig.old
examples/**/build/
examples/**/dependencies.lock
docs/_*/
docs/doxygen.log
*.swp

View File

@@ -0,0 +1,3 @@
[submodule "common"]
path = common
url = https://github.com/esp-idf-lib/common.git

View File

@@ -0,0 +1 @@
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-07-31T11:59:22.902787+00:00", "files": [{"path": ".ackrc", "size": 19, "hash": "ad251fb2384460920327843ab53f696de85a385a179e9ff06f2dd496607a2f31"}, {"path": ".astylerc", "size": 324, "hash": "07a888d14b14e7d39e3e645c7f75addb9620ec9026248a5393baa63ef207556f"}, {"path": ".clang-format", "size": 1814, "hash": "b23e699f74909593d399c31f56d38e02c8a71c7dae0102813429e73520783101"}, {"path": ".eil.yml", "size": 399, "hash": "81bef46273987e32b13c2abbe0c0218b298825835c2445489910673776967a59"}, {"path": ".gitignore", "size": 129, "hash": "44cc4e80bc8c455013cbdbd2338b690b62da9ded5368d66f4a576401e0aaa189"}, {"path": ".gitmodules", "size": 85, "hash": "bdbdfe664e912bb32da537de8e7137eccce5869bbe030b254bd96d31891295e5"}, {"path": "CMakeLists.txt", "size": 1856, "hash": "fc8eb0fd7edaf7dfa3417693b0c34a623bc3494e47c405388cef5c56882c1657"}, {"path": "Kconfig", "size": 1962, "hash": "310886b17b2008a2d158efe166d3a63618761a26827da8c3a064fed131bce9a3"}, {"path": "Kconfig.i2c", "size": 848, "hash": "541dae0eb78e6309736d6e6f0d1c415c0881d65e7a6668e9135d4e0b55f56a89"}, {"path": "LICENSE", "size": 1110, "hash": "f54868b4773b62faa573ab1b4fef25bce6ed3eeadd251aa1e64cba4aa2989e8c"}, {"path": "README.md", "size": 1390, "hash": "168a0788b5190354bbe8878bd31f27fead4ee632a7293d97c7d1d0ee8505dab9"}, {"path": "component.mk", "size": 997, "hash": "49cbc0a3b20a663096a511c21cb66ec38f8d76ba58e47ce9c301e787a57f570a"}, {"path": "i2cdev.c", "size": 38491, "hash": "e36b5d30420542c90b770900f9bfc6333c693b811355c94b844e717e84364d23"}, {"path": "i2cdev.h", "size": 20460, "hash": "76bfed831af20b08f7d427fab591b1cda4357c2d20372d39e9049f5a6df0b371"}, {"path": "i2cdev_legacy.c", "size": 30385, "hash": "2e5c8ecf2460c1e89a73f553d45b47a25c626747a19f31ce2d828b005457af11"}, {"path": "idf_component.yml", "size": 691, "hash": "cb9bfc8a87a4057949284a24b9f35cc1d7ac420900f980bc3a8d6846b144639a"}, {"path": "bin/eil-q", "size": 399, "hash": "52bfaca6bfc34bba78c5707ed4013bac1839ef67ab4fa6feb8885b6face2c86a"}, {"path": "common/.git", "size": 31, "hash": "3c08a82d0d8b9fc7bb41dbf0d0836636c5b5e89a73243f77817467f7d1ffccbc"}, {"path": "examples/default/CMakeLists.txt", "size": 241, "hash": "e24ced9feffe63db786ede147dc76cf3efd6a9fcbdc49b964e8da02c9b72ab85"}, {"path": "examples/default/Makefile", "size": 74, "hash": "6264f313c2f50e2389a6098ee3be3bdeaf9cd7869d430cc665b666a67812a81a"}, {"path": "examples/default/README.md", "size": 1122, "hash": "1948009bf2f95f8fae896c19c07ef8c37ea2ac5c31274e1294c0efc732d724c7"}, {"path": "examples/default/main/CMakeLists.txt", "size": 75, "hash": "27d6d39ef0cb6a9097984b41e080b09f7ff972339a0bb4ddf771db4001cb9782"}, {"path": "examples/default/main/Kconfig.projbuild", "size": 170, "hash": "ed0ad26fe91667f0938b979c958690492f49afbf74fdcecf2ac86c04120e4018"}, {"path": "examples/default/main/component.mk", "size": 39, "hash": "532e7836f005fd3b10f1955409e4956ad69fd386e80b12de11d96f8e84977843"}, {"path": "examples/default/main/idf_component.yml", "size": 141, "hash": "aa7904c346077651a3fdf34f8806125f057a443bb0154776963b3aae11b81882"}, {"path": "examples/default/main/main.c", "size": 1116, "hash": "ff14517c06a94dbe15832ea00b0f475e798e790a5931247a26cdd7048c1d1f40"}, {"path": "common/cmake/esp-idf-lib.cmake", "size": 1119, "hash": "ea0f8a819ac721dfbf5ab5f109c75c32c90c8060e6fcc9fbad1ce9de29451405"}]}

View File

@@ -0,0 +1,45 @@
# ESP-IDF CMake component for i2cdev library
set(req driver freertos esp_idf_lib_helpers)
# ESP-IDF version detection for automatic driver selection
# Check for manual override via Kconfig
if(CONFIG_I2CDEV_USE_LEGACY_DRIVER)
set(USE_LEGACY_DRIVER TRUE)
message(STATUS "i2cdev: Manual override - using legacy driver (CONFIG_I2CDEV_USE_LEGACY_DRIVER=y)")
elseif(NOT DEFINED IDF_VERSION_MAJOR)
# In case older ESP-IDF versions that don't define IDF_VERSION_MAJOR
set(USE_LEGACY_DRIVER TRUE)
message(STATUS "i2cdev: IDF_VERSION_MAJOR not defined, using legacy driver")
elseif(IDF_VERSION_MAJOR LESS 5)
set(USE_LEGACY_DRIVER TRUE)
message(STATUS "i2cdev: ESP-IDF v${IDF_VERSION_MAJOR}.x detected, using legacy driver")
elseif(IDF_VERSION_MAJOR EQUAL 5 AND IDF_VERSION_MINOR LESS 3)
set(USE_LEGACY_DRIVER TRUE)
message(STATUS "i2cdev: ESP-IDF v${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR} detected, using legacy driver")
else()
set(USE_LEGACY_DRIVER FALSE)
message(STATUS "i2cdev: ESP-IDF v${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR} detected, using new i2c_master driver")
endif()
# Conditionally set the source file based on version detection or Kconfig override
if(USE_LEGACY_DRIVER)
set(SRCS "i2cdev_legacy.c")
message(STATUS "i2cdev: Compiling with legacy I2C driver (i2cdev_legacy.c)")
else()
set(SRCS "i2cdev.c")
message(STATUS "i2cdev: Compiling with new I2C master driver (i2cdev.c)")
endif()
# Register the component
idf_component_register(SRCS ${SRCS}
INCLUDE_DIRS "."
REQUIRES ${req})
# include common cmake file for components
set(ESP_IDF_LIB_CMAKE ${CMAKE_CURRENT_LIST_DIR}/common/cmake/esp-idf-lib.cmake)
if(EXISTS ${ESP_IDF_LIB_CMAKE})
include(${ESP_IDF_LIB_CMAKE})
else()
message(WARNING "${ESP_IDF_LIB_CMAKE} not found")
endif()

View File

@@ -0,0 +1,65 @@
menu "I2C Device Library"
config I2CDEV_USE_LEGACY_DRIVER
bool "Use Legacy I2C Driver API"
default n
help
Select this option to use the older ESP-IDF I2C driver API (driver/i2c.h)
instead of the newer driver API (driver/i2c_master.h).
This is automatically determined by the build system based on your ESP-IDF version.
For ESP-IDF versions prior to v5.3, the legacy driver will be used automatically.
You can manually override this setting if needed.
config I2CDEV_AUTO_ENABLE_PULLUPS
bool "Automatically enable internal I2C pullups when not configured"
default n
depends on !IDF_TARGET_ESP8266
help
When enabled, internal pullup resistors are automatically enabled
when both sda_pullup_en and scl_pullup_en are false (default state).
Useful for development and prototyping. Disable for production
systems with external pullups to avoid interference.
Considerations:
- May increase power consumption slightly
- Could interfere with carefully tuned external pullups
- Not recommended for battery-powered applications
Note: This option only affects the modern i2cdev driver (ESP32 family).
Legacy driver behavior is unchanged for compatibility.
config I2CDEV_DEFAULT_SDA_PIN
int "Default I2C SDA pin"
default 21
help
Default SDA pin for I2C devices.
config I2CDEV_DEFAULT_SCL_PIN
int "Default I2C SCL pin"
default 22
help
Default SCL pin for I2C devices.
config I2CDEV_MAX_DEVICES_PER_PORT
int "Maximum number of devices per I2C port"
default 8
help
Maximum number of devices that can be registered on a single I2C port.
config I2CDEV_TIMEOUT
int "I2C transaction timeout, milliseconds"
default 1000
range 10 5000
config I2CDEV_NOLOCK
bool "Disable the use of mutexes"
default n
help
Attention! After enabling this option, all I2C device
drivers will become non-thread safe.
Use this option if you need to access your I2C devices
from interrupt handlers.
endmenu

View File

@@ -0,0 +1,19 @@
config EXAMPLE_I2C_MASTER_SCL
int "SCL GPIO Number"
default 5 if IDF_TARGET_ESP8266
default 6 if IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C61
default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 4 if IDF_TARGET_ESP32H2
default 4 if IDF_TARGET_ESP32P4
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_ESP32C2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C61
default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 3 if IDF_TARGET_ESP32H2
default 3 if IDF_TARGET_ESP32P4
help
GPIO number for I2C Master data line.

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 Ruslan V. Uss (https://github.com/UncleRus)
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.

View File

@@ -0,0 +1,30 @@
# esp-idf-lib/i2cdev
[![Build examples](https://github.com/esp-idf-lib/i2cdev/actions/workflows//build.yml/badge.svg)](https://github.com/esp-idf-lib/i2cdev/actions/workflows//build.yml)
[![Build docs](https://github.com/esp-idf-lib/i2cdev/actions/workflows//build-docs.yml/badge.svg)](https://github.com/esp-idf-lib/i2cdev/actions/workflows//build-docs.yml)
[![Validation](https://github.com/esp-idf-lib/i2cdev/actions/workflows//validate-component.yml/badge.svg)](https://github.com/esp-idf-lib/i2cdev/actions/workflows//validate-component.yml)
ESP-IDF I2C master thread-safe utilities.
* [Documentation](https://esp-idf-lib.github.io/i2cdev/)
* [Repository](https://github.com/esp-idf-lib/i2cdev)
* [Issues](https://github.com/esp-idf-lib/i2cdev/issues)
* [Discussions and questions](https://github.com/esp-idf-lib/core/discussions)
* [Component page at the ESP Component Registry](https://components.espressif.com/components/esp-idf-lib/i2cdev)
## Installation
```sh
idf.py add-dependency esp-idf-lib/i2cdev
```
## Support
For questions and discussions about the component, please use
[Discussions](https://github.com/esp-idf-lib/core/discussions)
at [esp-idf-lib/core](https://github.com/esp-idf-lib/core).
## Contributing
Please read [CONTRIBUTING.md](https://github.com/esp-idf-lib/core/blob/main/CONTRIBUTING.md)
at [esp-idf-lib/core](https://github.com/esp-idf-lib/core).

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env ruby
require "pathname"
require "yaml"
# A simple CLI to get values in .eil.yml
key = ARGV.shift
project_root = Pathname.new(File.expand_path(__FILE__)).parent.parent
eil_file = project_root / ".eil.yml"
doc = YAML.safe_load(File.read eil_file)
case key
when "copyright_string"
puts doc["copyrights"].map { |e| "#{e['year']}, #{e['name']}" }.join(", ")
else
puts doc[key]
end

View File

@@ -0,0 +1,36 @@
# Set common build flags but enable them only when the build is in our CI,
# making them optional. The idea is, fail when compiled in our CI but just
# warn in any other cases. Code that compiles with one toolchain warning-free
# may not do so with another toolchain, which creates a project dependency on
# specific toolchain vendors and versions.
#
# Define flags that may cause failures here.
if (DEFINED ENV{ESP_IDF_LIB_CI})
set(ESP_IDF_LIB_CI_FLAGS
-Werror=unused-variable
-Werror=unused-function
-Werror=write-strings
-Werror
)
endif()
# Set common build flags. Mandatory.
#
# Define flags that do not cause failures here.
set(ESP_IDF_LIB_FLAGS
-Wextra
-Wwrite-strings
-Wunused-variable
-Wunused-function
-Wreturn-type
)
# When COMPONENT_LIB is INTERFACE_LIBRARY, or a header-only library, do not
# set the flags.
get_target_property(COMPONENT_TYPE ${COMPONENT_LIB} TYPE)
if(NOT COMPONENT_TYPE STREQUAL "INTERFACE_LIBRARY")
target_compile_options(${COMPONENT_LIB} PRIVATE
${ESP_IDF_LIB_FLAGS}
${ESP_IDF_LIB_CI_FLAGS}
)
endif()

View File

@@ -0,0 +1,29 @@
COMPONENT_ADD_INCLUDEDIRS = .
ifdef CONFIG_IDF_TARGET_ESP8266
COMPONENT_DEPENDS = esp8266 freertos esp_idf_lib_helpers
# ESP8266 RTOS SDK auto-detects all .c files, so use COMPONENT_OBJS to override
# This prevents both i2cdev.c and i2cdev_legacy.c from being compiled
COMPONENT_OBJS := i2cdev_legacy.o
COMPONENT_SRCDIRS := .
else
COMPONENT_DEPENDS = driver freertos esp_idf_lib_helpers
# For ESP32 family, check for manual override first
ifdef CONFIG_I2CDEV_USE_LEGACY_DRIVER
COMPONENT_SRCS = i2cdev_legacy.c
else
# Check if version variables are available, fallback to legacy if not
ifdef IDF_VERSION_MAJOR
ifeq ($(shell test $(IDF_VERSION_MAJOR) -lt 5 && echo 1),1)
COMPONENT_SRCS = i2cdev_legacy.c
else ifeq ($(shell test $(IDF_VERSION_MAJOR) -eq 5 -a $(IDF_VERSION_MINOR) -lt 3 && echo 1),1)
COMPONENT_SRCS = i2cdev_legacy.c
else
COMPONENT_SRCS = i2cdev.c
endif
else
# Version variables not available - fallback to legacy driver for safety
COMPONENT_SRCS = i2cdev_legacy.c
endif
endif
endif

View File

@@ -0,0 +1,7 @@
# 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)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2c_scanner)

View File

@@ -0,0 +1,5 @@
#V := 1
PROJECT_NAME := i2c_scanner
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,31 @@
# I2C bus scanner
## What it does
This example scans the i2c bus in a loop and prints out a table with the addresses of the found i2c devices.
## 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` |
## Example output
Three devices found on a bus: 0x38, 0x60 and 0x77
```
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- 38 -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: 60 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
```

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,7 @@
menu "I2C scanner configuration"
config EXAMPLE_I2C_CLOCK_HZ
int "I2C clock frequency, Hz"
default 100000
rsource "../../../Kconfig.i2c"
endmenu

View File

@@ -0,0 +1 @@
COMPONENT_ADD_INCLUDEDIRS = . include/

View File

@@ -0,0 +1,7 @@
dependencies:
esp-idf-lib/esp_idf_lib_helpers:
version: '*'
esp-idf-lib/i2cdev:
version: '*'
description: default
version: 1.0.0

View File

@@ -0,0 +1,43 @@
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <i2cdev.h>
#include <stdio.h>
void task(void *ignore)
{
i2c_dev_t dev = { 0 };
dev.cfg.sda_io_num = CONFIG_EXAMPLE_I2C_MASTER_SDA;
dev.cfg.scl_io_num = CONFIG_EXAMPLE_I2C_MASTER_SCL;
#if HELPER_TARGET_IS_ESP32
dev.cfg.master.clk_speed = CONFIG_EXAMPLE_I2C_CLOCK_HZ; // 100kHz
#endif
while (1)
{
esp_err_t res;
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n");
printf("00: ");
for (uint8_t addr = 3; addr < 0x78; addr++)
{
if (addr % 16 == 0)
printf("\n%.2x:", addr);
dev.addr = addr;
res = i2c_dev_probe(&dev, I2C_DEV_WRITE);
if (res == 0)
printf(" %.2x", addr);
else
printf(" --");
}
printf("\n\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main()
{
// Init i2cdev library
ESP_ERROR_CHECK(i2cdev_init());
// Start task
xTaskCreate(task, "i2c_scanner", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL);
}

View File

@@ -0,0 +1,897 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2018 Ruslan V. Uss <unclerus@gmail.com>
*
* 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 i2cdev.c
*
* ESP-IDF I2C master thread-safe functions for communication with I2C slave
*
* Copyright (C) 2018 Ruslan V. Uss <unclerus@gmail.com>
* Updated 2025 by quinkq to use newer ESP-IDF I2C master driver API
*
* MIT Licensed as described in the file LICENSE
*/
#include "i2cdev.h"
#include <driver/i2c_master.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <inttypes.h>
#include <string.h>
static const char *TAG = "i2cdev";
// Fallback definition for platforms without 10-bit address support
#ifndef I2C_ADDR_BIT_LEN_10
#define I2C_ADDR_BIT_LEN_10 1
#endif
#define I2C_DEFAULT_FREQ_HZ 400000
#define I2C_MAX_RETRIES 3
#define I2C_RETRY_BASE_DELAY_MS 20
#define I2CDEV_MAX_STACK_ALLOC_SIZE 32 // Stack allocation threshold to avoid heap fragmentation for small buffers
typedef struct
{
SemaphoreHandle_t lock; // Mutex for exclusive access to this port's state
i2c_master_bus_handle_t bus_handle; // Handle to the initialized I2C master bus
bool installed; // Flag indicating if the bus for this port has been installed
uint32_t ref_count; // Number of devices currently active on this bus port
int sda_pin_current; // Actual SDA pin the bus was initialized with
int scl_pin_current; // Actual SCL pin the bus was initialized with
} i2c_port_state_t;
static i2c_port_state_t i2c_ports[I2C_NUM_MAX] = { 0 };
static i2c_dev_t *active_devices[I2C_NUM_MAX][CONFIG_I2CDEV_MAX_DEVICES_PER_PORT] = { { NULL } };
// Helper to register a device
static esp_err_t register_device(i2c_dev_t *dev)
{
if (!dev)
return ESP_ERR_INVALID_ARG;
int port = dev->port;
if (port >= I2C_NUM_MAX)
return ESP_ERR_INVALID_ARG;
// Note: Port mutex should be held by caller
for (int i = 0; i < CONFIG_I2CDEV_MAX_DEVICES_PER_PORT; i++)
{
if (active_devices[port][i] == NULL)
{
active_devices[port][i] = dev;
ESP_LOGV(TAG, "[0x%02x at %d] Registered device in slot %d", dev->addr, port, i);
return ESP_OK;
}
}
ESP_LOGE(TAG, "[0x%02x at %d] No free slots to register device - limit reached", dev->addr, port);
return ESP_ERR_NO_MEM;
}
// Helper to deregister a device
static void deregister_device(i2c_dev_t *dev)
{
if (!dev)
return;
int port = dev->port;
if (port >= I2C_NUM_MAX)
return;
for (int i = 0; i < CONFIG_I2CDEV_MAX_DEVICES_PER_PORT; i++)
{
if (active_devices[port][i] == dev)
{
active_devices[port][i] = NULL;
ESP_LOGV(TAG, "[0x%02x at %d] Deregistered device from slot %d", dev->addr, port, i);
return;
}
}
}
esp_err_t i2cdev_init(void)
{
ESP_LOGV(TAG, "Initializing I2C subsystem...");
memset(active_devices, 0, sizeof(active_devices));
for (int i = 0; i < I2C_NUM_MAX; i++)
{
if (!i2c_ports[i].lock)
{
i2c_ports[i].lock = xSemaphoreCreateMutex();
if (!i2c_ports[i].lock)
{
ESP_LOGE(TAG, "Could not create port mutex %d", i);
return ESP_ERR_NO_MEM;
}
ESP_LOGV(TAG, "Created port mutex %d", i);
}
i2c_ports[i].installed = false;
i2c_ports[i].ref_count = 0;
i2c_ports[i].bus_handle = NULL;
i2c_ports[i].sda_pin_current = -1;
i2c_ports[i].scl_pin_current = -1;
}
ESP_LOGV(TAG, "I2C subsystem initialized.");
return ESP_OK;
}
esp_err_t i2c_dev_create_mutex(i2c_dev_t *dev)
{
#if !CONFIG_I2CDEV_NOLOCK
if (!dev)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Creating device mutex...", dev->addr, dev->port);
if (dev->mutex)
{
ESP_LOGW(TAG, "[0x%02x at %d] device mutex already exists (Handle: %p)", dev->addr, dev->port, dev->mutex);
return ESP_OK; // Already created
}
dev->mutex = xSemaphoreCreateMutex();
if (!dev->mutex)
{
ESP_LOGE(TAG, "[0x%02x at %d] Could not create device mutex", dev->addr, dev->port);
return ESP_ERR_NO_MEM; // Use ESP_ERR_NO_MEM for memory allocation failures
}
ESP_LOGV(TAG, "[0x%02x at %d] Device mutex created (Handle: %p)", dev->addr, dev->port, dev->mutex);
// Register the device for cleanup tracking (under port mutex for consistency)
if (dev->port < I2C_NUM_MAX && i2c_ports[dev->port].lock)
{
if (xSemaphoreTake(i2c_ports[dev->port].lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)) == pdTRUE)
{
esp_err_t reg_res = register_device(dev);
if (reg_res != ESP_OK)
{
ESP_LOGW(TAG, "[0x%02x at %d] Failed to register device: %s - device will work but cleanup tracking disabled", dev->addr, dev->port, esp_err_to_name(reg_res));
// Continue - device can still function without registration tracking
}
else
{
ESP_LOGV(TAG, "[0x%02x at %d] Device registered successfully for cleanup tracking", dev->addr, dev->port);
}
xSemaphoreGive(i2c_ports[dev->port].lock);
}
else
{
ESP_LOGW(TAG, "[0x%02x at %d] Could not take port mutex for device registration", dev->addr, dev->port);
// Continue - device can still function without registration tracking
}
}
// Set default address bit length if not explicitly set
if (dev->addr_bit_len != I2C_ADDR_BIT_LEN_7 && dev->addr_bit_len != I2C_ADDR_BIT_LEN_10)
{
ESP_LOGV(TAG, "[0x%02x at %d] Setting default 7-bit address format", dev->addr, dev->port);
dev->addr_bit_len = I2C_ADDR_BIT_LEN_7;
}
#else
ESP_LOGV(TAG, "[0x%02x at %d] Mutex creation skipped (CONFIG_I2CDEV_NOLOCK=1)", dev->addr, dev->port);
#endif
return ESP_OK;
}
esp_err_t i2c_dev_delete_mutex(i2c_dev_t *dev)
{
#if !CONFIG_I2CDEV_NOLOCK
if (!dev)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Deleting device mutex and cleaning up resources", dev->addr, dev->port);
// Remove device from bus if handle exists
if (dev->dev_handle)
{
ESP_LOGV(TAG, "[0x%02x at %d] Removing device handle %p from bus", dev->addr, dev->port, dev->dev_handle);
esp_err_t rm_res = i2c_master_bus_rm_device((i2c_master_dev_handle_t)dev->dev_handle);
if (rm_res != ESP_OK)
{
ESP_LOGW(TAG, "[0x%02x at %d] Failed to remove device handle: %s", dev->addr, dev->port, esp_err_to_name(rm_res));
// Continue with cleanup despite error
}
dev->dev_handle = NULL;
}
// Deregister the device
deregister_device(dev);
// Update port reference count if port is valid
if (dev->port < I2C_NUM_MAX)
{
if (xSemaphoreTake(i2c_ports[dev->port].lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)) == pdTRUE)
{
if (i2c_ports[dev->port].installed && i2c_ports[dev->port].ref_count > 0)
{
i2c_ports[dev->port].ref_count--;
ESP_LOGV(TAG, "[Port %d] Decremented ref_count to %" PRIu32, dev->port, i2c_ports[dev->port].ref_count);
// If last device on this port, delete the bus
if (i2c_ports[dev->port].ref_count == 0)
{
ESP_LOGI(TAG, "[Port %d] Last device removed, cleaning up THIS port's bus", dev->port);
// Just clean up this port's bus
if (i2c_ports[dev->port].bus_handle)
{
ESP_LOGI(TAG, "[Port %d] Deleting bus handle %p", dev->port, i2c_ports[dev->port].bus_handle);
esp_err_t del_bus_res = i2c_del_master_bus(i2c_ports[dev->port].bus_handle);
if (del_bus_res != ESP_OK)
{
ESP_LOGE(TAG, "[Port %d] Failed to delete master bus: %s", dev->port, esp_err_to_name(del_bus_res));
}
i2c_ports[dev->port].bus_handle = NULL;
}
i2c_ports[dev->port].installed = false;
i2c_ports[dev->port].sda_pin_current = -1;
i2c_ports[dev->port].scl_pin_current = -1;
}
}
xSemaphoreGive(i2c_ports[dev->port].lock);
}
else
{
ESP_LOGW(TAG, "[0x%02x at %d] Could not take port mutex for ref_count update", dev->addr, dev->port);
}
}
// Delete the mutex itself last
if (dev->mutex)
{
vSemaphoreDelete(dev->mutex);
dev->mutex = NULL;
}
else
{
ESP_LOGV(TAG, "[0x%02x at %d] Device mutex was NULL, nothing to delete", dev->addr, dev->port);
}
#endif
return ESP_OK;
}
esp_err_t i2c_dev_take_mutex(i2c_dev_t *dev)
{
#if !CONFIG_I2CDEV_NOLOCK
if (!dev)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Attempting to take device mutex (Handle: %p)...", dev->addr, dev->port, dev->mutex);
if (!dev->mutex)
{
ESP_LOGE(TAG, "[0x%02x at %d] Attempt to take NULL device mutex!", dev->addr, dev->port);
return ESP_ERR_INVALID_STATE; // Mutex doesn't exist
}
TickType_t timeout_ticks = pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT);
ESP_LOGV(TAG, "[0x%02x at %d] Taking device mutex with timeout %d ms (%lu ticks)", dev->addr, dev->port, CONFIG_I2CDEV_TIMEOUT, (unsigned long)timeout_ticks);
if (!xSemaphoreTake(dev->mutex, timeout_ticks))
{
ESP_LOGE(TAG, "[0x%02x at %d] Could not take device mutex (Timeout after %d ms)", dev->addr, dev->port, CONFIG_I2CDEV_TIMEOUT);
return ESP_ERR_TIMEOUT;
}
ESP_LOGV(TAG, "[0x%02x at %d] Device mutex taken successfully.", dev->addr, dev->port);
#else
ESP_LOGV(TAG, "[0x%02x at %d] Mutex take skipped (CONFIG_I2CDEV_NOLOCK=1)", dev->addr, dev->port);
#endif
return ESP_OK;
}
esp_err_t i2c_dev_give_mutex(i2c_dev_t *dev)
{
#if !CONFIG_I2CDEV_NOLOCK
if (!dev)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Giving device mutex (Handle: %p)...", dev->addr, dev->port, dev->mutex);
if (!dev->mutex)
{
ESP_LOGE(TAG, "[0x%02x at %d] Attempt to give NULL device mutex!", dev->addr, dev->port);
return ESP_ERR_INVALID_STATE;
}
if (!xSemaphoreGive(dev->mutex))
{
// This case should ideally not happen if the mutex was taken correctly
ESP_LOGE(TAG, "[0x%02x at %d] Could not give device mutex (Was it taken?) (Handle: %p)", dev->addr, dev->port, dev->mutex);
return ESP_FAIL;
}
ESP_LOGV(TAG, "[0x%02x at %d] Device mutex given successfully.", dev->addr, dev->port);
#else
ESP_LOGV(TAG, "[0x%02x at %d] Mutex give skipped (CONFIG_I2CDEV_NOLOCK=1)", dev->addr, dev->port);
#endif
return ESP_OK;
}
// i2c_setup_port: Initializes the I2C master bus for a given port if not already done.
// It uses pin configurations from dev->cfg.sda_io_num and dev->cfg.scl_io_num.
// The pins for a port are fixed after the first device initializes it.
static esp_err_t i2c_setup_port(i2c_dev_t *dev) // dev is non-const to update dev->sda_pin, dev->scl_pin
{
if (!dev)
return ESP_ERR_INVALID_ARG;
if (dev->port >= I2C_NUM_MAX)
{
ESP_LOGE(TAG, "Invalid I2C port number: %d", dev->port);
return ESP_ERR_INVALID_ARG;
}
esp_err_t res = ESP_OK;
i2c_port_state_t *port_state = &i2c_ports[dev->port];
ESP_LOGV(TAG, "[Port %d] Setup request for device 0x%02x", dev->port, dev->addr);
if (xSemaphoreTake(port_state->lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)) != pdTRUE)
{
ESP_LOGE(TAG, "[Port %d] Could not take port mutex for setup", dev->port);
return ESP_ERR_TIMEOUT;
}
if (!port_state->installed)
{
// Pin Selection Logic: Use device-specified pins, fallback to Kconfig defaults if -1
gpio_num_t sda_pin = (dev->cfg.sda_io_num == (gpio_num_t) -1) ? (gpio_num_t)CONFIG_I2CDEV_DEFAULT_SDA_PIN : dev->cfg.sda_io_num;
gpio_num_t scl_pin = (dev->cfg.scl_io_num == (gpio_num_t) -1) ? (gpio_num_t)CONFIG_I2CDEV_DEFAULT_SCL_PIN : dev->cfg.scl_io_num;
// Validate pins (basic check, gpio_is_valid_gpio could be used for more robust check)
if (sda_pin < 0 || scl_pin < 0)
{
ESP_LOGE(TAG, "[Port %d] Invalid SCL/SDA pins: SDA=%d, SCL=%d. Check driver or Kconfig defaults.", dev->port, sda_pin, scl_pin);
xSemaphoreGive(port_state->lock);
return ESP_ERR_INVALID_ARG;
}
/*
* OPTIONAL I2C PULLUP AUTO-CONFIGURATION
*
* By default: Uses whatever sda_pullup_en/scl_pullup_en you set (usually false)
*
* When CONFIG_I2CDEV_AUTO_ENABLE_PULLUPS=y: If both pullup flags are false,
* automatically change them to true to enable internal pullups (~45kΩ).
*
* Manual pullup configuration:
* - Set sda_pullup_en=true, scl_pullup_en=true for internal pullups
* - Set sda_pullup_en=false, scl_pullup_en=false for external pullups
*/
// Read user's pullup configuration (default false if not set)
bool sda_pullup = dev->cfg.sda_pullup_en;
bool scl_pullup = dev->cfg.scl_pullup_en;
#if CONFIG_I2CDEV_AUTO_ENABLE_PULLUPS
// CONFIG_I2CDEV_AUTO_ENABLE_PULLUPS=y: If user didn't configure pullups, enable them automatically
if (!sda_pullup && !scl_pullup)
{
sda_pullup = true;
scl_pullup = true;
ESP_LOGI(TAG, "[Port %d] Auto-enabling internal pullups (CONFIG_I2CDEV_AUTO_ENABLE_PULLUPS=y)", dev->port);
}
#endif
ESP_LOGI(TAG,
"[Port %d] First initialization. Configuring bus with SDA=%d, SCL=%d (Pullups "
"SCL:%d SDA:%d)",
dev->port, sda_pin, scl_pin, scl_pullup, sda_pullup);
i2c_master_bus_config_t bus_config =
{
.i2c_port = dev->port,
.sda_io_num = sda_pin,
.scl_io_num = scl_pin,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = (sda_pullup || scl_pullup),
// Bus speed is not set here. It's per-device or a global target for the bus can be set
// if desired, but i2c_master supports per-device speeds.
};
res = i2c_new_master_bus(&bus_config, &port_state->bus_handle);
if (res == ESP_OK)
{
port_state->installed = true;
port_state->ref_count = 0; // Will be incremented when a device is successfully added
port_state->sda_pin_current = sda_pin;
port_state->scl_pin_current = scl_pin;
dev->sda_pin = sda_pin; // Update dev struct with actual pins used
dev->scl_pin = scl_pin;
ESP_LOGI(TAG, "[Port %d] Successfully installed I2C master bus (Handle: %p).", dev->port, port_state->bus_handle);
}
else
{
ESP_LOGE(TAG, "[Port %d] Failed to create master bus: %d (%s)", dev->port, res, esp_err_to_name(res));
port_state->installed = false;
port_state->bus_handle = NULL;
port_state->sda_pin_current = -1;
port_state->scl_pin_current = -1;
}
}
else
{
ESP_LOGV(TAG, "[Port %d] Port already installed (SDA=%d, SCL=%d, Handle: %p).", dev->port, port_state->sda_pin_current, port_state->scl_pin_current, port_state->bus_handle);
// Pin Consistency Check: For subsequent devices, ensure pins match already-configured bus
gpio_num_t sda_desired = (dev->cfg.sda_io_num == (gpio_num_t) -1) ? (gpio_num_t)port_state->sda_pin_current : dev->cfg.sda_io_num;
gpio_num_t scl_desired = (dev->cfg.scl_io_num == (gpio_num_t) -1) ? (gpio_num_t)port_state->scl_pin_current : dev->cfg.scl_io_num;
if (sda_desired != port_state->sda_pin_current || scl_desired != port_state->scl_pin_current)
{
ESP_LOGE(TAG,
"[Port %d] Pin mismatch for device 0x%02x! Bus on SDA=%d,SCL=%d. Device wants "
"SDA=%d,SCL=%d",
dev->port, dev->addr, port_state->sda_pin_current, port_state->scl_pin_current, sda_desired, scl_desired);
res = ESP_ERR_INVALID_STATE; // Cannot change pins for an installed bus
}
else
{
dev->sda_pin = port_state->sda_pin_current; // Update dev struct with actual pins used
dev->scl_pin = port_state->scl_pin_current;
}
// ref_count is managed by i2c_setup_device when adding/removing device handles
}
xSemaphoreGive(port_state->lock);
ESP_LOGV(TAG, "[Port %d] Port setup finished with res %d.", dev->port, res);
return res;
}
// i2c_setup_device: Ensures port is set up and adds the device to the bus if not already added.
// It also registers the device in active_devices for cleanup purposes.
static esp_err_t i2c_setup_device(i2c_dev_t *dev) // dev is non-const - modifies dev->dev_handle, dev->addr_bit_len
{
if (!dev)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Setting up device context...", dev->addr, dev->port);
esp_err_t res = i2c_setup_port(dev);
if (res != ESP_OK)
{
ESP_LOGE(TAG, "[0x%02x at %d] Port setup failed during device setup: %d (%s)", dev->addr, dev->port, res, esp_err_to_name(res));
return res;
}
// If addr_bit_len is not set (e.g. 0, which is invalid for i2c_addr_bit_len_t enum), default to
// 7-bit. Modified to conditionally check for I2C_ADDR_BIT_LEN_10 based on hardware support
if (dev->addr_bit_len != I2C_ADDR_BIT_LEN_7
#if SOC_I2C_SUPPORT_10BIT_ADDR
&& dev->addr_bit_len != I2C_ADDR_BIT_LEN_10
#endif
)
{
ESP_LOGD(TAG, "[0x%02x at %d] addr_bit_len not explicitly set, defaulting to 7-bit.", dev->addr, dev->port);
dev->addr_bit_len = I2C_ADDR_BIT_LEN_7;
}
// Only warn about address size if the device is actually using 10-bit addressing
if (dev->addr_bit_len == I2C_ADDR_BIT_LEN_7 && dev->addr > 0x7F)
{
ESP_LOGW(TAG,
"[0x%02x at %d] Device address > 0x7F but addr_bit_len is 7-bit. Ensure address "
"is correct.",
dev->addr, dev->port);
}
#if !defined(SOC_I2C_SUPPORT_10BIT_ADDR) || !SOC_I2C_SUPPORT_10BIT_ADDR
// On platforms without 10-bit support, force 7-bit addressing regardless of user setting
if (dev->addr_bit_len == I2C_ADDR_BIT_LEN_10)
{
ESP_LOGW(TAG, "[0x%02x at %d] 10-bit addressing not supported on this platform, forcing 7-bit mode", dev->addr, dev->port);
dev->addr_bit_len = I2C_ADDR_BIT_LEN_7;
}
#endif
if (dev->dev_handle == NULL)
{
i2c_port_state_t *port_state = &i2c_ports[dev->port];
if (xSemaphoreTake(port_state->lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)) != pdTRUE)
{
ESP_LOGE(TAG, "[0x%02x at %d] Could not take port mutex for device add", dev->addr, dev->port);
return ESP_ERR_TIMEOUT;
}
if (!port_state->installed || !port_state->bus_handle)
{
ESP_LOGE(TAG, "[0x%02x at %d] Cannot add device, bus for port %d not ready!", dev->addr, dev->port, dev->port);
xSemaphoreGive(port_state->lock);
return ESP_ERR_INVALID_STATE;
}
ESP_LOGV(TAG, "[0x%02x at %d] Adding device to bus (Bus Handle: %p)...", dev->addr, dev->port, port_state->bus_handle);
uint32_t effective_dev_speed = dev->cfg.master.clk_speed;
if (effective_dev_speed == 0)
{
ESP_LOGW(TAG,
"[0x%02x at %d] Device speed (dev->cfg.master.clk_speed) is 0, using default: "
"%" PRIu32 " Hz",
dev->addr, dev->port, (uint32_t)I2C_DEFAULT_FREQ_HZ);
effective_dev_speed = I2C_DEFAULT_FREQ_HZ;
}
i2c_device_config_t dev_config =
{
// Use the possibly modified addr_bit_len that respects hardware capabilities
.dev_addr_length = dev->addr_bit_len,
.device_address = dev->addr,
.scl_speed_hz = effective_dev_speed,
.flags.disable_ack_check = false,
};
res = i2c_master_bus_add_device(port_state->bus_handle, &dev_config, (i2c_master_dev_handle_t *)&dev->dev_handle);
if (res == ESP_OK)
{
ESP_LOGI(TAG, "[0x%02x at %d] Device added successfully (Device Handle: %p, Speed: %" PRIu32 " Hz).", dev->addr, dev->port, dev->dev_handle, effective_dev_speed);
// Increment the port reference count for each device successfully added
port_state->ref_count++;
ESP_LOGV(TAG, "[Port %d] Incremented ref_count to %" PRIu32, dev->port, port_state->ref_count);
}
else
{
ESP_LOGE(TAG, "[0x%02x at %d] Failed to add device to bus: %d (%s)", dev->addr, dev->port, res, esp_err_to_name(res));
dev->dev_handle = NULL;
}
xSemaphoreGive(port_state->lock);
}
else
{
ESP_LOGV(TAG, "[0x%02x at %d] Device handle %p already exists. Skipping add.", dev->addr, dev->port, dev->dev_handle);
res = ESP_OK;
}
ESP_LOGV(TAG, "[0x%02x at %d] Device context setup finished with res %d.", dev->addr, dev->port, res);
return res;
}
// Helper function with retry mechanism for I2C operations
static esp_err_t i2c_do_operation_with_retry(i2c_dev_t *dev, esp_err_t (*i2c_func)(i2c_master_dev_handle_t, const void *, size_t, void *, size_t, int), const void *write_buffer, size_t write_size,
void *read_buffer, size_t read_size)
{
if (!dev)
return ESP_ERR_INVALID_ARG;
esp_err_t res = ESP_FAIL;
int retry = 0;
int timeout_ms = CONFIG_I2CDEV_TIMEOUT;
ESP_LOGV(TAG, "[0x%02x at %d] Performing I2C operation (timeout %d ms)...", dev->addr, dev->port, timeout_ms);
while (retry <= I2C_MAX_RETRIES)
{
// Ensure device is set up before each attempt, in case handle became stale or bus was reset
// This is more robust if issues like bus errors or device resets occur.
res = i2c_setup_device(dev);
if (res != ESP_OK)
{
ESP_LOGE(TAG, "[0x%02x at %d] Device setup failed (Try %d): %d (%s). Retrying setup...", dev->addr, dev->port, retry, res, esp_err_to_name(res));
// No point continuing this attempt if setup fails, but the loop will retry setup.
vTaskDelay(pdMS_TO_TICKS(I2C_RETRY_BASE_DELAY_MS * (1 << (retry))));
retry++;
continue;
}
if (!dev->dev_handle)
{
ESP_LOGE(TAG,
"[0x%02x at %d] Device handle is NULL after setup (Try %d)! Cannot perform "
"operation.",
dev->addr, dev->port, retry);
// This indicates a persistent problem with adding the device to the bus.
// No point retrying the i2c_func if handle is null.
res = ESP_ERR_INVALID_STATE;
vTaskDelay(pdMS_TO_TICKS(I2C_RETRY_BASE_DELAY_MS * (1 << (retry))));
retry++;
continue;
}
ESP_LOGV(TAG, "[0x%02x at %d] Attempting I2C op (Try %d, Handle %p)", dev->addr, dev->port, retry, dev->dev_handle);
res = i2c_func(dev->dev_handle, write_buffer, write_size, read_buffer, read_size, timeout_ms);
if (res == ESP_OK)
{
ESP_LOGV(TAG, "[0x%02x at %d] I2C operation successful (Try %d).", dev->addr, dev->port, retry);
return ESP_OK;
}
ESP_LOGW(TAG, "[0x%02x at %d] I2C op failed (Try %d, Handle %p): %d (%s).", dev->addr, dev->port, retry, dev->dev_handle, res, esp_err_to_name(res));
// Only remove handle on errors that indicate handle corruption or permanent invalidity
// Don't remove on temporary errors like ESP_ERR_TIMEOUT, ESP_FAIL (NACK), etc.
bool should_remove_handle = false;
switch (res)
{
case ESP_ERR_INVALID_ARG:
// Handle was likely removed by another task or is corrupted
should_remove_handle = true;
ESP_LOGW(TAG, "[0x%02x at %d] Invalid argument error - handle may be corrupted", dev->addr, dev->port);
break;
case ESP_ERR_INVALID_STATE:
// I2C driver is in invalid state, handle likely needs recreation
should_remove_handle = true;
ESP_LOGW(TAG, "[0x%02x at %d] Invalid state error - handle may need recreation", dev->addr, dev->port);
break;
default:
// For other errors (timeout, NACK, bus busy, etc.), keep the handle
// These are usually temporary and don't require handle recreation
should_remove_handle = false;
ESP_LOGV(TAG, "[0x%02x at %d] Temporary error - keeping handle for retry", dev->addr, dev->port);
break;
}
if (should_remove_handle && dev->dev_handle)
{
ESP_LOGW(TAG, "[0x%02x at %d] Removing potentially corrupted device handle %p after permanent error", dev->addr, dev->port, dev->dev_handle);
// Try to remove the handle from the bus before nullifying
esp_err_t rm_res = i2c_master_bus_rm_device(dev->dev_handle);
if (rm_res != ESP_OK)
{
ESP_LOGW(TAG, "[0x%02x at %d] Failed to remove corrupted handle (expected): %s", dev->addr, dev->port, esp_err_to_name(rm_res));
// This is expected if the handle was already invalid - continue cleanup
}
dev->dev_handle = NULL;
}
retry++;
if (retry <= I2C_MAX_RETRIES)
{
vTaskDelay(pdMS_TO_TICKS(I2C_RETRY_BASE_DELAY_MS * (1 << retry))); // Exponential backoff
ESP_LOGW(TAG, "[0x%02x at %d] Retrying operation...", dev->addr, dev->port);
}
}
ESP_LOGE(TAG, "[0x%02x at %d] I2C operation failed after %d retries. Last error: %d (%s)", dev->addr, dev->port, I2C_MAX_RETRIES + 1, res, esp_err_to_name(res));
return res;
}
// Wrapper functions for the I2C master API to use with the retry mechanism
// i2c_do_operation_with_retry() needs a unified function signature for all I2C operations
static esp_err_t i2c_master_transmit_wrapper(i2c_master_dev_handle_t handle, const void *write_buffer, size_t write_size, void *read_buffer, size_t read_size, int timeout_ms)
{
return i2c_master_transmit(handle, write_buffer, write_size, timeout_ms);
}
static esp_err_t i2c_master_receive_wrapper(i2c_master_dev_handle_t handle, const void *write_buffer, size_t write_size, void *read_buffer, size_t read_size, int timeout_ms)
{
return i2c_master_receive(handle, read_buffer, read_size, timeout_ms);
}
static esp_err_t i2c_master_transmit_receive_wrapper(i2c_master_dev_handle_t handle, const void *write_buffer, size_t write_size, void *read_buffer, size_t read_size, int timeout_ms)
{
return i2c_master_transmit_receive(handle, write_buffer, write_size, read_buffer, read_size, timeout_ms);
}
esp_err_t i2c_dev_read(const i2c_dev_t *dev, const void *out_data, size_t out_size, void *in_data, size_t in_size)
{
if (!dev || !in_data || !in_size)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] i2c_dev_read called (out_size: %u, in_size: %u)", dev->addr, dev->port, out_size, in_size);
esp_err_t result = i2c_do_operation_with_retry((i2c_dev_t *)dev, // Cast to non-const for i2c_setup_device internal modifications
out_data && out_size ? i2c_master_transmit_receive_wrapper : i2c_master_receive_wrapper, out_data, out_size, in_data, in_size);
ESP_LOGV(TAG, "[0x%02x at %d] i2c_dev_read result: %s (%d)", dev->addr, dev->port, esp_err_to_name(result), result);
return result;
}
esp_err_t i2c_dev_write(const i2c_dev_t *dev, const void *out_reg, size_t out_reg_size, const void *out_data, size_t out_size)
{
if (!dev)
return ESP_ERR_INVALID_ARG;
if ((!out_reg || !out_reg_size) && (!out_data || !out_size))
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] i2c_dev_write called (reg_size: %u, data_size: %u)", dev->addr, dev->port, out_reg_size, out_size);
esp_err_t res;
if (out_reg && out_reg_size && out_data && out_size)
{
size_t total_write_size = out_reg_size + out_size;
// Check for overflow before proceeding
if (total_write_size < out_reg_size || total_write_size < out_size)
{
ESP_LOGE(TAG, "[0x%02x at %d] Write size overflow: reg_size=%u + data_size=%u", dev->addr, dev->port, out_reg_size, out_size);
return ESP_ERR_INVALID_ARG;
}
// Use stack for small buffers to avoid heap fragmentation
if (total_write_size <= I2CDEV_MAX_STACK_ALLOC_SIZE)
{
// Use stack allocation for small buffers
uint8_t stack_buf[I2CDEV_MAX_STACK_ALLOC_SIZE];
memcpy(stack_buf, out_reg, out_reg_size);
memcpy(stack_buf + out_reg_size, out_data, out_size);
res = i2c_do_operation_with_retry((i2c_dev_t *)dev, i2c_master_transmit_wrapper, stack_buf, total_write_size, NULL, 0);
}
else
{
uint8_t *heap_buf = malloc(total_write_size);
if (!heap_buf)
{
ESP_LOGE(TAG, "[0x%02x at %d] Failed to allocate %u bytes for write", dev->addr, dev->port, total_write_size);
return ESP_ERR_NO_MEM;
}
memcpy(heap_buf, out_reg, out_reg_size);
memcpy(heap_buf + out_reg_size, out_data, out_size);
res = i2c_do_operation_with_retry((i2c_dev_t *)dev, i2c_master_transmit_wrapper, heap_buf, total_write_size, NULL, 0);
free(heap_buf); // Free buffer regardless of operation result
}
}
else if (out_reg && out_reg_size)
{
res = i2c_do_operation_with_retry((i2c_dev_t *)dev, i2c_master_transmit_wrapper, out_reg, out_reg_size, NULL, 0);
}
else if (out_data && out_size)
{
res = i2c_do_operation_with_retry((i2c_dev_t *)dev, i2c_master_transmit_wrapper, out_data, out_size, NULL, 0);
}
else
{
return ESP_ERR_INVALID_ARG; // Shouldn't reach here given the earlier check
}
ESP_LOGV(TAG, "[0x%02x at %d] i2c_dev_write result: %s (%d)", dev->addr, dev->port, esp_err_to_name(res), res);
return res;
}
esp_err_t i2c_dev_read_reg(const i2c_dev_t *dev, uint8_t reg, void *data, size_t size)
{
ESP_LOGV(TAG, "[0x%02x at %d] i2c_dev_read_reg called (reg: 0x%02x, size: %u)", dev->addr, dev->port, reg, size);
return i2c_dev_read(dev, &reg, 1, data, size);
}
esp_err_t i2c_dev_write_reg(const i2c_dev_t *dev, uint8_t reg, const void *data, size_t size)
{
ESP_LOGV(TAG, "[0x%02x at %d] i2c_dev_write_reg called (reg: 0x%02x, size: %u)", dev->addr, dev->port, reg, size);
return i2c_dev_write(dev, &reg, 1, data, size);
}
esp_err_t i2c_dev_check_present(const i2c_dev_t *dev_const)
{
if (!dev_const)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Probing device presence...", dev_const->addr, dev_const->port);
// Cast to non-const for i2c_setup_port (which may modify internal state)
i2c_dev_t *dev = (i2c_dev_t *)dev_const;
// Ensure the I2C port is set up before probing
esp_err_t setup_res = i2c_setup_port(dev);
if (setup_res != ESP_OK)
{
ESP_LOGE(TAG, "[0x%02x at %d] Failed to setup port for probe: %s", dev_const->addr, dev_const->port, esp_err_to_name(setup_res));
return setup_res;
}
// Now probe using the initialized bus
if (dev_const->port < I2C_NUM_MAX && i2c_ports[dev_const->port].lock)
{
if (xSemaphoreTake(i2c_ports[dev_const->port].lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)) == pdTRUE)
{
if (i2c_ports[dev_const->port].installed && i2c_ports[dev_const->port].bus_handle)
{
// Use ESP-IDF's built-in probe function - completely non-intrusive
esp_err_t probe_res = i2c_master_probe(i2c_ports[dev_const->port].bus_handle, dev_const->addr, CONFIG_I2CDEV_TIMEOUT);
xSemaphoreGive(i2c_ports[dev_const->port].lock);
if (probe_res == ESP_OK)
{
ESP_LOGV(TAG, "[0x%02x at %d] Device probe successful - device present", dev_const->addr, dev_const->port);
return ESP_OK;
}
else
{
ESP_LOGV(TAG, "[0x%02x at %d] Device probe failed: %s", dev_const->addr, dev_const->port, esp_err_to_name(probe_res));
return probe_res;
}
}
else
{
xSemaphoreGive(i2c_ports[dev_const->port].lock);
ESP_LOGW(TAG, "[0x%02x at %d] Cannot probe - bus not ready on port %d", dev_const->addr, dev_const->port, dev_const->port);
return ESP_ERR_INVALID_STATE;
}
}
else
{
ESP_LOGE(TAG, "[0x%02x at %d] Could not take port mutex for probe", dev_const->addr, dev_const->port);
return ESP_ERR_TIMEOUT;
}
}
else
{
ESP_LOGE(TAG, "[0x%02x at %d] Invalid port or port not initialized", dev_const->addr, dev_const->port);
return ESP_ERR_INVALID_ARG;
}
}
// Compatibility wrapper for legacy code that still calls i2c_dev_probe
// The new driver implementation uses i2c_master_probe which doesn't need operation_type
esp_err_t i2c_dev_probe(const i2c_dev_t *dev, i2c_dev_type_t operation_type)
{
ESP_LOGV(TAG, "[0x%02x at %d] Legacy probe called (operation_type %d), redirecting to new implementation", dev->addr, dev->port, operation_type);
return i2c_dev_check_present(dev);
}
// Clean up function to be called at application exit
esp_err_t i2cdev_done(void)
{
esp_err_t result = ESP_OK;
ESP_LOGV(TAG, "Cleaning up I2C subsystem (i2c_master)...");
for (int i = 0; i < I2C_NUM_MAX; i++)
{
if (i2c_ports[i].lock)
{
ESP_LOGV(TAG, "[Port %d] Cleaning up port...", i);
if (xSemaphoreTake(i2c_ports[i].lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)) != pdTRUE)
{
ESP_LOGE(TAG, "[Port %d] Could not take port mutex for cleanup", i);
result = ESP_FAIL;
}
else
{
if (i2c_ports[i].installed)
{
ESP_LOGV(TAG, "[Port %d] Removing active devices before deleting bus...", i);
// Remove all registered devices for this port from the bus
for (int j = 0; j < CONFIG_I2CDEV_MAX_DEVICES_PER_PORT; j++)
{
i2c_dev_t *dev_ptr = active_devices[i][j];
if (dev_ptr != NULL && dev_ptr->dev_handle != NULL)
{
ESP_LOGV(TAG, "[Port %d] Removing device 0x%02x (Handle %p)", i, dev_ptr->addr, dev_ptr->dev_handle);
esp_err_t rm_res = i2c_master_bus_rm_device(dev_ptr->dev_handle);
if (rm_res != ESP_OK)
{
ESP_LOGE(TAG, "[Port %d] Failed to remove device 0x%02x handle: %d", i, dev_ptr->addr, rm_res);
// Continue cleanup despite error
if (result == ESP_OK)
result = rm_res; // Report first error
}
dev_ptr->dev_handle = NULL;
}
}
ESP_LOGV(TAG, "[Port %d] Deleting master bus handle %p...", i, i2c_ports[i].bus_handle);
esp_err_t del_res = i2c_del_master_bus(i2c_ports[i].bus_handle);
if (del_res != ESP_OK)
{
ESP_LOGE(TAG, "[Port %d] Failed to delete I2C bus during cleanup: %d", i, del_res);
if (result == ESP_OK)
result = del_res;
}
i2c_ports[i].installed = false;
i2c_ports[i].bus_handle = NULL;
i2c_ports[i].ref_count = 0;
}
xSemaphoreGive(i2c_ports[i].lock);
} // End else (mutex taken)
ESP_LOGV(TAG, "[Port %d] Deleting port mutex...", i);
vSemaphoreDelete(i2c_ports[i].lock);
i2c_ports[i].lock = NULL;
// Clear the active device list for this port
memset(active_devices[i], 0, sizeof(active_devices[i]));
ESP_LOGV(TAG, "[Port %d] Cleanup complete.", i);
} // end if lock exists
} // end for loop
ESP_LOGV(TAG, "I2C subsystem cleanup finished with result: %d", result);
return result;
}

View File

@@ -0,0 +1,381 @@
/**
* @file i2cdev.h
* @defgroup i2cdev i2cdev
* @{
*
* ESP-IDF I2C master thread-safe functions for communication with I2C slave
*
* This implementation uses the newer ESP-IDF I2C master driver (v5.0+).
* For ESP-IDF versions using the legacy I2C driver, use i2cdev_legacy.c instead.
*
* Copyright (C) 2018 Ruslan V. Uss <unclerus@gmail.com>
* Updated 2025 by quinkq to use newer ESP-IDF I2C master driver API
*
* MIT Licensed as described in the file LICENSE
*
* ============================================================================
* OPTIONAL I2C PULLUP AUTO-CONFIGURATION
* ============================================================================
*
* This library can optionally enable internal I2C pullups when no explicit
* pullup configuration is provided. Feature is DISABLED by default for
* backward compatibility (CONFIG_I2CDEV_AUTO_ENABLE_PULLUPS=n).
*
* Optional auto-pullup (CONFIG_I2CDEV_AUTO_ENABLE_PULLUPS=y):
* - If both pullup flags are false (not set/default state), automatically enables internal pullups
* - Only available on ESP32 family (modern driver)
* - Legacy driver always uses explicit configuration
*
*
* Example - Enable internal pullups:
* i2c_dev_t sensor = {
* .port = I2C_NUM_0,
* .addr = 0x48,
* .cfg = {
* .sda_io_num = GPIO_NUM_21,
* .scl_io_num = GPIO_NUM_22,
* .sda_pullup_en = true, // Enable internal pullups
* .scl_pullup_en = true, // Enable internal pullups
* .master.clk_speed = 400000
* }
* };
*
* ============================================================================
*/
#ifndef __I2CDEV_H__
#define __I2CDEV_H__
#include <driver/gpio.h>
#include <driver/i2c.h>
#include <esp_err.h>
#include <esp_idf_lib_helpers.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
// Define missing types for older ESP-IDF versions
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)
typedef enum
{
I2C_ADDR_BIT_LEN_7 = 0, /*!< I2C 7bit address for slave mode */
I2C_ADDR_BIT_LEN_10, /*!< I2C 10bit address for slave mode */
} i2c_addr_bit_len_t;
#endif
// Definition for I2CDEV_MAX_STRETCH_TIME
#if HELPER_TARGET_IS_ESP8266
#define I2CDEV_MAX_STRETCH_TIME 0xffffffff
#else
#include <soc/i2c_reg.h> // For I2C_TIME_OUT_VALUE_V, etc.
#if defined(I2C_TIME_OUT_VALUE_V)
#define I2CDEV_MAX_STRETCH_TIME I2C_TIME_OUT_VALUE_V
#elif defined(I2C_TIME_OUT_REG_V)
#define I2CDEV_MAX_STRETCH_TIME I2C_TIME_OUT_REG_V
#else
#define I2CDEV_MAX_STRETCH_TIME 0x00ffffff
#endif
#endif /* HELPER_TARGET_IS_ESP8266 */
#ifndef CONFIG_I2CDEV_TIMEOUT
#define CONFIG_I2CDEV_TIMEOUT 1000 // Default 1 second timeout
#endif
#ifndef CONFIG_I2CDEV_NOLOCK
#define CONFIG_I2CDEV_NOLOCK 0 // Enable locking by default
#endif
#ifndef CONFIG_I2CDEV_MAX_DEVICES_PER_PORT
#define CONFIG_I2CDEV_MAX_DEVICES_PER_PORT 8 // Maximum devices per I2C port
#endif
#ifndef CONFIG_I2CDEV_DEFAULT_SDA_PIN
#define CONFIG_I2CDEV_DEFAULT_SDA_PIN 21 // Default SDA pin
#endif
#ifndef CONFIG_I2CDEV_DEFAULT_SCL_PIN
#define CONFIG_I2CDEV_DEFAULT_SCL_PIN 22 // Default SCL pin
#endif
#ifndef CONFIG_FREERTOS_HZ
#define CONFIG_FREERTOS_HZ 100 // Default value in most ESP-IDF configs
#endif
#ifndef CONFIG_LOG_MAXIMUM_LEVEL
#define CONFIG_LOG_MAXIMUM_LEVEL 3 // INFO level as default
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief I2C transaction type for legacy probe
*/
typedef enum
{
I2C_DEV_WRITE = 0, /**< Write operation for probe */
I2C_DEV_READ /**< Read operation for probe */
} i2c_dev_type_t;
/**
* I2C device descriptor
*
* This structure supports both legacy ESP-IDF I2C driver and modern i2c_master driver.
*
* @note INITIALIZATION CHECKLIST - Set these fields before calling i2c_dev_create_mutex():
*
* ┌─── REQUIRED (Set by user) ───────────────────────────────────────────┐
* │ - dev->port - I2C port number (e.g., I2C_NUM_0) │
* │ - dev->addr - Device I2C address (e.g., 0x48) │
* │ - dev->cfg.sda_io_num - SDA pin (-1 = use Kconfig default) │
* │ - dev->cfg.scl_io_num - SCL pin (-1 = use Kconfig default) │
* │ - dev->cfg.master.clk_speed - Clock speed in Hz (e.g., 400000) │
* └──────────────────────────────────────────────────────────────────────┘
*
* ┌─── OPTIONAL (Set by user if needed) ─────────────────────────────────┐
* │ - dev->addr_bit_len - Address format (defaults to 7-bit) - NEW │
* │ - dev->cfg.sda_pullup_en - Enable internal SDA pullup │
* │ - dev->cfg.scl_pullup_en - Enable internal SCL pullup │
* │ - dev->timeout_ticks - Legacy driver timeout (legacy only) │
* └──────────────────────────────────────────────────────────────────────┘
*
* ┌─── AUTO-POPULATED (library fills these) ─────────────────────────────┐
* │ - dev->mutex - Device mutex handle │
* │ - dev->dev_handle - I2C device handle (modern driver) - NEW │
* │ - dev->sda_pin - Actual SDA pin used by bus │
* │ - dev->scl_pin - Actual SCL pin used by bus │
* └──────────────────────────────────────────────────────────────────────┘
*
* @note BACKWARD COMPATIBILITY DESIGN:
* The custom 'cfg' structure mimics ESP-IDF's deprecated i2c_config_t layout
* to maintain zero-change compatibility with existing device drivers.
* ESP-IDF ≥5.2 deprecated i2c_config_t and split it into separate bus/device
* configs, but this library preserves the familiar field paths like:
* dev->cfg.sda_io_num, dev->cfg.scl_io_num, dev->cfg.master.clk_speed
*/
typedef struct
{
// ═══ Core Device Identity (REQUIRED) ═══
i2c_port_t port; //!< I2C port number (e.g., I2C_NUM_0)
uint16_t addr; //!< Device I2C address (e.g., 0x48 for 7-bit)
i2c_addr_bit_len_t addr_bit_len; //!< Address format: I2C_ADDR_BIT_LEN_7 (default) or I2C_ADDR_BIT_LEN_10
// ═══ Library Internal State (AUTO-POPULATED) ═══
SemaphoreHandle_t mutex; //!< Device mutex - Created by i2c_dev_create_mutex()
void *dev_handle; //!< Device handle - Modern driver only, created lazily (when actual I2C operation is performed)
int sda_pin; //!< Actual SDA pin used - Populated after port setup
int scl_pin; //!< Actual SCL pin used - Populated after port setup
// ═══ Legacy Driver Compatibility ═══
uint32_t timeout_ticks; //!< Clock stretching timeout - Legacy driver only
// ═══ User Configuration (REQUIRED) ═══
// Configuration structure with i2c_config_t compatible field layout.
struct
{
gpio_num_t sda_io_num; //!< Desired SDA pin (-1 = use Kconfig default)
gpio_num_t scl_io_num; //!< Desired SCL pin (-1 = use Kconfig default)
uint8_t sda_pullup_en; //!< Enable internal SDA pullup (optional)
uint8_t scl_pullup_en; //!< Enable internal SCL pullup (optional)
uint32_t clk_flags; //!< Bitwise of ``I2C_SCLK_SRC_FLAG_**FOR_DFS**`` for clk source choice
struct
{
uint32_t clk_speed; //!< Clock speed in Hz
} master; //!< Master-specific config (mimics old i2c_config_t.master)
} cfg; //!< Configuration set by device drivers (i2c_config_t compatible layout)
} i2c_dev_t;
/**
* @brief Initialize I2C subsystem (port mutexes and internal states)
*
* @note This should be called once at the beginning of your application
* before any I2C devices are initialized.
*
* @return ESP_OK on success
*/
esp_err_t i2cdev_init(void);
/**
* @brief Release I2C subsystem (deletes all devices, buses, and mutexes)
*
* @note Call this when no more I2C operations will be performed
* to clean up resources.
*
* @return ESP_OK on success
*/
esp_err_t i2cdev_done(void);
/**
* @brief Create mutex for device descriptor and register device
*
* @note IMPORTANT: Before calling this function, you must properly initialize the i2c_dev_t
* structure with device address, port, and pin settings. For ESP-IDF legacy driver,
* set pins in dev->cfg.sda_io_num and dev->cfg.scl_io_num. For newer ESP-IDF version,
* either method is compatible. See the structure documentation for details.
*
* @param dev Pointer to device descriptor
* @return `ESP_OK` on success
*/
esp_err_t i2c_dev_create_mutex(i2c_dev_t *dev);
/**
* @brief Delete mutex for device descriptor and perform device cleanup
*
* @note This function performs cleanup tasks including removing the device from the
* I2C bus, deregistering it, and deleting its mutex.
*
* @param dev Pointer to device descriptor
* @return `ESP_OK` on success
*/
esp_err_t i2c_dev_delete_mutex(i2c_dev_t *dev);
/**
* @brief Take device mutex
*
* @param dev Pointer to device descriptor
* @return `ESP_OK` on success
*/
esp_err_t i2c_dev_take_mutex(i2c_dev_t *dev);
/**
* @brief Give device mutex
*
* @param dev Pointer to device descriptor
* @return `ESP_OK` on success
*/
esp_err_t i2c_dev_give_mutex(i2c_dev_t *dev);
/**
* @brief Check the availability of a device on the I2C bus (New Driver) - legacy's i2c_dev_probe function equivalent.
*
* This function attempts to communicate with the I2C device to see if it ACKs.
* It is non-intrusive; if the device is found, any temporary setup for
* the check is torn down. Uses the new I2C driver logic.
*
* @param dev Pointer to the device descriptor. Pins and address must be configured.
* @return `ESP_OK` if the device ACKs (is present), an error code otherwise.
*/
esp_err_t i2c_dev_check_present(const i2c_dev_t *dev);
/**
* @brief Check the availability of a device on the I2C bus (Legacy Driver).
*
* Issue an operation of `operation_type` to the I2C device then stops.
* Primarily for use with the legacy i2cdev_legacy.c implementation.
*
* @param dev Device descriptor.
* @param operation_type Operation type (I2C_DEV_WRITE or I2C_DEV_READ).
* @return `ESP_OK` if device is available for the specified operation type.
*/
esp_err_t i2c_dev_probe(const i2c_dev_t *dev, i2c_dev_type_t operation_type);
/**
* @brief Read from device
*
* @param dev Pointer to device descriptor
* @param[in] out_data Data to write before reading (can be NULL if out_size is 0)
* @param out_size Size of data to write
* @param[out] in_data Buffer to store data read
* @param in_size Number of bytes to read
* @return `ESP_OK` on success
*/
esp_err_t i2c_dev_read(const i2c_dev_t *dev, const void *out_data, size_t out_size, void *in_data, size_t in_size);
/**
* @brief Write to device
*
* @param dev Pointer to device descriptor
* @param[in] out_reg Register address to write to (can be NULL if out_reg_size is 0)
* @param out_reg_size Size of register address
* @param[in] out_data Data to write (can be NULL if out_size is 0)
* @param out_size Size of data to write
* @return `ESP_OK` on success
*/
esp_err_t i2c_dev_write(const i2c_dev_t *dev, const void *out_reg, size_t out_reg_size, const void *out_data, size_t out_size);
/**
* @brief Read from device register (8-bit register address)
*
* @param dev Pointer to device descriptor
* @param reg Command to write before reading
* @param[out] data Buffer to store data
* @param size Number of bytes to read
* @return `ESP_OK` on success
*/
esp_err_t i2c_dev_read_reg(const i2c_dev_t *dev, uint8_t reg, void *data, size_t size);
/**
* @brief Write to device register (8-bit register address)
*
* @param dev Pointer to device descriptor
* @param reg Command to write before writing data
* @param data Buffer with data to write
* @param size Number of bytes to write
* @return `ESP_OK` on success
*/
esp_err_t i2c_dev_write_reg(const i2c_dev_t *dev, uint8_t reg, const void *data, size_t size);
/**
* @brief Take device mutex with error checking
*/
#define I2C_DEV_TAKE_MUTEX(dev) \
do \
{ \
esp_err_t __ = i2c_dev_take_mutex(dev); \
if (__ != ESP_OK) \
return __; \
} \
while (0)
/**
* @brief Give device mutex with error checking
*/
#define I2C_DEV_GIVE_MUTEX(dev) \
do \
{ \
esp_err_t __ = i2c_dev_give_mutex(dev); \
if (__ != ESP_OK) \
return __; \
} \
while (0)
/**
* @brief Execute operation, assuming mutex is held. Gives mutex ONLY on error.
*/
#define I2C_DEV_CHECK(dev, X) \
do \
{ \
esp_err_t ___ = X; /* Execute operation */ \
if (___ != ESP_OK) \
{ \
/* Give mutex ONLY if error occurred */ \
i2c_dev_give_mutex(dev); \
return ___; \
} \
} \
while (0)
/**
* @brief Execute operation, assuming mutex is held. Gives mutex ONLY on error, logs error.
*/
#define I2C_DEV_CHECK_LOGE(dev, X, msg, ...) \
do \
{ \
esp_err_t ___ = X; /* Execute operation */ \
if (___ != ESP_OK) \
{ \
/* Give mutex ONLY if error occurred */ \
i2c_dev_give_mutex(dev); \
ESP_LOGE(TAG, msg, ##__VA_ARGS__); \
return ___; \
} \
} \
while (0)
#ifdef __cplusplus
}
#endif
/**@}*/
#endif /* __I2CDEV_H__ */

View File

@@ -0,0 +1,793 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2018 Ruslan V. Uss <unclerus@gmail.com>
*
* 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 i2cdev.c
*
* ESP-IDF I2C master thread-safe functions for communication with I2C slave
*
* Copyright (c) 2018 Ruslan V. Uss <unclerus@gmail.com>
* Updated 2025 by quinkq to use newer ESP-IDF I2C master driver API
* MIT Licensed as described in the file LICENSE
*/
#include "esp_idf_lib_helpers.h" // For HELPER_TARGET_IS_ESP32 etc.
#include "i2cdev.h" // Common header
#include <driver/i2c.h> // Legacy I2C driver
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <inttypes.h>
#include <sdkconfig.h>
#if !HELPER_TARGET_IS_ESP8266
#include <soc/clk_tree_defs.h> // For APB_CLK_FREQ
#endif
#include <string.h>
static const char *TAG = "i2cdev_legacy";
typedef struct
{
SemaphoreHandle_t lock;
i2c_config_t config; // Use legacy config struct
bool installed;
uint32_t ref_count;
i2c_dev_t *devices[CONFIG_I2CDEV_MAX_DEVICES_PER_PORT]; // Track devices registered on this port
} i2c_port_state_t;
static i2c_port_state_t states[I2C_NUM_MAX] = { 0 };
#if CONFIG_I2CDEV_NOLOCK
#define SEMAPHORE_TAKE(port)
#else
#define SEMAPHORE_TAKE(port) \
do \
{ \
if (!xSemaphoreTake(states[port].lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT))) \
{ \
ESP_LOGE(TAG, "Could not take port mutex %d", port); \
return ESP_ERR_TIMEOUT; \
} \
} \
while (0)
#endif
#if CONFIG_I2CDEV_NOLOCK
#define SEMAPHORE_GIVE(port)
#else
#define SEMAPHORE_GIVE(port) \
do \
{ \
if (!xSemaphoreGive(states[port].lock)) \
{ \
ESP_LOGE(TAG, "Could not give port mutex %d", port); \
return ESP_FAIL; \
} \
} \
while (0)
#endif
/**
* @brief Register an I2C device for tracking and resource management
*
* This function adds a device to the port's tracking array, which helps with:
* - Monitoring which devices are active on each port
* - Proper cleanup when the system shuts down
* - Diagnostics and debugging
*
* Each port can track up to CONFIG_I2CDEV_MAX_DEVICES_PER_PORT devices.
*
* @param dev Device descriptor to register
* @return ESP_OK if registration succeeded, or an error code
*/
static esp_err_t register_device(i2c_dev_t *dev)
{
if (!dev || dev->port >= I2C_NUM_MAX)
return ESP_ERR_INVALID_ARG;
if (!states[dev->port].lock)
return ESP_ERR_INVALID_STATE;
esp_err_t ret = ESP_ERR_NO_MEM;
// Take the mutex directly instead of using the macro
if (xSemaphoreTake(states[dev->port].lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)) != pdTRUE)
{
ESP_LOGE(TAG, "[0x%02x at %d] Could not take port mutex for registration", dev->addr, dev->port);
return ESP_ERR_TIMEOUT;
}
// Search for an empty slot in the device tracking array
for (int i = 0; i < CONFIG_I2CDEV_MAX_DEVICES_PER_PORT; i++)
{
if (states[dev->port].devices[i] == NULL)
{
// Found empty slot - register the device here
states[dev->port].devices[i] = dev;
ESP_LOGV(TAG, "[0x%02x at %d] Registered device in slot %d", dev->addr, dev->port, i);
ret = ESP_OK;
break;
}
}
// All slots full - this will still allow communication but prevents automatic cleanup
if (ret != ESP_OK)
{
ESP_LOGW(TAG, "[0x%02x at %d] No free slots to register device", dev->addr, dev->port);
}
// Release the mutex
if (!xSemaphoreGive(states[dev->port].lock))
{
ESP_LOGE(TAG, "[0x%02x at %d] Could not give port mutex after registration", dev->addr, dev->port);
// If can't give the mutex, that's a serious error that overrides the registration result
return ESP_FAIL;
}
return ret;
}
/**
* @brief Deregister a device and update reference counting
*
* This function:
* 1. Removes the device from the port's tracking array
* 2. Decrements the port's reference count
* 3. Cleans up the I2C driver if this was the last device on the port
*
* This is called during device cleanup to ensure proper resource management.
*
* @param dev Device descriptor to deregister
*/
static void deregister_device(i2c_dev_t *dev)
{
if (!dev || dev->port >= I2C_NUM_MAX)
return;
// Don't use macros that return values since this is a void function
if (states[dev->port].lock)
{
if (xSemaphoreTake(states[dev->port].lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)) != pdTRUE)
{
ESP_LOGE(TAG, "[0x%02x at %d] Could not take port mutex for deregistration", dev->addr, dev->port);
return; // Cannot proceed without lock
}
// Find the device in the tracking array
for (int i = 0; i < CONFIG_I2CDEV_MAX_DEVICES_PER_PORT; i++)
{
if (states[dev->port].devices[i] == dev)
{
// Clear this slot
states[dev->port].devices[i] = NULL;
ESP_LOGV(TAG, "[0x%02x at %d] Deregistered device from slot %d", dev->addr, dev->port, i);
break;
}
}
// Manage reference counting for this port
if (states[dev->port].ref_count > 0)
{
states[dev->port].ref_count--;
ESP_LOGD(TAG, "[Port %d] Decremented ref_count to %" PRIu32, dev->port, states[dev->port].ref_count);
// If this was the last device using this port, clean up the driver
if (states[dev->port].ref_count == 0 && states[dev->port].installed)
{
ESP_LOGI(TAG, "[Port %d] Last device removed, uninstalling driver", dev->port);
i2c_driver_delete(dev->port);
states[dev->port].installed = false;
}
}
// Release the mutex
if (!xSemaphoreGive(states[dev->port].lock))
{
ESP_LOGE(TAG, "[Port %d] Could not give port mutex after deregistration", dev->port);
// Can't do much about this error except log it
}
}
}
esp_err_t i2cdev_init()
{
memset(states, 0, sizeof(states));
#if !CONFIG_I2CDEV_NOLOCK
for (int i = 0; i < I2C_NUM_MAX; i++)
{
states[i].lock = xSemaphoreCreateMutex();
if (!states[i].lock)
{
ESP_LOGE(TAG, "Could not create port mutex %d", i);
return ESP_FAIL;
}
}
#endif
return ESP_OK;
}
esp_err_t i2cdev_done()
{
ESP_LOGV(TAG, "Cleaning up I2C subsystem (legacy)...");
for (int i = 0; i < I2C_NUM_MAX; i++)
{
if (!states[i].lock)
continue;
if (states[i].installed)
{
SEMAPHORE_TAKE(i);
// First, clean up any devices still registered on this port
for (int j = 0; j < CONFIG_I2CDEV_MAX_DEVICES_PER_PORT; j++)
{
if (states[i].devices[j] != NULL)
{
i2c_dev_t *dev = states[i].devices[j];
ESP_LOGW(TAG, "[Port %d] Device 0x%02x still registered during cleanup", i, dev->addr);
states[i].devices[j] = NULL;
}
}
i2c_driver_delete(i);
states[i].installed = false;
states[i].ref_count = 0;
SEMAPHORE_GIVE(i);
}
#if !CONFIG_I2CDEV_NOLOCK
vSemaphoreDelete(states[i].lock);
#endif
states[i].lock = NULL;
}
ESP_LOGV(TAG, "I2C subsystem cleanup finished (legacy).");
return ESP_OK;
}
esp_err_t i2c_dev_create_mutex(i2c_dev_t *dev)
{
#if !CONFIG_I2CDEV_NOLOCK
if (!dev)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Creating device mutex", dev->addr, dev->port);
// Initialize device pins to -1 to ensure consistent pattern with new driver
if (dev->sda_pin == 0 && dev->scl_pin == 0)
{
dev->sda_pin = -1;
dev->scl_pin = -1;
ESP_LOGD(TAG, "[0x%02x at %d] Initialized pins to -1", dev->addr, dev->port);
}
if (dev->mutex)
{
ESP_LOGW(TAG, "[0x%02x at %d] Device mutex already exists", dev->addr, dev->port);
return ESP_OK; // Already created
}
dev->mutex = xSemaphoreCreateMutex();
if (!dev->mutex)
{
ESP_LOGE(TAG, "[0x%02x at %d] Could not create device mutex", dev->addr, dev->port);
return ESP_FAIL;
}
// Register device for tracking
esp_err_t reg_res = register_device(dev);
if (reg_res != ESP_OK)
{
ESP_LOGW(TAG, "[0x%02x at %d] Could not register device: %s", dev->addr, dev->port, esp_err_to_name(reg_res));
// Continue anyway since this is not critical
}
#endif
return ESP_OK;
}
esp_err_t i2c_dev_delete_mutex(i2c_dev_t *dev)
{
#if !CONFIG_I2CDEV_NOLOCK
if (!dev)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Deleting device mutex and cleaning up", dev->addr, dev->port);
// Deregister and update ref counts
deregister_device(dev);
// Delete mutex if exists
if (dev->mutex)
{
vSemaphoreDelete(dev->mutex);
dev->mutex = NULL;
}
else
{
ESP_LOGV(TAG, "[0x%02x at %d] Device mutex was NULL", dev->addr, dev->port);
}
#endif
return ESP_OK;
}
esp_err_t i2c_dev_take_mutex(i2c_dev_t *dev)
{
#if !CONFIG_I2CDEV_NOLOCK
if (!dev)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Taking mutex", dev->addr, dev->port);
if (!dev->mutex)
{
ESP_LOGE(TAG, "[0x%02x at %d] Attempt to take NULL mutex!", dev->addr, dev->port);
return ESP_ERR_INVALID_STATE;
}
if (!xSemaphoreTake(dev->mutex, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)))
{
ESP_LOGE(TAG, "[0x%02x at %d] Could not take device mutex (timeout %d ms)", dev->addr, dev->port, CONFIG_I2CDEV_TIMEOUT);
return ESP_ERR_TIMEOUT;
}
#endif
return ESP_OK;
}
esp_err_t i2c_dev_give_mutex(i2c_dev_t *dev)
{
#if !CONFIG_I2CDEV_NOLOCK
if (!dev)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Giving mutex", dev->addr, dev->port);
if (!dev->mutex)
{
ESP_LOGE(TAG, "[0x%02x at %d] Attempt to give NULL mutex!", dev->addr, dev->port);
return ESP_ERR_INVALID_STATE;
}
if (!xSemaphoreGive(dev->mutex))
{
ESP_LOGE(TAG, "[0x%02x at %d] Could not give device mutex", dev->addr, dev->port);
return ESP_FAIL;
}
#endif
return ESP_OK;
}
inline static bool cfg_equal(const i2c_config_t *a, const i2c_config_t *b)
{
bool clock_equal;
#ifdef CONFIG_IDF_TARGET_ESP8266
clock_equal = (a->clk_stretch_tick == b->clk_stretch_tick);
#else
clock_equal = (a->master.clk_speed == b->master.clk_speed);
#endif
return a->mode == b->mode && a->scl_io_num == b->scl_io_num && a->sda_io_num == b->sda_io_num && a->scl_pullup_en == b->scl_pullup_en && a->sda_pullup_en == b->sda_pullup_en && clock_equal;
// Note: Ignoring clk_flags for comparison as it might not be consistently set by users
}
/**
* @brief Configure and initialize the I2C port for a device
*
* This function is responsible for:
* 1. Determining which pins to use (from device or config)
* 2. Validating pin configuration
* 3. Installing/configuring the I2C driver if not already done
* 4. Managing reference counting for the port
* 5. Setting up clock stretching timeout
*
* This is a critical function that must succeed before any I2C operations
* can be performed with a device.
*
* @param dev Device descriptor with configuration info
* @return ESP_OK on success, or an error code on failure
*/
static esp_err_t i2c_setup_port(i2c_dev_t *dev)
{
if (!dev)
{
ESP_LOGE(TAG, "Device is NULL");
return ESP_ERR_INVALID_ARG;
}
if (dev->port >= I2C_NUM_MAX)
{
ESP_LOGE(TAG, "Invalid I2C port number: %d", dev->port);
return ESP_ERR_INVALID_ARG;
}
// Pin Selection Logic:
// Pins are taken from dev->cfg.xyz_io_num.
// If -1, Kconfig defaults are used.
gpio_num_t sda_pin; // Effective SDA pin to be used
gpio_num_t scl_pin; // Effective SCL pin to be used
if (dev->cfg.sda_io_num == (gpio_num_t) -1)
{
sda_pin = (gpio_num_t)CONFIG_I2CDEV_DEFAULT_SDA_PIN;
}
else
{
sda_pin = dev->cfg.sda_io_num;
}
if (dev->cfg.scl_io_num == (gpio_num_t) -1)
{
scl_pin = (gpio_num_t)CONFIG_I2CDEV_DEFAULT_SCL_PIN;
}
else
{
scl_pin = dev->cfg.scl_io_num;
}
ESP_LOGD(TAG, "[0x%02x at %d] Based on cfg: sda_cfg=%d, scl_cfg=%d. Effective pins for setup: SDA=%d, SCL=%d", dev->addr, dev->port, dev->cfg.sda_io_num, dev->cfg.scl_io_num, sda_pin, scl_pin);
// Perform basic validation of effective pins
if (sda_pin < 0 || scl_pin < 0)
{
ESP_LOGE(TAG, "[0x%02x at %d] Invalid effective SDA/SCL pins (%d, %d). Check Kconfig defaults if cfg pins were -1.", dev->addr, dev->port, sda_pin, scl_pin);
return ESP_ERR_INVALID_ARG;
}
if (sda_pin == scl_pin)
{
ESP_LOGE(TAG, "[0x%02x at %d] Effective SDA and SCL pins cannot be the same (%d).", dev->addr, dev->port, sda_pin);
return ESP_ERR_INVALID_ARG;
}
// Initialize common fields
i2c_config_t legacy_cfg = { .mode = I2C_MODE_MASTER,
.sda_io_num = sda_pin, // Use locally determined pins
.scl_io_num = scl_pin, // Use locally determined pins
.sda_pullup_en = dev->cfg.sda_pullup_en ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE,
.scl_pullup_en = dev->cfg.scl_pullup_en ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE
};
#ifdef CONFIG_IDF_TARGET_ESP8266
// ESP8266 uses clk_stretch_tick instead of master.clk_speed
// Clock speed will be handled during driver installation
uint32_t desired_speed = dev->cfg.master.clk_speed > 0 ? dev->cfg.master.clk_speed : 400000;
ESP_LOGD(TAG, "Final I2C config for port %d: SDA=%d, SCL=%d, speed=%lu (ESP8266)", dev->port, legacy_cfg.sda_io_num, legacy_cfg.scl_io_num, (unsigned long)desired_speed);
#else
// ESP32 family uses master.clk_speed
legacy_cfg.master.clk_speed = dev->cfg.master.clk_speed > 0 ? dev->cfg.master.clk_speed : 400000;
ESP_LOGD(TAG, "Final I2C config for port %d: SDA=%d, SCL=%d, speed=%lu", dev->port, legacy_cfg.sda_io_num, legacy_cfg.scl_io_num, (unsigned long)legacy_cfg.master.clk_speed);
#endif
#ifdef CONFIG_IDF_TARGET_ESP32
legacy_cfg.clk_flags = 0;
#endif
esp_err_t err = ESP_OK;
// Part 1: Driver Installation / Reconfiguration
if (!cfg_equal(&legacy_cfg, &states[dev->port].config) || !states[dev->port].installed)
{
ESP_LOGD(TAG, "[0x%02x at %d] Reconfiguring I2C driver", dev->addr, dev->port);
if (states[dev->port].installed)
{
ESP_LOGD(TAG, "Uninstalling previous I2C driver configuration for port %d", dev->port);
i2c_driver_delete(dev->port);
states[dev->port].installed = false;
states[dev->port].ref_count = 0;
}
vTaskDelay(1);
// Target-specific driver installation/configuration sequence
#if HELPER_TARGET_IS_ESP32 || HELPER_TARGET_IS_ESP32S2 || HELPER_TARGET_IS_ESP32S3 || HELPER_TARGET_IS_ESP32C3 || HELPER_TARGET_IS_ESP32C6
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
ESP_LOGD(TAG, "Using IDF >= 5.1.0 driver install order for ESP32 family");
err = i2c_driver_install(dev->port, legacy_cfg.mode, 0, 0, 0);
if (err == ESP_OK)
{
err = i2c_param_config(dev->port, &legacy_cfg);
}
#else
ESP_LOGD(TAG, "Using IDF < 5.1.0 driver install order for ESP32 family");
err = i2c_param_config(dev->port, &legacy_cfg);
if (err == ESP_OK)
{
err = i2c_driver_install(dev->port, legacy_cfg.mode, 0, 0, 0);
}
#endif
#elif HELPER_TARGET_IS_ESP8266
ESP_LOGD(TAG, "Using ESP8266 specific driver installation");
legacy_cfg.clk_stretch_tick = dev->timeout_ticks ? dev->timeout_ticks : I2CDEV_MAX_STRETCH_TIME;
err = i2c_driver_install(dev->port, legacy_cfg.mode);
if (err == ESP_OK)
{
err = i2c_param_config(dev->port, &legacy_cfg);
}
// ESP8266 note: Clock speed is not directly configurable through i2c_config_t
// The desired speed was: %lu Hz", desired_speed
#else
// If legacy mode is off, and target detection fails, this avoids a compile error.
// The legacy driver just won't support any target in this case.
ESP_LOGW(TAG, "i2cdev_legacy.c: No specific target (ESP32/ESP32-S2/ESP32-S3/ESP32-C3/ESP32-C6/ESP8266) detected "
"for driver installation. Legacy driver might be inactive or misconfigured.");
err = ESP_ERR_NOT_SUPPORTED; // Indicate that setup can't proceed.
#endif
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to install/configure I2C driver for port %d: %d (%s)", dev->port, err, esp_err_to_name(err));
states[dev->port].installed = false; // Ensure state reflects failure
return err;
}
memcpy(&states[dev->port].config, &legacy_cfg, sizeof(i2c_config_t));
states[dev->port].installed = true;
states[dev->port].ref_count++;
dev->sda_pin = legacy_cfg.sda_io_num;
dev->scl_pin = legacy_cfg.scl_io_num;
ESP_LOGD(TAG, "I2C driver successfully installed/reconfigured on port %d, ref_count=%" PRIu32, dev->port, states[dev->port].ref_count);
}
else
{
states[dev->port].ref_count++;
ESP_LOGV(TAG, "I2C driver already installed on port %d with matching config, ref_count=%" PRIu32, dev->port, states[dev->port].ref_count);
dev->sda_pin = states[dev->port].config.sda_io_num;
dev->scl_pin = states[dev->port].config.scl_io_num;
}
// Part 2: Timeout Configuration (ESP32 family specific hardware timeout)
#if HELPER_TARGET_IS_ESP32 || HELPER_TARGET_IS_ESP32S2 || HELPER_TARGET_IS_ESP32S3 || HELPER_TARGET_IS_ESP32C3 || HELPER_TARGET_IS_ESP32C6
int current_timeout_hw;
err = i2c_get_timeout(dev->port, &current_timeout_hw);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to get HW timeout for port %d: %d (%s)", dev->port, err, esp_err_to_name(err));
return err;
}
uint32_t timeout_ticks_val = dev->timeout_ticks ? dev->timeout_ticks : I2CDEV_MAX_STRETCH_TIME;
if (timeout_ticks_val != (uint32_t)current_timeout_hw)
{
ESP_LOGV(TAG, "Port %d: Updating HW timeout from %d to %" PRIu32 " ticks", dev->port, current_timeout_hw, timeout_ticks_val);
err = i2c_set_timeout(dev->port, timeout_ticks_val);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to set HW timeout for port %d: %d (%s)", dev->port, err, esp_err_to_name(err));
return err;
}
ESP_LOGD(TAG, "HW Timeout: ticks = %" PRIu32 " (%" PRIu32 " usec) on port %d", timeout_ticks_val, timeout_ticks_val / 80, dev->port);
}
#endif
return ESP_OK;
}
esp_err_t i2c_dev_probe(const i2c_dev_t *dev, i2c_dev_type_t operation_type)
{
if (!dev)
return ESP_ERR_INVALID_ARG;
SEMAPHORE_TAKE(dev->port);
esp_err_t res = i2c_setup_port((i2c_dev_t *)dev);
if (res == ESP_OK)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, dev->addr << 1 | (operation_type == I2C_DEV_READ ? 1 : 0), true);
// Alternative Write-style probe for better device compatibility
// many devices don't respond well to blind read probes.
// i2c_master_write_byte(cmd, dev->addr << 1 | 0, true); // Force write bit (0)
i2c_master_stop(cmd);
res = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT));
i2c_cmd_link_delete(cmd);
}
SEMAPHORE_GIVE(dev->port);
return res;
}
esp_err_t i2c_dev_read(const i2c_dev_t *dev, const void *out_data, size_t out_size, void *in_data, size_t in_size)
{
if (!dev || !in_data || !in_size)
return ESP_ERR_INVALID_ARG;
SEMAPHORE_TAKE(dev->port);
// Use a local status variable to track errors
esp_err_t err = i2c_setup_port((i2c_dev_t *)dev);
if (err == ESP_OK)
{
// Only create a command handle if setup was successful
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
if (out_data && out_size)
{
// Write phase - typically used to specify a register address
i2c_master_start(cmd);
i2c_master_write_byte(cmd, dev->addr << 1, true); // Addr + Write bit (0)
i2c_master_write(cmd, (void *)out_data, out_size, true);
}
// Read phase - get data from the device
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->addr << 1) | 1, true); // Addr + Read bit (1)
i2c_master_read(cmd, in_data, in_size,
I2C_MASTER_LAST_NACK); // NACK the last byte to signal end
i2c_master_stop(cmd);
// Execute the command
err = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT));
if (err != ESP_OK)
{
ESP_LOGE(TAG, "i2c_master_cmd_begin failed for read: %d (%s)", err, esp_err_to_name(err));
}
// Always delete the command handle
i2c_cmd_link_delete(cmd);
}
// Always release the semaphore before returning
SEMAPHORE_GIVE(dev->port);
return err;
}
esp_err_t i2c_dev_write(const i2c_dev_t *dev, const void *out_reg, size_t out_reg_size, const void *out_data, size_t out_size)
{
if (!dev)
return ESP_ERR_INVALID_ARG;
if ((!out_reg || !out_reg_size) && (!out_data || !out_size))
return ESP_ERR_INVALID_ARG;
SEMAPHORE_TAKE(dev->port);
// Use a local status variable to track errors
esp_err_t err = i2c_setup_port((i2c_dev_t *)dev);
if (err == ESP_OK)
{
// Only create a command handle if setup was successful
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, dev->addr << 1, true);
// Write register address/command if provided
if (out_reg && out_reg_size)
{
i2c_master_write(cmd, (void *)out_reg, out_reg_size, true);
}
// Write data if provided
if (out_data && out_size)
{
i2c_master_write(cmd, (void *)out_data, out_size, true);
}
i2c_master_stop(cmd);
// Execute the command
err = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT));
if (err != ESP_OK)
{
ESP_LOGE(TAG, "i2c_master_cmd_begin failed for write: %d (%s)", err, esp_err_to_name(err));
}
// Always delete the command handle
i2c_cmd_link_delete(cmd);
}
// Always release the semaphore before returning
SEMAPHORE_GIVE(dev->port);
return err;
}
esp_err_t i2c_dev_write_reg(const i2c_dev_t *dev, uint8_t reg, const void *out_data, size_t out_size)
{
if (!dev || !out_data || !out_size)
return ESP_ERR_INVALID_ARG;
SEMAPHORE_TAKE(dev->port);
// Use a local status variable to track errors
esp_err_t err = i2c_setup_port((i2c_dev_t *)dev);
if (err == ESP_OK)
{
// Only create a command handle if setup was successful
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->addr << 1) | I2C_MASTER_WRITE, true); // Addr + Write bit
i2c_master_write_byte(cmd, reg, true); // Register address
if (out_data && out_size)
{
i2c_master_write(cmd, (void *)out_data, out_size, true); // Data to write
}
i2c_master_stop(cmd);
// Execute the command
err = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT));
if (err != ESP_OK)
{
ESP_LOGE(TAG, "i2c_master_cmd_begin failed for write_reg: %d (%s)", err, esp_err_to_name(err));
}
// Always delete the command handle
i2c_cmd_link_delete(cmd);
}
// Always release the semaphore before returning
SEMAPHORE_GIVE(dev->port);
return err;
}
esp_err_t i2c_dev_read_reg(const i2c_dev_t *dev, uint8_t reg, void *in_data, size_t in_size)
{
if (!dev || !in_data || !in_size)
return ESP_ERR_INVALID_ARG;
SEMAPHORE_TAKE(dev->port);
// Use a local status variable to track errors
esp_err_t err = i2c_setup_port((i2c_dev_t *)dev);
if (err == ESP_OK)
{
// Only create a command handle if setup was successful
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg, true);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->addr << 1) | I2C_MASTER_READ, true);
i2c_master_read(cmd, in_data, in_size, I2C_MASTER_LAST_NACK);
i2c_master_stop(cmd);
// Execute the command
err = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT));
if (err != ESP_OK)
{
ESP_LOGE(TAG, "i2c_master_cmd_begin failed for read_reg: %d (%s)", err, esp_err_to_name(err));
}
// Always delete the command handle
i2c_cmd_link_delete(cmd);
}
// Always release the semaphore before returning
SEMAPHORE_GIVE(dev->port);
return err;
}
// Implementation of i2c_dev_check_present (updated version of i2c_dev_probe) using legacy I2C driver
esp_err_t i2c_dev_check_present(const i2c_dev_t *dev)
{
if (!dev)
return ESP_ERR_INVALID_ARG;
ESP_LOGV(TAG, "[0x%02x at %d] Checking device presence (legacy driver)...", dev->addr, dev->port);
// Use the exact same pattern as i2c_dev_probe with WRITE operation to ensure consistent behavior
return i2c_dev_probe(dev, I2C_DEV_WRITE);
}

View File

@@ -0,0 +1,30 @@
dependencies:
esp-idf-lib/esp_idf_lib_helpers:
version: '*'
description: ESP-IDF I2C master thread-safe utilities
discussion: https://github.com/esp-idf-lib/core/discussions
documentation: https://esp-idf-lib.github.io/i2cdev/
files:
exclude:
- docs/**/*
issues: https://github.com/esp-idf-lib/i2cdev/issues
license: MIT
maintainers:
- Ruslan V. Uss (@UncleRus) <unclerus@gmail.com>
repository: git://github.com/esp-idf-lib/i2cdev.git
repository_info:
commit_sha: abf0ebc8f0f826373e9a9ee07cf96727a49ed87b
path: .
targets:
- esp32
- esp32c2
- esp32c3
- esp32c5
- esp32c6
- esp32c61
- esp32h2
- esp32p4
- esp32s2
- esp32s3
url: https://github.com/esp-idf-lib/core
version: 2.0.8