Skip to content

Commit

Permalink
feat(display): added ability to display text to the screen based on s…
Browse files Browse the repository at this point in the history
…potify states
  • Loading branch information
SreeDan committed Jul 10, 2024
1 parent 937a890 commit 1b9544d
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 79 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ ili9341 = "0.5.0"
display-interface-spi = "0.4.1"
models = { path = "models" }
graphics = { path = "graphics" }
crossbeam-channel = "0.5.13"
jpeg-decoder = "0.3.1"

[build-dependencies]
embuild = "0.31.3"
14 changes: 11 additions & 3 deletions graphics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
esp-idf-hal = "=0.43.1"
embedded-graphics = "0.7.1"
embedded-graphics-core = "0.4.0"
image = "0.25.1"
display-interface = "0.4.1"
embedded-canvas = "0.2.0"
embedded-layout = "0.2.0"
ili9341 = "0.5.0"
display-interface-spi = "0.4.1"
display-interface-spi = "0.4.1"
# embedded-graphics-core = "0.4.0"
# image = "0.25.1"
# embedded-canvas = "0.3.1"
# embedded-layout = "0.4.1"
# bytes = "1.6.0"
# embedded-graphics = "0.7.1"
# display-interface = "0.4.1"
# ili9341 = "0.5.0"
125 changes: 116 additions & 9 deletions graphics/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
use display_interface_spi::SPIInterfaceNoCS;
use std::io::Cursor;

use embedded_canvas::CanvasAt;
use embedded_graphics::{
draw_target::DrawTarget,
geometry::{Dimensions, Point},
pixelcolor::{PixelColor, Rgb565},
primitives::{Primitive, PrimitiveStyle, Rectangle, StyledDrawable},
geometry::{Dimensions, Point, Size},
image::{ImageDrawable, ImageRaw},
mono_font::{ascii::FONT_10X20, MonoTextStyle},
pixelcolor::{Rgb565, RgbColor},
primitives::{PointsIter, PrimitiveStyle, Rectangle, StyledDrawable},
text::Text,
Drawable,
};
use esp_idf_hal::gpio::{Gpio16, Output, OutputPin, PinDriver};
use ili9341::Ili9341;
use embedded_layout::{layout::linear::LinearLayout, prelude::*};
use ili9341::DisplayError;
use image::{io::Reader as ImageReader, DynamicImage, RgbImage};

fn rgb888_to_rgb565(r: u8, g: u8, b: u8) -> u16 {
pub fn rgb888_to_rgb565(r: u8, g: u8, b: u8) -> u16 {
let red = (r >> 3) as u16;
let green = (g >> 2) as u16;
let blue = (b >> 3) as u16;
Expand All @@ -35,9 +42,109 @@ pub fn fill_display<T>(display: &mut T, color: Rgb565)
where
T: DrawTarget<Color = Rgb565>,
{
let display_area = Rectangle::new(Point::new(0, 0), display.bounding_box().size);
let display_area = Rectangle::new(
embedded_graphics::geometry::Point::new(0, 0),
display.bounding_box().size,
);

let fill_style = PrimitiveStyle::with_fill(color);

display_area.draw_styled(&fill_style, display);
}
}

fn draw_canvas<T>(display: &mut T, canvas: CanvasAt<Rgb565>, background: Rgb565)
where
T: DrawTarget<Color = Rgb565>,
{
display.fill_contiguous(
&canvas.bounding_box(),
canvas
.bounding_box()
.points()
.map(|point| canvas.get_pixel(point).unwrap_or(background)),
);
}

pub fn draw_album_cover<T>(display: &mut T, image_bytes: Vec<u8>)
where
T: DrawTarget<Color = Rgb565>,
{
let mut album_canvas = CanvasAt::new(Point::zero(), Size::new(240, 240));

let img = ImageReader::new(Cursor::new(image_bytes))
.with_guessed_format()
.expect("Failed to guess image format")
.decode()
.expect("Failed to decode image");

let rgb_imag: RgbImage = img.into_rgb8();
let resized_image = DynamicImage::ImageRgb8(rgb_imag)
.resize(240, 240, image::imageops::FilterType::Nearest)
.to_rgb8();

let raw = resized_image.clone().into_raw();
let rgb565: Vec<u8> = convert_vec_rgb888_to_rgb565(&raw);

let out: ImageRaw<Rgb565> = ImageRaw::new(&rgb565, resized_image.width());
out.draw(&mut album_canvas).expect("Could not draw image");

draw_canvas(display, album_canvas, Rgb565::BLACK);
}

pub fn draw_title_and_artist<T>(display: &mut T, title: String, artist: String)
where
T: DrawTarget<Color = Rgb565, Error = DisplayError>,
{
let mut displayed_title = title.clone();
if displayed_title.len() > 23 {
displayed_title = title[..22].to_string();
displayed_title.push_str("..")
// displayed_title = &title[..22].to_owned() + "..".to_string();
}

let mut displayed_artist = artist.clone();
if displayed_artist.len() > 23 {
displayed_artist = artist[..22].to_string();
displayed_artist.push_str("..");
}

let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);

let mut combined_canvas = CanvasAt::<Rgb565>::new(Point::new(0, 240), Size::new(240, 50));
LinearLayout::vertical(
embedded_layout::object_chain::Chain::new(Text::new(
&displayed_title,
Point::zero(),
text_style,
))
.append(Text::new(&displayed_artist, Point::zero(), text_style)),
)
.with_alignment(horizontal::Left)
.arrange()
.align_to(
&combined_canvas.bounding_box(),
horizontal::Left,
vertical::Center,
)
.draw(&mut combined_canvas)
.unwrap();

draw_canvas(display, combined_canvas, Rgb565::BLACK);
}

pub fn something<T>(display: &mut T)
where
T: DrawTarget<Color = Rgb565>,
{
// Create a new character style
let style = MonoTextStyle::new(&FONT_10X20, Rgb565::RED);

// Create a text at position (20, 30) and draw it using the previously defined style
Text::with_alignment(
"First line\nSecond line",
Point::new(20, 30),
style,
embedded_graphics::text::Alignment::Center,
)
.draw(display);
}
26 changes: 13 additions & 13 deletions models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,27 @@ use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Artist {
name: String,
url: Option<String>,
pub name: String,
pub url: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Track {
name: String,
artists: Vec<Artist>,
image_url: Option<String>,
url: Option<String>,
duration: u32,
pub name: String,
pub artists: Vec<Artist>,
pub image_url: Option<String>,
pub url: Option<String>,
pub duration: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CurrentlyPlaying {
device: Device,
track: Track,
progress_secs: u32,
shuffled: bool,
playing: bool,
repeat_status: RepeatState,
pub device: Device,
pub track: Track,
pub progress_secs: u32,
pub shuffled: bool,
pub playing: bool,
pub repeat_status: RepeatState,
}

// Holds the structs from the `rspotify` package. It's easier to just copy the structs because it
Expand Down
4 changes: 3 additions & 1 deletion sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K)
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000

CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
# This allows to use 1 ms granuality for thread sleeps (10 ms by default).
#CONFIG_FREERTOS_HZ=1000
Expand Down
Loading

0 comments on commit 1b9544d

Please sign in to comment.