arduino-tnc

git clone https://git.bradleygannon.com/arduino-tnc.git
Log | Files | Refs

commit 76a7d242c117b572f995d520d46e6c0ab0dfe2ae
parent 8926e6cef64fb8ccee8766ebfa878e049316fc3f
Author: Bradley Gannon <bradley@bradleygannon.com>
Date:   Mon, 25 May 2026 17:10:04 -0400

Split main.rs into modules and improve KISS logic

Diffstat:
MCargo.toml | 1+
Mrust-toolchain.toml | 9++++++++-
Asrc/kiss.rs | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.rs | 376++++++++++++++++++-------------------------------------------------------------
Asrc/rx.rs | 32++++++++++++++++++++++++++++++++
Asrc/tx.rs | 238+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 533 insertions(+), 293 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Bradley Gannon <bradley@bradleygannon.com>"] edition = "2021" license = "MIT OR Apache-2.0" + [[bin]] name = "arduino-tnc" test = false diff --git a/rust-toolchain.toml b/rust-toolchain.toml @@ -1,4 +1,11 @@ [toolchain] channel = "nightly-2025-04-27" -components = ["rust-src"] +components = [ + "cargo", + "clippy", + "rust-analyzer", + "rust-src", + "rustc", + "rustfmt", +] profile = "minimal" diff --git a/src/kiss.rs b/src/kiss.rs @@ -0,0 +1,170 @@ +pub(crate) enum KissAction { + PushTx(u8), + ClearTx, + SendTx, + _SetTxDelay { tx_delay_10ms: u8 }, + _SetPersistence { persistence: u8 }, + _SetSlotTime { slot_time_10ms: u8 }, + _SetTxTail { tx_tail_10ms: u8 }, + _SetFullDuplex { full_duplex: bool }, + _SetHardware { argument: u8 }, + _Return, +} + +enum KissCommand { + DataFrame, + TxDelay, + Persistence, + SlotTime, + TxTail, + FullDuplex, + SetHardware, + Return, + #[allow(dead_code)] + Invalid(u8), +} + +enum KissPort { + Port0, + Port1, + Port2, + Port3, + Port4, + Port5, + Port6, + Port7, + Port8, + Port9, + Port10, + Port11, + Port12, + Port13, + Port14, + Port15, +} + +struct KissHostTypeIndicator { + _function: KissCommand, + _port: KissPort, +} + +impl From<u8> for KissHostTypeIndicator { + fn from(value: u8) -> Self { + let function_number = (value & 0b1111_0000) >> 4; + let function = match function_number { + 0 => KissCommand::DataFrame, + 1 => KissCommand::TxDelay, + 2 => KissCommand::Persistence, + 3 => KissCommand::SlotTime, + 4 => KissCommand::TxTail, + 5 => KissCommand::FullDuplex, + 6 => KissCommand::SetHardware, + 15 => KissCommand::Return, + f => KissCommand::Invalid(f), + }; + let port_index = value & 0b0000_1111; + let port = match port_index { + 0 => KissPort::Port0, + 1 => KissPort::Port1, + 2 => KissPort::Port2, + 3 => KissPort::Port3, + 4 => KissPort::Port4, + 5 => KissPort::Port5, + 6 => KissPort::Port6, + 7 => KissPort::Port7, + 8 => KissPort::Port8, + 9 => KissPort::Port9, + 10 => KissPort::Port10, + 11 => KissPort::Port11, + 12 => KissPort::Port12, + 13 => KissPort::Port13, + 14 => KissPort::Port14, + 15 => KissPort::Port15, + p => unreachable!("Expected port index in range [0, 15] but got {p}"), + }; + KissHostTypeIndicator { + _function: function, + _port: port, + } + } +} + +pub(crate) enum KissTncState { + Idle, + Fend, + TypeIndicator, + Data, + Fesc, +} + +impl KissTncState { + const FEND: u8 = 0xc0; + const FESC: u8 = 0xdb; + const TFEND: u8 = 0xdc; + const TFESC: u8 = 0xdd; + + pub(crate) fn update(&mut self, byte: u8) -> Option<KissAction> { + match (&self, byte) { + // FEND after data means the frame is complete and ready to send + (KissTncState::Data, Self::FEND) => { + *self = KissTncState::Fend; + Some(KissAction::SendTx) + } + + // FEND anywhere else means the frame is incomplete and should not be sent, but a new + // frame is beginning + (_, Self::FEND) => { + *self = KissTncState::Fend; + Some(KissAction::ClearTx) + } + + // Idle state waits for FEND + (KissTncState::Idle, _) => { + *self = KissTncState::Idle; + None + } + + // Type indicator follows FEND + (KissTncState::Fend, _) => { + *self = KissTncState::TypeIndicator; + None + } + + // FESC can follow frame type indicator or data + (KissTncState::TypeIndicator, Self::FESC) => { + *self = KissTncState::Fesc; + None + } + (KissTncState::Data, Self::FESC) => { + *self = KissTncState::Fesc; + None + } + + // Data follows type indicator + (KissTncState::TypeIndicator, _) => { + *self = KissTncState::Data; + None + } + + // Handle escaped bytes + (KissTncState::Fesc, Self::TFEND) => { + *self = KissTncState::Data; + Some(KissAction::PushTx(Self::FEND)) + } + (KissTncState::Fesc, Self::TFESC) => { + *self = KissTncState::Data; + Some(KissAction::PushTx(Self::FESC)) + } + (KissTncState::Fesc, b) => { + *self = KissTncState::Data; + Some(KissAction::PushTx(b)) + } + + // Data follows data + (KissTncState::Data, b) => { + *self = KissTncState::Data; + Some(KissAction::PushTx(b)) + } + } + } +} diff --git a/src/main.rs b/src/main.rs @@ -2,317 +2,106 @@ #![no_main] #![feature(abi_avr_interrupt)] +mod kiss; +mod rx; +mod tx; + use core::cell::RefCell; use core::hint::spin_loop; -use arduino_hal::hal::usart::Event; -use arduino_hal::prelude::*; -use arduino_hal::{ - hal::port::{PD0, PD1, PD2, PD3, PD4, PD5, PD6}, - pac::usart0::RegisterBlock, - port::{ - mode::{Input, Output}, - Pin, - }, - Usart, -}; -use avr_device::{ - generic::Periph, - interrupt::{self, Mutex}, -}; -use crc::{Crc, CRC_16_IBM_SDLC}; -use embedded_hal::digital::OutputPin; -use heapless::Vec; +use arduino_hal::clock::MHz16; +use arduino_hal::hal::usart::{Event, Usart0}; +use arduino_hal::Adc; +use avr_device::interrupt::{self, Mutex}; +use kiss::{KissAction, KissTncState}; use panic_halt as _; - -static SINE_TABLE: [u8; 32] = [ - 16, 19, 22, 24, 27, 29, 30, 31, 31, 31, 30, 29, 27, 24, 22, 19, 16, 12, 9, 7, 4, 2, 1, 0, 0, 0, - 1, 2, 4, 7, 9, 12, -]; - -type Usart0 = Usart<Periph<RegisterBlock, 192>, Pin<Input, PD0>, Pin<Output, PD1>>; - -static SERIAL: Mutex<RefCell<Option<Usart0>>> = Mutex::new(RefCell::new(None)); -static MESSAGE: Mutex<RefCell<Vec<u8, 512>>> = Mutex::new(RefCell::new(Vec::new())); -static KISS: Mutex<RefCell<KissState>> = Mutex::new(RefCell::new(KissState::Idle)); -static SENDER: Mutex<RefCell<Sender>> = Mutex::new(RefCell::new(Sender { - counter: 0, - increment: 0, - state: SenderState::Off, - dac_0: None, - dac_1: None, - dac_2: None, - dac_3: None, - dac_4: None, -})); - -const MARK_INCREMENT: u16 = 2100; -const SPACE_INCREMENT: u16 = 3838; - -enum KissState { - Idle, - Fend { send: bool }, - Command, - Data(u8), - Fesc, -} - -impl KissState { - const FEND: u8 = 0xc0; - const FESC: u8 = 0xdb; - const TFEND: u8 = 0xdc; - const TFESC: u8 = 0xdd; - const COMMAND_DATA: u8 = 0x00; - - fn update(&mut self, byte: u8) { - *self = match (&self, byte) { - // FEND after data means the frame is complete and ready to send - (KissState::Data(_), Self::FEND) => KissState::Fend { send: true }, - - // FEND anywhere else means the frame is incomplete and should not be sent, but a new - // frame is beginning - (_, Self::FEND) => KissState::Fend { send: false }, - - // Idle state waits for FEND - (KissState::Idle, _) => KissState::Idle, - - // Command byte follows FEND - (KissState::Fend { .. }, Self::COMMAND_DATA) => KissState::Command, - (KissState::Fend { .. }, _) => KissState::Command, - - // FESC can follow command or data - (KissState::Command, Self::FESC) => KissState::Fesc, - (KissState::Data(_), Self::FESC) => KissState::Fesc, - - // Data follows command byte - (KissState::Command, b) => KissState::Data(b), - - // Handle escaped bytes - (KissState::Fesc, Self::TFEND) => KissState::Data(Self::FEND), - (KissState::Fesc, Self::TFESC) => KissState::Data(Self::FESC), - (KissState::Fesc, b) => KissState::Data(b), - - // Data follows data - (KissState::Data(_), b) => KissState::Data(b), - } - } -} - -enum SenderState { - Off, - StartFlag { - bit_index: u8, - count: usize, - }, - Payload { - byte_index: usize, - bit_index: u8, - ones_count: usize, - stuff_bit: bool, - }, - EndFlag { - bit_index: u8, - count: usize, - }, -} - -struct Sender { - counter: u16, - increment: u16, - state: SenderState, - dac_0: Option<Pin<Output, PD2>>, - dac_1: Option<Pin<Output, PD3>>, - dac_2: Option<Pin<Output, PD4>>, - dac_3: Option<Pin<Output, PD5>>, - dac_4: Option<Pin<Output, PD6>>, +use rx::Receiver; +use tx::Transmitter; + +struct Tnc { + state: TncState, + serial: Option<Usart0<MHz16>>, + receiver: Receiver, + transmitter: Transmitter, + kiss_state: KissTncState, } -impl Sender { - const START_FLAG_BYTES: usize = 45; - const END_FLAG_BYTES: usize = 15; - - fn u8_index_msb(byte: u8, bit_index: u8) -> bool { - (byte >> (7 - bit_index)) & 1 != 0 - } - fn sample_update(&mut self) { - self.counter = self.counter.wrapping_add(self.increment); - let sine_index = ((self.counter >> 11) as usize).clamp(0, SINE_TABLE.len()); - if let Some(sine_value) = SINE_TABLE.get(sine_index) { - if let Some(ref mut dac_0) = self.dac_0 { - let _ = dac_0.set_state((sine_value & 0b10000 != 0).into()); - }; - if let Some(ref mut dac_1) = self.dac_1 { - let _ = dac_1.set_state((sine_value & 0b01000 != 0).into()); - }; - if let Some(ref mut dac_2) = self.dac_2 { - let _ = dac_2.set_state((sine_value & 0b00100 != 0).into()); - }; - if let Some(ref mut dac_3) = self.dac_3 { - let _ = dac_3.set_state((sine_value & 0b00010 != 0).into()); - }; - if let Some(ref mut dac_4) = self.dac_4 { - let _ = dac_4.set_state((sine_value & 0b00001 != 0).into()); - }; +impl Tnc { + const fn default() -> Tnc { + Tnc { + state: TncState::Receiving, + serial: None, + receiver: Receiver::default(), + transmitter: Transmitter::default(), + kiss_state: KissTncState::Idle, } } - fn symbol_update(&mut self) { - let new_symbol = match self.state { - SenderState::Off => None, - SenderState::StartFlag { - bit_index, - count: _, - } - | SenderState::EndFlag { - bit_index, - count: _, - } => Some(Self::u8_index_msb(0x7e, bit_index)), - SenderState::Payload { - byte_index, - bit_index, - ref mut ones_count, - ref mut stuff_bit, - } => { - if *stuff_bit { - *stuff_bit = false; - Some(false) - } else { - let maybe_byte = - interrupt::free(|cs| MESSAGE.borrow(cs).borrow().get(byte_index).cloned()); - if let Some(byte) = maybe_byte { - let bit = Self::u8_index_msb(byte, bit_index); - if bit { - *ones_count += 1; - } else { - *ones_count = 0; - } - Some(bit) - } else { - Some(false) - } - } - } - }; - self.increment = match new_symbol { - Some(true) => self.increment, - Some(false) => { - if self.increment == MARK_INCREMENT { - SPACE_INCREMENT - } else { - MARK_INCREMENT - } - } - None => 0, - }; - match self.state { - SenderState::Off => (), - SenderState::StartFlag { - ref mut bit_index, - ref mut count, - } => { - *bit_index += 1; - if *bit_index >= 8 { - *count += 1; - *bit_index = 0; - if *count > Self::START_FLAG_BYTES { - self.state = SenderState::Payload { - byte_index: 0, - bit_index: 0, - ones_count: 0, - stuff_bit: false, - }; - } - } - } - SenderState::Payload { - ref mut byte_index, - ref mut bit_index, - ref mut ones_count, - ref mut stuff_bit, - } => { - if *ones_count >= 5 { - *stuff_bit = true; - *ones_count = 0; - } else { - *bit_index += 1; - if *bit_index >= 8 { - *byte_index += 1; - *bit_index = 0; - let message_length = - interrupt::free(|cs| MESSAGE.borrow(cs).borrow().len()); - if *byte_index >= message_length { - self.state = SenderState::EndFlag { - bit_index: 0, - count: 0, - }; - } - } + pub(crate) fn handle_usart_byte(&mut self, byte: u8) { + if let Some(action) = self.kiss_state.update(byte) { + match action { + KissAction::PushTx(b) => { + let _ = self.transmitter.push_message_byte(b); } - } - SenderState::EndFlag { - ref mut bit_index, - ref mut count, - } => { - *bit_index += 1; - if *bit_index >= 8 { - *count += 1; - *bit_index = 0; - if *count > Self::END_FLAG_BYTES { - self.state = SenderState::Off; - interrupt::free(|cs| MESSAGE.borrow(cs).borrow_mut().clear()); + KissAction::ClearTx => self.transmitter.clear_message(), + KissAction::SendTx => { + if self.transmitter.has_pending_bytes() { + self.state = TncState::Transmitting; + self.transmitter.start(); } } + KissAction::_SetTxDelay { .. } => todo!(), + KissAction::_SetPersistence { .. } => todo!(), + KissAction::_SetSlotTime { .. } => todo!(), + KissAction::_SetTxTail { .. } => todo!(), + KissAction::_SetFullDuplex { .. } => todo!(), + KissAction::_SetHardware { .. } => todo!(), + KissAction::_Return => todo!(), } - } + }; } } +enum TncState { + Receiving, + Transmitting, +} + +static TNC: Mutex<RefCell<Tnc>> = Mutex::new(RefCell::new(Tnc::default())); + #[avr_device::interrupt(atmega328p)] fn TIMER0_COMPA() { - interrupt::free(|cs| SENDER.borrow(cs).borrow_mut().sample_update()); + interrupt::free(|cs| { + let mut tnc = TNC.borrow(cs).borrow_mut(); + match tnc.state { + TncState::Receiving => tnc.receiver.sample_update(), + TncState::Transmitting => { + tnc.transmitter.sample_update(); + if tnc.transmitter.is_done() { + tnc.state = TncState::Receiving; + } + } + } + }); } #[avr_device::interrupt(atmega328p)] fn TIMER1_COMPA() { - interrupt::free(|cs| SENDER.borrow(cs).borrow_mut().symbol_update()); + interrupt::free(|cs| { + let mut tnc = TNC.borrow(cs).borrow_mut(); + match tnc.state { + TncState::Receiving => tnc.receiver.symbol_update(), + TncState::Transmitting => tnc.transmitter.symbol_update(), + } + }); } #[avr_device::interrupt(atmega328p)] fn USART_RX() { interrupt::free(|cs| { - if let Some(ref mut serial) = SERIAL.borrow(cs).borrow_mut().as_mut() { - if let Ok(b) = serial.read() { - let mut kiss_state = KISS.borrow(cs).borrow_mut(); - kiss_state.update(b); - match *kiss_state { - KissState::Fend { send } if send => { - let mut sender = SENDER.borrow(cs).borrow_mut(); - let mut message = MESSAGE.borrow(cs).borrow_mut(); - - let crc = Crc::<u16>::new(&CRC_16_IBM_SDLC); - let mut digest = crc.digest(); - digest.update(message.as_slice()); - let crc_bytes = digest.finalize().to_le_bytes(); - - for byte in message.iter_mut() { - *byte = byte.reverse_bits(); - } - let _ = message.push(crc_bytes[0].reverse_bits()); - let _ = message.push(crc_bytes[1].reverse_bits()); - - sender.state = SenderState::StartFlag { - bit_index: 0, - count: 0, - }; - } - KissState::Data(d) => { - let _ = MESSAGE.borrow(cs).borrow_mut().push(d); - } - KissState::Idle - | KissState::Command - | KissState::Fend { .. } - | KissState::Fesc => (), - }; - } + let mut tnc = TNC.borrow(cs).borrow_mut(); + if let Some(ref mut serial) = tnc.serial { + let byte = serial.read_byte(); + tnc.handle_usart_byte(byte); } }); } @@ -324,15 +113,18 @@ fn main() -> ! { let mut serial = arduino_hal::default_serial!(dp, pins, 57600); serial.listen(Event::RxComplete); - interrupt::free(|cs| *SERIAL.borrow(cs).borrow_mut() = Some(serial)); interrupt::free(|cs| { - let mut sender = SENDER.borrow(cs).borrow_mut(); - sender.dac_0 = Some(pins.d2.into_output()); - sender.dac_1 = Some(pins.d3.into_output()); - sender.dac_2 = Some(pins.d4.into_output()); - sender.dac_3 = Some(pins.d5.into_output()); - sender.dac_4 = Some(pins.d6.into_output()); + let mut tnc = TNC.borrow(cs).borrow_mut(); + tnc.receiver + .initialize(Adc::new(dp.ADC, Default::default()), pins.a0); + tnc.transmitter.initialize( + pins.d2.into_output(), + pins.d3.into_output(), + pins.d4.into_output(), + pins.d5.into_output(), + pins.d6.into_output(), + ); }); let dac_timer = dp.TC0; diff --git a/src/rx.rs b/src/rx.rs @@ -0,0 +1,32 @@ +use arduino_hal::{ + hal::port::PC0, + port::{ + mode::{Analog, Floating, Input}, + Pin, + }, + Adc, +}; + +pub(crate) struct Receiver { + adc: Option<Adc>, + input: Option<Pin<Analog, PC0>>, +} + +impl Receiver { + pub(crate) const fn default() -> Receiver { + Receiver { + adc: None, + input: None, + } + } + pub(crate) fn initialize(&mut self, mut adc: Adc, input: Pin<Input<Floating>, PC0>) { + self.input = Some(input.into_analog_input(&mut adc)); + self.adc = Some(adc); + } + pub(crate) fn sample_update(&mut self) { + if let (Some(ref input), Some(ref mut adc)) = (&self.input, &mut self.adc) { + let _new_sample = input.analog_read(adc); + } + } + pub(crate) fn symbol_update(&mut self) {} +} diff --git a/src/tx.rs b/src/tx.rs @@ -0,0 +1,238 @@ +use arduino_hal::{ + hal::port::{PD2, PD3, PD4, PD5, PD6}, + port::{mode::Output, Pin}, +}; +use crc::{Crc, CRC_16_IBM_SDLC}; +use embedded_hal::digital::OutputPin; +use heapless::Vec; + +static SINE_TABLE: [u8; 32] = [ + 16, 19, 22, 24, 27, 29, 30, 31, 31, 31, 30, 29, 27, 24, 22, 19, 16, 12, 9, 7, 4, 2, 1, 0, 0, 0, + 1, 2, 4, 7, 9, 12, +]; + +pub(crate) struct Transmitter { + counter: u16, + increment: u16, + state: TransmitterState, + dac_0: Option<Pin<Output, PD2>>, + dac_1: Option<Pin<Output, PD3>>, + dac_2: Option<Pin<Output, PD4>>, + dac_3: Option<Pin<Output, PD5>>, + dac_4: Option<Pin<Output, PD6>>, + message: Vec<u8, 256>, +} + +#[derive(PartialEq)] +pub(crate) enum TransmitterState { + Off, + StartFlag { + bit_index: u8, + count: usize, + }, + Payload { + byte_index: usize, + bit_index: u8, + ones_count: usize, + stuff_bit: bool, + }, + EndFlag { + bit_index: u8, + count: usize, + }, +} + +impl Transmitter { + const START_FLAG_BYTES: usize = 45; + const END_FLAG_BYTES: usize = 15; + + const MARK_INCREMENT: u16 = 2100; + const SPACE_INCREMENT: u16 = 3838; + + pub(crate) const fn default() -> Transmitter { + Transmitter { + counter: 0, + increment: 0, + state: TransmitterState::Off, + dac_0: None, + dac_1: None, + dac_2: None, + dac_3: None, + dac_4: None, + message: Vec::new(), + } + } + pub(crate) fn initialize( + &mut self, + dac_0: Pin<Output, PD2>, + dac_1: Pin<Output, PD3>, + dac_2: Pin<Output, PD4>, + dac_3: Pin<Output, PD5>, + dac_4: Pin<Output, PD6>, + ) { + self.dac_0 = Some(dac_0); + self.dac_1 = Some(dac_1); + self.dac_2 = Some(dac_2); + self.dac_3 = Some(dac_3); + self.dac_4 = Some(dac_4); + } + fn u8_index_msb(byte: u8, bit_index: u8) -> bool { + (byte >> (7 - bit_index)) & 1 != 0 + } + pub(crate) fn start(&mut self) { + let crc = Crc::<u16>::new(&CRC_16_IBM_SDLC); + let mut digest = crc.digest(); + digest.update(self.message.as_slice()); + let crc_bytes = digest.finalize().to_le_bytes(); + + for byte in self.message.iter_mut() { + *byte = byte.reverse_bits(); + } + let _ = self.message.push(crc_bytes[0].reverse_bits()); + let _ = self.message.push(crc_bytes[1].reverse_bits()); + + self.state = TransmitterState::StartFlag { + bit_index: 0, + count: 0, + }; + } + pub(crate) fn push_message_byte(&mut self, byte: u8) -> Result<(), u8> { + self.message.push(byte) + } + pub(crate) fn clear_message(&mut self) { + self.message.clear(); + } + pub(crate) fn has_pending_bytes(&self) -> bool { + !self.message.is_empty() + } + pub(crate) fn is_done(&self) -> bool { + self.state == TransmitterState::Off + } + pub(crate) fn sample_update(&mut self) { + self.counter = self.counter.wrapping_add(self.increment); + let sine_index = ((self.counter >> 11) as usize).clamp(0, SINE_TABLE.len()); + if let Some(sine_value) = SINE_TABLE.get(sine_index) { + if let Some(ref mut dac_0) = self.dac_0 { + let _ = dac_0.set_state((sine_value & 0b10000 != 0).into()); + }; + if let Some(ref mut dac_1) = self.dac_1 { + let _ = dac_1.set_state((sine_value & 0b01000 != 0).into()); + }; + if let Some(ref mut dac_2) = self.dac_2 { + let _ = dac_2.set_state((sine_value & 0b00100 != 0).into()); + }; + if let Some(ref mut dac_3) = self.dac_3 { + let _ = dac_3.set_state((sine_value & 0b00010 != 0).into()); + }; + if let Some(ref mut dac_4) = self.dac_4 { + let _ = dac_4.set_state((sine_value & 0b00001 != 0).into()); + }; + } + } + pub(crate) fn symbol_update(&mut self) { + let new_symbol = match self.state { + TransmitterState::Off => None, + TransmitterState::StartFlag { + bit_index, + count: _, + } + | TransmitterState::EndFlag { + bit_index, + count: _, + } => Some(Self::u8_index_msb(0x7e, bit_index)), + TransmitterState::Payload { + byte_index, + bit_index, + ref mut ones_count, + ref mut stuff_bit, + } => { + if *stuff_bit { + *stuff_bit = false; + Some(false) + } else { + let maybe_byte = self.message.get(byte_index); + if let Some(byte) = maybe_byte { + let bit = Self::u8_index_msb(*byte, bit_index); + if bit { + *ones_count += 1; + } else { + *ones_count = 0; + } + Some(bit) + } else { + Some(false) + } + } + } + }; + self.increment = match new_symbol { + Some(true) => self.increment, + Some(false) => { + if self.increment == Self::MARK_INCREMENT { + Self::SPACE_INCREMENT + } else { + Self::MARK_INCREMENT + } + } + None => 0, + }; + match self.state { + TransmitterState::Off => (), + TransmitterState::StartFlag { + ref mut bit_index, + ref mut count, + } => { + *bit_index += 1; + if *bit_index >= 8 { + *count += 1; + *bit_index = 0; + if *count > Self::START_FLAG_BYTES { + self.state = TransmitterState::Payload { + byte_index: 0, + bit_index: 0, + ones_count: 0, + stuff_bit: false, + }; + } + } + } + TransmitterState::Payload { + ref mut byte_index, + ref mut bit_index, + ref mut ones_count, + ref mut stuff_bit, + } => { + if *ones_count >= 5 { + *stuff_bit = true; + *ones_count = 0; + } else { + *bit_index += 1; + if *bit_index >= 8 { + *byte_index += 1; + *bit_index = 0; + if *byte_index >= self.message.len() { + self.state = TransmitterState::EndFlag { + bit_index: 0, + count: 0, + }; + } + } + } + } + TransmitterState::EndFlag { + ref mut bit_index, + ref mut count, + } => { + *bit_index += 1; + if *bit_index >= 8 { + *count += 1; + *bit_index = 0; + if *count > Self::END_FLAG_BYTES { + self.state = TransmitterState::Off; + self.message.clear(); + } + } + } + } + } +}