-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #93 from varoonp123/feature/detectPort
Infer the arduino arcade serial port from available ports, falling back to an envvar. This port will be different on each arcade deployment.
- Loading branch information
Showing
6 changed files
with
308 additions
and
229 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Thetawave-arcade | ||
|
||
This crate exposes Bevy plugins that implement features specific to the arcade cabinet build target/platform. | ||
|
||
|
||
## Configuration | ||
|
||
- `THETAWAVE_ARCADE_LIGHT_SERIAL_PORT_NAME` = The name of the serial port that controls the lights. We will probably | ||
automate detecting this later, but this is currently related to a port used by Arduino. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,294 @@ | ||
use bevy::prelude::*; | ||
use bevy_serialport::{ | ||
DataBits, FlowControl, Parity, SerialPortPlugin, SerialPortRuntime, SerialPortSetting, | ||
SerialResource, StopBits, | ||
}; | ||
|
||
use derive_more::{Deref, DerefMut, From}; | ||
use serialport::{available_ports, SerialPortType}; | ||
use thetawave_interface::states; | ||
|
||
/// Environment variable name of the serial port that handles lights. | ||
const THETAWAVE_ARCADE_LIGHT_SERIAL_PORT_NAME: &'static str = | ||
"THETAWAVE_ARCADE_LIGHT_SERIAL_PORT_NAME"; | ||
|
||
/// The port for the Arduino that controls the lights. | ||
#[derive(Resource, Deref, DerefMut, From, Debug)] | ||
struct ArduinoSerialPort(String); | ||
impl ArduinoSerialPort { | ||
fn first_port_matching_manufacturer_product() -> Option<Self> { | ||
first_usb_port_name_matching_str("arduino").map(Self) | ||
} | ||
fn from_envvar() -> Option<Self> { | ||
std::env::var(THETAWAVE_ARCADE_LIGHT_SERIAL_PORT_NAME) | ||
.ok() | ||
.map(Self) | ||
} | ||
} | ||
|
||
/// Find a serial port from all available ports where the manufacturer or product match a string | ||
/// pattern. | ||
fn first_usb_port_name_matching_str(lowercase_pattern: &str) -> Option<String> { | ||
match available_ports() { | ||
Ok(ports) => ports | ||
.into_iter() | ||
.find(|x| match &x.port_type { | ||
SerialPortType::UsbPort(x) => format!( | ||
"{} {}", | ||
x.manufacturer.clone().unwrap_or_default().to_lowercase(), | ||
x.product.clone().unwrap_or_default().to_lowercase() | ||
) | ||
.contains(lowercase_pattern), | ||
_ => false, | ||
}) | ||
.map(|x| x.port_name.clone()), | ||
Err(e) => { | ||
error!("Failed to list serial ports. {}", e); | ||
None | ||
} | ||
} | ||
} | ||
use bytes::Bytes; | ||
use thetawave_interface::character_selection::PlayerJoinEvent; | ||
|
||
/// Features specific to an Arduino that is only running on custom-made arcade machines. Mostly | ||
/// lighting. The plugin no-ops if an Arduino serial port cannot be accessed. | ||
pub struct ArcadeArduinoPlugin; | ||
|
||
impl Plugin for ArcadeArduinoPlugin { | ||
fn build(&self, app: &mut App) { | ||
if let Some(res) = ArduinoSerialPort::first_port_matching_manufacturer_product() | ||
.or_else(ArduinoSerialPort::from_envvar) | ||
{ | ||
info!("arduino serial port: {:?}", &res); | ||
app.add_plugins(SerialPortPlugin).insert_resource(res); | ||
|
||
app.add_systems( | ||
Startup, | ||
(setup_serial_system, enter_main_menu_button_leds_system), | ||
); | ||
|
||
app.add_systems( | ||
OnEnter(states::AppStates::MainMenu), | ||
enter_main_menu_button_leds_system, | ||
); | ||
|
||
app.add_systems( | ||
OnEnter(states::AppStates::CharacterSelection), | ||
enter_character_selection_button_leds_system, | ||
); | ||
|
||
app.add_systems( | ||
Update, | ||
character_selection_button_leds_system | ||
.run_if(in_state(states::AppStates::CharacterSelection)), | ||
); | ||
|
||
app.add_systems( | ||
OnEnter(thetawave_interface::states::AppStates::Game), | ||
enter_game_button_leds_system, | ||
); | ||
|
||
app.add_systems( | ||
OnEnter(states::GameStates::Paused), | ||
enter_pause_button_leds_system, | ||
); | ||
|
||
app.add_systems( | ||
OnExit(states::GameStates::Paused), | ||
enter_game_button_leds_system, | ||
); | ||
|
||
app.add_systems( | ||
OnEnter(states::AppStates::Victory), | ||
enter_victory_button_leds_system, | ||
); | ||
|
||
app.add_systems( | ||
OnEnter(states::AppStates::GameOver), | ||
enter_gameover_button_leds_system, | ||
); | ||
} else { | ||
error!("Failed to find the arduino port for arcade lighting. no-op for the plugin. Enter the environment variable {} or compile without the --arcade feature", THETAWAVE_ARCADE_LIGHT_SERIAL_PORT_NAME); | ||
} | ||
} | ||
} | ||
fn setup_serial_system( | ||
mut serial_resource: ResMut<SerialResource>, | ||
arduino_port: Res<ArduinoSerialPort>, | ||
runtime: Res<SerialPortRuntime>, | ||
) { | ||
let serial_setting = SerialPortSetting { | ||
port_name: arduino_port.clone(), | ||
baud_rate: 115200, | ||
data_bits: DataBits::Eight, | ||
flow_control: FlowControl::None, | ||
parity: Parity::None, | ||
stop_bits: StopBits::One, | ||
timeout: Default::default(), | ||
}; | ||
serial_resource | ||
.open_with_setting(runtime.clone(), serial_setting) | ||
.expect(&format!("Error opening serial port {:?}", &arduino_port)); | ||
} | ||
|
||
enum ButtonLEDByte { | ||
EndMarker = 255, | ||
Off = 0, | ||
On = 1, | ||
Fade = 2, | ||
} | ||
|
||
impl ButtonLEDByte { | ||
fn enter_main_menu() -> Bytes { | ||
vec![ | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::EndMarker as u8, | ||
] | ||
.into() | ||
} | ||
|
||
fn enter_character_selection() -> Bytes { | ||
vec![ | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::EndMarker as u8, | ||
] | ||
.into() | ||
} | ||
|
||
fn player_one_joined() -> Bytes { | ||
vec![ | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::EndMarker as u8, | ||
] | ||
.into() | ||
} | ||
|
||
fn player_two_joined() -> Bytes { | ||
vec![ | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::EndMarker as u8, | ||
] | ||
.into() | ||
} | ||
|
||
fn enter_game() -> Bytes { | ||
vec![ | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::On as u8, | ||
ButtonLEDByte::EndMarker as u8, | ||
] | ||
.into() | ||
} | ||
|
||
fn enter_pause() -> Bytes { | ||
vec![ | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::EndMarker as u8, | ||
] | ||
.into() | ||
} | ||
|
||
fn enter_gameover() -> Bytes { | ||
vec![ | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::EndMarker as u8, | ||
] | ||
.into() | ||
} | ||
|
||
fn enter_victory() -> Bytes { | ||
vec![ | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::Fade as u8, | ||
ButtonLEDByte::Off as u8, | ||
ButtonLEDByte::EndMarker as u8, | ||
] | ||
.into() | ||
} | ||
} | ||
|
||
fn enter_main_menu_button_leds_system( | ||
mut serial_resource: ResMut<SerialResource>, | ||
arduino_port: Res<ArduinoSerialPort>, | ||
) { | ||
serial_resource.send_message(&(*arduino_port), ButtonLEDByte::enter_main_menu()); | ||
} | ||
|
||
fn enter_character_selection_button_leds_system( | ||
mut serial_resource: ResMut<SerialResource>, | ||
arduino_port: Res<ArduinoSerialPort>, | ||
) { | ||
serial_resource.send_message(&(*arduino_port), ButtonLEDByte::enter_character_selection()); | ||
} | ||
|
||
fn character_selection_button_leds_system( | ||
mut serial_resource: ResMut<SerialResource>, | ||
arduino_port: Res<ArduinoSerialPort>, | ||
mut player_join_event: EventReader<PlayerJoinEvent>, | ||
) { | ||
for event in player_join_event.iter() { | ||
if event.0 == 0 { | ||
serial_resource.send_message(&(*arduino_port), ButtonLEDByte::player_one_joined()); | ||
} else if event.0 == 1 { | ||
serial_resource.send_message(&(*arduino_port), ButtonLEDByte::player_two_joined()); | ||
} | ||
} | ||
} | ||
|
||
fn enter_game_button_leds_system( | ||
mut serial_resource: ResMut<SerialResource>, | ||
arduino_port: Res<ArduinoSerialPort>, | ||
) { | ||
serial_resource.send_message(&(*arduino_port), ButtonLEDByte::enter_game()); | ||
} | ||
|
||
fn enter_pause_button_leds_system( | ||
mut serial_resource: ResMut<SerialResource>, | ||
arduino_port: Res<ArduinoSerialPort>, | ||
) { | ||
serial_resource.send_message(&(*arduino_port), ButtonLEDByte::enter_pause()); | ||
} | ||
|
||
fn enter_gameover_button_leds_system( | ||
mut serial_resource: ResMut<SerialResource>, | ||
arduino_port: Res<ArduinoSerialPort>, | ||
) { | ||
serial_resource.send_message(&(*arduino_port), ButtonLEDByte::enter_gameover()); | ||
} | ||
|
||
fn enter_victory_button_leds_system( | ||
mut serial_resource: ResMut<SerialResource>, | ||
arduino_port: Res<ArduinoSerialPort>, | ||
) { | ||
serial_resource.send_message(&(*arduino_port), ButtonLEDByte::enter_victory()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
/// Exposes a single Bevy plugin for integrating the arcade IO into the game systems. | ||
pub mod plugin; | ||
pub mod arduino; |
Oops, something went wrong.