This commit is contained in:
Valentin Weber 2025-06-21 17:52:04 +02:00 committed by GitHub
commit dce7dcbb38
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 64 additions and 20 deletions

2
Cargo.lock generated
View file

@ -112,7 +112,7 @@ dependencies = [
[[package]] [[package]]
name = "badgemagic" name = "badgemagic"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",

View file

@ -1,7 +1,7 @@
[package] [package]
name = "badgemagic" name = "badgemagic"
version = "0.1.0" version = "0.2.0"
authors = ["Martin Michaelis <code@mgjm.de>"] authors = ["Martin Michaelis <code@mgjm.de>", "Valentin Weber <weva+code@kabelsalat.ch>"]
edition = "2021" edition = "2021"
description = "Badge Magic with LEDs - Library and CLI" description = "Badge Magic with LEDs - Library and CLI"
homepage = "https://badgemagic.fossasia.org" homepage = "https://badgemagic.fossasia.org"

View file

@ -1,4 +1,5 @@
Copyright 2024 Martin Michaelis Copyright 2024 Martin Michaelis
Copyright 2025 Valentin Weber
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal of this software and associated documentation files (the “Software”), to deal

View file

@ -5,13 +5,13 @@ use badgemagic::{
embedded_graphics::{ embedded_graphics::{
geometry::Point, mono_font::MonoTextStyle, pixelcolor::BinaryColor, text::Text, geometry::Point, mono_font::MonoTextStyle, pixelcolor::BinaryColor, text::Text,
}, },
protocol::{Mode, PayloadBuffer, Style}, protocol::{Brightness, Mode, PayloadBuffer, Style},
usb_hid::Device, usb_hid::Device,
util::DrawableLayoutExt, util::DrawableLayoutExt,
}; };
fn main() -> Result<()> { fn main() -> Result<()> {
let mut payload = PayloadBuffer::new(); let mut payload = PayloadBuffer::new(Brightness::default());
payload.add_message_drawable( payload.add_message_drawable(
Style::default().mode(Mode::Center), Style::default().mode(Mode::Center),

View file

@ -5,7 +5,7 @@ use std::{fs, path::PathBuf};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use badgemagic::{ use badgemagic::{
ble::Device as BleDevice, ble::Device as BleDevice,
protocol::{Mode, PayloadBuffer, Speed, Style}, protocol::{Brightness, Mode, PayloadBuffer, Speed, Style},
usb_hid::Device as UsbDevice, usb_hid::Device as UsbDevice,
}; };
use base64::Engine; use base64::Engine;
@ -43,6 +43,10 @@ struct Args {
#[clap(long)] #[clap(long)]
transport: TransportProtocol, transport: TransportProtocol,
/// Brightness of the panel
#[clap(long)]
brightness: Option<Brightness>,
/// List all devices visible to a transport and exit /// List all devices visible to a transport and exit
#[clap(long)] #[clap(long)]
list_devices: bool, list_devices: bool,
@ -146,7 +150,7 @@ fn gnerate_payload(args: &mut Args) -> Result<PayloadBuffer> {
} }
}; };
let mut payload = PayloadBuffer::new(); let mut payload = PayloadBuffer::new(args.brightness.unwrap_or_default());
for message in config.messages { for message in config.messages {
let mut style = Style::default(); let mut style = Style::default();
@ -164,6 +168,7 @@ fn gnerate_payload(args: &mut Args) -> Result<PayloadBuffer> {
Point::new(0, 7), Point::new(0, 7),
MonoTextStyle::new(&FONT_6X9, BinaryColor::On), MonoTextStyle::new(&FONT_6X9, BinaryColor::On),
); );
payload.add_message_drawable(style, &text); payload.add_message_drawable(style, &text);
} }
Content::Bitstring { bitstring } => { Content::Bitstring { bitstring } => {

View file

@ -1,7 +1,5 @@
//! Protocol used to update the badge //! Protocol used to update the badge
use std::num::TryFromIntError;
#[cfg(feature = "embedded-graphics")] #[cfg(feature = "embedded-graphics")]
use embedded_graphics::{ use embedded_graphics::{
draw_target::DrawTarget, draw_target::DrawTarget,
@ -11,6 +9,7 @@ use embedded_graphics::{
primitives::Rectangle, primitives::Rectangle,
Drawable, Drawable,
}; };
use std::num::TryFromIntError;
use time::OffsetDateTime; use time::OffsetDateTime;
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U16}; use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U16};
@ -54,7 +53,7 @@ impl Style {
self self
} }
/// Show a dotted border arround the display. /// Show a dotted border around the display.
/// ``` /// ```
/// use badgemagic::protocol::Style; /// use badgemagic::protocol::Style;
/// # ( /// # (
@ -161,7 +160,7 @@ impl TryFrom<u8> for Speed {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum Mode { pub enum Mode {
/// Scroll thorugh the message from left to right /// Scroll through the message from left to right
#[default] #[default]
Left, Left,
@ -193,14 +192,39 @@ pub enum Mode {
Laser, Laser,
} }
/// Display Brightness
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))]
pub enum Brightness {
#[default]
Full,
ThreeQuarters,
Half,
OneQuarter,
}
impl From<Brightness> for u8 {
fn from(value: Brightness) -> Self {
match value {
Brightness::Full => 0x00,
Brightness::ThreeQuarters => 0x10,
Brightness::Half => 0x20,
Brightness::OneQuarter => 0x30,
}
}
}
const MSG_PADDING_ALIGN: usize = 64; const MSG_PADDING_ALIGN: usize = 64;
const MAGIC: [u8; 6] = *b"wang\0\0"; const MAGIC: [u8; 5] = *b"wang\0";
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)] #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
#[repr(C)] #[repr(C)]
struct Header { struct Header {
magic: [u8; 6], magic: [u8; 5],
brightness: u8,
blink: u8, blink: u8,
border: u8, border: u8,
speed_and_mode: [u8; 8], speed_and_mode: [u8; 8],
@ -241,7 +265,7 @@ impl Timestamp {
/// Buffer to create a payload /// Buffer to create a payload
/// ///
/// A payload consits of up to 8 messages /// A payload consists of up to 8 messages
/// ``` /// ```
/// # #[cfg(feature = "embedded-graphics")] /// # #[cfg(feature = "embedded-graphics")]
/// # fn main() { /// # fn main() {
@ -252,7 +276,7 @@ impl Timestamp {
/// primitives::{PrimitiveStyle, Rectangle, Styled}, /// primitives::{PrimitiveStyle, Rectangle, Styled},
/// }; /// };
/// ///
/// let mut buffer = PayloadBuffer::new(); /// let mut buffer = PayloadBuffer::default();
/// buffer.add_message_drawable( /// buffer.add_message_drawable(
/// Style::default(), /// Style::default(),
/// &Styled::new( /// &Styled::new(
@ -271,18 +295,19 @@ pub struct PayloadBuffer {
impl Default for PayloadBuffer { impl Default for PayloadBuffer {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new(Brightness::Full)
} }
} }
impl PayloadBuffer { impl PayloadBuffer {
/// Create a new empty buffer /// Create a new empty buffer
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new(brightness: Brightness) -> Self {
Self { Self {
num_messages: 0, num_messages: 0,
data: Header { data: Header {
magic: MAGIC, magic: MAGIC,
brightness: brightness.into(),
blink: 0, blink: 0,
border: 0, border: 0,
speed_and_mode: [0; 8], speed_and_mode: [0; 8],
@ -368,7 +393,7 @@ impl PayloadBuffer {
&self.data &self.data
} }
/// Convert the payload buffe into bytes (with padding) /// Convert the payload buffer into bytes (with padding)
#[allow(clippy::missing_panics_doc)] // should never panic #[allow(clippy::missing_panics_doc)] // should never panic
#[must_use] #[must_use]
pub fn into_padded_bytes(self) -> impl AsRef<[u8]> { pub fn into_padded_bytes(self) -> impl AsRef<[u8]> {
@ -484,10 +509,9 @@ impl DrawTarget for MessageBuffer<'_> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{Brightness, Speed};
use std::ops::Range; use std::ops::Range;
use super::Speed;
#[test] #[test]
fn speed_to_u8_and_back() { fn speed_to_u8_and_back() {
const VALID_SPEED_VALUES: Range<u8> = 1..8; const VALID_SPEED_VALUES: Range<u8> = 1..8;
@ -499,4 +523,18 @@ mod test {
} }
} }
} }
#[test]
fn brightness_to_u8() {
const VALID_BRIGHTNESS_VALUES: [(Brightness, u8); 4] = [
(Brightness::Full, 0x00),
(Brightness::ThreeQuarters, 0x10),
(Brightness::Half, 0x20),
(Brightness::OneQuarter, 0x30),
];
for i in VALID_BRIGHTNESS_VALUES {
assert_eq!(u8::from(Brightness::from(i.0)), i.1);
}
}
} }