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::{
|
use arduino_hal::{
|
||||||
hal::port::Dynamic,
|
hal::port::Dynamic,
|
||||||
port::{
|
port::{
|
||||||
mode::{Floating, Input},
|
mode::{Input, PullUp},
|
||||||
Pin,
|
Pin,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const BUTTON_COUNT: usize = 8;
|
pub const BUTTON_COUNT: usize = 8;
|
||||||
|
|
||||||
// pub const BUTTONS: [Button; BUTTON_COUNT] = {
|
|
||||||
// Button {
|
|
||||||
// pin_id: 6,
|
|
||||||
// // last_state:
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
pub struct Button {
|
pub struct Button {
|
||||||
pub pin: Pin<Input<Floating>, Dynamic>,
|
pub index: usize,
|
||||||
|
pub pin: Pin<Input<PullUp>, Dynamic>,
|
||||||
pub is_low: bool,
|
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]
|
#![no_main]
|
||||||
|
|
||||||
mod buttons;
|
mod buttons;
|
||||||
mod state;
|
|
||||||
|
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
use ufmt::uDisplay;
|
|
||||||
|
|
||||||
use arduino_hal::{default_serial, delay_ms, pins, Peripherals};
|
use arduino_hal::{default_serial, delay_ms, pins, Peripherals};
|
||||||
use buttons::Button;
|
use buttons::{Button, BUTTON_COUNT};
|
||||||
use state::State;
|
|
||||||
|
const MESSAGE_START: char = '<';
|
||||||
|
const MESSAGE_END: char = '>';
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &PanicInfo) -> ! {
|
fn panic(_info: &PanicInfo) -> ! {
|
||||||
|
@ -28,20 +28,7 @@ fn panic(_info: &PanicInfo) -> ! {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
led.toggle();
|
led.toggle();
|
||||||
delay_ms(100);
|
delay_ms(500);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,66 +37,61 @@ fn main() -> ! {
|
||||||
let dp = Peripherals::take().unwrap();
|
let dp = Peripherals::take().unwrap();
|
||||||
let pins = pins!(dp);
|
let pins = pins!(dp);
|
||||||
|
|
||||||
let mut buttons = [
|
pins.d13.into_output().set_high();
|
||||||
|
|
||||||
|
let mut buttons: [Button; BUTTON_COUNT] = [
|
||||||
Button {
|
Button {
|
||||||
pin: pins.d5.downgrade(),
|
index: 1,
|
||||||
|
pin: pins.d5.into_pull_up_input().downgrade(),
|
||||||
is_low: true,
|
is_low: true,
|
||||||
},
|
},
|
||||||
Button {
|
Button {
|
||||||
pin: pins.d6.downgrade(),
|
index: 2,
|
||||||
|
pin: pins.d6.into_pull_up_input().downgrade(),
|
||||||
is_low: true,
|
is_low: true,
|
||||||
},
|
},
|
||||||
Button {
|
Button {
|
||||||
pin: pins.d7.downgrade(),
|
index: 3,
|
||||||
|
pin: pins.d7.into_pull_up_input().downgrade(),
|
||||||
is_low: true,
|
is_low: true,
|
||||||
},
|
},
|
||||||
Button {
|
Button {
|
||||||
pin: pins.d8.downgrade(),
|
index: 4,
|
||||||
|
pin: pins.d8.into_pull_up_input().downgrade(),
|
||||||
is_low: true,
|
is_low: true,
|
||||||
},
|
},
|
||||||
Button {
|
Button {
|
||||||
pin: pins.d9.downgrade(),
|
index: 5,
|
||||||
|
pin: pins.d9.into_pull_up_input().downgrade(),
|
||||||
is_low: true,
|
is_low: true,
|
||||||
},
|
},
|
||||||
Button {
|
Button {
|
||||||
pin: pins.d10.downgrade(),
|
index: 6,
|
||||||
|
pin: pins.d10.into_pull_up_input().downgrade(),
|
||||||
is_low: true,
|
is_low: true,
|
||||||
},
|
},
|
||||||
Button {
|
Button {
|
||||||
pin: pins.d11.downgrade(),
|
index: 7,
|
||||||
|
pin: pins.d11.into_pull_up_input().downgrade(),
|
||||||
is_low: true,
|
is_low: true,
|
||||||
},
|
},
|
||||||
Button {
|
Button {
|
||||||
pin: pins.d12.downgrade(),
|
index: 8,
|
||||||
|
pin: pins.d12.into_pull_up_input().downgrade(),
|
||||||
is_low: true,
|
is_low: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut serial = default_serial!(dp, pins, 57600);
|
let mut serial = default_serial!(dp, pins, 57600);
|
||||||
let mut current_state = State::WaitingForInput;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match current_state {
|
for button in buttons.iter_mut() {
|
||||||
State::WaitingForInput => {
|
button.check_state(|index, pressed| {
|
||||||
for (id, button) in buttons.iter_mut().enumerate() {
|
if !pressed {
|
||||||
if button.pin.is_high() {
|
ufmt::uwriteln!(&mut serial, "{}{}{}", MESSAGE_START, index, MESSAGE_END)
|
||||||
if button.is_low {
|
.unwrap();
|
||||||
button.is_low = false;
|
|
||||||
|
|
||||||
// let s = (id + 1).to_string();
|
|
||||||
ufmt::uwriteln!(&mut serial, "HIGH: {}", id + 1).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 anyhow::{anyhow, Result};
|
||||||
|
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
mod shared;
|
mod shared;
|
||||||
|
|
||||||
use shared::config::{Config, COMMAND_COUNT};
|
use shared::config::{Config, COMMAND_COUNT};
|
||||||
|
@ -24,7 +21,7 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_gui(builder: Builder) -> Result<()> {
|
fn setup_gui(builder: Builder) -> Result<()> {
|
||||||
let config = load_config()?;
|
let config = Config::load_config()?;
|
||||||
|
|
||||||
apply_config(&builder, &config)?;
|
apply_config(&builder, &config)?;
|
||||||
setup_save(&builder)?;
|
setup_save(&builder)?;
|
||||||
|
@ -44,20 +41,6 @@ fn setup_gui(builder: Builder) -> Result<()> {
|
||||||
Ok(())
|
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<()> {
|
fn setup_save(builder: &Builder) -> Result<()> {
|
||||||
let mut text_fields = Vec::with_capacity(COMMAND_COUNT);
|
let mut text_fields = Vec::with_capacity(COMMAND_COUNT);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
mod service_specific;
|
mod service_specific;
|
||||||
|
mod shared;
|
||||||
|
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use service_specific::*;
|
use service_specific::*;
|
||||||
|
use shared::config::Config;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let mut port = Port::open(SerialPortSettings {
|
let mut port = Port::open(SerialPortSettings {
|
||||||
|
@ -16,17 +20,41 @@ fn main() -> Result<()> {
|
||||||
let mut message_builder = MessageBuilder::default();
|
let mut message_builder = MessageBuilder::default();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match port.read()? {
|
if let Ok(r) = port.read() {
|
||||||
SerialReadResult::Message(msg) => {
|
match r {
|
||||||
if let Some(remaining) = message_builder.check(msg) {
|
SerialReadResult::Message(msg) => {
|
||||||
assert!(message_builder.is_complete());
|
if let Some(_remaining) = message_builder.check(msg) {
|
||||||
|
debug_assert!(message_builder.is_complete());
|
||||||
|
|
||||||
println!("{}", message_builder.message());
|
println!("{}", message_builder.message());
|
||||||
|
|
||||||
message_builder = MessageBuilder::default();
|
if let Ok(index) = message_builder.message().parse() {
|
||||||
|
execute_button_press(index)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
message_builder = MessageBuilder::default();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
SerialReadResult::Timeout => (),
|
||||||
}
|
}
|
||||||
SerialReadResult::Timeout => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
for available_port in available_ports.iter() {
|
||||||
if let serialport::SerialPortType::UsbPort(usb_info) = &available_port.port_type {
|
if let serialport::SerialPortType::UsbPort(usb_info) = &available_port.port_type {
|
||||||
// check for the correct device
|
// 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());
|
return Ok(available_port.port_name.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
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;
|
pub const COMMAND_COUNT: usize = 8;
|
||||||
|
|
||||||
|
@ -11,6 +15,20 @@ pub struct Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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> {
|
pub fn open(path: &str) -> Result<Config> {
|
||||||
if Path::new(path).exists() {
|
if Path::new(path).exists() {
|
||||||
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
|
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
|
||||||
|
|
Loading…
Reference in a new issue