guild icon
Toit
#RMT IR
Thread channel in help
catdog8516
catdog8516 04/30/2024 10:13 AM
Hi all, very new to Toit - but loving it so far.

I'm having a go at building a primitive 'laser tag' game using a few ESP32S3s, IR LEDs and IR receivers. The idea is a player has a 'laser' that beams its ID when a trigger is pressed, and the IR data is picked up by the other player's receiver.

I've started digging around in the RMT documentation, but I'm in over my head. Are there any code snippets around that I could use to get started (i.e. send an IR code, receive an IR code)?

Thanks
floitsch
floitsch 04/30/2024 10:23 AM
There isn't any library that uses the RMT for the intended purpose (IR codes of remote controls), but I don't think that's really needed.

Fundamentally, you configure the RMT to send a certain bit-pattern, optionally with a carrier frequency.
On the other side, you ask the receiver to detect these patterns.

So far, I have only used the RMT for wired peripherals (like the HC-SR04, the DHT11, or the one-wire bus) where the input is always "clean". I think it should work with noisy infra-red too, though.
floitsch
floitsch 04/30/2024 10:25 AM
Unfortunately, I don't have any IR receiver, so I can't try to get an example working.
catdog8516
catdog8516 05/01/2024 06:36 AM
@floitsch OK thanks - I'll see if I can re-purpose some code that makes use of the wired hardware you mentioned.
floitsch
floitsch 05/01/2024 07:54 AM
Don't hesitate to reach out to me if you have questions about the RMT peripheral.
catdog8516
catdog8516 05/02/2024 06:38 AM
@floitsch I got the transmit part working (well at least I have it pulsing a non-IR led!). But I'm getting an error creating a Rx channel. Here's my test code:
pin_ := gpio.Pin.in 18 rx := rmt.Channel pin_ --input --idle_threshold=26_000 --filter_ticks_threshold=10 --clk_div=233
And the error I'm getting is:
E (14590) rmt(legacy): rmt_set_gpio(528): RMT CHANNEL ERR E (14590) rmt(legacy): rmt_config(673): set gpio for RMT driver failed ---- EXCEPTION error. UNKNOWN ERROR 0x102(258) 0: rmt-config-rx_ <sdk>/rmt.toit:574:3 1: Channel.configure <sdk>/rmt.toit:381:5 2: Channel <sdk>/rmt.toit:294:12 3: main test-ir.toit:10:9

I've tried using a few different GPIO pins, but get the same error.

device has version <2.0.0-alpha.146>
esp32s3-20210327
Build:Mar 27 2021

Appreciate any suggestions
floitsch
floitsch 05/02/2024 06:41 AM
I think it's unrelated, but try to pin_ := gpio.Pin 18 (without the .in). The RMT will configure it correctly itself.
floitsch
floitsch 05/02/2024 06:41 AM
I will try to have a look once I'm at work.
catdog8516
catdog8516 05/02/2024 06:46 AM
Same error; Thanks for the speedy reply
floitschfloitsch
I will try to have a look once I'm at work.
floitsch
floitsch 05/02/2024 06:53 AM
In the meantime: could you maybe try to run the example code for the hc-sr04 package: https://pkg.toit.io/package/github.com%2Flask%[email protected]
floitsch
floitsch 05/02/2024 06:53 AM
It uses the RMT and should start fine without anything connected.
catdog8516
catdog8516 05/02/2024 07:01 AM
Will do
floitsch
floitsch 05/02/2024 07:41 AM
I can reproduce.
It's an issue with the esp32s3. The same code works fine on the esp32.
I will try to fix it.
catdog8516
catdog8516 05/02/2024 07:43 AM
Much appreciated. I just tested on a new board (S3) too. Not sure if it's relevant, but I use jag flash --chip esp32s3-spiram-octo for these boards.
floitsch
floitsch 05/02/2024 07:44 AM
Thanks. Since I can reproduce on the standard s3 devkit, that shouldn't matter.
Probably some initialization code that is different on the ESP32S3 compared to the ESP32.
catdog8516
catdog8516 05/02/2024 07:46 AM
Let me know if you'd like me to create an issue in github, or if I can help in any way.
πŸ‘1
floitsch
floitsch 05/02/2024 08:41 AM
The reason for the error is that the ESP32S3 uses the first half (channel 0-3) for transmitting and the second half (4-7) for receiving.
It's just a small line in the documentation and we overlooked it.
I will change the code to allocate the correct channel when initializing the RMT.
catdog8516
catdog8516 05/02/2024 08:52 AM
Great. So, how might one get hold of your fix when its ready?
floitsch
floitsch 05/02/2024 09:10 AM
It will be in the next release.
floitsch
floitsch 05/02/2024 09:11 AM
In the meantime, you can give a channel-id as argument.
If you use 4+ then you should get an rx channel.
catdog8516
catdog8516 05/02/2024 09:33 AM
Excellent - I'll give it a try
floitsch
floitsch 05/02/2024 12:39 PM
The RMT fix has been reviewed and is now in the commit queue. It will be part of the next release (which should happen within the next few days).
catdog8516
catdog8516 05/06/2024 09:47 AM
@floitsch do you think this ESP-IDF example could be a good starting point for a Toit IR package? I can see echos of Signals and Channels in there..
https://github.com/husarnet/esp-idf/blob/master/examples/peripherals/rmt_nec_tx_rx/main/infrared_nec_main.c
Espressif ESP-IDF with added support for Husarnet. Contribute to husarnet/esp-idf development by creating an account on GitHub.
floitsch
floitsch 05/06/2024 10:27 AM
Afaik, NEC is the standard in IR remote controls.
The example looks reasonable, and it should be possible to port it to Toit.
Having an ir-NEC package would be great.
I would start by trying to get the example working, but once that's done there is nothing in the way of making it a package. Maybe even with a way to get a database of common IR codes.
catdog8516
catdog8516 05/06/2024 11:54 PM
I don’t know C or Toit, but leave it with me :πŸ‘:
catdog8516
catdog8516 05/17/2024 07:01 AM
@floitsch I've been doing some tinkering with RMT and IR; Transmitting random signals is easy enough, and I can receive IR pulses too (signals.stringify shows a bunch of numbers when I press IR remote button).

But I'm struggling with a few things, maybe you can give me some pointers:

1. For transmitting, how can I represent the NEC message structure using signals? For example, it uses a 9ms burst as a header. How do I map 9ms to a signal period (i.e. ticks)?

2. And.. should I use a single rmt.Signals instance to represent the entire message (header + address + command + eom), or should the message be made and transmitted as a series of Signals instances?

3. NEC IR uses a carrier frequency of 38kHz. I assume this means I'll need to use the --carrier-frequency-hz option. What other options might I need? --clk-div and --carrier-duty-percent?

4. When it comes to reading in Signals - can you let me know a rough outline of how to parse these into usable data? I'm assuming that I would first match a signal pulse to the 9ms header, and then buffer/parse the remaining pulses (address + command, etc) until I detect the EOM pulse?

I've been trying to repurpose the 1wire source code, but I'm struggling to see the big picture of how it all hangs together.

Anyways - any tips appreciated.
catdog8516OPcatdog8516
@floitsch I've been doing some tinkering with RMT and IR; Transmitting random signals is easy enough, and I can receive IR pulses too (signals.stringify shows a bunch...
floitsch
floitsch 05/17/2024 09:39 AM
Fyi, we are still using the old API of the RMT, which ESP-IDF has here: https://docs.espressif.com/projects/esp-idf/en/v4.4.7/esp32/api-reference/peripherals/rmt.html

1. The RMT peripheral normally runs with the APB clock which is at 80MHz. That's way too fast for most use cases, and we thus normally divide the clock by a constant factor.
Dividing it by 80 would give you a 1MHz resolution. With that you could get a 9ms by just setting the length to 9000 ticks.
However, since NEC pulse bursts are 562.5us you will want something slightly different. If you divide the 80MHz clock by 45, then it has a resolution of 0.5625us (if I'm not mistaken). So that's probably the most convenient here. Each pulse would then be 1000 ticks.
Note that the divider must be in range 1 to 255. So you can't just go to ms resolution. Given that the length can go up to 16 bits, you should still be fine.

2. Depends. If there aren't any timing constraints between header, address, ..., then you could send them separatedly. I would expect that you must send the address at a very specific timing after the header. The main reason not to put it into one signals instance is that you don't have the resolution (like needing to wait for more than 65ms), or the space (only a limited amount of entries can be in a signals instance). Otherwise I would put everything together.

3. Yes. The carrier-frequency should be set to 38000. The duty-percent to 50. (At least I didn't see anything contrary to this).
floitsch
floitsch 05/17/2024 09:41 AM
4. the fun part :πŸ™‚:
- for development, the easiest is to connect your sending and receiving pins. This way you don't need to deal with bad IR reception,... That can wait until the code is working.
- you need to undo the carrier frequency. I think you can do that by just setting a ticks-threshold. That just says that anything that is shorter than that value is ignored. If you set it to something slightly higher than the carrier, then the carrier should be filtered out.
- make sure that the idle time is big enough so that you get the whole signal, but not much longer.
- once you get data in, print it and have a look to see if it sounds reasonable. You should basically be able to recognize the data that you sent out.
- the easiest for parsing is to just run through the individual entries and check whether they make sense. "Start-pulse must be in range 55000-65000us., then we should have xyz, ...".
(edited)
floitsch
floitsch 05/17/2024 09:41 AM
I found a project that uses the ESP RMT to implement NEC. No idea if it actually works, but it looks ok.
https://github.com/husarnet/esp-idf/blob/master/examples/peripherals/rmt_nec_tx_rx/main/infrared_nec_main.c
Espressif ESP-IDF with added support for Husarnet. Contribute to husarnet/esp-idf development by creating an account on GitHub.
floitsch
floitsch 05/17/2024 09:42 AM
For example it has:
static void nec_tx_init() { rmt_config_t rmt_tx; rmt_tx.channel = RMT_TX_CHANNEL; rmt_tx.gpio_num = RMT_TX_GPIO_NUM; rmt_tx.mem_block_num = 1; rmt_tx.clk_div = RMT_CLK_DIV; rmt_tx.tx_config.loop_en = false; rmt_tx.tx_config.carrier_duty_percent = 50; rmt_tx.tx_config.carrier_freq_hz = 38000; rmt_tx.tx_config.carrier_level = 1; rmt_tx.tx_config.carrier_en = RMT_TX_CARRIER_EN; rmt_tx.tx_config.idle_level = 0; rmt_tx.tx_config.idle_output_en = true; rmt_tx.rmt_mode = 0; rmt_config(&rmt_tx); rmt_driver_install(rmt_tx.channel, 0, 0); }
(edited)
floitsch
floitsch 05/17/2024 09:43 AM
The code uses a CLK_DIV of 100, but, as I said earlier, 45 (or 90) feels more advantageous.
catdog8516
catdog8516 05/20/2024 05:36 AM
Thanks for that - I think I've made some progress:
* I connected the TX and RX together on the same board.
* I built up a single signals instance, and loaded it up with items according to the spec (I think).
* The TX signals look good (I think)
* I switched off the carrier signal to keep things simple.
* The RX signals are only 2-3 items long. The timing on the first signal item is almost always correct:

SEND: 121 178 TX: 17 items -- 1-16 0-8 0-1 0-1 1-3 1-3 1-3 1-3 0-0 0-0 1-3 1-3 1-1 1-1 1-1 1-1 0-0 1-1 RX: Reading... RX: 4 items - 1-16 0-10 1-12 0-0 SEND: 121 178 TX: 17 items -- 1-16 0-8 0-1 0-1 1-3 1-3 1-3 1-3 0-0 0-0 1-3 1-3 1-1 1-1 1-1 1-1 0-0 1-1 RX: Reading... RX: 4 items - 1-16 0-10 1-12 0-0
I used these Channel settings:
CLK-DIV := 45 IDLE-LEVEL := 0 IDLE-THRESHOLD-TICKS := 15000 FILTER-TICKS-THRESHOLD := 5
I tried a range of values for idle threshold and filter ticks, but they don't appear to have much affect on the receiver.

My test code essentially calls rx.start-reading followed by a short sleep, then tx.send signals, then rx.read.

I feel as though I'm close, but missing something fundamental..
floitsch
floitsch 05/20/2024 08:13 AM
You are sending extremely short signals (16, 8, 1, 3 ...). Since your clock is divided by 45, each tick is 0.5625us. However, NEC requires 562.5us. So you are a factor 1000 too fast.
Your short pulses are then filtered out by the filter ticks threshold which ignores everything that is shorter than 5 ticks.
You will need to use the threshold when you later add a carrier frequency. A carrier frequency of 38khz will add pulses of 1/(2*38000) = 13.15us (I think). In ticks that will be 13.15/.5625=~23.
So your threshold will need to be at least 24. Since your shortest signal will be 1000 ticks you could set it a bit higher, like 100 or so.
catdog8516
catdog8516 05/20/2024 08:19 AM
Hmm - so would I use a divider of 23 instead?
floitsch
floitsch 05/20/2024 08:20 AM
No. The divider is fine.
catdog8516
catdog8516 05/20/2024 08:20 AM
ah
floitsch
floitsch 05/20/2024 08:21 AM
With the carrier frequency on, you will need to change FILTER-TICKS-THRESHOLD 5
catdog8516
catdog8516 05/20/2024 08:21 AM
each pulse should be 1000 ticks
catdog8516OPcatdog8516
each pulse should be 1000 ticks
floitsch
floitsch 05/20/2024 08:21 AM
Correct
catdog8516
catdog8516 05/21/2024 05:46 AM
Hot dog!
TX: 68 items -- 1-16000 0-8000 1-1000 0-3000 1-1000 0-1000 1-1000 0-3000 1-1000 0-1000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-1000 1-1000 0-3000 1-1000 0-1000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-0 RX: Reading... RX: 68 items - 1-16000 0-8000 1-1000 0-3000 1-1000 0-1000 1-1000 0-3000 1-1000 0-1000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-1000 1-1000 0-3000 1-1000 0-1000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-3000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-1000 1-1000 0-0
πŸ₯³1
bitphlipphar
bitphlipphar 05/21/2024 06:21 AM
:πŸ₯³:
bitphlipphar
bitphlipphar 05/24/2024 05:43 AM
Impress your friends by turning their TV off with an ESP32! :😁:
catdog8516OPcatdog8516
floitsch
floitsch 05/24/2024 07:18 AM
Nice.
Were you able to test it with a real device (TV...)?
catdog8516
catdog8516 05/24/2024 08:03 AM
No.. I have a Sony TV, I'm assuming they use a different protocol.
catdog8516OPcatdog8516
No.. I have a Sony TV, I'm assuming they use a different protocol.
floitsch
floitsch 05/24/2024 08:13 AM
I wouldn't be surprised if they use NEC.
The nec database has some entries for Sony: https://github.com/probonopd/irdb/tree/master/codes/Sony/TV
One of the largest crowd-sourced, manufacturer-independent databases of infrared remote control codes on the web, and aspiring to become the most comprehensive and most accurate one - probonopd/irdb
floitsch
floitsch 05/24/2024 08:14 AM
It might be a slight variant, though.
Here is a document that could be relevant: https://faculty-web.msoe.edu/johnsontimoj/Common/FILES/sony_sirc_protocol.pdf
floitsch
floitsch 05/24/2024 08:15 AM
Also: you could try to capture the output of the Sony remote. If you have an IR sensor, you could use the receiver part of the package to see what the remote actually sends.
floitsch
floitsch 05/24/2024 08:16 AM
catdog8516
catdog8516 05/24/2024 08:57 AM
Excellent - I'll look into it
51 messages in total