tasks: write an oled information driver
This commit is contained in:
944
Cargo.lock
generated
944
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -118,6 +118,8 @@ kfilter = "0.4.0"
|
|||||||
rmp = { version = "0.8.14", optional = true, default-features = false }
|
rmp = { version = "0.8.14", optional = true, default-features = false }
|
||||||
xtensa-lx-rt = { version = "*", features = ["float-save-restore"] }
|
xtensa-lx-rt = { version = "*", features = ["float-save-restore"] }
|
||||||
futures = { version = "0.3.31", default-features = false, features = ["async-await"] }
|
futures = { version = "0.3.31", default-features = false, features = ["async-await"] }
|
||||||
|
ssd1306 = { version = "0.10.0", features = ["async"] }
|
||||||
|
embedded-graphics = { version = "0.8.1", features = ["nalgebra_support"] }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
# Rust debug is too slow.
|
# Rust debug is too slow.
|
||||||
|
|||||||
171
src/tasks/oled.rs
Normal file
171
src/tasks/oled.rs
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
use core::{cell::RefCell, fmt::Binary};
|
||||||
|
|
||||||
|
use embassy_sync::pubsub::DynSubscriber;
|
||||||
|
use embassy_time::Timer;
|
||||||
|
use embedded_graphics::{image::{Image, ImageRaw}, mono_font::{ascii::{FONT_6X10, FONT_10X20, FONT_9X18_BOLD}, MonoTextStyleBuilder}, pixelcolor::BinaryColor, prelude::*, primitives::{Circle, Line, Polyline, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, StyledDrawable}, text::{self, Alignment, Baseline, Text}};
|
||||||
|
use esp_hal::{i2c::master::I2c, Async};
|
||||||
|
use nalgebra::Vector2;
|
||||||
|
use ssd1306::{mode::DisplayConfigAsync, prelude::DisplayRotation, size::DisplaySize128x64, I2CDisplayInterface, Ssd1306Async};
|
||||||
|
use log::*;
|
||||||
|
|
||||||
|
use alloc::format;
|
||||||
|
|
||||||
|
use crate::{backoff::Backoff, ego::engine::MotionState, events::{Notification, Prediction, Scene, SensorSource, Telemetry}};
|
||||||
|
|
||||||
|
mod images {
|
||||||
|
use embedded_graphics::{
|
||||||
|
image::ImageRaw,
|
||||||
|
pixelcolor::BinaryColor
|
||||||
|
};
|
||||||
|
include!("../../target/images.rs");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct OledUI {
|
||||||
|
scene: Scene,
|
||||||
|
motion: MotionState,
|
||||||
|
brakelight: bool,
|
||||||
|
headlight: bool,
|
||||||
|
gps_online: bool,
|
||||||
|
imu_online: bool,
|
||||||
|
velocity: f32,
|
||||||
|
location: Vector2<f64>,
|
||||||
|
sleep: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
pub async fn oled_task(i2c: I2c<'static, Async>, mut events: DynSubscriber<'static, Telemetry>) {
|
||||||
|
let interface = RefCell::new(Some(I2CDisplayInterface::new(i2c)));
|
||||||
|
// initialize the display
|
||||||
|
let mut display = Backoff::from_secs(3).forever().attempt(async || {
|
||||||
|
info!("Setting up OLED display");
|
||||||
|
let mut display = Ssd1306Async::new(interface.replace(None).unwrap(), DisplaySize128x64, DisplayRotation::Rotate0)
|
||||||
|
.into_buffered_graphics_mode();
|
||||||
|
if let Err(e) = display.init().await {
|
||||||
|
interface.replace(Some(display.release()));
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
Ok(display)
|
||||||
|
}
|
||||||
|
}).await.unwrap();
|
||||||
|
|
||||||
|
display.clear(BinaryColor::Off).unwrap();
|
||||||
|
display.flush().await.unwrap();
|
||||||
|
|
||||||
|
let text_style = MonoTextStyleBuilder::new()
|
||||||
|
.font(&FONT_6X10)
|
||||||
|
.text_color(BinaryColor::On)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let logo_style = MonoTextStyleBuilder::new()
|
||||||
|
.font(&FONT_9X18_BOLD)
|
||||||
|
.text_color(BinaryColor::On)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let speed_style = MonoTextStyleBuilder::new()
|
||||||
|
.font(&FONT_10X20)
|
||||||
|
.text_color(BinaryColor::On)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let draw_style = PrimitiveStyleBuilder::new()
|
||||||
|
.fill_color(BinaryColor::On)
|
||||||
|
.build();
|
||||||
|
let erase_style = PrimitiveStyleBuilder::new()
|
||||||
|
.fill_color(BinaryColor::Off)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let inactive_style = PrimitiveStyleBuilder::new()
|
||||||
|
.fill_color(BinaryColor::Off)
|
||||||
|
.stroke_color(BinaryColor::On)
|
||||||
|
.stroke_width(2)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let mut ui_state = OledUI::default();
|
||||||
|
|
||||||
|
info!("Running boot splash animation");
|
||||||
|
for pos in 0..16 {
|
||||||
|
Rectangle::new(Point::zero(), Size { width: figments::liber8tion::interpolate::ease_in_out_quad(pos*8) as u32, height: 64}).draw_styled(&draw_style, &mut display).unwrap();
|
||||||
|
display.flush().await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
for pos in 0..16 {
|
||||||
|
Rectangle::new(Point::zero(), Size { width: figments::liber8tion::interpolate::ease_in_out_quad(pos*8) as u32, height: 64}).draw_styled(&erase_style, &mut display).unwrap();
|
||||||
|
Text::with_baseline("Renderbug v4", Point::new(0, 16), logo_style, Baseline::Top)
|
||||||
|
.draw(&mut display)
|
||||||
|
.unwrap();
|
||||||
|
display.flush().await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer::after_secs(2).await;
|
||||||
|
|
||||||
|
info!("OLED display is ready!");
|
||||||
|
loop {
|
||||||
|
// FIXME: Need to implement sleep handling to electrically turn the display on/off and do a sleep animation
|
||||||
|
display.clear(BinaryColor::Off).unwrap();
|
||||||
|
if !ui_state.sleep {
|
||||||
|
|
||||||
|
// Sensor indicators
|
||||||
|
let gps_img = if ui_state.gps_online {
|
||||||
|
&images::GPS_ON
|
||||||
|
} else {
|
||||||
|
&images::GPS_OFF
|
||||||
|
};
|
||||||
|
let imu_img = if ui_state.imu_online {
|
||||||
|
&images::IMU_ON
|
||||||
|
} else {
|
||||||
|
&images::IMU_OFF
|
||||||
|
};
|
||||||
|
Image::new(gps_img, Point::zero()).draw(&mut display).unwrap();
|
||||||
|
Image::new(imu_img, Point::new((gps_img.size().width + 2) as i32, 0)).draw(&mut display).unwrap();
|
||||||
|
|
||||||
|
Image::new(&images::BIKE, Point::new((128 / 2 - images::BIKE.size().width / 2) as i32, 24)).draw(&mut display).unwrap();
|
||||||
|
|
||||||
|
let headlight_img = if ui_state.headlight {
|
||||||
|
&images::HEADLIGHT_ON
|
||||||
|
} else {
|
||||||
|
&images::HEADLIGHT_OFF
|
||||||
|
};
|
||||||
|
let brakelight_img = if ui_state.brakelight {
|
||||||
|
&images::BRAKELIGHT_ON
|
||||||
|
} else {
|
||||||
|
&images::BRAKELIGHT_OFF
|
||||||
|
};
|
||||||
|
Image::new(headlight_img, Point::new(((128 / 2 - images::BIKE.size().width / 2) - 18) as i32, 28)).draw(&mut display).unwrap();
|
||||||
|
Image::new(brakelight_img, Point::new(((128 / 2 + images::BIKE.size().width / 2) + 2) as i32, 28)).draw(&mut display).unwrap();
|
||||||
|
|
||||||
|
Text::with_alignment(&format!("{}", ui_state.velocity), Point::new(128 / 2, 12), speed_style, Alignment::Center)
|
||||||
|
.draw(&mut display)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// TODO: Replace the state texts with cute animations or smth
|
||||||
|
Text::with_alignment(&format!("{:?}", ui_state.motion), Point::new(128 / 2, 64 - 3), text_style, Alignment::Center)
|
||||||
|
.draw(&mut display)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Text::with_alignment(&format!("{:?}", ui_state.scene), Point::new(128 / 2, 64 - 13), text_style, Alignment::Center)
|
||||||
|
.draw(&mut display)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Line::new(Point::new(0, 18), Point::new(128, 18)).draw_styled(&inactive_style, &mut display).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
display.flush().await.unwrap();
|
||||||
|
|
||||||
|
let evt = events.next_message_pure().await;
|
||||||
|
match evt {
|
||||||
|
Telemetry::Prediction(Prediction::Velocity(v)) => ui_state.velocity = v,
|
||||||
|
Telemetry::Prediction(Prediction::Location(loc)) => ui_state.location = loc,
|
||||||
|
Telemetry::Prediction(Prediction::Motion(motion)) => ui_state.motion = motion,
|
||||||
|
Telemetry::Notification(crate::events::Notification::SceneChange(scene)) => ui_state.scene = scene,
|
||||||
|
Telemetry::Notification(Notification::SetBrakelight(b)) => ui_state.brakelight = b,
|
||||||
|
Telemetry::Notification(Notification::SetHeadlight(b)) => ui_state.headlight = b,
|
||||||
|
Telemetry::Notification(Notification::SensorOffline(SensorSource::IMU)) => ui_state.imu_online = false,
|
||||||
|
Telemetry::Notification(Notification::SensorOnline(SensorSource::IMU)) => ui_state.imu_online = true,
|
||||||
|
Telemetry::Notification(Notification::SensorOffline(SensorSource::GPS)) => ui_state.gps_online = false,
|
||||||
|
Telemetry::Notification(Notification::SensorOnline(SensorSource::GPS)) => ui_state.gps_online = true,
|
||||||
|
Telemetry::Notification(Notification::Sleep) => ui_state.sleep = true,
|
||||||
|
Telemetry::Notification(Notification::WakeUp) => ui_state.sleep = false,
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user