MAX6921: Replace "intensity" (0-16) with "brightness" in percent

Expose action "set_brightness".
This commit is contained in:
endym 2024-04-08 17:07:59 +02:00
parent 45580f2638
commit b9ee2b2deb
3 changed files with 80 additions and 41 deletions

View file

@ -1,8 +1,8 @@
from esphome import pins
from esphome import pins, automation
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import display, spi
from esphome.const import CONF_ID, CONF_INTENSITY, CONF_LAMBDA
from esphome.const import CONF_ID, CONF_BRIGHTNESS, CONF_LAMBDA
DEPENDENCIES = ["spi", "esp32"]
@ -41,6 +41,8 @@ MAX6921Component = max6921_ns.class_(
"MAX6921Component", cg.PollingComponent, spi.SPIDevice
)
MAX6921ComponentRef = MAX6921Component.operator("ref")
SetBrightnessAction = max6921_ns.class_("SetBrightnessAction", automation.Action)
# optional "demo_mode" configuration
CONF_DEMO_MODE_OFF = "off"
@ -105,6 +107,7 @@ OUT_PIN_MAPPING_SCHEMA = cv.Schema(
)
)
CONFIG_SCHEMA = (
display.BASIC_DISPLAY_SCHEMA.extend(
{
@ -112,7 +115,7 @@ CONFIG_SCHEMA = (
cv.Required(CONF_LOAD_PIN): pins.gpio_input_pin_schema,
cv.Required(CONF_BLANK_PIN): pins.internal_gpio_output_pin_schema,
cv.Required(CONF_OUT_PIN_MAPPING): OUT_PIN_MAPPING_SCHEMA,
cv.Optional(CONF_INTENSITY, default=16): cv.int_range(min=0, max=16),
cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.templatable(cv.percentage),
cv.Optional(CONF_DEMO_MODE, default=CONF_DEMO_MODE_OFF): cv.enum(
DEMO_MODES
),
@ -150,7 +153,7 @@ async def to_code(config):
cg.ArrayInitializer(*[tuple[1] for tuple in sorted_list_of_tuples])
)
)
cg.add(var.set_intensity(config[CONF_INTENSITY]))
cg.add(var.set_brightness(config[CONF_BRIGHTNESS]))
cg.add(var.set_demo_mode(config[CONF_DEMO_MODE]))
if CONF_LAMBDA in config:
@ -158,3 +161,34 @@ async def to_code(config):
config[CONF_LAMBDA], [(MAX6921ComponentRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))
ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(MAX6921Component),
}
)
ACTION_SET_BRIGHTNESS_SCHEMA = cv.All(
automation.maybe_simple_id(
ACTION_SCHEMA.extend(
cv.Schema(
{
cv.Required(CONF_BRIGHTNESS): cv.templatable(cv.percentage),
}
)
)
),
)
@automation.register_action(
"max6921.set_brightness", SetBrightnessAction, ACTION_SET_BRIGHTNESS_SCHEMA
)
async def max6921_set_brightness_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
template_ = await cg.templatable(config[CONF_BRIGHTNESS], args, float)
cg.add(var.set_brightness(template_))
return var

View file

@ -135,19 +135,19 @@ void MAX6921Component::init_display_(void) {
const uint8_t PWM_RESOLUTION = 8;
// setup PWM for blank pin (intensity)...
this->display_.intensity.pwm_channel = 0;
freq = ledcSetup(this->display_.intensity.pwm_channel, PWM_FREQ_WANTED, PWM_RESOLUTION);
this->display_.brightness.pwm_channel = 0;
freq = ledcSetup(this->display_.brightness.pwm_channel, PWM_FREQ_WANTED, PWM_RESOLUTION);
if (freq != 0) {
ledcAttachPin(this->display_.intensity.pwm_pin->get_pin(), this->display_.intensity.pwm_channel);
this->display_.intensity.max_duty = pow(2,PWM_RESOLUTION); // max. duty value for given resolution
this->display_.intensity.duty_quotient = this->display_.intensity.max_duty / MAX_DISPLAY_INTENSITY; // pre-calc fixed duty quotient (256 / 16)
ledcAttachPin(this->display_.brightness.pwm_pin->get_pin(), this->display_.brightness.pwm_channel);
this->display_.brightness.max_duty = pow(2,PWM_RESOLUTION); // max. duty value for given resolution
this->display_.brightness.duty_quotient = this->display_.brightness.max_duty / MAX_DISPLAY_INTENSITY; // pre-calc fixed duty quotient (256 / 16)
ESP_LOGD(TAG, "Prepare intensity PWM: pin=%u, channel=%u, freq=%uHz, resolution=%ubit, duty quotient=%u",
this->display_.intensity.pwm_pin->get_pin(),
this->display_.intensity.pwm_channel, freq, PWM_RESOLUTION,
this->display_.intensity.duty_quotient);
this->display_.brightness.pwm_pin->get_pin(),
this->display_.brightness.pwm_channel, freq, PWM_RESOLUTION,
this->display_.brightness.duty_quotient);
} else {
ESP_LOGE(TAG, "Failed to configure PWM -> set to max. intensity");
pinMode(this->display_.intensity.pwm_pin->get_pin(), OUTPUT);
pinMode(this->display_.brightness.pwm_pin->get_pin(), OUTPUT);
this->disable_blank(); // enable display (max. intensity)
}
@ -286,16 +286,16 @@ int MAX6921Component::set_display(uint8_t start_pos, const char *str) {
void MAX6921Component::update_display_(void) {
// handle display intensity...
if (this->display_.intensity.config_changed) {
if (this->display_.brightness.config_changed) {
// calc duty for low-active BLANK pin...
uint32_t inverted_duty = this->display_.intensity.max_duty - \
this->display_.intensity.duty_quotient * \
this->display_.intensity.config_value;
ESP_LOGD(TAG, "Change display intensity to %u (off-time duty=%u/%u)",
this->display_.intensity.config_value, inverted_duty,
this->display_.intensity.max_duty);
ledcWrite(this->display_.intensity.pwm_channel, inverted_duty);
this->display_.intensity.config_changed = false;
uint32_t inverted_duty = this->display_.brightness.max_duty - \
this->display_.brightness.max_duty * \
this->display_.brightness.config_value;
ESP_LOGD(TAG, "Change display brightness to %.1f (off-time duty=%u/%u)",
this->display_.brightness.config_value, inverted_duty,
this->display_.brightness.max_duty);
ledcWrite(this->display_.brightness.pwm_channel, inverted_duty);
this->display_.brightness.config_changed = false;
}
// handle demo modes...
@ -386,7 +386,7 @@ void MAX6921Component::dump_config() {
char seg_name[3];
ESP_LOGCONFIG(TAG, "MAX6921:");
LOG_PIN(" LOAD Pin: ", this->load_pin_);
ESP_LOGCONFIG(TAG, " BLANK Pin: GPIO%u", this->display_.intensity.pwm_pin->get_pin());
ESP_LOGCONFIG(TAG, " BLANK Pin: GPIO%u", this->display_.brightness.pwm_pin->get_pin());
// display segment to DOUTx mapping...
for (uint i=0; i<this->display_.seg_to_out_map.size(); i++) {
if (i < 7) {
@ -401,23 +401,19 @@ void MAX6921Component::dump_config() {
ESP_LOGCONFIG(TAG, " Display position %2u: OUT%u", i, this->display_.pos_to_out_map[i]);
}
// ESP_LOGCONFIG(TAG, " Number of digits: %u", this->display_.num_digits);
ESP_LOGCONFIG(TAG, " Intensity: %u", this->display_.intensity.config_value);
ESP_LOGCONFIG(TAG, " Brightness: %.1f", this->display_.brightness.config_value);
ESP_LOGCONFIG(TAG, " Demo mode: %u", this->display_.demo_mode);
}
void MAX6921Component::set_intensity(uint8_t intensity) {
void MAX6921Component::set_brightness(float brightness) {
if (!this->setup_finished) {
ESP_LOGD(TAG, "Set intensity: setup not finished -> discard intensity value");
ESP_LOGD(TAG, "Set brightness: setup not finished -> discard brightness value");
return;
}
if (intensity > MAX_DISPLAY_INTENSITY) {
ESP_LOGV(TAG, "Invalid intensity: %u (0..%u)", intensity, MAX_DISPLAY_INTENSITY);
intensity = MAX_DISPLAY_INTENSITY;
}
if ((intensity == 0) || (intensity != this->display_.intensity.config_value)) {
this->display_.intensity.config_value = intensity;
ESP_LOGD(TAG, "Set intensity: %u", this->display_.intensity.config_value);
this->display_.intensity.config_changed = true;
if ((brightness == 0.0) || (brightness != this->display_.brightness.config_value)) {
this->display_.brightness.config_value = brightness;
ESP_LOGD(TAG, "Set brightness: %.1f", this->display_.brightness.config_value);
this->display_.brightness.config_changed = true;
}
}

View file

@ -1,5 +1,6 @@
#pragma once
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/time.h"
#include "esphome/components/spi/spi.h"
@ -20,18 +21,18 @@ enum demo_mode_t {
typedef struct {
InternalGPIOPin *pwm_pin;
uint config_value; // intensity from 0..16
float config_value; // brightness in percent (0.0-1.0)
bool config_changed;
uint32_t max_duty;
uint32_t duty_quotient;
uint8_t pwm_channel;
}display_intensity_t;
}display_brightness_t;
typedef struct {
std::vector<uint8_t> seg_to_out_map;
std::vector<uint8_t> pos_to_out_map;
uint num_digits;
display_intensity_t intensity;
display_brightness_t brightness;
uint8_t *out_buf_;
size_t out_buf_size_;
uint seg_out_smallest;
@ -52,9 +53,9 @@ class MAX6921Component : public PollingComponent,
float get_setup_priority() const override;
uint8_t print(uint8_t pos, const char *str);
uint8_t print(const char *str);
void set_blank_pin(InternalGPIOPin *pin) { this->display_.intensity.pwm_pin = pin; }
void set_blank_pin(InternalGPIOPin *pin) { this->display_.brightness.pwm_pin = pin; }
void set_brightness(float brightness);
void set_demo_mode(demo_mode_t mode) { this->display_.demo_mode = mode; }
void set_intensity(uint8_t intensity);
void set_load_pin(GPIOPin *load) { this->load_pin_ = load; }
void set_num_digits(uint8_t num_digits) { this->display_.num_digits = num_digits; }
void set_seg_to_out_pin_map(const std::vector<uint8_t> &pin_map) { this->display_.seg_to_out_map = pin_map; }
@ -75,10 +76,10 @@ class MAX6921Component : public PollingComponent,
display_t display_;
uint8_t *ascii_out_data_;
void clear_display(int pos=-1);
void disable_blank() { digitalWrite(this->display_.intensity.pwm_pin->get_pin(), LOW); } // display on
void disable_blank() { digitalWrite(this->display_.brightness.pwm_pin->get_pin(), LOW); } // display on
void IRAM_ATTR HOT disable_load() { this->load_pin_->digital_write(false); }
static void display_refresh_task(void *pv);
void enable_blank() { digitalWrite(this->display_.intensity.pwm_pin->get_pin(), HIGH); } // display off
void enable_blank() { digitalWrite(this->display_.brightness.pwm_pin->get_pin(), HIGH); } // display off
void IRAM_ATTR HOT enable_load() { this->load_pin_->digital_write(true); }
demo_mode_t get_demo_mode(void) { return this->display_.demo_mode; }
int set_display(uint8_t pos, const char *str);
@ -91,5 +92,13 @@ class MAX6921Component : public PollingComponent,
void update_display_();
};
template<typename... Ts> class SetBrightnessAction : public Action<Ts...>, public Parented<MAX6921Component> {
public:
TEMPLATABLE_VALUE(float, brightness)
void play(Ts... x) override { this->parent_->set_brightness(this->brightness_.value(x...)); }
};
} // namespace max6921
} // namespace esphome