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

Implement feature flags for conditional compilation #113

Merged
merged 8 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
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
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