Skip to content

Commit

Permalink
feat: Rework all the sensors
Browse files Browse the repository at this point in the history
  • Loading branch information
dmtrKovalenko committed Dec 13, 2023
1 parent 5ad76d3 commit 54d8d3f
Show file tree
Hide file tree
Showing 24 changed files with 737 additions and 306 deletions.
File renamed without changes.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
target/
.pio/
.DS_Store
*.log
*.log
compile_commands.json
.cache/
63 changes: 42 additions & 21 deletions cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-trait = "0.1.68"
btleplug = { version = "0.10", features = ["serde"] }
chrono = "0.4.24"
crossterm = "0.26.1"
futures = "0.3.28"
lazy_static = "1.4.0"
notify-rust = "4.8.0"
pretty_env_logger = "0.4.0"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
spinners = "4.1.0"
textplots = "0.8.0"
throbber-widgets-tui = "0.1.3"
Expand Down
6 changes: 6 additions & 0 deletions cli/link.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

set -euf -o pipefail

cargo build --release
cp target/release/cli /usr/local/bin/co2
85 changes: 35 additions & 50 deletions cli/src/bluetooth.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,27 @@
use btleplug::api::{Central, Manager as _, Peripheral, ScanFilter};
use btleplug::api::{Central, CharPropFlags, Manager as _, Peripheral, ScanFilter};
use btleplug::platform::Manager;
use futures::stream::StreamExt;
use std::error::Error;

use std::str::FromStr;
use std::time::Duration;
use tokio::time::{self, sleep, timeout};
use uuid::Uuid;

/// Only devices whose name contains this string will be tried.
const PERIPHERAL_NAME_MATCH_FILTER: &str = "CO2CICKA";

#[derive(Debug)]
pub struct ClimateData {
pub temperature: f32,
pub e_co2: i32,
pub tvoc: i32,
pub pressure: f32,
pub humidity: f32,
pub light: f32,
}

impl ClimateData {
fn from_bytes(data: Vec<u8>) -> Self {
let str = String::from_utf8(data).unwrap();

let encoded_data = str.split(',').collect::<Vec<&str>>();
let co2 = encoded_data[0].parse::<i32>().unwrap_or_default();
let tvoc = encoded_data[1].parse::<i32>().unwrap_or_default();
let temp = encoded_data[2].parse::<f32>().unwrap_or_default();
let pressure = encoded_data[3].parse::<f32>().unwrap_or_default();
let humidity = encoded_data[4].parse::<f32>().unwrap_or_default();
let light = encoded_data[5].parse::<f32>().unwrap_or_default();

Self {
temperature: temp,
e_co2: co2,
tvoc,
pressure,
humidity,
light,
}
}
}

pub struct Connection<TPeripheral: Peripheral> {
manager: Manager,
_manager: Manager,
peripheral: TPeripheral,
characteristic: btleplug::api::Characteristic,
}

pub trait FromBleData {
fn from_bytes(data: Vec<u8>) -> Result<Self, Box<dyn Error>>
where
Self: Sized;
}

const TIMEOUT: Duration = Duration::from_secs(10);

impl<TPer: Peripheral> Connection<TPer> {
Expand Down Expand Up @@ -81,20 +53,21 @@ impl<TPer: Peripheral> Connection<TPer> {
}
}

pub async fn subscribe_to_sensor<TFun: FnMut(ClimateData)>(
pub async fn subscribe_to_sensor<TData: FromBleData, TFun: FnMut(TData)>(
&self,
mut fun: TFun,
) -> Result<(), Box<dyn Error>> {
tracing::debug!("Subscribing to sensor");
self.peripheral.subscribe(&self.characteristic).await?;

let mut notification_stream = self.peripheral.notifications().await?;

while let Some(data) = timeout(TIMEOUT, notification_stream.next()).await? {
let data = ClimateData::from_bytes(data.value);
tracing::debug!("Received data from sensor {data:?}");
match TData::from_bytes(data.value) {
Ok(data) => fun(data),
Err(e) => tracing::error!("Error decodring data from sensor {}", e),
}

fun(data);
sleep(Duration::from_millis(5000)).await;
tracing::debug!("Awake");

Expand All @@ -109,22 +82,28 @@ impl<TPer: Peripheral> Connection<TPer> {

Ok(())
}
}

pub async fn find_sensor() -> Result<Connection<impl Peripheral>, Box<dyn Error>> {
let notify_characteristic_uuid = Uuid::from_str("0000FFE1-0000-1000-8000-00805F9B34FB")?;
pub async fn read_from_sensor<TData: FromBleData>(&self) -> Result<TData, Box<dyn Error>> {
tracing::debug!("Reading sensor");

Ok(TData::from_bytes(
self.peripheral.read(&self.characteristic).await?,
)?)
}
}

pub async fn find_sensor(
characteristic_uuid: Uuid,
property: CharPropFlags,
) -> Result<Connection<impl Peripheral>, Box<dyn Error>> {
let manager = Manager::new().await?;
let adapter_list = manager.adapters().await?;
if adapter_list.is_empty() {
tracing::debug!("No Bluetooth adapters found");
}

for adapter in adapter_list.iter() {
adapter
.start_scan(ScanFilter::default())
.await
.expect("Can't scan BLE adapter for connected devices...");
adapter.start_scan(ScanFilter::default()).await?;

time::sleep(Duration::from_secs(2)).await;
let peripherals = adapter.peripherals().await?;
Expand Down Expand Up @@ -160,10 +139,14 @@ pub async fn find_sensor() -> Result<Connection<impl Peripheral>, Box<dyn Error>

if is_connected {
peripheral.discover_services().await?;

for characteristic in peripheral.characteristics().into_iter() {
if characteristic.uuid == notify_characteristic_uuid {
if characteristic.uuid == characteristic_uuid
&& characteristic.properties.contains(property)
{
tracing::debug!("Found characteristic {:?}", characteristic.uuid,);
return Ok(Connection {
manager,
_manager: manager,
peripheral,
characteristic,
});
Expand All @@ -179,7 +162,9 @@ pub async fn find_sensor() -> Result<Connection<impl Peripheral>, Box<dyn Error>
}
}
}

adapter.stop_scan().await?;
}

Err("No O found".into())
Err("No devices found".into())
}
Loading

0 comments on commit 54d8d3f

Please sign in to comment.