mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 16:38:16 +01:00
Add support for Qwiic PIR binary sensor (#5194)
This commit is contained in:
parent
258b0fbff3
commit
841b24f744
6 changed files with 278 additions and 0 deletions
|
@ -234,6 +234,7 @@ esphome/components/pulse_meter/* @TrentHouliston @cstaahl @stevebaxter
|
||||||
esphome/components/pvvx_mithermometer/* @pasiz
|
esphome/components/pvvx_mithermometer/* @pasiz
|
||||||
esphome/components/qmp6988/* @andrewpc
|
esphome/components/qmp6988/* @andrewpc
|
||||||
esphome/components/qr_code/* @wjtje
|
esphome/components/qr_code/* @wjtje
|
||||||
|
esphome/components/qwiic_pir/* @kahrendt
|
||||||
esphome/components/radon_eye_ble/* @jeffeb3
|
esphome/components/radon_eye_ble/* @jeffeb3
|
||||||
esphome/components/radon_eye_rd200/* @jeffeb3
|
esphome/components/radon_eye_rd200/* @jeffeb3
|
||||||
esphome/components/rc522/* @glmnet
|
esphome/components/rc522/* @glmnet
|
||||||
|
|
0
esphome/components/qwiic_pir/__init__.py
Normal file
0
esphome/components/qwiic_pir/__init__.py
Normal file
67
esphome/components/qwiic_pir/binary_sensor.py
Normal file
67
esphome/components/qwiic_pir/binary_sensor.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
from esphome import core
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import i2c, binary_sensor
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_DEBOUNCE,
|
||||||
|
DEVICE_CLASS_MOTION,
|
||||||
|
)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
CODEOWNERS = ["@kahrendt"]
|
||||||
|
|
||||||
|
qwiic_pir_ns = cg.esphome_ns.namespace("qwiic_pir")
|
||||||
|
|
||||||
|
DebounceMode = qwiic_pir_ns.enum("DebounceMode")
|
||||||
|
DEBOUNCE_MODE_OPTIONS = {
|
||||||
|
"RAW": DebounceMode.RAW_DEBOUNCE_MODE,
|
||||||
|
"NATIVE": DebounceMode.NATIVE_DEBOUNCE_MODE,
|
||||||
|
"HYBRID": DebounceMode.HYBRID_DEBOUNCE_MODE,
|
||||||
|
}
|
||||||
|
|
||||||
|
CONF_DEBOUNCE_MODE = "debounce_mode"
|
||||||
|
|
||||||
|
QwiicPIRComponent = qwiic_pir_ns.class_(
|
||||||
|
"QwiicPIRComponent", cg.Component, i2c.I2CDevice, binary_sensor.BinarySensor
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_no_debounce_unless_native(config):
|
||||||
|
if CONF_DEBOUNCE in config:
|
||||||
|
if config[CONF_DEBOUNCE_MODE] != "NATIVE":
|
||||||
|
raise cv.Invalid("debounce can only be set if debounce_mode is NATIVE")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
binary_sensor.binary_sensor_schema(
|
||||||
|
QwiicPIRComponent,
|
||||||
|
device_class=DEVICE_CLASS_MOTION,
|
||||||
|
)
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_DEBOUNCE): cv.All(
|
||||||
|
cv.time_period,
|
||||||
|
cv.Range(max=core.TimePeriod(milliseconds=65535)),
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_DEBOUNCE_MODE, default="HYBRID"): cv.enum(
|
||||||
|
DEBOUNCE_MODE_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(i2c.i2c_device_schema(0x12)),
|
||||||
|
validate_no_debounce_unless_native,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = await binary_sensor.new_binary_sensor(config)
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
if debounce_time_setting := config.get(CONF_DEBOUNCE):
|
||||||
|
cg.add(var.set_debounce_time(debounce_time_setting.total_milliseconds))
|
||||||
|
else:
|
||||||
|
cg.add(var.set_debounce_time(1)) # default to 1 ms if not configured
|
||||||
|
cg.add(var.set_debounce_mode(config[CONF_DEBOUNCE_MODE]))
|
137
esphome/components/qwiic_pir/qwiic_pir.cpp
Normal file
137
esphome/components/qwiic_pir/qwiic_pir.cpp
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
#include "qwiic_pir.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace qwiic_pir {
|
||||||
|
|
||||||
|
static const char *const TAG = "qwiic_pir";
|
||||||
|
|
||||||
|
void QwiicPIRComponent::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up Qwiic PIR...");
|
||||||
|
|
||||||
|
// Verify I2C communcation by reading and verifying the chip ID
|
||||||
|
uint8_t chip_id;
|
||||||
|
|
||||||
|
if (!this->read_byte(QWIIC_PIR_CHIP_ID, &chip_id)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read the chip's ID");
|
||||||
|
|
||||||
|
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chip_id != QWIIC_PIR_DEVICE_ID) {
|
||||||
|
ESP_LOGE(TAG, "Unknown chip ID, is this a Qwiic PIR?");
|
||||||
|
|
||||||
|
this->error_code_ = ERROR_WRONG_CHIP_ID;
|
||||||
|
this->mark_failed();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->write_byte_16(QWIIC_PIR_DEBOUNCE_TIME, this->debounce_time_)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to configure debounce time.");
|
||||||
|
|
||||||
|
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) {
|
||||||
|
// Publish the starting raw state of the PIR sensor
|
||||||
|
// If NATIVE mode, the binary_sensor state would be unknown until a motion event
|
||||||
|
if (!this->read_byte(QWIIC_PIR_EVENT_STATUS, &this->event_register_.reg)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read initial sensor state.");
|
||||||
|
|
||||||
|
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->publish_state(this->event_register_.raw_reading);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QwiicPIRComponent::loop() {
|
||||||
|
// Read Event Register
|
||||||
|
if (!this->read_byte(QWIIC_PIR_EVENT_STATUS, &this->event_register_.reg)) {
|
||||||
|
ESP_LOGW(TAG, "Failed to communicate with sensor");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->debounce_mode_ == HYBRID_DEBOUNCE_MODE) {
|
||||||
|
// Use a combination of the raw sensor reading and the device's event detection to determine state
|
||||||
|
// - The device is hardcoded to use a debounce time of 1 ms in this mode
|
||||||
|
// - Any event, even if it is object_removed, implies motion was active since the last loop, so publish true
|
||||||
|
// - Use ESPHome's built-in filters for debouncing
|
||||||
|
this->publish_state(this->event_register_.raw_reading || this->event_register_.event_available);
|
||||||
|
|
||||||
|
if (this->event_register_.event_available) {
|
||||||
|
this->clear_events_();
|
||||||
|
}
|
||||||
|
} else if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) {
|
||||||
|
// Uses the device's firmware to debounce the signal
|
||||||
|
// - Follows the logic of SparkFun's example implementation:
|
||||||
|
// https://github.com/sparkfun/SparkFun_Qwiic_PIR_Arduino_Library/blob/master/examples/Example2_PrintPIRStatus/Example2_PrintPIRStatus.ino
|
||||||
|
// (accessed July 2023)
|
||||||
|
// - Is unreliable at detecting an object being removed, especially at debounce rates even slightly large
|
||||||
|
if (this->event_register_.event_available) {
|
||||||
|
// If an object is detected, publish true
|
||||||
|
if (this->event_register_.object_detected)
|
||||||
|
this->publish_state(true);
|
||||||
|
|
||||||
|
// If an object has been removed, publish false
|
||||||
|
if (this->event_register_.object_removed)
|
||||||
|
this->publish_state(false);
|
||||||
|
|
||||||
|
this->clear_events_();
|
||||||
|
}
|
||||||
|
} else if (this->debounce_mode_ == RAW_DEBOUNCE_MODE) {
|
||||||
|
// Publishes the raw PIR sensor reading with no further logic
|
||||||
|
// - May miss a very short motion detection if the ESP's loop time is slow
|
||||||
|
this->publish_state(this->event_register_.raw_reading);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QwiicPIRComponent::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Qwiic PIR:");
|
||||||
|
|
||||||
|
if (this->debounce_mode_ == RAW_DEBOUNCE_MODE) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Debounce Mode: RAW");
|
||||||
|
} else if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Debounce Mode: NATIVE");
|
||||||
|
ESP_LOGCONFIG(TAG, " Debounce Time: %ums", this->debounce_time_);
|
||||||
|
} else if (this->debounce_mode_ == HYBRID_DEBOUNCE_MODE) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Debounce Mode: HYBRID");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this->error_code_) {
|
||||||
|
case NONE:
|
||||||
|
break;
|
||||||
|
case ERROR_COMMUNICATION_FAILED:
|
||||||
|
ESP_LOGE(TAG, " Communication with Qwiic PIR failed!");
|
||||||
|
break;
|
||||||
|
case ERROR_WRONG_CHIP_ID:
|
||||||
|
ESP_LOGE(TAG, " Qwiic PIR has wrong chip ID - please verify you are using a Qwiic PIR");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, " Qwiic PIR error code %d", (int) this->error_code_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
LOG_BINARY_SENSOR(" ", "Qwiic PIR Binary Sensor", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QwiicPIRComponent::clear_events_() {
|
||||||
|
// Clear event status register
|
||||||
|
if (!this->write_byte(QWIIC_PIR_EVENT_STATUS, 0x00))
|
||||||
|
ESP_LOGW(TAG, "Failed to clear events on sensor");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qwiic_pir
|
||||||
|
} // namespace esphome
|
70
esphome/components/qwiic_pir/qwiic_pir.h
Normal file
70
esphome/components/qwiic_pir/qwiic_pir.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Adds support for Qwiic PIR motion sensors that communicate over an I2C bus.
|
||||||
|
* These sensors use Sharp PIR motion sensors to detect motion. A firmware running on an ATTiny84 translates the digital
|
||||||
|
* output to I2C communications.
|
||||||
|
* ATTiny84 firmware: https://github.com/sparkfun/Qwiic_PIR (acccessed July 2023)
|
||||||
|
* SparkFun's Arduino library: https://github.com/sparkfun/SparkFun_Qwiic_PIR_Arduino_Library (accessed July 2023)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace qwiic_pir {
|
||||||
|
|
||||||
|
// Qwiic PIR I2C Register Addresses
|
||||||
|
enum {
|
||||||
|
QWIIC_PIR_CHIP_ID = 0x00,
|
||||||
|
QWIIC_PIR_EVENT_STATUS = 0x03,
|
||||||
|
QWIIC_PIR_DEBOUNCE_TIME = 0x05, // uint16_t debounce time in milliseconds
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DebounceMode {
|
||||||
|
RAW_DEBOUNCE_MODE,
|
||||||
|
NATIVE_DEBOUNCE_MODE,
|
||||||
|
HYBRID_DEBOUNCE_MODE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t QWIIC_PIR_DEVICE_ID = 0x72;
|
||||||
|
|
||||||
|
class QwiicPIRComponent : public Component, public i2c::I2CDevice, public binary_sensor::BinarySensor {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
void set_debounce_time(uint16_t debounce_time) { this->debounce_time_ = debounce_time; }
|
||||||
|
void set_debounce_mode(DebounceMode mode) { this->debounce_mode_ = mode; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint16_t debounce_time_{};
|
||||||
|
|
||||||
|
DebounceMode debounce_mode_{};
|
||||||
|
|
||||||
|
enum ErrorCode {
|
||||||
|
NONE = 0,
|
||||||
|
ERROR_COMMUNICATION_FAILED,
|
||||||
|
ERROR_WRONG_CHIP_ID,
|
||||||
|
} error_code_{NONE};
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool raw_reading : 1; // raw state of PIR sensor
|
||||||
|
bool event_available : 1; // a debounced object has been detected or removed
|
||||||
|
bool object_removed : 1; // a debounced object is no longer detected
|
||||||
|
bool object_detected : 1; // a debounced object has been detected
|
||||||
|
bool : 4;
|
||||||
|
};
|
||||||
|
uint8_t reg;
|
||||||
|
} event_register_ = {.reg = 0};
|
||||||
|
|
||||||
|
void clear_events_();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qwiic_pir
|
||||||
|
} // namespace esphome
|
|
@ -1813,6 +1813,9 @@ binary_sensor:
|
||||||
name: still
|
name: still
|
||||||
out_pin_presence_status:
|
out_pin_presence_status:
|
||||||
name: out pin presence status
|
name: out pin presence status
|
||||||
|
- platform: qwiic_pir
|
||||||
|
i2c_id: i2c_bus
|
||||||
|
name: "Qwiic PIR Motion Sensor"
|
||||||
|
|
||||||
pca9685:
|
pca9685:
|
||||||
frequency: 500
|
frequency: 500
|
||||||
|
|
Loading…
Reference in a new issue