Prototype working
This commit is contained in:
parent
eec38c074c
commit
36a2eaab32
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -2,6 +2,12 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.68"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@ -1056,7 +1062,10 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||||||
name = "weather_exporter"
|
name = "weather_exporter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"bme280",
|
"bme280",
|
||||||
|
"byteorder",
|
||||||
|
"embedded-hal",
|
||||||
"linux-embedded-hal",
|
"linux-embedded-hal",
|
||||||
"tokio",
|
"tokio",
|
||||||
"warp",
|
"warp",
|
||||||
|
@ -9,5 +9,8 @@ description = "A Prometheus exporter for weather data."
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bme280 = "0.4.4"
|
bme280 = "0.4.4"
|
||||||
linux-embedded-hal = "0.4.0-alpha.2"
|
linux-embedded-hal = "0.4.0-alpha.2"
|
||||||
|
embedded-hal = "1.0.0-alpha.7"
|
||||||
tokio = { version = "1.23.0", features = ["macros", "rt"] }
|
tokio = { version = "1.23.0", features = ["macros", "rt"] }
|
||||||
warp = "0.3.3"
|
warp = "0.3.3"
|
||||||
|
byteorder = "1.4.3"
|
||||||
|
anyhow = "1.0.68"
|
||||||
|
121
src/main.rs
121
src/main.rs
@ -1,23 +1,110 @@
|
|||||||
use bme280;
|
use bme280::{self, Measurements};
|
||||||
use linux_embedded_hal::{I2cdev, Delay};
|
use linux_embedded_hal::{I2cdev, Delay, I2CError};
|
||||||
use std::thread;
|
use embedded_hal::i2c::blocking::I2c;
|
||||||
use std::time::Duration;
|
use byteorder::{LittleEndian, ByteOrder};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use anyhow::{Result, anyhow};
|
||||||
|
use warp::Filter;
|
||||||
|
|
||||||
fn main() {
|
struct LightSensor {
|
||||||
|
addr: u8,
|
||||||
|
reg: u8,
|
||||||
|
bus: I2cdev,
|
||||||
|
}
|
||||||
|
|
||||||
let loop_time = Duration::from_secs(5);
|
impl LightSensor {
|
||||||
|
fn new(bus: I2cdev, addr: u8, port: u8) -> Self {
|
||||||
|
Self { bus, addr, reg: port + 0x10 }
|
||||||
|
}
|
||||||
|
|
||||||
let i2c = I2cdev::new("/dev/i2c-1")
|
fn measure(&mut self) -> Result<u16, I2CError> {
|
||||||
.expect("Could not open i2c bus");
|
self.bus.write(self.addr, &[self.reg])?;
|
||||||
|
//thread::sleep(Duration::from_millis(10));
|
||||||
let mut sensor = bme280::i2c::BME280::new_primary(i2c);
|
let mut buf = [0; 2];
|
||||||
|
self.bus.write_read(self.addr, &[self.reg], &mut buf[..])?;
|
||||||
sensor.init(&mut Delay).expect("Could not initialize BME280 sensor");
|
Ok(LittleEndian::read_u16(&buf))
|
||||||
|
|
||||||
loop {
|
|
||||||
let baro = sensor.measure(&mut Delay).expect("Could not perform measurement");
|
|
||||||
println!("H {}%, P {} PA, T {}°C", baro.humidity, baro.pressure, baro.temperature);
|
|
||||||
thread::sleep(loop_time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Station {
|
||||||
|
light: LightSensor,
|
||||||
|
bme280: bme280::i2c::BME280<I2cdev>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Station {
|
||||||
|
fn new() -> Result<Self> {
|
||||||
|
|
||||||
|
let i2c = I2cdev::new("/dev/i2c-1")
|
||||||
|
.map_err(|e| anyhow!("Could not open i2c bus: {}", e))?;
|
||||||
|
|
||||||
|
// TODO properly share the device across the two consumers. Probably need to implement I2cdev for RefCell<impl I2cdev> or something
|
||||||
|
// for now just open it twice :/
|
||||||
|
let i2c2 = I2cdev::new("/dev/i2c-1")
|
||||||
|
.map_err(|e| anyhow!("Could not open second i2c bus: {}", e))?;
|
||||||
|
|
||||||
|
let mut bme280 = bme280::i2c::BME280::new_primary(i2c);
|
||||||
|
bme280.init(&mut Delay).expect("Could not initialize BME280 sensor");
|
||||||
|
|
||||||
|
let light = LightSensor::new(i2c2, 0x08, 0x00);
|
||||||
|
|
||||||
|
Ok(Self { light, bme280 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn measure(&mut self) -> Result<(Measurements<I2CError>, u16)> {
|
||||||
|
let baro = self.bme280.measure(&mut Delay)
|
||||||
|
.map_err(|e| anyhow!("Cannot measure bme280: {:?}", e))?;
|
||||||
|
let lux = self.light.measure()
|
||||||
|
.map_err(|e| anyhow!("Cannot measure light: {:?}", e))?;
|
||||||
|
Ok((baro, lux))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scrape(&mut self) -> String {
|
||||||
|
|
||||||
|
let ( Measurements { temperature, pressure, humidity, .. }, lux) = match self.measure() {
|
||||||
|
Err(e) => {
|
||||||
|
return format!("# Error measuring: {:?}", e)
|
||||||
|
},
|
||||||
|
Ok(v) => v,
|
||||||
|
};
|
||||||
|
|
||||||
|
let lux = lux as f64 / 1000f64;
|
||||||
|
|
||||||
|
eprintln!("Scraped data: T={temperature}°C P={pressure}Pa H={humidity}% L={lux}");
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"# HELP weather_temperature_celsius Temperature in °C
|
||||||
|
# TYPE weather_temperature_celsius gauge
|
||||||
|
weather_temperature_celsius {temperature}
|
||||||
|
# HELP weather_humidity_percent Humidity percentage
|
||||||
|
# TYPE weather_humidity_percent gauge
|
||||||
|
weather_humidity_percent {humidity}
|
||||||
|
# HELP weather_pressure_pascals Atmospheric pressure in Pascals
|
||||||
|
# TYPE weather_pressure_pascals gauge
|
||||||
|
weather_pressure_pascals {pressure}
|
||||||
|
# HELP weather_illumination_relative Relative illumination as a fraction of the maximum
|
||||||
|
# TYPE weather_illumination_relative gauge
|
||||||
|
weather_illumination_relative {lux}
|
||||||
|
")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main(flavor="current_thread")]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
|
||||||
|
let home = format!("<html><body><h1>Weather Station v0.1</h1><ul><li><a href=\"https://git.xolus.net/max/weather_exporter\">source code</a></li><li><a href=\"/metrics\">metrics</a></li></ul></body></html>");
|
||||||
|
let home: &'static str = Box::leak(home.into_boxed_str());
|
||||||
|
|
||||||
|
let station = Station::new()?;
|
||||||
|
let station = Arc::new(Mutex::new(station));
|
||||||
|
|
||||||
|
let filter = warp::path!("metrics").map(move || station.lock().unwrap().scrape())
|
||||||
|
.or(warp::path!().map(move || { warp::reply::html(home) }));
|
||||||
|
|
||||||
|
eprintln!("Starting web server.");
|
||||||
|
Ok(warp::serve(filter).run(([0,0,0,0], 9073)).await)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user