Code Monkey home page Code Monkey logo

infrared's Introduction

crates.io version docs.rs

Infrared

Rust library for using Infrared hardware decoders (For example a Vishay TSOP* decoder), enabling remote control support for embedded project.

This library aims for to be useful with the any MCU hal that implements the embedded-hal traits, and at the same time provide functionality for using it with more efficient implementation such as input capture, and be useful in host applications (such as Blipper).

Supported protocols

  • The NEC Protocol and the Samsung variant of it
  • Philips Rc5 and Rc6
  • "Samsung BluRay Player protocol". Please let know if you know what it really is called :)
  • Denon 48 bit protocol

Tested with

  • Tested with bluepill board
  • Vishay TSOP382 IR receiver
  • Various ir leds
  • NEC Generic "Special for MP3" and Samsung remotes
  • Rc6 tested with a Philips Bluray player remote
  • Rc5 tested with a Marantz CD player remote

Boards

References

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

infrared's People

Contributors

jkristell avatar riskable avatar sjoerdsimons avatar striezel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

infrared's Issues

How to match CmdEnum when using MultiReceiver?

I got MultiReceiver working (in my RTIC-using code) but it doesn't look like there's a way to match against the cmd...

if let Ok(cmds) = ir_recv.event_iter(elapsed_us) {
    for cmd in cmds {
        defmt::println!("IR: {:?}", cmd); // TEMP
        match cmd {
            <what do I put here?>
        }
    }
}

The problem is that infrared::receiver::multireceiver is private, therefore infrared::receiver::multireceiver::CmdEnum is also private. So even if I wanted to do something like:

match cmd {
    infrared::receiver::multireceiver::CmdEnum::Nec(_) => {},
    (_) => {},
}

...I can't. I'm still pretty new to Rust though so maybe I'm missing something?

PeriodicReceiver::new() does not work.

Hi, i don't understand why your crate doesn't work with STM32H7xx-hal, which i'm using.
Does your crate support only F1 family ?

type annotations needed for infrared::hal::PeriodicReceiver<SM, stm32h7xx_hal::gpio::gpioa::PA0<stm32h7xx_hal::gpio::Input<stm32h7xx_hal::gpio::Floating>>>`

cannot infer type for type parameter SMrustc(E0282)
main.rs(86, 9): consider giving receiver the explicit type infrared::hal::PeriodicReceiver<SM, stm32h7xx_hal::gpio::gpioa::PA0<stm32h7xx_hal::gpio::Input<stm32h7xx_hal::gpio::Floating>>>, where the type parameter SM is specified
main.rs(86, 20): cannot infer type for type parameter SM
type annotations needed for infrared::hal::PeriodicReceiver<SM, stm32h7xx_hal::gpio::gpioa::PA0<stm32h7xx_hal::gpio::Input<stm32h7xx_hal::gpio::Floating>>>
cannot infer type for type parameter SMrustc(E0282)
main.rs(86, 9): consider giving receiver the explicit type infrared::hal::PeriodicReceiver<SM, stm32h7xx_hal::gpio::gpioa::PA0<stm32h7xx_hal::gpio::Input<stm32h7xx_hal::gpio::Floating>>>, where the type parameter SM is specified`

Received value always "0"

Hi! Thanks for this crate. I am new to embedded rust and thus bought this kit: https://www.amazon.ca/-/fr/gp/product/B07CVS1LBT

It's an Arduino Uno controlling the car. While the original C code is fully functional, I want to write my own Rust firmware.

My first step is to read infrared commands from the remote but I am having issues. I struggled to even get something since your example uses a bluepill instead of an arduino so I am trying to understand how the infrared receiver works. The avr-hal crate I am using (https://github.com/Rahix/avr-hal) does not seems to be handle interupts which you example uses...

It seems that the PeriodicReceiver::new()'s sample rate must match the delay between two successive calls to poll(). So for a 20kHz rate, I need to arduino_uno::delay_us(50); in my loop (1 / 20kHz = 50 mus):

loop {
    if let Ok(Some(cmd)) = ir_receiver.poll() {
        ufmt::uwriteln!(&mut serial, "{:?}", cmd.bits).void_unwrap();
    }

    // 20 kHz == period of 50 mus
    // The delay between polling must match the receiver's sample rate
    arduino_uno::delay_us(50);
}

If I change the delay by 1 mus, poll() returns an Err... Is that expected?

Using the arduino's "IRremote" library's IRrecvDump example I see the following in the serial console when I press the button "1" of the remote:

FD00FF
Decoded NEC: FD00FF (32 bits)
Raw (68): 5682 8950 -4450 600 -550 550 -600 550 -550 600 -500 600 -550 600 -500 600 -550 600 -500 600 -1600 600 -1600 650 -1600 600 -1600 600 -1600 600 -1600 650 -500 600 -1600 600 -550 550 -600 550 -550 550 -600 550 -550 550 -550 600 -550 550 -550 600 -1650 550 -1650 550 -1650 550 -1700 550 -1650 550 -1650 550 -1650 600 -1650 550 

From this I understand the protocol is NEC. I thus define my receiver as:

// The specs says the IR received is on pin D2
let mut ir_receiver: PeriodicReceiver<NecDebug, PD2<Input<Floating>>> = PeriodicReceiver::new(pins.d2, IR_SAMPLERATE);

I tried both Nec and NecDebug: The cmd+addr and bits are always 0. :(

The NecDebug::repeat does seems to work though; a short press gives me false while a long press gives true.

Am I missing something obvious?

Thanks a lot!!

Error compiling: The trait `Format` is not implemented for `<Mono as InfraMonotonic>::Duration`

With the defmt feature enabled using the latest git code (as of 20220505) I can't get infrared to compile:

rust/embedded/sbn_firmware is ๐Ÿ“ฆ v0.1.0 via ๐Ÿฆ€ v1.60.0 took 4s 
โฏ cargo build --release --bin sbn
   Compiling infrared v0.13.0 (https://github.com/jkristell/infrared.git?branch=master#634db796)
error[E0277]: the trait bound `<Mono as InfraMonotonic>::Duration: Format` is not satisfied
   --> /home/riskable/.cargo/git/checkouts/infrared-4eac89e2d8be9470/634db79/src/fmt.rs:13:13
    |
13  |             ::defmt::trace!($s $(, $x)*);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Format` is not implemented for `<Mono as InfraMonotonic>::Duration`
    |
   ::: /home/riskable/.cargo/git/checkouts/infrared-4eac89e2d8be9470/634db79/src/receiver.rs:202:9
    |
202 |         trace!("dt: {:?}, edge: {} s: {:?}", dt, edge, state);
    |         ----------------------------------------------------- in this macro invocation
    |
note: required by a bound in `defmt::export::fmt`
   --> /home/riskable/.cargo/registry/src/github.com-1ecc6299db9ec823/defmt-0.3.1/src/export/mod.rs:137:15
    |
137 | pub fn fmt<T: Format + ?Sized>(f: &T) {
    |               ^^^^^^ required by this bound in `defmt::export::fmt`
    = note: this error originates in the macro `::defmt::trace` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting the associated type
    |
198 |     ) -> Result<Option<Cmd>, DecodingError> where <Mono as InfraMonotonic>::Duration: Format {
    |                                             ++++++++++++++++++++++++++++++++++++++++++++++++

For more information about this error, try `rustc --explain E0277`.
error: could not compile `infrared` due to previous error

Seems that adding support for 64-bit fugit timers broke something.

Testing

Set up devices to do automated testing with blipper

Unexpected Sender Behavior When Trying to Send the Same Received Signal

I have written a code that receives IR signals using MultiReceiver's event_iter and then sends the same received commands using transmitter.load:

    #[task(binds = EXTI9_5, local = [mono, receiver,transmitter])]
    fn button_click4(mut ctx: button_click4::Context) {
    unsafe {
        static mut LAST: Option<Instant> = None;

        let now = ctx.local.mono.now();

        if let Some(dt) = LAST.map(|i| i.elapsed()) {
            if let Ok(cmds) = ctx.local.receiver.event_iter(dt) {
                for cmd in cmds {
                    if let MultiReceiverCommand::Nec(c) = cmd {
                        hprintln!("cmd: {:?}",c);
                        ctx.local.transmitter.tick();
                        ctx.local.transmitter.load::<Nec>(&c);
                    }
                }
            }
        }
        LAST.replace(now);
    }
    ctx.local.receiver.pin().clear_interrupt_pending_bit();
}

But the signal that is generated by the sender is completely different from the signal that is being sent to the receiver when I check them using oscilloscope.

This is the signal that I send to my receiver:

photo_2024-03-16_18-22-10
Which causes the sender to start making these signals continuously:

photo_2024-03-16_18-22-39

Until the second time that I send that same signal to my receiver and then the sender stops signaling.

Sender Support for Protocols than Don't Have ProtocolEncoder

Hi and thank you for this awesome crate.

I have written a code that receives IR signals using MultiReceiver's event_iter and then sends the same received commands using transmitter.load :

but this code only works for the nec, rc6, and rc5 protocols since they have their ProtocolEncoder, but for other protocols like Denon I get the following error:

the trait bound infrared::protocol::Denon: ProtocolEncoder<20000> is not satisfied
--> src\main.rs:152:43
|
<infrared::protocol::Rc5 as ProtocolEncoder>
<infrared::protocol::Rc6 as ProtocolEncoder>

Am I doing something wrong or is sending Denon commands not supported at the moment?

Support for Samsung remotes

I have two Samsung remotes that behaves a bit different.

One is an older (~8 years) TV-remote where the protocol is very similar to the NEC but with different timing.

The other is for a newer DVD-player. It's similar but with additional delays (for separating address and command maybe?)

Error handling

Errors such as validation errors for Nec should be propagated to the user.

Rc5 receiver synchronisation

The Rc5 receiver seems lose track of its state sometimes. Probably related to sending rc6 commands at the same time.

  • Investigate and create test case
  • Implement some kind of timeout

Unable to use Sender with avr-hal: the trait `_embedded_hal_PwmPin` is not implemented for `avr_hal_generic::port::Pin<PwmOutput<Timer2Pwm>, PD3>`

(I'm using arduino_hal with an Arduino UNO)

I use this code

let timer2 = Timer2Pwm::new(dp.TC2, Prescaler::Prescale64);
let mut sender_pin = pins.d3.into_output().into_pwm(&timer2);
let ir_sender = Sender::new(sender_pin);

to initialize the sender, but get an error:

the trait `_embedded_hal_PwmPin` is not implemented for `avr_hal_generic::port::Pin<PwmOutput<Timer2Pwm>, PD3>`

I was unable to implement the trait myself. Is it possible to use Sender with avr-hal?

Release 0.12

  • Update documentation and doc examples
  • Remove hal/reciever
  • Update all examples in infrared-examples
  • Check the naming
    • "GenericInput"...
  • Verify that hal/sender still works
  • Update Changelog
  • README.md
  • Blog post
  • MultiReceiver test

Apple remotes

I have a remote definition to contribute, but I thought it may warrant some discussion first. According to Wikipedia, Apple is using NEC with a different format. I'm not sure if the format matters since it seems to be working.

https://github.com/jhillyerd/bluepill-exp/blob/master/src/apple2009.rs

The remote button decoding works fine as is, but there are undecodable commands.

Hold-down repeat

I'm not sure if the crate supports this or not, but I see 0/0 when holding down a button:

14:03:22.111 IR button: Up
14:03:22.111 Unknown IR cmd: NecCommand { addr: 0, cmd: 0, var: PhantomData }
14:03:22.366 Unknown IR cmd: NecCommand { addr: 0, cmd: 0, var: PhantomData }
14:03:22.366 Unknown IR cmd: NecCommand { addr: 0, cmd: 0, var: PhantomData }

Legacy cmd 5

Apple sends an extra command when pressing Enter or Play/Pause, so that legacy receivers can still interpret it as a Play/Pause. Wikipedia documents this.

14:03:23.612 IR button: Enter
14:03:23.612 Unknown IR cmd: NecCommand { addr: 238, cmd: 5, var: PhantomData }

14:03:26.107 IR button: Play_Paus
14:03:26.363 Unknown IR cmd: NecCommand { addr: 238, cmd: 5, var: PhantomData }

Hangup when receiver is created

Heya! First of all, thank you a lot for making this crate.

I am currently trying to test it out on my atmega328p (The metro mini from adafruit) and have an issue with the whole thing freezing when I try to build a receiver.

I have the following setup:

type ReceiverPin = Pin<Input<Floating>, PD7>;

type Proto = Nec<NecCommand>;
type ProtoCmd = <Proto as Protocol>::Cmd;

type ReceiverKind = infrared::Receiver<Proto, Poll, PinInput<ReceiverPin>>;

let receiver: ReceiverKind = Receiver::with_pin(40_000, pins.d7.into_floating_input());

And it never progresses past that point. I have a small suspicion that this might be due to 328p's pointer width of 16, and thus having a maximum usize of just 2^16-1 which is way smaller than even the 'default' resolution of 1_000_000.

I am not sure how to verify whether it is panicking/aborting or not, but looking through the code one can find places like this: https://github.com/jkristell/infrared/blob/master/src/protocol/utils.rs#L39 where a default type of 'usize' is inferred but that gets truncated to 0 probably which would then divide by 0.

Throughout the crate 1_000_000 is used, with usize as types, a quick and dirty 'search and replace' fixed that issue I had (but I did not get my HX1838 to be detected yet).

Overflow when setting up NecApple EventReceiver at 1_000_000 hz

It looks like https://github.com/jkristell/infrared/blob/v0.11.0/src/protocols/utils.rs#L79 overflows with a microsecond sample rate:

   type IRInput = gpiob::PB6<Input<Floating>>;
   type IRRecv = infrared::hal::EventReceiver<infrared::protocols::NecApple, IRInput>;
   // ...
   let ir_recv = infrared::hal::EventReceiver::new(ir_input, 1_000_000);

is panicking on a blackpill stm32f411 board.

0 ERROR panicked at 'attempt to multiply with overflow', C:\Users\james\.cargo\registry\src\github.com-1ecc6299db9ec823\infrared-0.11.0\src\protocols\utils.rs:79:16
โ””โ”€ panic_probe::print_defmt::print @ C:\Users\james\.cargo\registry\src\github.com-1ecc6299db9ec823\panic-probe-0.2.0\src\lib.rs:94
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
stack backtrace:
   0: HardFaultTrampoline
      <exception entry>
...
   5: core::panicking::panic_fmt
        at /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/core/src/panicking.rs:92:14
   6: core::panicking::panic
        at /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/core/src/panicking.rs:50:5
   7: infrared::protocols::utils::InfraRange4::new
   8: <infrared::protocols::nec::receiver::NecReceiverState<C> as infrared::recv::InfraredReceiverState>::create
        at github.com-1ecc6299db9ec823\infrared-0.11.0\src\protocols\nec/receiver.rs:32:9
   9: infrared::recv::InfraredReceiver::receiver_state
        at github.com-1ecc6299db9ec823\infrared-0.11.0\src\recv/mod.rs:18:9
  10: infrared::recv::event::EventReceiver<Protocol>::new
        at github.com-1ecc6299db9ec823\infrared-0.11.0\src\recv/event.rs:18:20
  11: infrared::hal::receiver::EventReceiver<Protocol,Pin>::new
        at github.com-1ecc6299db9ec823\infrared-0.11.0\src\hal/receiver.rs:25:19
  12: rtic_infrared_int::app::init
        at src\bin/rtic-infrared-int.rs:57:23
...

Always the same code

First off, thanks for this crate!

I'm trying to decode a signal of a generic "Car MP3" remote, which apparently uses the NEC protocol.
When using the NecDebug protocol type, I always get the same bits for every button I press.
Am I doing something wrong?

I always get NecCommand { addr: 0, cmd: 0, repeat: true } or respectively NecDebugCmd { bits: 0 } for every button on my remote.

On rare occasions I also get a non-zero cmd, but it seems random.

Here is a condensed version of my code

type IrReceiver = infrared::Receiver<
  infrared::protocol::Nec,
  infrared::receiver::Poll,
  infrared::receiver::PinInput<Pin<Gpio3, Input<Floating>>>,
  Button<CarMp3>
>;

pub static IR_RECEIVER: Mutex<RefCell<Option<IrReceiver>>> = Mutex::new(RefCell::new(None));

pub static UART: Mutex<RefCell<Option<UartPeripheral<hal::uart::Enabled, pac::UART0>>>> =
  Mutex::new(RefCell::new(None));

pub static TIMER: Mutex<RefCell<Option<hal::Timer>>> = Mutex::new(RefCell::new(None));
pub static ALARM: Mutex<RefCell<Option<timer::Alarm0>>> = Mutex::new(RefCell::new(None));

const TIMER_FREQ: u32 = 20_000;
const TIMER_DURATION_US: u32 = 1_000_000 / TIMER_FREQ;

fn main() {
    // ...omitted
    
    let ir_pin: IrReceiverPin = pins.gpio3.into_floating_input();
    let receiver = IrReceiver::with_pin(TIMER_FREQ, ir_pin);
    let mut timer = Timer::new(p.TIMER, &mut p.RESETS);
    let alarm = timer.alarm_0().unwrap();

    cortex_m::interrupt::free(|cs| {
      IR_RECEIVER.borrow(cs).replace(Some(receiver));
      TIMER.borrow(cs).replace(Some(timer));
      ALARM.borrow(cs).replace(Some(alarm));

      let mut alarm = ALARM.borrow(cs).borrow_mut();
      let alarm = alarm.as_mut().unwrap();
      let mut timer = TIMER.borrow(cs).borrow_mut();
      let timer = timer.as_mut().unwrap();

      alarm
        .schedule(Microseconds::new(TIMER_DURATION_US))
        .unwrap();
      alarm.enable_interrupt(timer);
    });

    unsafe {
      pac::NVIC::unmask(pac::Interrupt::TIMER_IRQ_0);
    }
}

#[allow(non_snake_case)]
#[interrupt]
fn TIMER_IRQ_0() {
  cortex_m::interrupt::free(|cs| {
    let mut alarm = ALARM.borrow(cs).borrow_mut();
    let alarm = alarm.as_mut().unwrap();
    let mut timer = TIMER.borrow(cs).borrow_mut();
    let timer = timer.as_mut().unwrap();
    let mut receiver = IR_RECEIVER.borrow(cs).borrow_mut();
    let receiver = receiver.as_mut().unwrap();

    if let Ok(Some(cmd)) = receiver.poll() {
      let action = cmd.action();
      let mut uart = UART.borrow(cs).borrow_mut();
      let uart = uart.as_mut().unwrap();
      let string = format!(" {}|{:?} ", cmd.command().cmd != 0, action);
      uart.write_full_blocking(string.as_bytes());
    }

    alarm.clear_interrupt(timer);
    alarm
      .schedule(Microseconds::new(TIMER_DURATION_US))
      .unwrap();
  });
}

#[derive(Default, Debug)]
pub struct CarMp3;
impl RemoteControlModel for CarMp3 {
    const MODEL: &'static str = "Car Mp3";

    const DEVTYPE: infrared::remotecontrol::DeviceType = infrared::remotecontrol::DeviceType::Generic;

    const PROTOCOL: infrared::ProtocolId = ProtocolId::Nec;

    const ADDRESS: u32 = 0;

    type Cmd = NecCommand;

    const BUTTONS: &'static [(u32, infrared::remotecontrol::Action)] = &[
        (0xFFA25D, infrared::remotecontrol::Action::ChannelListPrev),
        (0xFF629D, infrared::remotecontrol::Action::ChannelList),
        (0xFFE21D, infrared::remotecontrol::Action::ChannelListNext),
        (0xFF22DD, infrared::remotecontrol::Action::Prev),
        (0xFF02FD, infrared::remotecontrol::Action::Next),
        (0xFFC23D, infrared::remotecontrol::Action::Play_Pause),
        (0xFFE01F, infrared::remotecontrol::Action::VolumeDown),
        (0xFFA857, infrared::remotecontrol::Action::VolumeUp),
        (0xFF906F, infrared::remotecontrol::Action::Eq),
        (0xFF6897, infrared::remotecontrol::Action::Zero),
        //(0xFF9867, infrared::remotecontrol::Action::?),
        //(0xFFB04F, infrared::remotecontrol::Action::?),
        (0xFF30CF, infrared::remotecontrol::Action::One),
        (0xFF18E7, infrared::remotecontrol::Action::Two),
        (0xFF7A85, infrared::remotecontrol::Action::Three),
        (0xFF10EF, infrared::remotecontrol::Action::Four),
        (0xFF38C7, infrared::remotecontrol::Action::Five),
        (0xFF5AA5, infrared::remotecontrol::Action::Six),
        (0xFF42BD, infrared::remotecontrol::Action::Seven),
        (0xFF4AB5, infrared::remotecontrol::Action::Eight),
        (0xFF52AD, infrared::remotecontrol::Action::Nine),

    ];
}

I'm using a raspberry pi pico with the rp_pico board crate.

Repeats

Repeats needs to be added to the Command trait and the RemoteControl trait and implemented properly for the protocol Commands.

  • Nec: Repeats have a special encoding
  • Rc5 and Rc6 A bit is toggled in the command to signal repeat

Some remotes and devices seems to expect commands to always be sent with repeats.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.