initial commit

This commit is contained in:
Michael Doppler 2024-04-28 08:05:33 +00:00
parent 16e0b78c64
commit 0bcdc4bf56
13 changed files with 435 additions and 0 deletions

View file

@ -11,6 +11,8 @@ esphome/*.py @esphome/core
esphome/core/* @esphome/core esphome/core/* @esphome/core
# Integrations # Integrations
esphome/components/MCP3428/* @mdop
esphome/components/MCP3428/sensor/* @mdop
esphome/components/a01nyub/* @MrSuicideParrot esphome/components/a01nyub/* @MrSuicideParrot
esphome/components/a02yyuw/* @TH-Braemer esphome/components/a02yyuw/* @TH-Braemer
esphome/components/absolute_humidity/* @DAVe3283 esphome/components/absolute_humidity/* @DAVe3283

View file

@ -0,0 +1,33 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c
from esphome.const import CONF_ID
CODEOWNERS = ["@mdop"]
DEPENDENCIES = ["i2c"]
MULTI_CONF = True
mcp3428_ns = cg.esphome_ns.namespace("mcp3428")
MCP3428Component = mcp3428_ns.class_("MCP3428Component", cg.Component, i2c.I2CDevice)
CONF_CONTINUOUS_MODE = "continuous_mode"
CONF_MCP3428_ID = "mcp3428_id"
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(MCP3428Component),
cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(None))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))

View file

@ -0,0 +1,141 @@
#include "mcp3428.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
namespace mcp3428 {
static const char *const TAG = "mcp3426/7/8";
void MCP3428Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up MCP3426/7/8...");
uint8_t anwser[3];
if (!this->read(anwser, 3)) {
this->mark_failed();
return;
}
ESP_LOGCONFIG(TAG, "Configuring MCP3426/7/8...");
/* config byte structure: (bit|description)
* 7 | ready bit, 0 means new data in the result
* 6 | Channel selection bit 1
* 5 | Channel selection bit 0
* 4 | conversion mode bit (1 continuous mode, 0 singe shot)
* 3 | Resolution bit 1
* 2 | Resolution bit 0
* 1 | Gain selection bit 1
* 0 | Gain selection bit 0
*/
uint8_t config = 0;
// set ready bit and conversion mode bit
// 0b0xx0xxxx
if (this->continuous_mode_) {
// initial state should be no new measurement in continuous mode
config = config | 0b10010000;
} else {
// don't initiate measurement in single shot mode
config = config & 0b01101111;
}
// leave channel at 1, gain at 1x, and resolution at 12 bit
if (!this->write(&config, 1)) {
this->mark_failed();
return;
}
this->prev_config_ = config;
}
void MCP3428Component::dump_config() {
ESP_LOGCONFIG(TAG, "Setting up MCP3426/7/8...");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with MCP3426/7/8 failed!");
}
}
float MCP3428Component::request_measurement(MCP3428Multiplexer multiplexer, MCP3428Gain gain,
MCP3428Resolution resolution) {
uint8_t config = 0;
// set ready bit to 1, will starts measurement in single shot mode and mark measurement as not yet ready in continuous
// mode
config |= 1 << 7;
// set channel
config |= multiplexer << 5;
// set conversion mode
if (this->continuous_mode_) {
config |= 1 << 4;
}
// set resolution
config |= resolution << 2;
// set gain
config |= gain;
// If continuous mode and config (besides ready bit) are the same there is no need to upload new config, reading the
// result is enough
if (!((this->prev_config_ & 0b00010000) > 0 and (this->prev_config_ & 0b01111111) == (config & 0b01111111))) {
if (!this->write(&config, 1)) {
this->status_set_warning();
return NAN;
}
this->prev_config_ = config;
}
// MCP is now configured, read output until ready flag is 0 for a valid measurement
uint32_t start = millis();
uint8_t anwser[3];
while (true) {
if (!this->read(anwser, 3)) {
this->status_set_warning();
return NAN;
}
if ((anwser[2] & 0b10000000) == 0) {
// ready flag is 0, valid measurement received
break;
}
if (millis() - start > 100) {
ESP_LOGW(TAG, "Reading MCP3428 measurement timed out");
this->status_set_warning();
return NAN;
}
yield();
}
// got valid measurement, clean up unused bits from the anwser code and prepare tick size
float tick_voltage = 2.048f / 32768; // ref voltage 2.048V/non-sign bits, default 15 bits
switch (resolution) {
case MCP3428Resolution::MCP3428_12_BITS:
// Structure [sign][sign][sign][sign][sign][value][value][value], only keep sign at MSB for int16_t
anwser[0] &= 0b10000111;
tick_voltage *= 16;
break;
case MCP3428Resolution::MCP3428_14_BITS:
// Structure [sign][sign][sign][value][value][value][value][value]
anwser[0] &= 0b10011111;
tick_voltage *= 4;
break;
default: // nothing to do for 16 bit
break;
}
switch (gain) {
case MCP3428Gain::MCP3428_GAIN_2:
tick_voltage /= 2;
break;
case MCP3428Gain::MCP3428_GAIN_4:
tick_voltage /= 4;
break;
case MCP3428Gain::MCP3428_GAIN_8:
tick_voltage /= 8;
break;
default:
break;
}
// convert code (first 2 bytes of cleaned up anwser) into voltage ticks
int16_t ticks = anwser[0] << 8 | anwser[1];
this->status_clear_warning();
return tick_voltage * ticks;
}
} // namespace mcp3428
} // namespace esphome

View file

@ -0,0 +1,47 @@
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/component.h"
namespace esphome {
namespace mcp3428 {
// the second bit is ignored in MCP3426/7 and will result in measurement of channel 1 or 2
enum MCP3428Multiplexer {
MCP3428_MULTIPLEXER_CHANNEL_1 = 0b00,
MCP3428_MULTIPLEXER_CHANNEL_2 = 0b01,
MCP3428_MULTIPLEXER_CHANNEL_3 = 0b10,
MCP3428_MULTIPLEXER_CHANNEL_4 = 0b11,
};
enum MCP3428Gain {
MCP3428_GAIN_1 = 0b00,
MCP3428_GAIN_2 = 0b01,
MCP3428_GAIN_4 = 0b10,
MCP3428_GAIN_8 = 0b11,
};
enum MCP3428Resolution {
MCP3428_12_BITS = 0b00,
MCP3428_14_BITS = 0b01,
MCP3428_16_BITS = 0b10,
};
class MCP3428Component : public Component, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
/// HARDWARE_LATE setup priority
float get_setup_priority() const override { return setup_priority::DATA; }
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
/// Helper method to request a measurement from a sensor.
float request_measurement(MCP3428Multiplexer multiplexer, MCP3428Gain gain, MCP3428Resolution resolution);
protected:
uint8_t prev_config_{0};
bool continuous_mode_;
};
} // namespace mcp3428
} // namespace esphome

View file

@ -0,0 +1,75 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, voltage_sampler
from esphome.const import (
CONF_GAIN,
CONF_MULTIPLEXER,
CONF_RESOLUTION,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
CONF_ID,
)
from .. import mcp3428_ns, MCP3428Component, CONF_MCP3428_ID
CODEOWNERS = ["@mdop"]
AUTO_LOAD = ["voltage_sampler"]
DEPENDENCIES = ["mcp3428"]
MCP3428Multiplexer = mcp3428_ns.enum("MCP3428Multiplexer")
MUX = {
1: MCP3428Multiplexer.MCP3428_MULTIPLEXER_CHANNEL_1,
2: MCP3428Multiplexer.MCP3428_MULTIPLEXER_CHANNEL_2,
3: MCP3428Multiplexer.MCP3428_MULTIPLEXER_CHANNEL_3,
4: MCP3428Multiplexer.MCP3428_MULTIPLEXER_CHANNEL_4,
}
MCP3428Gain = mcp3428_ns.enum("MCP3428Gain")
GAIN = {
1: MCP3428Gain.MCP3428_GAIN_1,
2: MCP3428Gain.MCP3428_GAIN_2,
4: MCP3428Gain.MCP3428_GAIN_4,
8: MCP3428Gain.MCP3428_GAIN_8,
}
MCP3428Resolution = mcp3428_ns.enum("MCP3428Resolution")
RESOLUTION = {
12: MCP3428Resolution.MCP3428_12_BITS,
14: MCP3428Resolution.MCP3428_14_BITS,
16: MCP3428Resolution.MCP3428_16_BITS,
}
MCP3428Sensor = mcp3428_ns.class_(
"MCP3428Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
)
CONFIG_SCHEMA = (
sensor.sensor_schema(
MCP3428Sensor,
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=6,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{
cv.GenerateID(CONF_MCP3428_ID): cv.use_id(MCP3428Component),
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, int=True),
cv.Optional(CONF_GAIN, default=1): cv.enum(GAIN, int=True),
cv.Optional(CONF_RESOLUTION, default=16): cv.enum(RESOLUTION, int=True),
}
)
.extend(cv.polling_component_schema("60s"))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await sensor.register_sensor(var, config)
await cg.register_component(var, config)
await cg.register_parented(var, config[CONF_MCP3428_ID])
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
cg.add(var.set_gain(config[CONF_GAIN]))
cg.add(var.set_resolution(config[CONF_RESOLUTION]))

View file

@ -0,0 +1,30 @@
#include "mcp3428_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace mcp3428 {
static const char *const TAG = "mcp3426/7/8.sensor";
float MCP3428Sensor::sample() {
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_);
}
void MCP3428Sensor::update() {
float v = this->sample();
if (!std::isnan(v)) {
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
this->publish_state(v);
}
}
void MCP3428Sensor::dump_config() {
LOG_SENSOR(" ", "MCP3426/7/8 Sensor", this);
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_);
}
} // namespace mcp3428
} // namespace esphome

View file

@ -0,0 +1,35 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h"
#include "../mcp3428.h"
namespace esphome {
namespace mcp3428 {
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
class MCP3428Sensor : public sensor::Sensor,
public PollingComponent,
public voltage_sampler::VoltageSampler,
public Parented<MCP3428Component> {
public:
void update() override;
void set_multiplexer(MCP3428Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
void set_gain(MCP3428Gain gain) { this->gain_ = gain; }
void set_resolution(MCP3428Resolution resolution) { this->resolution_ = resolution; }
float sample() override;
void dump_config() override;
protected:
MCP3428Multiplexer multiplexer_;
MCP3428Gain gain_;
MCP3428Resolution resolution_;
};
} // namespace mcp3428
} // namespace esphome

View file

@ -0,0 +1,12 @@
i2c:
- id: i2c_mcp3428
scl: 16
sda: 17
mcp3428:
address: 0b1101000
sensor:
- platform: mcp3428
multiplexer: 1
id: mcp3428_sensor

View file

@ -0,0 +1,12 @@
i2c:
- id: i2c_mcp3428
scl: 5
sda: 4
mcp3428:
address: 0b1101000
sensor:
- platform: mcp3428
multiplexer: 1
id: mcp3428_sensor

View file

@ -0,0 +1,12 @@
i2c:
- id: i2c_mcp3428
scl: 16
sda: 17
mcp3428:
address: 0b1101000
sensor:
- platform: mcp3428
multiplexer: 1
id: mcp3428_sensor

View file

@ -0,0 +1,12 @@
i2c:
- id: i2c_mcp3428
scl: 16
sda: 17
mcp3428:
address: 0b1101000
sensor:
- platform: mcp3428
multiplexer: 1
id: mcp3428_sensor

View file

@ -0,0 +1,12 @@
i2c:
- id: i2c_mcp3428
scl: 5
sda: 4
mcp3428:
address: 0b1101000
sensor:
- platform: mcp3428
multiplexer: 1
id: mcp3428_sensor

View file

@ -0,0 +1,12 @@
i2c:
- id: i2c_mcp3428
scl: 5
sda: 4
mcp3428:
address: 0b1101000
sensor:
- platform: mcp3428
multiplexer: 1
id: mcp3428_sensor