mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 09:17:46 +01:00
Add PZEM004T/PZEMAC/PZEMDC Support (#587)
* Add PZEM004T Support * Don't flush as much * Update pzem004t.cpp * Add generalized modbus * Add PZEMAC * Add PZEMDC * Fix file modes * Lint * Fix * Fix * Add check_uart_settings
This commit is contained in:
parent
f1e00f8c8e
commit
e077ad56bd
26 changed files with 738 additions and 7 deletions
|
@ -172,6 +172,7 @@ void CSE7766Component::dump_config() {
|
||||||
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
|
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
|
||||||
LOG_SENSOR(" ", "Current", this->current_sensor_);
|
LOG_SENSOR(" ", "Current", this->current_sensor_);
|
||||||
LOG_SENSOR(" ", "Power", this->power_sensor_);
|
LOG_SENSOR(" ", "Power", this->power_sensor_);
|
||||||
|
this->check_uart_settings(4800);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cse7766
|
} // namespace cse7766
|
||||||
|
|
|
@ -94,6 +94,7 @@ void MHZ19Component::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "MH-Z19:");
|
ESP_LOGCONFIG(TAG, "MH-Z19:");
|
||||||
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
|
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
|
||||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
|
this->check_uart_settings(9600);
|
||||||
|
|
||||||
if (this->abc_boot_logic_ == MHZ19_ABC_ENABLED) {
|
if (this->abc_boot_logic_ == MHZ19_ABC_ENABLED) {
|
||||||
ESP_LOGCONFIG(TAG, " Automatic baseline calibration enabled on boot");
|
ESP_LOGCONFIG(TAG, " Automatic baseline calibration enabled on boot");
|
||||||
|
|
43
esphome/components/modbus/__init__.py
Normal file
43
esphome/components/modbus/__init__.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import uart
|
||||||
|
from esphome.const import CONF_ID, CONF_ADDRESS
|
||||||
|
from esphome.core import coroutine
|
||||||
|
|
||||||
|
DEPENDENCIES = ['uart']
|
||||||
|
|
||||||
|
modbus_ns = cg.esphome_ns.namespace('modbus')
|
||||||
|
Modbus = modbus_ns.class_('Modbus', cg.Component, uart.UARTDevice)
|
||||||
|
ModbusDevice = modbus_ns.class_('ModbusDevice')
|
||||||
|
MULTI_CONF = True
|
||||||
|
|
||||||
|
CONF_MODBUS_ID = 'modbus_id'
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(Modbus),
|
||||||
|
}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
cg.add_global(modbus_ns.using)
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
|
||||||
|
yield uart.register_uart_device(var, config)
|
||||||
|
|
||||||
|
|
||||||
|
def modbus_device_schema(default_address):
|
||||||
|
schema = {
|
||||||
|
cv.GenerateID(CONF_MODBUS_ID): cv.use_id(Modbus),
|
||||||
|
}
|
||||||
|
if default_address is None:
|
||||||
|
schema[cv.Required(CONF_ADDRESS)] = cv.hex_uint8_t
|
||||||
|
else:
|
||||||
|
schema[cv.Optional(CONF_ADDRESS, default=default_address)] = cv.hex_uint8_t
|
||||||
|
return cv.Schema(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@coroutine
|
||||||
|
def register_modbus_device(var, config):
|
||||||
|
parent = yield cg.get_variable(config[CONF_MODBUS_ID])
|
||||||
|
cg.add(var.set_parent(parent))
|
||||||
|
cg.add(var.set_address(config[CONF_ADDRESS]))
|
119
esphome/components/modbus/modbus.cpp
Normal file
119
esphome/components/modbus/modbus.cpp
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#include "modbus.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace modbus {
|
||||||
|
|
||||||
|
static const char *TAG = "modbus";
|
||||||
|
|
||||||
|
void Modbus::loop() {
|
||||||
|
const uint32_t now = millis();
|
||||||
|
if (now - this->last_modbus_byte_ > 50) {
|
||||||
|
this->rx_buffer_.clear();
|
||||||
|
this->last_modbus_byte_ = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this->available()) {
|
||||||
|
uint8_t byte;
|
||||||
|
this->read_byte(&byte);
|
||||||
|
if (this->parse_modbus_byte_(byte)) {
|
||||||
|
this->last_modbus_byte_ = now;
|
||||||
|
} else {
|
||||||
|
this->rx_buffer_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t crc16(uint8_t *data, uint8_t len) {
|
||||||
|
uint16_t crc = 0xFFFF;
|
||||||
|
while (len--) {
|
||||||
|
crc ^= *data++;
|
||||||
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
|
if ((crc & 0x01) != 0) {
|
||||||
|
crc >>= 1;
|
||||||
|
crc ^= 0xA001;
|
||||||
|
} else {
|
||||||
|
crc >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Modbus::parse_modbus_byte_(uint8_t byte) {
|
||||||
|
size_t at = this->rx_buffer_.size();
|
||||||
|
this->rx_buffer_.push_back(byte);
|
||||||
|
uint8_t *raw = &this->rx_buffer_[0];
|
||||||
|
|
||||||
|
// Byte 0: modbus address (match all)
|
||||||
|
if (at == 0)
|
||||||
|
return true;
|
||||||
|
uint8_t address = raw[0];
|
||||||
|
|
||||||
|
// Byte 1: Function (msb indicates error)
|
||||||
|
if (at == 1)
|
||||||
|
return (byte & 0x80) != 0x80;
|
||||||
|
|
||||||
|
// Byte 2: Size (with modbus rtu function code 4/3)
|
||||||
|
// See also https://en.wikipedia.org/wiki/Modbus
|
||||||
|
if (at == 2)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
uint8_t data_len = raw[2];
|
||||||
|
// Byte 3..3+data_len-1: Data
|
||||||
|
if (at < 3 + data_len)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Byte 3+data_len: CRC_LO (over all bytes)
|
||||||
|
if (at == 3 + data_len)
|
||||||
|
return true;
|
||||||
|
// Byte 3+len+1: CRC_HI (over all bytes)
|
||||||
|
uint16_t computed_crc = crc16(raw, 3 + data_len);
|
||||||
|
uint16_t remote_crc = uint16_t(raw[3 + data_len]) | (uint16_t(raw[3 + data_len]) << 8);
|
||||||
|
if (computed_crc != remote_crc) {
|
||||||
|
ESP_LOGW(TAG, "Modbus CRC Check failed! %02X!=%02X", computed_crc, remote_crc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> data(this->rx_buffer_.begin() + 3, this->rx_buffer_.begin() + 3 + data_len);
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (auto *device : this->devices_) {
|
||||||
|
if (device->address_ == address) {
|
||||||
|
device->on_modbus_data(data);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
ESP_LOGW(TAG, "Got Modbus frame from unknown address 0x%02X!", address);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return false to reset buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Modbus::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Modbus:");
|
||||||
|
this->check_uart_settings(9600, 2);
|
||||||
|
}
|
||||||
|
float Modbus::get_setup_priority() const {
|
||||||
|
// After UART bus
|
||||||
|
return setup_priority::BUS - 1.0f;
|
||||||
|
}
|
||||||
|
void Modbus::send(uint8_t address, uint8_t function, uint16_t start_address, uint16_t register_count) {
|
||||||
|
uint8_t frame[8];
|
||||||
|
frame[0] = address;
|
||||||
|
frame[1] = function;
|
||||||
|
frame[2] = start_address >> 8;
|
||||||
|
frame[3] = start_address >> 0;
|
||||||
|
frame[4] = register_count >> 8;
|
||||||
|
frame[5] = register_count >> 0;
|
||||||
|
auto crc = crc16(frame, 6);
|
||||||
|
frame[6] = crc >> 0;
|
||||||
|
frame[7] = crc >> 8;
|
||||||
|
|
||||||
|
this->write_array(frame, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace modbus
|
||||||
|
} // namespace esphome
|
51
esphome/components/modbus/modbus.h
Normal file
51
esphome/components/modbus/modbus.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace modbus {
|
||||||
|
|
||||||
|
class ModbusDevice;
|
||||||
|
|
||||||
|
class Modbus : public uart::UARTDevice, public Component {
|
||||||
|
public:
|
||||||
|
Modbus() = default;
|
||||||
|
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
void register_device(ModbusDevice *device) { this->devices_.push_back(device); }
|
||||||
|
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
void send(uint8_t address, uint8_t function, uint16_t start_address, uint16_t register_count);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool parse_modbus_byte_(uint8_t byte);
|
||||||
|
|
||||||
|
std::vector<uint8_t> rx_buffer_;
|
||||||
|
uint32_t last_modbus_byte_{0};
|
||||||
|
std::vector<ModbusDevice *> devices_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModbusDevice {
|
||||||
|
public:
|
||||||
|
void set_parent(Modbus *parent) { parent_ = parent; }
|
||||||
|
void set_address(uint8_t address) { address_ = address; }
|
||||||
|
virtual void on_modbus_data(const std::vector<uint8_t> &data) = 0;
|
||||||
|
|
||||||
|
void send(uint8_t function, uint16_t start_address, uint16_t register_count) {
|
||||||
|
this->parent_->send(this->address_, function, start_address, register_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend Modbus;
|
||||||
|
|
||||||
|
Modbus *parent_;
|
||||||
|
uint8_t address_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace modbus
|
||||||
|
} // namespace esphome
|
|
@ -169,6 +169,7 @@ void PMSX003Component::dump_config() {
|
||||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
LOG_SENSOR(" ", "Formaldehyde", this->formaldehyde_sensor_);
|
LOG_SENSOR(" ", "Formaldehyde", this->formaldehyde_sensor_);
|
||||||
|
this->check_uart_settings(9600);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace pmsx003
|
} // namespace pmsx003
|
||||||
|
|
0
esphome/components/pzem004t/__init__.py
Normal file
0
esphome/components/pzem004t/__init__.py
Normal file
103
esphome/components/pzem004t/pzem004t.cpp
Normal file
103
esphome/components/pzem004t/pzem004t.cpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#include "pzem004t.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pzem004t {
|
||||||
|
|
||||||
|
static const char *TAG = "pzem004t";
|
||||||
|
|
||||||
|
void PZEM004T::loop() {
|
||||||
|
const uint32_t now = millis();
|
||||||
|
if (now - this->last_read_ > 500 && this->available()) {
|
||||||
|
while (this->available())
|
||||||
|
this->read();
|
||||||
|
this->last_read_ = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PZEM004T packet size is 7 byte
|
||||||
|
while (this->available() >= 7) {
|
||||||
|
auto resp = *this->read_array<7>();
|
||||||
|
// packet format:
|
||||||
|
// 0: packet type
|
||||||
|
// 1-5: data
|
||||||
|
// 6: checksum (sum of other bytes)
|
||||||
|
// see https://github.com/olehs/PZEM004T
|
||||||
|
uint8_t sum = 0;
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
sum += resp[i];
|
||||||
|
|
||||||
|
if (sum != resp[6]) {
|
||||||
|
ESP_LOGV(TAG, "PZEM004T invalid checksum! 0x%02X != 0x%02X", sum, resp[6]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (resp[0]) {
|
||||||
|
case 0xA4: { // Set Module Address Response
|
||||||
|
this->write_state_(READ_VOLTAGE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xA0: { // Voltage Response
|
||||||
|
uint16_t int_voltage = (uint16_t(resp[1]) << 8) | (uint16_t(resp[2]) << 0);
|
||||||
|
float voltage = int_voltage + (resp[3] / 10.0f);
|
||||||
|
if (this->voltage_sensor_ != nullptr)
|
||||||
|
this->voltage_sensor_->publish_state(voltage);
|
||||||
|
ESP_LOGD(TAG, "Got Voltage %.1f V", voltage);
|
||||||
|
this->write_state_(READ_CURRENT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xA1: { // Current Response
|
||||||
|
uint16_t int_current = (uint16_t(resp[1]) << 8) | (uint16_t(resp[2]) << 0);
|
||||||
|
float current = int_current + (resp[3] / 100.0f);
|
||||||
|
if (this->current_sensor_ != nullptr)
|
||||||
|
this->current_sensor_->publish_state(current);
|
||||||
|
ESP_LOGD(TAG, "Got Current %.2f A", current);
|
||||||
|
this->write_state_(READ_POWER);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xA2: { // Active Power Response
|
||||||
|
uint16_t power = (uint16_t(resp[1]) << 8) | (uint16_t(resp[2]) << 0);
|
||||||
|
if (this->power_sensor_ != nullptr)
|
||||||
|
this->power_sensor_->publish_state(power);
|
||||||
|
ESP_LOGD(TAG, "Got Power %u W", power);
|
||||||
|
this->write_state_(DONE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0xA3: // Energy Response
|
||||||
|
case 0xA5: // Set Power Alarm Response
|
||||||
|
case 0xB0: // Voltage Request
|
||||||
|
case 0xB1: // Current Request
|
||||||
|
case 0xB2: // Active Power Response
|
||||||
|
case 0xB3: // Energy Request
|
||||||
|
case 0xB4: // Set Module Address Request
|
||||||
|
case 0xB5: // Set Power Alarm Request
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->last_read_ = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void PZEM004T::update() { this->write_state_(SET_ADDRESS); }
|
||||||
|
void PZEM004T::write_state_(PZEM004T::PZEM004TReadState state) {
|
||||||
|
if (state == DONE) {
|
||||||
|
this->read_state_ = state;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::array<uint8_t, 7> data{};
|
||||||
|
data[0] = state;
|
||||||
|
data[1] = 192;
|
||||||
|
data[2] = 168;
|
||||||
|
data[3] = 1;
|
||||||
|
data[4] = 1;
|
||||||
|
data[5] = 0;
|
||||||
|
data[6] = 0;
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
data[6] += data[i];
|
||||||
|
|
||||||
|
this->write_array(data);
|
||||||
|
this->read_state_ = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pzem004t
|
||||||
|
} // namespace esphome
|
39
esphome/components/pzem004t/pzem004t.h
Normal file
39
esphome/components/pzem004t/pzem004t.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pzem004t {
|
||||||
|
|
||||||
|
class PZEM004T : public PollingComponent, public uart::UARTDevice {
|
||||||
|
public:
|
||||||
|
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||||
|
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
|
||||||
|
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
|
||||||
|
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sensor::Sensor *voltage_sensor_;
|
||||||
|
sensor::Sensor *current_sensor_;
|
||||||
|
sensor::Sensor *power_sensor_;
|
||||||
|
|
||||||
|
enum PZEM004TReadState {
|
||||||
|
SET_ADDRESS = 0xB4,
|
||||||
|
READ_VOLTAGE = 0xB0,
|
||||||
|
READ_CURRENT = 0xB1,
|
||||||
|
READ_POWER = 0xB2,
|
||||||
|
DONE = 0x00,
|
||||||
|
} read_state_{DONE};
|
||||||
|
|
||||||
|
void write_state_(PZEM004TReadState state);
|
||||||
|
|
||||||
|
uint32_t last_read_{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pzem004t
|
||||||
|
} // namespace esphome
|
37
esphome/components/pzem004t/sensor.py
Normal file
37
esphome/components/pzem004t/sensor.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, uart
|
||||||
|
from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \
|
||||||
|
UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT
|
||||||
|
|
||||||
|
DEPENDENCIES = ['uart']
|
||||||
|
|
||||||
|
pzem004t_ns = cg.esphome_ns.namespace('pzem004t')
|
||||||
|
PZEM004T = pzem004t_ns.class_('PZEM004T', cg.PollingComponent, uart.UARTDevice)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(PZEM004T),
|
||||||
|
|
||||||
|
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
|
||||||
|
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
|
||||||
|
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 0),
|
||||||
|
}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield uart.register_uart_device(var, config)
|
||||||
|
|
||||||
|
if CONF_VOLTAGE in config:
|
||||||
|
conf = config[CONF_VOLTAGE]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_voltage_sensor(sens))
|
||||||
|
if CONF_CURRENT in config:
|
||||||
|
conf = config[CONF_CURRENT]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_current_sensor(sens))
|
||||||
|
if CONF_POWER in config:
|
||||||
|
conf = config[CONF_POWER]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_power_sensor(sens))
|
0
esphome/components/pzemac/__init__.py
Normal file
0
esphome/components/pzemac/__init__.py
Normal file
62
esphome/components/pzemac/pzemac.cpp
Normal file
62
esphome/components/pzemac/pzemac.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#include "pzemac.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pzemac {
|
||||||
|
|
||||||
|
static const char *TAG = "pzemac";
|
||||||
|
|
||||||
|
static const uint8_t PZEM_CMD_READ_IN_REGISTERS = 0x04;
|
||||||
|
static const uint8_t PZEM_REGISTER_COUNT = 10; // 10x 16-bit registers
|
||||||
|
|
||||||
|
void PZEMAC::on_modbus_data(const std::vector<uint8_t> &data) {
|
||||||
|
if (data.size() < 20) {
|
||||||
|
ESP_LOGW(TAG, "Invalid size for PZEM AC!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://github.com/esphome/feature-requests/issues/49#issuecomment-538636809
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
||||||
|
// 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34
|
||||||
|
// Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc--
|
||||||
|
|
||||||
|
auto pzem_get_16bit = [&](size_t i) -> uint16_t {
|
||||||
|
return (uint16_t(data[i + 0]) << 8) | (uint16_t(data[i + 1]) << 0);
|
||||||
|
};
|
||||||
|
auto pzem_get_32bit = [&](size_t i) -> uint32_t {
|
||||||
|
return (uint32_t(pzem_get_16bit(i + 2)) << 16) | (uint32_t(pzem_get_16bit(i + 0)) << 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t raw_voltage = pzem_get_16bit(0);
|
||||||
|
float voltage = raw_voltage / 10.0f; // max 6553.5 V
|
||||||
|
|
||||||
|
uint32_t raw_current = pzem_get_32bit(2);
|
||||||
|
float current = raw_current / 1000.0f; // max 4294967.295 A
|
||||||
|
|
||||||
|
uint32_t raw_active_power = pzem_get_32bit(6);
|
||||||
|
float active_power = raw_active_power / 10.0f; // max 429496729.5 W
|
||||||
|
|
||||||
|
uint16_t raw_frequency = pzem_get_16bit(14);
|
||||||
|
float frequency = raw_frequency / 10.0f;
|
||||||
|
|
||||||
|
uint16_t raw_power_factor = pzem_get_16bit(16);
|
||||||
|
float power_factor = raw_power_factor / 100.0f;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "PZEM AC: V=%.1f V, I=%.3f A, P=%.1f W, F=%.1f Hz, PF=%.2f", voltage, current, active_power, frequency,
|
||||||
|
power_factor);
|
||||||
|
if (this->voltage_sensor_ != nullptr)
|
||||||
|
this->voltage_sensor_->publish_state(voltage);
|
||||||
|
if (this->current_sensor_ != nullptr)
|
||||||
|
this->current_sensor_->publish_state(current);
|
||||||
|
if (this->power_sensor_ != nullptr)
|
||||||
|
this->power_sensor_->publish_state(active_power);
|
||||||
|
if (this->frequency_sensor_ != nullptr)
|
||||||
|
this->frequency_sensor_->publish_state(frequency);
|
||||||
|
if (this->power_factor_sensor_ != nullptr)
|
||||||
|
this->power_factor_sensor_->publish_state(power_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PZEMAC::update() { this->send(PZEM_CMD_READ_IN_REGISTERS, 0, PZEM_REGISTER_COUNT); }
|
||||||
|
|
||||||
|
} // namespace pzemac
|
||||||
|
} // namespace esphome
|
31
esphome/components/pzemac/pzemac.h
Normal file
31
esphome/components/pzemac/pzemac.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/modbus/modbus.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pzemac {
|
||||||
|
|
||||||
|
class PZEMAC : public PollingComponent, public modbus::ModbusDevice {
|
||||||
|
public:
|
||||||
|
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||||
|
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
|
||||||
|
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
|
||||||
|
void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; }
|
||||||
|
void set_power_factor_sensor(sensor::Sensor *power_factor_sensor) { power_factor_sensor_ = power_factor_sensor; }
|
||||||
|
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void on_modbus_data(const std::vector<uint8_t> &data) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sensor::Sensor *voltage_sensor_;
|
||||||
|
sensor::Sensor *current_sensor_;
|
||||||
|
sensor::Sensor *power_sensor_;
|
||||||
|
sensor::Sensor *frequency_sensor_;
|
||||||
|
sensor::Sensor *power_factor_sensor_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pzemac
|
||||||
|
} // namespace esphome
|
47
esphome/components/pzemac/sensor.py
Normal file
47
esphome/components/pzemac/sensor.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, modbus
|
||||||
|
from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \
|
||||||
|
CONF_FREQUENCY, UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, \
|
||||||
|
ICON_POWER, CONF_POWER_FACTOR, ICON_CURRENT_AC
|
||||||
|
|
||||||
|
AUTO_LOAD = ['modbus']
|
||||||
|
|
||||||
|
pzemac_ns = cg.esphome_ns.namespace('pzemac')
|
||||||
|
PZEMAC = pzemac_ns.class_('PZEMAC', cg.PollingComponent, modbus.ModbusDevice)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(PZEMAC),
|
||||||
|
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
|
||||||
|
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_CURRENT_AC, 3),
|
||||||
|
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_POWER, 1),
|
||||||
|
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_EMPTY, ICON_CURRENT_AC, 1),
|
||||||
|
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 2),
|
||||||
|
}).extend(cv.polling_component_schema('60s')).extend(modbus.modbus_device_schema(0x01))
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield modbus.register_modbus_device(var, config)
|
||||||
|
|
||||||
|
if CONF_VOLTAGE in config:
|
||||||
|
conf = config[CONF_VOLTAGE]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_voltage_sensor(sens))
|
||||||
|
if CONF_CURRENT in config:
|
||||||
|
conf = config[CONF_CURRENT]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_current_sensor(sens))
|
||||||
|
if CONF_POWER in config:
|
||||||
|
conf = config[CONF_POWER]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_power_sensor(sens))
|
||||||
|
if CONF_FREQUENCY in config:
|
||||||
|
conf = config[CONF_FREQUENCY]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_frequency_sensor(sens))
|
||||||
|
if CONF_POWER_FACTOR in config:
|
||||||
|
conf = config[CONF_POWER_FACTOR]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_power_factor_sensor(sens))
|
0
esphome/components/pzemdc/__init__.py
Normal file
0
esphome/components/pzemdc/__init__.py
Normal file
52
esphome/components/pzemdc/pzemdc.cpp
Normal file
52
esphome/components/pzemdc/pzemdc.cpp
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#include "pzemdc.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pzemdc {
|
||||||
|
|
||||||
|
static const char *TAG = "pzemdc";
|
||||||
|
|
||||||
|
static const uint8_t PZEM_CMD_READ_IN_REGISTERS = 0x04;
|
||||||
|
static const uint8_t PZEM_REGISTER_COUNT = 10; // 10x 16-bit registers
|
||||||
|
|
||||||
|
void PZEMDC::on_modbus_data(const std::vector<uint8_t> &data) {
|
||||||
|
if (data.size() < 16) {
|
||||||
|
ESP_LOGW(TAG, "Invalid size for PZEM DC!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://github.com/esphome/feature-requests/issues/49#issuecomment-538636809
|
||||||
|
// 0 1 2 3 4 5 6 7 = ModBus register
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 = Buffer index
|
||||||
|
// 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29
|
||||||
|
// Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc--
|
||||||
|
|
||||||
|
auto pzem_get_16bit = [&](size_t i) -> uint16_t {
|
||||||
|
return (uint16_t(data[i + 0]) << 8) | (uint16_t(data[i + 1]) << 0);
|
||||||
|
};
|
||||||
|
auto pzem_get_32bit = [&](size_t i) -> uint32_t {
|
||||||
|
return (uint32_t(pzem_get_16bit(i + 2)) << 16) | (uint32_t(pzem_get_16bit(i + 0)) << 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t raw_voltage = pzem_get_16bit(0);
|
||||||
|
float voltage = raw_voltage / 100.0f; // max 655.35 V
|
||||||
|
|
||||||
|
uint16_t raw_current = pzem_get_16bit(2);
|
||||||
|
float current = raw_current / 100.0f; // max 655.35 A
|
||||||
|
|
||||||
|
uint32_t raw_power = pzem_get_32bit(4);
|
||||||
|
float power = raw_power / 10.0f; // max 429496729.5 W
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "PZEM DC: V=%.1f V, I=%.3f A, P=%.1f W", voltage, current, power);
|
||||||
|
if (this->voltage_sensor_ != nullptr)
|
||||||
|
this->voltage_sensor_->publish_state(voltage);
|
||||||
|
if (this->current_sensor_ != nullptr)
|
||||||
|
this->current_sensor_->publish_state(current);
|
||||||
|
if (this->power_sensor_ != nullptr)
|
||||||
|
this->power_sensor_->publish_state(power);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PZEMDC::update() { this->send(PZEM_CMD_READ_IN_REGISTERS, 0, 8); }
|
||||||
|
|
||||||
|
} // namespace pzemdc
|
||||||
|
} // namespace esphome
|
31
esphome/components/pzemdc/pzemdc.h
Normal file
31
esphome/components/pzemdc/pzemdc.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/modbus/modbus.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pzemdc {
|
||||||
|
|
||||||
|
class PZEMDC : public PollingComponent, public modbus::ModbusDevice {
|
||||||
|
public:
|
||||||
|
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||||
|
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
|
||||||
|
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
|
||||||
|
void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; }
|
||||||
|
void set_powerfactor_sensor(sensor::Sensor *powerfactor_sensor) { power_factor_sensor_ = powerfactor_sensor; }
|
||||||
|
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void on_modbus_data(const std::vector<uint8_t> &data) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sensor::Sensor *voltage_sensor_;
|
||||||
|
sensor::Sensor *current_sensor_;
|
||||||
|
sensor::Sensor *power_sensor_;
|
||||||
|
sensor::Sensor *frequency_sensor_;
|
||||||
|
sensor::Sensor *power_factor_sensor_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pzemdc
|
||||||
|
} // namespace esphome
|
36
esphome/components/pzemdc/sensor.py
Normal file
36
esphome/components/pzemdc/sensor.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, modbus
|
||||||
|
from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \
|
||||||
|
UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT, ICON_POWER, ICON_CURRENT_AC
|
||||||
|
|
||||||
|
AUTO_LOAD = ['modbus']
|
||||||
|
|
||||||
|
pzemdc_ns = cg.esphome_ns.namespace('pzemdc')
|
||||||
|
PZEMDC = pzemdc_ns.class_('PZEMDC', cg.PollingComponent, modbus.ModbusDevice)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(PZEMDC),
|
||||||
|
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
|
||||||
|
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_CURRENT_AC, 3),
|
||||||
|
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_POWER, 1),
|
||||||
|
}).extend(cv.polling_component_schema('60s')).extend(modbus.modbus_device_schema(0x01))
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield modbus.register_modbus_device(var, config)
|
||||||
|
|
||||||
|
if CONF_VOLTAGE in config:
|
||||||
|
conf = config[CONF_VOLTAGE]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_voltage_sensor(sens))
|
||||||
|
if CONF_CURRENT in config:
|
||||||
|
conf = config[CONF_CURRENT]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_current_sensor(sens))
|
||||||
|
if CONF_POWER in config:
|
||||||
|
conf = config[CONF_POWER]
|
||||||
|
sens = yield sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_power_sensor(sens))
|
|
@ -56,6 +56,7 @@ void SDS011Component::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " RX-only mode: %s", ONOFF(this->rx_mode_only_));
|
ESP_LOGCONFIG(TAG, " RX-only mode: %s", ONOFF(this->rx_mode_only_));
|
||||||
LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_);
|
LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_);
|
||||||
LOG_SENSOR(" ", "PM10.0", this->pm_10_0_sensor_);
|
LOG_SENSOR(" ", "PM10.0", this->pm_10_0_sensor_);
|
||||||
|
this->check_uart_settings(9600);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDS011Component::loop() {
|
void SDS011Component::loop() {
|
||||||
|
|
|
@ -73,6 +73,7 @@ bool SenseAirComponent::senseair_write_command_(const uint8_t *command, uint8_t
|
||||||
void SenseAirComponent::dump_config() {
|
void SenseAirComponent::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "SenseAir:");
|
ESP_LOGCONFIG(TAG, "SenseAir:");
|
||||||
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
|
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
|
||||||
|
this->check_uart_settings(9600);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace senseair
|
} // namespace senseair
|
||||||
|
|
|
@ -36,6 +36,7 @@ void Tuya::dump_config() {
|
||||||
else
|
else
|
||||||
ESP_LOGCONFIG(TAG, " Datapoint %d: unknown", info.id);
|
ESP_LOGCONFIG(TAG, " Datapoint %d: unknown", info.id);
|
||||||
}
|
}
|
||||||
|
this->check_uart_settings(9600);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Tuya::validate_message_() {
|
bool Tuya::validate_message_() {
|
||||||
|
|
|
@ -29,11 +29,13 @@ def validate_rx_pin(value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
CONF_STOP_BITS = 'stop_bits'
|
||||||
CONFIG_SCHEMA = cv.All(cv.Schema({
|
CONFIG_SCHEMA = cv.All(cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(UARTComponent),
|
cv.GenerateID(): cv.declare_id(UARTComponent),
|
||||||
cv.Required(CONF_BAUD_RATE): cv.int_range(min=1),
|
cv.Required(CONF_BAUD_RATE): cv.int_range(min=1),
|
||||||
cv.Optional(CONF_TX_PIN): pins.output_pin,
|
cv.Optional(CONF_TX_PIN): pins.output_pin,
|
||||||
cv.Optional(CONF_RX_PIN): validate_rx_pin,
|
cv.Optional(CONF_RX_PIN): validate_rx_pin,
|
||||||
|
cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True),
|
||||||
}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN))
|
}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN))
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,6 +50,7 @@ def to_code(config):
|
||||||
cg.add(var.set_tx_pin(config[CONF_TX_PIN]))
|
cg.add(var.set_tx_pin(config[CONF_TX_PIN]))
|
||||||
if CONF_RX_PIN in config:
|
if CONF_RX_PIN in config:
|
||||||
cg.add(var.set_rx_pin(config[CONF_RX_PIN]))
|
cg.add(var.set_rx_pin(config[CONF_RX_PIN]))
|
||||||
|
cg.add(var.set_stop_bits(config[CONF_STOP_BITS]))
|
||||||
|
|
||||||
|
|
||||||
# A schema to use for all UART devices, all UART integrations must extend this!
|
# A schema to use for all UART devices, all UART integrations must extend this!
|
||||||
|
|
|
@ -25,7 +25,10 @@ void UARTComponent::setup() {
|
||||||
}
|
}
|
||||||
int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1;
|
int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1;
|
||||||
int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1;
|
int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1;
|
||||||
this->hw_serial_->begin(this->baud_rate_, SERIAL_8N1, rx, tx);
|
uint32_t config = SERIAL_8N1;
|
||||||
|
if (this->stop_bits_ == 2)
|
||||||
|
config = SERIAL_8N2;
|
||||||
|
this->hw_serial_->begin(this->baud_rate_, config, rx, tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UARTComponent::dump_config() {
|
void UARTComponent::dump_config() {
|
||||||
|
@ -37,6 +40,7 @@ void UARTComponent::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " RX Pin: GPIO%d", *this->rx_pin_);
|
ESP_LOGCONFIG(TAG, " RX Pin: GPIO%d", *this->rx_pin_);
|
||||||
}
|
}
|
||||||
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UARTComponent::write_byte(uint8_t data) {
|
void UARTComponent::write_byte(uint8_t data) {
|
||||||
|
@ -102,21 +106,27 @@ void UARTComponent::setup() {
|
||||||
// Use Arduino HardwareSerial UARTs if all used pins match the ones
|
// Use Arduino HardwareSerial UARTs if all used pins match the ones
|
||||||
// preconfigured by the platform. For example if RX disabled but TX pin
|
// preconfigured by the platform. For example if RX disabled but TX pin
|
||||||
// is 1 we still want to use Serial.
|
// is 1 we still want to use Serial.
|
||||||
|
uint32_t mode = UART_NB_BIT_8 | UART_PARITY_NONE;
|
||||||
|
if (this->stop_bits_ == 1)
|
||||||
|
mode |= UART_NB_STOP_BIT_1;
|
||||||
|
else
|
||||||
|
mode |= UART_NB_STOP_BIT_2;
|
||||||
|
SerialConfig config = static_cast<SerialConfig>(mode);
|
||||||
if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) {
|
if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) {
|
||||||
this->hw_serial_ = &Serial;
|
this->hw_serial_ = &Serial;
|
||||||
this->hw_serial_->begin(this->baud_rate_);
|
this->hw_serial_->begin(this->baud_rate_, config);
|
||||||
} else if (this->tx_pin_.value_or(15) == 15 && this->rx_pin_.value_or(13) == 13) {
|
} else if (this->tx_pin_.value_or(15) == 15 && this->rx_pin_.value_or(13) == 13) {
|
||||||
this->hw_serial_ = &Serial;
|
this->hw_serial_ = &Serial;
|
||||||
this->hw_serial_->begin(this->baud_rate_);
|
this->hw_serial_->begin(this->baud_rate_, config);
|
||||||
this->hw_serial_->swap();
|
this->hw_serial_->swap();
|
||||||
} else if (this->tx_pin_.value_or(2) == 2 && this->rx_pin_.value_or(8) == 8) {
|
} else if (this->tx_pin_.value_or(2) == 2 && this->rx_pin_.value_or(8) == 8) {
|
||||||
this->hw_serial_ = &Serial1;
|
this->hw_serial_ = &Serial1;
|
||||||
this->hw_serial_->begin(this->baud_rate_);
|
this->hw_serial_->begin(this->baud_rate_, config);
|
||||||
} else {
|
} else {
|
||||||
this->sw_serial_ = new ESP8266SoftwareSerial();
|
this->sw_serial_ = new ESP8266SoftwareSerial();
|
||||||
int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1;
|
int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1;
|
||||||
int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1;
|
int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1;
|
||||||
this->sw_serial_->setup(tx, rx, this->baud_rate_);
|
this->sw_serial_->setup(tx, rx, this->baud_rate_, this->stop_bits_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +139,7 @@ void UARTComponent::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " RX Pin: GPIO%d", *this->rx_pin_);
|
ESP_LOGCONFIG(TAG, " RX Pin: GPIO%d", *this->rx_pin_);
|
||||||
}
|
}
|
||||||
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
|
||||||
if (this->hw_serial_ != nullptr) {
|
if (this->hw_serial_ != nullptr) {
|
||||||
ESP_LOGCONFIG(TAG, " Using hardware serial interface.");
|
ESP_LOGCONFIG(TAG, " Using hardware serial interface.");
|
||||||
} else {
|
} else {
|
||||||
|
@ -231,7 +242,7 @@ void UARTComponent::flush() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate) {
|
void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits) {
|
||||||
this->bit_time_ = F_CPU / baud_rate;
|
this->bit_time_ = F_CPU / baud_rate;
|
||||||
if (tx_pin != -1) {
|
if (tx_pin != -1) {
|
||||||
auto pin = GPIOPin(tx_pin, OUTPUT);
|
auto pin = GPIOPin(tx_pin, OUTPUT);
|
||||||
|
@ -246,6 +257,7 @@ void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_ra
|
||||||
this->rx_buffer_ = new uint8_t[this->rx_buffer_size_];
|
this->rx_buffer_ = new uint8_t[this->rx_buffer_size_];
|
||||||
pin.attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, FALLING);
|
pin.attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, FALLING);
|
||||||
}
|
}
|
||||||
|
this->stop_bits_ = stop_bits;
|
||||||
}
|
}
|
||||||
void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) {
|
void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) {
|
||||||
uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500;
|
uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500;
|
||||||
|
@ -262,6 +274,8 @@ void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg
|
||||||
rec |= arg->read_bit_(&wait, start) << 7;
|
rec |= arg->read_bit_(&wait, start) << 7;
|
||||||
// Stop bit
|
// Stop bit
|
||||||
arg->wait_(&wait, start);
|
arg->wait_(&wait, start);
|
||||||
|
if (arg->stop_bits_ == 2)
|
||||||
|
arg->wait_(&wait, start);
|
||||||
|
|
||||||
arg->rx_buffer_[arg->rx_in_pos_] = rec;
|
arg->rx_buffer_[arg->rx_in_pos_] = rec;
|
||||||
arg->rx_in_pos_ = (arg->rx_in_pos_ + 1) % arg->rx_buffer_size_;
|
arg->rx_in_pos_ = (arg->rx_in_pos_ + 1) % arg->rx_buffer_size_;
|
||||||
|
@ -289,6 +303,8 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
|
||||||
this->write_bit_(data & (1 << 7), &wait, start);
|
this->write_bit_(data & (1 << 7), &wait, start);
|
||||||
// Stop bit
|
// Stop bit
|
||||||
this->write_bit_(true, &wait, start);
|
this->write_bit_(true, &wait, start);
|
||||||
|
if (this->stop_bits_ == 2)
|
||||||
|
this->wait_(&wait, start);
|
||||||
enable_interrupts();
|
enable_interrupts();
|
||||||
}
|
}
|
||||||
void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
|
void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
|
||||||
|
@ -344,5 +360,16 @@ int UARTComponent::peek() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits) {
|
||||||
|
if (this->parent_->baud_rate_ != baud_rate) {
|
||||||
|
ESP_LOGE(TAG, " Invalid baud_rate: Integration requested baud_rate %u but you have %u!", baud_rate,
|
||||||
|
this->parent_->baud_rate_);
|
||||||
|
}
|
||||||
|
if (this->parent_->stop_bits_ != stop_bits) {
|
||||||
|
ESP_LOGE(TAG, " Invalid stop bits: Integration requested stop_bits %u but you have %u!", stop_bits,
|
||||||
|
this->parent_->stop_bits_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace uart
|
} // namespace uart
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace uart {
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
class ESP8266SoftwareSerial {
|
class ESP8266SoftwareSerial {
|
||||||
public:
|
public:
|
||||||
void setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate);
|
void setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits);
|
||||||
|
|
||||||
uint8_t read_byte();
|
uint8_t read_byte();
|
||||||
uint8_t peek_byte();
|
uint8_t peek_byte();
|
||||||
|
@ -33,6 +33,7 @@ class ESP8266SoftwareSerial {
|
||||||
size_t rx_buffer_size_{512};
|
size_t rx_buffer_size_{512};
|
||||||
volatile size_t rx_in_pos_{0};
|
volatile size_t rx_in_pos_{0};
|
||||||
size_t rx_out_pos_{0};
|
size_t rx_out_pos_{0};
|
||||||
|
uint8_t stop_bits_;
|
||||||
ISRInternalGPIOPin *tx_pin_{nullptr};
|
ISRInternalGPIOPin *tx_pin_{nullptr};
|
||||||
ISRInternalGPIOPin *rx_pin_{nullptr};
|
ISRInternalGPIOPin *rx_pin_{nullptr};
|
||||||
};
|
};
|
||||||
|
@ -72,9 +73,11 @@ class UARTComponent : public Component, public Stream {
|
||||||
|
|
||||||
void set_tx_pin(uint8_t tx_pin) { this->tx_pin_ = tx_pin; }
|
void set_tx_pin(uint8_t tx_pin) { this->tx_pin_ = tx_pin; }
|
||||||
void set_rx_pin(uint8_t rx_pin) { this->rx_pin_ = rx_pin; }
|
void set_rx_pin(uint8_t rx_pin) { this->rx_pin_ = rx_pin; }
|
||||||
|
void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool check_read_timeout_(size_t len = 1);
|
bool check_read_timeout_(size_t len = 1);
|
||||||
|
friend class UARTDevice;
|
||||||
|
|
||||||
HardwareSerial *hw_serial_{nullptr};
|
HardwareSerial *hw_serial_{nullptr};
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
@ -83,6 +86,7 @@ class UARTComponent : public Component, public Stream {
|
||||||
optional<uint8_t> tx_pin_;
|
optional<uint8_t> tx_pin_;
|
||||||
optional<uint8_t> rx_pin_;
|
optional<uint8_t> rx_pin_;
|
||||||
uint32_t baud_rate_;
|
uint32_t baud_rate_;
|
||||||
|
uint8_t stop_bits_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
@ -100,6 +104,9 @@ class UARTDevice : public Stream {
|
||||||
|
|
||||||
void write_array(const uint8_t *data, size_t len) { this->parent_->write_array(data, len); }
|
void write_array(const uint8_t *data, size_t len) { this->parent_->write_array(data, len); }
|
||||||
void write_array(const std::vector<uint8_t> &data) { this->parent_->write_array(data); }
|
void write_array(const std::vector<uint8_t> &data) { this->parent_->write_array(data); }
|
||||||
|
template<size_t N> void write_array(const std::array<uint8_t, N> &data) {
|
||||||
|
this->parent_->write_array(data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
void write_str(const char *str) { this->parent_->write_str(str); }
|
void write_str(const char *str) { this->parent_->write_str(str); }
|
||||||
|
|
||||||
|
@ -107,6 +114,13 @@ class UARTDevice : public Stream {
|
||||||
bool peek_byte(uint8_t *data) { return this->parent_->peek_byte(data); }
|
bool peek_byte(uint8_t *data) { return this->parent_->peek_byte(data); }
|
||||||
|
|
||||||
bool read_array(uint8_t *data, size_t len) { return this->parent_->read_array(data, len); }
|
bool read_array(uint8_t *data, size_t len) { return this->parent_->read_array(data, len); }
|
||||||
|
template<size_t N> optional<std::array<uint8_t, N>> read_array() { // NOLINT
|
||||||
|
std::array<uint8_t, N> res;
|
||||||
|
if (!this->read_array(res.data(), N)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
int available() override { return this->parent_->available(); }
|
int available() override { return this->parent_->available(); }
|
||||||
|
|
||||||
|
@ -116,6 +130,9 @@ class UARTDevice : public Stream {
|
||||||
int read() override { return this->parent_->read(); }
|
int read() override { return this->parent_->read(); }
|
||||||
int peek() override { return this->parent_->peek(); }
|
int peek() override { return this->parent_->peek(); }
|
||||||
|
|
||||||
|
/// Check that the configuration of the UART bus matches the provided values and otherwise print a warning
|
||||||
|
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits = 1);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UARTComponent *parent_{nullptr};
|
UARTComponent *parent_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
|
@ -323,6 +323,7 @@ CONF_PM_2_5 = 'pm_2_5'
|
||||||
CONF_PORT = 'port'
|
CONF_PORT = 'port'
|
||||||
CONF_POSITION = 'position'
|
CONF_POSITION = 'position'
|
||||||
CONF_POWER = 'power'
|
CONF_POWER = 'power'
|
||||||
|
CONF_POWER_FACTOR = 'power_factor'
|
||||||
CONF_POWER_ON_VALUE = 'power_on_value'
|
CONF_POWER_ON_VALUE = 'power_on_value'
|
||||||
CONF_POWER_SAVE_MODE = 'power_save_mode'
|
CONF_POWER_SAVE_MODE = 'power_save_mode'
|
||||||
CONF_POWER_SUPPLY = 'power_supply'
|
CONF_POWER_SUPPLY = 'power_supply'
|
||||||
|
@ -479,6 +480,7 @@ ICON_BRIEFCASE_DOWNLOAD = 'mdi:briefcase-download'
|
||||||
ICON_BRIGHTNESS_5 = 'mdi:brightness-5'
|
ICON_BRIGHTNESS_5 = 'mdi:brightness-5'
|
||||||
ICON_CHEMICAL_WEAPON = 'mdi:chemical-weapon'
|
ICON_CHEMICAL_WEAPON = 'mdi:chemical-weapon'
|
||||||
ICON_CHECK_CIRCLE_OUTLINE = 'mdi:check-circle-outline'
|
ICON_CHECK_CIRCLE_OUTLINE = 'mdi:check-circle-outline'
|
||||||
|
ICON_CURRENT_AC = 'mdi:current-ac'
|
||||||
ICON_EMPTY = ''
|
ICON_EMPTY = ''
|
||||||
ICON_FLASH = 'mdi:flash'
|
ICON_FLASH = 'mdi:flash'
|
||||||
ICON_FLOWER = 'mdi:flower'
|
ICON_FLOWER = 'mdi:flower'
|
||||||
|
|
|
@ -291,6 +291,31 @@ sensor:
|
||||||
name: ADE7953 Active Power A
|
name: ADE7953 Active Power A
|
||||||
active_power_b:
|
active_power_b:
|
||||||
name: ADE7953 Active Power B
|
name: ADE7953 Active Power B
|
||||||
|
- platform: pzem004t
|
||||||
|
voltage:
|
||||||
|
name: "PZEM00T Voltage"
|
||||||
|
current:
|
||||||
|
name: "PZEM004T Current"
|
||||||
|
power:
|
||||||
|
name: "PZEM004T Power"
|
||||||
|
- platform: pzemac
|
||||||
|
voltage:
|
||||||
|
name: "PZEMAC Voltage"
|
||||||
|
current:
|
||||||
|
name: "PZEMAC Current"
|
||||||
|
power:
|
||||||
|
name: "PZEMAC Power"
|
||||||
|
frequency:
|
||||||
|
name: "PZEMAC Frequency"
|
||||||
|
power_factor:
|
||||||
|
name: "PZEMAC Power Factor"
|
||||||
|
- platform: pzemdc
|
||||||
|
voltage:
|
||||||
|
name: "PZEMDC Voltage"
|
||||||
|
current:
|
||||||
|
name: "PZEMDC Current"
|
||||||
|
power:
|
||||||
|
name: "PZEMDC Power"
|
||||||
|
|
||||||
time:
|
time:
|
||||||
- platform: homeassistant
|
- platform: homeassistant
|
||||||
|
|
Loading…
Reference in a new issue