Rename crate to escpos-vfd and add tokio support

Rename crate to escpos-vfd and add tokio support

Introduce a configurable crate-ready API with presets, typed errors,
optional Tokio async support, and expanded documentation.
This commit is contained in:
Кобелев Андрей Андреевич
2026-08-16 11:24:20 +05:00
parent efc53e6ef5
commit 244d907cc2
25 changed files with 3823 additions and 782 deletions
+33 -25
View File
@@ -2,12 +2,13 @@
mod common;
use anyhow::Result;
use std::error::Error;
use m::worker::VfdWorker;
use escpos_vfd::VfdWorker;
fn pad_left(s: &str, width: usize) -> String {
// простая подгонка ширины, чтобы при уменьшении числа не оставались хвосты
// Подгоняем ширину поля, чтобы при уменьшении числа не оставались "хвосты" от
// предыдущего значения. Например, после 1000 должно появиться " 999", а не "9990".
if s.chars().count() >= width {
s.chars().take(width).collect()
} else {
@@ -15,28 +16,34 @@ fn pad_left(s: &str, width: usize) -> String {
}
}
fn main() -> Result<()> {
// args:
// 1) port (default: /dev/cu.usbmodem101)
// 2) width (default: 20)
// 3) delay_ms (default: 200)
let mut args = common::ExampleArgs::from_env();
fn main() -> Result<(), Box<dyn Error>> {
// Аргументы:
// 1) port - serial-порт дисплея;
// 2) width - ширина строки;
// 3) delay_ms - пауза между изменениями данных.
let mut args = common::ExampleArgs::from_env()?;
let delay_ms: u64 = args.parse_or(200);
let width = args.width;
if width < 20 {
anyhow::bail!("update_at requires a display width of at least 20 columns");
let columns = args.columns;
// Координаты ниже подобраны под 20 символов. Для более узких дисплеев лучше
// сделать другой layout, поэтому пример явно сообщает об ограничении.
if columns < 20 {
return Err("update_at requires a display width of at least 20 columns".into());
}
// Worker сохраняет кэш строк. Благодаря этому `print_at` и `print_line_diff`
// согласованно обновляют только изменившиеся области.
let worker = VfdWorker::start(args.config)?;
let vfd = worker.handle();
vfd.clear();
vfd.clear()?;
// Рисуем шаблон один раз (как UI)
// Рисуем "шаблон" один раз. Дальше меняем только числовые поля внутри шаблона.
// 12345678901234567890
// Temp: __.__C RPM:____
// Load: ___% Uptime:____
let _ = vfd.print_line_diff(1, "Temp: 00.00C RPM:0000");
let _ = vfd.print_line_diff(2, "Load: 000% Up:0000s");
vfd.print_line_diff(1, "Temp: 00.00C RPM:0000")?;
vfd.print_line_diff(2, "Load: 000% Up:0000s")?;
// Координаты (1-based):
// "Temp: 00.00C ..." -> числа начинаются с x=7, длина 5 (00.00)
@@ -64,7 +71,7 @@ fn main() -> Result<()> {
let mut si = 0usize;
loop {
// чуть шевелим данные
// Чуть "шевелим" данные, чтобы на дисплее были видны частичные обновления.
t += 0.03;
if t > 29.99 {
t = 21.50;
@@ -74,21 +81,22 @@ fn main() -> Result<()> {
load = (load + 3) % 100;
uptime = uptime.wrapping_add(1);
// Важно: обновляем только куски строки в фиксированных местах
// Важно: обновляем только куски строки в фиксированных местах. Это меньше
// нагружает serial-линию и обычно выглядит спокойнее, чем полная перерисовка.
let temp_s = pad_left(&format!("{:.2}", t), temp_w);
let rpm_s = pad_left(&format!("{}", rpm), rpm_w);
let load_s = pad_left(&format!("{}", load), load_w);
let up_s = pad_left(&format!("{}", uptime % 10000), up_w);
let _ = vfd.print_at(temp_x, 1, temp_s);
let _ = vfd.print_at(rpm_x, 1, rpm_s);
vfd.print_at(temp_x, 1, temp_s)?;
vfd.print_at(rpm_x, 1, rpm_s)?;
let _ = vfd.print_at(load_x, 2, load_s);
let _ = vfd.print_at(up_x, 2, up_s);
vfd.print_at(load_x, 2, load_s)?;
vfd.print_at(up_x, 2, up_s)?;
// “живой индикатор справа (если ширина позволяет)
if width >= 20 {
let _ = vfd.print_at(20, 2, spinner[si]);
// ивой" индикатор справа показывает, что цикл продолжает выполняться.
if columns >= 20 {
vfd.print_at(20, 2, spinner[si])?;
si = (si + 1) % spinner.len();
}