Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNS - Optical Flow #32

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions optical_flow.rs
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to lib/sensors/src/optical_flow.rs (you'll need to add it to lib/sensors/src/lib.rs too

Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use crate::spi::HypedSpi;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use byteorder::{LittleEndian, ReadBytesExt};
use std::thread::sleep;
use serde::{Serialize, Deserialize};
use serde_json::json;


// implement get_motion function first
// add readByteFromRegister (?) function to HypedSPI trait which takes register address and gives data

const REG_MOTION_BURST: u8 = 0x16;
const TIMEOUT: Duration = Duration::from_secs(5);


pub struct PMW3901<SPI> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct PMW3901<SPI> {
pub struct PMW3901<SPI: HypedSpi> {

It's also standard to have single uppercase characters as type parameters, like T. But I don't mind SPI for now

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probs better to call this OpticalFlow also

spi: SPI,

}

impl<SPI> PMW3901<SPI>
where
SPI: HypedSpi,
{
Comment on lines +21 to +24
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
impl<SPI> PMW3901<SPI>
where
SPI: HypedSpi,
{
impl<SPI: HypedSpi> PMW3901<SPI>

You should be able to do something shorter like this instead



pub fn new(spi: SPI) -> Self {
PMW3901 { spi }
}

// **ATTEMPTED PYTHON IMPLEMENTATION OF get_motion**
pub fn get_motion(&mut self) -> Result<(i16, i16), &'static str>{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll want to define an error enum type like OpticalFlowError instead of returning a string here. Have a look at TemperatureError as an example (you'll probably want to have an enum variant for SpiError for example

let start = Instant::now();


while start.elapsed() < TIMEOUT {
// Send burst read command and read 12 bytes
let mut data = [0u8; 13]; // Includes the command byte
data[0] = REG_MOTION_BURST; // first element in data is the register
self.spi.read(&mut data).map_err(|_| "SPI read failed")?; //read data using HypedSpi trait

// Parse the response data
let mut cursor = &data[1..]; //slice data and use cursor to go thrugh the data

Check warning on line 43 in optical_flow.rs

View workflow job for this annotation

GitHub Actions / Spell check with typos

"thrugh" should be "through".
let _ = cursor.read_u8().unwrap(); // read each byte from cursor and assign it to variables
let dr = cursor.read_u8().unwrap();
let obs = cursor.read_u8().unwrap();
let x = cursor.read_i16::<LittleEndian>().unwrap(); // x and y value is what we need and little endian is used to make sure bytes are ordered ith LSB first
let y = cursor.read_i16::<LittleEndian>().unwrap(); // LittleEndian takes (advances) 2 bytes from cursor
let quality = cursor.read_u8().unwrap();
let raw_sum = cursor.read_u8().unwrap();
let raw_max = cursor.read_u8().unwrap();
let raw_min = cursor.read_u8().unwrap();
let shutter_upper = cursor.read_u8().unwrap();
let shutter_lower = cursor.read_u8().unwrap();

if (dr & 0b1000_0000) != 0 && (quality >= 0x19 || shutter_upper != 0x1F) {
return Ok((x, y)); // Return delta x and y if valid
}

// Delay before retrying
sleep(Duration::from_millis(10));

}

Err("Timed out waiting for motion data")
}

}


// the # implements the Serialize trait from the serde crate for the strcuts. this is apparantly needed to convert strcut to json

Check warning on line 71 in optical_flow.rs

View workflow job for this annotation

GitHub Actions / Spell check with typos

"apparantly" should be "apparently".
#[derive(Serialize)]
struct Measurement {
header: Header,
payload: Payload,
}

#[derive(Serialize)]
struct Header {
timestamp: u64,
priority: u8,
}

#[derive(Serialize)]
struct Payload {
x: i16,
y: i16,
}


// Make main function with flexible error handling
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function won't live here, it'll be in one of the board/{board_name}/tasks folders as a task. Have a look at boards/stm32l476rg/src/tasks/temperature.rs as an example. (Just copy this code into a structure similar to that for now)



let spi = HypedSPI::new() // spi implementation?
let mut sensor = PMW3901::new(spi);


// While loop implementation from python code
loop {

// Get motion data
let (x, y) = sensor.get_motion()?;

// Prepare measurement data
let measurement = Measurement {
header: Header {
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)? // Get time in secs from epoch as in the python code
.as_secs(),
priority: 1,
},
payload: Payload { x, y },
};

// Serialize measurement data to JSON
let measurement_data = serde_json::to_string(&measurement)?;

// Print payload
//println!("{}", measurement_data);


// delay
sleep(Duration::from_millis(50));

}
}
Loading