negicon-protocol 1.0.0

Definitions and utility functions for the Negicon v3 communication protocol
Documentation
use defmt::{bitflags, Format};
pub use ux::u7;

use crate::{make_u32, util::make_u16};
use core::ops::Shr;
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct NegiconFrame {
    pub origin: u16,
    pub destination: u16,
    pub message_type: NegiconMessageType,
    reserved: u8,
    pub message_data: [u8; 8],
}

#[derive(PartialEq, Clone, Copy, Debug)]
pub enum NegiconMessageType {
    Nop = 0,
    Ping = 1,
    ModuleEventShort = 2,
    ModuleEventLong = 3,
    ModuleEventDouble = 4,
}

impl NegiconMessageType {
    fn from_u8(value: u8) -> Result<Self, InvalidMessage> {
        match value {
            0 => Ok(NegiconMessageType::Nop),
            1 => Ok(NegiconMessageType::Ping),
            2 => Ok(NegiconMessageType::ModuleEventLong),
            3 => Ok(NegiconMessageType::ModuleEventDouble),
            _ => Err(InvalidMessage::InvalidMessageType),
        }
    }
}

#[derive(PartialEq, Clone, Copy, Debug)]
pub enum NegiconModuleType {
    Invalid = 0,
    Router,
    Buttons,
    SmoothEncoder,
    DetentEncoder,
    ClickWheel,
    DigitalPaddle,
    AnalogPaddle,
}

impl NegiconModuleType {
    fn from_u8(value: u8) -> Self {
        match value {
            0 => NegiconModuleType::Router,
            1 => NegiconModuleType::Buttons,
            2 => NegiconModuleType::SmoothEncoder,
            3 => NegiconModuleType::DetentEncoder,
            4 => NegiconModuleType::ClickWheel,
            5 => NegiconModuleType::DigitalPaddle,
            6 => NegiconModuleType::AnalogPaddle,
            _ => NegiconModuleType::Invalid,
        }
    }
}

#[derive(PartialEq, Clone, Copy, Debug, Format)]
pub enum InvalidMessage {
    CrcError,
    InvalidMessageType,
}

const CRC: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_3740);

fn crc(data: &[u8]) -> u16 {
    CRC.checksum(data)
}

fn set_crc(data: &mut [u8]) {
    let crc = crc(&data[2..]);
    data[1] = crc as u8;
    data[0] = (crc >> 8) as u8;
}
fn verify_crc(data: &[u8]) -> Result<(), InvalidMessage> {
    let expected = make_u16(data[0], data[1]);
    let actual = crc(&data[2..]);
    if expected == actual {
        Ok(())
    } else {
        Err(InvalidMessage::CrcError)
    }
}

#[derive(PartialEq, Clone, Copy, Debug)]
pub struct NegiconPingMessage {
    pub sequence: u8,
    pub ttl: u8,
    pub module_type: NegiconModuleType,
    pub fw_version: u16,
}

impl NegiconPingMessage {
    pub fn serialize(&self) -> [u8; 8] {
        [
            self.sequence,
            self.ttl,
            self.module_type as u8,
            self.fw_version.shr(8) as u8,
            self.fw_version as u8,
            0u8,
            0u8,
            0u8,
        ]
    }

    pub fn deserialize(data: &[u8; 8]) -> Result<Self, InvalidMessage> {
        Ok(NegiconPingMessage {
            sequence: data[0],
            ttl: data[1],
            module_type: NegiconModuleType::from_u8(data[2]),
            fw_version: make_u16(data[3], data[4]),
        })
    }
}

#[derive(PartialEq, Clone, Copy, Debug)]
pub struct NegiconModuleEventLongMessage {
    pub sequence: u8,
    pub sub_id: u8,
    pub event_data: [u8; 6],
}

impl NegiconModuleEventLongMessage {
    pub fn serialize(&self) -> [u8; 8] {
        [
            self.sequence,
            self.sub_id,
            self.event_data[0],
            self.event_data[1],
            self.event_data[2],
            self.event_data[3],
            self.event_data[4],
            self.event_data[5],
        ]
    }

    pub fn deserialize(data: &[u8; 8]) -> Result<Self, InvalidMessage> {
        Ok(NegiconModuleEventLongMessage {
            sequence: data[0],
            sub_id: data[1],
            event_data: [data[2], data[3], data[4], data[5], data[6], data[7]],
        })
    }
}

#[derive(PartialEq, Clone, Copy, Debug)]
pub struct NegiconModuleEventShortMessage {
    pub sequence: u8,
    pub sub_id: u8,
    pub value: u16,
}

impl NegiconModuleEventShortMessage {
    pub fn new(sequence: u8, sub_id: u8, value: u16) -> Self {
        Self {
            sequence,
            sub_id,
            value,
        }
    }

    pub fn serialize(&self) -> [u8; 4] {
        [
            self.sequence,
            self.sub_id,
            self.value as u8,
            (self.value >> 8) as u8,
        ]
    }

    pub fn deserialize(data: &[u8; 4]) -> Self {
        NegiconModuleEventShortMessage {
            sequence: data[0],
            sub_id: data[1],
            value: make_u16(data[3], data[2]),
        }
    }

    pub fn deserialize_double(data: &[u8; 8]) -> (Self, Self) {
        let first =
            NegiconModuleEventShortMessage::deserialize(&[data[0], data[1], data[2], data[3]]);
        let second =
            NegiconModuleEventShortMessage::deserialize(&[data[4], data[5], data[6], data[7]]);
        (first, second)
    }

    pub fn serialize_double(first: &Self, second: &Self) -> [u8; 8] {
        let first_data = first.serialize();
        let second_data = second.serialize();
        [
            first_data[0],
            first_data[1],
            first_data[2],
            first_data[3],
            second_data[0],
            second_data[1],
            second_data[2],
            second_data[3],
        ]
    }
}

impl NegiconFrame {
    pub fn new(
        origin: u16,
        destination: u16,
        message_type: NegiconMessageType,
        message_data: [u8; 8],
    ) -> Self {
        NegiconFrame {
            origin,
            destination,
            message_type,
            reserved: 0,
            message_data,
        }
    }

    pub fn ping(id: u16, module_type: NegiconModuleType, sequence: u8, fw_version: u16) -> Self {
        let message = NegiconPingMessage {
            sequence,
            ttl: 8,
            module_type,
            fw_version,
        };
        let message_data = message.serialize();
        NegiconFrame::new(id, 0, NegiconMessageType::Ping, message_data)
    }

    pub fn nop() -> Self {
        NegiconFrame {
            origin: 0,
            destination: 0,
            message_type: NegiconMessageType::Nop,
            reserved: 0,
            message_data: [0; 8],
        }
    }

    pub fn serialize(&self) -> [u8; 16] {
        let mut data = [
            0u8,
            0u8,
            self.origin as u8,
            (self.origin >> 8) as u8,
            self.destination as u8,
            (self.destination >> 8) as u8,
            self.message_type as u8,
            0u8,
            self.message_data[0],
            self.message_data[1],
            self.message_data[2],
            self.message_data[3],
            self.message_data[4],
            self.message_data[5],
            self.message_data[6],
            self.message_data[7],
        ];
        set_crc(&mut data);
        data
    }

    pub fn is_ping(&self) -> bool {
        self.message_type == NegiconMessageType::Ping
    }

    pub fn is_nop(&self) -> bool {
        self.message_type == NegiconMessageType::Nop
    }

    pub fn serialize_u16(&self) -> [u16; 4] {
        let bytes = self.serialize();
        [
            make_u16(bytes[0], bytes[1]),
            make_u16(bytes[2], bytes[3]),
            make_u16(bytes[4], bytes[5]),
            make_u16(bytes[6], bytes[7]),
        ]
    }

    pub fn serialize_u32(&self) -> [u32; 2] {
        let bytes = self.serialize();
        [
            make_u32(bytes[0], bytes[1], bytes[2], bytes[3]),
            make_u32(bytes[4], bytes[5], bytes[6], bytes[7]),
        ]
    }

    pub fn deserialize(data: &[u8; 16]) -> Result<Self, InvalidMessage> {
        verify_crc(data)?;
        Ok(NegiconFrame {
            origin: make_u16(data[2], data[3]),
            destination: make_u16(data[4], data[5]),
            message_type: NegiconMessageType::from_u8(data[6])?,
            reserved: data[7],
            message_data: [
                data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
            ],
        })
    }
}