pico_gpio/
lib.rs

1mod pwm_streamer;
2use std::time::Duration;
3
4pub use pwm_streamer::*;
5
6use readformat::{readf, readf1};
7pub use serialport;
8use serialport::{Error, SerialPort};
9
10#[derive(Clone, Copy, PartialEq, Eq)]
11pub enum PicoGPIOVersion {
12    V1_0,
13    Unknown,
14}
15
16#[derive(Clone, Copy, PartialEq, Eq)]
17pub enum PinValueRead {
18    Floating(Option<bool>),
19    Analog(u32),
20    Digital(bool),
21    PWM(u32),
22}
23#[derive(Clone, Copy, PartialEq, Eq)]
24pub enum PinValueWrite {
25    Floating,
26    Digital(bool),
27    PWM(u32),
28}
29
30impl PinValueRead {
31    pub fn matches(&self, written: &PinValueWrite) -> bool {
32        match (self, written) {
33            (PinValueRead::Floating(_), PinValueWrite::Floating) => true,
34            (PinValueRead::Digital(a), PinValueWrite::Digital(b)) if a == b => true,
35            (PinValueRead::PWM(a), PinValueWrite::PWM(b)) if a == b => true,
36            _ => false,
37        }
38    }
39
40    pub fn matches_in(&self, asked: &PinInput) -> bool {
41        matches!(
42            (self, asked),
43            (PinValueRead::Floating(_), PinInput::Floating)
44                | (PinValueRead::Analog(_), PinInput::Analog)
45                | (PinValueRead::Digital(_), PinInput::PDown)
46                | (PinValueRead::Digital(_), PinInput::PUp)
47        )
48    }
49}
50
51/*
52impl From<PinValueRead> for PinValueWrite {
53    fn from(value: PinValueRead) -> Self {
54        match value {
55            PinValueRead::Floating(_) => PinValueWrite::Floating,
56            PinValueRead::Analog(_) => PinValueWrite::Floating,
57            PinValueRead::Digital(b) => PinValueWrite::Digital(b),
58            PinValueRead::PWM(v) => PinValueWrite::PWM(v),
59        }
60    }
61}
62*/
63
64#[derive(Clone, Copy, PartialEq, Eq)]
65pub enum PinInput {
66    Analog,
67    Floating,
68    PDown,
69    PUp,
70}
71
72struct Params<PVType, const PINS: usize> {
73    pins: [PVType; PINS],
74    pwmfreq: u32,
75    pwmres: u8,
76    inares: u8,
77    streaming: bool,
78}
79
80pub struct PicoGPIO<Port: SerialPort, const PINS: usize = 256> {
81    port: Port,
82    version: PicoGPIOVersion,
83    // Intended values, set immediately
84    intended: Params<PinValueWrite, PINS>,
85    // Actual values, set when receiving a response
86    actual: Params<PinValueRead, PINS>,
87    blocking: bool,
88}
89
90impl<Port: SerialPort, const PINS: usize> PicoGPIO<Port, PINS> {
91    pub fn new(mut serial_port: Port) -> Result<Self, Error> {
92        serial_port.set_timeout(Duration::from_millis(500))?;
93        serial_port.write_all("\r\n".as_bytes())?;
94        Ok(Self {
95            port: serial_port,
96            version: PicoGPIOVersion::Unknown,
97            intended: Params {
98                pins: [PinValueWrite::Floating; PINS],
99                pwmfreq: 0,
100                pwmres: 8,
101                inares: 10,
102                streaming: false,
103            },
104            actual: Params {
105                pins: [PinValueRead::Floating(None); PINS],
106                pwmfreq: 0,
107                pwmres: 8,
108                inares: 10,
109                streaming: false,
110            },
111            blocking: true,
112        })
113    }
114
115    pub fn poll(&mut self, mut min_lines: usize) -> Result<(), Error> {
116        loop {
117            if self.port.bytes_to_read()? == 0 && min_lines == 0 {
118                break;
119            }
120            let mut buf = [0u8; 1];
121            self.port.read_exact(&mut buf)?;
122            let mut line = vec![buf[0]];
123            loop {
124                let mut buf = [0u8; 1024];
125                let amt = self.port.read(&mut buf)?;
126                line.append(&mut buf[..amt].to_vec());
127                if *line.last().unwrap() as char == '\n' {
128                    break;
129                }
130            }
131            for line in String::from_utf8(line).unwrap().split('\n') {
132                self.parse_line(line.trim())?;
133                min_lines = min_lines.saturating_sub(1);
134            }
135        }
136        Ok(())
137    }
138
139    fn parse_line(&mut self, line: &str) -> Result<(), Error> {
140        if line == "!OK" {
141            // pass
142        } else if let Some(v) = readf1("+PICO_GPIO {}", line) {
143            self.version = match v.as_str() {
144                "V1.0" => PicoGPIOVersion::V1_0,
145                _ => PicoGPIOVersion::Unknown,
146            };
147        } else if let Some(err) = readf1("!ERROR:{}", line) {
148            panic!("PicoGPIO version mismatch: ERROR {err}.");
149        } else if let Some(freq) = readf1("!PWMFREQ:{}", line) {
150            self.actual.pwmfreq = freq.parse().unwrap();
151        } else if let Some(res) = readf1("!PWMRES:{}", line) {
152            self.actual.pwmres = res.parse().unwrap();
153        } else if let Some(res) = readf1("!INARES:{}", line) {
154            self.actual.inares = res.parse().unwrap();
155        } else if line == "!STREAMING" {
156            self.actual.streaming = true;
157        } else if let Some([pin, val]) = readf("~{}={}", line).as_deref() {
158            self.actual.pins[pin.parse::<usize>().expect("invalid data from PicoGPIO!")] =
159                PinValueRead::Floating(Some(
160                    val.parse::<u8>().expect("invalid data from PicoGPIO!") != 0,
161                ))
162        } else if let Some([pin, val]) = readf("/{}={}", line).as_deref() {
163            self.actual.pins[pin.parse::<usize>().expect("invalid data from PicoGPIO!")] =
164                PinValueRead::Analog(val.parse::<u32>().expect("invalid data from PicoGPIO!"))
165        } else if let Some([pin, val]) = readf("#{}={}", line).as_deref() {
166            self.actual.pins[pin.parse::<usize>().expect("invalid data from PicoGPIO!")] =
167                PinValueRead::PWM(val.parse::<u32>().expect("invalid data from PicoGPIO!"))
168        } else if let Some([pin, val]) = readf("{}={}", line).as_deref() {
169            self.actual.pins[pin.parse::<usize>().expect("invalid data from PicoGPIO!")] =
170                PinValueRead::Digital(val.parse::<u8>().expect("invalid data from PicoGPIO!") != 0)
171        }
172        Ok(())
173    }
174
175    pub fn set_manual(
176        &mut self,
177        pin: usize,
178        value: PinValueWrite,
179        block: bool,
180    ) -> Result<(), Error> {
181        self.intended.pins[pin] = value;
182        match value {
183            PinValueWrite::Floating => {
184                self.port.write_all(format!("float {pin}\r\n").as_bytes())?
185            }
186            PinValueWrite::Digital(val) => self
187                .port
188                .write_all(format!("out {pin}={}\r\n", if val { 1 } else { 0 }).as_bytes())?,
189            PinValueWrite::PWM(val) => self
190                .port
191                .write_all(format!("pwm {pin}={val}\r\n").as_bytes())?,
192        }
193        self.poll(0)?;
194        if block {
195            while !self.actual.pins[pin].matches(&value) {
196                self.poll(1)?;
197            }
198        }
199        Ok(())
200    }
201
202    pub fn get_manual(
203        &mut self,
204        pin: usize,
205        kind: PinInput,
206        cached: bool,
207        block: bool,
208    ) -> Result<PinValueRead, Error> {
209        if !cached {
210            self.intended.pins[pin] = PinValueWrite::Floating;
211            self.poll(0)?;
212            match kind {
213                PinInput::Floating => self.port.write_all(format!("float {pin}\r\n").as_bytes())?,
214                PinInput::Analog => self.port.write_all(format!("ina {pin}\r\n").as_bytes())?,
215                PinInput::PDown => self.port.write_all(format!("in {pin}\r\n").as_bytes())?,
216                PinInput::PUp => self.port.write_all(format!("in^ {pin}\r\n").as_bytes())?,
217            }
218            if block {
219                self.poll(1)?;
220            }
221            while !self.actual.pins[pin].matches_in(&kind) {
222                self.poll(1)?;
223            }
224        }
225
226        Ok(self.actual.pins[pin])
227    }
228
229    pub fn float(&mut self, pin: usize) -> Result<(), Error> {
230        self.set_manual(pin, PinValueWrite::Floating, self.blocking)
231    }
232
233    pub fn out_d(&mut self, pin: usize, val: bool) -> Result<(), Error> {
234        self.set_manual(pin, PinValueWrite::Digital(val), self.blocking)
235    }
236
237    pub fn out_pwm(&mut self, pin: usize, val: u32) -> Result<(), Error> {
238        self.set_manual(pin, PinValueWrite::PWM(val), self.blocking)
239    }
240
241    pub fn in_float(&mut self, pin: usize) -> Result<bool, Error> {
242        self.get_manual(pin, PinInput::Floating, false, self.blocking)
243            .map(|x| match x {
244                PinValueRead::Floating(Some(x)) => x,
245                _ => unreachable!(),
246            })
247    }
248
249    pub fn in_pulldn(&mut self, pin: usize) -> Result<bool, Error> {
250        self.get_manual(pin, PinInput::PDown, false, self.blocking)
251            .map(|x| match x {
252                PinValueRead::Digital(x) => x,
253                _ => unreachable!(),
254            })
255    }
256
257    pub fn in_pullup(&mut self, pin: usize) -> Result<bool, Error> {
258        self.get_manual(pin, PinInput::PUp, false, self.blocking)
259            .map(|x| match x {
260                PinValueRead::Digital(x) => x,
261                _ => unreachable!(),
262            })
263    }
264
265    pub fn in_analog(&mut self, pin: usize) -> Result<u32, Error> {
266        self.get_manual(pin, PinInput::Analog, false, self.blocking)
267            .map(|x| match x {
268                PinValueRead::Analog(x) => x,
269                _ => unreachable!(),
270            })
271    }
272
273    pub fn init_pwm(&mut self, freq: u32, res: u8, block: bool) -> Result<(), Error> {
274        self.intended.pwmres = res;
275        self.intended.pwmfreq = freq;
276        self.port
277            .write_all(format!("pwmres {res}\r\npwmfreq {freq}\r\n").as_bytes())?;
278        self.poll(0)?;
279        if block {
280            while self.actual.pwmfreq != freq || self.actual.pwmres != res {
281                self.poll(1)?;
282            }
283        }
284        Ok(())
285    }
286
287    pub fn pwmstream(mut self, pin: usize) -> Result<PwmStreamer<Port, PINS>, (Self, Error)> {
288        if let Err(e) = self
289            .init_pwm(self.intended.pwmfreq, 8, true)
290            .and_then(|()| {
291                self.port
292                    .write_all(format!("pwmstream {pin}").as_bytes())
293                    .map_err(|x| x.into())
294            })
295        {
296            return Err((self, e));
297        }
298        Ok(PwmStreamer::new(self, PwmStreamMode::PWM))
299    }
300
301    pub fn audiostream(mut self, pin: usize) -> Result<PwmStreamer<Port, PINS>, (Self, Error)> {
302        if let Err(e) = self
303            .init_pwm(self.intended.pwmfreq, 8, true)
304            .and_then(|()| {
305                self.port
306                    .write_all(format!("audiostream {pin}").as_bytes())
307                    .map_err(|x| x.into())
308            })
309        {
310            return Err((self, e));
311        }
312        Ok(PwmStreamer::new(self, PwmStreamMode::Audio))
313    }
314}