-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Update esp32.RMT module to the new API #16293
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
base: master
Are you sure you want to change the base?
Conversation
6b14d89
to
1cb1e6c
Compare
9247d15
to
77ed23a
Compare
Thank you. I have been trying to figure this out but with little success. When working with neopixel strips I noticed that the legacy RMT IDF4 + Bitstream has some oddness (extra bytes, noise) and the legacy bitbang is erratic on startup. With C++ and ESPIDF I get good results with RMT IDF 5, but haven't been able to integrated with micropython or bitbang. If I can help with testing, let me know. Thanks again |
@ricksorensen testing would be great, since my use cases are limited here (433MHz key fob emulator). At least in this application, it has been working perfectly. The only known showstopper is calling RMT.disable() while transmitting, sometimes it panics with a WDT interrupt timeout, but only on ESP32, not in an ESP32-C3 board. Smeels more like an ESP-IDF issue. |
@elvis-epx I can get some evaluation done at the end of this week.... fingers crossed. IDF 5.2.2 is the plan (I can try others, but I have some issues with 5.2.3 and 5.3.1 that I have not resolved yet) Today (Friday 20241129 CST) I ran a few simple experiments. Boards:
I then tried simple
wiith subsequent writes giving A few experiments seem to indicate that if number of bytes sent is 1 or 2 then the timeout occurs. On the C3 RMT printed some more debug information:
I will try some other experiments at end of weekend possibly. Thanks again- Rick |
@ricksorensen Have pushed an additional commit 9cdb7dd that should fix at least items 2.1 and 3. We weren't waiting on TX completion for long enough and were treating the timeout as a fatal error, not running the cleanup code lines right after. |
@elvis-epx Thank you all, this is a great feature update. hope it can be released in the stable version soon. Does the ESP32-C3 require specific IO pins to properly use the RMT function for receiving infrared signals? |
As far as I know, any pin capable of input can do RMT. I have only one C3 (devkit-rust-1) and play with the onboard led pin (Pin 7). |
@ricksorensen good catch, one possible workaround is to keep the channel for the bitstream after the first use (do not delete it, so not calling rmt_del_channel() and not exercising the offending behavior). |
Yes that is a work around- to store and save the allocated RTM channel for
a bitstream. Can you change the pin assigned to an active RTM channel?
Maybe (extreme pseudo code:)
if no bitstream RTM, instantiate one
else assign new pin to RTM
transfer data
do not delete/disable channel/encoder/ .
Add new function to release bitstream.
…On Wed, Dec 4, 2024 at 5:50 PM Elvis Pfützenreuter ***@***.***> wrote:
@ricksorensen <https://github.com/ricksorensen> good catch, one possible
workaround is to keep the channel for the bitstream after the first use (do
not delete it, so not calling rmt_del_channel() and not exercising the
offending behavior).
—
Reply to this email directly, view it on GitHub
<#16293 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA5QPQESRXQNHGRRCVWRZKD2D6IN7AVCNFSM6AAAAABSLVHTG6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMJYG44TCMBXGU>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
--
Rick Sorensen
***@***.***
651-470-5511
|
@ricksorensen no, if the pin changes, we need to release the channel and create another one, so there is extra logic. We can try calling esp_rom_gpio_connect_out_signal() before rmt_del_channel(), if it works, it is a cheaper solution. |
The RTM class seems to work well-
Worth trying again, but I believe the problem is the pin reset in the
del_channel function which adds PULL_UP to the pin. Maybe if the
micropython API is to allow independent bitstream we could override the
del_channel function and remove the pin reset?
Most boards use bitbanging for bitstream I believe. The micropython
bitbang for ESP32 seems to work- but it has a timing glitch on the first
few pulses--- maybe from the RTOS? I have been fiddling with the bitbang
too but have not figured out how to get the timing on the first few bit
pulses correct.
EDIT: I just noticed that the rmt_del_channel on ESP IDF 5.3.1 does NOT reset the pin. Unfortunately I cannot get 5.3.1 to work for my builds yet (not knowledgable enough on esp tools).. I have changed the rmt_common.c function on 5.2.2 to remove the reset and will test tomorrow.
…On Wed, Dec 4, 2024 at 6:34 PM Elvis Pfützenreuter ***@***.***> wrote:
@ricksorensen <https://github.com/ricksorensen> no, if the pin changes,
we need to release the channel and create another one, so there is extra
logic. We can try calling esp_rom_gpio_connect_out_signal() before
rmt_del_channel(), if it works, it is a cheaper solution.
—
Reply to this email directly, view it on GitHub
<#16293 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA5QPQD4VAJNSHS4H4NETFL2D6NSXAVCNFSM6AAAAABSLVHTG6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMJYHAZTSMZVGA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
--
Rick Sorensen
***@***.***
651-470-5511
|
@ricksorensen nice, so this moves the ball to ESP-IDF court. It is not the only issue stemming from ESP-IDF in this PR (see FIXMEs). Have tried changing the position of esp_rom_gpio_connect_out_signal() without success (nothing changes or the problem is worsened). Delaying channel release is also not a perfect solution because a) suppose a use case where 2 or more pins are bitstream'ed in parallel b) the pull up still happens, albeit in a delayed moment, so it would only solve the problem for the rather narrow case that only 1 pin is bitstream'ed. |
I have an ad hoc work around which only affects
EDIT: @elvis-epx I think I figured out how to include the
I have not verified the 5.3.1 config. |
I have keen anticipation for this as my application requires high speed temporal dithering of dozens of neopixels. A solution that does not block the main core while data is streaming is critical. Any solution that also minimizes setup and teardown associated with each stream is ideal, as there need to be hundreds of streams per second for flicker-free dithering Thank you for your efforts. |
@seanmpuckett Rember there are only a finite number of RMT channels- I believe 8 on ESP32 and ESP32S3 and only 2 on ESP32C3 (single mcu too) . |
6bcd481
to
4d77fa6
Compare
@ricksorensen pushed additonal commit with your suggested changes, confirmed they fixed the issue here as well. |
@ricksorensen tested with ESP-IDF 5.3.2 with the help of PR #15733. The long pulse problem reappears since gpio_reset_pin() was moved from rmt_common.c to rmt_tx.c, and calling base->destroy() directly no longer avoids it. |
Ugh. I will look at it too later this week. Thanks again
…On Mon, Dec 9, 2024, 3:38 PM Elvis Pfützenreuter ***@***.***> wrote:
@ricksorensen <https://github.com/ricksorensen> tested with ESP-IDF 5.3.2
with the help of PR #15733
<#15733>. The long pulse
problem reappears since gpio_reset_pin() was moved from rmt_common.c to
rmt_tx.c, and calling base->destroy() directly no longer avoids it.
Screenshot.from.2024-12-09.18-26-13.png (view on web)
<https://github.com/user-attachments/assets/283d53b7-8b95-49f3-b968-f26f38f293e5>
—
Reply to this email directly, view it on GitHub
<#16293 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA5QPQBG4HCLQEIFIZ22RST2EYEV3AVCNFSM6AAAAABSLVHTG6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMRZGU3TMNZWGM>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Signed-off-by: Elvis Pfutzenreuter <elvis.pfutzenreuter@gmail.com>
Signed-off-by: Elvis Pfutzenreuter <elvis.pfutzenreuter@gmail.com>
No point in trying to calculate a maximum safe waiting time. Signed-off-by: Elvis Pfutzenreuter <epxx@epxx.co>
The commit [1] fixed issues when mpy was moved from core 1 to core 0, but since then it has been moved back to core 1. Fixes a issue with the new RMT API: hangups when a RMT channel is repeatedly created and released (e.g. calling bitstream in a tight loop). [1] micropython@e754c2e Signed-off-by: Elvis Pfutzenreuter <epxx@epxx.co>
Workaround collaborated by @ricksorensen for an issue in ESP-IDF < 5.3 where channel deletion pulls the logic level HI. Signed-off-by: Elvis Pfutzenreuter <epxx@epxx.co>
@elvis-epx It was long week I guess. I finally got around to rechecking this, and tried with IDF 5.4. The extended high pulse seems to be gone again! For my builds: IDF 5.2.2 Looks good, no high pulse at end I have a micropython clone, created a branch with your updates for RMT and synched to the 20250224 micropython version. I tried XIAO C3 and XIAO S3 with same results for each. Here is my playing repository https://github.com/ricksorensen/micropython.git and the branch is Here is my test code: import sys
import platform
import machine
import neopixel
import time
pinu = None
pf = sys.platform
if "samd" in pf:
pinu = machine.Pin("A0_D0", mode=machine.Pin.OUT)
if "rp2" in pf:
pinu = machine.Pin("GP26")
if "esp32" in pf:
if "S3" in sys.implementation._machine:
pinu = machine.Pin(1)
if "C3" in sys.implementation._machine:
pinu = machine.Pin(2)
print(" platform: ", sys.platform)
print(" version: ", sys.version)
print(" implementation: ", sys.implementation)
print(" build: ", platform.platform())
print(" Pin ", pinu)
# t=(400, 850, 800, 450) 1.25 uS/bit
def t1(b, t=(400, 850, 800, 450)):
global pinu
machine.bitstream(pinu, 0, t, b)
# 3 pixels, 24 bits/pixel, 72bits total
# 90uS @ 1.25uS/bit
pixt = [
(0xFF, 0x00, 0x55),
(0x00, 0xAA, 0xFF),
(0x55, 0xFF, 0xAA),
]
b6 = b"abcdef" # 6*8 = 48 bits 60uS @ 1.25uS/bit
b300 = bytearray(300) # 300*8 = 2400 bits, 3000uS=3ms @ 1.25uS/bit
p = neopixel.NeoPixel(pinu, len(pixt))
for i in range(len(p)):
p[i] = pixt[i]
# total: nc*( 65 us + 100us) = nc*165us
# nc=4 --> 660us
def runall(nc=4):
n = nc
while n > 0:
t1(b6)
# time.sleep_us(5)
n = n - 1
n = nc
while n > 0:
p.write()
# time.sleep_us(10)
n = n - 1 |
@ricksorensen so I guess we can remove the "RFC" off the title and consider it worthy? |
I think it can be a full pull request. Caveat is removal of bitbanging mode. Thanks again. |
Summary
The current esp32.RMT module uses a legacy API from ESP-IDF 4.x. The ESP-IDF 5.x offers a new API, which is overall better, and easier to implement the RX side in the future. This PR updates the module and the documentation, preserving the current MicroPython RMT API as much as possible.
The bitstream RMT implementation was updated as well, since ESP-IDF does not allow an image to reference legacy and new APIs at the same time (it resets right after boot with an error message, even if neither module is imported).
Once this PR is accepted, we plan to submit a follow-up PR to add RX capabilities to the RMT module. We already have working code for it, see [1].
[1] https://github.com/elvis-epx/micropython/blob/rmt_rx/ports/esp32/esp32_rmt2.c
Testing
Tested on ESP32, ESP32-S3, ESP32-C3 and ESP32-C6. (Commenters may have tested on other variants.) We have tested all aspects of the RMT API and all seem to work fine.
Trade-offs and Alternatives
The RMT channel used by bitstream cannot be a simple number anymore since the channel is an opaque struct in the new API. In this PR, we allocate and deallocate a channel every time bitstream is used, and this may fail in case all RMT channels are in use.
In this PR, bitstream no longer falls back to bitbanging (should it, anyway?). A possible change is to reserve a RMT channel just for bitstream i.e. create it upon boot and leave it there, or reserve it upon first usage of bitstream.