Implement communication with ProntoKey devices

This commit is contained in:
ninjadev64 committed 2023-11-18 17:12:18 +00:00
1 parent 3b648014a0
commit 4e7be5f1d9
5 files changed
+234 -3

No files matched your search

+1
View File
@@ -7,6 +7,7 @@
"": {
"name": "opendeck",
"version": "2.0.0",
"license": "BSD-3-Clause",
"devDependencies": {
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.20.4",
+78
View File
@@ -2,6 +2,27 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "CoreFoundation-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b"
dependencies = [
"libc",
"mach",
]
[[package]]
name = "IOKit-sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a"
dependencies = [
"CoreFoundation-sys",
"libc",
"mach",
]
[[package]]
name = "addr2line"
version = "0.21.0"
@@ -1823,6 +1844,26 @@ dependencies = [
"redox_syscall 0.4.1",
]
[[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 = "line-wrap"
version = "0.1.1"
@@ -1894,6 +1935,24 @@ dependencies = [
"time",
]
[[package]]
name = "mach"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9"
dependencies = [
"libc",
]
[[package]]
name = "mach2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
dependencies = [
"libc",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
@@ -2221,6 +2280,7 @@ version = "2.0.0"
dependencies = [
"serde",
"serde_json",
"serialport",
"tauri",
"tauri-build",
]
@@ -3140,6 +3200,24 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "serialport"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c32634e2bd4311420caa504404a55fad2131292c485c97014cbed89a5899885f"
dependencies = [
"CoreFoundation-sys",
"IOKit-sys",
"bitflags 1.3.2",
"cfg-if",
"libudev",
"mach2",
"nix",
"regex",
"scopeguard",
"winapi",
]
[[package]]
name = "servo_arc"
version = "0.1.1"
+1
View File
@@ -16,6 +16,7 @@ tauri-build = { version = "1.5.0", features = [] }
serde_json = "1.0"
serde = { version = "1.0", features = [ "derive" ] }
tauri = { version = "1.5.2", features = [ "system-tray", "api-all" ] }
serialport = "4.2.2"
[features]
custom-protocol = [ "tauri/custom-protocol" ]
+146
View File
@@ -0,0 +1,146 @@
use std::thread;
use std::time::Duration;
use serde_json::Value;
/// Implemented by all supported devices.
trait BaseDevice {
fn num_keys(&self) -> u8;
fn num_dials(&self) -> u8;
fn key_down(&self, key: u8);
fn key_up(&self, key: u8);
fn dial_rotate(&self, dial: u8, ticks: i16);
}
/// A representation of a ProntoKey device.
struct ProntoKeyDevice {
address: String
}
impl BaseDevice for ProntoKeyDevice {
fn num_keys(&self) -> u8 { 9 }
fn num_dials(&self) -> u8 { 2 }
fn key_down(&self, key: u8) {
println!("{} down {}", &self.address, key);
}
fn key_up(&self, key: u8) {
println!("{} up {}", &self.address, key);
}
fn dial_rotate(&self, dial: u8, ticks: i16) {
println!("{} rotate {} by {}", &self.address, dial, ticks);
}
}
impl ProntoKeyDevice {
/// Attempt to open a serial connection with the device and handle incoming data.
fn init(port: String) {
thread::spawn(|| {
let mut device: Option<ProntoKeyDevice> = Option::None;
let mut last_key: u8 = 0;
let mut last_sliders: Vec<i16> = vec![0; 2];
let mut port = match
serialport::new(port, 57600)
.timeout(Duration::from_millis(10))
.open()
{
Ok(p) => p,
Err(e) => panic!("Failed to open serial port: {}", e.description)
};
let _ = port.write("register".as_bytes());
let mut serial_buf: Vec<u8> = vec![0; 1024];
let mut holding_string: String = "".to_owned();
loop {
match port.read(serial_buf.as_mut_slice()) {
Ok(t) => {
match std::str::from_utf8(&serial_buf[..t]) {
Ok(data) => holding_string += data,
Err(_) => break
}
// Iterate through JSON objects received from device that are being held in the buffer.
while holding_string.contains("}") {
let index = holding_string.find("}").unwrap_or_default();
let chunk = holding_string[..=index].trim();
let j: Value = match serde_json::from_str(chunk) {
Ok(data) => data,
Err(_) => continue
};
holding_string = holding_string[(index + 1)..].to_owned();
// If the device is uninitialised, attempt to read its MAC address and initialise.
if device.is_none() {
match &j["address"] {
Value::String(address) => {
device = Some(ProntoKeyDevice { address: address.clone() });
},
_ => {}
}
continue;
}
let device = device.as_ref().unwrap();
// Handle key presses and releases.
match &j["key"] {
Value::Number(num) => {
match num.as_u64().unwrap_or_default() as u8 {
0 => device.key_up(last_key),
val => {
device.key_down(val);
last_key = val;
}
}
},
_ => {}
}
// Handle slider value changes.
match &j["slider0"] {
Value::Number(val) => {
let val: i16 = match val.as_i64() {
Some(v) => v as i16,
_ => last_sliders[0]
};
device.dial_rotate(0, val - last_sliders[0]);
last_sliders[0] = val;
},
_ => ()
}
match &j["slider1"] {
Value::Number(val) => {
let val: i16 = match val.as_i64() {
Some(v) => v as i16,
_ => last_sliders[1]
};
device.dial_rotate(1, val - last_sliders[1]);
last_sliders[1] = val;
},
_ => {}
}
}
},
Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => (),
Err(e) => eprintln!("{:?}", e)
}
thread::sleep(Duration::from_millis(10));
}
});
}
}
/// Attempt to initialise all connected devices.
pub fn initialise_devices() {
// Iterate through available serial ports and attempt to register them as ProntoKey devices.
for port in serialport::available_ports().unwrap_or_default() {
match port.port_type {
serialport::SerialPortType::UsbPort(info) => {
if info.vid == 0x10c4 && info.pid == 0xea60 {
ProntoKeyDevice::init(port.port_name);
}
}
_ => {}
}
}
}
+8 -3
View File
@@ -1,8 +1,13 @@
// Prevents additional console window on Windows in release
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
mod devices;
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
devices::initialise_devices();
match tauri::Builder::default().run(tauri::generate_context!()) {
Err(e) => panic!("Error while running Tauri application: {}", e.to_string()),
_ => {}
}
}