mirror of
https://github.com/marcriera/ddgo-pnp-controller.git
synced 2025-04-18 09:39:28 +02:00
Switch to evdev, split into modules
This commit is contained in:
parent
d1df749e38
commit
7e640edcc6
5 changed files with 158 additions and 133 deletions
|
@ -6,7 +6,5 @@ edition = "2021"
|
||||||
[package.metadata.cross.target.arm-unknown-linux-musleabi]
|
[package.metadata.cross.target.arm-unknown-linux-musleabi]
|
||||||
pre-build = ["apt-get update && apt-get install -y python3"]
|
pre-build = ["apt-get update && apt-get install -y python3"]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
evdev-rs = "0.6.1"
|
evdev = "0.12.1"
|
||||||
|
|
168
src/main.rs
168
src/main.rs
|
@ -1,150 +1,58 @@
|
||||||
use std::fs::File;
|
mod physical_controller;
|
||||||
use std::io::Write;
|
mod virtual_controller;
|
||||||
|
mod state;
|
||||||
|
|
||||||
use std::io::Result;
|
use std::io::Result;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Duration;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
|
|
||||||
use evdev_rs::Device;
|
use virtual_controller::ControllerModel;
|
||||||
use evdev_rs::InputEvent;
|
|
||||||
use evdev_rs::ReadFlag;
|
|
||||||
use evdev_rs::enums::EventCode;
|
|
||||||
use evdev_rs::enums::EV_KEY;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ControllerState {
|
|
||||||
power: u8,
|
|
||||||
brake: u8,
|
|
||||||
button_sl: bool,
|
|
||||||
button_st: bool,
|
|
||||||
button_a: bool,
|
|
||||||
button_b: bool,
|
|
||||||
button_c: bool,
|
|
||||||
button_d: bool,
|
|
||||||
button_up: bool,
|
|
||||||
button_down: bool,
|
|
||||||
button_left: bool,
|
|
||||||
button_right: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum ControllerModel {
|
|
||||||
NONE,
|
|
||||||
DGOC44U,
|
|
||||||
TYPE2,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let d1 = Device::new_from_path("/dev/input/event1");
|
match physical_controller::init() {
|
||||||
let d2 = Device::new_from_path("/dev/input/event2");
|
Ok(mut dev) => {
|
||||||
|
// Wait 3 seconds and get current state of the controller
|
||||||
|
println!("Press a button to select the controller model...");
|
||||||
|
sleep(Duration::from_secs(3));
|
||||||
|
let mut controller_state = physical_controller::get_state(dev);
|
||||||
|
let controller_model = virtual_controller::set_model(&controller_state);
|
||||||
|
|
||||||
match (d1, d2) {
|
// If no model selected, quit
|
||||||
(Ok(d1), Ok(d2)) => {
|
if controller_model == ControllerModel::NONE {
|
||||||
let mut controller_state: ControllerState = Default::default();
|
return Result::Ok(());
|
||||||
let mut controller_model: ControllerModel = ControllerModel::NONE;
|
}
|
||||||
|
|
||||||
// Save current time and 5 seconds in the future to check for pressed buttons later
|
// Vibrate to end selection mode
|
||||||
let start_time = Instant::now();
|
physical_controller::set_rumble(true);
|
||||||
let init_time = start_time + Duration::from_secs(5);
|
sleep(Duration::from_millis(500));
|
||||||
|
physical_controller::set_rumble(false);
|
||||||
// Turn on door light to indicate selection mode
|
|
||||||
set_lamp(true);
|
// Stop main game
|
||||||
println!("Hold a button to select the controller model...");
|
stop_game();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Process events from both input devices
|
// Process events from input devices
|
||||||
for device in [&d1, &d2] {
|
/*for mut device in [&mut d1, &mut d2] {
|
||||||
let ev = device.next_event(ReadFlag::NORMAL);
|
let evs = device.fetch_events();
|
||||||
match ev {
|
match evs {
|
||||||
Ok(ev) => read_input(ev.1, &mut controller_state),
|
Ok(evs) => {
|
||||||
|
for event in evs {
|
||||||
|
if event.event_type() == EventType::KEY {
|
||||||
|
physical_controller::read_input(&mut controller_state, Key(event.code()), event.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(_e) => (),
|
Err(_e) => (),
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// If init time has passed, try to select model or quit
|
|
||||||
if controller_model == ControllerModel::NONE && Instant::now() >= init_time {
|
|
||||||
if controller_state.button_right {
|
|
||||||
controller_model = ControllerModel::DGOC44U;
|
|
||||||
println!("Selected controller DGOC44-U, starting gadget...");
|
|
||||||
}
|
|
||||||
else if controller_state.button_left {
|
|
||||||
controller_model = ControllerModel::TYPE2;
|
|
||||||
println!("Selected controller TCPP-20009, starting gadget...");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Turn off door light and quit
|
|
||||||
set_lamp(false);
|
|
||||||
println!("No controller selected, exiting...");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Turn off door light and vibrate to end selection mode
|
|
||||||
set_lamp(false);
|
|
||||||
set_rumble(true);
|
|
||||||
sleep(Duration::from_millis(500));
|
|
||||||
set_rumble(false);
|
|
||||||
|
|
||||||
// Stop main game
|
|
||||||
stop_game();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => println!("ERROR: Could not read input devices! Stopping..."),
|
Err(_e) => println!("ERROR: Could not read input devices! Exiting."),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_input(event: InputEvent, controller: &mut ControllerState) {
|
|
||||||
// Save input status to object for easier processing
|
|
||||||
match event.event_code{
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_0)=>if event.value == 1 {controller.power = 0},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_1)=>if event.value == 1 {controller.power = 1},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_2)=>if event.value == 1 {controller.power = 2},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_3)=>if event.value == 1 {controller.power = 3},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_4)=>if event.value == 1 {controller.power = 4},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_5)=>if event.value == 1 {controller.power = 5},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_B)=>if event.value == 1 {controller.brake = 0},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_C)=>if event.value == 1 {controller.brake = 1},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_D)=>if event.value == 1 {controller.brake = 2},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_E)=>if event.value == 1 {controller.brake = 3},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_F)=>if event.value == 1 {controller.brake = 4},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_G)=>if event.value == 1 {controller.brake = 5},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_H)=>if event.value == 1 {controller.brake = 6},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_I)=>if event.value == 1 {controller.brake = 7},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_J)=>if event.value == 1 {controller.brake = 8},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_P)=>if event.value == 1 {controller.brake = 9},
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_SPACE)=>controller.button_sl = event.value != 0,
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_ENTER)=>controller.button_st = event.value != 0,
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_A)=>controller.button_a = event.value != 0,
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_Z)=>controller.button_b = event.value != 0,
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_X)=>controller.button_c = event.value != 0,
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_S)=>controller.button_d = event.value != 0,
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_UP)=>controller.button_up = event.value != 0,
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_DOWN)=>controller.button_down = event.value != 0,
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_LEFT)=>controller.button_left = event.value != 0,
|
|
||||||
EventCode::EV_KEY(EV_KEY::KEY_RIGHT)=>controller.button_right = event.value != 0,
|
|
||||||
_=>(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_lamp(status: bool) {
|
|
||||||
if let Ok(mut out) = File::create("/sys/class/leds/led2/brightness") {
|
|
||||||
out.write(if status {b"1"} else {b"0"}).ok();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
println!("WARNING: Could not set door lamp status!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_rumble(status: bool) {
|
|
||||||
if let Ok(mut out) = File::create("/sys/class/leds/led1/brightness") {
|
|
||||||
out.write(if status {b"1"} else {b"0"}).ok();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
println!("WARNING: Could not set rumble motor status!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stop_game() {
|
fn stop_game() {
|
||||||
Command::new("/etc/init.d/S99dgtype3").arg("stop").output().ok();
|
Command::new("/etc/init.d/S99dgtype3").arg("stop").output().ok();
|
||||||
}
|
}
|
81
src/physical_controller.rs
Normal file
81
src/physical_controller.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::io::Result;
|
||||||
|
|
||||||
|
use evdev::Device;
|
||||||
|
use evdev::Key;
|
||||||
|
|
||||||
|
use crate::state::ControllerState;
|
||||||
|
|
||||||
|
const USED_KEYS: [Key; 26] = [Key::KEY_0, Key::KEY_1, Key::KEY_2, Key::KEY_3, Key::KEY_4, Key::KEY_5,
|
||||||
|
Key::KEY_B, Key::KEY_C, Key::KEY_D, Key::KEY_E, Key::KEY_F, Key::KEY_G, Key::KEY_H, Key::KEY_I, Key::KEY_J, Key::KEY_P,
|
||||||
|
Key::KEY_SPACE, Key::KEY_ENTER, Key::KEY_A, Key::KEY_Z, Key::KEY_X, Key::KEY_S, Key::KEY_UP, Key::KEY_DOWN, Key::KEY_LEFT, Key::KEY_RIGHT];
|
||||||
|
|
||||||
|
pub fn init() -> Result<[Device; 2]> {
|
||||||
|
let d1 = Device::open("/dev/input/event14")?;
|
||||||
|
let d2 = Device::open("/dev/input/event14")?;
|
||||||
|
Ok([d1, d2])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_state(dev: [Device; 2]) -> ControllerState {
|
||||||
|
let mut state: ControllerState = Default::default();
|
||||||
|
for d in dev {
|
||||||
|
if let Ok(key_vals) = d.get_key_state() {
|
||||||
|
for key in USED_KEYS {
|
||||||
|
read_input(&mut state, key, key_vals.contains(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_input(controller: &mut ControllerState, key: Key, value: bool) {
|
||||||
|
// Save input status to object for processing
|
||||||
|
match key{
|
||||||
|
Key::KEY_0=>if value {controller.power = 0},
|
||||||
|
Key::KEY_1=>if value {controller.power = 1},
|
||||||
|
Key::KEY_2=>if value {controller.power = 2},
|
||||||
|
Key::KEY_3=>if value {controller.power = 3},
|
||||||
|
Key::KEY_4=>if value {controller.power = 4},
|
||||||
|
Key::KEY_5=>if value {controller.power = 5},
|
||||||
|
Key::KEY_B=>if value {controller.brake = 0},
|
||||||
|
Key::KEY_C=>if value {controller.brake = 1},
|
||||||
|
Key::KEY_D=>if value {controller.brake = 2},
|
||||||
|
Key::KEY_E=>if value {controller.brake = 3},
|
||||||
|
Key::KEY_F=>if value {controller.brake = 4},
|
||||||
|
Key::KEY_G=>if value {controller.brake = 5},
|
||||||
|
Key::KEY_H=>if value {controller.brake = 6},
|
||||||
|
Key::KEY_I=>if value {controller.brake = 7},
|
||||||
|
Key::KEY_J=>if value {controller.brake = 8},
|
||||||
|
Key::KEY_P=>if value {controller.brake = 9},
|
||||||
|
Key::KEY_SPACE=>controller.button_select = value,
|
||||||
|
Key::KEY_ENTER=>controller.button_start = value,
|
||||||
|
Key::KEY_A=>controller.button_a = value,
|
||||||
|
Key::KEY_Z=>controller.button_b = value,
|
||||||
|
Key::KEY_X=>controller.button_c = value,
|
||||||
|
Key::KEY_S=>controller.button_d = value,
|
||||||
|
Key::KEY_UP=>controller.button_up = value,
|
||||||
|
Key::KEY_DOWN=>controller.button_down = value,
|
||||||
|
Key::KEY_LEFT=>controller.button_left = value,
|
||||||
|
Key::KEY_RIGHT=>controller.button_right = value,
|
||||||
|
_=>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_lamp(status: bool) {
|
||||||
|
if let Ok(mut out) = File::create("/sys/class/leds/led2/brightness") {
|
||||||
|
out.write(if status {b"1"} else {b"0"}).ok();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println!("WARNING: Could not set door lamp status!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_rumble(status: bool) {
|
||||||
|
if let Ok(mut out) = File::create("/sys/class/leds/led1/brightness") {
|
||||||
|
out.write(if status {b"1"} else {b"0"}).ok();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println!("WARNING: Could not set rumble motor status!")
|
||||||
|
}
|
||||||
|
}
|
15
src/state.rs
Normal file
15
src/state.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ControllerState {
|
||||||
|
pub power: u8,
|
||||||
|
pub brake: u8,
|
||||||
|
pub button_select: bool,
|
||||||
|
pub button_start: bool,
|
||||||
|
pub button_a: bool,
|
||||||
|
pub button_b: bool,
|
||||||
|
pub button_c: bool,
|
||||||
|
pub button_d: bool,
|
||||||
|
pub button_up: bool,
|
||||||
|
pub button_down: bool,
|
||||||
|
pub button_left: bool,
|
||||||
|
pub button_right: bool,
|
||||||
|
}
|
23
src/virtual_controller.rs
Normal file
23
src/virtual_controller.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use crate::state::ControllerState;
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum ControllerModel {
|
||||||
|
NONE,
|
||||||
|
DGOC44U,
|
||||||
|
TYPE2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_model(state: &ControllerState) -> ControllerModel {
|
||||||
|
if state.button_right {
|
||||||
|
println!("Selected controller DGOC44-U.");
|
||||||
|
return ControllerModel::DGOC44U;
|
||||||
|
}
|
||||||
|
else if state.button_left {
|
||||||
|
println!("Selected controller TCPP-20009.");
|
||||||
|
return ControllerModel::TYPE2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println!("No controller selected.");
|
||||||
|
return ControllerModel::NONE;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue