Skip to content

Commit

Permalink
Merge pull request #113 from UCI-HyperXite/feature/conditional-compil…
Browse files Browse the repository at this point in the history
…ation

Implement feature flags for conditional compilation
  • Loading branch information
taesungh authored Jun 1, 2024
2 parents dd20036 + 5afbf4a commit 72ad551
Show file tree
Hide file tree
Showing 19 changed files with 285 additions and 40 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/run-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,14 @@ jobs:
- name: Run cargo fmt
run: cargo fmt --all -- --check

- name: Lint pod operation
- name: Lint pod operation (w/o rpi)
run: cargo clippy -- -D warnings

- name: Lint pod operation (w/ rpi)
run: cargo clippy --features rpi -- -D warnings

- name: Build Pod Operation Program (debug)
run: cargo build --target $TARGET --config target.$TARGET.linker=\"aarch64-linux-gnu-gcc\"

- name: Build Pod Operation Program (release)
run: cargo build --target $TARGET --config target.$TARGET.linker=\"aarch64-linux-gnu-gcc\" --release
run: cargo brpi --config target.$TARGET.linker=\"aarch64-linux-gnu-gcc\"
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rust-analyzer.cargo.features": [
// "rpi"
]
}
3 changes: 3 additions & 0 deletions pod-operation/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Configuration to cross-compile for Raspberry Pi
# Uncomment lines as needed

[alias]
brpi = "build --release --features rpi --target=aarch64-unknown-linux-gnu"

[build]
# target = "aarch64-unknown-linux-gnu"

Expand Down
14 changes: 11 additions & 3 deletions pod-operation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@ tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
enum-map = "2.7.3"
once_cell = "1.19.0"
num_enum = "0.7.2"

rppal = { version = "0.18.0", features = ["hal"] }
rppal = { version = "0.18.0", features = ["hal"], optional = true }
ina219 = "0.1.0"
ads1x1x = "0.2.2"
nb = "1.1.0"
mpu6050 = "0.1.6"
lidar_lite_v3 = "0.1.0"
lidar_lite_v3 = { version = "0.1.0", optional = true }
i2cdev = "0.3.1"
byteorder = "1.4.3"

[features]
gpio = ["dep:rppal"]
ina219 = ["dep:rppal"]
ads1015 = ["dep:rppal"]
mpu6050 = ["dep:rppal"]
inverter = ["dep:rppal"]
lidar = ["dep:lidar_lite_v3"]
rpi = ["gpio", "ads1015", "ina219", "mpu6050", "inverter", "lidar"]
4 changes: 2 additions & 2 deletions pod-operation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ cargo watch -x run

Uncomment the aarch64 Linux linker for your operating system in `.cargo/config.toml`.

To build for production, use the `--release` option:
To do a release build for the Raspberry Pi, use the `brpi` alias:

```shell
cargo build --target aarch64-unknown-linux-gnu --release
cargo brpi
```

Alternatively, use `cross` to compile in a container:
Expand Down
2 changes: 1 addition & 1 deletion pod-operation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"description": "This Rust package is for the main program to be run on the pod. The program runs a finite-state machine to operate the pod components and acts as a Socket.IO server to communicate with the control station.",
"scripts": {
"lint": "cargo clippy",
"lint": "cargo clippy && cargo clippy --features rpi",
"format": "cargo fmt",
"test": "cargo test"
},
Expand Down
9 changes: 9 additions & 0 deletions pod-operation/src/components/brakes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#[cfg(not(feature = "gpio"))]
use crate::utils::mock::gpio::OutputPin;
#[cfg(feature = "gpio")]
use rppal::gpio::{Gpio, OutputPin};

use tracing::debug;

use crate::utils::GpioPins;
Expand All @@ -10,6 +14,11 @@ pub struct Brakes {
impl Brakes {
pub fn new() -> Self {
Brakes {
#[cfg(not(feature = "gpio"))]
pin: OutputPin {
pin: GpioPins::PNEUMATICS_RELAY.into(),
},
#[cfg(feature = "gpio")]
pin: Gpio::new()
.unwrap()
.get(GpioPins::PNEUMATICS_RELAY.into())
Expand Down
25 changes: 22 additions & 3 deletions pod-operation/src/components/gyro.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use mpu6050::Mpu6050;
use rppal::hal::Delay;
use rppal::i2c::I2c;
#[cfg(feature = "mpu6050")]
use {
mpu6050::Mpu6050,
rppal::{hal::Delay, i2c::I2c},
};

use serde::Serialize;

pub struct Gyroscope {
#[cfg(feature = "mpu6050")]
mpu6050: Mpu6050<I2c>,
}

Expand All @@ -14,18 +18,33 @@ pub struct Orientation {
}

impl Gyroscope {
#[cfg(feature = "mpu6050")]
pub fn new() -> Self {
let i2c = I2c::new().unwrap();
let mut mpu6050 = Mpu6050::new(i2c);
mpu6050.init(&mut Delay::new()).unwrap();
Gyroscope { mpu6050 }
}

#[cfg(feature = "mpu6050")]
pub fn read_orientation(&mut self) -> Orientation {
let angles = self.mpu6050.get_acc_angles().unwrap();
Orientation {
pitch: angles[1],
roll: angles[0],
}
}

#[cfg(not(feature = "mpu6050"))]
pub fn new() -> Self {
Gyroscope {}
}

#[cfg(not(feature = "mpu6050"))]
pub fn read_orientation(&mut self) -> Orientation {
Orientation {
pitch: 0.0,
roll: 0.0,
}
}
}
9 changes: 9 additions & 0 deletions pod-operation/src/components/high_voltage_system.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#[cfg(not(feature = "gpio"))]
use crate::utils::mock::gpio::OutputPin;
#[cfg(feature = "gpio")]
use rppal::gpio::{Gpio, OutputPin};

use tracing::debug;

use crate::utils::GpioPins;
Expand All @@ -10,6 +14,11 @@ pub struct HighVoltageSystem {
impl HighVoltageSystem {
pub fn new() -> Self {
HighVoltageSystem {
#[cfg(not(feature = "gpio"))]
pin: OutputPin {
pin: GpioPins::CONTACTOR_RELAY.into(),
},
#[cfg(feature = "gpio")]
pin: Gpio::new()
.unwrap()
.get(GpioPins::CONTACTOR_RELAY.into())
Expand Down
39 changes: 34 additions & 5 deletions pod-operation/src/components/inverter_board.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,54 @@
#[cfg(feature = "inverter")]
use rppal::uart::{Parity, Uart};

const SERIAL_PATH: &str = "/dev/tty/ACM0";
const BAUD_RATE: u32 = 9600;
const PARITY: Parity = Parity::None;
const DATA_BITS: u8 = 8;
const STOP_BITS: u8 = 1;
use tracing::debug;

#[cfg(feature = "inverter")]
mod serial_constants {
use super::Parity;
pub const SERIAL_PATH: &str = "/dev/ttyACM0";
pub const BAUD_RATE: u32 = 9600;
pub const PARITY: Parity = Parity::None;
pub const DATA_BITS: u8 = 8;
pub const STOP_BITS: u8 = 1;
}

pub struct InverterBoard {
#[cfg(feature = "inverter")]
uart: Uart,
}

impl InverterBoard {
#[cfg(feature = "inverter")]
pub fn new() -> Self {
use serial_constants::*;
let uart = Uart::with_path(SERIAL_PATH, BAUD_RATE, PARITY, DATA_BITS, STOP_BITS).unwrap();
Self { uart }
}

/// Combine velocity and throttle into a space-separated string message and then send it over to
/// the Pico as bytes.
#[cfg(feature = "inverter")]
pub fn send_control(&mut self, velocity: f32, throttle: f32) {
let message = format!("{velocity} {throttle}\n");
debug!(
"Sending inverter control message: {} {}",
velocity, throttle
);
self.uart.write(message.as_bytes()).unwrap();
}

#[cfg(not(feature = "inverter"))]
pub fn new() -> Self {
InverterBoard {}
}

/// Combine velocity and throttle into a space-separated string message
#[cfg(not(feature = "inverter"))]
pub fn send_control(&mut self, velocity: f32, throttle: f32) {
debug!(
"Mocking inverter sending message: {} {}",
velocity, throttle
);
}
}
22 changes: 20 additions & 2 deletions pod-operation/src/components/lidar.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,42 @@
use i2cdev::linux::LinuxI2CDevice;
use lidar_lite_v3::LidarLiteV3;
use tracing::info;

#[cfg(feature = "lidar")]
use {i2cdev::linux::LinuxI2CDevice, lidar_lite_v3::LidarLiteV3};

#[cfg(feature = "lidar")]
const LIDAR_ADDRESS: u16 = 0x62;

pub struct Lidar {
#[cfg(feature = "lidar")]
lidar_lite: LidarLiteV3<LinuxI2CDevice>,
}

impl Lidar {
#[cfg(feature = "lidar")]
pub fn new() -> Lidar {
let i2cdev = LinuxI2CDevice::new("/dev/i2c-1", LIDAR_ADDRESS)
.expect("Failed to initialize I2C device");
let lidar_lite = LidarLiteV3::new(i2cdev).expect("Failed to initialize LidarLiteV3");

info!("Initialized LidarLite");
Lidar { lidar_lite }
}

#[cfg(not(feature = "lidar"))]
pub fn new() -> Lidar {
info!("Mocking lidar device");
Lidar {}
}

/// Convert the distance from centimeters to meters
#[cfg(feature = "lidar")]
pub fn read_distance(&mut self) -> f32 {
let distance = self.lidar_lite.read_distance(false).unwrap();
f32::from(distance) / 100.0
}

#[cfg(not(feature = "lidar"))]
pub fn read_distance(&mut self) -> f32 {
100.0
}
}
39 changes: 29 additions & 10 deletions pod-operation/src/components/lim_current.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,61 @@
use ads1x1x::ic::{Ads1015, Resolution12Bit};
use ads1x1x::interface::I2cInterface;
use ads1x1x::mode::OneShot;
use ads1x1x::ChannelSelection::{SingleA0, SingleA1, SingleA2};
use ads1x1x::{Ads1x1x, DynamicOneShot, FullScaleRange, SlaveAddr};
use nb::block;
use rppal::i2c::I2c;
use ads1x1x::SlaveAddr;
use tracing::info;
#[cfg(feature = "ads1015")]
use {
ads1x1x::ic::{Ads1015, Resolution12Bit},
ads1x1x::interface::I2cInterface,
ads1x1x::mode::OneShot,
ads1x1x::ChannelSelection::{SingleA0, SingleA1, SingleA2},
ads1x1x::{Ads1x1x, DynamicOneShot, FullScaleRange},
nb::block,
rppal::i2c::I2c,
};

const QUIESCENT_VOLTAGE: f32 = 2.5; //Units: volts (v)
const SENSITIVITY: f32 = 0.066; //Unit: vots/amp (v/a)

fn voltage_to_current(voltage: i16) -> f32 {
let voltage = f32::from(voltage) / 1000.0;
let current = (voltage - QUIESCENT_VOLTAGE) / SENSITIVITY;
println!("Voltage: {}", voltage);
current
(voltage - QUIESCENT_VOLTAGE) / SENSITIVITY
}

pub struct LimCurrent {
#[cfg(feature = "ads1015")]
ads1015: Ads1x1x<I2cInterface<I2c>, Ads1015, Resolution12Bit, OneShot>,
}

impl LimCurrent {
#[cfg(feature = "ads1015")]
pub fn new(device_address: SlaveAddr) -> Self {
let i2cdev = I2c::new().unwrap();
let mut adc = Ads1x1x::new_ads1015(i2cdev, device_address);
adc.set_full_scale_range(FullScaleRange::Within4_096V)
.unwrap();
info!("Configured ADS1015 for for LimCurrent");
LimCurrent { ads1015: adc }
}

#[cfg(not(feature = "ads1015"))]
pub fn new(device_address: SlaveAddr) -> Self {
info!("Mocking ADS at {:?} for LimCurrent", device_address);
LimCurrent {}
}

pub fn cleanup(self) {
#[cfg(feature = "ads1015")]
self.ads1015.destroy_ads1015();
}

#[cfg(feature = "ads1015")]
pub fn read_currents(&mut self) -> (f32, f32, f32) {
[SingleA0, SingleA1, SingleA2]
.map(|channel| block!(self.ads1015.read(channel)).unwrap() * 2)
.map(voltage_to_current)
.into()
}

#[cfg(not(feature = "ads1015"))]
pub fn read_currents(&mut self) -> (f32, f32, f32) {
[2500, 2500, 2500].map(voltage_to_current).into()
}
}
Loading

0 comments on commit 72ad551

Please sign in to comment.