#![doc(html_root_url = "https://docs.rs/blinkt/0.5.0")]
#![recursion_limit = "128"]
#[macro_use]
extern crate quick_error;
extern crate rppal;
use std::{io, result};
use rppal::gpio::{Gpio, Level, Mode};
use rppal::spi;
pub use rppal::gpio::Error as GpioError;
pub use rppal::spi::Error as SpiError;
const DAT: u8 = 23;
const CLK: u8 = 24;
const NUM_PIXELS: usize = 8;
const DEFAULT_BRIGHTNESS: u8 = 7;
quick_error! {
#[derive(Debug)]
pub enum Error {
Gpio(err: GpioError) { description(err.description()) from() }
Spi(err: SpiError) { description(err.description()) from() }
Io(err: io::Error) { description(err.description()) from() }
}
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug, Copy, Clone)]
struct Pixel {
value: [u8; 4], }
impl Pixel {
fn set_rgb(&mut self, red: u8, green: u8, blue: u8) {
self.value[1] = blue;
self.value[2] = green;
self.value[3] = red;
}
fn set_brightness(&mut self, brightness: f32) {
self.value[0] = 0b1110_0000 | ((31.0 * brightness.max(0.0).min(1.0)) as u8);
}
fn set_rgbb(&mut self, red: u8, green: u8, blue: u8, brightness: f32) {
self.set_rgb(red, green, blue);
self.set_brightness(brightness);
}
fn bytes(&self) -> &[u8] {
&self.value
}
}
impl Default for Pixel {
fn default() -> Pixel {
Pixel {
value: [0b1110_0000 | DEFAULT_BRIGHTNESS, 0, 0, 0],
}
}
}
trait SerialOutput {
fn cleanup(&mut self);
fn write(&mut self, data: &[u8]) -> Result<()>;
}
struct BlinktGpio {
gpio: Gpio,
pin_data: u8,
pin_clock: u8,
}
impl BlinktGpio {
pub fn with_settings(pin_data: u8, pin_clock: u8) -> Result<BlinktGpio> {
let mut gpio = Gpio::new()?;
gpio.set_mode(pin_data, Mode::Output);
gpio.write(pin_data, Level::Low);
gpio.set_mode(pin_clock, Mode::Output);
gpio.write(pin_clock, Level::Low);
Ok(BlinktGpio {
gpio,
pin_data,
pin_clock,
})
}
}
impl SerialOutput for BlinktGpio {
fn cleanup(&mut self) {
self.gpio.cleanup();
}
fn write(&mut self, data: &[u8]) -> Result<()> {
for byte in data {
for n in 0..8 {
if (byte & (1 << (7 - n))) > 0 {
self.gpio.write(self.pin_data, Level::High);
} else {
self.gpio.write(self.pin_data, Level::Low);
}
self.gpio.write(self.pin_clock, Level::High);
self.gpio.write(self.pin_clock, Level::Low);
}
}
Ok(())
}
}
struct BlinktSpi {
spi: spi::Spi,
}
impl BlinktSpi {
pub fn with_settings(clock_speed_hz: u32) -> Result<BlinktSpi> {
Ok(BlinktSpi {
spi: spi::Spi::new(
spi::Bus::Spi0,
spi::SlaveSelect::Ss0,
clock_speed_hz,
spi::Mode::Mode0,
)?,
})
}
}
impl SerialOutput for BlinktSpi {
fn cleanup(&mut self) {}
fn write(&mut self, data: &[u8]) -> Result<()> {
self.spi.write(data)?;
Ok(())
}
}
pub struct Blinkt {
serial_output: Box<SerialOutput>,
pixels: Vec<Pixel>,
clear_on_drop: bool,
end_frame: Vec<u8>,
}
impl Blinkt {
pub fn new() -> Result<Blinkt> {
Blinkt::with_settings(DAT, CLK, NUM_PIXELS)
}
pub fn with_settings(pin_data: u8, pin_clock: u8, num_pixels: usize) -> Result<Blinkt> {
Ok(Blinkt {
serial_output: Box::new(BlinktGpio::with_settings(pin_data, pin_clock)?),
pixels: vec![Pixel::default(); num_pixels],
clear_on_drop: true,
end_frame: vec![0u8; 4 + (((num_pixels as f32 / 16.0f32) + 0.94f32) as usize)],
})
}
pub fn with_spi(clock_speed_hz: u32, num_pixels: usize) -> Result<Blinkt> {
Ok(Blinkt {
serial_output: Box::new(BlinktSpi::with_settings(clock_speed_hz)?),
pixels: vec![Pixel::default(); num_pixels],
clear_on_drop: true,
end_frame: vec![0u8; 4 + (((num_pixels as f32 / 16.0f32) + 0.94f32) as usize)],
})
}
pub fn set_clear_on_drop(&mut self, clear_on_drop: bool) {
self.clear_on_drop = clear_on_drop;
}
pub fn cleanup(&mut self) -> Result<()> {
if self.clear_on_drop {
self.clear();
self.show()?;
}
self.serial_output.cleanup();
Ok(())
}
pub fn set_pixel(&mut self, pixel: usize, red: u8, green: u8, blue: u8) {
if let Some(pixel) = self.pixels.get_mut(pixel) {
pixel.set_rgb(red, green, blue);
}
}
pub fn set_pixel_rgbb(&mut self, pixel: usize, red: u8, green: u8, blue: u8, brightness: f32) {
if let Some(pixel) = self.pixels.get_mut(pixel) {
pixel.set_rgbb(red, green, blue, brightness);
}
}
pub fn set_pixel_brightness(&mut self, pixel: usize, brightness: f32) {
if let Some(pixel) = self.pixels.get_mut(pixel) {
pixel.set_brightness(brightness);
}
}
pub fn set_all_pixels(&mut self, red: u8, green: u8, blue: u8) {
for pixel in &mut self.pixels {
pixel.set_rgb(red, green, blue);
}
}
pub fn set_all_pixels_rgbb(&mut self, red: u8, green: u8, blue: u8, brightness: f32) {
for pixel in &mut self.pixels {
pixel.set_rgbb(red, green, blue, brightness);
}
}
pub fn set_all_pixels_brightness(&mut self, brightness: f32) {
for pixel in &mut self.pixels {
pixel.set_brightness(brightness);
}
}
pub fn clear(&mut self) {
self.set_all_pixels(0, 0, 0);
}
pub fn show(&mut self) -> Result<()> {
self.serial_output.write(&[0u8; 4])?;
for pixel in &self.pixels {
self.serial_output.write(pixel.bytes())?;
}
self.serial_output.write(&self.end_frame)?;
Ok(())
}
}
impl Drop for Blinkt {
fn drop(&mut self) {
self.cleanup().unwrap_or(());
}
}
#[test]
fn test_new() {
let mut blinkt = match Blinkt::new() {
Err(_) => return,
Ok(blinkt) => blinkt,
};
blinkt.set_clear_on_drop(false);
}