2026-10-31 17:29:09 +01:00
|
|
|
#include "samsung_vfd.h"
|
2026-10-31 17:55:36 +01:00
|
|
|
#include "charmaps.h"
|
2026-10-31 17:29:09 +01:00
|
|
|
|
|
|
|
|
#include "esphome/core/log.h"
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
2026-10-31 17:55:36 +01:00
|
|
|
#include <cmath>
|
2026-10-31 17:29:09 +01:00
|
|
|
|
|
|
|
|
namespace esphome {
|
|
|
|
|
namespace samsung_vfd {
|
|
|
|
|
|
2026-10-31 17:55:36 +01:00
|
|
|
// Unaccented stand-ins for U+00C0..U+00FF (À..ÿ), one per code point. Used when
|
|
|
|
|
// the selected code table has no glyph for a character.
|
2026-10-31 17:29:09 +01:00
|
|
|
static const char LATIN1_FOLD[] = "AAAAAAACEEEEIIIIDNOOOOOxOUUUUYPs"
|
|
|
|
|
"aaaaaaaceeeeiiiidnooooo/ouuuuypy";
|
|
|
|
|
|
2026-10-31 17:55:36 +01:00
|
|
|
static const char *const CODE_TABLE_NAMES[] = {"PC437", "PC850", "PC866", "PC737",
|
|
|
|
|
"PC775", "CP10", "PC864", "PC852"};
|
|
|
|
|
|
2026-10-31 17:29:09 +01:00
|
|
|
void SamsungVfd::setup() {
|
|
|
|
|
this->resize_lines_();
|
|
|
|
|
this->init_display_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::dump_config() {
|
|
|
|
|
ESP_LOGCONFIG(TAG, "Samsung VFD:");
|
|
|
|
|
ESP_LOGCONFIG(TAG, " Size: %u cols x %u rows", this->cols_, this->rows_);
|
|
|
|
|
ESP_LOGCONFIG(TAG, " Newline between lines: %s", YESNO(this->send_newlines_));
|
2026-10-31 17:55:36 +01:00
|
|
|
ESP_LOGCONFIG(TAG, " Brightness level: %u/4", this->brightness_level_);
|
|
|
|
|
ESP_LOGCONFIG(TAG, " Code table: %s", CODE_TABLE_NAMES[this->code_table_ & 7]);
|
|
|
|
|
ESP_LOGCONFIG(TAG, " International charset: %u", this->charset_);
|
|
|
|
|
ESP_LOGCONFIG(TAG, " User characters: %u", (unsigned) this->user_chars_.size());
|
2026-10-31 17:29:09 +01:00
|
|
|
}
|
|
|
|
|
|
2026-10-31 17:55:36 +01:00
|
|
|
// ---------- low-level output ----------
|
2026-10-31 17:29:09 +01:00
|
|
|
|
|
|
|
|
void SamsungVfd::write_str_(const std::string &s) {
|
|
|
|
|
if (!s.empty())
|
|
|
|
|
this->write_array(reinterpret_cast<const uint8_t *>(s.data()), s.size());
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-31 17:55:36 +01:00
|
|
|
void SamsungVfd::send_esc_(uint8_t cmd) {
|
|
|
|
|
const uint8_t b[] = {CMD_ESC, cmd};
|
|
|
|
|
this->write_array(b, sizeof(b));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::send_esc_(uint8_t cmd, uint8_t arg) {
|
|
|
|
|
const uint8_t b[] = {CMD_ESC, cmd, arg};
|
|
|
|
|
this->write_array(b, sizeof(b));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::send_us_(uint8_t cmd) {
|
|
|
|
|
const uint8_t b[] = {CMD_US, cmd};
|
|
|
|
|
this->write_array(b, sizeof(b));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::send_us_(uint8_t cmd, uint8_t arg) {
|
|
|
|
|
const uint8_t b[] = {CMD_US, cmd, arg};
|
|
|
|
|
this->write_array(b, sizeof(b));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------- initialisation ----------
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::init_display_() {
|
|
|
|
|
// ESC @ resets every setting, clears the screen and homes the cursor.
|
|
|
|
|
this->send_esc_(ESC_INIT);
|
|
|
|
|
this->apply_settings_();
|
|
|
|
|
// Force the next flush to retransmit whatever is in the shadow buffer.
|
|
|
|
|
this->invalidate_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::apply_settings_() {
|
|
|
|
|
this->send_esc_(ESC_CODE_TABLE, this->code_table_);
|
|
|
|
|
this->send_esc_(ESC_INTL_CHARSET, this->charset_);
|
|
|
|
|
this->send_user_chars_();
|
|
|
|
|
this->send_us_(US_BRIGHTNESS, this->brightness_level_);
|
|
|
|
|
this->send_us_(US_BLINK, this->on_ ? this->blink_ : BLINK_BLANK);
|
|
|
|
|
this->send_annunciators_();
|
|
|
|
|
if (this->mode_ != MODE_OVERWRITE)
|
|
|
|
|
this->send_us_(this->mode_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::send_user_chars_() {
|
|
|
|
|
if (this->user_chars_.empty())
|
|
|
|
|
return;
|
|
|
|
|
for (const auto &uc : this->user_chars_) {
|
|
|
|
|
// ESC & s n m a p1..p5: s = 1 byte per column, one character (n = m), 5 columns.
|
|
|
|
|
const uint8_t b[] = {CMD_ESC, ESC_DEFINE_CHAR, 0x01, uc.code, uc.code, 0x05};
|
|
|
|
|
this->write_array(b, sizeof(b));
|
|
|
|
|
this->write_array(uc.columns.data(), uc.columns.size());
|
|
|
|
|
}
|
|
|
|
|
this->send_esc_(ESC_USER_FONT_SET, 0x01);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::send_annunciators_() {
|
|
|
|
|
const uint32_t all = 0x1FFFFE; // columns 1..20
|
|
|
|
|
if ((this->annunciators_ & all) == all) {
|
|
|
|
|
const uint8_t b[] = {CMD_US, US_ANNUNCIATOR, 0x01, 0x00};
|
|
|
|
|
this->write_array(b, sizeof(b));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (uint8_t col = 1; col <= 20; col++) {
|
|
|
|
|
if (this->annunciators_ & (1UL << col)) {
|
|
|
|
|
const uint8_t b[] = {CMD_US, US_ANNUNCIATOR, 0x01, col};
|
|
|
|
|
this->write_array(b, sizeof(b));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::reinit() {
|
|
|
|
|
this->init_display_();
|
|
|
|
|
this->flush_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------- text encoding ----------
|
|
|
|
|
|
|
|
|
|
char SamsungVfd::encode_codepoint_(uint32_t cp) const {
|
|
|
|
|
if (this->code_table_ < sizeof(CHARMAPS) / sizeof(CHARMAPS[0])) {
|
|
|
|
|
const uint16_t *map = CHARMAPS[this->code_table_];
|
|
|
|
|
if (map != nullptr) {
|
|
|
|
|
for (uint8_t i = 0; i < 128; i++) {
|
|
|
|
|
if (map[i] == cp)
|
|
|
|
|
return static_cast<char>(0x80 + i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (cp == 0xA0)
|
|
|
|
|
return ' ';
|
|
|
|
|
if (cp >= 0xC0 && cp <= 0xFF)
|
|
|
|
|
return LATIN1_FOLD[cp - 0xC0];
|
|
|
|
|
return '?';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string SamsungVfd::encode(const std::string &text, bool keep_newlines) const {
|
2026-10-31 17:29:09 +01:00
|
|
|
std::string out;
|
|
|
|
|
out.reserve(text.size());
|
2026-10-31 17:55:36 +01:00
|
|
|
size_t i = 0;
|
|
|
|
|
while (i < text.size()) {
|
2026-10-31 17:29:09 +01:00
|
|
|
auto c = static_cast<uint8_t>(text[i]);
|
2026-10-31 17:55:36 +01:00
|
|
|
uint32_t cp;
|
|
|
|
|
size_t len;
|
|
|
|
|
if (c < 0x80) {
|
|
|
|
|
cp = c;
|
|
|
|
|
len = 1;
|
|
|
|
|
} else if ((c & 0xE0) == 0xC0) {
|
|
|
|
|
cp = c & 0x1F;
|
|
|
|
|
len = 2;
|
|
|
|
|
} else if ((c & 0xF0) == 0xE0) {
|
|
|
|
|
cp = c & 0x0F;
|
|
|
|
|
len = 3;
|
|
|
|
|
} else if ((c & 0xF8) == 0xF0) {
|
|
|
|
|
cp = c & 0x07;
|
|
|
|
|
len = 4;
|
|
|
|
|
} else {
|
|
|
|
|
i++; // stray continuation byte
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool valid = i + len <= text.size();
|
|
|
|
|
for (size_t k = 1; valid && k < len; k++) {
|
|
|
|
|
auto cc = static_cast<uint8_t>(text[i + k]);
|
|
|
|
|
if ((cc & 0xC0) != 0x80)
|
|
|
|
|
valid = false;
|
|
|
|
|
else
|
|
|
|
|
cp = (cp << 6) | (cc & 0x3F);
|
|
|
|
|
}
|
|
|
|
|
if (!valid) {
|
|
|
|
|
out.push_back('?');
|
|
|
|
|
i++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
i += len;
|
|
|
|
|
|
|
|
|
|
if (cp == '\n') {
|
2026-10-31 17:29:09 +01:00
|
|
|
out.push_back(keep_newlines ? '\n' : ' ');
|
2026-10-31 17:55:36 +01:00
|
|
|
} else if (cp < 0x20 || cp == 0x7F) {
|
2026-10-31 17:29:09 +01:00
|
|
|
out.push_back(' ');
|
2026-10-31 17:55:36 +01:00
|
|
|
} else if (cp < 0x80) {
|
|
|
|
|
out.push_back(static_cast<char>(cp));
|
|
|
|
|
} else {
|
|
|
|
|
out.push_back(this->encode_codepoint_(cp));
|
2026-10-31 17:29:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-31 17:55:36 +01:00
|
|
|
// ---------- buffered text ----------
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::resize_lines_() { this->lines_.assign(this->rows_, std::string(this->cols_, ' ')); }
|
|
|
|
|
|
2026-10-31 17:29:09 +01:00
|
|
|
std::string SamsungVfd::fit_(const std::string &text, bool centered) const {
|
|
|
|
|
std::string line = text.substr(0, this->cols_);
|
|
|
|
|
if (centered && line.size() < this->cols_)
|
|
|
|
|
line.insert(0, (this->cols_ - line.size()) / 2, ' ');
|
|
|
|
|
line.resize(this->cols_, ' ');
|
|
|
|
|
return line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::print(const std::string &raw, bool centered) {
|
|
|
|
|
this->resize_lines_();
|
2026-10-31 17:55:36 +01:00
|
|
|
const std::string text = this->encode(raw, true);
|
2026-10-31 17:29:09 +01:00
|
|
|
|
|
|
|
|
// Lay the text out over the lines: split on explicit newlines, then wrap each
|
|
|
|
|
// part into line-width chunks, breaking at the last space where possible.
|
|
|
|
|
uint8_t row = 0;
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
while (row < this->rows_) {
|
|
|
|
|
size_t nl = text.find('\n', i);
|
|
|
|
|
size_t end = (nl == std::string::npos) ? text.size() : nl;
|
|
|
|
|
|
|
|
|
|
// A part always occupies at least one line, so "A\nB" puts B on line 2.
|
|
|
|
|
do {
|
|
|
|
|
size_t n = std::min<size_t>(this->cols_, end - i);
|
|
|
|
|
size_t next = i + n;
|
|
|
|
|
if (next < end && text[next] != ' ') {
|
|
|
|
|
size_t sp = text.rfind(' ', next);
|
|
|
|
|
if (sp != std::string::npos && sp > i) {
|
|
|
|
|
n = sp - i;
|
|
|
|
|
next = sp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this->lines_[row++] = this->fit_(text.substr(i, n), centered);
|
|
|
|
|
i = next;
|
|
|
|
|
while (i < end && text[i] == ' ')
|
|
|
|
|
i++; // a wrapped line never starts with a space
|
|
|
|
|
} while (i < end && row < this->rows_);
|
|
|
|
|
|
|
|
|
|
if (nl == std::string::npos)
|
|
|
|
|
break;
|
|
|
|
|
i = nl + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->flush_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::print_line(uint8_t line, const std::string &text, bool centered) {
|
|
|
|
|
if (line >= this->rows_) {
|
|
|
|
|
ESP_LOGW(TAG, "print_line: line %u out of range (has %u lines)", line, this->rows_);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (this->lines_.size() < this->rows_)
|
|
|
|
|
this->resize_lines_();
|
|
|
|
|
|
2026-10-31 17:55:36 +01:00
|
|
|
this->lines_[line] = this->fit_(this->encode(text, false), centered);
|
2026-10-31 17:29:09 +01:00
|
|
|
this->flush_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::clear() {
|
|
|
|
|
this->resize_lines_();
|
2026-10-31 17:55:36 +01:00
|
|
|
this->invalidate_();
|
2026-10-31 17:29:09 +01:00
|
|
|
this->write_byte(CMD_CLEAR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::flush_() {
|
|
|
|
|
std::string payload;
|
|
|
|
|
for (uint8_t r = 0; r < this->rows_; r++) {
|
|
|
|
|
if (r != 0 && this->send_newlines_)
|
|
|
|
|
payload += "\r\n";
|
|
|
|
|
payload += this->lines_[r];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (payload == this->last_payload_)
|
|
|
|
|
return; // nothing changed -> no re-send
|
|
|
|
|
|
|
|
|
|
// No clear: every line is padded to the full width, so going home and
|
|
|
|
|
// overwriting replaces all characters without the blank-screen flicker.
|
2026-10-31 17:55:36 +01:00
|
|
|
// In overwrite mode the cursor wraps from the end of line 1 to line 2.
|
2026-10-31 17:29:09 +01:00
|
|
|
this->write_byte(CMD_HOME);
|
|
|
|
|
this->write_str_(payload);
|
|
|
|
|
this->last_payload_ = payload;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-31 17:55:36 +01:00
|
|
|
// ---------- settings ----------
|
|
|
|
|
|
|
|
|
|
uint8_t SamsungVfd::brightness_to_level_(float b) {
|
|
|
|
|
// Levels 1..4 = 40/60/80/100 %.
|
|
|
|
|
int level = static_cast<int>(std::lround((b - 0.2f) / 0.2f));
|
|
|
|
|
return static_cast<uint8_t>(std::max(1, std::min(4, level)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::set_brightness(float brightness) {
|
|
|
|
|
this->brightness_level_ = brightness_to_level_(brightness);
|
|
|
|
|
this->send_us_(US_BRIGHTNESS, this->brightness_level_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::set_blink(uint32_t ms) {
|
|
|
|
|
// US E n: n * 13 ms on, n * 13 ms off. 0xFF means "off", so stop at 0xFE.
|
|
|
|
|
this->blink_ = ms == 0 ? BLINK_OFF : static_cast<uint8_t>(std::max<uint32_t>(1, std::min<uint32_t>(0xFE, (ms + 6) / 13)));
|
|
|
|
|
if (this->on_)
|
|
|
|
|
this->send_us_(US_BLINK, this->blink_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::turn_off() {
|
|
|
|
|
this->on_ = false;
|
|
|
|
|
this->send_us_(US_BLINK, BLINK_BLANK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::turn_on() {
|
|
|
|
|
this->on_ = true;
|
|
|
|
|
this->send_us_(US_BLINK, this->blink_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::set_annunciator(uint8_t column, bool state) {
|
|
|
|
|
if (column > 20) {
|
|
|
|
|
ESP_LOGW(TAG, "set_annunciator: column %u out of range (0 = all, 1..20)", column);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (column == 0) {
|
|
|
|
|
this->annunciators_ = state ? 0x1FFFFE : 0;
|
|
|
|
|
} else if (state) {
|
|
|
|
|
this->annunciators_ |= 1UL << column;
|
|
|
|
|
} else {
|
|
|
|
|
this->annunciators_ &= ~(1UL << column);
|
|
|
|
|
}
|
|
|
|
|
const uint8_t b[] = {CMD_US, US_ANNUNCIATOR, static_cast<uint8_t>(state ? 1 : 0), column};
|
|
|
|
|
this->write_array(b, sizeof(b));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::set_code_table(CodeTable table) {
|
|
|
|
|
this->code_table_ = table;
|
|
|
|
|
this->send_esc_(ESC_CODE_TABLE, table);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::set_international_charset(InternationalCharset charset) {
|
|
|
|
|
this->charset_ = charset;
|
|
|
|
|
this->send_esc_(ESC_INTL_CHARSET, charset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::define_char(uint8_t code, const std::array<uint8_t, 5> &columns) {
|
|
|
|
|
if (code < 0x20 || code > 0x7E) {
|
|
|
|
|
ESP_LOGW(TAG, "define_char: code 0x%02X out of range (0x20..0x7E)", code);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto it = std::find_if(this->user_chars_.begin(), this->user_chars_.end(),
|
|
|
|
|
[code](const UserChar &uc) { return uc.code == code; });
|
|
|
|
|
if (it != this->user_chars_.end()) {
|
|
|
|
|
it->columns = columns;
|
|
|
|
|
} else if (this->user_chars_.size() >= MAX_USER_CHARS) {
|
|
|
|
|
ESP_LOGW(TAG, "define_char: at most %u user characters", MAX_USER_CHARS);
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
this->user_chars_.push_back(UserChar{code, columns});
|
|
|
|
|
}
|
|
|
|
|
const uint8_t b[] = {CMD_ESC, ESC_DEFINE_CHAR, 0x01, code, code, 0x05};
|
|
|
|
|
this->write_array(b, sizeof(b));
|
|
|
|
|
this->write_array(columns.data(), columns.size());
|
|
|
|
|
this->send_esc_(ESC_USER_FONT_SET, 0x01);
|
|
|
|
|
this->invalidate_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::cancel_char(uint8_t code) {
|
|
|
|
|
this->user_chars_.erase(std::remove_if(this->user_chars_.begin(), this->user_chars_.end(),
|
|
|
|
|
[code](const UserChar &uc) { return uc.code == code; }),
|
|
|
|
|
this->user_chars_.end());
|
|
|
|
|
this->send_esc_(ESC_CANCEL_CHAR, code);
|
|
|
|
|
this->invalidate_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------- raw cursor and mode commands ----------
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::home() { this->write_byte(CMD_HOME); }
|
|
|
|
|
void SamsungVfd::cursor_left() { this->write_byte(CMD_BS); }
|
|
|
|
|
void SamsungVfd::cursor_right() { this->write_byte(CMD_HT); }
|
|
|
|
|
void SamsungVfd::cursor_down() { this->write_byte(CMD_LF); }
|
|
|
|
|
void SamsungVfd::cursor_up() { this->send_us_(US_CURSOR_UP); }
|
|
|
|
|
void SamsungVfd::cursor_line_start() { this->write_byte(CMD_CR); }
|
|
|
|
|
void SamsungVfd::cursor_line_end() { this->send_us_(US_LINE_END); }
|
|
|
|
|
void SamsungVfd::cursor_bottom() { this->send_us_(US_BOTTOM); }
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::set_cursor(uint8_t column, uint8_t line) {
|
|
|
|
|
if (column >= this->cols_ || line >= this->rows_) {
|
|
|
|
|
ESP_LOGW(TAG, "set_cursor: %u,%u out of range", column, line);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const uint8_t b[] = {CMD_US, US_SET_CURSOR, static_cast<uint8_t>(column + 1), static_cast<uint8_t>(line + 1)};
|
|
|
|
|
this->write_array(b, sizeof(b));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::clear_cursor_line() {
|
|
|
|
|
this->write_byte(CMD_CAN);
|
|
|
|
|
this->invalidate_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::set_display_mode(DisplayMode mode) {
|
|
|
|
|
this->mode_ = mode;
|
|
|
|
|
this->send_us_(mode);
|
|
|
|
|
if (mode != MODE_OVERWRITE)
|
|
|
|
|
ESP_LOGW(TAG, "Scroll mode set: print/print_line assume overwrite mode and may scroll the screen");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::select_peripheral(uint8_t n) {
|
|
|
|
|
if (n < 1 || n > 3) {
|
|
|
|
|
ESP_LOGW(TAG, "select_peripheral: %u out of range (1..3)", n);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this->send_esc_(ESC_PERIPHERAL, n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::self_test() {
|
|
|
|
|
this->send_us_(US_SELF_TEST);
|
|
|
|
|
this->invalidate_();
|
|
|
|
|
// The self-test resets every setting except user characters and ignores
|
|
|
|
|
// input for about 20 s; restore settings and the screen afterwards.
|
|
|
|
|
this->set_timeout("self_test", 21000, [this]() {
|
|
|
|
|
this->apply_settings_();
|
|
|
|
|
this->flush_();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::write_text(const std::string &text) {
|
|
|
|
|
this->write_str_(this->encode(text, false));
|
|
|
|
|
this->invalidate_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::write_raw(const std::vector<uint8_t> &data) { this->write_raw(data.data(), data.size()); }
|
|
|
|
|
|
|
|
|
|
void SamsungVfd::write_raw(const uint8_t *data, size_t len) {
|
|
|
|
|
if (len != 0)
|
|
|
|
|
this->write_array(data, len);
|
|
|
|
|
this->invalidate_();
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-31 17:29:09 +01:00
|
|
|
} // namespace samsung_vfd
|
|
|
|
|
} // namespace esphome
|