Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Investigate how to make tolerances configurable #37

Open
jkristell opened this issue Sep 11, 2020 · 3 comments
Open

Investigate how to make tolerances configurable #37

jkristell opened this issue Sep 11, 2020 · 3 comments

Comments

@jkristell
Copy link
Owner

No description provided.

@jkristell jkristell added this to the Release 0.8.0 milestone Sep 11, 2020
@jkristell jkristell removed this from the Release 0.8.0 milestone Sep 29, 2020
@riskable
Copy link
Contributor

riskable commented Oct 7, 2022

FYI: I was looking for a workaround to the inability to control this very thing (tolerances) and came up with this:

    // The infrared module is a bit too picky about timing so we fudge it a bit to make the IR
    // receiver *much* more reliable (at least with NEC remotes):
    if elapsed_us < 850 { // No idea how close these values are to their theoretical exact timings
        elapsed_us = 550;
    } else if elapsed_us > 1400 && elapsed_us < 2000 {
        elapsed_us = 1600;
    }

...and it works surprisingly well! Like, flawlessly. Basically, I've got the IR pin tied to IO_IRQ_BANK0 and triggering on High and Low (just like in the RP2040 example I wrote a while back). The value that gets passed to ir_recv.event_iter() seems to be the key part.

Basically, when I first started this code the IR remote worked OK-ish... Had to re-press a button about 25% of the time. However, as time went on and the project grew the IR events became more and more unreliable to the point where they would only work like 10% of the time. I knew why: I had too much simultaneous crap going on! haha. This was messing with the very precise timing required by infrared. I had defmt print out the elapsed_us value with each call of my interrupt-bound function and did the export DEFMT_LOG=trace; cargo run --release thing and that's when I had my "Aha!" moment.

Here's the full function code in case you need more context:

// NOTE: This needs to be bound to the IO_IRQ_BANK0 interrupt
#[inline(never)]
#[link_section = ".data.ram_func"]
pub(crate) fn handle_ir_event(mut c: crate::app::handle_ir_event::Context) {
    // static mut LAST: Option<Instant<u64, 1, 1000000>> = None;
    let ir_edge = c.local.ir_edge;
    let ir_recv = c.local.ir_recv;

    let now = monotonics::now();
    let elapsed = now.checked_duration_since(*ir_edge).unwrap();
    let mut elapsed_us: u32 = elapsed.to_micros() as u32;
    // debug!("IR elapsed_us: {}", elapsed_us);

    // The infrared module is a bit too picky about timing so we fudge it a bit to make the IR
    // receiver *much* more reliable (at least with NEC remotes):
    if elapsed_us < 850 {
        elapsed_us = 550;
    } else if elapsed_us > 1400 && elapsed_us < 2000 {
        elapsed_us = 1600;
    }

    if let Ok(cmds) = ir_recv.event_iter(elapsed_us) {
        for cmd in cmds {
            // debug!("IR: {:?}", cmd); // TEMP
            match cmd {
                MultiReceiverCommand::Nec(nec) => {
                    debug!("Nec cmd: {:?}, elapsed_us: {}", nec, elapsed_us);
                    let since_last_cmd = (now
                        .checked_duration_since(*c.local.ir_last_cmd_time)
                        .unwrap())
                    .to_millis();
                    if let Some(button) = IRRemote::decode(&nec) {
                        // debug!("Button decoded: {}", button);
                        if since_last_cmd > MAX_REPEAT_TIME {
                            // No commands for a while so reset the repeat counter
                            *c.local.ir_last_cmd_time = now;
                            *c.local.ir_repeat_counter = 0;
                        }
                        c.shared.states.lock(|states| {
                            if !nec.repeat
                                || *c.local.ir_repeat_counter == 0
                                || *c.local.ir_repeat_counter >= REPEAT_INTERVAL
                            {
                                let index = IRRemote::index(button) as u8;
                                let (_code, _action) = IRRemote::BUTTONS[index as usize];
                                *states.ir_button = index; // This is how keeb_scanner() can know what was pressed
                                *c.local.ir_repeat_counter = 0; // Reset the repeat counter
                            }
                        });
                        *c.local.ir_repeat_counter += 1;
                    }
                }
                MultiReceiverCommand::NecSamsung(nec_samsung) => {
                    debug!("nec_samsung cmd: {:?}", nec_samsung);
                }
                MultiReceiverCommand::NecApple(nec_apple) => {
                    debug!("nec_apple cmd: {:?}", nec_apple);
                }
                MultiReceiverCommand::Rc5(rc5) => {
                    debug!("rc5 cmd: {:?}", rc5);
                }
                MultiReceiverCommand::Rc6(rc6) => {
                    debug!("rc6 cmd: {:?}", rc6);
                }
                MultiReceiverCommand::Denon(denon) => {
                    debug!("denon cmd: {:?}", denon);
                }
                _ => {} // Just ignore other protocols (since we can only support 6 at a time)
            }
        }
    }

    // Clear the interrupts (won't work unless we do this)
    ir_recv.pin().clear_interrupt(Interrupt::EdgeLow); // Does the order matter?
    ir_recv.pin().clear_interrupt(Interrupt::EdgeHigh); // I don't know!

    // let elapsed_since_processing = monotonics::now().checked_duration_since(now).unwrap();
    *ir_edge = now;
}

I haven't had the chance to test it with other kinds of remotes yet. NEC is the one I'm planning on putting my full support behind regardless.

@jkristell
Copy link
Owner Author

Hi!

Interesting. I have to look into this a bit more. Could you send me a log of the raw elapsed_us values?

@dotcypress
Copy link

@riskable I found this issue a hard way. In my device I using internal MCU oscillator(too much ppm's) as system clocks and NEC command parsing become really unstable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants