diff --git a/esphome/components/sm16716/__init__.py b/esphome/components/sm16716/__init__.py new file mode 100644 index 0000000000..4e342588f9 --- /dev/null +++ b/esphome/components/sm16716/__init__.py @@ -0,0 +1,31 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.const import (CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_ID, + CONF_NUM_CHANNELS, CONF_NUM_CHIPS) + +AUTO_LOAD = ['output'] +sm16716_ns = cg.esphome_ns.namespace('sm16716') +SM16716 = sm16716_ns.class_('SM16716', cg.Component) + +MULTI_CONF = True +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(SM16716), + cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_NUM_CHANNELS, default=3): cv.int_range(min=3, max=255), + cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=85), +}).extend(cv.COMPONENT_SCHEMA) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + + data = yield cg.gpio_pin_expression(config[CONF_DATA_PIN]) + cg.add(var.set_data_pin(data)) + clock = yield cg.gpio_pin_expression(config[CONF_CLOCK_PIN]) + cg.add(var.set_clock_pin(clock)) + + cg.add(var.set_num_channels(config[CONF_NUM_CHANNELS])) + cg.add(var.set_num_chips(config[CONF_NUM_CHIPS])) diff --git a/esphome/components/sm16716/output.py b/esphome/components/sm16716/output.py new file mode 100644 index 0000000000..93c9ed4ce1 --- /dev/null +++ b/esphome/components/sm16716/output.py @@ -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 SM16716 + +DEPENDENCIES = ['sm16716'] + +Channel = SM16716.class_('Channel', output.FloatOutput) + +CONF_SM16716_ID = 'sm16716_id' +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ + cv.GenerateID(CONF_SM16716_ID): cv.use_id(SM16716), + cv.Required(CONF_ID): cv.declare_id(Channel), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=65535), +}).extend(cv.COMPONENT_SCHEMA) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield output.register_output(var, config) + + parent = yield cg.get_variable(config[CONF_SM16716_ID]) + cg.add(var.set_parent(parent)) + cg.add(var.set_channel(config[CONF_CHANNEL])) diff --git a/esphome/components/sm16716/sm16716.cpp b/esphome/components/sm16716/sm16716.cpp new file mode 100644 index 0000000000..bc8e4fc1f4 --- /dev/null +++ b/esphome/components/sm16716/sm16716.cpp @@ -0,0 +1,52 @@ +#include "sm16716.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace sm16716 { + +static const char *TAG = "sm16716"; + +void SM16716::setup() { + ESP_LOGCONFIG(TAG, "Setting up SM16716OutputComponent..."); + this->data_pin_->setup(); + this->data_pin_->digital_write(false); + this->clock_pin_->setup(); + this->clock_pin_->digital_write(false); + this->pwm_amounts_.resize(this->num_channels_, 0); +} +void SM16716::dump_config() { + ESP_LOGCONFIG(TAG, "SM16716:"); + LOG_PIN(" Data Pin: ", this->data_pin_); + LOG_PIN(" Clock Pin: ", this->clock_pin_); + ESP_LOGCONFIG(TAG, " Total number of channels: %u", this->num_channels_); + ESP_LOGCONFIG(TAG, " Number of chips: %u", this->num_chips_); +} +void SM16716::loop() { + if (!this->update_) + return; + + for (uint8_t i = 0; i < 50; i++) { + this->write_bit_(false); + } + + // send 25 bits (1 start bit plus 24 data bits) for each chip + for (uint8_t index = 0; index < this->num_channels_; index++) { + // send a start bit initially and after every 3 channels + if (index % 3 == 0) { + this->write_bit_(true); + } + + this->write_byte_(this->pwm_amounts_[index]); + } + + // send a blank 25 bits to signal the end + this->write_bit_(false); + this->write_byte_(0); + this->write_byte_(0); + this->write_byte_(0); + + this->update_ = false; +} + +} // namespace sm16716 +} // namespace esphome diff --git a/esphome/components/sm16716/sm16716.h b/esphome/components/sm16716/sm16716.h new file mode 100644 index 0000000000..fe534d93fe --- /dev/null +++ b/esphome/components/sm16716/sm16716.h @@ -0,0 +1,70 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/output/float_output.h" + +namespace esphome { +namespace sm16716 { + +class SM16716 : public Component { + public: + class Channel; + + void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; } + void set_clock_pin(GPIOPin *clock_pin) { clock_pin_ = clock_pin; } + void set_num_channels(uint8_t num_channels) { num_channels_ = num_channels; } + void set_num_chips(uint8_t num_chips) { num_chips_ = num_chips; } + + void setup() override; + + void dump_config() override; + + float get_setup_priority() const override { return setup_priority::HARDWARE; } + + /// Send new values if they were updated. + void loop() override; + + class Channel : public output::FloatOutput { + public: + void set_parent(SM16716 *parent) { parent_ = parent; } + void set_channel(uint8_t channel) { channel_ = channel; } + + protected: + void write_state(float state) override { + auto amount = uint8_t(state * 0xFF); + this->parent_->set_channel_value_(this->channel_, amount); + } + + SM16716 *parent_; + uint8_t channel_; + }; + + protected: + void set_channel_value_(uint8_t channel, uint8_t value) { + uint8_t index = this->num_channels_ - channel - 1; + if (this->pwm_amounts_[index] != value) { + this->update_ = true; + } + this->pwm_amounts_[index] = value; + } + void write_bit_(bool value) { + this->data_pin_->digital_write(value); + this->clock_pin_->digital_write(true); + this->clock_pin_->digital_write(false); + } + void write_byte_(uint8_t data) { + for (uint8_t mask = 0x80; mask; mask >>= 1) { + this->write_bit_(data & mask); + } + } + + GPIOPin *data_pin_; + GPIOPin *clock_pin_; + uint8_t num_channels_; + uint8_t num_chips_; + std::vector pwm_amounts_; + bool update_{true}; +}; + +} // namespace sm16716 +} // namespace esphome