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:
| M | Cargo.toml | | | 1 | + |
| M | rust-toolchain.toml | | | 9 | ++++++++- |
| A | src/kiss.rs | | | 170 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/main.rs | | | 376 | ++++++++++++++++++------------------------------------------------------------- |
| A | src/rx.rs | | | 32 | ++++++++++++++++++++++++++++++++ |
| A | src/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();
+ }
+ }
+ }
+ }
+ }
+}