unfinished pca9634 component
This commit is contained in:
4
components/pca9634/README.md
Normal file
4
components/pca9634/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# PCA9634 output component
|
||||
|
||||
This component is not finished. I'm submitting it only for reference.
|
||||
|
||||
39
components/pca9634/__init__.py
Normal file
39
components/pca9634/__init__.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c
|
||||
from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_INVERTED
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
MULTI_CONF = True
|
||||
|
||||
pca9634_ns = cg.esphome_ns.namespace('pca9634')
|
||||
|
||||
PCA9634GroupMode = mcp23008_ns.enum('PCA9634GroupMode')
|
||||
PCA9634_GROUP_MODES = {
|
||||
'DIM': PCA9634GroupMode.PCA9634_MODE2_DIM,
|
||||
'BLINK': PCA9634GroupMode.PCA9634_MODE2_BLINK,
|
||||
}
|
||||
|
||||
PCA9634OutputMode = mcp23008_ns.enum('PCA9634OutputMode')
|
||||
PCA9634_OUTPUT_MODES = {
|
||||
'TOTEM_POLE': PCA9634OutputMode.PCA9634_MODE2_TOTEM_POLE,
|
||||
'OPEN_DRAIN': PCA9634OutputMode.PCA9634_MODE2_OPEN_DRAIN,
|
||||
}
|
||||
|
||||
PCA9634Output = pca9634_ns.class_('PCA9345Output', cg.Component, i2c.I2CDevice)
|
||||
|
||||
CONF_GROUP_MODE = 'group_mode'
|
||||
CONF_OUTPUT_MODE = 'output_mode'
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(PCA9634Output),
|
||||
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
cv.Optional(CONF_GROUP_MODE, default="DIM"): cv.enum(PCA9634_GROUP_MODES, upper=True),
|
||||
cv.Optional(CONF_OUTPUT_MODE, default="TOTEM_POLE"): cv.enum(PCA9634_OUTPUT_MODES, upper=True),
|
||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(None))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
25
components/pca9634/output.py
Normal file
25
components/pca9634/output.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import output
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
from . import PCA9634Output, pca9634_ns
|
||||
|
||||
DEPENDENCIES = ['pca9634']
|
||||
|
||||
PCA9634Channel = pca9634_ns.class_('PCA9634Channel', output.FloatOutput)
|
||||
CONF_PCA9634_ID = 'pca9634_id'
|
||||
CONF_ISOLATE = 'isolate'
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||
cv.Required(CONF_ID): cv.declare_id(PCA9634Channel),
|
||||
cv.GenerateID(CONF_PCA9634_ID): cv.use_id(PCA9634Output),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=9),
|
||||
cv.Optional(CONF_ISOLATE, default=False): cv.boolean,
|
||||
})
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
paren = await cg.get_variable(config[CONF_PCA9634_ID])
|
||||
rhs = paren.create_channel(config[CONF_CHANNEL])
|
||||
var = cg.Pvariable(config[CONF_ID], rhs)
|
||||
await output.register_output(var, config)
|
||||
140
components/pca9634/pca9634_output.cpp
Normal file
140
components/pca9634/pca9634_output.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "pca9634_output.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace pca9634 {
|
||||
|
||||
static const char *TAG = "pca9634";
|
||||
|
||||
static const uint8_t PCA9634_ADDR_SOFTWARE_RESET = 0x06;
|
||||
|
||||
static const uint8_t PCA9634_MODE2_BLINK = 0x20;
|
||||
static const uint8_t PCA9634_MODE2_INVERTED = 0x10;
|
||||
static const uint8_t PCA9634_MODE2_OUTPUT_ONACK = 0x08;
|
||||
static const uint8_t PCA9634_MODE2_OUTPUT_TOTEM_POLE = 0x04;
|
||||
static const uint8_t PCA9634_MODE2_OUTNE_DEFAULT = 0x01;
|
||||
|
||||
static const uint8_t PCA9634_REGISTER_MODE1 = 0x00;
|
||||
static const uint8_t PCA9634_REGISTER_MODE2 = 0x01;
|
||||
static const uint8_t PCA9634_REGISTER_LED0 = 0x02;
|
||||
static const uint8_t PCA9634_REGISTER_PRE_SCALE = 0xFE;
|
||||
|
||||
|
||||
void PCA9634Output::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up PCA9634OutputComponent...");
|
||||
|
||||
uint8_t data[14];
|
||||
ESP_LOGV(TAG, " Resetting device...");
|
||||
data[0] = 0xa5; data[1] = 0x5a;
|
||||
if (!this->write_bytes_raw(PCA9634_ADDR_SOFTWARE_RESET, data, 2)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
data[0] = 0; // MODE1
|
||||
data[1] = this->group_mode_ | this->output_mode_ | PCA9634_MODE2_OUTNE_DEFAULT; // MODE2
|
||||
if (this->inverted_)
|
||||
data[1] |= PCA9634_MODE2_INVERTED;
|
||||
|
||||
if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (!this->write_byte(PCA9685_REGISTER_MODE2, this->mode_)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
int pre_scaler = static_cast<int>((25000000 / (4096 * this->frequency_)) - 1);
|
||||
if (pre_scaler > 255)
|
||||
pre_scaler = 255;
|
||||
if (pre_scaler < 3)
|
||||
pre_scaler = 3;
|
||||
|
||||
ESP_LOGV(TAG, " -> Prescaler: %d", pre_scaler);
|
||||
|
||||
uint8_t mode1;
|
||||
if (!this->read_byte(PCA9685_REGISTER_MODE1, &mode1)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
mode1 = (mode1 & ~PCA9685_MODE1_RESTART) | PCA9685_MODE1_SLEEP;
|
||||
if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (!this->write_byte(PCA9685_REGISTER_PRE_SCALE, pre_scaler)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
mode1 = (mode1 & ~PCA9685_MODE1_SLEEP) | PCA9685_MODE1_RESTART;
|
||||
if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
delayMicroseconds(500);
|
||||
|
||||
this->loop();
|
||||
}
|
||||
|
||||
void PCA9634Output::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "PCA9634:");
|
||||
ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Setting up PCA9634 failed!");
|
||||
}
|
||||
}
|
||||
|
||||
void PCA9634Output::loop() {
|
||||
if (!this->update_)
|
||||
return;
|
||||
|
||||
const uint16_t num_channels = this->max_channel_ - this->min_channel_ + 1;
|
||||
for (uint8_t channel = this->min_channel_; channel <= this->max_channel_; channel++) {
|
||||
uint16_t phase_begin = uint16_t(channel - this->min_channel_) / num_channels * 4096;
|
||||
uint16_t phase_end;
|
||||
uint16_t amount = this->pwm_amounts_[channel];
|
||||
if (amount == 0) {
|
||||
phase_end = 4096;
|
||||
} else if (amount >= 4096) {
|
||||
phase_begin = 4096;
|
||||
phase_end = 0;
|
||||
} else {
|
||||
phase_end = phase_begin + amount;
|
||||
if (phase_end >= 4096)
|
||||
phase_end -= 4096;
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "Channel %02u: amount=%04u phase_begin=%04u phase_end=%04u", channel, amount, phase_begin,
|
||||
phase_end);
|
||||
|
||||
uint8_t data[4];
|
||||
data[0] = phase_begin & 0xFF;
|
||||
data[1] = (phase_begin >> 8) & 0xFF;
|
||||
data[2] = phase_end & 0xFF;
|
||||
data[3] = (phase_end >> 8) & 0xFF;
|
||||
|
||||
uint8_t reg = PCA9634_REGISTER_LED0 + 4 * channel;
|
||||
if (!this->write_bytes(reg, data, 4)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->status_clear_warning();
|
||||
this->update_ = false;
|
||||
}
|
||||
|
||||
PCA9634Channel *PCA9634Output::create_channel(uint8_t channel) {
|
||||
return new PCA9634Channel(this, channel);
|
||||
}
|
||||
|
||||
void PCA9634Channel::write_state(float state) {
|
||||
uint8_t value = static_cast<uint8_t>(roundf(state * 255));
|
||||
this->parent_->set_channel_value_(this->channel_, value);
|
||||
}
|
||||
|
||||
} // namespace pca9634
|
||||
} // namespace esphome
|
||||
80
components/pca9634/pca9634_output.h
Normal file
80
components/pca9634/pca9634_output.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace pca9634 {
|
||||
|
||||
/// Inverts polarity of channel output signal
|
||||
extern const uint8_t PCA9685_MODE_INVERTED;
|
||||
/// Channel update happens upon ACK (post-set) rather than on STOP (endTransmission)
|
||||
extern const uint8_t PCA9685_MODE_OUTPUT_ONACK;
|
||||
/// Use a totem-pole (push-pull) style output rather than an open-drain structure.
|
||||
extern const uint8_t PCA9685_MODE_OUTPUT_TOTEM_POLE;
|
||||
/// For active low output enable, sets channel output to high-impedance state
|
||||
extern const uint8_t PCA9685_MODE_OUTNE_HIGHZ;
|
||||
/// Similarly, sets channel output to high if in totem-pole mode, otherwise
|
||||
extern const uint8_t PCA9685_MODE_OUTNE_LOW;
|
||||
|
||||
enum PCA9634GroupMode {
|
||||
PCA9634_MODE2_DIM = 0x00,
|
||||
PCA9634_MODE2_BLINK = 0x20,
|
||||
};
|
||||
|
||||
enum PCA9634OutputMode {
|
||||
PCA9634_MODE2_OPEN_DRAIN = 0x00,
|
||||
PCA9634_MODE2_TOTEM_POLE = 0x04,
|
||||
};
|
||||
|
||||
class PCA9634Output;
|
||||
|
||||
class PCA9634Channel : public output::FloatOutput {
|
||||
public:
|
||||
PCA9634Channel(PCA9634Output *parent, uint8_t channel) : parent_(parent), channel_(channel) {}
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
PCA9634Output *parent_;
|
||||
uint8_t channel_;
|
||||
};
|
||||
|
||||
/// PCA9634 float output component
|
||||
class PCA9634Output : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
PCA9685Output();
|
||||
|
||||
PCA9685Channel *create_channel(uint8_t channel);
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void loop() override;
|
||||
void set_group_mode(PCA9634GroupMode mode) { this->group_mode_ = mode; }
|
||||
void set_output_mode(PCA9634OutputMode mode) { this->output_mode_ = mode; }
|
||||
void set_inverted(boolean inverted) { this->inverted_ = inverted; }
|
||||
|
||||
protected:
|
||||
friend PCA9634Channel;
|
||||
|
||||
void set_channel_value_(uint8_t channel, uint8_t value) {
|
||||
if (this->pwm_values_[channel] != value) {
|
||||
this->update_ = true;
|
||||
this->pwm_values_[channel] = value;
|
||||
}
|
||||
}
|
||||
|
||||
PCA9634GroupMode group_mode_{PCA9634_MODE2_DIM};
|
||||
PCA9634OutputMode output_mode_{PCA9634_MODE2_TOTEM_POLE};
|
||||
boolean inverted_{false};
|
||||
|
||||
uint8_t pwm_values_[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint8_t grppwm_{0};
|
||||
uint8_t grpfreq_{0};
|
||||
bool update_{false};
|
||||
};
|
||||
|
||||
} // namespace pca9634
|
||||
} // namespace esphome
|
||||
Reference in New Issue
Block a user