use device_envoy_core::lcd_text::{LcdTextDriver, LcdTextError, LcdTextFrame, LcdTextWrite};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::signal::Signal;
use heapless::Vec;
#[doc(hidden)]
pub use paste;
pub use device_envoy_core::lcd_text::LcdText;
#[cfg(doc)]
pub mod lcd_text_generated {
use crate::Result;
pub struct LcdTextGenerated;
pub struct I2csGenerated;
pub struct LcdTextGenerated20x4;
impl I2csGenerated {
pub fn new<I2cPeripheral, SdaPin, SclPin>(
i2c_peripheral: I2cPeripheral,
sda: SdaPin,
scl: SclPin,
spawner: embassy_executor::Spawner,
) -> Result<(&'static LcdTextGenerated, &'static LcdTextGenerated20x4)> {
static INSTANCE_16X2: LcdTextGenerated = LcdTextGenerated;
static INSTANCE_20X4: LcdTextGenerated20x4 = LcdTextGenerated20x4;
let _ = (i2c_peripheral, sda, scl, spawner);
Ok((&INSTANCE_16X2, &INSTANCE_20X4))
}
}
impl LcdTextGenerated {
pub const WIDTH: usize = 16;
pub const HEIGHT: usize = 2;
pub const ADDRESS: u8 = 0x27;
pub fn new<I2cPeripheral, SdaPin, SclPin>(
i2c_peripheral: I2cPeripheral,
sda: SdaPin,
scl: SclPin,
spawner: embassy_executor::Spawner,
) -> Result<&'static Self> {
static INSTANCE: LcdTextGenerated = LcdTextGenerated;
let _ = (i2c_peripheral, sda, scl, spawner);
Ok(&INSTANCE)
}
}
impl crate::lcd_text::LcdText<16, 2> for LcdTextGenerated {
const ADDRESS: u8 = 0x27;
fn write_text(&self, text: impl AsRef<str>) {
let _ = text;
}
}
impl LcdTextGenerated20x4 {
pub const WIDTH: usize = 20;
pub const HEIGHT: usize = 4;
pub const ADDRESS: u8 = 0x3F;
pub fn new<I2cPeripheral, SdaPin, SclPin>(
i2c_peripheral: I2cPeripheral,
sda: SdaPin,
scl: SclPin,
spawner: embassy_executor::Spawner,
) -> Result<&'static Self> {
static INSTANCE: LcdTextGenerated20x4 = LcdTextGenerated20x4;
let _ = (i2c_peripheral, sda, scl, spawner);
Ok(&INSTANCE)
}
}
impl crate::lcd_text::LcdText<20, 4> for LcdTextGenerated20x4 {
const ADDRESS: u8 = 0x3F;
fn write_text(&self, text: impl AsRef<str>) {
let _ = text;
}
}
}
#[doc(hidden)]
pub type __I2csSignal<T> = Signal<CriticalSectionRawMutex, T>;
#[doc(hidden)]
pub use device_envoy_core::lcd_text::LcdText as __LcdText;
#[doc(hidden)]
pub use device_envoy_core::lcd_text::LcdTextDriver as __LcdTextDriver;
#[doc(hidden)]
pub use device_envoy_core::lcd_text::render_lcd_text_frame as __render_lcd_text_frame;
#[doc(hidden)]
pub type __LcdTextFrame<const MAX_CHARS: usize> =
device_envoy_core::lcd_text::LcdTextFrame<MAX_CHARS>;
#[doc(hidden)]
pub const fn __max_lcd_cells<const N: usize>(widths: [usize; N], heights: [usize; N]) -> usize {
let mut max_cells = 0;
let mut index = 0;
while index < N {
let cells = widths[index] * heights[index];
if cells > max_cells {
max_cells = cells;
}
index += 1;
}
max_cells
}
#[doc(hidden)]
pub async fn __select_array<Fut, const N: usize>(futures: [Fut; N]) -> (Fut::Output, usize)
where
Fut: core::future::Future,
{
embassy_futures::select::select_array(futures).await
}
#[doc(hidden)]
pub const fn __assert_unique_addresses<const N: usize>(addresses: [u8; N]) {
let mut first_index = 0;
while first_index < N {
let mut second_index = first_index + 1;
while second_index < N {
if addresses[first_index] == addresses[second_index] {
panic!("duplicate lcd_text I2C address in i2cs! group");
}
second_index += 1;
}
first_index += 1;
}
}
#[doc(hidden)]
pub async fn __write_lcd_text_cells<const ADDRESS_COUNT: usize, const MAX_CHARS: usize>(
lcd_text_driver: &mut LcdTextDriver,
lcd_text_write: &mut impl LcdTextWrite,
initialized_addresses: &mut Vec<u8, ADDRESS_COUNT>,
address: u8,
width: usize,
height: usize,
cells: &[u8],
) {
let first_use_of_address = !initialized_addresses
.iter()
.any(|initialized_address| *initialized_address == address);
lcd_text_driver.set_address(address);
if first_use_of_address {
if lcd_text_driver.init(lcd_text_write).await.is_err() {
return;
}
let _ = initialized_addresses.push(address);
}
let mut lcd_text_frame = LcdTextFrame::<MAX_CHARS>::new_blank(width, height);
let cell_count = core::cmp::min(width * height, cells.len());
for cell_index in 0..cell_count {
lcd_text_frame.cells[cell_index] = cells[cell_index];
}
let _ = lcd_text_driver
.write_frame(lcd_text_write, &lcd_text_frame)
.await;
}
#[doc(hidden)]
pub struct EspLcdTextWrite {
i2c: crate::esp_hal::i2c::master::I2c<'static, crate::esp_hal::Blocking>,
}
impl EspLcdTextWrite {
#[doc(hidden)]
pub fn __new(i2c: crate::esp_hal::i2c::master::I2c<'static, crate::esp_hal::Blocking>) -> Self {
Self { i2c }
}
}
impl LcdTextWrite for EspLcdTextWrite {
fn write(&mut self, address: u8, data: u8) -> core::result::Result<(), LcdTextError> {
self.i2c
.write(address, &[data])
.map_err(|_| LcdTextError::I2cWrite { address })
}
}
#[cfg(not(feature = "host"))]
#[doc(hidden)]
#[macro_export]
macro_rules! i2cs {
($($tt:tt)*) => { $crate::__i2cs_impl! { $($tt)* } };
}
#[cfg(not(feature = "host"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __i2cs_impl {
(
i2c: $i2c:ident,
sda_pin: $sda_pin:ident,
scl_pin: $scl_pin:ident,
$group_vis:vis $group_name:ident {
$(
$lcd_vis:vis $lcd_name:ident {
width: $width:expr,
height: $height:expr,
address: $address:expr
}
),+ $(,)?
}
) => {
$crate::lcd_text::paste::paste! {
const _: () = {
$crate::lcd_text::__assert_unique_addresses([$($address,)+]);
};
const [<__ $group_name:upper _MAX_LCD_CELLS>]: usize =
$crate::lcd_text::__max_lcd_cells([$($width,)+], [$($height,)+]);
$(
static [<$lcd_name:upper _FRAME_SIGNAL>]:
$crate::lcd_text::__I2csSignal<
$crate::lcd_text::__LcdTextFrame<{ [<__ $group_name:upper _MAX_LCD_CELLS>] }>
> =
$crate::lcd_text::__I2csSignal::new();
)+
$group_vis struct $group_name;
struct [<__ $group_name Devices>] {
$(
[<$lcd_name:snake>]: &'static $lcd_name,
)+
}
impl [<__ $group_name Devices>] {
fn into_tuple(self) -> ($(&'static $lcd_name,)+) {
(
$(self.[<$lcd_name:snake>],)+
)
}
}
impl $group_name {
fn __new_devices(
i2c_peripheral: $crate::esp_hal::peripherals::$i2c<'static>,
sda: $crate::esp_hal::peripherals::$sda_pin<'static>,
scl: $crate::esp_hal::peripherals::$scl_pin<'static>,
spawner: embassy_executor::Spawner,
) -> $crate::Result<[<__ $group_name Devices>]> {
let i2c = $crate::esp_hal::i2c::master::I2c::new(
i2c_peripheral,
$crate::esp_hal::i2c::master::Config::default(),
)
.map_err($crate::Error::I2cConfig)?
.with_sda(sda)
.with_scl(scl);
let token = [<__i2cs_task_ $group_name:snake>](i2c);
spawner.spawn(token.map_err($crate::Error::TaskSpawn)?);
$(
static [<$lcd_name:upper _INSTANCE>]: $lcd_name = $lcd_name;
let [<$lcd_name:snake>] = &[<$lcd_name:upper _INSTANCE>];
)+
Ok([<__ $group_name Devices>] {
$(
[<$lcd_name:snake>],
)+
})
}
pub fn new(
i2c_peripheral: $crate::esp_hal::peripherals::$i2c<'static>,
sda: $crate::esp_hal::peripherals::$sda_pin<'static>,
scl: $crate::esp_hal::peripherals::$scl_pin<'static>,
spawner: embassy_executor::Spawner,
) -> $crate::Result<($(&'static $lcd_name,)+)> {
Ok(Self::__new_devices(i2c_peripheral, sda, scl, spawner)?.into_tuple())
}
}
$(
$lcd_vis struct $lcd_name;
impl $crate::lcd_text::__LcdText<$width, $height> for $lcd_name {
const ADDRESS: u8 = $address;
fn write_text(&self, text: impl AsRef<str>) {
::core::assert!($width > 0, "lcd_text width must be > 0");
::core::assert!($height > 0, "lcd_text height must be > 0");
::core::assert!(
$height <= 4,
"lcd_text height must be <= 4 for HD44780 row map"
);
let lcd_text_frame =
$crate::lcd_text::__render_lcd_text_frame::<
$width,
$height,
{ [<__ $group_name:upper _MAX_LCD_CELLS>] }
>(text.as_ref());
[<$lcd_name:upper _FRAME_SIGNAL>].signal(lcd_text_frame);
}
}
impl $lcd_name {
pub const WIDTH: usize = $width;
pub const HEIGHT: usize = $height;
pub const ADDRESS: u8 = $address;
pub fn new(
i2c_peripheral: $crate::esp_hal::peripherals::$i2c<'static>,
sda: $crate::esp_hal::peripherals::$sda_pin<'static>,
scl: $crate::esp_hal::peripherals::$scl_pin<'static>,
spawner: embassy_executor::Spawner,
) -> $crate::Result<&'static Self> {
let [<__ $group_name:snake _devices>] =
$group_name::__new_devices(i2c_peripheral, sda, scl, spawner)?;
Ok([<__ $group_name:snake _devices>].[<$lcd_name:snake>])
}
}
)+
#[embassy_executor::task]
async fn [<__i2cs_task_ $group_name:snake>](
i2c: $crate::esp_hal::i2c::master::I2c<'static, $crate::esp_hal::Blocking>,
) -> ! {
let mut esp_lcd_text_write = $crate::lcd_text::EspLcdTextWrite::__new(i2c);
let mut lcd_text_driver = $crate::lcd_text::__LcdTextDriver::new(0x27);
const ADDRESS_COUNT: usize = [$($address,)+].len();
let mut initialized_addresses: heapless::Vec<u8, ADDRESS_COUNT> = heapless::Vec::new();
let addresses = [$($address,)+];
let widths = [$($width,)+];
let heights = [$($height,)+];
loop {
let (lcd_text_frame, ready_index) = $crate::lcd_text::__select_array([
$([<$lcd_name:upper _FRAME_SIGNAL>].wait(),)+
]).await;
$crate::lcd_text::__write_lcd_text_cells::<
ADDRESS_COUNT,
{ [<__ $group_name:upper _MAX_LCD_CELLS>] }
>(
&mut lcd_text_driver,
&mut esp_lcd_text_write,
&mut initialized_addresses,
addresses[ready_index],
widths[ready_index],
heights[ready_index],
&lcd_text_frame.cells,
).await;
}
}
}
};
}
#[cfg(not(feature = "host"))]
#[doc(inline)]
pub use i2cs;
#[cfg(not(feature = "host"))]
#[doc(hidden)]
#[macro_export]
macro_rules! lcd_text {
($($tt:tt)*) => { $crate::__lcd_text_impl! { $($tt)* } };
}
#[cfg(not(feature = "host"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __lcd_text_impl {
(
i2c: $i2c:ident,
sda_pin: $sda_pin:ident,
scl_pin: $scl_pin:ident,
$lcd_vis:vis $lcd_name:ident {
width: $width:expr,
height: $height:expr,
address: $address:expr
}
) => {
$crate::lcd_text::paste::paste! {
$crate::i2cs! {
i2c: $i2c,
sda_pin: $sda_pin,
scl_pin: $scl_pin,
[<LcdTextGroupFor $lcd_name>] {
$lcd_vis $lcd_name {
width: $width,
height: $height,
address: $address
}
}
}
}
};
}
#[cfg(not(feature = "host"))]
#[doc(inline)]
pub use lcd_text;