manchester_code/lib.rs
1//! # Manchester Decoder
2//!
3//! Features:
4//!
5//! * Decode monotonically sampled data stream that is Manchester modulated
6//! like it is used in Philips RC5
7//!
8//! <https://techdocs.altium.com/display/FPGA/Philips+RC5+Infrared+Transmission+Protocol>
9//!
10//! * High/low IN-Activitity configuration
11//! * Automatic start and end of datagram detection
12//! * Sampling needs to be 3 times the length of half a bit. (i.e. only a
13//! single periodic timer is needed), for a infrared receiver
14//! 889 µs halfbit period => the periodic timer should run all 297 µs.
15//!
16//! # Manchester Modulation
17//!
18//! <https://en.wikipedia.org/wiki/Manchester_code>
19//!
20//!
21//! * A datagram starts after a pause period longer than the time of two bits
22//! * A datagram is finished if their is no edge anymore longer than a bit
23//! period and all subsequent samples are at inactive level
24//! * all other situations are treated as errors and are rejected
25//! * Bit order of a datagram:
26//! * The first bit received is the most significant bit (MSB) and
27//! the last bit
28//!
29//!
30//! ## Receiving Algorithm Details
31//!
32//! A Periodic sampling is used.
33//!
34//! * Three samples per half bit period, will do. It gives a one third (of half period)
35//! tolerance. And allows for one third (of half period) where the signal is
36//! expected to be stable.
37//!
38//! Thus, the Philips half bit time can vary 889 µs +/- 296 µs = [595; 1175] µs
39//!
40//! * For every bit there is an edge at the transition from first half bit to
41//! second half bit. This is period is used to synchronize bit value measurement
42//!
43//! * The first bit value must be pre-known, because it determines where the
44//! synchronization edges are to be expected:
45//!
46//! |
47//! # Example
48//!
49//! The lib runs in no_std environments
50//!
51//! ```ignore
52//! #![deny(warnings)]
53//! #![deny(unsafe_code)]
54//! #![no_main]
55//! #![no_std]
56//!
57//! use nucleo_stm32g071rb as board; // it also includes mem, defmt
58//!
59//! use board::hal::prelude::*;
60//! use board::hal::stm32;
61//! use board::hal::nb::block;
62//!
63//! use manchester_code::{ActivityLevel, SyncOnTurningEdge, BitOrder, Decoder};
64//!
65//! #[cortex_m_rt::entry]
66//! fn main() -> ! {
67//! let dp = stm32::Peripherals::take().expect("cannot take peripherals");
68//! let mut rcc = dp.RCC.constrain();
69//!
70//! let gpioa = dp.GPIOA.split(&mut rcc);
71//! let infrared = gpioa.pa8.into_pull_up_input();
72//!
73//! let mut timer = dp.TIM17.timer(&mut rcc);
74//! timer.start(297.us());
75//! let mut receiver = Decoder::new(
76//! ActivityLevel::High,
77//! SyncOnTurningEdge::First,
78//! BitOrder::LittleEndian);
79//! loop {
80//! match receiver.next(infrared.is_high().unwrap()) {
81//! None => (),
82//! Some(t) => if t.length() > 2 {
83//! defmt::println!("Datagram: {:?}", t ); },
84//! };
85//! block!(timer.wait()).unwrap();
86//! }
87//! }
88//! ```
89
90#![no_std]
91// #![deny(warnings)]
92#![deny(unsafe_code)]
93
94use defmt::Format;
95
96use core::iter::Iterator;
97use core::ops::Index;
98
99use embedded_hal::Pwm;
100
101/// BitOrder or endian describes the ordering of bits during transmission
102///
103/// Big endian: MSB is transmitted first; LSB is transmitted last
104/// Little endian: LSB is transmitted first; MSB is transmitted last
105#[derive(Copy, Clone, Debug)]
106pub enum BitOrder {
107 BigEndian,
108 LittleEndian,
109}
110
111/// Representation of a datagram
112///
113/// The total length is limited to 128 bits
114/// The bits of a telegram are internally enumerated from 0 to 127.
115/// A default datagram is expected to be empty (i.e. containing zero bits)
116#[derive(Default, Copy, Clone, Debug)]
117pub struct Datagram {
118 length_in_bit: u8,
119 buffer: u128,
120}
121
122#[derive(Debug)]
123struct Error;
124
125impl Datagram {
126 /// Add a bit to a datagram
127 ///
128 /// The new bit is placed at index zero.
129 /// The index of all previously added bits gets increased by one.
130 ///
131 /// # Arguments
132 ///
133 /// * `bit` - The bit value to record at index 0
134 /// * `bit_order`- The bit order either BigEndian or LittleEndian determines
135 /// if the bit is added at the LSB or MSB position
136 ///
137 /// # Returns
138 ///
139 /// * Error - if the datagram is already filled up to its capacity.
140 /// * () - if the bit was successfully added
141 fn add_bit(&mut self, bit: bool, order: BitOrder) -> Result<(), Error> {
142 if self.length_in_bit == 127 {
143 Err(Error)
144 } else {
145 match order {
146 BitOrder::BigEndian => {
147 self.buffer <<= 1;
148 if bit {
149 self.buffer += 1;
150 };
151 }
152 BitOrder::LittleEndian => {
153 if bit {
154 self.buffer += 1 << self.length_in_bit;
155 }
156 }
157 }
158 self.length_in_bit += 1;
159 Ok(())
160 }
161 }
162
163 pub fn len(&self) -> u8 {
164 self.length_in_bit
165 }
166
167 pub fn is_empty(&self) -> bool {
168 0 == self.length_in_bit
169 }
170
171 /// Extract a data slice from the datagram
172 ///
173 /// # Args
174 ///
175 /// * `min` - start index (included)
176 /// * `max` - max index (not included)
177 ///
178 /// # Returns
179 ///
180 /// the extracted value
181 ///
182 /// # Panics
183 /// if 0 <= min < max <= len() is violated
184 ///
185 /// # Example
186 /// ```rust
187 ///
188 /// use manchester_code::Datagram;
189 ///
190 /// let datagram = Datagram::new("0-111_10101_00001111");
191 /// assert_eq!(0b1111, datagram.extract_data(0, 4));
192 /// assert_eq!(0b1111, datagram.extract_data(0, 8));
193 /// assert_eq!(0b1111, datagram.extract_data(datagram.len()-5, datagram.len()));
194 /// ```
195 pub fn extract_data(&self, min: u8, max: u8) -> u128 {
196 if max > self.length_in_bit {
197 panic!("Max index to big");
198 }
199 if min >= max {
200 panic!("Min index to greater than max index");
201 }
202
203 let mut value = 0_u128;
204 for index in min..max {
205 let mask: u128 = 1 << (max + min - index - 1);
206 let bit = if (mask & self.buffer) == 0 { &0 } else { &1 };
207 value <<= 1;
208 value += bit;
209 }
210 value
211 }
212
213 /// Create a new datagram from "binary" string
214 ///
215 /// # Arguments
216 ///
217 /// * `bit_repr` - Bit representation as string of zeros and ones.
218 /// Arbitrary delimiter signs (for readability) are ignored
219 /// # Example
220 ///
221 /// ```rust
222 /// use manchester_code::Datagram;
223 ///
224 /// let datagram = Datagram::new("0-111_10101_00001111");
225 /// ```
226 pub fn new(bit_repr: &str) -> Self {
227 let mut datagram = Datagram::default();
228 for bit in bit_repr.bytes() {
229 match bit {
230 b'0' => datagram.add_bit(false, BitOrder::BigEndian).unwrap(),
231 b'1' => datagram.add_bit(true, BitOrder::BigEndian).unwrap(),
232 _ => (),
233 }
234 }
235 datagram
236 }
237
238 fn into_big_endian_iter(self) -> DatagramBigEndianIterator {
239 DatagramBigEndianIterator {
240 datagram: self,
241 index: self.len(),
242 }
243 }
244
245 fn into_little_endian_iter(self) -> DatagramLittleEndianIterator {
246 DatagramLittleEndianIterator {
247 datagram: self,
248 index: 0,
249 }
250 }
251}
252
253impl Index<u8> for Datagram {
254 type Output = u128;
255
256 /// Access the n-th element via index
257 ///
258 /// # Panics
259 ///
260 /// * if the index is out of range
261 ///
262 /// # Example
263 ///
264 /// ```rust
265 /// use manchester_code::Datagram;
266 ///
267 /// let datagram = Datagram::new("0-111_10101_00001111");
268 /// assert_eq!(1, datagram[0]);
269 /// assert_eq!(0, datagram[5]);
270 /// ```
271 fn index(&self, index: u8) -> &Self::Output {
272 if index >= self.length_in_bit {
273 panic!("Wrong Index")
274 }
275 let mask: u128 = 1 << index;
276 if mask & self.buffer == 0 {
277 &0
278 } else {
279 &1
280 }
281 }
282}
283
284impl PartialEq for Datagram {
285 fn eq(&self, other: &Self) -> bool {
286 self.buffer == other.buffer && self.length_in_bit == other.length_in_bit
287 }
288}
289
290impl Eq for Datagram {}
291
292impl Format for Datagram {
293 fn format(&self, f: defmt::Formatter) {
294 for index in 0..self.length_in_bit {
295 if 0 == index % 4 {
296 defmt::write!(f, "-");
297 }
298 if 1 == self[self.length_in_bit - 1 - index] {
299 defmt::write!(f, "1");
300 } else {
301 defmt::write!(f, "0");
302 }
303 }
304 }
305}
306
307#[derive(Debug)]
308pub struct DatagramBigEndianIterator {
309 datagram: Datagram,
310 index: u8,
311}
312
313impl Iterator for DatagramBigEndianIterator {
314 type Item = bool;
315 fn next(&mut self) -> Option<Self::Item> {
316 if 0 < self.index {
317 self.index -= 1;
318 Some(1 == self.datagram[self.index])
319 } else {
320 None
321 }
322 }
323}
324
325#[derive(Debug)]
326pub struct DatagramLittleEndianIterator {
327 datagram: Datagram,
328 index: u8,
329}
330
331impl Iterator for DatagramLittleEndianIterator {
332 type Item = bool;
333 fn next(&mut self) -> Option<Self::Item> {
334 if self.datagram.len() > self.index {
335 self.index += 1;
336 Some(1 == self.datagram[self.index - 1])
337 } else {
338 None
339 }
340 }
341}
342
343/// Encodes a datagram to Manchester code
344///
345/// The encoder turns into an iterator.
346/// the encoding happens by calling the iterator.
347///
348/// # Example
349///
350/// ```rust
351/// use manchester_code::{
352/// Datagram,
353/// DatagramBigEndianIterator,
354/// Encoder};
355///
356/// let mut encoder = Encoder::<DatagramBigEndianIterator>::new(Datagram::new("01"));
357/// assert_eq!(Some(true), encoder.next());
358/// assert_eq!(Some(false), encoder.next());
359/// assert_eq!(Some(false), encoder.next());
360/// assert_eq!(Some(true), encoder.next());
361/// assert_eq!(None, encoder.next());
362/// ```
363
364#[derive(Debug)]
365pub struct Encoder<I> {
366 datagram_iter: I,
367 first_half_bit: bool,
368 last_value: Option<bool>,
369}
370
371impl Encoder<DatagramBigEndianIterator> {
372 /// Create a new Encoder ready to encode the datagram passed along
373 ///
374 /// # Arguments
375 ///
376 /// * `datagram` - the datagram to be encoded
377 pub fn new(d: Datagram) -> Self {
378 let mut datagram_iter = d.into_big_endian_iter();
379 let last_value = datagram_iter.next();
380 Encoder::<DatagramBigEndianIterator> {
381 datagram_iter,
382 first_half_bit: true,
383 last_value,
384 }
385 }
386}
387
388impl Encoder<DatagramLittleEndianIterator> {
389 /// Create a new Encoder ready to encode the datagram passed along
390 ///
391 /// # Arguments
392 ///
393 /// * `datagram` - the datagram to be encoded
394 pub fn new(d: Datagram) -> Self {
395 let mut datagram_iter = d.into_little_endian_iter();
396 let last_value = datagram_iter.next();
397 Encoder::<DatagramLittleEndianIterator> {
398 datagram_iter,
399 first_half_bit: true,
400 last_value,
401 }
402 }
403}
404
405impl<I: Iterator<Item = bool>> Iterator for Encoder<I> {
406 type Item = bool;
407 fn next(&mut self) -> Option<Self::Item> {
408 match self.last_value {
409 Some(bit) => {
410 if self.first_half_bit {
411 self.first_half_bit = false;
412 Some(!bit)
413 } else {
414 self.first_half_bit = true;
415 self.last_value = self.datagram_iter.next();
416 Some(bit)
417 }
418 }
419 None => None,
420 }
421 }
422}
423
424/// Activity level of the Pin where the infrared receiver is attached to.
425/// It is the opposite level the pin takes if no datagram is transmitted.
426#[derive(PartialEq)]
427pub enum ActivityLevel {
428 High,
429 Low,
430}
431
432/// A priori knowledge about the first expected bit of a telegram
433///
434/// It is needed for correct decoding if the datagram length is unknown
435pub enum SyncOnTurningEdge {
436 First,
437 Second,
438}
439
440/// Decode a Manchester encoded stream of periodically taken samples into
441/// a datagram.
442pub struct Decoder {
443 // Config data
444 activity_level: ActivityLevel,
445 sync_on_turning_edge: SyncOnTurningEdge,
446 bit_order: BitOrder,
447 // Collected output data
448 datagram: Datagram,
449 // Internal processing control data
450 previous_sample: bool,
451 edge_distance: u8,
452 recording_distance: u8,
453 receiving_started: bool,
454 record_marker_reached: bool,
455}
456
457const SAMPLES_PER_HALF_BIT_PERIOD: u8 = 3;
458const TOLERANCE: u8 = 1;
459
460// ___---___------ e - first edge
461// xxx012345678901 x - exit criteria no bits are send anymore
462// f----tttt--xxx t - tolerance range an edge is expected
463
464const LOWER_BARRIER: u8 = 2 * SAMPLES_PER_HALF_BIT_PERIOD - TOLERANCE;
465const UPPER_BARRIER: u8 = 2 * SAMPLES_PER_HALF_BIT_PERIOD + TOLERANCE;
466const NO_EDGE_EXIT_LIMIT: u8 = 3 * SAMPLES_PER_HALF_BIT_PERIOD;
467
468impl Decoder {
469 /// Create an instance of a new manchester encoder
470 ///
471 /// # Arguments
472 ///
473 /// * `activity_level` - Low and High indicate what the activity level is
474 /// the negation of the activity level is the inactivity
475 /// level where no datagram is transmitted.
476 /// * `sync_on_turning_edge` - Indication if on the First or the Second
477 /// edge bits are aligned.
478 /// * `bit_order` - Either BigEndian (MSP is received first) or
479 /// LittleEndian (LSB is received first)
480 /// In combination of activity_level and sync_on_turning_edge it is determined
481 /// what if the first bit is either zero or one
482 ///
483 /// | Activity level | Sync on turning edge | Resulting first bit datagram |
484 /// |----------------|----------------------|------------------------------|
485 /// | High | First | Zero |
486 /// | High | Second | One |
487 /// | Low | First | One |
488 /// | Low | Second | Zero |
489 ///
490 pub const fn new(
491 activity_level: ActivityLevel,
492 sync_on_turning_edge: SyncOnTurningEdge,
493 bit_order: BitOrder,
494 ) -> Self {
495 let previous_sample = match activity_level {
496 ActivityLevel::High => false,
497 ActivityLevel::Low => true,
498 };
499 Decoder {
500 datagram: Datagram {
501 buffer: 0,
502 length_in_bit: 0,
503 },
504 previous_sample,
505 edge_distance: NO_EDGE_EXIT_LIMIT,
506 recording_distance: NO_EDGE_EXIT_LIMIT,
507 receiving_started: false,
508 activity_level,
509 sync_on_turning_edge,
510 record_marker_reached: false,
511 bit_order,
512 }
513 }
514
515 /// Sample a manchester modulated signal periodically and extract datagrams
516 ///
517 /// To cover some jitter the sampling rate is three times the half bit frequency
518 /// i.e. an Infrared manchester decoded bit lasts 2x 889 us),
519 /// Thus sampling period should be 296 us.
520 ///
521 /// Note: three times the bit frequency is good enough to consider the Nyquist
522 /// criterion and some potential jitter in sending frequency.
523 ///
524 /// # Arguments
525 ///
526 /// * `sample` - the level of the pin true equals high, false equals low
527 ///
528 /// # Returns
529 ///
530 /// Option of an infrared datagram
531 ///
532 /// * None - if no complete datagram is received
533 /// * Some(datagram) - a completely received datagram
534 ///
535 pub fn next(&mut self, sample: bool) -> Option<Datagram> {
536 // To understand the algorithm record marker are introduced.
537 //
538 // Record marker are the sample taken directly after the edge
539 // in the middle of the transmission of a bit
540 // Record marker are aligned to SyncOnTurningEdge.
541 //
542 // As of the manchester protocol definition, there must be always
543 // an edge in the middle of transmission of the bit.
544 //
545 // Example: Start with "1" (high inactivity)
546 //
547 // |bit_1|bit_0|bit_0 - The bits
548 // -------...------...---... - The signal
549 // ^ ^ ^ - The record marker
550 //
551 // Example: Start with "0" (high inactivity)
552 //
553 // |bit_0|bit_1|bit_1 - The bits
554 // ----------......---...--- - The signal
555 // ^ ^ ^ - The record marker
556 //
557 // At each record marker the bit value is determined and recorded
558 let mut return_value: Option<Datagram> = None;
559
560 if sample != self.previous_sample {
561 if !self.receiving_started {
562 // cover the start of the telegram
563 match self.sync_on_turning_edge {
564 SyncOnTurningEdge::First => {
565 // first edge is the record marker
566 self.record_marker_reached = true;
567 self.receiving_started = true;
568 }
569 SyncOnTurningEdge::Second => {
570 // by protocol design it is guaranteed that there is a second edge
571 // within half-bit time aka within SAMPLES_PER_HALF_BIT_PERIOD
572 if self.edge_distance <= SAMPLES_PER_HALF_BIT_PERIOD + TOLERANCE {
573 // first edge at the record marker
574 self.record_marker_reached = true;
575 self.receiving_started = true;
576 } else {
577 // very first edge -> do nothing on purpose
578 }
579 }
580 }
581 }
582 if self.recording_distance >= LOWER_BARRIER && self.recording_distance <= UPPER_BARRIER
583 {
584 self.record_marker_reached = true;
585 }
586 if self.record_marker_reached {
587 // In the middle of a bit transmission the value is derived from the new sample
588 self.datagram
589 .add_bit(!sample, self.bit_order) // the sample is NOT mixed with activity_level
590 .unwrap();
591 // reset internal data for the next record_marker
592 self.recording_distance = 1;
593 self.record_marker_reached = false;
594 }
595 self.previous_sample = sample;
596 self.edge_distance = 1;
597 } else {
598 self.edge_distance += 1;
599 self.recording_distance += 1;
600 }
601
602 if self.edge_distance > NO_EDGE_EXIT_LIMIT {
603 // end of datagram condition no edge anymore
604 if !self.datagram.is_empty() && (sample ^ (self.activity_level == ActivityLevel::High))
605 {
606 return_value = Some(self.datagram);
607 self.receiving_started = false;
608 }
609 self.datagram = Datagram::default();
610 self.edge_distance -= 1; // prevent number overflow
611 }
612 if self.recording_distance > NO_EDGE_EXIT_LIMIT {
613 self.recording_distance -= 1; // prevent number overflow
614 }
615 return_value
616 }
617}
618
619/// Control sending of datagrams, manage infrared radiation pollution
620///
621/// The InfraredEmitter behaves socially by enforcing a pause time between
622/// subsequent
623/// Required resources:
624///
625/// * A configured PWM - typically at a frequency of 36..38 kHz (RC5 protocol)
626/// * A facility that periodically runs half bit sending, e.g. a timer ISR
627/// typically at a period of 889 µs (half bit time, RC5 protocol)
628///
629/// # Example
630///
631/// ```ignore
632/// #[cortex_m_rt::entry]
633///
634/// fn main() -> ! {
635/// let dp = stm32::Peripherals::take().expect("cannot take peripherals");
636///
637/// // Set up the system clock
638/// //// let mut flash = dp.FLASH.constrain();
639/// let mut rcc = dp.RCC.constrain();
640///
641/// // Setup PWM we use Arduino PIN D5 -> is PB4 / TIM3_CH1 on stm32g071
642/// let gpiob = dp.GPIOB.split(&mut rcc);
643///
644/// // let clocks = rcc.cfgr.sysclk(16.MHz()).freeze(&mut flash.acr);
645///
646/// let pwm_pin = gpiob.pb4;
647/// let pwm = dp.TIM3.pwm(36_u32.khz(), &mut rcc);
648/// let mut pwm_send_ir = pwm.bind_pin(pwm_pin);
649///
650/// pwm_send_ir.set_duty(pwm_send_ir.get_max_duty() / 4); // 25% duty cyle
651///
652/// // Set up the interrupt timer
653/// let mut timer = dp.TIM2.timer(&mut rcc);
654/// timer.start(889.us());
655///
656/// const PAUSE_HALF_BITS_BETWEEN_DATAGRAMS: u8 = 3;
657/// let mut infrared_emitter = InfraredEmitter::new(PAUSE_HALF_BITS_BETWEEN_DATAGRAMS, pwm_send_ir, ());
658///
659/// defmt::println!("Init done");
660///
661/// let datagram = Datagram::new("0101_0011_0111_0001");
662/// defmt::println!("Send new datagram {}", datagram);
663/// infrared_emitter.send_if_possible(datagram, 25);
664///
665/// loop {
666/// infrared_emitter.send_half_bit();
667/// block!(timer.wait()).unwrap();
668/// }
669/// }
670/// ```
671#[derive(Debug)]
672pub struct InfraredEmitter<P, C, I> {
673 encoder: Option<Encoder<I>>,
674 max_pause_cycles: u8,
675 current_pause_cycles: u8,
676 pwm: P,
677 channel: C,
678}
679
680impl<P, C, D, I> InfraredEmitter<P, C, I>
681where
682 P: Pwm + Pwm<Channel = C> + Pwm<Duty = D>,
683 C: Copy,
684 D: core::ops::Mul<Output = D> + core::ops::Div<Output = D>,
685 I: Iterator<Item = bool>,
686{
687 /// Create a new infrared Emitter
688 ///
689 /// # Arguments
690 ///
691 /// * `pause_cycles` - configures the time between subsequent datagram
692 /// emissions. The total duration is half-bit-time (889 µs)
693 /// times number of pause bit cycles. In the pause time
694 /// no infrared radiation is emitted and other
695 /// participants can occupy the radiation space.
696 /// * `pwm` - the PWM to be used for ir pulse emission
697 /// * `channel` - the channel to be used by the PWM
698 pub fn new(pause_cycles: u8, pwm: P, channel: C) -> Self {
699 InfraredEmitter {
700 encoder: None,
701 max_pause_cycles: pause_cycles,
702 current_pause_cycles: 0,
703 pwm,
704 channel,
705 }
706 }
707
708 /// Progress on sending a datagram by emitting a half bit
709 ///
710 /// This function needs to be called every half-bit period, i.e. each 889 µs.
711 /// The periodically required call is most likely delegated to a timer ISR.
712 ///
713 /// half-bit emitting happens by enabling/disabling a a properly configured
714 /// PWM.
715 pub fn send_half_bit(&mut self) {
716 match &mut self.encoder {
717 Some(encoder) => match encoder.next() {
718 Some(half_bit) => {
719 if half_bit {
720 self.pwm.enable(self.channel);
721 } else {
722 self.pwm.disable(self.channel);
723 }
724 }
725 None => {
726 self.pwm.disable(self.channel);
727 self.encoder = None;
728 self.current_pause_cycles = 0;
729 }
730 },
731 None => {
732 // the pwm is already disabled -> manage pause period
733 self.current_pause_cycles += 1;
734 }
735 }
736 }
737}
738
739impl<P, C, D> InfraredEmitter<P, C, DatagramBigEndianIterator>
740where
741 P: Pwm + Pwm<Channel = C> + Pwm<Duty = D>,
742 C: Copy,
743 D: core::ops::Mul<Output = D> + core::ops::Div<Output = D>,
744{
745 /// Immediately start sending a datagram if possible
746 ///
747 /// Sending is possible iff there is no sending procedure in progress.
748 /// A call to this function is not blocking
749 ///
750 /// # Arguments
751 ///
752 /// * `datagram` - The datagram to be send
753 /// * `sending_power` - The duty cycle of the pwm in percent
754 /// should be less than or equal 25 (percent)
755 /// Is reduced to 25 if a higher value is given.
756 /// Lower sending power is appropriate for pairing datagrams.
757 ///
758 /// # Returns
759 ///
760 /// * *true* - if sending was initiated
761 /// * *false* - if sending was not possible to initiate
762 pub fn send_if_possible(&mut self, datagram: Datagram, sending_power: D) -> bool {
763 if self.current_pause_cycles < self.max_pause_cycles {
764 false
765 } else {
766 // let mut sending_power: D = if sending_power > 25 { 25 } else { sending_power };
767 // let duty = self.pwm.get_max_duty() * sending_power / 100;
768 // self.pwm.set_duty(self.channel, duty);
769 self.pwm.set_duty(self.channel, sending_power);
770 self.encoder = Some(Encoder::<DatagramBigEndianIterator>::new(datagram));
771 true
772 }
773 }
774}
775
776impl<P, C, D> InfraredEmitter<P, C, DatagramLittleEndianIterator>
777where
778 P: Pwm + Pwm<Channel = C> + Pwm<Duty = D>,
779 C: Copy,
780 D: core::ops::Mul<Output = D> + core::ops::Div<Output = D>,
781{
782 /// Immediately start sending a datagram if possible
783 ///
784 /// Sending is possible iff there is no sending procedure in progress.
785 /// A call to this function is not blocking
786 ///
787 /// # Arguments
788 ///
789 /// * `datagram` - The datagram to be send
790 /// * `sending_power` - The duty cycle of the pwm in percent
791 /// should be less than or equal 25 (percent)
792 /// Is reduced to 25 if a higher value is given.
793 /// Lower sending power is appropriate for pairing datagrams.
794 ///
795 /// # Returns
796 ///
797 /// * *true* - if sending was initiated
798 /// * *false* - if sending was not possible to initiate
799 pub fn send_if_possible(&mut self, datagram: Datagram, sending_power: D) -> bool {
800 if self.current_pause_cycles < self.max_pause_cycles {
801 false
802 } else {
803 // let mut sending_power: D = if sending_power > 25 { 25 } else { sending_power };
804 // let duty = self.pwm.get_max_duty() * sending_power / 100;
805 // self.pwm.set_duty(self.channel, duty);
806 self.pwm.set_duty(self.channel, sending_power);
807 self.encoder = Some(Encoder::<DatagramLittleEndianIterator>::new(datagram));
808 true
809 }
810 }
811}
812#[cfg(test)]
813mod tests;