I'm working on a rust plugin that talks to loupedeck devices, specifically the razer streamdeck. Here's my current codebase:

////// src/constants.rs //////

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq)]
#[repr(u16)]
pub enum MessageHeader 
{
    Confirm = 0x0302,
    SerialOut = 0x0303,
    VersionOut = 0x0307,
    Tick = 0x0400,
    SetBrightness = 0x0409,
    ConfirmFrameBuffer = 0x0410,
    SetVibration = 0x041b,
    ButtonPress = 0x0500,
    Reset = 0x0506,
    DrawOut = 0x050F,
    DrawIn = 0x040F,
    SetColor = 0x0702,
    VersionIn = 0x0c07,
    MCU = 0x180d,
    SerialIn = 0x1f03,
    WriteFrameBuffer = 0xff10,
}

impl TryFrom<u16> for MessageHeader 
{
    type Error = u16;
    fn try_from(value: u16) -> Result<Self, Self::Error> 
    {
        let result = match value 
        {
            0x0302 => Ok(MessageHeader::Confirm),
            0x0303 => Ok(MessageHeader::SerialOut),
            0x0307 => Ok(MessageHeader::VersionOut),
            0x0400 => Ok(MessageHeader::Tick),
            0x0409 => Ok(MessageHeader::SetBrightness),
            0x0410 => Ok(MessageHeader::ConfirmFrameBuffer),
            0x041b => Ok(MessageHeader::SetVibration),
            0x0500 => Ok(MessageHeader::ButtonPress),
            0x050F => Ok(MessageHeader::DrawOut),
            0x040F => Ok(MessageHeader::DrawIn),
            0x0506 => Ok(MessageHeader::Reset),
            0x0702 => Ok(MessageHeader::SetColor),
            0x0c07 => Ok(MessageHeader::VersionIn),
            0x180d => Ok(MessageHeader::MCU),
            0x1f03 => Ok(MessageHeader::SerialIn),
            0xff10 => Ok(MessageHeader::WriteFrameBuffer),
            value => Err(value),
        };
        println!("Message: {result:?}");
        result
    }
}

impl From<MessageHeader> for Vec<u8> 
{
    fn from(value: MessageHeader) -> Vec<u8> 
    {
        vec![((value.clone() as u16) >> 8) as u8, value.clone() as u8]
    }
}

#[derive(Debug, Serialize, Clone)]
#[repr(u8)]
pub enum Button 
{
    B00 = 27,
    B10 = 28,
    B20 = 29,
    B30 = 30,
    B40 = 31,
    B01 = 32,
    B11 = 33,
    B21 = 34,
    B31 = 35,
    B41 = 36,
    B02 = 37,
    B12 = 38,
    B22 = 39,
    B32 = 40,
    B42 = 41,
}

impl Button
{
    pub fn get_x(&self) -> u8
    {
        match self
        {
            Self::B00 | Self::B01 | Self::B02 => 0,
            Self::B10 | Self::B11 | Self::B12 => 1,
            Self::B20 | Self::B21 | Self::B22 => 2,
            Self::B30 | Self::B31 | Self::B32 => 3,
            Self::B40 | Self::B41 | Self::B42 => 4,
        }
    }

    pub fn get_y(&self) -> u8
    {
        match self
        {
            Self::B00 | Self::B10 | Self::B20 | Self::B30 | Self::B40 => 0,
            Self::B01 | Self::B11 | Self::B21 | Self::B31 | Self::B41 => 1,
            Self::B02 | Self::B12 | Self::B22 | Self::B32 | Self::B42 => 2,
        }
    }
}

impl TryFrom<u8> for Button 
{
    type Error = u8;
    fn try_from(value: u8) -> Result<Self, Self::Error>
    {
        match value 
        {
            27 => Ok(Button::B00),
            28 => Ok(Button::B10),
            29 => Ok(Button::B20),
            30 => Ok(Button::B30),
            31 => Ok(Button::B40),
            32 => Ok(Button::B01),
            33 => Ok(Button::B11),
            34 => Ok(Button::B21),
            35 => Ok(Button::B31),
            36 => Ok(Button::B41),
            37 => Ok(Button::B02),
            38 => Ok(Button::B12),
            39 => Ok(Button::B22),
            40 => Ok(Button::B32),
            41 => Ok(Button::B42),
            value => Err(value),
        }
    }
}

#[derive(Debug, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u16)]
pub enum Screen 
{
    Left,
    Center,
    Right,
}

impl From<Screen> for u16 
{
    fn from(value: Screen) -> u16 
    {
        match value 
        {
            Screen::Left => 0x004C,
            Screen::Center => 0x0041,
            Screen::Right => 0x0052,
        }
    }
}

impl TryFrom<u8> for Screen 
{
    type Error = u8;
    fn try_from(value: u8) -> Result<Self, Self::Error> 
    {
        match value 
        {
            0x4C => Ok(Screen::Left),
            0x41 => Ok(Screen::Center),
            0x52 => Ok(Screen::Right),
            value => Err(value),
        }
    }
}

impl From<Screen> for Vec<u8> 
{
    fn from(value: Screen) -> Vec<u8> 
    {
        let base = u16::from(value);
        vec![((base.clone() as u16) >> 8) as u8, base.clone() as u8]
    }
}

struct Display 
{
    id: u16,
    width: u16,
    height: u16,
}

impl Screen 
{
    pub fn from_x_coor(x: u16) -> Option<Screen> 
    {
        match x 
        {
            0..=60 => Some(Screen::Left),
            61..=420 => Some(Screen::Center),
            421..=480 => Some(Screen::Right),
            _ => None,
        }
    }
}

#[derive(Debug, Serialize, Clone)]
#[repr(u8)]
pub enum PressDirection 
{
    Up = 0x00,
    Down = 0x01,
}

impl TryFrom<u8> for PressDirection 
{
    type Error = u8;
    fn try_from(value: u8) -> Result<Self, Self::Error> 
    {
        match value 
        {
            0x00 => Ok(PressDirection::Down),
            0x01 => Ok(PressDirection::Up),
            value => Err(value),
        }
    }
}

#[derive(Debug, Serialize, Clone)]
pub struct ButtonPressEvent 
{
    pub tx_id: u8,
    pub button: Button,
    pub dir: PressDirection,
}

#[derive(Debug, Serialize, Clone)]
pub struct FallbackEvent 
{
    pub tx_id: u8,
}

#[derive(Debug, Serialize, Clone)]
pub struct SerialInEvent 
{
    pub tx_id: u8,
    pub serial_number: String,
}

#[derive(Debug, Serialize, Clone)]
pub struct ConfirmFrameBufferInEvent 
{
    pub tx_id: u8,
}

#[derive(Debug, Serialize, Clone)]
pub struct VersionInEvent 
{
    pub tx_id: u8,
    pub version: String,
}

#[derive(Debug, Serialize, Clone)]
pub struct DrawInEvent 
{
    pub tx_id: u8,
}

#[derive(Debug, Serialize, Clone)]
pub enum Event 
{
    ButtonPress(ButtonPressEvent),
    Other(FallbackEvent),
    SerialIn(SerialInEvent),
    VersionIn(VersionInEvent),
    ConfirmFrameBufferIn(ConfirmFrameBufferInEvent),
    DrawIn(DrawInEvent),
}

pub static KEY_SIZE: u16 = 80;

#[derive(Debug, Serialize, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
pub struct KeyLocation 
{
    pub x: u8,
    pub y: u8,
}

impl KeyLocation 
{
    pub fn new(x: u8, y: u8) -> KeyLocation 
    {
        KeyLocation { x, y }
    }

    pub fn from_location(raw_x: u16, raw_y: u16) -> KeyLocation 
    {
        let x = ((raw_x - 60) / KEY_SIZE) as u8;
        let y = (raw_y / KEY_SIZE) as u8;
        KeyLocation { x, y }
    }
}
////// src/controller/mod.rs //////

use crate::constants::{Button, ButtonPressEvent, Event, KeyLocation, PressDirection};
use crate::loupedeck::{Device, ExternalDeviceEventEmitter};
use crate::plugin::{PluginRegistrar, PluginScreenContext, ScreenPlugin};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use std::collections::HashMap;
use tokio::runtime::Runtime;
use tokio::sync::mpsc;
use tokio::time;

mod plugin;
use plugin::*;

pub use plugin::PluginIdentifier;

struct Page 
{
    name: String,
    screen: HashMap<KeyLocation, ScreenPluginProxy>,
}

impl From<PageConfig> for Page 
{
    fn from(config: PageConfig) -> Self {
        Page {
            name: config.name,
            screen: HashMap::new(),
        }
    }
}

pub struct ScreenPluginProxy 
{
    plugin: Box<dyn ScreenPlugin>,
}

unsafe impl Send for ScreenPluginProxy {}
unsafe impl Sync for ScreenPluginProxy {}

#[serde_as]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PageConfig 
{
    pub name: String,
    #[serde_as(as = "Vec<(_, _)>")]
    pub screen: HashMap<KeyLocation, PluginIdentifier>,
}

pub struct ControllerState 
{
    current_page: Option<Page>,
    notify: mpsc::Sender<Page>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ControllerConfig 
{
    pub pages: HashMap<String, PageConfig>,
}

#[derive(Debug, Serialize, Clone, PartialEq)]
pub enum DeviceConnectionStatus 
{
    Connecting,
    Connected,
    Disconnected,
}

pub struct Controller 
{
    plugin_registry: PluginRegistry,
    state: Option<ControllerState>,
    config: ControllerConfig,
    runtime: Option<Runtime>,
    event_emitter: Option<ExternalDeviceEventEmitter>,
}

impl Controller 
{
    pub fn new() -> Controller 
    {
        return Controller 
        {
            plugin_registry: PluginRegistry::new(),
            state: None,
            runtime: None,
            event_emitter: None,
            config: ControllerConfig 
            {
                pages: HashMap::default(),
            },
        };
    }

    pub fn load_plugin(&mut self, plugin_path: &str) -> Result<(), Box<dyn std::error::Error>> 
    {
        unsafe { self.plugin_registry.load_from_path(plugin_path.to_string()) }
    }

    pub fn get_connection_status(&self) -> DeviceConnectionStatus
    {
        if self.runtime.is_none() 
        {
            DeviceConnectionStatus::Disconnected
        }
        else
        {
            DeviceConnectionStatus::Connected
        }
    }

    pub async fn set_current_page(&mut self, page_name: String) -> std::io::Result<()> 
    {
        let page_config = self.config.pages.get(&page_name);
        if page_config.is_none() {
            return Ok(());
        }

        let current_page = self.create_page_instance(page_config.unwrap()).unwrap();

        let state = self.state.as_ref();

        if state.is_some() 
        {
            let state = state.unwrap();
            state.notify.send(current_page).await;
        }

        println!("Set page to {}", page_name);
        return Ok(());
    }

    fn create_page_instance(&self, page_config: &PageConfig) -> std::io::Result<Page> 
    {
        let mut page_instance = Page::from(page_config.clone());

        for (key, plugin_identifier) in page_config.screen.iter() {
            let plugin_id = &plugin_identifier.plugin_id;

            let plugin = self.plugin_registry.plugins.get(plugin_id);
            if plugin.is_none() {
                continue;
            }

            let plugin = plugin.unwrap();
            let screen = plugin.screens.get(&plugin_identifier.plugin_ref);

            if screen.is_none() {
                continue;
            }

            let screen = screen.unwrap();

            if self.event_emitter.is_some() {
                let event_emitter_copy = self.event_emitter.clone().unwrap();
                let plugin_context = PluginScreenContext::new(
                    event_emitter_copy,
                    crate::constants::Screen::Center,
                    (*key).clone(),
                );

                println!(
                    "Creating screen plugin instance for {:?} at {:?}",
                    plugin_identifier.plugin_ref, *key
                );

                let screen_instance = screen(plugin_context);

                page_instance.screen.insert(
                    *key,
                    ScreenPluginProxy {
                        plugin: screen_instance,
                    },
                );
            }
        }

        Ok(page_instance)
    }

    pub fn set_page(&mut self, page: PageConfig) -> std::io::Result<()> 
    {
        self.config.pages.insert(page.name.clone(), page);
        Ok(())
    }

    pub fn start(&mut self, mut device: Device) 
    {
        self.runtime = Some(Runtime::new().unwrap());
        let runtime = self.runtime.as_ref().unwrap();

        let (tx_pending_send, mut rx_pending_send) = mpsc::channel(10);

        self.event_emitter = device.create_external_event_emitter();

        let current_state = ControllerState 
        {
            current_page: None,
            notify: tx_pending_send,
        };

        self.state = Some(current_state);

        runtime.spawn(async move 
        {
            let mut rx_event = device.tx_event.clone().unwrap().subscribe();

            let mut current_page: Option<Page> = None;

            loop 
            {
                while let Ok(next_event) = rx_event.try_recv() 
                {
                    match next_event 
                    {
                        Event::ButtonPress(ButtonPressEvent 
                        {
                            tx_id,
                            button,
                            dir: PressDirection::Down,
                        }) => 
                        {
                            println!("Button press: {tx_id:?}:{button:?}:{:?}", PressDirection::Down);
                            device.draw_key(button.get_x().into(), button.get_y().into(), vec![200; 6400 * 2]).await
                            //time::sleep(time::Duration::from_millis(1)).await
                            //device.vibrate(Haptic::ShortLow).await
                        },
                        _ => {}
                    }

                    time::sleep(time::Duration::from_millis(1)).await
                }

                while let Ok(next_page) = rx_pending_send.try_recv() {
                    println!("Updating page");
                    println!(
                        "Name {:?}, Keys: {:?}",
                        next_page.name,
                        next_page.screen.keys()
                    );
                    current_page = Some(next_page);
                }
            }
        });
    }

    pub fn get_page(&self, page_name: String) -> Option<PageConfig> 
    {
        let page_config = self.config.pages.get(&page_name);
        if page_config.is_none() {
            return None;
        }

        return Some(page_config.unwrap().clone());
    }

    pub fn get_page_names(&self) -> Vec<String> 
    {
        return self.config.pages.keys().map(|x| x.to_string()).collect();
    }

    pub fn list_plugins(&self) -> std::io::Result<Vec<PluginIdentifier>> 
    {
        let mut plugins: Vec<PluginIdentifier> = Vec::new();

        for (key, plugin) in &self.plugin_registry.plugins {
            for (screen_key, _) in &plugin.screens {
                plugins.push(PluginIdentifier {
                    plugin_id: key.clone(),
                    plugin_ref: screen_key.clone(),
                });
            }
        }

        return Ok(plugins);
    }
}
////// src/controller/plugin.rs //////

use crate::plugin::{PluginDeclaration, PluginRegistrar, ScreenPluginFactory, ScreenPluginOptions};
use libloading::Library;
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::sync::Arc;
use std::{alloc::System, collections::HashMap, env, ffi::OsStr, io, sync::Mutex};

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PluginIdentifier 
{
    pub plugin_id: String,
    pub plugin_ref: String,
}

#[derive(Debug, Clone)]
pub struct LocalLoadedPlugin 
{
    lib: Arc<Library>,
    pub plugin_id: String,
    pub screens: HashMap<String, ScreenPluginFactory>,
}

pub struct TempPluginRegistrar 
{
    screens: HashMap<String, ScreenPluginFactory>,
    lib: Arc<Library>,
}

impl TempPluginRegistrar 
{
    pub fn new(lib: Arc<Library>) -> TempPluginRegistrar 
    {
        TempPluginRegistrar 
        {
            screens: HashMap::default(),
            lib,
        }
    }

    pub fn to_local_plugin(self, plugin_id: String) -> LocalLoadedPlugin 
    {
        LocalLoadedPlugin 
        {
            lib: self.lib,
            plugin_id,
            screens: self.screens,
        }
    }
}

impl PluginRegistrar for TempPluginRegistrar 
{
    fn register_screen
    (
        &mut self,
        name: &str,
        options: ScreenPluginOptions,
        create: ScreenPluginFactory,
    ) -> Result<(), std::io::Error> 
    {
        self.screens.insert(name.to_string(), create);

        return Ok(());
    }
}

pub struct PluginRegistry 
{
    pub plugins: HashMap<String, LocalLoadedPlugin>,
}

impl PluginRegistry 
{
    pub fn new() -> Self 
    {
        Self 
        {
            plugins: HashMap::default(),
        }
    }

    pub unsafe fn load_from_path<P: AsRef<OsStr>>(&mut self, path: P) -> Result<(), Box<dyn Error>> 
    {
        let library = Arc::new(Library::new(path)?);
        let decl = library
            .get::<*mut PluginDeclaration>(b"plugin_declaration\0")?
            .read();

        // if decl.rustc_version != RUSTC_VERSION || decl.core_version != CORE_VERSION 
        // {
        //     Err(io::Error::new(io::ErrorKind::Other, "Version mismatch"))?
        // }

        let mut registrar = TempPluginRegistrar::new(Arc::clone(&library));
        (decl.register)(&mut registrar);

        let local_plugin = registrar.to_local_plugin(decl.plugin_id.to_string());

        println!(
            "Loaded plugin_id: {:?} with handlers: {:?}",
            local_plugin.plugin_id,
            local_plugin.screens.keys()
        );

        self.plugins
            .insert(decl.plugin_id.to_string(), local_plugin);

        Ok(())
    }
}
////// src/lib.rs //////

mod constants;
mod loupedeck;
mod plugin;
mod controller;

pub use controller::*;
pub use constants::*;
pub use loupedeck::*;
pub use plugin::*;
////// src/loupedeck.rs //////

use mio_serial::SerialPort;
use raqote::DrawTarget;
use std::collections::HashSet;
use std::io::prelude::*;
use std::io::Result;
use tokio::runtime::Runtime;
use tokio::sync::broadcast;
use tokio::sync::mpsc;
use tokio::sync::oneshot;
use tokio::sync::oneshot::error::RecvError;
use tokio::time;
use tokio_serial::SerialPortBuilderExt;
use tokio_serial::SerialStream;

use crate::constants::*;

fn parse_serial_message(message: &[u8]) -> Result<Option<Event>> 
{
    let header: u16 = u16::from_be_bytes([message[0], message[1]]);
    // println!("Message type: {:?}", header);

    let message_type: MessageHeader = MessageHeader::try_from(header).unwrap();
    let tx_id = message[2];

    match message_type {
        MessageHeader::ButtonPress => {
            let button = Button::try_from(message[3]).expect("Invalid button address");
            let dir = PressDirection::try_from(message[4]).expect("Invalid button direction");
            Ok(Some(Event::ButtonPress(ButtonPressEvent 
            {
                button,
                dir,
                tx_id,
            })))
        }
        MessageHeader::SerialIn => Ok(Some(Event::SerialIn(SerialInEvent 
        {
            tx_id,
            serial_number: String::from_utf8(message[3..].to_vec())
                .unwrap()
                .trim()
                .to_string(),
        }))),
        MessageHeader::VersionIn => Ok(Some(Event::VersionIn(VersionInEvent 
        {
            tx_id,
            version: format!("{}.{}.{}", message[3], message[4], message[5]).to_string(),
        }))),
        MessageHeader::ConfirmFrameBuffer => Ok(Some(Event::ConfirmFrameBufferIn
        (
            ConfirmFrameBufferInEvent { tx_id },
        ))),
        MessageHeader::DrawIn => Ok(Some(Event::DrawIn(DrawInEvent { tx_id }))),
        _ => Ok(Some(Event::Other(FallbackEvent { tx_id }))),
    }
}

fn get_tx_id(evt: Event) -> Option<u8> 
{
    match evt 
    {
        Event::ButtonPress(ButtonPressEvent { tx_id, .. }) => Some(tx_id),
        Event::Other(FallbackEvent { tx_id }) => Some(tx_id),
        Event::SerialIn(SerialInEvent { tx_id, .. }) => Some(tx_id),
        Event::VersionIn(VersionInEvent { tx_id, .. }) => Some(tx_id),
        Event::ConfirmFrameBufferIn(ConfirmFrameBufferInEvent { tx_id }) => Some(tx_id),
        Event::DrawIn(DrawInEvent { tx_id }) => Some(tx_id),
        _ => panic!("Invalid event"),
    }
}

const WS_UPGRADE_HEADER: &str = "GET /index.html
HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: 123abc

";

const WS_UPGRADE_RESPONSE: &str = "HTTP/1.1";

#[derive(Debug, Clone)]
pub struct DeviceInfo 
{
    serial: String,
    version: String,
}

pub struct Device 
{
    pub port: String,
    pub(crate) runtime: Option<Runtime>,
    tx_pending_send: Option<mpsc::Sender<Vec<u8>>>,
    pub tx_event: Option<broadcast::Sender<Event>>,
    next_tx_id: u8,
}

fn construct_draw_buffer_payload
(
    screen: Screen,
    x: u16,
    y: u16,
    width: u16,
    height: u16,
    buffer: &[u8],
) -> Result<Vec<u8>> 
{
    if (width * height * 2) as usize != buffer.len() 
    {
        return Err(std::io::Error::new
        (
            std::io::ErrorKind::InvalidInput,
            "Invalid buffer length",
        ));
    }

    let mut buff = Vec::new();
    // buff.append(&mut vec![0x00, 0x4D, 0x9A, 0xD1, 0xFF, 0xED, 0x9B, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 
    //     0x04, 0x06, 0x07, 0x09, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 
    //     0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0B, 0x0A, 0x0A, 0x08, 0x05, 0x03, 
    //     0x01, ]);
    buff.append(&mut Vec::from(screen));
    buff.append(&mut x.to_be_bytes().to_vec());
    buff.append(&mut y.to_be_bytes().to_vec());
    buff.append(&mut width.to_be_bytes().to_vec());
    buff.append(&mut height.to_be_bytes().to_vec());
    buff.append(&mut buffer.to_vec());

    return Ok(buff);
}

//
// 82 ff 00 00 00 00 00 00 3f 55 00 00 00 00 ff 10 02 00 41 00 00 00 00 00 5a 00 5a

fn is_redraw_event(msg: &[u8]) -> Option<Screen> 
{
    if msg.len() > 18 && msg[1] == 0xff && msg[14] == 0xff && msg[15] == 0x10 
    {
        Some(msg[18])
    } 
    else if msg.len() > 10 && msg[6] == 0xff && msg[7] == 0x10 
    {
        Some(msg[10])
    } 
    else
    {
        None
    }.map(Screen::try_from).and_then(|result| result.ok())
}

fn construct_basic_message(action: Vec<u8>, data: Vec<u8>) -> Result<Vec<u8>> 
{
    let header: Vec<u8> = vec![action[0], action[1], 0x01];

    let mut message: Vec<u8> = Vec::with_capacity(header.len() + data.len());
    message.extend_from_slice(&header);
    message.extend_from_slice(&data);

    return Ok(message);
}

fn construct_message_payload(message_buffer: Vec<u8>, tx_id: u8) -> Vec<u8> 
{
    let mut prefix_buff: Vec<u8>;

    if message_buffer.len() > 0xff 
    {
        prefix_buff = Vec::with_capacity(14);

        for i in 0..14 
        {
            prefix_buff.push(0x00);
        }

        prefix_buff[0] = 0x82;
        prefix_buff[1] = 0xFF;

        let offset = 6;
        // println!("Message length: {:?}", message_buffer.len());

        (message_buffer.len() as u32)
            .to_be_bytes()
            .iter()
            .enumerate()
            .for_each(|(i, b)| {
                prefix_buff[offset + i] = *b;
            });

        // prefix_buff[2 + offset] = (len >> 24) as u8;
        // prefix_buff[3 + offset] = (len >> 16) as u8;
        // prefix_buff[4 + offset] = (len >> 8) as u8;
        // prefix_buff[5 + offset] = len as u8;
    } 
    else 
    {
        prefix_buff = Vec::with_capacity(6);

        for i in 0..6 
        {
            prefix_buff.push(0x00);
        }

        prefix_buff[0] = 0x82;
        prefix_buff[1] = 0x80 + message_buffer.len() as u8;
    }

    let mut message = Vec::with_capacity(prefix_buff.len() + message_buffer.len());
    message.extend_from_slice(&prefix_buff);
    message.extend_from_slice(&message_buffer);

    message[prefix_buff.len() + 2] = tx_id;

    message
}

fn to_hex(bytes: &[u8]) -> String 
{
    bytes
        .iter()
        .map(|b| format!("{:02x}", b))
        .collect::<Vec<String>>()
        .join(" ")
}

#[derive(Debug, Clone)]
pub struct ExternalMessage 
{
    action: Vec<u8>,
    data: Vec<u8>,
}

impl From<ExternalMessage> for Vec<u8> 
{
    fn from(external_message: ExternalMessage) -> Vec<u8> 
    {
        let mut header: Vec<u8> = Vec::with_capacity(3);
        header.push(external_message.action[0]);
        header.push(external_message.action[1]);
        header.push(0x01);

        let mut message: Vec<u8> = Vec::with_capacity(header.len() + external_message.data.len());
        message.extend_from_slice(&header);
        message.extend_from_slice(&external_message.data);

        return construct_message_payload(message, 1);
    }
}

#[derive(Debug, Clone)]
pub struct ExternalDeviceEventEmitter 
{
    tx_event: mpsc::Sender<ExternalMessage>,
}

impl ExternalDeviceEventEmitter 
{
    fn new(tx_event: mpsc::Sender<ExternalMessage>) -> Self 
    {
        Self { tx_event }
    }

    async fn send_message(&self, message: ExternalMessage) -> Result<()> 
    {
        // println!("Sending message: {:?}", message);
        self.tx_event.send(message).await;
        Ok(())
    }

    pub async fn draw_rgb565
    (
        &self,
        screen: Screen,
        x: u16,
        y: u16,
        width: u16,
        height: u16,
        data: Vec<u8>,
    ) -> Result<()> 
    {
        let buff_res =
            construct_draw_buffer_payload(screen.clone(), x, y, width, height, data.as_slice());

        self.send_message(ExternalMessage 
        {
            action: Vec::from(MessageHeader::WriteFrameBuffer),
            data: buff_res.unwrap(),
        })
        .await;

        Ok(())
    }

    pub async fn draw_target
    (
        &self,
        screen: Screen,
        x: u16,
        y: u16,
        width: u16,
        height: u16,
        target: DrawTarget,
    ) -> Result<()> 
    {
        let draw_vec = convert_draw_target_to_rgb565(target);
        self.draw_rgb565(screen, x, y, width, height, draw_vec)
            .await
    }
}

impl Device {
    pub fn new(port: String) -> Device 
    {
        Device 
        {
            port,
            runtime: None,
            tx_pending_send: None,
            tx_event: None,
            next_tx_id: 2,
        }
    }

    pub fn create_external_event_emitter(&self) -> Option<ExternalDeviceEventEmitter> 
    {
        let runtime_ref = self.runtime.as_ref();

        if runtime_ref.is_none() 
        {
            return None;
        }

        let runtime = runtime_ref.unwrap();

        let (tx_ext_message, mut rx_ext_message): 
        (
            mpsc::Sender<ExternalMessage>,
            mpsc::Receiver<ExternalMessage>,
        ) = mpsc::channel(10);

        let tx_pending_send = self.tx_pending_send.as_ref().unwrap().clone();

        runtime.spawn(async move 
        {
            loop 
            {
                // Try to write everything we can from the port
                while let Ok(ext_message) = rx_ext_message.try_recv() 
                {
                    let message = Vec::from(ext_message);
                    // println!("Sending external message: {:?}", to_hex(&message));
                    tx_pending_send.send(message).await.unwrap();
                }
            }
        });

        Some(ExternalDeviceEventEmitter::new(tx_ext_message))
    }

    pub async fn connect(&mut self) -> Result<()> 
    {
        if self.runtime.is_some() 
        {
            return Ok(());
        }

        println!("Connecting to Loupedeck on port {}", self.port);

        let mut port = tokio_serial::new(self.port.clone(), 9600)
            .open_native_async()
            .expect("Failed to open port");

        println!("Waiting for writable");
        port.writable().await;

        port.try_write(WS_UPGRADE_HEADER.as_bytes())
            .expect("Failed to write to port");

        println!("Waiting for readable");
        port.readable().await;
        let mut buf = vec![0; 1024];

        port.try_read(buf.as_mut_slice())
            .expect("Failed to read from port");

        let res = String::from_utf8_lossy(&buf);

        if !res.contains(WS_UPGRADE_RESPONSE) {
            return Err(std::io::Error::new(
                std::io::ErrorKind::Other,
                "Failed to upgrade to websocket",
            ));
        }

        self.start_polling(port);

        return Ok(());
    }

    pub fn disconnect(&mut self) {
        println!("Disconnecting from loupedeck on port {}", self.port);
        self.tx_event = None;
        self.tx_pending_send = None;

        let runtime = self.runtime.take();

        if let Some(runtime) = runtime {
            runtime.shutdown_background();
        }
    }

    pub async fn draw_key(&mut self, key_x: u16, key_y: u16, img: Vec<u8>) 
    {
        let x: u16 = KEY_SIZE * key_x;
        let y: u16 = KEY_SIZE * key_y;
        let width: u16 = 80;
        let height: u16 = 80;

        self.draw_buffer(Screen::Center, x, y, width, height, img.as_slice())
            .await.unwrap();
    }

    pub async fn draw_buffer
    (
        &mut self,
        screen: Screen,
        x: u16,
        y: u16,
        width: u16,
        height: u16,
        buffer: &[u8],
    ) -> Result<()> 
    {
        let buff_res = construct_draw_buffer_payload(screen.clone(), x, y, width, height, buffer);

        if buff_res.is_err() 
        {
            return Err(buff_res.unwrap_err());
        }

        self.send_message
        (
            Vec::from(MessageHeader::WriteFrameBuffer),
            buff_res.unwrap(),
            true,
        )
        .await;

        Ok(())
    }

    pub async fn get_info(&mut self) -> Result<DeviceInfo> {
        let serial_evt = self
            .send_message(Vec::from(MessageHeader::SerialOut), vec![], true)
            .await;
        let version_evt = self
            .send_message(Vec::from(MessageHeader::VersionOut), vec![], true)
            .await;

        let mut version_number: Option<String> = None;
        let mut serial: Option<String> = None;

        if serial_evt.is_some() {
            let serial_evt_result = serial_evt.unwrap();
            if serial_evt_result.is_ok() {
                let serial_evt_event = serial_evt_result.unwrap();
                if let Event::SerialIn(SerialInEvent { serial_number, .. }) = serial_evt_event {
                    serial = Some(serial_number);
                }
            }
        }

        if version_evt.is_some() {
            let version_in_result = version_evt.unwrap();
            if version_in_result.is_ok() {
                let version_in_event = version_in_result.unwrap();
                if let Event::VersionIn(VersionInEvent { version, .. }) = version_in_event {
                    version_number = Some(version);
                }
            }
        }

        if version_number.is_none() || serial.is_none() {
            return Err(std::io::Error::new(
                std::io::ErrorKind::Other,
                "Failed to get device info",
            ));
        }

        return Ok(DeviceInfo {
            serial: serial.unwrap(),
            version: version_number.unwrap(),
        });
    }

    async fn send_message(
        &mut self,
        action: Vec<u8>,
        data: Vec<u8>,
        expect_event: bool,
    ) -> Option<std::result::Result<Event, RecvError>> 
    {
        let message = construct_basic_message(action, data);
        return self.send(message.unwrap(), expect_event).await;
    }

    async fn send
    (
        &mut self,
        message_buffer: Vec<u8>,
        expect_event: bool,
    ) -> Option<std::result::Result<Event, RecvError>> {
        let (response_tx, response_rx): (oneshot::Sender<Event>, oneshot::Receiver<Event>) =
            oneshot::channel();

        let mut tx_id: u8 = 0x01;
        if expect_event {
            tx_id = self.next_tx_id;
            self.next_tx_id += 1;
        }

        let runtime = self.runtime.as_ref().unwrap();
        let tx_pending_send = self.tx_pending_send.as_ref().unwrap().clone();
        let message = construct_message_payload(message_buffer, tx_id);

        if self.tx_event.is_some() && expect_event {
            let mut rx_event = self.tx_event.clone().unwrap().subscribe();

            runtime.spawn(async move {
                while let Ok(res) = rx_event.recv().await {
                    let evt_tx_id = get_tx_id(res.clone());

                    if evt_tx_id.is_some() && evt_tx_id.unwrap() == tx_id {
                        response_tx.send(res).unwrap();
                        break;
                    }
                }
            });
        }

        runtime.spawn(async move {
            tx_pending_send.send(message).await;
        });

        if expect_event {
            return Some(response_rx.await);
        } else {
            return None;
        }
    }

    fn start_polling(&mut self, mut serial: SerialStream) 
    {
        let (tx_event, mut rx_event) = broadcast::channel(10);
        let (tx_pending_send, mut rx_pending_send) = mpsc::channel(100);
        self.runtime = Some(Runtime::new().unwrap());

        self.tx_pending_send = Some(tx_pending_send.clone());
        self.tx_event = Some(tx_event.clone());

        println!("Starting polling on port {}", self.port);

        let runtime = self.runtime.as_ref().unwrap();

        runtime.spawn(async move 
        {
            loop 
            {
                // Send any pending messages

                let mut redrawn_screens = HashSet::new();

                // Try to write everything we can from the port
                while let Ok(message) = rx_pending_send.try_recv() 
                {
                    time::sleep(time::Duration::from_millis(2)).await;

                    loop 
                    {
                        let write_res = serial.write_all(&message);

                        match write_res {
                            Ok(_) => {
                                if let Some(redraw) = is_redraw_event(message.as_slice()) {
                                    redrawn_screens.insert(redraw);
                                }
                                break;
                            }
                            Err(e) => {
                                println!("Error writing to serial: {:?}", e);
                                break;
                            }
                        }
                    }
                }

                if !redrawn_screens.is_empty() 
                {
                    // Redraw any screens that have been updated
                    for screen in redrawn_screens 
                    {
                        let redraw_payload = construct_basic_message
                        (
                            Vec::from(MessageHeader::DrawOut),
                            Vec::from(screen),
                        )
                        .unwrap();

                        let redraw_message = construct_message_payload(redraw_payload, 1);

                        tx_pending_send.send(redraw_message).await.unwrap();
                    }
                }

                loop {
                    let mut single_byte: Vec<u8> = vec![0; 1];
                    let res = serial.try_read(single_byte.as_mut_slice());

                    if res.is_err() {
                        break;
                    }

                    if single_byte[0] == 0x82 {
                        // Found delimiter
                        serial
                            .try_read(single_byte.as_mut_slice())
                            .expect("Found no length data");
                        let length = single_byte[0] as usize;
                        // println!("Found message with length {}", length);
                        let mut data: Vec<u8> = vec![0; length];
                        serial
                            .try_read(data.as_mut_slice())
                            .expect("Found no data!");
                        let event = parse_serial_message(&data);

                        if event.is_ok() {
                            let evt_unrw = event.unwrap();
                            if evt_unrw.is_some() {
                                tx_event.send(evt_unrw.unwrap());
                            }
                        }
                    }
                }

                time::sleep(time::Duration::from_millis(5)).await
            }
        });
    }
}

const LOUPEDECK_VENDOR_ID: u16 = 11970;

pub fn get_loupedeck_ports() -> Vec<String> 
{
    let mut ports = Vec::new();

    tokio_serial::available_ports()
        .unwrap()
        .iter()
        .for_each(|port| {
            if let tokio_serial::SerialPortType::UsbPort(port_info) = &port.port_type {
                if port_info.vid == LOUPEDECK_VENDOR_ID {
                    ports.push(port.port_name.to_string());
                }
            }
        });

    return ports;
}

#[cfg(test)]
mod tests 
{
    use super::construct_draw_buffer_payload;

    #[test]
    fn it_create_draw_buffer_red_key_payload() {
        let mut half_red: Vec<u8> = Vec::with_capacity(80 * 80 * 2);
        for _ in 0..(80 * 80) {
            half_red.push(0x00);
            half_red.push(0xF8);
        }

        let mut payload = construct_draw_buffer_payload(
            crate::constants::Screen::Center,
            180,
            0,
            80,
            80,
            half_red.as_slice(),
        )
        .expect("Failed to create payload");

        let header = vec![0x00, 0x41, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x5a];

        assert_eq!(header, payload.drain(0..header.len()).collect::<Vec<u8>>());
        // assert_eq!(payload.len(), 16213);
    }
}

pub fn convert_draw_target_to_rgb565(dt: DrawTarget) -> Vec<u8> {
    let mut result: Vec<u8> = Vec::new();

    let draw_data = dt.get_data();
    for px in draw_data {
        let rgb16 = convert_argb888_to_rgb565(*px);
        result.append(&mut rgb16.to_le_bytes().to_vec());
    }

    result
}

fn convert_argb888_to_rgb565(argb: u32) -> u16 {
    let r = ((argb >> 16) & 0xff) as u16;
    let g = ((argb >> 8) & 0xff) as u16;
    let b = (argb & 0xff) as u16;
    ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
}

#[cfg(test)]
mod draw_tests {
    use raqote::{DrawOptions, DrawTarget, SolidSource, Source};

    use crate::loupedeck::convert_draw_target_to_rgb565;

    use super::convert_argb888_to_rgb565;

    #[test]
    fn it_converts() {
        assert_eq!(convert_argb888_to_rgb565(0xFF000000), 0);
        assert_eq!(convert_argb888_to_rgb565(0xFFFFFFFF), 0xffff);
        assert_eq!(convert_argb888_to_rgb565(0xFF0000FF), 0x001f);
        assert_eq!(convert_argb888_to_rgb565(0xFFFF0000), 0xf800);
        assert_eq!(convert_argb888_to_rgb565(0xFF00FF00), 0x07e0);
    }

    #[test]
    fn it_works_for_dt_red() {
        let mut dt = DrawTarget::new(1, 1);

        let mut solidRed: SolidSource =
            SolidSource::from_unpremultiplied_argb(255, 0xFF, 0x00, 0x00);

        dt.fill_rect(
            0.0,
            0.0,
            1.0,
            1.0,
            &Source::Solid(solidRed),
            &DrawOptions::new(),
        );

        assert_eq!(convert_draw_target_to_rgb565(dt), vec![0x00, 0xF8,]);
    }
    #[test]
    fn it_works_for_dt_green() {
        let mut dt = DrawTarget::new(1, 1);

        let mut solidGreen: SolidSource =
            SolidSource::from_unpremultiplied_argb(255, 0x00, 0xFF, 0x00);

        dt.fill_rect(
            0.0,
            0.0,
            1.0,
            1.0,
            &Source::Solid(solidGreen),
            &DrawOptions::new(),
        );

        assert_eq!(convert_draw_target_to_rgb565(dt), vec![0xE0, 0x07,]);
    }

    #[test]
    fn it_works_for_dt_blue() {
        let mut dt = DrawTarget::new(1, 1);

        let mut solidBlue: SolidSource =
            SolidSource::from_unpremultiplied_argb(255, 0x00, 0x00, 0xFF);

        dt.fill_rect(
            0.0,
            0.0,
            1.0,
            1.0,
            &Source::Solid(solidBlue),
            &DrawOptions::new(),
        );

        assert_eq!(convert_draw_target_to_rgb565(dt), vec![0x1F, 0x00]);
    }
}
////// src/main.rs //////

mod constants;
mod loupedeck;
mod plugin;
mod controller;

use std::collections::HashMap;
use tokio::time;
use controller::*;
use constants::*;

#[tokio::main]
async fn main() 
{
    let mut ld = loupedeck::Device::new("COM4".to_string());
    ld.connect().await.expect("Failed to connect to loupedeck");

    let mut controller = controller::Controller::new();

    let time_plugin_dir: &str =
        "C:\\Project\\Workspace\\Rust\\loupedeck-rs\\time_plugin\\target\\debug\\loupedeck_plugin_time";

    controller
        .load_plugin(time_plugin_dir)
        .expect("Failed to load plugin");

    let mut screen_map: HashMap<KeyLocation, PluginIdentifier> = HashMap::default();

    screen_map.insert(
        KeyLocation::new(0, 0),
        PluginIdentifier {
            plugin_id: "time-plugin".to_string(),
            plugin_ref: "current-time".to_string(),
        },
    );

    screen_map.insert(
        KeyLocation::new(2, 2),
        PluginIdentifier {
            plugin_id: "time-plugin".to_string(),
            plugin_ref: "current-date".to_string(),
        },
    );

    controller.start(ld);

    let page_config = PageConfig {
        name: "basic".to_string(),
        screen: screen_map,
    };

    controller
        .set_page(page_config.clone())
        .expect("Failed to add page");

    controller
        .set_current_page("basic".to_string())
        .await
        .expect("Failed to set page");

    time::sleep(time::Duration::from_secs(1000)).await
}
////// src/plugin.rs //////

use std::io::Result;

use raqote::DrawTarget;

use crate::constants::{KeyLocation, Screen, KEY_SIZE};

#[macro_export]
macro_rules! export_plugin 
{
    ($plugin_id:literal, $register:expr) => 
    {
        #[doc(hidden)]
        #[no_mangle]
        pub static plugin_declaration: $crate::PluginDeclaration = $crate::PluginDeclaration {
            rustc_version: "0.0", // $crate::RUSTC_VERSION,
            core_version: "0.0", // $crate::CORE_VERSION,
            plugin_id: $plugin_id,
            register: $register,
        };
    };
}

pub struct LDPluginRequirement 
{
    exclusive: bool,
}

pub enum PluginType 
{
    Button,
}

#[derive(Debug, Clone)]
pub struct PluginScreenContext 
{
    device_event_emitter: crate::loupedeck::ExternalDeviceEventEmitter,
    position: Screen,
    key_id: KeyLocation,
}

impl PluginScreenContext 
{
    pub(crate) fn new
    (
        device_event_emitter: crate::loupedeck::ExternalDeviceEventEmitter,
        position: Screen,
        key_id: KeyLocation,
    ) -> Self 
    {
        println!("PluginScreenContext::new {:?}", key_id);
        Self 
        {
            position,
            key_id,
            device_event_emitter,
        }
    }

    pub async fn draw_target(&self, target: DrawTarget) -> Result<()> 
    {
        let x: u16 = KEY_SIZE * (self.key_id.x as u16);
        let y: u16 = KEY_SIZE * (self.key_id.y as u16);

        self.device_event_emitter
            .draw_target(self.position.clone(), x, y, KEY_SIZE, KEY_SIZE, target)
            .await;

        Ok(())
    }

    pub async fn draw_rgb565(&self, data: Vec<u8>) -> Result<()> 
    {
        let x: u16 = KEY_SIZE * (self.key_id.x as u16);
        let y: u16 = KEY_SIZE * (self.key_id.y as u16);

        println!("draw_rgb565({:?},{x},{y},{KEY_SIZE},{KEY_SIZE},{data:?})", self.position.clone());
        self.device_event_emitter
            .draw_rgb565(self.position.clone(), x, y, KEY_SIZE, KEY_SIZE, data)
            .await;

        Ok(())
    }
}

pub trait ScreenPlugin 
{
    fn on_button_press(&self, position: crate::constants::ButtonPressEvent) -> Result<()>;
}

#[derive(Debug, Clone, PartialEq)]
pub struct ScreenPluginOptions 
{
    pub exclusive: bool,
}

pub type ScreenPluginFactory = fn(ctx: PluginScreenContext) -> Box<dyn ScreenPlugin>;

pub trait PluginRegistrar 
{
    fn register_screen(
        &mut self,
        name: &str,
        options: ScreenPluginOptions,
        create: ScreenPluginFactory,
    ) -> Result<()>;
}

#[derive(Copy, Clone)]
pub struct PluginDeclaration 
{
    pub rustc_version: &'static str,
    pub core_version: &'static str,
    pub plugin_id: &'static str,
    pub register: unsafe extern "C" fn(&mut dyn PluginRegistrar),
}
////// end of source code //////

Curruently, when I run it I get "STATUS_ACCESS_VIOLATION". Help me identify where this is coming from.