qwiic_button_led/
lib.rs

1//! This crate provides an interface to the [SparkFun Qwiic Button LED] over the I2C protocol. It
2//! supports manipulating the LED and responding to button presses. It builds on [embedded-hal] and
3//! thus supports any platform which implements that crate's traits, like popular microcontrollers
4//! and Raspberry Pi models.
5//!
6//! An example for the Raspberry Pi:
7//!
8//! ```rust
9//! use linux_embedded_hal as hal;
10//! use qwiic_button_led::*;
11//! use std::{thread, time};
12//!
13//! // The rPi model 4 B has /dev/i2c-1 as its only I2C device
14//! let i2c = hal::I2cdev::new("/dev/i2c-1").unwrap();
15//! // The Qwiic Button LED's default address is 0x6F, but is user-configurable
16//! let address = 0x6F;
17//! let mut button = ButtonLED::init(i2c, address);
18//! loop {
19//!     let status = button.button_status().unwrap();
20//!     if status.pressed {
21//!         // if the button is pressed, turn the LED on
22//!         button.set_led_brightness(255).unwrap()
23//!     } else {
24//!         // otherwise, turn it off
25//!         button.set_led_brightness(0).unwrap();
26//!     }
27//!     // sleep a bit to not hammer the I2C bus
28//!     thread::sleep(time::Duration::from_millis(10));
29//! }
30//!
31//! ```
32//! This example turns the LED on when the button is depressed and turns the LED off when the
33//! button is released.
34//!
35//! The button LED supports both static brightness settings and dynamic pulsing. When setting the
36//! brightness statically, the LED stays on. The pulse cycle and pulse off time values configure
37//! LED pulsing. The pulse cycle time configures how long the LED is on for while pulsing, and the
38//! off time configures how long the LED is off while pulsing.
39//!
40//! ```rust
41//! let mut button = ButtonLED::init(i2c, address);
42//! // 300 ms on, 300 ms off, in a loop
43//! button.set_led_pulse_cycle_time(300);
44//! button.set_led_pulse_off_time(300);
45//! ```
46//!
47//! [SparkFun Qwiic Button LED]: https://www.sparkfun.com/products/15932
48//! [embedded-hal]: https://docs.rs/embedded-hal
49
50#![no_std]
51#![forbid(unsafe_code)]
52#![forbid(missing_docs)]
53
54// based on https://github.com/ferrous-systems/adafruit-seesaw. thanks,
55// jamesmunns!
56
57use embedded_hal::blocking::i2c::{Write, WriteRead};
58
59#[repr(u8)]
60#[derive(Clone, Copy, Debug)]
61enum Register {
62    DeviceId = 0x00,
63    FirmwareMinor = 0x01,
64    ButtonStatus = 0x03,
65    InterruptConfig = 0x04,
66    ButtonDebounceTime = 0x05,
67    PressedQueueStatus = 0x07,
68    PressedQueueFront = 0x08,
69    PressedQueueBack = 0x0C,
70    ClickedQueueStatus = 0x10,
71    ClickedQueueFront = 0x11,
72    ClickedQueueBack = 0x15,
73    LedBrightness = 0x19,
74    LedPulseGranularity = 0x1A,
75    LedPulseCycleTime = 0x1B,
76    LedPulseOffTime = 0x1D,
77    I2CAddress = 0x1F,
78}
79
80/// The Error crate represents errors the API can encounter.
81#[derive(Debug)]
82pub enum Error {
83    /// An error occurred in I2C communication, e.g. device no longer responding or it sent an
84    /// unexpected response.
85    I2C,
86    /// A runtime error representing incorrect API usage.
87    QwiicButton(QwiicButtonError),
88}
89
90/// Runtime API errors.
91#[derive(Debug)]
92pub enum QwiicButtonError {
93    /// The address specified for the `set_device_address` function is outside of the valid range
94    /// of 0x08 - 0x77.
95    AddressError,
96}
97
98/// This struct contains all the functions to interface with the Qwiic button LED.
99#[derive(Debug)]
100pub struct ButtonLED<I2C> {
101    i2c: I2C,
102    address: u8,
103}
104
105/// The button has several status fields which change during operation.
106#[derive(Debug)]
107pub struct ButtonStatus {
108    /// An event (clicked or pressed) is available on the button.
109    pub event_available: bool,
110    /// The button has been clicked (pressed and released).
111    pub clicked: bool,
112    /// The button is pressed presently.
113    pub pressed: bool,
114}
115
116impl From<u8> for ButtonStatus {
117    fn from(val: u8) -> Self {
118        ButtonStatus {
119            event_available: 0b1 & val == 1,
120            clicked: 0b10 & val == 2,
121            pressed: 0b100 & val == 4,
122        }
123    }
124}
125
126impl From<ButtonStatus> for u8 {
127    fn from(val: ButtonStatus) -> u8 {
128        (val.event_available as u8 & 0b1)
129            | (val.clicked as u8 & 0b1) << 1
130            | (val.pressed as u8 & 0b1) << 2
131    }
132}
133
134/// The button can raise interrupts when pressed and clicked.
135#[derive(Debug)]
136pub struct InterruptConfig {
137    /// The button raises an interrupt when pressed.
138    pub pressed: bool,
139    /// The button raises an interrupt when clicked (pressed and released).
140    pub clicked: bool,
141}
142
143impl From<u8> for InterruptConfig {
144    fn from(val: u8) -> Self {
145        InterruptConfig {
146            pressed: 0b01 & val == 1,
147            clicked: 0b10 & val == 2,
148        }
149    }
150}
151
152impl From<InterruptConfig> for u8 {
153    fn from(val: InterruptConfig) -> u8 {
154        (val.clicked as u8 & 0b1) << 1 | (val.pressed as u8 & 0b1)
155    }
156}
157
158/// The press queue is a 15 element buffer recording timestamps of button presses.
159#[derive(Debug)]
160pub struct PressedQueueStatus {
161    /// Press queue empty.
162    pub buffer_empty: bool,
163    /// Press queue full.
164    pub buffer_full: bool,
165}
166
167impl From<u8> for PressedQueueStatus {
168    fn from(val: u8) -> Self {
169        PressedQueueStatus {
170            buffer_empty: 0b010 & val == 2,
171            buffer_full: 0b100 & val == 4,
172        }
173    }
174}
175
176/// The click queue is a 15 element buffer recording timestamps of button clicks.
177#[derive(Debug)]
178pub struct ClickedQueueStatus {
179    /// Click queue empty.
180    pub buffer_empty: bool,
181    /// Click queue full.
182    pub buffer_full: bool,
183}
184
185impl From<u8> for ClickedQueueStatus {
186    fn from(val: u8) -> Self {
187        ClickedQueueStatus {
188            buffer_empty: 0b010 & val == 2,
189            buffer_full: 0b100 & val == 4,
190        }
191    }
192}
193
194const ADDRESS_MIN: u8 = 0x08;
195const ADDRESS_MAX: u8 = 0x77;
196
197impl<I2C> ButtonLED<I2C>
198where
199    I2C: Write + WriteRead,
200{
201    fn write_u8(&mut self, op: Register, payload: u8) -> Result<(), Error> {
202        self.i2c
203            .write(self.address, &[op as u8, payload])
204            .map_err(|_| Error::I2C)
205    }
206
207    fn read_u8(&mut self, op: Register) -> Result<u8, Error> {
208        let mut buf: [u8; 1] = [0u8; 1];
209        match self.i2c.write_read(self.address, &[op as u8], &mut buf) {
210            Ok(_) => Ok(buf[0]),
211            Err(_) => Err(Error::I2C),
212        }
213    }
214
215    fn write_u16(&mut self, op: Register, payload: u16) -> Result<(), Error> {
216        let mut cmd: [u8; 3] = [0u8; 3];
217        cmd[0] = op as u8;
218        cmd[1..].copy_from_slice(&payload.to_le_bytes());
219        self.i2c.write(self.address, &cmd).map_err(|_| Error::I2C)
220    }
221
222    fn read_u16(&mut self, op: Register) -> Result<u16, Error> {
223        let mut buf: [u8; 2] = [0u8; 2];
224        match self.i2c.write_read(self.address, &[op as u8], &mut buf) {
225            Ok(_) => Ok(u16::from_le_bytes(buf)),
226            Err(_) => Err(Error::I2C),
227        }
228    }
229
230    fn read_u32(&mut self, op: Register) -> Result<u32, Error> {
231        let mut buf: [u8; 4] = [0u8; 4];
232        match self.i2c.write_read(self.address, &[op as u8], &mut buf) {
233            Ok(_) => Ok(u32::from_le_bytes(buf)),
234            Err(_) => Err(Error::I2C),
235        }
236    }
237
238    /// Initialize a ButtonLED struct
239    pub fn init(i2c: I2C, address: u8) -> Self {
240        Self { i2c, address }
241    }
242
243    /// The button ID is read-only and always returns 0x5D.
244    pub fn button_id(&mut self) -> Result<u8, Error> {
245        self.read_u8(Register::DeviceId)
246    }
247
248    /// The firmware version for this button LED.
249    pub fn firmware_version(&mut self) -> Result<u16, Error> {
250        self.read_u16(Register::FirmwareMinor)
251    }
252
253    /// Determine the status of this button.
254    pub fn button_status(&mut self) -> Result<ButtonStatus, Error> {
255        self.read_u8(Register::ButtonStatus).map(|r| r.into())
256    }
257
258    /// Set the button status, e.g. to clear events
259    pub fn set_button_status(&mut self, new_status: ButtonStatus) -> Result<(), Error> {
260        self.write_u8(Register::ButtonStatus, new_status.into())
261    }
262
263    /// Query the interrupt configuration.
264    pub fn interrupt_config(&mut self) -> Result<InterruptConfig, Error> {
265        self.read_u8(Register::InterruptConfig).map(|r| r.into())
266    }
267
268    /// Manipulate the interrupt configuration.
269    pub fn set_interrupt_config(&mut self, config: InterruptConfig) -> Result<(), Error> {
270        self.write_u8(Register::InterruptConfig, config.into())
271    }
272
273    /// Query the debounce time, a measurement of how long after the button is released that it
274    /// requires to reset to the default state.
275    pub fn button_debounce_time(&mut self) -> Result<u16, Error> {
276        self.read_u16(Register::ButtonDebounceTime)
277    }
278
279    /// Manipulate the debounce time.
280    pub fn set_button_debounce_time(&mut self, time: u16) -> Result<(), Error> {
281        self.write_u16(Register::ButtonDebounceTime, time)
282    }
283
284    /// Is the button pressed queue full.
285    pub fn pressed_queue_full(&mut self) -> Result<bool, Error> {
286        self.read_u8(Register::PressedQueueStatus)
287            .map(|r| r & 0b100 == 4)
288    }
289
290    /// Is the button pressed queue empty.
291    pub fn pressed_queue_empty(&mut self) -> Result<bool, Error> {
292        self.read_u8(Register::PressedQueueStatus)
293            .map(|r| r & 0b010 == 2)
294    }
295
296    /// Query the button pressed queue: empty / full.
297    pub fn pressed_queue_status(&mut self) -> Result<PressedQueueStatus, Error> {
298        self.read_u8(Register::PressedQueueStatus).map(|r| r.into())
299    }
300
301    /// The timestamp in milliseconds since the most recent button press in the queue.
302    pub fn newest_press_timestamp(&mut self) -> Result<u32, Error> {
303        let stamp = self.read_u32(Register::PressedQueueFront)?;
304        let mut current_status = self.read_u8(Register::PressedQueueStatus)?;
305        current_status |= 1;
306        self.write_u8(Register::PressedQueueStatus, current_status)?;
307        Ok(stamp)
308    }
309
310    /// The timestamp in milliseconds since the first button press in the queue.
311    pub fn oldest_press_timestamp(&mut self) -> Result<u32, Error> {
312        let stamp = self.read_u32(Register::PressedQueueBack)?;
313        let mut current_status = self.read_u8(Register::PressedQueueStatus)?;
314        current_status |= 1;
315        self.write_u8(Register::PressedQueueStatus, current_status)?;
316        Ok(stamp)
317    }
318
319    /// Is the button clicked queue full.
320    pub fn clicked_queue_full(&mut self) -> Result<bool, Error> {
321        self.read_u8(Register::ClickedQueueStatus)
322            .map(|r| r & 0b100 == 4)
323    }
324
325    /// Is the button clicked queue empty.
326    pub fn clicked_queue_empty(&mut self) -> Result<bool, Error> {
327        self.read_u8(Register::ClickedQueueStatus)
328            .map(|r| r & 0b010 == 2)
329    }
330
331    /// Query the button pressed queue: empty / full.
332    pub fn clicked_queue_status(&mut self) -> Result<ClickedQueueStatus, Error> {
333        self.read_u8(Register::ClickedQueueStatus).map(|r| r.into())
334    }
335
336    /// The timestamp in milliseconds since the most recent button click in the queue.
337    pub fn newest_click_timestamp(&mut self) -> Result<u32, Error> {
338        let stamp = self.read_u32(Register::ClickedQueueFront)?;
339        let mut current_status = self.read_u8(Register::ClickedQueueStatus)?;
340        current_status |= 1;
341        self.write_u8(Register::ClickedQueueStatus, current_status)?;
342        Ok(stamp)
343    }
344
345    /// The timestamp in milliseconds since the first button click in the queue.
346    pub fn oldest_click_timestamp(&mut self) -> Result<u32, Error> {
347        let stamp = self.read_u32(Register::ClickedQueueBack)?;
348        let mut current_status = self.read_u8(Register::ClickedQueueStatus)?;
349        current_status |= 1;
350        self.write_u8(Register::ClickedQueueStatus, current_status)?;
351        Ok(stamp)
352    }
353
354    /// The LED brightness.
355    pub fn led_brightness(&mut self) -> Result<u8, Error> {
356        self.read_u8(Register::LedBrightness)
357    }
358
359    /// Set LED brightness.
360    pub fn set_led_brightness(&mut self, brightness: u8) -> Result<(), Error> {
361        self.write_u8(Register::LedBrightness, brightness)
362    }
363
364    /// The number of steps the LED uses from one brightness setting another when updated.
365    pub fn led_pulse_granularity(&mut self) -> Result<u8, Error> {
366        self.read_u8(Register::LedPulseGranularity)
367    }
368
369    /// Set the LED update granularity.
370    pub fn set_led_pulse_granularity(&mut self, brightness: u8) -> Result<(), Error> {
371        self.write_u8(Register::LedPulseGranularity, brightness)
372    }
373
374    /// When set, the LED pulses on for this value in milliseconds.
375    pub fn led_pulse_cycle_time(&mut self) -> Result<u16, Error> {
376        self.read_u16(Register::LedPulseCycleTime)
377    }
378
379    /// Set the LED pulse on time in milliseconds (0 disables).
380    pub fn set_led_pulse_cycle_time(&mut self, cycle_time: u16) -> Result<(), Error> {
381        self.write_u16(Register::LedPulseCycleTime, cycle_time)
382    }
383
384    /// The duration in milliseconds the LED is off while pulsing.
385    pub fn led_pulse_off_time(&mut self) -> Result<u16, Error> {
386        self.read_u16(Register::LedPulseOffTime)
387    }
388
389    /// Set the LED off time in milliseconds (0 disables).
390    pub fn set_led_pulse_off_time(&mut self, off_time: u16) -> Result<(), Error> {
391        self.write_u16(Register::LedPulseOffTime, off_time)
392    }
393
394    /// The address of this button LED.
395    pub fn device_address(&mut self) -> Result<u8, Error> {
396        self.read_u8(Register::I2CAddress)
397    }
398
399    /// Set the address of this button LED (0x08 - 0x77).
400    pub fn set_device_address(&mut self, new_address: u8) -> Result<(), Error> {
401        if !(ADDRESS_MIN..=ADDRESS_MAX).contains(&new_address) {
402            return Err(Error::QwiicButton(QwiicButtonError::AddressError));
403        }
404        match self.write_u8(Register::I2CAddress, new_address) {
405            Ok(_) => {
406                self.address = new_address;
407                Ok(())
408            }
409            Err(e) => Err(e),
410        }
411    }
412}