Prototype working

This commit is contained in:
Maxime Augier 2022-12-19 22:08:30 +01:00
parent eec38c074c
commit 36a2eaab32
3 changed files with 116 additions and 17 deletions

9
Cargo.lock generated
View File

@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
[[package]]
name = "autocfg"
version = "1.1.0"
@ -1056,7 +1062,10 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
name = "weather_exporter"
version = "0.1.0"
dependencies = [
"anyhow",
"bme280",
"byteorder",
"embedded-hal",
"linux-embedded-hal",
"tokio",
"warp",

View File

@ -9,5 +9,8 @@ description = "A Prometheus exporter for weather data."
[dependencies]
bme280 = "0.4.4"
linux-embedded-hal = "0.4.0-alpha.2"
embedded-hal = "1.0.0-alpha.7"
tokio = { version = "1.23.0", features = ["macros", "rt"] }
warp = "0.3.3"
byteorder = "1.4.3"
anyhow = "1.0.68"

View File

@ -1,23 +1,110 @@
use bme280;
use linux_embedded_hal::{I2cdev, Delay};
use std::thread;
use std::time::Duration;
use bme280::{self, Measurements};
use linux_embedded_hal::{I2cdev, Delay, I2CError};
use embedded_hal::i2c::blocking::I2c;
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")
.expect("Could not open i2c bus");
let mut sensor = bme280::i2c::BME280::new_primary(i2c);
sensor.init(&mut Delay).expect("Could not initialize BME280 sensor");
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);
fn measure(&mut self) -> Result<u16, I2CError> {
self.bus.write(self.addr, &[self.reg])?;
//thread::sleep(Duration::from_millis(10));
let mut buf = [0; 2];
self.bus.write_read(self.addr, &[self.reg], &mut buf[..])?;
Ok(LittleEndian::read_u16(&buf))
}
}
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)
}