1#![doc = include_str!("../README.md")]
4
5use anyhow::Context;
6use ebyte_e32::{mode::Normal, Ebyte};
7use embedded_hal::{
8 blocking::delay,
9 digital::v2::{InputPin, OutputPin},
10 serial::{self, Read, Write},
11};
12use linux_embedded_hal::{
13 gpio_cdev::{Chip, LineRequestFlags},
14 serial_core::{BaudRate, CharSize, FlowControl, PortSettings, SerialPort},
15 serial_unix::TTYPort,
16 CdevPin as Pin, {Delay, Serial},
17};
18use nb::block;
19use rustyline::{error::ReadlineError, Editor};
20use std::fmt::Debug;
21
22use {
23 arguments::{Args, Mode},
24 config::{load, StopBits},
25};
26
27pub mod config;
29
30pub mod arguments;
32
33pub fn create(args: &Args) -> anyhow::Result<Ebyte<Serial, Pin, Pin, Pin, Delay, Normal>> {
40 let config = load(&args.config).context("Failed to get config")?;
41 let baud_rate = BaudRate::from_speed(config.baudrate as usize);
42 let stop_bits = StopBits::try_from(config.stop_bits)
43 .context("Failed to parse stop bits")?
44 .into();
45 let settings: PortSettings = PortSettings {
46 baud_rate,
47 char_size: CharSize::Bits8,
48 parity: config.parity.into(),
49 stop_bits,
50 flow_control: FlowControl::FlowNone,
51 };
52
53 let mut serial = TTYPort::open(&config.serial_path)
54 .with_context(|| format!("Failed to open TTY {}", config.serial_path.display()))?;
55 serial
56 .configure(&settings)
57 .context("Failed to set up serial port")?;
58 let serial = Serial(serial);
59
60 let mut gpiochip = Chip::new(&config.gpiochip_path)
61 .with_context(|| format!("Failed to open gpiochip {}", config.gpiochip_path.display()))?;
62
63 let aux = gpiochip
64 .get_line(config.aux_pin)
65 .context("Failed to get AUX line")?
66 .request(LineRequestFlags::INPUT, 0, "ebyte-e32-ui")
67 .context("Failed to request settings for AUX pin")?;
68 let aux = Pin::new(aux).context("Failed to create AUX CDEV pin")?;
69
70 let m0 = gpiochip
71 .get_line(config.m0_pin)
72 .context("Failed to get M0 line")?
73 .request(LineRequestFlags::OUTPUT, 0, "ebyte-e32-ui")
74 .context("Failed to request settings for M0 pin")?;
75 let m0 = Pin::new(m0).context("Failed to create M0 CDEV pin")?;
76
77 let m1 = gpiochip
78 .get_line(config.m1_pin)
79 .context("Failed to get M1 line")?
80 .request(LineRequestFlags::OUTPUT, 0, "ebyte-e32-ui")
81 .context("Failed to request settings for M1 pin")?;
82 let m1 = Pin::new(m1).context("Failed to create M1 CDEV pin")?;
83
84 Ok(Ebyte::new(serial, aux, m0, m1, Delay).expect("Failed to initialize driver"))
85}
86
87pub fn run<S, AUX, M0, M1, D>(
88 args: &Args,
89 mut ebyte: Ebyte<S, AUX, M0, M1, D, Normal>,
90) -> anyhow::Result<()>
91where
92 S: serial::Read<u8> + serial::Write<u8>,
93 <S as serial::Read<u8>>::Error: Debug,
94 <S as serial::Write<u8>>::Error: Debug,
95 AUX: InputPin,
96 M0: OutputPin,
97 M1: OutputPin,
98 D: delay::DelayMs<u32>,
99{
100 match args.mode {
101 Mode::Send => send(ebyte),
102 Mode::Listen => loop {
103 let b = block!(ebyte.read()).expect("Failed to read");
104 print!("{}", b as char);
105 std::io::Write::flush(&mut std::io::stdout()).expect("Failed to flush");
106 },
107 Mode::ReadModelData => {
108 println!("Reading model data");
109 let model_data = ebyte.model_data().expect("Failed to read model data");
110 println!("{model_data:#?}");
111 Ok(())
112 }
113 Mode::ReadParameters => {
114 println!("Reading parameter data");
115 let parameters = ebyte.parameters().expect("Failed to read parameter data");
116 println!("{parameters:#?}");
117 Ok(())
118 }
119 Mode::Configure(ref parameters) => configure(ebyte, parameters),
120 }
121}
122
123fn send<S, AUX, M0, M1, D>(
124 mut ebyte: Ebyte<S, AUX, M0, M1, D, ebyte_e32::mode::Normal>,
125) -> anyhow::Result<()>
126where
127 S: serial::Read<u8> + serial::Write<u8>,
128 <S as serial::Read<u8>>::Error: Debug,
129 <S as serial::Write<u8>>::Error: Debug,
130 AUX: InputPin,
131 M0: OutputPin,
132 M1: OutputPin,
133 D: delay::DelayMs<u32>,
134{
135 let mut prompt = Editor::<()>::new().expect("Failed to set up prompt");
136 loop {
137 match prompt.readline("Enter message >> ") {
138 Ok(line) => {
139 if line == "exit" || line == "quit" {
140 break;
141 }
142 prompt.add_history_entry(&line);
143
144 for b in line.as_bytes() {
145 block!(ebyte.write(*b)).expect("Failed to write");
146 print!("{}", *b as char);
147 std::io::Write::flush(&mut std::io::stdout()).expect("Failed to flush");
148 }
149 block!(ebyte.write(b'\n')).expect("Failed to write");
150 println!();
151 }
152 Err(ReadlineError::Interrupted) => {
153 println!("CTRL-C");
154 break;
155 }
156 Err(ReadlineError::Eof) => {
157 println!("CTRL-D");
158 break;
159 }
160 Err(err) => {
161 println!("Error: {err:?}");
162 break;
163 }
164 }
165 }
166 Ok(())
167}
168
169fn configure<S, AUX, M0, M1, D>(
175 mut ebyte: Ebyte<S, AUX, M0, M1, D, ebyte_e32::mode::Normal>,
176 parameters: &arguments::Parameters,
177) -> anyhow::Result<()>
178where
179 S: serial::Read<u8> + serial::Write<u8>,
180 <S as serial::Read<u8>>::Error: Debug,
181 <S as serial::Write<u8>>::Error: Debug,
182 AUX: InputPin,
183 M0: OutputPin,
184 M1: OutputPin,
185 D: delay::DelayMs<u32>,
186{
187 println!("Loading existing parameters");
188 let old_params = ebyte
189 .parameters()
190 .expect("Failed to read existing parameters");
191 println!("Loaded parameters: {old_params:#?}");
192
193 let new_params = ebyte_e32::Parameters::from(parameters);
195
196 if new_params == old_params {
197 println!("Leaving parameters unchanged");
198 } else {
199 println!(
200 "Updating parameters (persistence: {:?})",
201 parameters.persistence
202 );
203 ebyte
204 .set_parameters(&new_params, parameters.persistence)
205 .expect("Failed to set new parameters");
206
207 let current_params = ebyte
209 .parameters()
210 .expect("Failed to read current parameters");
211 if current_params != new_params {
212 eprintln!("Error: parameters unchanged: {current_params:#?}");
213 }
214 }
215 Ok(())
216}