mirror of
https://github.com/esphome/esphome.git
synced 2024-11-09 16:57:47 +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/qmp6988/* @andrewpc
|
||||
esphome/components/qr_code/* @wjtje
|
||||
esphome/components/qwiic_pir/* @kahrendt
|
||||
esphome/components/radon_eye_ble/* @jeffeb3
|
||||
esphome/components/radon_eye_rd200/* @jeffeb3
|
||||
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
|
||||
out_pin_presence_status:
|
||||
name: out pin presence status
|
||||
- platform: qwiic_pir
|
||||
i2c_id: i2c_bus
|
||||
name: "Qwiic PIR Motion Sensor"
|
||||
|
||||
pca9685:
|
||||
frequency: 500
|
||||
|
|
Loading…
Reference in a new issue