1
Fork 0

Compare commits

..

15 commits

25 changed files with 3873 additions and 89 deletions

2
.gitignore vendored
View file

@ -1 +1 @@
/target */target

65
common/Cargo.lock generated Normal file
View file

@ -0,0 +1,65 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "common"
version = "0.1.0"
dependencies = [
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[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 = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"

7
common/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "common"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1.0.219", default-features = false, features = ["serde_derive", "derive"] }

18
common/src/lib.rs Normal file
View file

@ -0,0 +1,18 @@
#![no_std]
use serde::Serialize;
use serde::Deserialize;
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct Command {
/// duty cycle, -1.0 - 1.0
pub left: f32,
/// duty cycle, -1.0 - 1.0
pub right: f32,
pub ultrasonic_enable: bool,
}
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct Telemetry {
/// inches, front ultrasonic sensor
pub distance: f32,
}

395
converter/Cargo.lock generated Normal file
View file

@ -0,0 +1,395 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anyhow"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [
"critical-section",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
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.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "cobs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
dependencies = [
"thiserror",
]
[[package]]
name = "common"
version = "0.1.0"
dependencies = [
"serde",
]
[[package]]
name = "converter"
version = "0.1.0"
dependencies = [
"anyhow",
"common",
"heapless",
"postcard",
"serde_json",
"serialport",
]
[[package]]
name = "core-foundation"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "hash32"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
dependencies = [
"atomic-polyfill",
"hash32",
"rustc_version",
"serde",
"spin",
"stable_deref_trait",
]
[[package]]
name = "io-kit-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b"
dependencies = [
"core-foundation-sys",
"mach2",
]
[[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.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "libudev"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0"
dependencies = [
"libc",
"libudev-sys",
]
[[package]]
name = "libudev-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
dependencies = [
"libc",
"pkg-config",
]
[[package]]
name = "lock_api"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "mach2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "nix"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
]
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "postcard"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c1de96e20f51df24ca73cafcc4690e044854d803259db27a00a461cb3b9d17a"
dependencies = [
"cobs",
"heapless",
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
[[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 = "serde_json"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "serialport"
version = "4.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb0bc984f6af6ef8bab54e6cf2071579ee75b9286aa9f2319a0d220c28b0a2b"
dependencies = [
"bitflags 2.9.1",
"cfg-if",
"core-foundation",
"core-foundation-sys",
"io-kit-sys",
"libudev",
"mach2",
"nix",
"scopeguard",
"unescaper",
"winapi",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unescaper"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26"
dependencies = [
"thiserror",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

12
converter/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "converter"
version = "0.1.0"
edition = "2024"
[dependencies]
serialport = "4.7.2"
postcard = "1.1.2"
common = { path = "../common" }
heapless = "0.7.17"
serde_json = "1.0.140"
anyhow = "1.0.98"

92
converter/src/main.rs Normal file
View file

@ -0,0 +1,92 @@
use std::{f32::NAN, io::{BufRead, BufReader, BufWriter, Read, Write}, net::TcpListener, sync::{Arc, Mutex}, thread::sleep, time::Duration};
use common::{Command, Telemetry};
use postcard::to_vec_cobs;
use heapless::Vec;
fn main() {
let mut port = serialport::new("/dev/ttyACM0", 115200)
.timeout(Duration::from_secs(1000))
.open().expect("port missing");
println!("connected over serial");
let command: Arc<Mutex<Command>> = Arc::new(Mutex::new(Command { left: 0.0, right: 0.0, ultrasonic_enable: true}));
let telemetry: Arc<Mutex<Telemetry>> = Arc::new(Mutex::new(Telemetry { distance: f32::NAN }));
// LabVIEW communication thread
{
let command = command.clone();
let telemetry = telemetry.clone();
std::thread::spawn(move || server(command,telemetry));
}
// Telemetry receive thread
{
let reader = port.try_clone().unwrap();
let reader = BufReader::new(reader);
let telemetry = telemetry.clone();
std::thread::spawn(move || recv_telem(telemetry, reader));
}
// Command transmit thread (main)
loop {
let command = command.lock().unwrap().clone();
let encoded: Vec<u8, 20> = to_vec_cobs(&command).unwrap();
port.write(&encoded).expect("port write fail");
sleep(Duration::from_millis(200));
}
}
fn recv_telem<T: Read>(telemetry: Arc<Mutex<Telemetry>>, mut reader: BufReader<T>) {
let mut recv_buf = std::vec::Vec::new();
loop {
let Ok(len) = reader.read_until(0, &mut recv_buf) else {continue;};
if let Ok(telem) = postcard::from_bytes_cobs::<Telemetry>(&mut recv_buf[0..len]) {
*telemetry.lock().unwrap() = telem;
}
recv_buf.clear();
}
}
fn server(command: Arc<Mutex<Command>>, telemetry: Arc<Mutex<Telemetry>>) {
let listener = TcpListener::bind("0.0.0.0:4242").unwrap();
while let Ok((stream, _)) = listener.accept() {
println!("connected over IP");
let command1 = command.clone();
let telemetry1 = telemetry.clone();
std::thread::spawn(move || {
if let Err(e) = handle_connection(command1, telemetry1, stream) {
println!("IP connection dropped: {}", e);
}
});
}
}
fn handle_connection(command: Arc<Mutex<Command>>, telemetry: Arc<Mutex<Telemetry>>, stream: std::net::TcpStream) -> anyhow::Result<()> {
let writer = stream.try_clone().unwrap();
let mut writer = BufWriter::new(writer);
let mut reader = BufReader::new(stream);
loop {
let mut data_buf = std::vec::Vec::new();
let len = reader.read_until(b'\n', &mut data_buf)?;
let Ok(new_command) = serde_json::from_slice::<Command>(&data_buf[0..len]) else {continue;};
*command.lock().unwrap() = new_command.clone();
println!("received l{} r{}", new_command.left, new_command.right);
let mut telem = telemetry.lock().unwrap().clone();
if telem.distance.is_nan() {
telem.distance = -1.0;
}
println!("sent d{}", telem.distance);
serde_json::to_writer(&mut writer, &telem).unwrap();
writer.write(&[b'\r',b'\n'])?;
writer.flush()?;
}
}

View file

1
decoder/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
decoderlib.h

355
decoder/Cargo.lock generated Normal file
View file

@ -0,0 +1,355 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "backtrace"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bytes"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "decoder"
version = "0.1.0"
dependencies = [
"tokio",
"zune-jpeg",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "libc"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi",
"windows-sys",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "pin-project-lite"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
[[package]]
name = "proc-macro2"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
"bitflags",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "syn"
version = "2.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys",
]
[[package]]
name = "tokio-macros"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zune-core"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]]
name = "zune-jpeg"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028"
dependencies = [
"zune-core",
]

11
decoder/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "decoder"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1.42.0", features = ["full"] }
zune-jpeg = "0.4.14"
[lib]
crate-type = ["cdylib","lib"]

1
decoder/WIZ.txt Normal file
View file

@ -0,0 +1 @@
uint8_t=unsigned __int8;uint16_t=unsigned __int16;uint32_t=unsigned __int32;uint64_t=unsigned __int64;int8_t=__int8;int16_t=__int16;int32_t=__int32;int64_t=__int64

16
decoder/justfile Normal file
View file

@ -0,0 +1,16 @@
build: build-lvheader
cargo build --release
build-win: build-lvheader
cargo build --release --target x86_64-pc-windows-gnu
build-lvheader:
cbindgen --crate decoder --lang c --output decoderlib.h
sed -i 's/uint64_t/unsigned __int64/g' decoderlib.h
sed -i 's/uint32_t/unsigned __int32/g' decoderlib.h
sed -i 's/uint16_t/unsigned __int16/g' decoderlib.h
sed -i 's/uint8_t/unsigned __int8/g' decoderlib.h
upload: build-win
scp decoderlib.h hz:site/lvrs
scp target/x86_64-pc-windows-gnu/release/decoder.dll hz:site/lvrs

32
decoder/src/lib.rs Normal file
View file

@ -0,0 +1,32 @@
use std::{mem::transmute, slice};
use zune_jpeg::{zune_core::{colorspace::ColorSpace, options::DecoderOptions}, JpegDecoder};
#[no_mangle]
pub extern "C" fn add(a: u64, b: u64) -> u64 {
a + b
}
#[no_mangle]
/// decode jpeg image of given length
///
/// image format is 0RGB
pub extern "C" fn decode(image: &mut u32, packet: &u8, length: u32) -> u64 {
let packet = unsafe {slice::from_raw_parts(packet, length as usize)};
let image: &mut [u8; 4*320*240] = unsafe{transmute(image)};
let options = DecoderOptions::new_fast().jpeg_set_out_colorspace(ColorSpace::RGB);
let Ok(mut frame) = JpegDecoder::new_with_options(packet, options).decode() else {
return 1; // decoder failure
};
// convert from RGB to 0RGB (and swap endianness)
for (buffer, image) in frame.chunks_exact_mut(3).zip(image.chunks_exact_mut(4)) {
image[0] = buffer[2];
image[1] = buffer[1];
image[2] = buffer[0];
image[3] = 0;
}
0 // success
}

2055
encoder/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

15
encoder/Cargo.toml Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "encoder"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.95"
battery = "0.7.8"
image = "0.25.5"
nokhwa = { version = "0.10.7", features = ["input-native"] }
openh264 = "0.6.6"
serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0.134"
tokio = { version = "1.42.0", features = ["full", "io-util"] }
zune-jpeg = "0.4.14"

150
encoder/src/main.rs Normal file
View file

@ -0,0 +1,150 @@
use std::{array::from_ref, net::SocketAddr, result, sync::Arc, thread::{self}, time::{Duration, SystemTime, UNIX_EPOCH}};
use battery::Manager;
use image::{codecs::jpeg::JpegEncoder, ImageBuffer, Rgb};
use nokhwa::{pixel_format::RgbFormat, utils::{ApiBackend, RequestedFormat, RequestedFormatType, Resolution}, Camera};
use anyhow::{Context, Ok, Result};
use serde::{Deserialize, Serialize};
use tokio::{io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, net::{TcpListener, TcpStream}, runtime::Runtime, sync::{Notify, RwLock}, task::{JoinHandle, LocalSet}, time::sleep};
fn main() -> Result<()>{
let await_frame = Arc::new(Notify::new());
let latest_frame = Arc::new(RwLock::new(Vec::new()));
{
let runtime = Runtime::new()?;
let await_frame = await_frame.clone();
let latest_frame = latest_frame.clone();
thread::spawn(move || {
let local = LocalSet::new();
local.block_on(&runtime, camera_manager(await_frame, latest_frame)).unwrap();
});
}
{
let runtime = Runtime::new()?;
let await_frame = await_frame.clone();
let latest_frame = latest_frame.clone();
thread::spawn(move || {
let local = LocalSet::new();
local.block_on(&runtime, server(await_frame, latest_frame)).unwrap();
});
}
let runtime = Runtime::new()?;
runtime.block_on(telemetry_server()).unwrap();
Ok(())
}
#[derive(Serialize)]
struct Telemetry {
battery_level: f32,
timestamp: u64,
}
#[derive(Deserialize, Debug)]
struct Control {
data: f32,
}
async fn telemetry_server() -> Result<()>{
let port = 2994;
let listener = TcpListener::bind(format!("0.0.0.0:{port}")).await?;
while let result::Result::Ok((connection, addr)) = listener.accept().await {
println!("telemetry connection from {:?}", addr);
let (receiver, mut sender) = connection.into_split();
tokio::spawn(async move {
let mut reader = BufReader::new(receiver).lines();
while let Some(line) = reader.next_line().await? {
//println!("recv: {line}");
if let result::Result::Ok(control) = serde_json::from_str::<Control>(&line) {
println!("deserialized: data is {}", control.data);
}
}
Ok(())
});
let _: JoinHandle<Result<()>> = tokio::spawn(async move {
let battery = Manager::new()?.batteries()?.nth(0).context("no battery")??;
let full = battery.energy_full();
loop {
let time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis();
let mock = Telemetry { battery_level: (battery.energy()/full).value * 100., timestamp: time as u64 } ;
let mut send = serde_json::to_vec(&mock)?;
send.push(b'\r');
send.push(b'\n');
sender.write_all(&send).await?;
sleep(Duration::from_millis(20)).await;
}
});
}
Ok(())
}
async fn camera_manager(await_frame: Arc<Notify>, latest_frame: Arc<RwLock<Vec<u8>>>) -> Result<()>{
let cameras = nokhwa::query(ApiBackend::Auto)?;
for cam in &cameras {
println!("cam {cam:?}");
}
let camera = cameras.get(1).context("no cameras")?;
println!("using: {}",camera.human_name());
let requested = RequestedFormat::new::<RgbFormat>(RequestedFormatType::HighestResolution(Resolution::new(320, 240)));
let mut camera = Camera::new(camera.index().clone(), requested)?;
camera.open_stream()?;
loop {
let frame = camera.frame()?;
let frame: ImageBuffer<Rgb<u8>, Vec<u8>> = frame.decode_image::<RgbFormat>()?;
let mut output = Vec::new();
let mut encoder = JpegEncoder::new_with_quality(&mut output, 30);
encoder.encode_image(&frame)?;
*latest_frame.write().await = output;
await_frame.notify_waiters();
}
}
async fn stream_video(await_frame: Arc<Notify>, latest_frame: Arc<RwLock<Vec<u8>>>, mut client: TcpStream) -> Result<()>{
loop {
await_frame.notified().await;
let data = latest_frame.read().await;
println!("len: {}", data.len());
let len = data.len();
// holding data across await, likely nonissue
client.write_u32(len as u32).await?;
client.write(&data).await?;
}
}
async fn server(await_frame: Arc<Notify>, latest_frame: Arc<RwLock<Vec<u8>>>) -> Result<()> {
let port = 2993;
let listener = TcpListener::bind(format!("0.0.0.0:{port}")).await?;
println!("listening on {port}");
while let result::Result::Ok((client, addr)) = listener.accept().await {
println!("connected to {:?}", addr);
tokio::task::spawn_local(stream_video(await_frame.clone(), latest_frame.clone(), client));
}
Ok(())
}

404
robot-controller/Cargo.lock generated Normal file
View file

@ -0,0 +1,404 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "arduino-hal"
version = "0.1.0"
source = "git+https://github.com/rahix/avr-hal?rev=fafaf587a32a4500239fd073f89d1b9c36b48092#fafaf587a32a4500239fd073f89d1b9c36b48092"
dependencies = [
"atmega-hal",
"avr-device",
"avr-hal-generic",
"cfg-if",
"embedded-hal 1.0.0",
"ufmt",
]
[[package]]
name = "atmega-hal"
version = "0.1.0"
source = "git+https://github.com/rahix/avr-hal?rev=fafaf587a32a4500239fd073f89d1b9c36b48092#fafaf587a32a4500239fd073f89d1b9c36b48092"
dependencies = [
"avr-device",
"avr-hal-generic",
]
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [
"critical-section",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "avr-device"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f2031240156132bd83639d86aeb5b1907e6a228d9a4b44c3e9699827e6dae"
dependencies = [
"avr-device-macros",
"bare-metal",
"cfg-if",
"vcell",
]
[[package]]
name = "avr-device-macros"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47c26fd925156183eb10e821b2ef7e06f8163f5a64a0bbe52fc896be2c6cbd3f"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "avr-hal-generic"
version = "0.1.0"
source = "git+https://github.com/rahix/avr-hal?rev=fafaf587a32a4500239fd073f89d1b9c36b48092#fafaf587a32a4500239fd073f89d1b9c36b48092"
dependencies = [
"avr-device",
"embedded-hal 0.2.7",
"embedded-hal 1.0.0",
"embedded-hal-bus",
"embedded-storage",
"nb 1.1.0",
"paste",
"ufmt",
"unwrap-infallible",
]
[[package]]
name = "bare-metal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
[[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.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "cobs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
dependencies = [
"thiserror",
]
[[package]]
name = "common"
version = "0.1.0"
dependencies = [
"serde",
]
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
"nb 0.1.3",
"void",
]
[[package]]
name = "embedded-hal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
[[package]]
name = "embedded-hal-bus"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57b4e6ede84339ebdb418cd986e6320a34b017cdf99b5cc3efceec6450b06886"
dependencies = [
"critical-section",
"embedded-hal 1.0.0",
]
[[package]]
name = "embedded-storage"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "723dce4e9f25b6e6c5f35628e144794e5b459216ed7da97b7c4b66cdb3fa82ca"
[[package]]
name = "hash32"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
dependencies = [
"atomic-polyfill",
"hash32",
"rustc_version",
"serde",
"spin",
"stable_deref_trait",
]
[[package]]
name = "lock_api"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
"nb 1.1.0",
]
[[package]]
name = "nb"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
[[package]]
name = "panic-halt"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11"
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "postcard"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c1de96e20f51df24ca73cafcc4690e044854d803259db27a00a461cb3b9d17a"
dependencies = [
"cobs",
"heapless",
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "robot-controller"
version = "0.1.0"
dependencies = [
"arduino-hal",
"avr-device",
"common",
"embedded-hal 1.0.0",
"nb 1.1.0",
"panic-halt",
"postcard",
"serde",
"ufmt",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
[[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 2.0.104",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "ufmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a64846ec02b57e9108d6469d98d1648782ad6bb150a95a9baac26900bbeab9d"
dependencies = [
"ufmt-macros",
"ufmt-write",
]
[[package]]
name = "ufmt-macros"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d337d3be617449165cb4633c8dece429afd83f84051024079f97ad32a9663716"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "ufmt-write"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69"
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unwrap-infallible"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "151ac09978d3c2862c4e39b557f4eceee2cc72150bc4cb4f16abf061b6e381fb"
[[package]]
name = "vcell"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"

View file

@ -15,6 +15,10 @@ panic-halt = "1.0.0"
ufmt = "0.2.0" ufmt = "0.2.0"
nb = "1.1.0" nb = "1.1.0"
embedded-hal = "1.0" embedded-hal = "1.0"
avr-device = "0.7.0"
serde = { version = "1.0.219", default-features = false, features = ["serde_derive", "derive"] }
postcard = "1.1.2"
common = { path = "../common" }
[dependencies.arduino-hal] [dependencies.arduino-hal]
git = "https://github.com/rahix/avr-hal" git = "https://github.com/rahix/avr-hal"
@ -32,4 +36,4 @@ panic = "abort"
codegen-units = 1 codegen-units = 1
debug = true debug = true
lto = true lto = true
opt-level = "s" opt-level = "z"

View file

@ -0,0 +1,238 @@
#![no_std]
#![no_main]
#![feature(abi_avr_interrupt)]
use core::cell::RefCell;
use core::f32;
use core::mem::MaybeUninit;
use arduino_hal::{clock::MHz16, hal::usart::UsartWriter};
use arduino_hal::hal::port::*;
use arduino_hal::hal::usart::UsartReader;
use arduino_hal::pac::USART0;
use arduino_hal::port::mode::*;
use arduino_hal::prelude::*;
use avr_device::interrupt::{self, Mutex};
use common::{Command, Telemetry};
use panic_halt as _;
fn shift_out(data_pin: &mut Pin<Output, PB0>, clock_pin: &mut Pin<Output, PD4>, data: &u8) {
for i in 0..8 {
let n = data & (1 << i);
if n == 0 {
data_pin.set_low();
} else {
data_pin.set_high();
}
clock_pin.set_high();
clock_pin.set_low();
}
}
fn update_shift_register(
data_pin: &mut Pin<Output, PB0>,
latch_pin: &mut Pin<Output, PB4>,
clock_pin: &mut Pin<Output, PD4>,
data: &u8,
) {
latch_pin.set_low();
shift_out(data_pin, clock_pin, data);
latch_pin.set_high();
}
static mut UART_RX: MaybeUninit<UsartReader<USART0, Pin<Input, PD0>, Pin<Output, PD1>, MHz16>> = MaybeUninit::uninit();
static INPUT_LINE: Mutex<RefCell<[u8; 20]>> = Mutex::new(RefCell::new([0;20]));
#[avr_device::interrupt(atmega328p)]
unsafe fn USART_RX() {
let rx = &mut *UART_RX.as_mut_ptr();
static mut BUF: [u8; 20] = [0;20];
static mut N: usize = 0; // index into line
if let Ok(val) = rx.read() {
if val == b'\0' {
interrupt::free(|cs| {
let mut line = INPUT_LINE.borrow(cs).borrow_mut();
*line = BUF;
line[N] = b'\0';
});
N = 0;
} else {
BUF[N] = val;
N += 1;
if N == 20 {
N = 0;
}
}
}
}
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
// shift register
let mut data_pin = pins.d8.into_output();
let mut clock_pin = pins.d4.into_output();
let mut enable_pin = pins.d7.into_output();
let mut latch_pin = pins.d12.into_output();
enable_pin.set_low(); // enable outputs
pins.d3.into_output(); // right
pins.d11.into_output(); // left
// TCCR2A |= _BV(COM2A1) | _BV(WGM20) | _BV(WGM21); // fast PWM, turn on oc2a
// TCCR2B = freq & 0x7;
// OCR2A = 0;
let tc2 = dp.TC2;
tc2.tccr2a.write(|w| { w
.wgm2().pwm_fast()
.com2a().match_clear()
.com2b().match_clear()
});
tc2.tccr2b.write(|w| w.cs2().prescale_8().wgm22().clear_bit());
tc2.ocr2a.write(|w| w.bits(0));
tc2.ocr2b.write(|w| w.bits(0));
let mut trig = pins.a4.into_output();
let echo = pins.a5;
let ultrasonic_timer = dp.TC1;
// 4us per tick
ultrasonic_timer.tccr1b.write(|w| w.cs1().prescale_64());
let mut led = pins.d13.into_output();
let mut serial = arduino_hal::default_serial!(dp, pins, 115200);
avr_device::interrupt::disable();
serial.listen(arduino_hal::hal::usart::Event::RxComplete);
let (rx,mut tx) = serial.split();
unsafe {
UART_RX = MaybeUninit::new(rx);
avr_device::interrupt::enable();
};
let mut telem = Telemetry { distance: f32::NAN };
loop {
let mut shift_register = 0;
const LEFT_FWD: u8 = 1 << 5; // 1a
const LEFT_REV: u8 = 1 << 4; // 1b
const RIGHT_FWD: u8 = 1 << 3; // 2b
const RIGHT_REV: u8 = 1 << 6; // 2a
led.toggle();
let command = decode_command();
if command.left > 0. {
shift_register |= LEFT_FWD;
}
if command.left < 0. {
shift_register |= LEFT_REV;
}
if command.right > 0. {
shift_register |= RIGHT_FWD;
}
if command.right < 0. {
shift_register |= RIGHT_REV;
}
fn to_pwm(val: f32) -> u8 {
(val.abs().min(1.0) * 255.0) as u8
}
// 16/255
tc2.ocr2a.write(|w| w.bits(to_pwm(command.left))); // left
tc2.ocr2b.write(|w| w.bits(to_pwm(command.right))); // right
update_shift_register(&mut data_pin, &mut latch_pin, &mut clock_pin, &shift_register);
if command.ultrasonic_enable {
// reset timer
ultrasonic_timer.tcnt1.write(|w| w.bits(0));
// send ultrasonic pulse
trig.set_high();
arduino_hal::delay_us(10);
trig.set_low();
let mut response = true;
/// 0.1s/4µs = 25,000
const SIGNAL_START_TIMEOUT: u16 = 25000;
// wait for return signal start
while echo.is_low() {
if ultrasonic_timer.tcnt1.read().bits() >= SIGNAL_START_TIMEOUT {
response = false;
break;
}
}
if response {
let remainder = ultrasonic_timer.tcnt1.read().bits();
ultrasonic_timer.tcnt1.write(|w| w.bits(0));
// Wait for the echo to get low again
while echo.is_high() {}
let counts = ultrasonic_timer.tcnt1.read().bits();
let microseconds = counts.saturating_mul(4);
// inch per 148 microseconds
telem.distance = microseconds as f32 / 148.;
if microseconds == u16::MAX { telem.distance = f32::NAN };
// delay for the rest of the return signal timeout if any is left
ultrasonic_timer.tcnt1.write(|w| w.bits(SIGNAL_START_TIMEOUT.saturating_sub(remainder + counts)));
while ultrasonic_timer.tcnt1.read().bits() <= SIGNAL_START_TIMEOUT {}
} else {
telem.distance = f32::NAN;
}
} else {
// ultrasonic disabled
telem.distance = f32::NAN;
}
let mut buf = [0;20];
let encoded = postcard::to_slice_cobs(&telem, &mut buf).unwrap();
for word in encoded {
let _ = tx.write(*word);
let _ = tx.flush();
arduino_hal::delay_us(100); // delay to ensure linux can keep up
}
if command.ultrasonic_enable {
// wait for the echoes to fade
arduino_hal::delay_ms(50);
}
}
}
fn decode_command() -> Command {
let mut input = interrupt::free(|cs| {
INPUT_LINE.borrow(cs).borrow().clone()
});
let Some(length) = input.iter().position(|n| *n == 0) else { return Default::default() };
postcard::from_bytes_cobs(&mut input[..=length]).unwrap_or(Default::default())
}

View file

@ -1,87 +0,0 @@
#![no_std]
#![no_main]
use arduino_hal::hal::port::*;
use arduino_hal::port::mode::*;
use embedded_hal::digital::OutputPin;
use panic_halt as _;
fn shift_out(data_pin: &mut Pin<Output, PB0>, clock_pin: &mut Pin<Output, PD4>, data: &u8) {
for i in 0..8 {
let n = data & (1 << i);
if n == 0 {
data_pin.set_low();
} else {
data_pin.set_high();
}
clock_pin.set_high();
clock_pin.set_low();
}
}
fn update_shift_register(
data_pin: &mut Pin<Output, PB0>,
latch_pin: &mut Pin<Output, PB4>,
clock_pin: &mut Pin<Output, PD4>,
data: &u8,
) {
latch_pin.set_low();
shift_out(data_pin, clock_pin, data);
latch_pin.set_high();
}
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
// shift register
let mut data_pin = pins.d8.into_output();
let mut clock_pin = pins.d4.into_output();
let mut enable_pin = pins.d7.into_output();
let mut latch_pin = pins.d12.into_output();
enable_pin.set_low(); // enable outputs
let _ = pins.d3.into_output(); // right
let _ = pins.d11.into_output(); // left
// TCCR2A |= _BV(COM2A1) | _BV(WGM20) | _BV(WGM21); // fast PWM, turn on oc2a
// TCCR2B = freq & 0x7;
// OCR2A = 0;
let tc2 = dp.TC2;
tc2.tccr2a.write(|w| { w
.wgm2().pwm_fast()
.com2a().match_set()
.com2b().match_set()
});
tc2.tccr2b.write(|w| w.cs2().prescale_8().foc2b().clear_bit().foc2a().clear_bit().wgm22().clear_bit());
tc2.ocr2a.write(|w| w.bits(2));
tc2.ocr2b.write(|w| w.bits(2));
let mut led = pins.d13.into_output();
loop {
let mut shift_register = 0;
const LEFT_FWD: u8 = 1 << 5; // 1a
const LEFT_REV: u8 = 1 << 4; // 1b
const RIGHT_FWD: u8 = 1 << 3; // 2b
const RIGHT_REV: u8 = 1 << 6; // 2a
led.toggle();
shift_register |= LEFT_FWD | RIGHT_FWD;
// /255
tc2.ocr2a.write(|w| w.bits(1)); // right
tc2.ocr2b.write(|w| w.bits(1)); // left
update_shift_register(&mut data_pin, &mut latch_pin, &mut clock_pin, &shift_register);
arduino_hal::delay_ms(1000);
}
}