1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
extern crate spidev;
extern crate failure;

use failure::Error;
use std::io::prelude::*;
use spidev::{Spidev, SpidevOptions, SPI_MODE_0};

/// Provide high-level access to the Unicorn Hat HD.
pub struct UnicornHatHd {
  leds: [[UnicornHatHdLed; 16]; 16],
  spi: Spidev,
}

impl UnicornHatHd {
  /// Create a new `UnicornHatHd` with the provided path
  ///
  /// The Unicorn HAT HD should be addressable using the spidev
  /// device with the provided path
  ///
  /// Typically, the path will be something like `"/dev/spidev0.0"`
  /// where the first number if the bus and the second number
  /// is the chip select on that bus for the device being targeted.
  fn new(spi_path: &str) -> Result<UnicornHatHd, Error> {
    let mut spidev = try!(Spidev::open(spi_path));
    let options = SpidevOptions::new()
         .bits_per_word(8)
         .max_speed_hz(9_000_000)
         .mode(SPI_MODE_0)
         .build();
    try!(spidev.configure(&options));
    Ok(UnicornHatHd {
      leds: [[UnicornHatHdLed::default(); 16]; 16],
      spi: spidev,
    })
  }

  /// Write the display buffer to the Unicorn HAT HD.
  pub fn display(&mut self) -> Result<(), Error> {
    self.spi.write(&[0x72])?;
    let data = self.as_array();
    self.spi.write(&data)?;
    Ok(())
  }

  /// Set an individual pixel's RGB value.
  pub fn set_pixel(&mut self, x: usize, y: usize, r: u8, g: u8, b: u8) {
    self.leds[x][y].set_rgb(r, g, b);
  }

  /// Return a tuple of an individual pixel's RGB value.
  ///
  /// This returns what's in the display buffer, not what the
  /// physical pixel is set to.
  pub fn get_pixel(&self, x: usize, y: usize) -> (u8, u8, u8) {
    self.leds[x][y].get_rgb()
  }

  fn as_array(&self) -> Vec<u8> {
    let mut arr: Vec<u8> = vec![];

    for row in self.leds.iter() {
      for led in row.iter() {
        let (r, g, b) = led.get_rgb();
        arr.push(r);
        arr.push(g);
        arr.push(b);
      }
    }

    arr
  }
}

impl Default for UnicornHatHd {
  /// Create a `UnicornHatHd` using the default path of "`/dev/spidev0.0`".
  ///
  /// This will panic if the default path is not usable.
  fn default() -> UnicornHatHd {
    UnicornHatHd::new("/dev/spidev0.0").unwrap()
  }
}

#[derive(Clone,Copy)]
struct UnicornHatHdLed {
  r: u8,
  b: u8,
  g: u8,
}

impl UnicornHatHdLed {
  pub fn set_rgb(&mut self, r: u8, g: u8, b: u8) {
    self.r = r;
    self.g = g;
    self.b = b;
  }

  pub fn get_rgb(&self) -> (u8, u8, u8) {
    (self.r, self.g, self.b)
  }
}

impl Default for UnicornHatHdLed {
  fn default() -> UnicornHatHdLed {
    UnicornHatHdLed {
      r: 0,
      g: 0,
      b: 0,
    }
  }
}