extern crate byteorder;
extern crate i2cdev;
extern crate measurements;
#[cfg(feature = "led-matrix")]
extern crate tint;
#[cfg(feature = "rtimu")]
extern crate libc;
#[cfg(feature = "led-matrix")]
extern crate sensehat_screen;
mod hts221;
mod lps25h;
mod rh;
pub use measurements::Angle;
pub use measurements::Pressure;
pub use measurements::Temperature;
pub use rh::RelativeHumidity;
use i2cdev::linux::{LinuxI2CDevice, LinuxI2CError};
#[cfg(feature = "rtimu")]
mod lsm9ds1;
#[cfg(not(feature = "rtimu"))]
mod lsm9ds1_dummy;
#[cfg(not(feature = "rtimu"))]
use lsm9ds1_dummy as lsm9ds1;
#[cfg(feature = "led-matrix")]
use sensehat_screen::color::PixelColor;
#[derive(Debug, Copy, Clone)]
pub struct Orientation {
pub roll: Angle,
pub pitch: Angle,
pub yaw: Angle,
}
#[derive(Debug, Copy, Clone)]
pub struct Vector3D {
pub x: f64,
pub y: f64,
pub z: f64,
}
#[cfg(feature = "led-matrix")]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Colour(PixelColor);
#[cfg(feature = "led-matrix")]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Fps(pub u8);
#[derive(Debug, Default)]
struct ImuData {
timestamp: u64,
fusion_pose: Option<Orientation>,
gyro: Option<Vector3D>,
accel: Option<Vector3D>,
compass: Option<Vector3D>,
pressure: Option<f64>,
temperature: Option<f64>,
humidity: Option<f64>,
}
pub struct SenseHat<'a> {
pressure_chip: lps25h::Lps25h<LinuxI2CDevice>,
humidity_chip: hts221::Hts221<LinuxI2CDevice>,
accelerometer_chip: lsm9ds1::Lsm9ds1<'a>,
data: ImuData,
}
#[derive(Debug)]
pub enum SenseHatError {
NotReady,
GenericError,
I2CError(LinuxI2CError),
LSM9DS1Error(lsm9ds1::Error),
ScreenError,
CharacterError(std::string::FromUtf16Error),
}
pub type SenseHatResult<T> = Result<T, SenseHatError>;
impl<'a> SenseHat<'a> {
pub fn new() -> SenseHatResult<SenseHat<'a>> {
Ok(SenseHat {
humidity_chip: hts221::Hts221::new(LinuxI2CDevice::new("/dev/i2c-1", 0x5f)?)?,
pressure_chip: lps25h::Lps25h::new(LinuxI2CDevice::new("/dev/i2c-1", 0x5c)?)?,
accelerometer_chip: lsm9ds1::Lsm9ds1::new()?,
data: ImuData::default(),
})
}
pub fn get_temperature_from_pressure(&mut self) -> SenseHatResult<Temperature> {
let status = self.pressure_chip.status()?;
if (status & 1) != 0 {
Ok(Temperature::from_celsius(
self.pressure_chip.get_temp_celcius()?
))
} else {
Err(SenseHatError::NotReady)
}
}
pub fn get_pressure(&mut self) -> SenseHatResult<Pressure> {
let status = self.pressure_chip.status()?;
if (status & 2) != 0 {
Ok(Pressure::from_hectopascals(
self.pressure_chip.get_pressure_hpa()?
))
} else {
Err(SenseHatError::NotReady)
}
}
pub fn get_temperature_from_humidity(&mut self) -> SenseHatResult<Temperature> {
let status = self.humidity_chip.status()?;
if (status & 1) != 0 {
let celcius = self.humidity_chip.get_temperature_celcius()?;
Ok(Temperature::from_celsius(celcius))
} else {
Err(SenseHatError::NotReady)
}
}
pub fn get_humidity(&mut self) -> SenseHatResult<RelativeHumidity> {
let status = self.humidity_chip.status()?;
if (status & 2) != 0 {
let percent = self.humidity_chip.get_relative_humidity_percent()?;
Ok(RelativeHumidity::from_percent(percent))
} else {
Err(SenseHatError::NotReady)
}
}
pub fn get_orientation(&mut self) -> SenseHatResult<Orientation> {
self.accelerometer_chip.set_fusion();
if self.accelerometer_chip.imu_read() {
self.data = self.accelerometer_chip.get_imu_data()?;
}
match self.data.fusion_pose {
Some(o) => Ok(o),
None => Err(SenseHatError::NotReady),
}
}
pub fn get_compass(&mut self) -> SenseHatResult<Angle> {
self.accelerometer_chip.set_compass_only();
if self.accelerometer_chip.imu_read() {
let data = self.accelerometer_chip.get_imu_data()?;
match data.fusion_pose {
Some(o) => Ok(o.yaw),
None => Err(SenseHatError::NotReady),
}
} else {
Err(SenseHatError::NotReady)
}
}
pub fn get_gyro(&mut self) -> SenseHatResult<Orientation> {
self.accelerometer_chip.set_gyro_only();
if self.accelerometer_chip.imu_read() {
let data = self.accelerometer_chip.get_imu_data()?;
match data.fusion_pose {
Some(o) => Ok(o),
None => Err(SenseHatError::NotReady),
}
} else {
Err(SenseHatError::NotReady)
}
}
pub fn get_accel(&mut self) -> SenseHatResult<Orientation> {
self.accelerometer_chip.set_accel_only();
if self.accelerometer_chip.imu_read() {
let data = self.accelerometer_chip.get_imu_data()?;
match data.fusion_pose {
Some(o) => Ok(o),
None => Err(SenseHatError::NotReady),
}
} else {
Err(SenseHatError::NotReady)
}
}
pub fn get_accel_raw(&mut self) -> SenseHatResult<Vector3D> {
self.accelerometer_chip.set_accel_only();
if self.accelerometer_chip.imu_read() {
self.data = self.accelerometer_chip.get_imu_data()?;
}
match self.data.accel {
Some(a) => Ok(a),
None => Err(SenseHatError::NotReady),
}
}
#[cfg(feature = "led-matrix")]
pub fn show_message<INT, FG, BG>(&mut self, message: &str, interval: INT, fg: FG, bg: BG) -> SenseHatResult<()>
where
INT: Into<::std::time::Duration>,
FG: Into<Colour>,
BG: Into<Colour>,
{
let wait_time = interval.into();
let mut screen =
sensehat_screen::Screen::open("/dev/fb1").map_err(|_| SenseHatError::ScreenError)?;
let fonts = sensehat_screen::FontCollection::new();
let sanitized = fonts.sanitize_str(message)?;
let pixel_frames = sanitized.pixel_frames(fg.into().0, bg.into().0);
let scroll = sensehat_screen::Scroll::new(&pixel_frames);
scroll.right_to_left().for_each(|frame| {
screen.write_frame(&frame.frame_line());
::std::thread::sleep(wait_time);
});
Ok(())
}
#[cfg(feature = "led-matrix")]
pub fn text<FG, BG>(&mut self, message: &str, fg: FG, bg: BG) -> SenseHatResult<()>
where
FG: Into<Colour>,
BG: Into<Colour>,
{
self.show_message(message, ::std::time::Duration::from_millis(100), fg, bg)
}
#[cfg(feature = "led-matrix")]
pub fn clear(&mut self) -> SenseHatResult<()> {
let mut screen =
sensehat_screen::Screen::open("/dev/fb1").map_err(|_| SenseHatError::ScreenError)?;
const OFF: [u8; 128] = [0x00; 128];
screen.write_frame(&sensehat_screen::FrameLine::from_slice(&OFF));
Ok(())
}
}
impl From<LinuxI2CError> for SenseHatError {
fn from(err: LinuxI2CError) -> SenseHatError {
SenseHatError::I2CError(err)
}
}
impl From<lsm9ds1::Error> for SenseHatError {
fn from(err: lsm9ds1::Error) -> SenseHatError {
SenseHatError::LSM9DS1Error(err)
}
}
impl From<std::string::FromUtf16Error> for SenseHatError {
fn from(err: std::string::FromUtf16Error) -> SenseHatError {
SenseHatError::CharacterError(err)
}
}
#[cfg(feature = "led-matrix")]
impl<'a> Into<Colour> for &'a str {
fn into(self) -> Colour {
let rgb = tint::Color::name(self).unwrap();
Colour(rgb.to_rgb255().into())
}
}
#[cfg(feature = "led-matrix")]
impl<'a> Into<Colour> for (u8, u8, u8) {
fn into(self) -> Colour {
Colour(self.into())
}
}
#[cfg(feature = "led-matrix")]
impl<'a> Into<::std::time::Duration> for Fps {
fn into(self) -> ::std::time::Duration {
::std::time::Duration::new(0, 1_000_000_000 / self.0 as u32)
}
}
#[cfg(feature = "led-matrix")]
impl Colour {
pub const WHITE: Colour = Colour(PixelColor::WHITE);
pub const RED: Colour = Colour(PixelColor::RED);
pub const GREEN: Colour = Colour(PixelColor::GREEN);
pub const BLUE: Colour = Colour(PixelColor::BLUE);
pub const BLACK: Colour = Colour(PixelColor::BLACK);
pub const YELLOW: Colour = Colour(PixelColor::YELLOW);
pub const MAGENTA: Colour = Colour(PixelColor::MAGENTA);
pub const CYAN: Colour = Colour(PixelColor::CYAN);
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(feature = "led-matrix")]
#[test]
fn check_colours_string() {
let colour_str: Colour = "red".into();
let colour_const: Colour = Colour::RED;
assert_eq!(colour_str, colour_const);
}
#[cfg(feature = "led-matrix")]
#[test]
fn check_colours_tuple() {
let colour_tuple: Colour = (0xFF, 0, 0).into();
let colour_const: Colour = Colour::RED;
assert_eq!(colour_tuple, colour_const);
}
}