[max6921] Add options "repeat_num" and "repeat_interval" to "set_text" action

With these options a defined effect (e.g. blink 3 times) can be repeated
x times or endless.
This commit is contained in:
endym 2024-09-05 20:24:24 +02:00
parent 36c5e829d6
commit 4f8f38ce40
4 changed files with 324 additions and 124 deletions

View file

@ -28,15 +28,20 @@ template<typename... Ts> class SetTextAction : public Action<Ts...> {
TEMPLATABLE_VALUE(std::string, text_effect) TEMPLATABLE_VALUE(std::string, text_effect)
TEMPLATABLE_VALUE(uint8_t, text_effect_cycle_num) TEMPLATABLE_VALUE(uint8_t, text_effect_cycle_num)
TEMPLATABLE_VALUE(uint32_t, text_effect_duration) TEMPLATABLE_VALUE(uint32_t, text_effect_duration)
TEMPLATABLE_VALUE(uint8_t, text_repeat_num)
TEMPLATABLE_VALUE(uint32_t, text_repeat_interval)
TEMPLATABLE_VALUE(uint32_t, text_effect_update_interval) TEMPLATABLE_VALUE(uint32_t, text_effect_update_interval)
void play(Ts... x) override { void play(Ts... x) override {
auto pos = this->text_position_.value(x...); auto pos = this->text_position_.value(x...);
auto cycle_num = this->text_effect_cycle_num_.value(x...); auto effect_cycle_num = this->text_effect_cycle_num_.value(x...);
auto duration = this->text_effect_duration_.value(x...); auto duration = this->text_effect_duration_.value(x...);
auto repeat_num = this->text_repeat_num_.value(x...);
auto repeat_interval = this->text_repeat_interval_.value(x...);
auto update_interval = this->text_effect_update_interval_.value(x...); auto update_interval = this->text_effect_update_interval_.value(x...);
this->max9621_->display_->set_text(this->text_.value(x...), pos, this->text_align_.value(x...), duration, this->max9621_->display_->set_text(this->text_.value(x...), pos, this->text_align_.value(x...), duration,
this->text_effect_.value(x...), update_interval, cycle_num); this->text_effect_.value(x...), effect_cycle_num, repeat_num, repeat_interval,
update_interval);
} }
protected: protected:

View file

@ -277,11 +277,13 @@ void Display::clear(int pos) {
* @param interval_ms update interval in ms (optional, default=restore) * @param interval_ms update interval in ms (optional, default=restore)
*/ */
void Display::set_update_interval(uint32_t interval_ms) { void Display::set_update_interval(uint32_t interval_ms) {
if ((interval_ms > 0) && (this->max6921_->get_update_interval() != interval_ms)) {
ESP_LOGD(TAG, "Change polling interval: %" PRIu32 "ms", interval_ms); ESP_LOGD(TAG, "Change polling interval: %" PRIu32 "ms", interval_ms);
this->max6921_->stop_poller(); this->max6921_->stop_poller();
this->max6921_->set_update_interval(interval_ms); this->max6921_->set_update_interval(interval_ms);
this->max6921_->start_poller(); this->max6921_->start_poller();
} }
}
/** /**
* @brief Restores the configured display update interval. * @brief Restores the configured display update interval.
@ -311,39 +313,47 @@ void Display::update() {
} }
// handle text effects... // handle text effects...
if (this->disp_text_.effect != TEXT_EFFECT_NONE) { // any effect enabled? if ((this->mode != DISP_MODE_PRINT) && (this->disp_text_.effect != TEXT_EFFECT_NONE)) { // any effect enabled?
switch (this->disp_text_.effect) { switch (this->disp_text_.effect) {
case TEXT_EFFECT_BLINK: case TEXT_EFFECT_BLINK:
update_out_buf_(); update_out_buf_();
this->disp_text_.blink(); if (this->disp_text_.blink())
set_mode(DISP_MODE_PRINT);
break; break;
case TEXT_EFFECT_SCROLL_LEFT: case TEXT_EFFECT_SCROLL_LEFT:
update_out_buf_(); update_out_buf_();
this->disp_text_.scroll_left(); if (this->disp_text_.scroll_left())
set_mode(DISP_MODE_PRINT);
break; break;
default: default:
set_mode(DISP_MODE_PRINT); set_mode(DISP_MODE_PRINT);
break; break;
} }
if (this->disp_text_.effect == TEXT_EFFECT_NONE) { // effect finished?
this->duration_ms = 0;
restore_update_interval();
set_mode(DISP_MODE_PRINT); // switch back to "it" interface
}
} }
// handle duration... // handle duration...
if (this->duration_ms > 0) { if ((this->mode != DISP_MODE_PRINT) && (this->disp_text_.duration_ms > 0)) {
if ((millis() - this->duration_ms_start_) >= this->duration_ms) { if ((millis() - this->disp_text_.get_duration_start()) >= this->disp_text_.duration_ms) {
ESP_LOGD(TAG, "Effect duration of %" PRIu32 "ms expired", this->duration_ms); ESP_LOGD(TAG, "Effect duration of %" PRIu32 "ms expired", this->disp_text_.duration_ms);
this->disp_text_.set_text_effect(TEXT_EFFECT_NONE);
set_mode(DISP_MODE_PRINT); set_mode(DISP_MODE_PRINT);
} }
} }
// handle repeats...
if (this->mode == DISP_MODE_PRINT) {
DisplayText &disp_text_other = this->disp_text_ctrl_[DISP_MODE_OTHER];
if (disp_text_other.repeat_on) {
if ((millis() - disp_text_other.get_repeat_start()) >= disp_text_other.repeat_interval_ms) {
ESP_LOGD(TAG, "Repeat interval of %" PRIu32 "ms expired", disp_text_other.repeat_interval_ms);
set_mode(DISP_MODE_OTHER);
}
}
}
} }
void Display::set_demo_mode(DemoModeT mode, uint32_t interval, uint8_t cycle_num) { void Display::set_demo_mode(DemoModeT mode, uint32_t interval, uint8_t cycle_num) {
uint text_idx, font_idx; uint text_idx, font_idx;
DisplayText &disp_text = this->disp_text_ctrl_[DISP_MODE_OTHER];
ESP_LOGD(TAG, "Set demo mode: mode=%i, update-interval=%" PRIu32 "ms, cycle_num=%u", mode, interval, cycle_num); ESP_LOGD(TAG, "Set demo mode: mode=%i, update-interval=%" PRIu32 "ms, cycle_num=%u", mode, interval, cycle_num);
@ -352,25 +362,24 @@ void Display::set_demo_mode(DemoModeT mode, uint32_t interval, uint8_t cycle_num
// generate scroll text based on font... // generate scroll text based on font...
for (text_idx = 0, font_idx = 0; font_idx < ARRAY_ELEM_COUNT(ASCII_TO_SEG); font_idx++) { for (text_idx = 0, font_idx = 0; font_idx < ARRAY_ELEM_COUNT(ASCII_TO_SEG); font_idx++) {
if (this->ascii_out_data_[font_idx] > 0) // displayable character? if (this->ascii_out_data_[font_idx] > 0) // displayable character?
this->disp_text_.text[text_idx++] = ' ' + font_idx; // add character to string disp_text.text[text_idx++] = ' ' + font_idx; // add character to string
if (text_idx >= sizeof(this->disp_text_.text) - 1) { // max. text buffer lenght reached? if (text_idx >= sizeof(disp_text.text) - 1) { // max. text buffer lenght reached?
ESP_LOGD(TAG, "Font too large for internal text buffer"); ESP_LOGD(TAG, "Font too large for internal text buffer");
break; break;
} }
} }
this->disp_text_.text[text_idx] = 0; disp_text.text[text_idx] = 0;
ESP_LOGV(TAG, "%s(): text: %s", __func__, this->disp_text_.text); ESP_LOGV(TAG, "%s(): text: %s", __func__, disp_text.text);
// set text effect... // set text effect...
this->disp_text_.set_text_effect(TEXT_EFFECT_SCROLL_LEFT, cycle_num); disp_text.set_text_effect(TEXT_EFFECT_SCROLL_LEFT, cycle_num, interval);
disp_text.set_duration(0);
disp_text.set_repeats(0, 0);
set_mode(DISP_MODE_OTHER); set_mode(DISP_MODE_OTHER);
break; break;
default: default:
set_mode(DISP_MODE_PRINT); set_mode(DISP_MODE_PRINT);
break; break;
} }
if ((mode != DEMO_MODE_OFF) && (interval > 0))
set_update_interval(interval);
clear();
} }
void Display::set_demo_mode(const std::string &mode, uint32_t interval, uint8_t cycle_num) { void Display::set_demo_mode(const std::string &mode, uint32_t interval, uint8_t cycle_num) {
@ -383,6 +392,62 @@ void Display::set_demo_mode(const std::string &mode, uint32_t interval, uint8_t
} }
} }
/**
* @brief Sets the display mode.
*
* @param mode display mode to set
*
* @return current mode
*/
DisplayModeT Display::set_mode(DisplayModeT mode) {
this->disp_text_ = this->disp_text_ctrl_[DisplayMode::set_mode(mode)];
switch (this->mode) {
case DISP_MODE_PRINT: {
clear(); // clear display buffer
restore_update_interval();
// handle repeats of "other" mode...
DisplayText &disp_text_other = this->disp_text_ctrl_[DISP_MODE_OTHER];
if (disp_text_other.repeat_on) {
if (disp_text_other.repeat_num > 0) { // defined number of repeats?
if (disp_text_other.repeat_current < disp_text_other.repeat_num) {
ESP_LOGV(TAG, "Repeat cycle finished (%u left)",
disp_text_other.repeat_num - disp_text_other.repeat_current);
++disp_text_other.repeat_current;
} else {
ESP_LOGD(TAG, "Repeats finished");
disp_text_other.repeat_on = false;
}
}
if (disp_text_other.repeat_on)
disp_text_other.start_repeat();
}
break;
}
case DISP_MODE_OTHER: {
clear(); // clear display buffer
if (this->disp_text_.effect == TEXT_EFFECT_NONE) {
update_out_buf_(); // show static text again
} else {
set_update_interval(this->disp_text_.update_interval_ms);
}
this->disp_text_.start_duration();
break;
}
default:
ESP_LOGE(TAG, "Invalid display mode: %i", this->mode);
break;
}
ESP_LOGD(TAG, "Set display mode: mode=%i, repeat-on=%i, repeat-cur/num=%u/%u, repeat-start=%" PRIu32 "ms", this->mode,
this->disp_text_.repeat_on, this->disp_text_.repeat_current, this->disp_text_.repeat_num,
this->disp_text_.get_repeat_start());
return this->mode;
}
/** /**
* @brief Triggered by action. Shows the given text on the display. * @brief Triggered by action. Shows the given text on the display.
* *
@ -391,35 +456,55 @@ void Display::set_demo_mode(const std::string &mode, uint32_t interval, uint8_t
* @param align text alignment * @param align text alignment
* @param duration text effect duration in ms * @param duration text effect duration in ms
* @param effect text effect * @param effect text effect
* @param interval text effect update interval in ms * @param effect_cycle_num text effect cycle count
* @param cycle_num text effect cycle count * @param repeat_num text repeat count (needs duration or effect_cycle_num)
* @param repeat_interval text repeat interval in ms (needs duration or effect_cycle_num)
* @param update_interval text effect update interval in ms
* *
* @return number of characters displayed * @return number of characters displayed
*/ */
int Display::set_text(const std::string &text, uint8_t start_pos, const std::string &align, uint32_t duration, int Display::set_text(const std::string &text, uint8_t start_pos, const std::string &align, uint32_t duration,
const std::string &effect, uint32_t interval, uint8_t cycle_num) { const std::string &effect, uint8_t effect_cycle_num, uint8_t repeat_num, uint32_t repeat_interval,
ESP_LOGD(TAG, uint32_t update_interval) {
DisplayText &disp_text = this->disp_text_ctrl_[DISP_MODE_OTHER];
ESP_LOGV(TAG,
"Set text (given): text=%s, start-pos=%u, align=%s, duration=%" PRIu32 "ms, " "Set text (given): text=%s, start-pos=%u, align=%s, duration=%" PRIu32 "ms, "
"effect=%s, effect-update-interval=%" PRIu32 "ms, cycles=%u", "effect=%s, cycles=%u, repeat-num=%u, repeat-interval=%" PRIu32 "ms, "
text.c_str(), start_pos, align.c_str(), duration, effect.c_str(), interval, cycle_num); "effect-update-interval=%" PRIu32 "ms",
text.c_str(), start_pos, align.c_str(), duration, effect.c_str(), effect_cycle_num, repeat_num,
repeat_interval, update_interval);
// store new text... // store new text...
this->disp_text_.set_text(start_pos, this->num_digits_ - 1, text); disp_text.set_text(start_pos, this->num_digits_ - 1, text);
// set text align... // set text align...
this->disp_text_.set_text_align(align); disp_text.set_text_align(align);
// set text effect... // set text effect...
this->disp_text_.set_text_effect(effect, cycle_num); disp_text.set_text_effect(effect, effect_cycle_num, update_interval);
if ((this->disp_text_.effect != TEXT_EFFECT_NONE) && (interval > 0)) { if (disp_text.effect == TEXT_EFFECT_NONE)
set_update_interval(interval); effect_cycle_num = 0;
// set duration...
disp_text.set_duration(duration);
disp_text.start_duration();
// set repeats...
if ((effect_cycle_num == 0) && (duration == 0)) {
repeat_num = 0;
repeat_interval = 0;
} }
disp_text.set_repeats(repeat_num, repeat_interval);
// update display mode... // update display mode...
set_mode(DISP_MODE_OTHER, duration); set_mode(DISP_MODE_OTHER);
ESP_LOGD(TAG, "Set text (result): text=%s, start-pos=%u, vi-idx=%u, vi-len=%u", this->disp_text_.text, ESP_LOGV(TAG,
this->disp_text_.start_pos, this->disp_text_.visible_idx, this->disp_text_.visible_len); "Set text (result): text=%s, start-pos=%u, vi-idx=%u, vi-len=%u, "
"duration=%" PRIu32 "ms, repeat-on=%i, repeat-num=%u, repeat-interval=%" PRIu32 "ms",
disp_text.text, disp_text.start_pos, disp_text.visible_idx, disp_text.visible_len, disp_text.duration_ms,
disp_text.repeat_on, disp_text.repeat_num, disp_text.repeat_interval_ms);
return update_out_buf_(); return update_out_buf_();
} }
@ -433,14 +518,16 @@ int Display::set_text(const std::string &text, uint8_t start_pos, const std::str
* @return number of characters displayed * @return number of characters displayed
*/ */
int Display::set_text(const char *text, uint8_t start_pos) { int Display::set_text(const char *text, uint8_t start_pos) {
ESP_LOGVV(TAG, "%s(): str=%s, prev=%s", __func__, text, this->disp_text_.text); DisplayText &disp_text = this->disp_text_ctrl_[DISP_MODE_PRINT];
if (strncmp(text, this->disp_text_.text, sizeof(this->disp_text_.text)) == 0) // text not changed?
ESP_LOGVV(TAG, "%s(): str=%s, prev=%s", __func__, text, disp_text.text);
if (strncmp(text, disp_text.text, sizeof(disp_text.text)) == 0) // text not changed?
return strlen(text); // yes -> exit function return strlen(text); // yes -> exit function
ESP_LOGV(TAG, "%s(): Text changed: str=%s, prev=%s", __func__, text, this->disp_text_.text); ESP_LOGV(TAG, "%s(): Text changed: str=%s, prev=%s", __func__, text, disp_text.text);
// store new text... // store new text...
std::string text_str = text; std::string text_str = text;
this->disp_text_.set_text(start_pos, this->num_digits_ - 1, text_str); disp_text.set_text(start_pos, this->num_digits_ - 1, text_str);
return update_out_buf_(); return update_out_buf_();
} }
@ -554,6 +641,55 @@ DisplayText::DisplayText() {
this->max_pos = 0; this->max_pos = 0;
this->align = TEXT_ALIGN_CENTER; this->align = TEXT_ALIGN_CENTER;
this->effect = TEXT_EFFECT_NONE; this->effect = TEXT_EFFECT_NONE;
this->cycle_num = 0; // endless
this->cycle_current = 0;
this->update_interval_ms = 0;
this->duration_ms = 0; // endless
this->repeat_on = false;
this->repeat_num = 0; // endless repeats
this->repeat_current = 0;
this->repeat_interval_ms = 0; // no repeat
}
/**
* @brief Sets the text display time.
*
* @param duration_ms text display time in [ms]
*/
void DisplayText::set_duration(uint32_t duration_ms) {
ESP_LOGD(TAG, "Set text duration: %" PRIu32 "ms", duration_ms);
this->duration_ms = duration_ms;
}
/**
* @brief Starts the text display time period.
*/
void DisplayText::start_duration() {
if (this->duration_ms > 0) {
ESP_LOGV(TAG, "Start text duration (%" PRIu32 "ms)", this->duration_ms);
this->duration_ms_start_ = millis();
}
}
/**
* @brief Sets the text display repeat interval(s) after duration has expired.
*
* @param repeat_num text display repeat number (0 = endless)
* @param repeat_interval_ms text display repeat interval [ms]
*/
void DisplayText::set_repeats(uint8_t repeat_num, uint32_t repeat_interval_ms) {
this->repeat_num = repeat_num;
this->repeat_current = 0;
this->repeat_interval_ms = repeat_interval_ms;
this->repeat_on = (repeat_interval_ms > 0);
}
/**
* @brief Starts the text display repeat period.
*/
void DisplayText::start_repeat() {
if (this->repeat_interval_ms > 0)
this->repeat_ms_start_ = millis();
} }
/** /**
@ -578,6 +714,10 @@ int DisplayText::set_text(uint start_pos, uint max_pos, const std::string &text)
this->text[sizeof(this->text) - 1] = 0; this->text[sizeof(this->text) - 1] = 0;
this->visible_idx = 0; this->visible_idx = 0;
this->visible_len = std::min(strlen(this->text), this->max_pos - this->start_pos + 1); this->visible_len = std::min(strlen(this->text), this->max_pos - this->start_pos + 1);
ESP_LOGD(TAG, "Set text: start-pos=%u, vi-idx=%u, vi-len=%u, text=%s", this->start_pos, this->visible_idx,
this->visible_len, this->text);
return strlen(this->text); return strlen(this->text);
} }
@ -633,7 +773,7 @@ void DisplayText::set_text_align(TextAlignT align) {
} }
this->align = align; this->align = align;
init_text_align_(); init_text_align_();
ESP_LOGD(TAG, "Set align: text=%s, align=%i, start-pos=%u, max-pos=%u, vi-idx=%u, vi-len=%u", this->text, this->align, ESP_LOGD(TAG, "Set text align: align=%i, start-pos=%u, max-pos=%u, vi-idx=%u, vi-len=%u", this->align,
this->start_pos, this->max_pos, this->visible_idx, this->visible_len); this->start_pos, this->max_pos, this->visible_idx, this->visible_len);
} }
@ -667,22 +807,31 @@ void DisplayText::set_text_align(const std::string &align) {
* *
* @param effect text effect * @param effect text effect
* @param cycle_num number of effect cycles (optional, default=endless) * @param cycle_num number of effect cycles (optional, default=endless)
* @param update_interval effect update interval in [ms] (optional, default=standard)
*/ */
void DisplayText::set_text_effect(TextEffectT effect, uint8_t cycle_num) { void DisplayText::set_text_effect(TextEffectT effect, uint8_t cycle_num, uint32_t update_interval) {
if (effect >= TEXT_EFFECT_LAST_ENUM) { if (effect >= TEXT_EFFECT_LAST_ENUM) {
ESP_LOGE(TAG, "Invalid display text effect: %i", effect); ESP_LOGE(TAG, "Invalid display text effect: %i", effect);
return; return;
} }
if (effect != this->effect) {
this->effect = effect; this->effect = effect;
switch (effect) {
case TEXT_EFFECT_BLINK:
case TEXT_EFFECT_SCROLL_LEFT:
this->cycle_current = 1;
this->cycle_num = cycle_num; this->cycle_num = cycle_num;
if (effect == TEXT_EFFECT_BLINK) this->update_interval_ms = update_interval;
this->effect_change_count_ = -1; break;
init_text_effect_(); default:
ESP_LOGD(TAG, "Set effect: text=%s, effect=%i, cycles=%u, start-pos=%u, max-pos=%u, vi-idx=%u, vi-len=%u", break;
this->text, this->effect, this->cycle_num, this->start_pos, this->max_pos, this->visible_idx,
this->visible_len);
} }
init_text_effect_();
ESP_LOGD(TAG,
"Set text effect: effect=%i, cycles=%u, upd-interval=%" PRIu32 "ms, start-pos=%u, "
"max-pos=%u, vi-idx=%u, vi-len=%u",
this->effect, this->cycle_num, this->update_interval_ms, this->start_pos, this->max_pos, this->visible_idx,
this->visible_len);
} }
/** /**
@ -690,8 +839,9 @@ void DisplayText::set_text_effect(TextEffectT effect, uint8_t cycle_num) {
* *
* @param effect text effect (as string) * @param effect text effect (as string)
* @param cycle_num number of effect cycles (optional, default=endless) * @param cycle_num number of effect cycles (optional, default=endless)
* @param update_interval effect update interval in [ms] (optional, default=standard)
*/ */
void DisplayText::set_text_effect(const std::string &effect, uint8_t cycle_num) { void DisplayText::set_text_effect(const std::string &effect, uint8_t cycle_num, uint32_t update_interval) {
TextEffectT text_effect = TEXT_EFFECT_LAST_ENUM; TextEffectT text_effect = TEXT_EFFECT_LAST_ENUM;
if (!effect.empty()) { if (!effect.empty()) {
@ -708,56 +858,61 @@ void DisplayText::set_text_effect(const std::string &effect, uint8_t cycle_num)
ESP_LOGW(TAG, "No text effect given"); ESP_LOGW(TAG, "No text effect given");
if (text_effect >= TEXT_EFFECT_LAST_ENUM) if (text_effect >= TEXT_EFFECT_LAST_ENUM)
return; return;
set_text_effect(text_effect, cycle_num); set_text_effect(text_effect, cycle_num, update_interval);
} }
/** /**
* @brief Updates the mode "blink". The display buffer must be updated before. * @brief Updates the mode "blink". The display buffer must be updated before.
*
* @return true = effect cycles finished
* false = effect cycles not finished
*/ */
void DisplayText::blink() { bool DisplayText::blink() {
ESP_LOGV(TAG, "%s(): ENTRY: start-idx=%u, text-idx=%u, text-len=%u", __func__, this->start_pos, this->visible_idx, ESP_LOGV(TAG, "%s(): ENTRY: start-idx=%u, text-idx=%u, text-len=%u, cycle-cur/num=%u/%u", __func__, this->start_pos,
this->visible_len); this->visible_idx, this->visible_len, this->cycle_current, this->cycle_num);
// update effect mode... if ((this->cycle_num > 0) && (this->cycle_current > this->cycle_num)) { // cycles finished?
if (++this->effect_change_count_ >= 2) { // one on/off phase complete?
this->effect_change_count_ = 0;
if (this->cycle_num > 0) {
ESP_LOGD(TAG, "Blink cycle finished (%u left)", this->cycle_num - 1);
if (--this->cycle_num == 0) {
this->effect = TEXT_EFFECT_NONE;
ESP_LOGD(TAG, "Blink finished"); ESP_LOGD(TAG, "Blink finished");
return; return true;
}
}
} }
// update visible text... // update visible text...
if (this->visible_len > 0) { // "on" phase? if (this->visible_len > 0) { // "on" phase?
this->visible_len = 0; // yes -> switch to "off" phase this->visible_len = 0; // yes -> next display buffer update starts "off" phase
} else { } else { // "off" phase...
init_text_effect_(); // no -> switch to "on" phase ESP_LOGV(TAG, "\"Off\" phase of blink cycle started (%u cycles left)", this->cycle_num - this->cycle_current);
if ((this->cycle_num == 0) || // endless cycles or
(this->cycle_current < this->cycle_num)) { // number of cycles not finished?
init_text_effect_(); // next display buffer update starts "on" phase
}
if (this->cycle_num > 0) // defined number of cycles?
++this->cycle_current; // yes -> increase current cycle count
} }
ESP_LOGV(TAG, "%s(): EXIT: start-idx=%u, text-idx=%u, text-len=%u", __func__, this->start_pos, this->visible_idx, ESP_LOGV(TAG, "%s(): EXIT: start-idx=%u, text-idx=%u, text-len=%u, cycle-cur/num=%u/%u", __func__, this->start_pos,
this->visible_len); this->visible_idx, this->visible_len, this->cycle_current, this->cycle_num);
return false;
} }
/** /**
* @brief Updates the mode "scroll left". The display buffer must be updated before. * @brief Updates the mode "scroll left". The display buffer must be updated before.
*
* @return true = effect cycles finished
* false = effect cycles not finished
*/ */
void DisplayText::scroll_left() { bool DisplayText::scroll_left() {
ESP_LOGV(TAG, "%s(): ENTRY: start-idx=%u, text-idx=%u, text-len=%u", __func__, this->start_pos, this->visible_idx, ESP_LOGV(TAG, "%s(): ENTRY: start-idx=%u, text-idx=%u, text-len=%u", __func__, this->start_pos, this->visible_idx,
this->visible_len); this->visible_len);
// update effect mode... // update effect mode...
if (this->visible_len == 0) { if (this->visible_len == 0) { // cycle finished?
init_text_effect_(); init_text_effect_();
if (this->cycle_num > 0) { if (this->cycle_current <= this->cycle_num) {
ESP_LOGD(TAG, "Scroll cycle finished (%u left)", this->cycle_num - 1); ESP_LOGV(TAG, "Scroll cycle finished (%u left)", this->cycle_num - this->cycle_current);
if (--this->cycle_num == 0) { if (this->cycle_current++ >= this->cycle_num) {
this->effect = TEXT_EFFECT_NONE;
ESP_LOGD(TAG, "Scroll finished"); ESP_LOGD(TAG, "Scroll finished");
return; return true;
} }
} }
} }
@ -775,6 +930,8 @@ void DisplayText::scroll_left() {
ESP_LOGV(TAG, "%s(): EXIT: start-idx=%u, text-idx=%u, text-len=%u", __func__, this->start_pos, this->visible_idx, ESP_LOGV(TAG, "%s(): EXIT: start-idx=%u, text-idx=%u, text-len=%u", __func__, this->start_pos, this->visible_idx,
this->visible_len); this->visible_len);
return false;
} }
/** /**
@ -820,31 +977,25 @@ void DisplayBrightness::set_brightness(float percent) {
/** /**
* @brief Constructor. * @brief Constructor.
*/ */
DisplayMode::DisplayMode() { DisplayMode::DisplayMode() { this->mode = DISP_MODE_PRINT; }
this->mode = DISP_MODE_PRINT;
this->duration_ms = 0; // endless
}
/** /**
* @brief Sets the display mode. * @brief Sets the display mode.
* *
* @param mode display start position of text (0..n) * @param mode display mode to set
* @param duration duration in ms (optional, default=endless) *
* not applicable for "it" mode * @return current mode
*/ */
void DisplayMode::set_mode(DisplayModeT mode, uint32_t duration_ms) { DisplayModeT DisplayMode::set_mode(DisplayModeT mode) {
if (mode >= DISP_MODE_LAST_ENUM) { if (mode >= DISP_MODE_LAST_ENUM) {
ESP_LOGE(TAG, "Invalid display mode: %i", mode); ESP_LOGE(TAG, "Invalid display mode: %i", mode);
return; return this->mode;
} }
if (mode != this->mode) { if (mode != this->mode) {
this->mode = mode; this->mode = mode;
this->duration_ms = duration_ms;
if (duration_ms > 0)
this->duration_ms_start_ = millis();
ESP_LOGD(TAG, "Set display mode: mode=%i, duration=%" PRIu32 "ms, duration-start=%" PRIu32 "ms", this->mode,
this->duration_ms, this->duration_ms_start_);
} }
return this->mode;
} }
} // namespace max6921 } // namespace max6921

View file

@ -16,7 +16,8 @@ static const uint FONT_SIZE = 95;
static const uint DISPLAY_TEXT_LEN = FONT_SIZE; // at least font size for demo mode "scroll font" static const uint DISPLAY_TEXT_LEN = FONT_SIZE; // at least font size for demo mode "scroll font"
enum DisplayModeT { enum DisplayModeT {
DISP_MODE_PRINT, // input by it-functions // Used as index!
DISP_MODE_PRINT = 0, // input by it-functions
DISP_MODE_OTHER, // input by actions DISP_MODE_OTHER, // input by actions
DISP_MODE_LAST_ENUM DISP_MODE_LAST_ENUM
}; };
@ -52,17 +53,16 @@ class DisplayBrightness {
class DisplayMode { class DisplayMode {
public: public:
DisplayModeT mode; DisplayModeT mode;
uint32_t duration_ms;
DisplayMode(); DisplayMode();
void set_mode(DisplayModeT mode, uint32_t duration_ms = 0); DisplayModeT set_mode(DisplayModeT mode);
protected: protected:
uint32_t duration_ms_start_;
}; };
class DisplayText { class DisplayText {
public: public:
bool content_changed; bool content_changed;
bool repeat_on; // repetitions are enabled
uint max_pos; // max. display position uint max_pos; // max. display position
uint start_pos; // current display start position (0..n) uint start_pos; // current display start position (0..n)
char text[DISPLAY_TEXT_LEN + 1]; // current text to display (may be larger then display) char text[DISPLAY_TEXT_LEN + 1]; // current text to display (may be larger then display)
@ -70,18 +70,31 @@ class DisplayText {
uint visible_len; // current length of visible text uint visible_len; // current length of visible text
TextAlignT align; TextAlignT align;
TextEffectT effect; TextEffectT effect;
uint8_t cycle_num; uint32_t duration_ms; // text/effect duration
uint32_t repeat_interval_ms; // repeat interval
uint8_t repeat_num; // number of repeats of showing text/effect
uint8_t repeat_current; // current repeat cycle
uint8_t cycle_num; // number of effect cycles
uint8_t cycle_current; // current effect cycle
uint32_t update_interval_ms; // effect update interval
DisplayText(); DisplayText();
void blink(); bool blink();
void scroll_left(); uint32_t get_duration_start() { return this->duration_ms_start_; }
uint32_t get_repeat_start() { return this->repeat_ms_start_; }
bool scroll_left();
void set_duration(uint32_t duration_ms);
void start_duration();
void start_repeat();
void set_repeats(uint8_t repeat_num, uint32_t repeat_interval_ms);
int set_text(uint start_pos, uint max_pos, const std::string &text); int set_text(uint start_pos, uint max_pos, const std::string &text);
void set_text_align(TextAlignT align); void set_text_align(TextAlignT align);
void set_text_align(const std::string &align); void set_text_align(const std::string &align);
void set_text_effect(TextEffectT effect, uint8_t cycle_num = 0); void set_text_effect(TextEffectT effect, uint8_t cycle_num = 0, uint32_t update_interval = 0);
void set_text_effect(const std::string &effect, uint8_t cycle_num = 0); void set_text_effect(const std::string &effect, uint8_t cycle_num = 0, uint32_t update_interval = 0);
protected: protected:
int effect_change_count_; uint32_t duration_ms_start_;
uint32_t repeat_ms_start_;
void init_text_align_(); void init_text_align_();
void init_text_effect_(); void init_text_effect_();
}; };
@ -96,9 +109,11 @@ class Display : public DisplayBrightness, public DisplayMode {
void setup(std::vector<uint8_t> &seg_to_out_map, std::vector<uint8_t> &pos_to_out_map); void setup(std::vector<uint8_t> &seg_to_out_map, std::vector<uint8_t> &pos_to_out_map);
void set_demo_mode(DemoModeT mode, uint32_t interval, uint8_t cycle_num); void set_demo_mode(DemoModeT mode, uint32_t interval, uint8_t cycle_num);
void set_demo_mode(const std::string &mode, uint32_t interval, uint8_t cycle_num); void set_demo_mode(const std::string &mode, uint32_t interval, uint8_t cycle_num);
DisplayModeT set_mode(DisplayModeT mode);
int set_text(const char *text, uint8_t start_pos); int set_text(const char *text, uint8_t start_pos);
int set_text(const std::string &text, uint8_t start_pos, const std::string &align, uint32_t duration, int set_text(const std::string &text, uint8_t start_pos, const std::string &align, uint32_t duration,
const std::string &effect, uint32_t interval, uint8_t cycle_num); const std::string &effect, uint8_t effect_cycle_num, uint8_t repeat_num, uint32_t repeat_interval,
uint32_t update_interval);
void set_update_interval(uint32_t interval_ms); void set_update_interval(uint32_t interval_ms);
void update(); void update();
@ -112,7 +127,8 @@ class Display : public DisplayBrightness, public DisplayMode {
size_t out_buf_size_; size_t out_buf_size_;
uint seg_out_smallest_; uint seg_out_smallest_;
uint32_t refresh_period_us_; uint32_t refresh_period_us_;
DisplayText disp_text_; DisplayText disp_text_ctrl_[DISP_MODE_LAST_ENUM];
DisplayText &disp_text_ = disp_text_ctrl_[0];
uint32_t default_update_interval_; uint32_t default_update_interval_;
static void display_refresh_task(void *pv); static void display_refresh_task(void *pv);
int update_out_buf_(); int update_out_buf_();

View file

@ -44,11 +44,13 @@ CONF_POS_11_PIN = "pos_11_pin"
CONF_POS_12_PIN = "pos_12_pin" CONF_POS_12_PIN = "pos_12_pin"
# CONF_DEMO_MODE = "demo_mode" # CONF_DEMO_MODE = "demo_mode"
CONF_ALIGN = "align" CONF_ALIGN = "align"
CONF_EFFECT_CYCLE_NUM = "effect_cycle_num"
CONF_CYCLE_NUM = "cycle_num" CONF_CYCLE_NUM = "cycle_num"
CONF_OFF = "off" CONF_OFF = "off"
CONF_REPEAT_INTERVAL = "repeat_interval"
CONF_REPEAT_NUM = "repeat_num"
CONF_SCROLL_FONT = "scroll_font" CONF_SCROLL_FONT = "scroll_font"
max6921_ns = cg.esphome_ns.namespace("max6921") max6921_ns = cg.esphome_ns.namespace("max6921")
MAX6921Component = max6921_ns.class_( MAX6921Component = max6921_ns.class_(
"MAX6921Component", cg.PollingComponent, spi.SPIDevice "MAX6921Component", cg.PollingComponent, spi.SPIDevice
@ -208,13 +210,27 @@ async def max6921_set_brightness_to_code(config, action_id, template_arg, args):
def validate_action_set_text(value): def validate_action_set_text(value):
# duration = value.get(CONF_DURATION) duration = value.get(CONF_DURATION)
# cycle_num = value.get(CONF_CYCLE_NUM) effect_cycle_num = value.get(CONF_EFFECT_CYCLE_NUM)
# if not isinstance(duration, cv.Lambda) and not isinstance(cycle_num, cv.Lambda): repeat_interval = value.get(CONF_REPEAT_INTERVAL)
# if duration.total_milliseconds > 0 and cycle_num > 0: repeat_num = value.get(CONF_REPEAT_NUM)
# raise cv.Invalid( if (
# f"Only one of following config value must be set: {CONF_CYCLE_NUM}, {CONF_DURATION}" not isinstance(duration, cv.Lambda)
# ) and not isinstance(effect_cycle_num, cv.Lambda)
and not isinstance(repeat_interval, cv.Lambda)
and not isinstance(repeat_num, cv.Lambda)
):
if ((repeat_interval.total_milliseconds > 0) or (repeat_num > 0)) and (
(effect_cycle_num == 0) and (duration.total_milliseconds == 0)
):
raise cv.Invalid(
f'If "{CONF_REPEAT_INTERVAL}" or "{CONF_REPEAT_NUM}" is non-zero, '
f'then "{CONF_EFFECT_CYCLE_NUM}" or "{CONF_DURATION}" must be non-zero too'
)
if (repeat_num > 0) and (repeat_interval.total_milliseconds == 0):
raise cv.Invalid(
f'If "{CONF_REPEAT_NUM}" is non-zero, then "{CONF_REPEAT_INTERVAL}" must be non-zero too'
)
return value return value
@ -234,10 +250,16 @@ ACTION_SET_TEXT_SCHEMA = cv.All(
cv.positive_time_period_milliseconds cv.positive_time_period_milliseconds
), ),
cv.Optional(CONF_EFFECT, default="none"): cv.templatable(cv.string), cv.Optional(CONF_EFFECT, default="none"): cv.templatable(cv.string),
cv.Optional(CONF_EFFECT_CYCLE_NUM, default=0): cv.templatable(
cv.uint8_t
),
cv.Optional(CONF_REPEAT_INTERVAL, default="0ms"): cv.templatable(
cv.positive_time_period_milliseconds
),
cv.Optional(CONF_REPEAT_NUM, default=0): cv.templatable(cv.uint8_t),
cv.Optional(CONF_UPDATE_INTERVAL, default="150ms"): cv.templatable( cv.Optional(CONF_UPDATE_INTERVAL, default="150ms"): cv.templatable(
cv.positive_time_period_milliseconds cv.positive_time_period_milliseconds
), ),
cv.Optional(CONF_CYCLE_NUM, default=0): cv.templatable(cv.uint8_t),
} }
) )
) )
@ -264,12 +286,18 @@ async def max6921_set_text_to_code(config, action_id, template_arg, args):
if CONF_EFFECT in config: if CONF_EFFECT in config:
template_ = await cg.templatable(config[CONF_EFFECT], args, cg.std_string) template_ = await cg.templatable(config[CONF_EFFECT], args, cg.std_string)
cg.add(var.set_text_effect(template_)) cg.add(var.set_text_effect(template_))
if CONF_EFFECT_CYCLE_NUM in config:
template_ = await cg.templatable(config[CONF_EFFECT_CYCLE_NUM], args, cg.uint8)
cg.add(var.set_text_effect_cycle_num(template_))
if CONF_REPEAT_NUM in config:
template_ = await cg.templatable(config[CONF_REPEAT_NUM], args, cg.uint8)
cg.add(var.set_text_repeat_num(template_))
if CONF_REPEAT_INTERVAL in config:
template_ = await cg.templatable(config[CONF_REPEAT_INTERVAL], args, cg.uint32)
cg.add(var.set_text_repeat_interval(template_))
if CONF_UPDATE_INTERVAL in config: if CONF_UPDATE_INTERVAL in config:
template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, cg.uint32) template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, cg.uint32)
cg.add(var.set_text_effect_update_interval(template_)) cg.add(var.set_text_effect_update_interval(template_))
if CONF_CYCLE_NUM in config:
template_ = await cg.templatable(config[CONF_CYCLE_NUM], args, cg.uint8)
cg.add(var.set_text_effect_cycle_num(template_))
return var return var