mirror of
https://github.com/FliegendeWurst/raspi-oled.git
synced 2024-11-24 11:14:58 +00:00
Various changes..
This commit is contained in:
parent
98acedca0e
commit
a24f9d86f1
151
Cargo.lock
generated
151
Cargo.lock
generated
@ -2,6 +2,17 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -50,6 +61,21 @@ version = "0.1.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dht-hal"
|
||||||
|
version = "0.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb5423e6132fe67189d1aeac8a7412e7d09816a7374e22201a49a2d5081c8549"
|
||||||
|
dependencies = [
|
||||||
|
"embedded-hal",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "display-interface"
|
name = "display-interface"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -110,6 +136,18 @@ dependencies = [
|
|||||||
"void",
|
"void",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-iterator"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-streaming-iterator"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "float-cmp"
|
name = "float-cmp"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -129,6 +167,46 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gpio-cdev"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da59d440ef8c26208b3960ed58b71b896db7dfd6bb138622bba733b66d978c3a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
"nix",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashlink"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "i2cdev"
|
name = "i2cdev"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
@ -152,9 +230,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.65"
|
version = "0.2.98"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8"
|
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libsqlite3-sys"
|
||||||
|
version = "0.22.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-embedded-hal"
|
name = "linux-embedded-hal"
|
||||||
@ -178,6 +267,12 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8197ad28c61566fb74b2167563865906215fbc32c61fd866bd54e7c195eb84ed"
|
checksum = "8197ad28c61566fb74b2167563865906215fbc32c61fd866bd54e7c195eb84ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "micromath"
|
name = "micromath"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
@ -207,7 +302,7 @@ checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if 0.1.10",
|
||||||
"libc",
|
"libc",
|
||||||
"void",
|
"void",
|
||||||
]
|
]
|
||||||
@ -221,16 +316,48 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raspi-oled"
|
name = "raspi-oled"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"dht-hal",
|
||||||
"embedded-graphics",
|
"embedded-graphics",
|
||||||
|
"embedded-hal",
|
||||||
|
"gpio-cdev",
|
||||||
|
"libc",
|
||||||
"linux-embedded-hal",
|
"linux-embedded-hal",
|
||||||
"machine-ip",
|
"machine-ip",
|
||||||
|
"rusqlite",
|
||||||
"ssd1306",
|
"ssd1306",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusqlite"
|
||||||
|
version = "0.25.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57adcf67c8faaf96f3248c2a7b419a0dbc52ebe36ba83dd57fe83827c1ea4eb3"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"fallible-iterator",
|
||||||
|
"fallible-streaming-iterator",
|
||||||
|
"hashlink",
|
||||||
|
"libsqlite3-sys",
|
||||||
|
"memchr",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serial-core"
|
name = "serial-core"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -252,6 +379,12 @@ dependencies = [
|
|||||||
"termios",
|
"termios",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spidev"
|
name = "spidev"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -301,6 +434,12 @@ version = "1.13.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
@ -312,3 +451,9 @@ name = "void"
|
|||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
@ -9,8 +9,13 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-graphics = "0.7.1"
|
embedded-graphics = "0.7.1"
|
||||||
linux-embedded-hal = "0.3.0"
|
linux-embedded-hal = "0.3.0"
|
||||||
|
embedded-hal = "0.2.5"
|
||||||
machine-ip = "0.2.1"
|
machine-ip = "0.2.1"
|
||||||
ssd1306 = "0.6.0"
|
ssd1306 = "0.6.0"
|
||||||
|
libc = "0.2.98"
|
||||||
|
gpio-cdev = "0.4"
|
||||||
|
dht-hal = "0.0.1"
|
||||||
|
rusqlite = { version = "0.25.3", features = ["bundled"] }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
19
LICENSE
Normal file
19
LICENSE
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2022 Arne Keller
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
140
src/lib.rs
Normal file
140
src/lib.rs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
use std::{thread::sleep, time::{self, Duration}};
|
||||||
|
|
||||||
|
use gpio_cdev::{EventType, Line, LineRequestFlags};
|
||||||
|
|
||||||
|
fn read_events(line: &gpio_cdev::Line, timeout: std::time::Duration) -> Result<Vec<(u64, EventType)>, SensorError> {
|
||||||
|
let input = line.request(
|
||||||
|
LineRequestFlags::INPUT,
|
||||||
|
0,
|
||||||
|
"read-data")?;
|
||||||
|
|
||||||
|
let mut last_state = 1;
|
||||||
|
let start = time::Instant::now();
|
||||||
|
|
||||||
|
let mut events = Vec::with_capacity(81);
|
||||||
|
while start.elapsed() < timeout && events.len() < 81 {
|
||||||
|
let new_state = input.get_value()?;
|
||||||
|
if new_state != last_state {
|
||||||
|
let timestamp = start.elapsed();
|
||||||
|
let event_type = if last_state < new_state {
|
||||||
|
EventType::RisingEdge
|
||||||
|
} else {
|
||||||
|
EventType::FallingEdge
|
||||||
|
};
|
||||||
|
events.push((timestamp.as_micros() as u64, event_type));
|
||||||
|
last_state = new_state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if events.len() < 81 {
|
||||||
|
return Err(SensorError::Timeout);
|
||||||
|
}
|
||||||
|
Ok(events)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn events_to_data(events: Vec<(u64, EventType)>) -> Vec<u8> {
|
||||||
|
events[1..]
|
||||||
|
.windows(2)
|
||||||
|
.map(|pair| {
|
||||||
|
let prev = pair.get(0).unwrap();
|
||||||
|
let next = pair.get(1).unwrap();
|
||||||
|
match next.1 {
|
||||||
|
EventType::FallingEdge => Some(next.0 - prev.0),
|
||||||
|
EventType::RisingEdge => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(|&d| d.is_some())
|
||||||
|
.map(|elapsed| {
|
||||||
|
if elapsed.unwrap() > 35 { 1 } else { 0 }
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_HUMIDITY: u16 = 1000;
|
||||||
|
|
||||||
|
fn process_data(mut bits: &[u8]) -> Result<(u16, u16), SensorError> {
|
||||||
|
if bits[0] == 1 {
|
||||||
|
// definitely incorrect first bit
|
||||||
|
// (the humidity can't be this big..)
|
||||||
|
bits = &bits[1..];
|
||||||
|
}
|
||||||
|
let bytes: Vec<u8> = bits
|
||||||
|
.chunks(8)
|
||||||
|
.map(|chunk| chunk.iter()
|
||||||
|
.enumerate()
|
||||||
|
// 8 bits, starting with the MSB
|
||||||
|
.map(|(bit_idx, &x)| x << (7 - bit_idx))
|
||||||
|
.sum()
|
||||||
|
).collect();
|
||||||
|
let rh = (bytes[0] as u16) << 8 | bytes[1] as u16;
|
||||||
|
if rh > MAX_HUMIDITY {
|
||||||
|
return Err(SensorError::HumidityTooHigh);
|
||||||
|
}
|
||||||
|
let celsius = (bytes[2] as u16) << 8 | bytes[3] as u16;
|
||||||
|
|
||||||
|
if bits.len() >= 40 {
|
||||||
|
let cksum: u8 = bits[32..40]
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, &x)| x << (7 - idx))
|
||||||
|
.sum();
|
||||||
|
let actual_sum = (bytes[0].wrapping_add(bytes[1]).wrapping_add(bytes[2]).wrapping_add(bytes[3])) & 0xff;
|
||||||
|
if actual_sum != cksum {
|
||||||
|
return Err(SensorError::ChecksumMismatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((rh, celsius))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_data() {
|
||||||
|
let x = process_data(&[1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1]).unwrap();
|
||||||
|
assert_eq!(471, x.0);
|
||||||
|
assert_eq!(268, x.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SensorError {
|
||||||
|
Io(gpio_cdev::Error),
|
||||||
|
ChecksumMismatch,
|
||||||
|
HumidityTooHigh,
|
||||||
|
Timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<gpio_cdev::Error> for SensorError {
|
||||||
|
fn from(error: gpio_cdev::Error) -> Self {
|
||||||
|
SensorError::Io(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn am2302_reading(line: &Line) -> Result<(u16, u16), SensorError> {
|
||||||
|
line.request(LineRequestFlags::OUTPUT, 1, "rust-am2302").unwrap();
|
||||||
|
sleep(Duration::from_millis(500));
|
||||||
|
set_max_priority();
|
||||||
|
// set low for 20 ms
|
||||||
|
if let Err(e) = line.request(LineRequestFlags::OUTPUT, 0, "rust-am2302") {
|
||||||
|
set_normal_priority();
|
||||||
|
return Err(SensorError::Io(e));
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(3));
|
||||||
|
|
||||||
|
let events = read_events(&line, Duration::from_secs(1));
|
||||||
|
println!("{:?} {:?}", events, events.as_ref().map(|x| x.len()));
|
||||||
|
set_normal_priority();
|
||||||
|
let events = events?;
|
||||||
|
let data = events_to_data(events);
|
||||||
|
process_data(&data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_max_priority() {
|
||||||
|
unsafe {
|
||||||
|
let mut sched_para: libc::sched_param = std::mem::transmute([0u8; std::mem::size_of::<libc::sched_param>()]);
|
||||||
|
sched_para.sched_priority = libc::sched_get_priority_max(libc::SCHED_FIFO);
|
||||||
|
libc::sched_setscheduler(0, libc::SCHED_FIFO, (&sched_para) as *const libc::sched_param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_normal_priority() {
|
||||||
|
unsafe {
|
||||||
|
let sched_para: libc::sched_param = std::mem::transmute([0u8; std::mem::size_of::<libc::sched_param>()]);
|
||||||
|
libc::sched_setscheduler(0, libc::SCHED_OTHER, (&sched_para) as *const libc::sched_param);
|
||||||
|
}
|
||||||
|
}
|
203
src/main.rs
203
src/main.rs
@ -1,4 +1,6 @@
|
|||||||
|
use dht_hal::{Dht22, Reading};
|
||||||
use embedded_graphics::image::{Image, ImageRaw};
|
use embedded_graphics::image::{Image, ImageRaw};
|
||||||
|
use embedded_graphics::mono_font::iso_8859_7::FONT_9X18;
|
||||||
use embedded_graphics::pixelcolor::BinaryColor;
|
use embedded_graphics::pixelcolor::BinaryColor;
|
||||||
use embedded_graphics::prelude::*;
|
use embedded_graphics::prelude::*;
|
||||||
use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle};
|
use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle};
|
||||||
@ -7,17 +9,144 @@ use embedded_graphics::{
|
|||||||
mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder},
|
mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder},
|
||||||
text::Text,
|
text::Text,
|
||||||
};
|
};
|
||||||
|
use embedded_hal::digital::v2::{InputPin, OutputPin};
|
||||||
|
use embedded_hal::prelude::_embedded_hal_blocking_i2c_Write;
|
||||||
|
use gpio_cdev::{Chip, EventRequestFlags, EventType, LineRequestFlags};
|
||||||
|
use linux_embedded_hal::i2cdev::core::I2CDevice;
|
||||||
|
use linux_embedded_hal::i2cdev::linux::LinuxI2CError;
|
||||||
|
use rusqlite::{Connection, params};
|
||||||
use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306};
|
use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306};
|
||||||
use linux_embedded_hal::I2cdev;
|
use linux_embedded_hal::{Delay, I2cdev};
|
||||||
use machine_ip;
|
use machine_ip;
|
||||||
|
use std::intrinsics::transmute;
|
||||||
|
use std::{env, mem, time};
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
static IMG_DATA: &[u8; 512] = include_bytes!("../rust.raw");
|
static IMG_DATA: &[u8; 512] = include_bytes!("../rust.raw");
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let i2c = I2cdev::new("/dev/i2c-1").unwrap();
|
|
||||||
|
|
||||||
|
const CCS811_ADDR: u8 = 0x5A; // or 0x5B
|
||||||
|
|
||||||
|
const CCS811_STATUS: u8 = 0x00;
|
||||||
|
const CCS811_MEAS_MODE: u8 = 0x01;
|
||||||
|
const CCS811_ALG_RESULT_DATA: u8 = 0x02;
|
||||||
|
const CCS811_RAW_DATA: u8 = 0x03;
|
||||||
|
const CCS811_ENV_DATA: u8 = 0x05;
|
||||||
|
const CCS811_NTC: u8 = 0x06;
|
||||||
|
const CCS811_THRESHOLDS: u8 = 0x10;
|
||||||
|
const CCS811_BASELINE: u8 = 0x11;
|
||||||
|
const CCS811_HW_ID: u8 = 0x20;
|
||||||
|
const CCS811_HW_VERSION: u8 = 0x21;
|
||||||
|
const CCS811_FW_BOOT_VERSION: u8 = 0x23;
|
||||||
|
const CCS811_FW_APP_VERSION: u8 = 0x24;
|
||||||
|
const CCS811_ERROR_ID: u8 = 0xE0;
|
||||||
|
const CCS811_APP_START: u8 = 0xF4;
|
||||||
|
const CCS811_SW_RESET: u8 = 0xFF;
|
||||||
|
|
||||||
|
struct CCS811 {
|
||||||
|
i2c: I2cdev,
|
||||||
|
addr: u8
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CCS811 {
|
||||||
|
fn new(mut i2c: I2cdev, addr: u8) -> Self {
|
||||||
|
i2c.set_slave_address(addr as u16).unwrap();
|
||||||
|
Self {
|
||||||
|
i2c,
|
||||||
|
addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_for_error(&mut self) -> Option<u8> {
|
||||||
|
let x = self.i2c.smbus_read_byte_data(CCS811_STATUS).unwrap();
|
||||||
|
if (x & 1) != 0 {
|
||||||
|
let err_code = self.i2c.smbus_read_byte_data(CCS811_ERROR_ID).unwrap();
|
||||||
|
Some(err_code)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hardware_id(&mut self) -> u8 {
|
||||||
|
self.i2c.smbus_read_byte_data(CCS811_HW_ID).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app_valid(&mut self) -> bool {
|
||||||
|
let x = self.i2c.smbus_read_byte_data(CCS811_STATUS).unwrap();
|
||||||
|
x & 1 << 4 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_drive_mode(&mut self, mode: CCS811DriveMode) -> Result<(), Option<LinuxI2CError>> {
|
||||||
|
self.i2c.smbus_write_byte(CCS811_APP_START).map_err(Some)?;
|
||||||
|
if let Some(x) = self.check_for_error() {
|
||||||
|
println!("error ignored {:b}", x);
|
||||||
|
}
|
||||||
|
let mut setting = self.i2c.smbus_read_byte_data(CCS811_MEAS_MODE).map_err(Some)?;
|
||||||
|
setting &= !(0b00000111 << 4);
|
||||||
|
setting |= (mode as u8) << 4;
|
||||||
|
self.i2c.smbus_write_byte_data(CCS811_MEAS_MODE, setting).map_err(Some)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_baseline(&mut self) -> u16 {
|
||||||
|
let x = self.i2c.smbus_read_i2c_block_data(CCS811_BASELINE, 2).unwrap();
|
||||||
|
((x[0] as u16) << 8) | (x[1] as u16)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns (eCO2, tVOC)
|
||||||
|
fn get_reading(&mut self) -> (u16, u16) {
|
||||||
|
let x = self.i2c.smbus_read_i2c_block_data(CCS811_ALG_RESULT_DATA, 4).unwrap();
|
||||||
|
(((x[0] as u16) << 8) | (x[1] as u16), ((x[2] as u16) << 8) | (x[3] as u16))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CCS811DriveMode {
|
||||||
|
Idle = 0,
|
||||||
|
EverySecond = 1,
|
||||||
|
Every10Seconds = 2,
|
||||||
|
Every60Seconds = 3,
|
||||||
|
/// Note the English manual states this is calculated every 10 ms!
|
||||||
|
Every250Milliseconds = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = env::args().collect::<Vec<_>>();
|
||||||
|
if args.len() < 2 {
|
||||||
|
panic!("missing argument: database path");
|
||||||
|
}
|
||||||
|
let database = Connection::open(&args[1]).expect("failed to open database");
|
||||||
|
database.execute("
|
||||||
|
CREATE TABLE IF NOT EXISTS sensor_readings(
|
||||||
|
time INTEGER PRIMARY KEY,
|
||||||
|
humidity INTEGER NOT NULL,
|
||||||
|
celsius INTEGER NOT NULL
|
||||||
|
)", []).unwrap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
let mut ccs = CCS811::new(i2c, CCS811_ADDR);
|
||||||
|
println!("HW ID, should be 0x81 {:x}", ccs.hardware_id());
|
||||||
|
println!("Error code, should be None: {:?}", ccs.check_for_error());
|
||||||
|
println!("app valid = {:?}", ccs.app_valid());
|
||||||
|
println!("baseline = {:x}", ccs.get_baseline());
|
||||||
|
println!("reading {:?}", ccs.get_reading());
|
||||||
|
println!("setting drive mode to 1: {:?}", ccs.set_drive_mode(CCS811DriveMode::EverySecond));
|
||||||
|
*/
|
||||||
|
let mut chip = Chip::new("/dev/gpiochip0").unwrap();
|
||||||
|
let line = chip.get_line(26).unwrap();
|
||||||
|
for _attempt in 0..5 {
|
||||||
|
let time = std::time::SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
|
||||||
|
if let Ok((rh, temp)) = raspi_oled::am2302_reading(&line) {
|
||||||
|
database.execute("INSERT INTO sensor_readings (time, humidity, celsius) VALUES (?1, ?2, ?3)", params![time, rh, temp]).unwrap();
|
||||||
|
display_on_ssd1306(rh, temp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn display_on_ssd1306(rh: u16, temp: u16) {
|
||||||
|
let i2c = I2cdev::new("/dev/i2c-1").unwrap();
|
||||||
let interface = I2CDisplayInterface::new(i2c);
|
let interface = I2CDisplayInterface::new(i2c);
|
||||||
let mut disp = Ssd1306::new(
|
let mut disp = Ssd1306::new(
|
||||||
interface,
|
interface,
|
||||||
@ -26,53 +155,63 @@ fn main() {
|
|||||||
).into_buffered_graphics_mode();
|
).into_buffered_graphics_mode();
|
||||||
|
|
||||||
disp.init().unwrap();
|
disp.init().unwrap();
|
||||||
disp.flush().unwrap();
|
|
||||||
|
|
||||||
let text_style = MonoTextStyleBuilder::new()
|
let text_style = MonoTextStyleBuilder::new()
|
||||||
.font(&FONT_6X10)
|
.font(&FONT_9X18)
|
||||||
.text_color(BinaryColor::On)
|
.text_color(BinaryColor::On)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let text = "0123456789012345678901";
|
let text = format!("{}.{}% {}.{}°C", rh / 10, rh % 10, temp / 10, temp % 10);
|
||||||
Text::new(&text, Point::new(0, 10), text_style)
|
Text::new(&text, Point::new(0, 10), text_style)
|
||||||
.draw(&mut disp)
|
.draw(&mut disp)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
disp.flush().unwrap();
|
disp.flush().unwrap();
|
||||||
|
/*
|
||||||
sleep(Duration::from_secs(2));
|
sleep(Duration::from_secs(2));
|
||||||
disp.clear();
|
disp.clear();
|
||||||
|
|
||||||
|
let base_y = 0.0;
|
||||||
|
let max_dy = 32.0;
|
||||||
|
let mut tick = 0;
|
||||||
loop {
|
loop {
|
||||||
Line::new(Point::new(8, 16 + 16), Point::new(8 + 16, 16 + 16))
|
let y = if tick % 32 < 16 {
|
||||||
|
base_y + (tick % 16) as f32 / 16.0 * max_dy
|
||||||
|
} else {
|
||||||
|
base_y + max_dy - (tick % 16) as f32 / 16.0 * max_dy
|
||||||
|
} as i32;
|
||||||
|
tick += 1;
|
||||||
|
Line::new(Point::new(8, y + 16), Point::new(8 + 16, y + 16))
|
||||||
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
|
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
|
||||||
.draw(&mut disp).unwrap();
|
.draw(&mut disp).unwrap();
|
||||||
Line::new(Point::new(8, 16 + 16), Point::new(8 + 8, 16))
|
Line::new(Point::new(8, y + 16), Point::new(8 + 8, y))
|
||||||
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
|
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
|
||||||
.draw(&mut disp).unwrap();
|
.draw(&mut disp).unwrap();
|
||||||
|
|
||||||
Line::new(Point::new(8 + 16, 16 + 16), Point::new(8 + 8, 16))
|
Line::new(Point::new(8 + 16, y + 16), Point::new(8 + 8, y))
|
||||||
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
|
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
|
||||||
.draw(&mut disp).unwrap();
|
.draw(&mut disp).unwrap();
|
||||||
|
|
||||||
Rectangle::new(Point::new(48, 16), Size::new(16, 16))
|
Rectangle::new(Point::new(48, y), Size::new(16, 16))
|
||||||
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
|
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
|
||||||
.draw(&mut disp).unwrap();
|
.draw(&mut disp).unwrap();
|
||||||
|
|
||||||
|
|
||||||
Circle::new(Point::new(88, 16), 17)
|
Circle::new(Point::new(88, y), 16)
|
||||||
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
|
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
|
||||||
.draw(&mut disp).unwrap();
|
.draw(&mut disp).unwrap();
|
||||||
|
|
||||||
let local_addr = machine_ip::get().unwrap();
|
/*
|
||||||
|
Text::new(&format!("Hello from frame {}", tick), Point::new(0, 56), text_style)
|
||||||
Text::new(&format!("IP: {}", local_addr.to_string()), Point::new(0, 56), text_style)
|
|
||||||
.draw(&mut disp)
|
.draw(&mut disp)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
*/
|
||||||
disp.flush().unwrap();
|
disp.flush().unwrap();
|
||||||
|
|
||||||
sleep(Duration::from_secs(2));
|
sleep(Duration::from_millis(10));
|
||||||
|
|
||||||
disp.clear();
|
disp.clear();
|
||||||
|
|
||||||
|
/*
|
||||||
let im: ImageRaw<BinaryColor> = ImageRaw::new(IMG_DATA, 64);
|
let im: ImageRaw<BinaryColor> = ImageRaw::new(IMG_DATA, 64);
|
||||||
let img = Image::new(&im, Point::new(32, 0));
|
let img = Image::new(&im, Point::new(32, 0));
|
||||||
img.draw(&mut disp).unwrap();
|
img.draw(&mut disp).unwrap();
|
||||||
@ -80,5 +219,35 @@ fn main() {
|
|||||||
|
|
||||||
sleep(Duration::from_secs(2));
|
sleep(Duration::from_secs(2));
|
||||||
disp.clear();
|
disp.clear();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LineWrapper(gpio_cdev::Line);
|
||||||
|
|
||||||
|
impl InputPin for LineWrapper {
|
||||||
|
type Error = gpio_cdev::Error;
|
||||||
|
|
||||||
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
|
let handle = self.0.request(LineRequestFlags::INPUT, 0, "rust-line-wrapper")?;
|
||||||
|
Ok(handle.get_value()? == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
|
let handle = self.0.request(LineRequestFlags::INPUT, 0, "rust-line-wrapper")?;
|
||||||
|
Ok(handle.get_value()? == 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputPin for LineWrapper {
|
||||||
|
type Error = gpio_cdev::Error;
|
||||||
|
|
||||||
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.0.request(LineRequestFlags::OUTPUT, 1, "rust-line-wrapper")?.set_value(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.0.request(LineRequestFlags::OUTPUT, 1, "rust-line-wrapper")?.set_value(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user