diff --git a/docs/ATOL-PD_2800.-Rukovodstvo-po-ekspluatatsii.pdf b/docs/ATOL-PD_2800.-Rukovodstvo-po-ekspluatatsii.pdf new file mode 100644 index 0000000..c6b9348 Binary files /dev/null and b/docs/ATOL-PD_2800.-Rukovodstvo-po-ekspluatatsii.pdf differ diff --git a/docs/UM_PD-30xUE_Ori_17122215101.pdf b/docs/UM_PD-30xUE_Ori_17122215101.pdf new file mode 100644 index 0000000..4f6957f Binary files /dev/null and b/docs/UM_PD-30xUE_Ori_17122215101.pdf differ diff --git a/docs/USPD2800_320.pdf b/docs/USPD2800_320.pdf new file mode 100644 index 0000000..7340696 Binary files /dev/null and b/docs/USPD2800_320.pdf differ diff --git a/examples/blink.rs b/examples/blink.rs new file mode 100644 index 0000000..85c7fa7 --- /dev/null +++ b/examples/blink.rs @@ -0,0 +1,62 @@ +use anyhow::Result; +use std::time::Duration; + +use m::vfd::VfdConfig; +use m::worker::VfdWorker; + +fn spaces(n: usize) -> String { + " ".repeat(n) +} + +fn main() -> Result<()> { + // args: + // 1) port (default: /dev/cu.usbmodem101) + // 2) width (default: 20) + // 3) delay_ms (default: 400) + // 4) x (default: 1) 1-based + // 5) y (default: 1) 1-based line + // 6) text (default: "BLINK") + let mut args = std::env::args().skip(1); + + let port_name = args.next().unwrap_or("/dev/cu.usbmodem101".into()); + + let width: usize = args.next().as_deref().unwrap_or("20").parse().unwrap_or(20); + + let delay_ms: u64 = args + .next() + .as_deref() + .unwrap_or("400") + .parse() + .unwrap_or(400); + + let x: u8 = args.next().as_deref().unwrap_or("1").parse().unwrap_or(1); + + let y: u8 = args.next().as_deref().unwrap_or("1").parse().unwrap_or(1); + + let text = args.next().unwrap_or("BLINK".into()); + let text_len = text.chars().count(); + + let cfg = VfdConfig::new(port_name).with_width(width); + let worker = VfdWorker::start(cfg)?; + let vfd = worker.handle(); + + vfd.clear(); + + let _ = vfd.print_line_diff(1, "blink demo (print_at)"); + if y != 1 { + let _ = vfd.print_at(1, y, ""); // просто чтобы “активировать” строку у некоторых дисплеев + } + + let blank = spaces(text_len); + + let mut on = false; + loop { + if on { + let _ = vfd.print_at(x, y, &blank); + } else { + let _ = vfd.print_at(x, y, &text); + } + on = !on; + std::thread::sleep(Duration::from_millis(delay_ms)); + } +} diff --git a/examples/position.rs b/examples/position.rs new file mode 100644 index 0000000..f9f700f --- /dev/null +++ b/examples/position.rs @@ -0,0 +1,74 @@ +use anyhow::Result; +use std::time::Duration; + +use m::vfd::VfdConfig; +use m::worker::VfdWorker; + +fn main() -> Result<()> { + // args: + // 1) port (default: /dev/cu.usbmodem101) + // 2) width (default: 20) + // 3) delay_ms (default: 80) + let port_name = std::env::args() + .nth(1) + .unwrap_or("/dev/cu.usbmodem101".into()); + + let width: usize = std::env::args() + .nth(2) + .as_deref() + .unwrap_or("20") + .parse() + .unwrap_or(20); + + let delay_ms: u64 = std::env::args() + .nth(3) + .as_deref() + .unwrap_or("80") + .parse() + .unwrap_or(80); + + let cfg = VfdConfig::new(port_name).with_width(width); + let worker = VfdWorker::start(cfg)?; + let vfd = worker.handle(); + + vfd.clear(); + + // Статика + let _ = vfd.print_line_diff(1, "print_at demo"); + let _ = vfd.print_line_diff(2, "--------------------"); // будет обрезано по width + + // “курсор” бегает по 2-й строке + let y = 2u8; + let mut x: u8 = 1; + let mut dir: i8 = 1; + + // чтобы не оставлять хвост — помним прошлую позицию и стираем её пробелом + let mut prev_x: u8 = x; + + loop { + // стереть прошлую позицию + let _ = vfd.print_at(prev_x, y, " "); + + // нарисовать новую + let _ = vfd.print_at(x, y, "█"); // можно заменить на "*" если надо + + prev_x = x; + + // шаг + if dir > 0 { + if (x as usize) >= width { + dir = -1; + } else { + x += 1; + } + } else { + if x <= 1 { + dir = 1; + } else { + x -= 1; + } + } + + std::thread::sleep(Duration::from_millis(delay_ms)); + } +} diff --git a/examples/update_at.rs b/examples/update_at.rs new file mode 100644 index 0000000..3ed9e30 --- /dev/null +++ b/examples/update_at.rs @@ -0,0 +1,108 @@ +use anyhow::Result; +use std::time::Duration; + +use m::vfd::VfdConfig; +use m::worker::VfdWorker; + +fn pad_left(s: &str, width: usize) -> String { + // простая подгонка ширины, чтобы при уменьшении числа не оставались “хвосты” + if s.chars().count() >= width { + s.chars().take(width).collect() + } else { + format!("{:>width$}", s, width = width) + } +} + +fn main() -> Result<()> { + // args: + // 1) port (default: /dev/cu.usbmodem101) + // 2) width (default: 20) + // 3) delay_ms (default: 200) + let port_name = std::env::args() + .nth(1) + .unwrap_or("/dev/cu.usbmodem101".into()); + + let width: usize = std::env::args() + .nth(2) + .as_deref() + .unwrap_or("20") + .parse() + .unwrap_or(20); + + let delay_ms: u64 = std::env::args() + .nth(3) + .as_deref() + .unwrap_or("200") + .parse() + .unwrap_or(200); + + let cfg = VfdConfig::new(port_name).with_width(width); + let worker = VfdWorker::start(cfg)?; + let vfd = worker.handle(); + + 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"); + + // Координаты (1-based): + // "Temp: 00.00C ..." -> числа начинаются с x=7, длина 5 (00.00) + let temp_x = 7u8; + let temp_w = 5usize; + + // "RPM:0000" -> числа начинаются после "RPM:"; в строке выше это x=16, длина 4 + let rpm_x = 16u8; + let rpm_w = 4usize; + + // "Load: 000%" -> числа x=7, длина 3 + let load_x = 7u8; + let load_w = 3usize; + + // "Up:0000s" -> числа x=12, длина 4 + let up_x = 12u8; + let up_w = 4usize; + + let mut t: f32 = 21.50; + let mut rpm: u32 = 900; + let mut load: u32 = 10; + let mut uptime: u32 = 0; + + let spinner = ["|", "/", "-", "\\"]; + let mut si = 0usize; + + loop { + // чуть “шевелим” данные + t += 0.03; + if t > 29.99 { + t = 21.50; + } + + rpm = (rpm + 37) % 9999; + load = (load + 3) % 100; + uptime = uptime.wrapping_add(1); + + // Важно: обновляем только куски строки в фиксированных местах + 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); + + let _ = vfd.print_at(load_x, 2, load_s); + let _ = vfd.print_at(up_x, 2, up_s); + + // “живой” индикатор справа (если ширина позволяет) + if width >= 20 { + let _ = vfd.print_at(20, 2, spinner[si]); + si = (si + 1) % spinner.len(); + } + + std::thread::sleep(Duration::from_millis(delay_ms)); + } +} diff --git a/taskfile.yml b/taskfile.yml index 3d89da1..7ebe28c 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -18,8 +18,8 @@ tasks: - cargo run --example marquee -- {{.VFD_PORT}} {{.VFD_WIDTH}} {{.VFD_CPS | default "8"}} {{.VFD_END_PAUSE_MS | default "1500"}} {{.VFD_BRIGHTNESS | default "2"}} vars: VFD_CPS: "8" - VFD_END_PAUSE_MS: "1500" - VFD_BRIGHTNESS: "2" + VFD_END_PAUSE_MS: "1000" + VFD_BRIGHTNESS: "3" vfd:brightness: desc: Run VFD brightness example @@ -27,3 +27,27 @@ tasks: - cargo run --example brightness -- {{.VFD_PORT}} {{.VFD_WIDTH}} {{.VFD_DELAY_MS | default "800"}} vars: VFD_DELAY_MS: "800" + + vfd:position: + desc: Run VFD print_at position demo + cmds: + - cargo run --example position -- {{.VFD_PORT}} {{.VFD_WIDTH}} {{.VFD_DELAY_MS | default "80"}} + vars: + VFD_DELAY_MS: "80" + + vfd:update_at: + desc: Run VFD partial updates demo (print_at) + cmds: + - cargo run --example update_at -- {{.VFD_PORT}} {{.VFD_WIDTH}} {{.VFD_DELAY_MS | default "200"}} + vars: + VFD_DELAY_MS: "200" + + vfd:blink: + desc: Run VFD blink demo (print_at) + cmds: + - cargo run --example blink -- {{.VFD_PORT}} {{.VFD_WIDTH}} {{.VFD_DELAY_MS | default "400"}} {{.VFD_X | default "1"}} {{.VFD_Y | default "2"}} {{.VFD_TEXT | default "BLINK"}} + vars: + VFD_DELAY_MS: "400" + VFD_X: "1" + VFD_Y: "2" + VFD_TEXT: "BLINK"