mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
[esp32_camera] Allow sharing i2c bus (#9137)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
parent
2c17b2bacc
commit
4d0f8528d2
@ -1,5 +1,6 @@
|
|||||||
from esphome import automation, pins
|
from esphome import automation, pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import i2c
|
||||||
from esphome.components.esp32 import add_idf_component
|
from esphome.components.esp32 import add_idf_component
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
@ -7,6 +8,7 @@ from esphome.const import (
|
|||||||
CONF_CONTRAST,
|
CONF_CONTRAST,
|
||||||
CONF_DATA_PINS,
|
CONF_DATA_PINS,
|
||||||
CONF_FREQUENCY,
|
CONF_FREQUENCY,
|
||||||
|
CONF_I2C_ID,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
CONF_RESET_PIN,
|
CONF_RESET_PIN,
|
||||||
@ -149,93 +151,104 @@ CONF_ON_IMAGE = "on_image"
|
|||||||
|
|
||||||
camera_range_param = cv.int_range(min=-2, max=2)
|
camera_range_param = cv.int_range(min=-2, max=2)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
CONFIG_SCHEMA = cv.All(
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(
|
||||||
cv.GenerateID(): cv.declare_id(ESP32Camera),
|
{
|
||||||
# pin assignment
|
cv.GenerateID(): cv.declare_id(ESP32Camera),
|
||||||
cv.Required(CONF_DATA_PINS): cv.All(
|
# pin assignment
|
||||||
[pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8)
|
cv.Required(CONF_DATA_PINS): cv.All(
|
||||||
),
|
[pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8)
|
||||||
cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number,
|
),
|
||||||
cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number,
|
cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number,
|
||||||
cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number,
|
cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number,
|
||||||
cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema(
|
cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number,
|
||||||
{
|
cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema(
|
||||||
cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
|
{
|
||||||
cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
|
cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
|
||||||
cv.frequency, cv.Range(min=8e6, max=20e6)
|
cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
|
||||||
),
|
cv.frequency, cv.Range(min=8e6, max=20e6)
|
||||||
}
|
),
|
||||||
),
|
}
|
||||||
cv.Required(CONF_I2C_PINS): cv.Schema(
|
),
|
||||||
{
|
cv.Optional(CONF_I2C_PINS): cv.Schema(
|
||||||
cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number,
|
{
|
||||||
cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number,
|
cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number,
|
||||||
}
|
cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number,
|
||||||
),
|
}
|
||||||
cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number,
|
),
|
||||||
cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number,
|
cv.Optional(CONF_I2C_ID): cv.Any(
|
||||||
# image
|
cv.use_id(i2c.InternalI2CBus),
|
||||||
cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum(
|
msg="I2C bus must be an internal ESP32 I2C bus",
|
||||||
FRAME_SIZES, upper=True
|
),
|
||||||
),
|
cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number,
|
||||||
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63),
|
cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number,
|
||||||
cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
|
# image
|
||||||
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
|
cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum(
|
||||||
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
|
FRAME_SIZES, upper=True
|
||||||
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
|
),
|
||||||
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
|
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63),
|
||||||
cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum(
|
cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
|
||||||
ENUM_SPECIAL_EFFECT, upper=True
|
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
|
||||||
),
|
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
|
||||||
# exposure
|
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
|
||||||
cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum(
|
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
|
||||||
ENUM_GAIN_CONTROL_MODE, upper=True
|
cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum(
|
||||||
),
|
ENUM_SPECIAL_EFFECT, upper=True
|
||||||
cv.Optional(CONF_AEC2, default=False): cv.boolean,
|
),
|
||||||
cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param,
|
# exposure
|
||||||
cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200),
|
cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum(
|
||||||
# gains
|
ENUM_GAIN_CONTROL_MODE, upper=True
|
||||||
cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum(
|
),
|
||||||
ENUM_GAIN_CONTROL_MODE, upper=True
|
cv.Optional(CONF_AEC2, default=False): cv.boolean,
|
||||||
),
|
cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param,
|
||||||
cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30),
|
cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200),
|
||||||
cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum(
|
# gains
|
||||||
ENUM_GAIN_CEILING, upper=True
|
cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum(
|
||||||
),
|
ENUM_GAIN_CONTROL_MODE, upper=True
|
||||||
# white balance
|
),
|
||||||
cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(ENUM_WB_MODE, upper=True),
|
cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30),
|
||||||
# test pattern
|
cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum(
|
||||||
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
|
ENUM_GAIN_CEILING, upper=True
|
||||||
# framerates
|
),
|
||||||
cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All(
|
# white balance
|
||||||
cv.framerate, cv.Range(min=0, min_included=False, max=60)
|
cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(
|
||||||
),
|
ENUM_WB_MODE, upper=True
|
||||||
cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All(
|
),
|
||||||
cv.framerate, cv.Range(min=0, max=1)
|
# test pattern
|
||||||
),
|
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2),
|
# framerates
|
||||||
cv.Optional(CONF_ON_STREAM_START): automation.validate_automation(
|
cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All(
|
||||||
{
|
cv.framerate, cv.Range(min=0, min_included=False, max=60)
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
),
|
||||||
ESP32CameraStreamStartTrigger
|
cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All(
|
||||||
),
|
cv.framerate, cv.Range(min=0, max=1)
|
||||||
}
|
),
|
||||||
),
|
cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2),
|
||||||
cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation(
|
cv.Optional(CONF_ON_STREAM_START): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
ESP32CameraStreamStopTrigger
|
ESP32CameraStreamStartTrigger
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_IMAGE): automation.validate_automation(
|
cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32CameraImageTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
}
|
ESP32CameraStreamStopTrigger
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
),
|
||||||
|
cv.Optional(CONF_ON_IMAGE): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
|
ESP32CameraImageTrigger
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
|
cv.has_exactly_one_key(CONF_I2C_PINS, CONF_I2C_ID),
|
||||||
|
)
|
||||||
|
|
||||||
SETTERS = {
|
SETTERS = {
|
||||||
# pin assignment
|
# pin assignment
|
||||||
@ -280,8 +293,12 @@ async def to_code(config):
|
|||||||
|
|
||||||
extclk = config[CONF_EXTERNAL_CLOCK]
|
extclk = config[CONF_EXTERNAL_CLOCK]
|
||||||
cg.add(var.set_external_clock(extclk[CONF_PIN], extclk[CONF_FREQUENCY]))
|
cg.add(var.set_external_clock(extclk[CONF_PIN], extclk[CONF_FREQUENCY]))
|
||||||
i2c_pins = config[CONF_I2C_PINS]
|
if i2c_id := config.get(CONF_I2C_ID):
|
||||||
cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL]))
|
i2c_hub = await cg.get_variable(i2c_id)
|
||||||
|
cg.add(var.set_i2c_id(i2c_hub))
|
||||||
|
else:
|
||||||
|
i2c_pins = config[CONF_I2C_PINS]
|
||||||
|
cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL]))
|
||||||
cg.add(var.set_max_update_interval(1000 / config[CONF_MAX_FRAMERATE]))
|
cg.add(var.set_max_update_interval(1000 / config[CONF_MAX_FRAMERATE]))
|
||||||
if config[CONF_IDLE_FRAMERATE] == 0:
|
if config[CONF_IDLE_FRAMERATE] == 0:
|
||||||
cg.add(var.set_idle_update_interval(0))
|
cg.add(var.set_idle_update_interval(0))
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include "esp32_camera.h"
|
#include "esp32_camera.h"
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
|
|
||||||
@ -16,6 +16,12 @@ static const char *const TAG = "esp32_camera";
|
|||||||
void ESP32Camera::setup() {
|
void ESP32Camera::setup() {
|
||||||
global_esp32_camera = this;
|
global_esp32_camera = this;
|
||||||
|
|
||||||
|
#ifdef USE_I2C
|
||||||
|
if (this->i2c_bus_ != nullptr) {
|
||||||
|
this->config_.sccb_i2c_port = this->i2c_bus_->get_port();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* initialize time to now */
|
/* initialize time to now */
|
||||||
this->last_update_ = millis();
|
this->last_update_ = millis();
|
||||||
|
|
||||||
@ -246,6 +252,13 @@ void ESP32Camera::set_i2c_pins(uint8_t sda, uint8_t scl) {
|
|||||||
this->config_.pin_sccb_sda = sda;
|
this->config_.pin_sccb_sda = sda;
|
||||||
this->config_.pin_sccb_scl = scl;
|
this->config_.pin_sccb_scl = scl;
|
||||||
}
|
}
|
||||||
|
#ifdef USE_I2C
|
||||||
|
void ESP32Camera::set_i2c_id(i2c::InternalI2CBus *i2c_bus) {
|
||||||
|
this->i2c_bus_ = i2c_bus;
|
||||||
|
this->config_.pin_sccb_sda = -1;
|
||||||
|
this->config_.pin_sccb_scl = -1;
|
||||||
|
}
|
||||||
|
#endif // USE_I2C
|
||||||
void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; }
|
void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; }
|
||||||
void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; }
|
void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; }
|
||||||
|
|
||||||
|
@ -2,13 +2,17 @@
|
|||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <esp_camera.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/queue.h>
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/entity_base.h"
|
#include "esphome/core/entity_base.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include <esp_camera.h>
|
|
||||||
#include <freertos/FreeRTOS.h>
|
#ifdef USE_I2C
|
||||||
#include <freertos/queue.h>
|
#include "esphome/components/i2c/i2c_bus.h"
|
||||||
|
#endif // USE_I2C
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_camera {
|
namespace esp32_camera {
|
||||||
@ -118,6 +122,9 @@ class ESP32Camera : public EntityBase, public Component {
|
|||||||
void set_pixel_clock_pin(uint8_t pin);
|
void set_pixel_clock_pin(uint8_t pin);
|
||||||
void set_external_clock(uint8_t pin, uint32_t frequency);
|
void set_external_clock(uint8_t pin, uint32_t frequency);
|
||||||
void set_i2c_pins(uint8_t sda, uint8_t scl);
|
void set_i2c_pins(uint8_t sda, uint8_t scl);
|
||||||
|
#ifdef USE_I2C
|
||||||
|
void set_i2c_id(i2c::InternalI2CBus *i2c_bus);
|
||||||
|
#endif // USE_I2C
|
||||||
void set_reset_pin(uint8_t pin);
|
void set_reset_pin(uint8_t pin);
|
||||||
void set_power_down_pin(uint8_t pin);
|
void set_power_down_pin(uint8_t pin);
|
||||||
/* -- image */
|
/* -- image */
|
||||||
@ -210,6 +217,9 @@ class ESP32Camera : public EntityBase, public Component {
|
|||||||
|
|
||||||
uint32_t last_idle_request_{0};
|
uint32_t last_idle_request_{0};
|
||||||
uint32_t last_update_{0};
|
uint32_t last_update_{0};
|
||||||
|
#ifdef USE_I2C
|
||||||
|
i2c::InternalI2CBus *i2c_bus_{nullptr};
|
||||||
|
#endif // USE_I2C
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user