Working prototype
This commit is contained in:
parent
ee075738b8
commit
ceff1ce385
8 changed files with 131 additions and 88 deletions
20
arduino/.vscode/tasks.json
vendored
Normal file
20
arduino/.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build Arduino Release Binary",
|
||||
"type": "shell",
|
||||
"command": "cargo build --release",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Write to Arduino",
|
||||
"type": "shell",
|
||||
"command": "ravedude uno -b 57600 ${workspaceFolder}/target/avr-atmega328p/release/arduino.elf",
|
||||
"problemMatcher": [],
|
||||
"dependsOn": "Build Arduino Release Binary"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,21 +1,36 @@
|
|||
use arduino_hal::{
|
||||
hal::port::Dynamic,
|
||||
port::{
|
||||
mode::{Floating, Input},
|
||||
mode::{Input, PullUp},
|
||||
Pin,
|
||||
},
|
||||
};
|
||||
|
||||
pub const BUTTON_COUNT: usize = 8;
|
||||
|
||||
// pub const BUTTONS: [Button; BUTTON_COUNT] = {
|
||||
// Button {
|
||||
// pin_id: 6,
|
||||
// // last_state:
|
||||
// }
|
||||
// };
|
||||
|
||||
pub struct Button {
|
||||
pub pin: Pin<Input<Floating>, Dynamic>,
|
||||
pub index: usize,
|
||||
pub pin: Pin<Input<PullUp>, Dynamic>,
|
||||
pub is_low: bool,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
pub fn check_state<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(usize, bool),
|
||||
{
|
||||
if self.pin.is_high() {
|
||||
if self.is_low {
|
||||
self.is_low = false;
|
||||
|
||||
f(self.index, true);
|
||||
}
|
||||
} else {
|
||||
if !self.is_low {
|
||||
self.is_low = true;
|
||||
|
||||
f(self.index, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
#![no_main]
|
||||
|
||||
mod buttons;
|
||||
mod state;
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use ufmt::uDisplay;
|
||||
|
||||
use arduino_hal::{default_serial, delay_ms, pins, Peripherals};
|
||||
use buttons::Button;
|
||||
use state::State;
|
||||
use buttons::{Button, BUTTON_COUNT};
|
||||
|
||||
const MESSAGE_START: char = '<';
|
||||
const MESSAGE_END: char = '>';
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &PanicInfo) -> ! {
|
||||
|
@ -28,20 +28,7 @@ fn panic(_info: &PanicInfo) -> ! {
|
|||
|
||||
loop {
|
||||
led.toggle();
|
||||
delay_ms(100);
|
||||
}
|
||||
}
|
||||
|
||||
struct PinId(usize);
|
||||
|
||||
impl uDisplay for PinId {
|
||||
fn fmt<W>(&self, fmt: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
|
||||
where
|
||||
W: ufmt::uWrite + ?Sized,
|
||||
{
|
||||
// fmt.
|
||||
|
||||
todo!()
|
||||
delay_ms(500);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,66 +37,61 @@ fn main() -> ! {
|
|||
let dp = Peripherals::take().unwrap();
|
||||
let pins = pins!(dp);
|
||||
|
||||
let mut buttons = [
|
||||
pins.d13.into_output().set_high();
|
||||
|
||||
let mut buttons: [Button; BUTTON_COUNT] = [
|
||||
Button {
|
||||
pin: pins.d5.downgrade(),
|
||||
index: 1,
|
||||
pin: pins.d5.into_pull_up_input().downgrade(),
|
||||
is_low: true,
|
||||
},
|
||||
Button {
|
||||
pin: pins.d6.downgrade(),
|
||||
index: 2,
|
||||
pin: pins.d6.into_pull_up_input().downgrade(),
|
||||
is_low: true,
|
||||
},
|
||||
Button {
|
||||
pin: pins.d7.downgrade(),
|
||||
index: 3,
|
||||
pin: pins.d7.into_pull_up_input().downgrade(),
|
||||
is_low: true,
|
||||
},
|
||||
Button {
|
||||
pin: pins.d8.downgrade(),
|
||||
index: 4,
|
||||
pin: pins.d8.into_pull_up_input().downgrade(),
|
||||
is_low: true,
|
||||
},
|
||||
Button {
|
||||
pin: pins.d9.downgrade(),
|
||||
index: 5,
|
||||
pin: pins.d9.into_pull_up_input().downgrade(),
|
||||
is_low: true,
|
||||
},
|
||||
Button {
|
||||
pin: pins.d10.downgrade(),
|
||||
index: 6,
|
||||
pin: pins.d10.into_pull_up_input().downgrade(),
|
||||
is_low: true,
|
||||
},
|
||||
Button {
|
||||
pin: pins.d11.downgrade(),
|
||||
index: 7,
|
||||
pin: pins.d11.into_pull_up_input().downgrade(),
|
||||
is_low: true,
|
||||
},
|
||||
Button {
|
||||
pin: pins.d12.downgrade(),
|
||||
index: 8,
|
||||
pin: pins.d12.into_pull_up_input().downgrade(),
|
||||
is_low: true,
|
||||
},
|
||||
];
|
||||
|
||||
let mut serial = default_serial!(dp, pins, 57600);
|
||||
let mut current_state = State::WaitingForInput;
|
||||
|
||||
loop {
|
||||
match current_state {
|
||||
State::WaitingForInput => {
|
||||
for (id, button) in buttons.iter_mut().enumerate() {
|
||||
if button.pin.is_high() {
|
||||
if button.is_low {
|
||||
button.is_low = false;
|
||||
|
||||
// let s = (id + 1).to_string();
|
||||
ufmt::uwriteln!(&mut serial, "HIGH: {}", id + 1).unwrap();
|
||||
for button in buttons.iter_mut() {
|
||||
button.check_state(|index, pressed| {
|
||||
if !pressed {
|
||||
ufmt::uwriteln!(&mut serial, "{}{}{}", MESSAGE_START, index, MESSAGE_END)
|
||||
.unwrap();
|
||||
}
|
||||
} else {
|
||||
if !button.is_low {
|
||||
button.is_low = true;
|
||||
|
||||
// let s = (id + 1).to_string();
|
||||
ufmt::uwriteln!(&mut serial, "LOW: {}\n ---", id + 1).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
State::WaitingForAcknowledge => (),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
pub enum State {
|
||||
WaitingForInput,
|
||||
WaitingForAcknowledge,
|
||||
}
|
19
src/gui.rs
19
src/gui.rs
|
@ -4,9 +4,6 @@ use gtk::{Builder, Window};
|
|||
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
mod shared;
|
||||
|
||||
use shared::config::{Config, COMMAND_COUNT};
|
||||
|
@ -24,7 +21,7 @@ fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
fn setup_gui(builder: Builder) -> Result<()> {
|
||||
let config = load_config()?;
|
||||
let config = Config::load_config()?;
|
||||
|
||||
apply_config(&builder, &config)?;
|
||||
setup_save(&builder)?;
|
||||
|
@ -44,20 +41,6 @@ fn setup_gui(builder: Builder) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn load_config() -> Result<Config> {
|
||||
let home_dir = std::env::var("HOME")?;
|
||||
|
||||
if !Path::new(&format!("{}/.config", home_dir)).exists() {
|
||||
fs::create_dir(format!("{}/.config", home_dir))?;
|
||||
}
|
||||
|
||||
if !Path::new(&format!("{}/.config/MacroBoard", home_dir)).exists() {
|
||||
fs::create_dir(format!("{}/.config/MacroBoard", home_dir))?;
|
||||
}
|
||||
|
||||
Config::open(&format!("{}/.config/MacroBoard/commands.cfg", home_dir))
|
||||
}
|
||||
|
||||
fn setup_save(builder: &Builder) -> Result<()> {
|
||||
let mut text_fields = Vec::with_capacity(COMMAND_COUNT);
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
mod service_specific;
|
||||
mod shared;
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::Result;
|
||||
use service_specific::*;
|
||||
use shared::config::Config;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut port = Port::open(SerialPortSettings {
|
||||
|
@ -16,13 +20,18 @@ fn main() -> Result<()> {
|
|||
let mut message_builder = MessageBuilder::default();
|
||||
|
||||
loop {
|
||||
match port.read()? {
|
||||
if let Ok(r) = port.read() {
|
||||
match r {
|
||||
SerialReadResult::Message(msg) => {
|
||||
if let Some(remaining) = message_builder.check(msg) {
|
||||
assert!(message_builder.is_complete());
|
||||
if let Some(_remaining) = message_builder.check(msg) {
|
||||
debug_assert!(message_builder.is_complete());
|
||||
|
||||
println!("{}", message_builder.message());
|
||||
|
||||
if let Ok(index) = message_builder.message().parse() {
|
||||
execute_button_press(index)?;
|
||||
}
|
||||
|
||||
message_builder = MessageBuilder::default();
|
||||
}
|
||||
}
|
||||
|
@ -30,3 +39,22 @@ fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_button_press(button_id: usize) -> Result<()> {
|
||||
let config = Config::load_config()?;
|
||||
|
||||
if button_id < 1 && button_id > 8 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(cmd) = &config.commands[button_id - 1] {
|
||||
if !cmd.is_empty() {
|
||||
println!("cmd: {}", cmd);
|
||||
|
||||
Command::new(cmd).spawn()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -68,7 +68,8 @@ impl Port {
|
|||
for available_port in available_ports.iter() {
|
||||
if let serialport::SerialPortType::UsbPort(usb_info) = &available_port.port_type {
|
||||
// check for the correct device
|
||||
if usb_info.vid == 0x0403 && usb_info.pid == 0x6001 {
|
||||
// if usb_info.vid == 0x0403 && usb_info.pid == 0x6001 {
|
||||
if usb_info.vid == 0x2341 && usb_info.pid == 0x0043 {
|
||||
return Ok(available_port.port_name.clone());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::{fs::File, io::Write, path::Path};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::Write,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
pub const COMMAND_COUNT: usize = 8;
|
||||
|
||||
|
@ -11,6 +15,20 @@ pub struct Config {
|
|||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load_config() -> Result<Config> {
|
||||
let home_dir = std::env::var("HOME")?;
|
||||
|
||||
if !Path::new(&format!("{}/.config", home_dir)).exists() {
|
||||
fs::create_dir(format!("{}/.config", home_dir))?;
|
||||
}
|
||||
|
||||
if !Path::new(&format!("{}/.config/MacroBoard", home_dir)).exists() {
|
||||
fs::create_dir(format!("{}/.config/MacroBoard", home_dir))?;
|
||||
}
|
||||
|
||||
Config::open(&format!("{}/.config/MacroBoard/commands.cfg", home_dir))
|
||||
}
|
||||
|
||||
pub fn open(path: &str) -> Result<Config> {
|
||||
if Path::new(path).exists() {
|
||||
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
|
||||
|
|
Loading…
Reference in a new issue