add person sensor (SEN21231) from usefulsensors (#4454)

* add person sensor (SEN21231) from usefulsensors

* add person sensor (SEN21231) from usefulsensors

* change file mode

* fix tests

* fix tests

* rollback un-intended changes

* Update esphome/components/sen21231/sen21231.cpp

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sen21231.cpp

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sen21231.cpp

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sen21231.cpp

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sen21231.cpp

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sen21231.h

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sensor.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sensor.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sensor.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sensor.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* remove unused import

* Update esphome/components/sen21231/sen21231.h

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sensor.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sensor.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sen21231.h

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/sen21231/sen21231.h

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* remove unused import

* Update sen21231.h

* lint changes

* linting

* linting

* Update sen21231.h

* Update sen21231.cpp linting

* linting fixes

* fix codeowners

---------

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Shreyas Karnik 2023-02-22 16:37:23 -08:00 committed by GitHub
parent 8e1430243e
commit 91e037346b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 137 additions and 1 deletions

View file

@ -210,6 +210,7 @@ esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sdp3x/* @Azimath
esphome/components/selec_meter/* @sourabhjaiswal
esphome/components/select/* @esphome/core
esphome/components/sen21231/* @shreyaskarnik
esphome/components/sen5x/* @martgras
esphome/components/sensirion_common/* @martgras
esphome/components/sensor/* @esphome/core

View file

View file

@ -0,0 +1,32 @@
#include "sen21231.h"
#include "esphome/core/log.h"
namespace esphome {
namespace sen21231_sensor {
static const char *const TAG = "sen21231_sensor.sensor";
void Sen21231Sensor::update() { this->read_data_(); }
void Sen21231Sensor::dump_config() {
ESP_LOGCONFIG(TAG, "SEN21231:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with SEN21231 failed!");
}
ESP_LOGI(TAG, "SEN21231: %s", this->is_failed() ? "FAILED" : "OK");
LOG_UPDATE_INTERVAL(this);
}
void Sen21231Sensor::read_data_() {
person_sensor_results_t results;
this->read_bytes(PERSON_SENSOR_I2C_ADDRESS, (uint8_t *) &results, sizeof(results));
ESP_LOGD(TAG, "SEN21231: %d faces detected", results.num_faces);
this->publish_state(results.num_faces);
if (results.num_faces == 1) {
ESP_LOGD(TAG, "SEN21231: is facing towards camera: %d", results.faces[0].is_facing);
}
}
} // namespace sen21231_sensor
} // namespace esphome

View file

@ -0,0 +1,77 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
// ref:
// https://github.com/usefulsensors/person_sensor_pico_c/blob/main/person_sensor.h
namespace esphome {
namespace sen21231_sensor {
// The I2C address of the person sensor board.
static const uint8_t PERSON_SENSOR_I2C_ADDRESS = 0x62;
static const uint8_t PERSON_SENSOR_REG_MODE = 0x01;
static const uint8_t PERSON_SENSOR_REG_ENABLE_ID = 0x02;
static const uint8_t PERSON_SENSOR_REG_SINGLE_SHOT = 0x03;
static const uint8_t PERSON_SENSOR_REG_CALIBRATE_ID = 0x04;
static const uint8_t PERSON_SENSOR_REG_PERSIST_IDS = 0x05;
static const uint8_t PERSON_SENSOR_REG_ERASE_IDS = 0x06;
static const uint8_t PERSON_SENSOR_REG_DEBUG_MODE = 0x07;
static const uint8_t PERSON_SENSOR_MAX_FACES_COUNT = 4;
static const uint8_t PERSON_SENSOR_MAX_IDS_COUNT = 7;
// The results returned from the sensor have a short header providing
// information about the length of the data packet:
// reserved: Currently unused bytes.
// data_size: Length of the entire packet, excluding the header and
// checksum.
// For version 1.0 of the sensor, this should be 40.
using person_sensor_results_header_t = struct {
uint8_t reserved[2]; // Bytes 0-1.
uint16_t data_size; // Bytes 2-3.
};
// Each face found has a set of information associated with it:
// box_confidence: How certain we are we have found a face, from 0 to 255.
// box_left: X coordinate of the left side of the box, from 0 to 255.
// box_top: Y coordinate of the top edge of the box, from 0 to 255.
// box_width: Width of the box, where 255 is the full view port size.
// box_height: Height of the box, where 255 is the full view port size.
// id_confidence: How sure the sensor is about the recognition result.
// id: Numerical ID assigned to this face.
// is_looking_at: Whether the person is facing the camera, 0 or 1.
using person_sensor_face_t = struct __attribute__((__packed__)) {
uint8_t box_confidence; // Byte 1.
uint8_t box_left; // Byte 2.
uint8_t box_top; // Byte 3.
uint8_t box_right; // Byte 4.
uint8_t box_bottom; // Byte 5.
int8_t id_confidence; // Byte 6.
int8_t id; // Byte 7
uint8_t is_facing; // Byte 8.
};
// This is the full structure of the packet returned over the wire from the
// sensor when we do an I2C read from the peripheral address.
// The checksum should be the CRC16 of bytes 0 to 38. You shouldn't need to
// verify this in practice, but we found it useful during our own debugging.
using person_sensor_results_t = struct __attribute__((__packed__)) {
person_sensor_results_header_t header; // Bytes 0-4.
int8_t num_faces; // Byte 5.
person_sensor_face_t faces[PERSON_SENSOR_MAX_FACES_COUNT]; // Bytes 6-37.
uint16_t checksum; // Bytes 38-39.
};
class Sen21231Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
public:
void update() override;
void dump_config() override;
protected:
void read_data_();
};
} // namespace sen21231_sensor
} // namespace esphome

View file

@ -0,0 +1,24 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import ICON_MOTION_SENSOR
CODEOWNERS = ["@shreyaskarnik"]
DEPENDENCIES = ["i2c"]
sen21231_sensor_ns = cg.esphome_ns.namespace("sen21231_sensor")
Sen21231Sensor = sen21231_sensor_ns.class_(
"Sen21231Sensor", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
sensor.sensor_schema(Sen21231Sensor, icon=ICON_MOTION_SENSOR, accuracy_decimals=1)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x62))
)
async def to_code(config):
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)

View file

@ -1221,7 +1221,9 @@ sensor:
name: "Still Energy"
detection_distance:
name: "Distance Detection"
- platform: sen21231
name: "Person Sensor"
i2c_id: i2c_bus
esp32_touch:
setup_mode: false
iir_filter: 10ms