Added ads131m08, etc. external component

This commit is contained in:
Chris Stuurman 2026-04-27 18:00:46 +02:00
parent da6e5961e9
commit d0f97b46a6
14 changed files with 4438 additions and 3270 deletions

6
.gitignore vendored
View File

@ -2,10 +2,4 @@
# This is an example and may include too much for your use-case.
# You can modify this file to suit your needs.
/.esphome/
.esphome/external_components
/secrets.yaml
/.esphome.bak/
/fonts/
**/__pycache__/
components/ads131m08/__pycache__/sensor.cpython-312.pyc
components/ads131m08/__pycache__/__init__.cpython-312.pyc

View File

@ -1,29 +1,80 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import spi
from esphome.const import (
CONF_ID
)
CONF_DRDY_PIN = "drdy_pin"
CONF_REFERENCE_VOLTAGE = "reference_voltage"
DEPENDENCIES = ["spi"]
ads131m08_ns = cg.esphome_ns.namespace("ads131m08")
ADS131M08Hub = ads131m08_ns.class_("ADS131M08Hub", cg.Component, spi.SPIDevice)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(ADS131M08Hub),
cv.Required(CONF_DRDY_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_REFERENCE_VOLTAGE, default=1.2): cv.float_range(min=1.1, max=1.3),
}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema(cs_pin_required=True))
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await spi.register_spi_device(var, config)
drdy = await cg.gpio_pin_expression(config[CONF_DRDY_PIN])
reference_voltage = config[CONF_REFERENCE_VOLTAGE]
cg.add(var.set_reference_voltage(reference_voltage))
cg.add(var.set_drdy_pin(drdy))
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import spi
from esphome.const import CONF_ID #, CONF_MODE
# Import ThreadModel enum
#from esphome.core import ThreadModel
MULTI_CONF = True
CONF_DRDY_PIN = "drdy_pin"
CONF_SYNC_RESET_PIN = "sync_reset_pin"
CONF_REFERENCE_VOLTAGE = "reference_voltage"
CONF_CLOCK_FREQUENCY = "clock_frequency"
CONF_ADS131M08_ID = "_ads131m08_id"
CONF_OSR = "oversampling_ratio"
DEPENDENCIES = ["spi"]
ads131m08_ns = cg.esphome_ns.namespace("ads131m08")
ADS131M08Hub = ads131m08_ns.class_("ADS131M08Hub", cg.Component, spi.SPIDevice)
OSR = ads131m08_ns.enum("ADS131M08_OVERSAMPLING_RATIO")
ALLOWED_OSRS = {
128: OSR.OSR_128,
256: OSR.OSR_256,
512: OSR.OSR_512,
1024: OSR.OSR_1024,
2048: OSR.OSR_2048,
4096: OSR.OSR_4096,
8192: OSR.OSR_8192,
16256: OSR.OSR_16256,
}
ALLOWED_CLOCK_FREQUENCIES = {
2048000: 2048000,
4096000: 4096000,
8192000: 8192000,
"2048kHz": 2048000,
"4096kHz": 4096000,
"8192kHz": 8192000,
"2.048MHz": 2048000,
"4.096MHz": 4096000,
"8.192MHz": 8192000,
}
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ADS131M08Hub),
cv.Required(CONF_DRDY_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_SYNC_RESET_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_CLOCK_FREQUENCY, default="8192kHz"): cv.All(cv.frequency, cv.one_of(*ALLOWED_CLOCK_FREQUENCIES.keys())),
cv.Optional(CONF_OSR, default=1024): cv.enum(ALLOWED_OSRS, int=True),
cv.Optional(CONF_REFERENCE_VOLTAGE, default=1.2): cv.float_range(min=1.1, max=1.3),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(spi.spi_device_schema(cs_pin_required=True, default_data_rate="10MHz"))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await spi.register_spi_device(var, config)
drdy = await cg.gpio_pin_expression(config[CONF_DRDY_PIN])
if CONF_SYNC_RESET_PIN in config:
sync_reset = await cg.gpio_pin_expression(config[CONF_SYNC_RESET_PIN])
cg.add(var.set_sync_reset_pin(sync_reset))
clock_frequency = ALLOWED_CLOCK_FREQUENCIES[config[CONF_CLOCK_FREQUENCY]]
cg.add(var.set_clock_frequency(clock_frequency))
osr = config[CONF_OSR]
cg.add(var.set_osr(osr))
reference_voltage = config[CONF_REFERENCE_VOLTAGE]
cg.add(var.set_reference_voltage(reference_voltage))
cg.add(var.set_drdy_pin(drdy))
#cg.add(var.set_thread_model(ThreadModel.MULTI_ATOMICS))

File diff suppressed because it is too large Load Diff

View File

@ -1,503 +1,229 @@
#pragma once
// using external voltage reference
#include "esphome/core/component.h"
#include "esphome/components/spi/spi.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace ads131m08 {
typedef union {
int32_t i;
float f;
uint16_t u[2];
uint8_t b[4];
} flex32_t;
/* Adc Structure. Ch can be read as int32 or float*/
struct AdcOutput
{
uint16_t status;
flex32_t ch[8];
};
enum ADS131M08_DRDY_STATE
{
DS_LOGIC_HIGH = 0, // DEFAULT
DS_HI_Z = 1
};
enum ADS131M08_POWERMODE
{
PM_VERY_LOW_POWER = 0,
PM_LOW_POWER = 1,
PM_HIGH_RESOLUTION = 2 // DEFAULT
};
enum ADS131M08_PGA_GAIN
{
PGA_1 = 0,
PGA_2 = 1,
PGA_4 = 2,
PGA_8 = 3,
PGA_16 = 4,
PGA_32 = 5,
PGA_64 = 6,
PGA_128 = 7,
PGA_INVALID
};
enum ADS131M08_INPUT_CHANNEL_MUX
{
ICM_AIN0P_AIN0N = 0, // DEFAULT
ICM_INPUT_SHORTED = 1,
ICM_POSITIVE_DC_TEST_SIGNAL = 2,
ICM_NEGATIVE_DC_TEST_SIGNAL = 3,
};
enum ADS131M08_OVERSAMPLING_RATIO
{
OSR_128 = 0,
OSR_256 = 1,
OSR_512 = 2,
OSR_1024 = 3, // default
OSR_2048 = 4,
OSR_4096 = 5,
OSR_8192 = 6,
OSR_16384 = 7
};
enum ADS131M08_WAIT_TIME
{
WT_128 = 856,
WT_256 = 1112,
WT_512 = 1624,
WT_1024 = 2648,
WT_2048 = 4696,
WT_4096 = 8792,
WT_8192 = 16984,
WT_16384 = 33368
};
// MODE Register
enum ADS131M08_RESET : uint16_t
{
MODE_NO_RESET = 0x0000, // DEFAULT
MODE_RESET_HAPPENED = 0x0400
};
enum ADS131M08_CRC_TYPE : uint16_t
{
CRC_CCITT_16BIT = 0x0000, // DEFAULT
CRC_ANSI_16BIT = 0x0800
};
enum ADS131M08_WORD_LENGTH : uint16_t
{
WLENGTH_16_BITS = 0x0000,
WLENGTH_24_BITS = 0x0100, // DEFAULT
WLENGTH_32_BITS_LSB_ZERO_PADDING = 0x0200,
WLENGTH_32_BITS_MSB_SIGN_EXTEND = 0x0300
};
enum ADS131M08_TIMEOUT : uint16_t
{
TIMEOUT_DISABLED = 0x0000,
TIMEOUT_ENABLED = 0x0010 // DEFAULT
};
enum ADS131M08_DRDY_SELECTION : uint16_t
{
DRDY_SEL_MOST_LAGGING = 0x0000, // DEFAULT
DRDY_SEL_LOGICAL_OR = 0x0004,
DRDY_SEL_MOST_LEADING_CHAN = 0x0008,
DRDY_SEL_MOST_LEADING_CHAN = 0x000C
};
enum ADS131M08_DRDY_FORMAT : uint16_t
{
DRDY_FMT_LEVEL = 0x0000, // Logic low (default)
DRDY_FMT_PULSE = 0x0001 // Low pulse with a fixed duration
};
// end of MODE Register
// GAIN1 Register
enum ADS131M08_GAIN1_CHANNEL_PGA : uint16_t {
PGAGAIN0_1 = 0x0000, // default
PGAGAIN0_2 = 0x0001,
PGAGAIN0_4 = 0x0002,
PGAGAIN0_8 = 0x0003,
PGAGAIN0_16 = 0x0004,
PGAGAIN0_32 = 0x0005,
PGAGAIN0_64 = 0x0006,
PGAGAIN0_128 = 0x0007,
PGAGAIN1_1 = 0x0000, // default
PGAGAIN1_2 = 0x0010,
PGAGAIN1_4 = 0x0020,
PGAGAIN1_8 = 0x0030,
PGAGAIN1_16 = 0x0040,
PGAGAIN1_32 = 0x0050,
PGAGAIN1_64 = 0x0060,
PGAGAIN1_128 = 0x0070,
PGAGAIN2_1 = 0x0000, // default
PGAGAIN2_2 = 0x0100,
PGAGAIN2_4 = 0x0200,
PGAGAIN2_8 = 0x0300,
PGAGAIN2_16 = 0x0400,
PGAGAIN2_32 = 0x0500,
PGAGAIN2_64 = 0x0600,
PGAGAIN2_128 = 0x0700,
PGAGAIN3_1 = 0x0000, // default
PGAGAIN3_2 = 0x1000,
PGAGAIN3_4 = 0x2000,
PGAGAIN3_8 = 0x3000,
PGAGAIN3_16 = 0x4000,
PGAGAIN3_32 = 0x5000, // Set PGA gain to 32 for channel
PGAGAIN3_64 = 0x6000,
PGAGAIN3_128 = 0x7000
};
// end of GAIN1 Register
// GAIN2 Register
enum ADS131M08_GAIN2_CHANNEL_PGA : uint16_t {
PGAGAIN4_1 = 0x0000, // default
PGAGAIN4_2 = 0x0001,
PGAGAIN4_4 = 0x0002,
PGAGAIN4_8 = 0x0003,
PGAGAIN4_16 = 0x0004,
PGAGAIN4_32 = 0x0005,
PGAGAIN4_64 = 0x0006,
PGAGAIN4_128 = 0x0007,
PGAGAIN5_1 = 0x0000, // default
PGAGAIN5_2 = 0x0010,
PGAGAIN5_4 = 0x0020,
PGAGAIN5_8 = 0x0030,
PGAGAIN5_16 = 0x0040,
PGAGAIN5_32 = 0x0050,
PGAGAIN5_64 = 0x0060,
PGAGAIN5_128 = 0x0070,
PGAGAIN6_1 = 0x0000, // default
PGAGAIN6_2 = 0x0100,
PGAGAIN6_4 = 0x0200,
PGAGAIN6_8 = 0x0300,
PGAGAIN6_16 = 0x0400,
PGAGAIN6_32 = 0x0500,
PGAGAIN6_64 = 0x0600,
PGAGAIN6_128 = 0x0700,
PGAGAIN7_1 = 0x0000, // default
PGAGAIN7_2 = 0x1000,
PGAGAIN7_4 = 0x2000,
PGAGAIN7_8 = 0x3000,
PGAGAIN7_16 = 0x4000,
PGAGAIN7_32 = 0x5000,
PGAGAIN7_64 = 0x6000,
PGAGAIN7_128 = 0x7000
};
// end of GAIN2 Register
// Commands
enum ADS131M08_COMMANDS : uint16_t {
CMD_NULL = 0x0000, // No operation; used to read STATUS register
CMD_RESET = 0x0011, // RESET the device
CMD_STANDBY = 0x0022, // Place the device into standby mode
CMD_WAKEUP = 0x0033, // Wake up device from standby mode to conversion mode
CMD_LOCK = 0x0555, // Lock the interface such that only the NULL, UNLOCK, and RREG commands are valid
CMD_UNLOCK = 0x0655, // Unlock the interface after the interface is locked
CMD_RREG = 0xA000, // Read register command base; number of registers to read added to lower byte; register address to upper byte
CMD_WREG = 0x6000, // Write register command base; number of registers to write added to lower byte; register address to upper byte
};
// Responses
enum ADS131M08_RESPONSES : uint16_t {
RSP_RESET_OK = 0xFF28,
RSP_RESET_NOK = 0x0011
};
enum ADS131M08_REG {
REG_ID = 0x00,
REG_STATUS = 0x01,
REG_MODE = 0x02,
REG_CLOCK = 0x03,
REG_GAIN1 = 0x04,
REG_GAIN2 = 0x05,
REG_CFG = 0x06,
REG_THRSHLD_MSB = 0x07,
REG_THRSHLD_LSB = 0x08,
REG_CH0_CFG = 0x09,
REG_CH0_OCAL_MSB = 0x0A,
REG_CH0_OCAL_LSB = 0x0B,
REG_CH0_GCAL_MSB = 0x0C,
REG_CH0_GCAL_LSB = 0x0D,
REG_CH1_CFG = 0x0E,
REG_CH1_OCAL_MSB = 0x0F,
REG_CH1_OCAL_LSB = 0x10,
REG_CH1_GCAL_MSB = 0x11,
REG_CH1_GCAL_LSB = 0x12,
REG_CH2_CFG = 0x13,
REG_CH2_OCAL_MSB= 0x14,
REG_CH2_OCAL_LSB= 0x15,
REG_CH2_GCAL_MSB= 0x16,
REG_CH2_GCAL_LSB= 0x17,
REG_CH3_CFG = 0x18,
REG_CH3_OCAL_MSB= 0x19,
REG_CH3_OCAL_LSB= 0x1A,
REG_CH3_GCAL_MSB= 0x1B,
REG_CH3_GCAL_LSB= 0x1C,
REG_CH4_CFG = 0x1D,
REG_CH4_OCAL_MSB= 0x1E,
REG_CH4_OCAL_LSB= 0x1F,
REG_CH4_GCAL_MSB= 0x20,
REG_CH4_GCAL_LSB= 0x21,
REG_CH5_CFG = 0x22,
REG_CH5_OCAL_MSB= 0x23,
REG_CH5_OCAL_LSB= 0x24,
REG_CH5_GCAL_MSB= 0x25,
REG_CH5_GCAL_LSB= 0x26,
REG_CH6_CFG = 0x27,
REG_CH6_OCAL_MSB= 0x28,
REG_CH6_OCAL_LSB= 0x29,
REG_CH6_GCAL_MSB= 0x2A,
REG_CH6_GCAL_LSB= 0x2B,
REG_CH7_CFG = 0x2C,
REG_CH7_OCAL_MSB= 0x2D,
REG_CH7_OCAL_LSB= 0x2E,
REG_CH7_GCAL_MSB= 0x2F,
REG_CH7_GCAL_LSB= 0x30,
REGMAP_CRC = 0x3E,
};
// Mask READ_REG
static constexpr uint16_t MASK_CMD_READ_REG_ADDRESS = 0x1F80;
static constexpr uint16_t MASK_CMD_READ_REG_BYTES = 0x007F;
// Mask Register STATUS
static constexpr uint16_t MASK_STATUS_LOCK = 0x8000;
static constexpr uint16_t MASK_STATUS_RESYNC = 0x4000;
static constexpr uint16_t MASK_STATUS_REGMAP = 0x2000;
static constexpr uint16_t MASK_STATUS_CRC_ERR = 0x1000;
static constexpr uint16_t MASK_STATUS_CRC_TYPE = 0x0800;
static constexpr uint16_t MASK_STATUS_RESET = 0x0400;
static constexpr uint16_t MASK_STATUS_WLENGTH = 0x0300;
static constexpr uint16_t MASK_STATUS_DRDY7 = 0x0080;
static constexpr uint16_t MASK_STATUS_DRDY6 = 0x0040;
static constexpr uint16_t MASK_STATUS_DRDY5 = 0x0020;
static constexpr uint16_t MASK_STATUS_DRDY4 = 0x0010;
static constexpr uint16_t MASK_STATUS_DRDY3 = 0x0008;
static constexpr uint16_t MASK_STATUS_DRDY2 = 0x0004;
static constexpr uint16_t MASK_STATUS_DRDY1 = 0x0002;
static constexpr uint16_t MASK_STATUS_DRDY0 = 0x0001;
// Mask Register MODE
static constexpr uint16_t MASK_MODE_REG_CRC_EN = 0x2000;
static constexpr uint16_t MASK_MODE_RX_CRC_EN = 0x1000;
static constexpr uint16_t MASK_MODE_CRC_TYPE = 0x0800;
static constexpr uint16_t MASK_MODE_RESET = 0x0400;
static constexpr uint16_t MASK_MODE_WLENGTH = 0x0300;
static constexpr uint16_t MASK_MODE_TIMEOUT = 0x0010;
static constexpr uint16_t MASK_MODE_DRDY_SEL = 0x000C;
static constexpr uint16_t MASK_MODE_DRDY_HiZ = 0x0002;
static constexpr uint16_t MASK_MODE_DRDY_FMT = 0x0001;
// Mask Register CLOCK
static constexpr uint16_t MASK_CLOCK_CH7_EN = 0x8000;
static constexpr uint16_t MASK_CLOCK_CH6_EN = 0x4000;
static constexpr uint16_t MASK_CLOCK_CH5_EN = 0x2000;
static constexpr uint16_t MASK_CLOCK_CH4_EN = 0x1000;
static constexpr uint16_t MASK_CLOCK_CH3_EN = 0x0800;
static constexpr uint16_t MASK_CLOCK_CH2_EN = 0x0400;
static constexpr uint16_t MASK_CLOCK_CH1_EN = 0x0200;
static constexpr uint16_t MASK_CLOCK_CH0_EN = 0x0100;
static constexpr uint16_t MASK_CLOCK_OSR = 0x001C;
static constexpr uint16_t MASK_CLOCK_PWR = 0x0003;
static constexpr uint32_t MASK_CLOCK_ALL_CH_DISABLE = 0x0000;
static constexpr uint32_t MASK_CLOCK_ALL_CH_ENABLE = 0xFF00;
// Mask Register GAIN
static constexpr uint16_t MASK_GAIN_PGAGAIN7 = 0x7000;
static constexpr uint16_t MASK_GAIN_PGAGAIN6 = 0x0700;
static constexpr uint16_t MASK_GAIN_PGAGAIN5 = 0x0070;
static constexpr uint16_t MASK_GAIN_PGAGAIN4 = 0x0007;
static constexpr uint16_t MASK_GAIN_PGAGAIN3 = 0x7000;
static constexpr uint16_t MASK_GAIN_PGAGAIN2 = 0x0700;
static constexpr uint16_t MASK_GAIN_PGAGAIN1 = 0x0070;
static constexpr uint16_t MASK_GAIN_PGAGAIN0 = 0x0007;
// Mask Register CFG
static constexpr uint16_t MASK_CFG_GC_DLY = 0x1E00;
static constexpr uint16_t MASK_CFG_GC_EN = 0x0100;
static constexpr uint16_t MASK_CFG_CD_ALLCH = 0x0080;
static constexpr uint16_t MASK_CFG_CD_NUM = 0x0070;
static constexpr uint16_t MASK_CFG_CD_LEN = 0x000E;
static constexpr uint16_t MASK_CFG_CD_EN = 0x0001;
// Mask Register THRSHLD_MSB - dummy, for completeness
static constexpr uint16_t MASK_THRSHLD_MSB_CD_TH_MSB = 0xFFFF;
// Mask Register THRSHLD_LSB
static constexpr uint16_t MASK_THRSHLD_LSB_CD_TH_LSB = 0xFF00;
static constexpr uint16_t MASK_THRSHLD_LSB_DCBLOCK = 0x000F;
// Mask Register CHX_CFG
static constexpr uint16_t MASK_CHX_CFG_PHASE = 0xFFC0;
static constexpr uint16_t MASK_CHX_CFG_DCBLKX_DIS = 0x0004;
static constexpr uint16_t MASK_CHX_CFG_MUX = 0x0003;
// Mask Register CHX_OCAL_MSB - dummy, for completeness
static constexpr uint16_t MASK_CHX_OCAL_MSB = 0xFFFF;
// Mask Register CHX_OCAL_LSB
static constexpr uint16_t MASK_CHX_OCAL_LSB = 0xFF00;
// Mask Register CHX_GCAL_MSB - dummy, for completeness
static constexpr uint16_t MASK_CHX_GCAL_MSB = 0xFFFF;
// Mask Register CHX_GCAL_LSB
static constexpr uint16_t MASK_CHX_GCAL_LSB = 0xFF00;
// --------------------------------------------------------------------
// Conversion modes
static constexpr uint16_t CONVERSION_MODE_CONT = 0;
static constexpr uint16_t CONVERSION_MODE_SINGLE = 1;
// Data Format
static constexpr uint16_t DATA_FORMAT_TWO_COMPLEMENT = 0;
static constexpr uint16_t DATA_FORMAT_BINARY = 1;
// Measure Mode
static constexpr uint8_t MEASURE_UNIPOLAR = 1;
static constexpr uint8_t MEASURE_BIPOLAR = 0;
// Clock Type
static constexpr uint8_t CLOCK_EXTERNAL = 1;
static constexpr uint8_t CLOCK_INTERNAL = 0;
// PGA Gain
// static constexpr uint16_t PGA_GAIN_1 = 0;
// static constexpr uint16_t PGA_GAIN_2 = 1;
// static constexpr uint16_t PGA_GAIN_4 = 2;
// static constexpr uint16_t PGA_GAIN_8 = 3;
// static constexpr uint16_t PGA_GAIN_16 = 4;
// static constexpr uint16_t PGA_GAIN_32 = 5;
// static constexpr uint16_t PGA_GAIN_64 = 6;
// static constexpr uint16_t PGA_GAIN_128 = 7;
// Input Filter
static constexpr uint16_t FILTER_SYNC = 0;
static constexpr uint16_t FILTER_FIR = 2;
static constexpr uint16_t FILTER_FIR_IIR = 3;
// Data Mode
static constexpr uint8_t DATA_MODE_24BITS = 0;
static constexpr uint8_t DATA_MODE_32BITS = 1;
// Data Rate
static constexpr uint8_t DATA_RATE_0 = 0;
static constexpr uint8_t DATA_RATE_1 = 1;
static constexpr uint8_t DATA_RATE_2 = 2;
static constexpr uint8_t DATA_RATE_3 = 3;
static constexpr uint8_t DATA_RATE_4 = 4;
static constexpr uint8_t DATA_RATE_5 = 5;
static constexpr uint8_t DATA_RATE_6 = 6;
static constexpr uint8_t DATA_RATE_7 = 7;
static constexpr uint8_t DATA_RATE_8 = 8;
static constexpr uint8_t DATA_RATE_9 = 9;
static constexpr uint8_t DATA_RATE_10 = 10;
static constexpr uint8_t DATA_RATE_11 = 11;
static constexpr uint8_t DATA_RATE_12 = 12;
static constexpr uint8_t DATA_RATE_13 = 13;
static constexpr uint8_t DATA_RATE_14 = 14;
static constexpr uint8_t DATA_RATE_15 = 15;
// Sync Mpdes
static constexpr uint16_t SYNC_CONTINUOUS = 1;
static constexpr uint16_t SYNC_PULSE = 0;
// DIO Config Mode
static constexpr uint8_t DIO_OUTPUT = 1;
static constexpr uint8_t DIO_INPUT = 0;
static constexpr uint8_t SPI_MASTER_DUMMY = 0xFF;
static constexpr uint16_t SPI_MASTER_DUMMY16 = 0xFFFF;
static constexpr uint32_t SPI_MASTER_DUMMY32 = 0xFFFFFFFF;
// end of from datasheet
class ADS131M08Sensor : public sensor::Sensor, public Component
{
};
class ADS131M08Hub : public Component, public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_8MHZ>
{
public:
// from datasheet pg. 93:
const int numFrameWords = 10; // Number of words in a full ADS131M08 SPI frame
unsigned long spiDummyWord[10 /*numFrameWords*/] = {
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000
}; // Dummy word frame to write ADC during ADC data reads
bool firstRead = true; // Flag to tell us if we are reading ADC data for the first time
signed long adcData; // Location where DMA will store ADC data in memory, length defined elsewhere
void txf_init();
bool adcRegisterWrite(unsigned short addrMask, unsigned short data, unsigned char adcWordLength);
void initialize_ads131m08_datasheet();
// end of from datasheet
bool setChannelPGA(uint8_t channel, uint8_t pga);
void setup() override;
void loop() override;
void set_drdy_pin(InternalGPIOPin *pin) { drdy_pin_ = pin; }
void register_sensor(int channel, ADS131M08Sensor *s) { sensors_[channel] = s; }
void set_reference_voltage(float reference_voltage) { this->reference_voltage_ = reference_voltage; }
void dump_config() override;
// from tpcorrea
void begin(uint8_t clk_pin, uint8_t miso_pin, uint8_t mosi_pin, uint8_t cs_pin, uint8_t drdy_pin, uint8_t reset_pin);
int8_t isDataReadySoft(uint8_t channel);
bool isDataReady(void);
bool isResetStatus(void);
bool isLockSPI(void);
bool setDrdyFormat(uint8_t drdyFormat);
bool setDrdyStateWhenUnavailable(uint8_t drdyState);
bool setPowerMode(uint8_t powerMode);
bool setChannelEnable(uint8_t channel, uint16_t enable);
bool setChannelPGA(uint8_t channel, ADS131M08_PGA_GAIN pga);
ADS131M08_PGA_GAIN getChannelPGA(uint8_t channel);
void setGlobalChop(uint16_t global_chop);
void setGlobalChopDelay(uint16_t delay);
bool setInputChannelSelection(uint8_t channel, uint8_t input);
bool setChannelOffsetCalibration(uint8_t channel, int32_t offset);
bool setChannelGainCalibration(uint8_t channel, uint32_t gain);
bool setOsr(uint16_t osr);
void setFullScale(uint8_t channel, float scale);
float getFullScale(uint8_t channel);
void reset();
uint16_t getId();
uint16_t getModeReg();
uint16_t getClockReg();
uint16_t getCfgReg();
AdcOutput readAdcRaw(void);
AdcOutput readAdcFloat(void);
protected:
float reference_voltage_;
InternalGPIOPin *drdy_pin_;
ADS131M08Sensor *sensors_[8] = {nullptr};
volatile bool data_ready_{false};
static void isr(ADS131M08Hub *arg);
void read_data_();
void write_register(uint8_t reg, uint16_t value);
};
} // namespace ads131m08
} // namespace esphome
#pragma once
// using external voltage reference
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/optional.h"
#include "ads131m08_defs.h"
#include "frame.h"
#include <string>
#include <charconv>
#include <format>
//#include "esphome/core/component.h"
//#include <esphome/core/hal.h>
#include "esphome/components/spi/spi.h"
#include "esphome/components/sensor/sensor.h"
//
//#include <vector>
//#include <esphome/core/gpio.h>
//#include <freertos/FreeRTOS.h>
//#include <freertos/semphr.h>
//#include <atomic>
namespace esphome {
namespace ads131m08 {
static const int MAX_CHANNELS = 11; // for debugging
static const int ADC_CHANNELS = 8;
static const int SAMPLE_TIME_SENSOR = 8; // for debugging
static const int MAX_SAMPLES_SENSOR = 9; // for debugging
static const int CRC_ERRORS_SENSOR = 10; // for debugging
typedef union {
int32_t i;
float f;
uint16_t u[2];
uint8_t b[4];
} flex32_t;
/* Adc Structure. Ch can be read as int32 or float*/
struct AdcOutput
{
uint16_t status;
flex32_t ch[ADC_CHANNELS];
};
//#define SET_IRAM IRAM_ATTR
#define SET_IRAM
typedef std::vector<uint8_t> spiframe;
typedef uint_str<uint16_t>::Ty_string uint16_str; // we want to use the stack to pass small arrays
class ADS131M08Hub : public Component, public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_8MHZ>
{
friend class ads131m08_select;
public:
enum crc_pos: uint8_t {
crc_after_frame,
crc_after_firstword,
};
// register config values used
static constexpr uint16_t reg_clock_cfg = MASK_CLOCK_EXTREF_EN | OSR_1024 | PM_HIGH_RESOLUTION;
static constexpr uint16_t reg_clock_allch_on = MASK_CLOCK_ALLCH | reg_clock_cfg;
static constexpr uint16_t reg_clock_allch_off = ~MASK_CLOCK_ALLCH & reg_clock_cfg;
static constexpr uint16_t reg_mode_cfg16 = WLENGTH_16_BITS | DRDY_FMT_PULSE | TIMEOUT_ENABLED;
static constexpr uint16_t reg_mode_cfg24 = WLENGTH_24_BITS | DRDY_FMT_PULSE | TIMEOUT_ENABLED;
static constexpr uint16_t reg_mode_cfg32 = WLENGTH_32_BITS_MSB_SIGN_EXTEND | DRDY_FMT_PULSE | TIMEOUT_ENABLED;
// masks for the above
static constexpr uint16_t reg_clock_cfg_mask = MASK_CLOCK_EXTREF_EN | MASK_CLOCK_OSR | MASK_CLOCK_PWR;
static constexpr uint16_t reg_clock_allch_mask = MASK_CLOCK_ALLCH_OFF | reg_clock_cfg_mask;
static constexpr uint16_t reg_mode_cfg_mask = MASK_MODE_WLENGTH | MASK_MODE_DRDY_FMT | MASK_MODE_TIMEOUT;
// Semaphore handles
SemaphoreHandle_t data_ready_semhandle = NULL;
//SemaphoreHandle_t spi_mutex = NULL;
// from datasheet pg. 93:
const int numFrameWords = 10; // Number of words in a full ADS131M08 SPI frame
spiframe base_frame;
bool firstRead = true; // Flag to tell us if we are reading ADC data for the first time
signed long adcData; // Location where DMA will store ADC data in memory, length defined elsewhere
ADS131M08Hub();
void txf_init();
bool adc_initialize(uint8_t word_length);
bool adc_set_word_length(uint8_t word_length);
// end of from datasheet
void set_channel_gain(uint8_t channel, ADS131M08_PGA_GAIN gain);
//virtual void spi_setup() override;
void setup() override;
void loop() override;
void set_drdy_pin(InternalGPIOPin *pin) { drdy_pin_ = pin; }
void set_sync_reset_pin(InternalGPIOPin *pin) { sync_reset_pin_ = pin; }
void set_clock_frequency(float clock_frequency) { this->clock_frequency_ = clock_frequency; }
void register_sensor_ac(int channel, sensor::Sensor *s) { sensors_ac[channel] = s; }
void register_sensor_dc(int channel, sensor::Sensor *s) { sensors_dc[channel] = s; }
void set_reference_voltage(float reference_voltage) { this->reference_voltage_ = reference_voltage; }
void set_osr(uint16_t osr) { this->osr_ = osr; }
void dump_config() override;
// from tpcorrea
ADS131M08_PGA_GAIN pgaGain[ADC_CHANNELS] = {PGA_1}; // Store the current PGA gain settings for each channel
int8_t is_data_ready_soft(uint8_t channel);
bool is_data_ready();
bool is_reset_status();
bool is_lock_spi();
bool set_drdy_format(uint8_t drdy_format);
bool set_drdy_idle_state(uint8_t drdy_state);
bool set_power_mode(uint8_t power_mode);
bool set_channel_enable(uint8_t channel, bool enable);
bool set_channel_pga(uint8_t channel, ADS131M08_PGA_GAIN pga);
ADS131M08_PGA_GAIN get_channel_pga(uint8_t channel);
void set_global_chop(uint16_t global_chop);
void set_global_chop_delay(uint16_t delay);
bool set_channel_input_selection(uint8_t channel, ADS131M08_INPUT_CHANNEL_MUX input);
bool set_channel_offset_calibration(uint8_t channel, int32_t offset);
bool set_channel_gain_calibration(uint8_t channel, uint32_t gain);
bool set_channel_phase_calibration(uint8_t channel, int16_t phase_offset);
bool set_dcblock_filter_disable(uint8_t channel, bool disable);
void set_measure_rms(uint8_t channel, bool enable);
float get_sampled_value(uint8_t channel) { return sampled_values_[channel]; }
bool set_reg_osr();
void set_full_scale(uint8_t channel, float scale);
float get_full_scale(uint8_t channel);
uint16_t get_id();
uint16_t get_mode_reg();
uint16_t get_clock_reg();
uint16_t get_cfg_reg();
AdcOutput read_adc_raw();
AdcOutput read_adc_float();
protected:
static void IRAM_ATTR isr_handler(ADS131M08Hub *arg);
//volatile bool data_ready_{false};
//static void isr(ADS131M08Hub *arg);
float reference_voltage_;
float clock_frequency_;
InternalGPIOPin *drdy_pin_;
InternalGPIOPin *sync_reset_pin_= {nullptr};
sensor::Sensor *sensors_ac[MAX_CHANNELS] = {nullptr};
sensor::Sensor *sensors_dc[MAX_CHANNELS] = {nullptr};
float sampled_values_[ADC_CHANNELS];
bool rms_enabled_[ADC_CHANNELS];
bool rms_calc_req_{false};
uint16_t osr_{3};
uint8_t update_adc_word_length();
void SET_IRAM read_single();
std::pair<uint32_t, uint32_t> SET_IRAM read_multi();
bool adc_lock(bool enable);
bool adc_register_write(uint16_t address, uint16_t data);
bool adc_register_write(uint16_t address, const uint16_str& data);
uint16_str adc_register_read(uint8_t address, uint8_t nregs);
bool adc_register_write_masked(uint8_t address, uint16_t value, uint16_t mask, int line);
bool adc_register_write_masked(uint8_t start_address, const uint16_str& values, const uint16_str& masks, int line);
uint16_t readRegister(uint8_t address);
bool adc_reset_retry();
bool adc_soft_reset();
void adc_hard_reset();
void adc_sync();
void write_byte(uint8_t byte);
bool write(uint32_t data, uint8_t adcWordLength);
uint8_t read_byte();
void write_array(const spiframe& data);
void SET_IRAM read_array(spiframe& buffer);
void transfer_array(const spiframe& data_out, spiframe& data_in);
uint16_t SET_IRAM get_crc(const spiframe& frame);
bool SET_IRAM check_crc(const spiframe& frame_with_crc);
size_t SET_IRAM add_crc(spiframe& frame, crc_pos crcpos);
uint16_t SET_IRAM read_frame_crc(const spiframe& frame);
uint16_t SET_IRAM crc(uint16_t crc_register, uint8_t data);
bool SET_IRAM set_frame_word(spiframe& frame, int w_index, uint16_t data); // write 16 bit data to first two bytes of word
bool SET_IRAM set_frame_word(spiframe& frame, int w_index, uint32_t data);
uint32_t SET_IRAM get_unsigned_frame_word(const spiframe& frame, int w_index, bool force_16bits = false);
int32_t SET_IRAM get_sign_ext_frame_word(const spiframe& frame, int w_index);
std::atomic<int> adc_init_{0};
std::atomic<int> cs_ctr_{0}; // Counter to track nested CS enable/disable calls for proper handling of multiple transfers in a row
std::atomic<uint16_t> isr_ctr_{0};
void enable(const char *txt);
void disable(const char *txt);
struct chipselect {
ADS131M08Hub* parent_{nullptr};
const char *txt_;
chipselect(ADS131M08Hub *parent, const char *txt = nullptr) {
parent_ = parent;
if(parent_ != nullptr)
txt_ = txt;
parent_->enable(txt_);
}
~chipselect() {
if(parent_ != nullptr) {
parent_->disable(txt_);
parent_ = nullptr;
}
}
};
private:
uint8_t adc_word_length_{24};
float conversion_factor_{1.2/8388608.0};
const uint32_t sample_time_ = 40000; // 250 ms
float sps_{0.0f};
uint32_t num_samples_[ADC_CHANNELS];
int64_t sample_sum_[ADC_CHANNELS];
float sample_squared_sum_[ADC_CHANNELS];
// for debug only
std::string frame_to_string(const spiframe& frame);
std::string conversion_frame_to_string(const spiframe& frame);
std::string command_to_string(uint16_t cmdadr);
void print_command_response_to_string(uint16_t cmdadr_sent, const spiframe& frame);
std::string rwreg_command_frame_to_string(const spiframe& frame);
std::string status_to_string(uint16_t response);
std::string reg_data_to_string(int address, uint16_t data, bool nameonly = false);
std::string reg_addr_to_string(int address);
std::string reg_config_to_string(int address, uint16_t data, bool nameonly = false);
//temp
int first_read_data {0};
};
#ifndef CHIP_SELECT
# if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
# define CHIP_SELECT volatile chipselect cs(this, __FUNCTION__);
# else
# define CHIP_SELECT volatile chipselect cs(this);
# endif
# define CHIP_SELECTx volatile chipselect cs(this);
#endif
} // namespace ads131m08
} // namespace esphome

View File

@ -23,7 +23,7 @@ void DS3231Component::dump_config() {
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
// ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
if (!this->read_bytes(0, this->ds3231_.raw, sizeof(this->ds3231_.raw))) {
ESP_LOGE(TAG, "Can't read I2C data.");
}
@ -50,7 +50,8 @@ void DS3231Component::read_time() {
.year = uint16_t(ds3231_.reg.year + 10u * ds3231_.reg.year_10 + 2000),
.is_dst = false, // not used
.timestamp = 0 // overwritten by recalc_timestamp_utc(false)
};
};
rtc_time.recalc_timestamp_utc(false);
if (!rtc_time.is_valid()) {
ESP_LOGE(TAG, "Invalid RTC time, not syncing to system clock.");
@ -79,7 +80,7 @@ void DS3231Component::write_time() {
ds3231_.reg.second = now.second % 10;
ds3231_.reg.second_10 = now.second / 10;
ds3231_.reg.ch = false;
// ESPHOME emits warning about synchronous mode; must check datasheet to see if this is a problem for us. Datasheet pg. 6 says "The DS3231 is designed to operate in a synchronous mode, where the timekeeping registers are updated at the end of each second. This ensures that the timekeeping registers always contain valid data and eliminates the need for software to handle potential timing issues when reading or writing the registers."
this->write_rtc_();
}

View File

@ -77,30 +77,30 @@ void TLC59208FOutput::setup() {
auto errcode = this->bus_->write_readv(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ, sizeof TLC59208F_SWRST_SEQ, nullptr, 0);
if (errcode != i2c::ERROR_OK) {
ESP_LOGE(TAG, "RESET failed");
this->mark_failed("RESET failed");
this->mark_failed(LOG_STR("RESET failed"));
return;
}
// Auto increment registers, and respond to all-call address
if (!this->write_byte(TLC59208F_REG_MODE1, TLC59208F_MODE1_AI2 | TLC59208F_MODE1_ALLCALL)) {
ESP_LOGE(TAG, "MODE1 failed");
this->mark_failed("MODE1 failed");
this->mark_failed(LOG_STR("MODE1 failed"));
return;
}
if (!this->write_byte(TLC59208F_REG_MODE2, this->mode_)) {
ESP_LOGE(TAG, "MODE2 failed");
this->mark_failed("MODE2 failed");
this->mark_failed(LOG_STR("MODE2 failed"));
return;
}
// Set all 3 outputs to be individually controlled
// TODO: think of a way to support group dimming
if (!this->write_byte(TLC59208F_REG_LEDOUT0, (LDR_PWM << 6) | (LDR_PWM << 4) | (LDR_PWM << 2) | (LDR_PWM << 0))) {
ESP_LOGE(TAG, "LEDOUT0 failed");
this->mark_failed("LEDOUT0 failed");
this->mark_failed(LOG_STR("LEDOUT0 failed"));
return;
}
if (!this->write_byte(TLC59208F_REG_LEDOUT1, (LDR_PWM << 6) | (LDR_PWM << 4) | (LDR_PWM << 2) | (LDR_PWM << 0))) {
ESP_LOGE(TAG, "LEDOUT1 failed");
this->mark_failed("LEDOUT1 failed");
this->mark_failed(LOG_STR("LEDOUT1 failed"));
return;
}
delayMicroseconds(500);

View File

@ -2,6 +2,7 @@
#define __SOLAR_CB_FRAME
#include "esphome.h"
#include <utility>
#include <queue>
#include "common.h"
using namespace esphome;

View File

@ -12,8 +12,8 @@ namespace solar
#define SEARCHWILD 0x0100000 // do wildcard comparison (only searchkey should contain wildcards)
#define num_compare(a, b) ((a) > (b)) ? +1 : ((a) < (b)) ? -1 : 0
#define bool_compare(a, b) ((!(a) && !(b)) || ((a) && (b))) ? 0 : (a) ? +1 : -1
#define max(a, b) ((a) > (b)) ? (a) : (b)
#define min(a, b) ((a) < (b)) ? (a) : (b)
#define mmax(a, b) ((a) > (b)) ? (a) : (b)
#define mmin(a, b) ((a) < (b)) ? (a) : (b)
typedef unsigned int uint;
typedef std::vector<uint8_t> byte_vector;

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
packages:
- !include common/wifi.yaml
- !include common/felicityinverter.yaml
- !include common/canbus.yaml
- !include common/modbus.yaml
substitutions:
name: sthome-ut2
@ -9,13 +11,38 @@ substitutions:
esphome:
name: "${name}"
friendly_name: "${friendly_name}"
includes:
- source # copies folder with files to relevant to be included in esphome compile
- <source/solar/cb_frame.h> # angle brackets ensure file is included above globals in main.cpp. Make sure to use include GUARDS in the file to prevent double inclusion
- <source/solar/cbf_store.h>
- <source/solar/cbf_pylon.h>
- <source/solar/cbf_store_pylon.h>
- <source/solar/cbf_sthome.h>
- <source/solar/cbf_store_sthome.h>
- <source/solar/cbf_cache.h>
external_components:
- source: github://pr#8103
components: [uart]
- source: github://pr#8032
components: [modbus, modbus_controller, growatt_solar]
refresh: 1h
#external_components:
# - source: github://pr#8103
# components: [uart]
# - source: github://pr#8032
# components: [modbus, modbus_controller, growatt_solar]
# refresh: 1h
globals:
- id: can_msgctr
type: int
restore_value: no
- id: can2_msgctr
type: int
restore_value: no
- id: last_battery_message_time
type: time_t
restore_value: no
- id: g_cb_cache # the cache is used to only accept a frame after it has been received a specified number of times within a specified period. this hopefully will iron out spurious corrupted frames
type: solar::cbf_cache
restore_value: no
- id: g_cb_request_queue
type: std::queue< std::set<uint32_t> >
restore_value: no
esp32:
board: esp32dev
@ -27,9 +54,10 @@ logger:
level: VERY_VERBOSE
initial_level: INFO
logs:
uart: VERY_VERBOSE
modbus: DEBUG
modbus_controller: DEBUG
canbus: INFO
uart_debug: VERY_VERBOSE
modbus: VERBOSE
modbus_controller: VERBOSE
# Enable Home Assistant API
api:
@ -51,18 +79,30 @@ wifi:
captive_portal:
spi:
- id: spi_bus0
clk_pin: GPIO18
mosi_pin: GPIO23
miso_pin: GPIO19
interface: any
uart:
- id: inv_uart1
rx_pin: GPIO16
tx_pin: GPIO17
baud_rate: 2400
- id: sth_uart
rx_pin:
number: GPIO17
inverted: false
mode:
input: true
pullup: true
tx_pin: GPIO16
baud_rate: 9600
stop_bits: 1
parity: NONE
debug:
direction: BOTH
dummy_receiver: false
after:
delimiter: "\r"
delimiter: "\n"
sequence:
- lambda: UARTDebug::log_hex(direction, bytes, ',');
@ -80,328 +120,404 @@ switch:
name: "${name} Restart"
id: "restart_switch"
interval:
- interval: 10s
then:
- lambda: |-
id(canbus_send_heartbeat).execute();
# - interval: 2.36s
# then:
# - uart.write:
# data: "sthome2 heartbeat\r\n"
# - sth_uart.write: !lambda
# return std::vector<uint8_t>({0x0D, 0x0A, 0x43, 0x50, 0x53, 0x00, 0x0D, 0x0A, 0x43, 0x50, 0x53, 0x00});
canbus:
- platform: mcp2515
cs_pin: GPIO32 # CBCS
spi_id: spi_bus0
id: canbus_sthome
mode: NORMAL
can_id: ${CB_CANBUS_ID03}
bit_rate: 500KBPS
on_frame:
- can_id: 0
can_id_mask: 0
then:
- lambda: |-
id(can_msgctr)++;
using namespace solar;
auto time_obj = id(time_source).now();
//ESP_LOGI("CB REC", "%s", id(time_source).now().strftime("%a %d %b %Y - %I:%M:%S %p"));
if(time_obj.is_valid()) {
if (can_id >= 0x380 && can_id < 0x3C0) {
auto cbitem = cbf_store_sthome(id(can_msgctr), can_id, x, remote_transmission_request, time_obj.timestamp);
bool publish = id(g_cb_cache).additem(cbitem);
if(publish) {
ESP_LOGI(cbitem.tag().c_str(), "%s", cbitem.to_string().c_str());
}
} else {
auto cbitem = cbf_store_sthome(id(can_msgctr), can_id, x, remote_transmission_request, time_obj.timestamp);
ESP_LOGI(cbitem.tag().c_str(), "%s", cbitem.to_string().c_str());
}
}
modbus:
- id: modbus1
uart_id: inv_uart1
flow_control_pin: GPIO4
send_wait_time: 1000ms #250ms
- id: modbus_server
uart_id: sth_uart
send_wait_time: 1200ms #250ms
disable_crc: false
role: server
modbus_controller:
- id: modbus_device1
modbus_id: modbus1
address: 0x1
allow_duplicate_commands: False
command_throttle: 0ms
update_interval: 4s #30s
offline_skip_updates: 2
max_cmd_retries: 04
setup_priority: -10
- id: modbus_alarm_server
modbus_id: modbus_server
address: 0x01
server_registers:
- address: ${Felicity_Inv_WorkingMode} # 0x1101
value_type: U_WORD
read_lambda: |-
return 0x0001;
- address: ${Felicity_Inv_BatteryChargingStage} # 0x1102
value_type: U_WORD
read_lambda: |-
return 0x0002;
- address: ${Felicity_Inv_FaultCode} # 0x1103
value_type: U_WORD
read_lambda: |-
return 0x0003;
- address: ${Felicity_Inv_PowerFlowMsg} # 0x1104
value_type: U_DWORD_R
read_lambda: |-
return 0x0004;
- address: 0x1106
value_type: U_DWORD_R
read_lambda: |-
return 0;
- address: ${Felicity_Inv_BatteryVoltage} # 0x1108
value_type: U_WORD
read_lambda: |-
return 0x0005;
- address: ${Felicity_Inv_BatteryCurrent} # 0x1109
value_type: U_WORD
read_lambda: |-
return 0x0006;
- address: ${Felicity_Inv_BatteryPower} # 0x110A
value_type: U_DWORD_R
read_lambda: |-
return 0x0007;
- address: 0x110C
value_type: U_QWORD_R
read_lambda: |-
return 0;
- address: 0x1110
value_type: U_WORD
read_lambda: |-
return 0;
- address: ${Felicity_Inv_ACOutputVoltage} # 0x1111
value_type: U_QWORD_R
read_lambda: |-
return 0x0008;
- address: 0x1115
value_type: U_DWORD_R
read_lambda: |-
return 0;
- address: ${Felicity_Inv_ACInputVoltage} # 0x1117
value_type: U_DWORD_R
read_lambda: |-
return 0x0009;
- address: ${Felicity_Inv_ACInputFrequency} # 0x1119
value_type: U_QWORD_R
read_lambda: |-
return 0x000A;
- address: 0x111D
value_type: U_WORD
read_lambda: |-
return 0;
- address: ${Felicity_Inv_ACOutputActivePower} # 0x111E
value_type: U_WORD
read_lambda: |-
return 0x000B;
- address: ${Felicity_Inv_ACOutputApparentPower} # 0x111F
value_type: U_WORD
read_lambda: |-
return 0x000C;
- address: ${Felicity_Inv_LoadPercentage} # 0x1120
value_type: U_QWORD_R
read_lambda: |-
return 0x000D;
- address: 0x1124
value_type: U_DWORD_R
read_lambda: |-
return 0;
- address: ${Felicity_Inv_PVInputVoltage} # 0x1126
value_type: U_QWORD_R
read_lambda: |-
return 0x000E;
- address: ${Felicity_Inv_PVInputPower} # 0x112A
value_type: U_WORD
read_lambda: |-
return 0x0010;
- address: 0x112C
value_type: U_WORD
read_lambda: |-
return 0;
- address: 0xF800
- address: 0x0001
value_type: S_DWORD_R
read_lambda: |-
return 0x400D;
- address: 0xF802
value_type: S_DWORD_R
read_lambda: |-
return 0x500E;
- address: 0xF804
value_type: S_DWORD_R
read_lambda: |-
return 0x4142;
- address: 0xF806
value_type: S_DWORD_R
read_lambda: |-
return 0x4344;
- address: 0xF808
value_type: S_DWORD_R
read_lambda: |-
return 0x4546;
- address: 0xF80A
value_type: S_DWORD_R
read_lambda: |-
return 0x4748;
- address: 0xF80C
value_type: S_DWORD_R
read_lambda: |-
return 0x494A;
- address: 0xF80E
value_type: S_DWORD_R
read_lambda: |-
return 0x4B4C;
- address: 0xF810
value_type: S_DWORD_R
read_lambda: |-
return 0;
uint16_t alarm_status = id(person_detected_switch).state ? 1 : 0;
alarm_status |= id(alarm_zone4_relay).state ? 2 : 0;
alarm_status |= id(lights_backyard_relay).state ? 4 : 0;
alarm_status |= id(lights_frontyard_relay).state ? 8 : 0;
alarm_status |= id(floodlight_test).state ? 16 : 0;
alarm_status |= id(night_time).state ? 32 : 0;
alarm_status |= id(zone1_triggered).state ? 64 : 0;
alarm_status |= id(zone2_triggered).state ? 128 : 0;
alarm_status |= id(zone3_triggered).state ? 256 : 0;
alarm_status |= id(zone4_triggered).state ? 512 : 0;
alarm_status |= id(zone5_triggered).state ? 1024 : 0;
alarm_status |= id(zone6_triggered).state ? 2048 : 0;
return alarm_status;
binary_sensor:
- platform: template
id: person_detected_switch
name: "Person Detected Switch"
lambda: |-
return true;
- platform: template
id: alarm_zone4_relay
name: "Alarm Zone 4 Relay"
lambda: |-
return true;
- platform: template
id: lights_backyard_relay
name: "Lights Backyard Relay"
lambda: |-
return true;
- platform: template
id: lights_frontyard_relay
name: "Lights Frontyard Relay"
lambda: |-
return true;
- platform: template
id: floodlight_test
name: "Floodlight Test"
lambda: |-
return true;
- platform: template
id: night_time
name: "Night Time"
lambda: |-
return true;
- platform: template
id: zone1_triggered
name: "Zone 1 Triggered"
lambda: |-
return true;
- platform: template
id: zone2_triggered
name: "Zone 2 Triggered"
lambda: |-
return true;
- platform: template
id: zone3_triggered
name: "Zone 3 Triggered"
lambda: |-
return true;
- platform: template
id: zone4_triggered
name: "Zone 4 Triggered"
lambda: |-
return true;
- platform: template
id: zone5_triggered
name: "Zone 5 Triggered"
lambda: |-
return true;
- platform: template
id: zone6_triggered
name: "Zone 6 Triggered"
lambda: |-
return true;
#modbus:
# - id: modbus1
# uart_id: inv_uart1
# flow_control_pin: GPIO4
# send_wait_time: 1000ms #250ms
# - id: modbus_client
# uart_id: sth_uart
# send_wait_time: 1200ms #250ms
# disable_crc: false
# role: client
#
#modbus_controller:
# - id: modbus_device1
# modbus_id: modbus1
# - id: modbus_alarm_client
# modbus_id: modbus_client
# address: 0x01
# allow_duplicate_commands: False
# command_throttle: 0ms
# update_interval: 4s #30s
# command_throttle: 700ms #2022ms
# update_interval: 10s #305s
# offline_skip_updates: 2
# max_cmd_retries: 0
# max_cmd_retries: 1
# setup_priority: -10
#
#binary_sensor:
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: person_detected
# name: "Person Detected"
# register_type: read
# address: 0x0001
# bitmask: 0x1
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: alarm_zone4_relay
# name: "Alarm Zone 4 Relay"
# register_type: read
# address: 0x0001
# bitmask: 0x2
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: lights_backyard_relay
# name: "Lights Backyard Relay"
# register_type: read
# address: 0x0001
# bitmask: 0x4
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: lights_frontyard_relay
# name: "Lights Frontyard Relay"
# register_type: read
# address: 0x0001
# bitmask: 0x8
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: floodlight_test
# name: "Floodlight Test"
# register_type: read
# address: 0x0001
# bitmask: 0x10
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: night_time
# name: "Night Time"
# register_type: read
# address: 0x0001
# bitmask: 0x20
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: zone1_triggered
# name: "Zone 1 Triggered"
# register_type: read
# address: 0x0001
# bitmask: 0x40
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: zone2_triggered
# name: "Zone 2 Triggered"
# register_type: read
# address: 0x0001
# bitmask: 0x80
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: zone3_triggered
# name: "Zone 3 Triggered"
# register_type: read
# address: 0x0001
# bitmask: 0x100
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: zone4_triggered
# name: "Zone 4 Triggered"
# register_type: read
# address: 0x0001
# bitmask: 0x200
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: zone5_triggered
# name: "Zone 5 Triggered"
# register_type: read
# address: 0x0001
# bitmask: 0x400
#- platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: zone6_triggered
# name: "Zone 6 Triggered"
# register_type: read
# address: 0x0001
# bitmask: 0x800
#
#sensor:
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 Type"
# register_type: holding
# address: ${Felicity_Inv_Type} # 0xF800
# value_type: U_DWORD
# register_count: 1
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 Sub Type"
# register_type: holding
# address: ${Felicity_Inv_SubType} # 0xF801
# value_type: U_DWORD
# register_count: 3
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z1z4_current
# name: "Z1Z4 Current"
# address: 0x0002
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "mA"
# lambda: |-
# return x / 1024.0;
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 CPU1 F/W Version"
# register_type: holding
# address: ${Felicity_Inv_CPU1_FW_Version} # 0xF80B
# value_type: U_DWORD
# register_count: 1
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z2z5_current
# name: "Z2Z5 Current"
# address: 0x0006
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "mA"
# lambda: |-
# return x / 1024.0;
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 CPU2 F/W Version"
# register_type: holding
# address: ${Felicity_Inv_CPU2_FW_Version} # 0xF80C
# value_type: U_DWORD
# register_count: 3
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z3z6_current
# name: "Z3Z6 Current"
# address: 0x000A
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "mA"
# lambda: |-
# return x / 1024.0;
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 Working Mode"
# register_type: holding
# address: ${Felicity_Inv_WorkingMode} # 0x1101
# value_type: U_WORD
# register_count: 1
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z1z4_bus_voltage
# name: "Z1Z4 Bus Voltage"
# address: 0x000E
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "V"
# lambda: |-
# return x / 1024.0;
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 Charge Mode"
# register_type: holding
# address: ${Felicity_Inv_BatteryChargingStage} # 0x1102
# value_type: U_WORD
# register_count: 1
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z2z5_bus_voltage
# name: "Z2Z5 Bus Voltage"
# address: 0x0012
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "V"
# lambda: |-
# return x / 1024.0;
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 Fault Code"
# register_type: holding
# address: ${Felicity_Inv_FaultCode} # 0x1103
# value_type: U_WORD
# register_count: 1
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z3z6_bus_voltage
# name: "Z3Z6 Bus Voltage"
# address: 0x0016
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "V"
# lambda: |-
# return x / 1024.0;
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 Power Flow"
# register_type: holding
# address: ${Felicity_Inv_PowerFlowMsg} # 0x1104
# value_type: U_WORD
# register_count: 4
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z1z4_power
# name: "Z1Z4 Power"
# address: 0x001A
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "W"
# lambda: |-
# return x / 1024.0;
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 Battery Voltage"
# register_type: holding
# address: ${Felicity_Inv_BatteryVoltage} # 0x1108
# value_type: U_WORD
# register_count: 1
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 Battery Current"
# register_type: holding
# address: ${Felicity_Inv_BatteryCurrent} # 0x1109
# value_type: U_WORD
# register_count: 1
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 BatteryPower"
# register_type: holding
# address: ${Felicity_Inv_BatteryPower} # 0x110A
# value_type: U_WORD
# register_count: 7
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 AC Output Voltage"
# register_type: holding
# address: ${Felicity_Inv_ACOutputVoltage} # 0x1111
# value_type: U_WORD
# register_count: 6
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 AC Input Voltage"
# register_type: holding
# address: ${Felicity_Inv_ACInputVoltage} # 0x1117
# value_type: U_WORD
# register_count: 2
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 AC Input Frequency"
# register_type: holding
# address: ${Felicity_Inv_ACInputFrequency} # 0x1119
# value_type: U_WORD
# register_count: 5
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 AC Output Active Power"
# register_type: holding
# address: ${Felicity_Inv_ACOutputActivePower} # 0x111E
# value_type: U_WORD
# register_count: 1
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 AC Output Apparent Power"
# register_type: holding
# address: ${Felicity_Inv_ACOutputApparentPower} # 0x111F
# value_type: U_WORD
# register_count: 1
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 Load Percentage"
# register_type: holding
# address: ${Felicity_Inv_LoadPercentage} # 0x1120
# value_type: U_WORD
# register_count: 6
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 PV Input Voltage"
# register_type: holding
# address: ${Felicity_Inv_PVInputVoltage} # 0x1126
# value_type: U_WORD
# register_count: 4
#
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 PV Input Power"
# register_type: holding
# address: ${Felicity_Inv_PVInputPower} # 0x112A
# value_type: U_WORD
# register_count: 1
#
#text_sensor:
#- platform: modbus_controller
# modbus_controller_id: modbus_device1
# name: "Inverter1 SerialNo"
# register_type: holding
# address: ${Felicity_Inv_SerialNo} # 0xF804
# response_size: 14
# register_count: 7
# raw_encode: HEXBYTES
#
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z2z5_power
# name: "Z2Z5 Power"
# address: 0x001E
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "W"
# lambda: |-
# return x / 1024.0;
#
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z3z6_power
# name: "Z3Z6 Power"
# address: 0x0022
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "W"
# lambda: |-
# return x / 1024.0;
#
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z1z4_shunt_voltage
# name: "Z1Z4 Shunt Voltage"
# address: 0x0026
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "V"
# lambda: |-
# return x / 1024.0;
#
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z2z5_shunt_voltage
# name: "Z2Z5 Shunt Voltage"
# address: 0x002A
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "V"
# lambda: |-
# return x / 1024.0;
#
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: z3z6_shunt_voltage
# name: "Z3Z6 Shunt Voltage"
# address: 0x002E
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "V"
# lambda: |-
# return x / 1024.0;
#
# - platform: modbus_controller
# modbus_controller_id: modbus_alarm_client
# id: alarm_signal
# name: "Alarm Signal"
# address: 0x0032
# register_type: holding
# value_type: S_WORD
# register_count: 4
# unit_of_measurement: "V"
# lambda: |-
# return x / 1024.0;
script:
- id: canbus_send_heartbeat
then:
lambda: |-
using namespace solar;
std::vector<uint8_t> x(cbf_sthome::heartbeat.begin(), cbf_sthome::heartbeat.end());
id(g_cb_cache).send_frame(id(canbus_sthome), cbf_sthome::CB_CANBUS_ID02, x);

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,30 @@ wifi:
captive_portal:
uart:
- id: sth_uart
rx_pin: GPIO15
tx_pin: GPIO16
baud_rate: 2400
stop_bits: 1
parity: NONE
debug:
direction: BOTH
dummy_receiver: true
after:
delimiter: "\r"
sequence:
- lambda: UARTDebug::log_hex(direction, bytes, ',');
interval:
# - interval: 10s
# then:
# - lambda: |-
# id(canbus_send_heartbeat).execute();
- interval: 3.14s
then:
- uart.write:
data: "sthome9 heartbeat\r\n"
sun:
id: sun_sensor
@ -69,126 +93,12 @@ text_sensor:
id: uptime_human
icon: mdi:clock-start
#define OUTPUT_R1 16
#define OUTPUT_R2 17
#define OUTPUT_R3 18
#define OUTPUT_R4 19
switch:
- platform: restart
name: "${name} Restart"
id: "restart_switch"
- platform: gpio
pin:
number: GPIO16
inverted: true
id: relay1
name: "Floodlights Backyard"
icon: "mdi:light-flood-down"
restore_mode: RESTORE_DEFAULT_OFF
# the backyard floodlight auto turns off in 4min 14 sec. So we need to switch relay off just before or at this time
# TODO: remove or extend auto turn off to >= 10min
on_turn_on:
- delay: 250s
- switch.turn_off: relay1
- platform: gpio
pin:
number: GPIO17
inverted: true
id: relay2
name: "Relay 2"
icon: "mdi:run-fast"
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- delay: 1000ms
- switch.turn_off: relay2
- platform: gpio
pin:
number: GPIO18
inverted: true
id: relay3
name: "Relay 3"
icon: "mdi:run-fast"
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- delay: 1000ms
- switch.turn_off: relay3
- platform: gpio
pin:
number: GPIO19
inverted: true
id: relay4
name: "Alarm Zone 4"
icon: "mdi:alarm-light-outline"
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- if:
condition:
- lambda: |-
double sun_elevation = id(sun_sensor).elevation();
return (sun_elevation <= -6); // -6° = civil twilight, -12° = nautical twilight, -18° = astronomical twilight
#- sun.is_below_horizon:
then:
- switch.turn_on: relay1
- if:
condition:
- binary_sensor.is_on: floodlight_test
then:
- switch.turn_on: relay1
- delay: 30s
- switch.turn_off: relay4
# define DIGITAL_D1 04
binary_sensor:
- platform: gpio
# device_class: light
id: floodlight_test
pin:
number: GPIO04
mode:
input: true
pullup: true
filters:
- delayed_off: 100ms
name: "Floodlights Test Mode"
icon: "mdi:lightbulb-on-outline"
#define ANALOG_A1 33
#define ANALOG_A2 32
#define ANALOG_A3 35
#define ANALOG_A4 34
#define ANALOG_A5 39
#define ANALOG_A6 36
sensor:
# - platform: adc
# pin: 35
# name: "Alarm Signal"
# id: alarm_signal
# update_interval: 2000ms
# attenuation: 12db
# sampling_mode: avg
# filters:
# - lambda:
# if (x >= 3.11) {
# return x * 1.60256;
# } else if (x <= 0.25) {
# return 0;
# } else {
# return x * 1.51;
# }
# on_value:
# then:
# # switch on floodlights
# lambda: |-
# if (id(alarm_signal).state > 1.5) {
# id(relay1).turn_on();
# }
sensor:
# human readable uptime sensor output to the text sensor above
- platform: uptime
name: Uptime in Days