feat: parse DMX on ESP32, relay colors via OSC on host
This commit is contained in:
parent
c6bb7aa1e3
commit
1138424c04
|
@ -64,6 +64,12 @@ version = "2.9.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -132,11 +138,21 @@ version = "0.8.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dmxusb"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"rosc",
|
||||
"serialport",
|
||||
]
|
||||
|
||||
|
@ -162,6 +178,12 @@ version = "1.70.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
|
@ -197,6 +219,18 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.4"
|
||||
|
@ -208,6 +242,22 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
|
@ -220,6 +270,12 @@ version = "0.3.32"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
|
@ -238,12 +294,43 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rosc"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd37602e1513794e952274082d074e8d31aa7f64d047e3acb746c91db40600a5"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"nom",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serialport"
|
||||
version = "4.7.2"
|
||||
|
@ -300,6 +387,37 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unescaper"
|
||||
version = "0.1.6"
|
||||
|
|
|
@ -5,4 +5,5 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.39", features = ["derive"] }
|
||||
rosc = "0.11.4"
|
||||
serialport = "4.7.2"
|
||||
|
|
99
src/main.rs
99
src/main.rs
|
@ -1,28 +1,37 @@
|
|||
use clap::Parser;
|
||||
|
||||
use serialport::{DataBits, FlowControl, Parity, SerialPort, StopBits};
|
||||
use rosc::encoder;
|
||||
use rosc::{OscMessage, OscPacket, OscType};
|
||||
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::io::{self, Write};
|
||||
use std::net::{IpAddr, Ipv6Addr, TcpListener};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
const BAUD_RATE: u32 = 250000;
|
||||
const BAUD_RATE: u32 = 115_200;
|
||||
|
||||
const MAX_SLOTS: usize = 513;
|
||||
|
||||
const MICROS_PER_SLOT: u64 = 44;
|
||||
const BREAK_MICROS: u64 = 88;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
#[arg(short, long, conflicts_with = "port")]
|
||||
list_ports: bool,
|
||||
|
||||
#[arg(short = 'h', long, default_value_t = IpAddr::V6(Ipv6Addr::LOCALHOST), conflicts_with = "list_ports")]
|
||||
osc_host: IpAddr,
|
||||
|
||||
#[arg(
|
||||
short = 'p',
|
||||
long,
|
||||
default_value_t = 9996,
|
||||
conflicts_with = "list_ports"
|
||||
)]
|
||||
osc_port: u16,
|
||||
|
||||
#[arg(required_unless_present = "list_ports")]
|
||||
port: Option<String>,
|
||||
}
|
||||
|
||||
type Packet = [u8; MAX_SLOTS];
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
|
@ -34,48 +43,60 @@ fn main() {
|
|||
return;
|
||||
}
|
||||
|
||||
let listener = TcpListener::bind((args.osc_host, args.osc_port)).unwrap();
|
||||
let (mut osc, _) = listener.accept().unwrap();
|
||||
|
||||
let mut serial = serialport::new(args.port.unwrap(), BAUD_RATE)
|
||||
.data_bits(DataBits::Eight)
|
||||
.flow_control(FlowControl::None)
|
||||
.parity(Parity::None)
|
||||
.stop_bits(StopBits::Two)
|
||||
.timeout(Duration::from_micros(MICROS_PER_SLOT))
|
||||
.open()
|
||||
.unwrap();
|
||||
|
||||
// ignore first (possibly incomplete) packet
|
||||
let _ = read_packet(&mut serial).unwrap();
|
||||
|
||||
println!("{:#?}", read_packet(&mut serial).unwrap());
|
||||
}
|
||||
|
||||
fn read_packet(serial: &mut Box<dyn SerialPort>) -> Result<Packet, io::Error> {
|
||||
let mut buf = [0; MAX_SLOTS];
|
||||
let mut pos = 0;
|
||||
|
||||
let mut color = [0; 3];
|
||||
let mut strobe = false;
|
||||
loop {
|
||||
match serial.read(&mut buf[pos..]) {
|
||||
Ok(bytes_read) => {
|
||||
pos += bytes_read;
|
||||
|
||||
if pos == MAX_SLOTS {
|
||||
break;
|
||||
}
|
||||
|
||||
match serial.read_exact(&mut color) {
|
||||
Ok(()) => {
|
||||
if color[0] == 0 && color[1] == 0 && color[2] == 0 {
|
||||
continue;
|
||||
}
|
||||
Err(err) if err.kind() == ErrorKind::Interrupted => continue,
|
||||
Err(err) if err.kind() == ErrorKind::TimedOut => {
|
||||
if pos > 0 {
|
||||
break;
|
||||
|
||||
println!("{:#?}", color);
|
||||
|
||||
let _ = send_value(&mut osc, "dmx_red".into(), color[0] as f32 / 255.0);
|
||||
let _ = send_value(&mut osc, "dmx_green".into(), color[1] as f32 / 255.0);
|
||||
let _ = send_value(&mut osc, "dmx_blue".into(), color[2] as f32 / 255.0);
|
||||
|
||||
if color[0] == 255 && color[1] == 255 && color[2] == 255 {
|
||||
if !strobe {
|
||||
let _ = send_value(&mut osc, "dmx_strobe".into(), true);
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_micros(MICROS_PER_SLOT));
|
||||
continue;
|
||||
strobe = true;
|
||||
} else {
|
||||
if strobe {
|
||||
let _ = send_value(&mut osc, "dmx_strobe".into(), false);
|
||||
}
|
||||
|
||||
strobe = false;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
thread::sleep(Duration::from_micros(BREAK_MICROS));
|
||||
}
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(buf)
|
||||
fn send_value<W, T>(w: &mut W, addr: String, v: T) -> io::Result<()>
|
||||
where
|
||||
W: Write,
|
||||
T: Into<OscType>,
|
||||
{
|
||||
let buf = encoder::encode_tcp(&OscPacket::Message(OscMessage {
|
||||
addr,
|
||||
args: vec![v.into()],
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
w.write_all(&buf[..])?;
|
||||
w.flush()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
[build]
|
||||
target = "riscv32imc-unknown-none-elf"
|
||||
|
||||
[target.riscv32imc-unknown-none-elf]
|
||||
runner = "espflash flash"
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlinkall.x",
|
||||
]
|
|
@ -0,0 +1 @@
|
|||
/target
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "uart-relay"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
esp-hal = { version = "0.23.1", features = ["esp32c3"] }
|
||||
panic-halt = "1.0.0"
|
|
@ -0,0 +1,67 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use esp_hal::uart::{self, DataBits, Parity, StopBits, UartRx};
|
||||
use esp_hal::usb_serial_jtag::UsbSerialJtag;
|
||||
use esp_hal::{main, Async};
|
||||
|
||||
use panic_halt as _;
|
||||
|
||||
const BAUD_RATE: u32 = 250_000;
|
||||
|
||||
const MAX_SLOTS: usize = 513;
|
||||
|
||||
const START_CODE: u8 = 0;
|
||||
|
||||
type Packet = [u8; MAX_SLOTS];
|
||||
|
||||
#[main]
|
||||
fn main() -> ! {
|
||||
let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
let mut rx = UartRx::new(
|
||||
peripherals.UART0,
|
||||
uart::Config::default()
|
||||
.with_baudrate(BAUD_RATE)
|
||||
.with_data_bits(DataBits::_8)
|
||||
.with_parity(Parity::None)
|
||||
.with_stop_bits(StopBits::_2)
|
||||
.with_rx_timeout(1), // slots
|
||||
)
|
||||
.unwrap()
|
||||
.with_rx(peripherals.GPIO3)
|
||||
.into_async();
|
||||
|
||||
let mut usb_serial = UsbSerialJtag::new(peripherals.USB_DEVICE);
|
||||
|
||||
// ignore first (possibly incomplete) packet
|
||||
let _ = read_packet(&mut rx).unwrap();
|
||||
|
||||
loop {
|
||||
if let Ok(packet) = read_packet(&mut rx) {
|
||||
if packet[0] == START_CODE {
|
||||
let _ = usb_serial.write_bytes(&packet[1..4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_packet(rx: &mut UartRx<'_, Async>) -> Result<Packet, uart::Error> {
|
||||
let mut buf = [0; MAX_SLOTS];
|
||||
let mut pos = 0;
|
||||
|
||||
loop {
|
||||
match rx.read_buffered_bytes(&mut buf[pos..]) {
|
||||
Ok(bytes_read) => {
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
pos += bytes_read;
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(buf)
|
||||
}
|
Loading…
Reference in New Issue