Home icon indicating copy to clipboard operation
Home copied to clipboard

Issues with RMT TransmitterChannel and ReceiverChannel

Open NazzaN opened this issue 4 months ago • 16 comments

Library/API/IoT binding

nanoFramework.Hardware.Esp32.Rmt

Visual Studio version

VS2022

.NET nanoFramework extension version

2022.14.1.2

Target name(s)

ESP32_S3_BLE, XIAO_ESP32C3, ESP32_C3_REV3, ESP32_C6_THREAD

Firmware version

1.14.0.59

Device capabilities

No response

Description

Hi, I found a probably issue on nanoFramework.Hardware.Esp32.Rmt v2.0.35

with this code:

                var rxChannelSettings = new ReceiverChannelSettings(3)
                {
                    // 1us clock ( 80Mhz / 80 ) = 1Mhz
                    //ClockDivider = 80,
                    // no filter
                    EnableFilter = true,
                    FilterThreshold = 100,
                    // max time 1us clock
                    //IdleThreshold = ushort.MaxValue,
                    IdleThreshold = 40_000,
                    // timeout after 1 second
                    ReceiveTimeout = TimeSpan.FromSeconds(1),
                    EnableDemodulation = false
                };
               using var rxChannel = new ReceiverChannel(rxChannelSettings);

I receive:

++++ Exception System.Exception - CLR_E_DRIVER_NOT_REGISTERED (3) ++++
++++ Message: 
++++ nanoFramework.Hardware.Esp32.Rmt.ReceiverChannel::NativeRxInit [IP: 0000] ++++
++++ nanoFramework.Hardware.Esp32.Rmt.ReceiverChannel::.ctor [IP: 001a] ++++

on ESP32_S3_BLE XIAO_ESP32C3 ESP32_C3_REV3 ESP32_C6_THREAD

workaround is to manualy set the channel

   var rxChannelSettings = new ReceiverChannelSettings(3)
                {
                    EnableFilter = true,
                    FilterThreshold = 100,
                    IdleThreshold = 40_000,
                    ReceiveTimeout = TimeSpan.FromSeconds(1),
                    //Inserting this work but non on all boards
                    **Channel = 4,**
                    EnableDemodulation = false
                };
               using var rxChannel = new ReceiverChannel(rxChannelSettings);

but this work only on ESP32_S3_BLE non on C3 and not on C6

this problem affect also other classes using Esp32 Rtm: Iot.Device.DHTxx.Esp32.DhtBase constructor (workaround ok only on S3)

[nanoFramework.IoT.Device/DhtBase.csdevices/Dhtxx.Esp32/DhtBase.cs]https://github.com/nanoframework/nanoFramework.IoT.Device/blob/develop/devices/Dhtxx.Esp32/DhtBase.cs

NeoPixel.NeopixelChain (affected only C3 and C6) this sample is using TransmitterChannel samples/Hardware.Esp32.Rmt/NeoPixelStrip/NeoPixel/NeopixelChain.cs https://github.com/nanoframework/Samples/blob/main/samples/Hardware.Esp32.Rmt/NeoPixelStrip/NeoPixel/NeopixelChain.cs

I find the exception on C++ code but I've not IDE and skills to investigate: ....nanoFramework.Hardware.Esp32.Rmt/nanoFramework_hardware_esp32_rmt_native_nanoFramework_Hardware_Esp32_Rmt_ReceiverChannel.cpp::NativeRxInit___I4 ....nanoFramework.Hardware.Esp32.Rmt/nanoFramework_hardware_esp32_rmt_native_nanoFramework_Hardware_Esp32_Rmt_TransmitterChannel.cpp::NativeTxInit___I4

Thanks for attention.

N.

How to reproduce

Try to use Dht22 with Dhtxx.Esp32 or initialize ReceiverChannelSettings without an explicit channel in settings. For XIAO_ESP32C3, ESP32_C3_REV3, ESP32_C6_THREAD: Try to initialize ReceiverChannelSettings with or without an explicit channel in settings.

Expected behaviour

No response

Screenshots

No response

Sample project or code

  var rxChannelSettings = new ReceiverChannelSettings(3)
                {
                    // 1us clock ( 80Mhz / 80 ) = 1Mhz
                    //ClockDivider = 80,
                    // no filter
                    EnableFilter = true,
                    FilterThreshold = 100,
                    // max time 1us clock
                    //IdleThreshold = ushort.MaxValue,
                    IdleThreshold = 40_000,
                    // timeout after 1 second
                    ReceiveTimeout = TimeSpan.FromSeconds(1),
                    EnableDemodulation = false
                };
               using var rxChannel = new ReceiverChannel(rxChannelSettings);

Aditional information

XIAO_ESP32C3, ESP32_C3_REV3, ESP32_C6_THREAD has problem with TransmitterChannel and ReceiverChannel

NazzaN avatar Oct 26 '25 16:10 NazzaN

Have you setup the pins properly and checked they can be used for this RMT function for the C3 and C6? See: https://docs.espressif.com/projects/esp-idf/en/stable/esp32c3/api-reference/peripherals/gpio.html (and similar for the C6)

Ellerbach avatar Oct 27 '25 11:10 Ellerbach

Have you setup the pins properly and checked they can be used for this RMT function for the C3 and C6? See: https://docs.espressif.com/projects/esp-idf/en/stable/esp32c3/api-reference/peripherals/gpio.html (and similar for the C6)

Yes, I tried all pin but no one is working for C3 and C6.

I add: also this this parameter in esp32 s3 cause exception and must be commented to work: //ClockDivider = 80,

Tell me if can do some more tests.

NazzaN avatar Oct 27 '25 21:10 NazzaN

Yes, I tried all pin but no one is working for C3 and C6.

How are you setting the pins in your code?

Ellerbach avatar Oct 28 '25 16:10 Ellerbach

Yes, I tried all pin but no one is working for C3 and C6.

How are you setting the pins in your code?

var rxChannelSettings = new ReceiverChannelSettings(0)

tried also

var rxChannelSettings = new ReceiverChannelSettings(0) { PinNumber = 0, ....

NazzaN avatar Oct 28 '25 18:10 NazzaN

0 in that case is the pin number. Have you tried any other pin number? I think 0 is not the best to use on a C3 like 4, 5, 8, 9?

Ellerbach avatar Oct 29 '25 14:10 Ellerbach

0 in that case is the pin number. Have you tried any other pin number? I think 0 is not the best to use on a C3 like 4, 5, 8, 9?

Yes I tried. I've an update: The problem seem to be related to the channel on C3 and C6 only setting channel to 2 or to 3 is working. From https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32c3/api-reference/peripherals/rmt.htm

The RMT has four channels numbered from zero to three. The first half (i.e. Channel 0 ~ 1) channels can only be configured for transmitting, and the other half (i.e. Channel 2 ~ 3) channels can only be configured for receiving.

I've also found (this can be inserted in documentation of nanoframework, I think is usefull):

The ESP32 has 8 channels, each of them can be either receiver or transmitter The ESP32-C3 has 4 channels, Channel<0> and Channel<1> hardcoded for transmitting signals and Channel<2> and Channel<3> hardcoded for receiving signals. The ESP32-C6 has 4 channels, Channel<0> and Channel<1> hardcoded for transmitting signals and Channel<2> and Channel<3> hardcoded for receiving signals. The ESP32-H2 has 4 channels, Channel<0> and Channel<1> hardcoded for transmitting signals and Channel<2> and Channel<3> hardcoded for receiving signals. The ESP32-S2 has 4 channels, each of them can be either receiver or transmitter. The ESP32-S3 has 8 channels, Channel<0>-Channel<3> hardcoded for transmitting signals and Channel<4>-Channel<7> hardcoded for receiving signals.

all this is explain the problem and difference of working channels I found between C3-C6/S3

Now the open points are:

  • without an explicit channel the constructor for TransmitterChannel and ReceiverChannel fails often
  • ClockDivider = 80 in the ChannelSettings cause exception, I will investigate a bit more on this.

Thanks for support.

NazzaN avatar Oct 29 '25 16:10 NazzaN

@NazzaN thank you very much for all your testing and investigation on this issue. Yes, agreed that this should be in the documentation. Most obvious location seems to be in the repo README. Following that, it's probably better to update the IntelliSense comment on the constructor to point to the readme for details. I suppose that you are able to build the fw locally in order to test your changes/fixes to the clock divider...

josesimoes avatar Oct 29 '25 16:10 josesimoes

@NazzaN thank you very much for all your testing and investigation on this issue. Yes, agreed that this should be in the documentation. Most obvious location seems to be in the repo README. Following that, it's probably better to update the IntelliSense comment on the constructor to point to the readme for details. I suppose that you are able to build the fw locally in order to test your changes/fixes to the clock divider...

I can do some check on C# code but this logic in on cpp: /nanoFramework_hardware_esp32_rmt_native_nanoFramework_Hardware_Esp32_Rmt_ReceiverChannel.cpp

there is also code for all specific boards, maybe something is changed in sdk, I've not skills to fix this

NazzaN avatar Oct 29 '25 19:10 NazzaN

there is also code for all specific boards, maybe something is changed in sdk, I've not skills to fix this

+1, thanks a lot for the investigation! Before the fix, maybe just the update on the README from the repo and a special note about the C3/C6 will be a great start! Now, with the history of the conversation in this issue, it will allow for a later fix.

Ellerbach avatar Oct 30 '25 08:10 Ellerbach

@NazzaN understood! No worries, we'll take it from here. You've already done a lot of work digging into this. 👌🏻

josesimoes avatar Oct 30 '25 10:10 josesimoes

@NazzaN understood! No worries, we'll take it from here. You've already done a lot of work digging into this. 👌🏻

It's a great project, I'm happy to help in some way!

NazzaN avatar Oct 30 '25 14:10 NazzaN

Hey all, I just hit this bug with the HC-SR04 library on this ESP32 board:

System Information
HAL build info: nanoCLR running @ ESP32 built with ESP-IDF v5.4.2
  Target:   ESP32_REV0
  Platform: ESP32

Firmware build Info:
  Date:        Nov  1 2025
  Type:        MinSizeRel build, chip rev. >= 0, without support for PSRAM
  CLR Version: 1.14.0.92
  Compiler:    GNU ARM GCC v14.2.0

OEM Product codes (vendor, model, SKU): 0, 0, 0

Serial Numbers (module, system):
  00000000000000000000000000000000
  1000000000C8C9A3D19DEC

Target capabilities:
  Has nanoBooter: NO
  IFU capable: NO
  Has proprietary bootloader: YES

HC-SR04 has a dependency on the ESP32 RMT library and, by default, does not specify a channel number. The RMT library, as far as I can recall, should attempt to find the next available channel.

My whole dev environment was nuked previously so it'll take me a bit of time to get set up again with NF and investigate this but I wanted to let you know that I'm looking into this.

MrCSharp22 avatar Nov 02 '25 06:11 MrCSharp22

It appears that the RMT drivers that nf uses currently have been "deprecated". The docs are referencing new types and the ones used by nf are now moved to /driver/deprecated/rmt_legacy.c.

I am not 100% sure this is why we're seeing this error but I can't rule it out yet.

MrCSharp22 avatar Nov 02 '25 12:11 MrCSharp22

@MrCSharp22 thanks for reporting this. And yes, there is already an open issue to track the migration to the new versions.

josesimoes avatar Nov 03 '25 09:11 josesimoes

After further investigation, looks like there are 2 issues here:

  1. Dedicated TX/RX RMT Channels

As per @NazzaN's comment, some boards have dedicated (potentially exclusive) channels for TX and RX.

The current automatic channel allocation logic in the native nf RMT lib does not account for this fact.

CLR_INT32 Library_nanoFramework_hardware_esp32_rmt_native_nanoFramework_Hardware_Esp32_Rmt_RmtChannel::FindNextChannel()
{
    for (signed int channel = RMT_CHANNEL_0; channel < RMT_CHANNEL_MAX; ++channel)
    {
        if (registredChannels.find(CHANNEL(channel)) == registredChannels.end())
        {
            return channel;
        }
    }
    return -1;
}

I believe we should update this code to handle these cases appropriately. I think we can set specific flags per device target to control how the channel allocation logic in this method can behave.

  1. GPIO Pin Restrictions

(This is what I ran into above)

ESP's RMT driver allows only specific GPIO pins to be used for input/output.

soc_caps.h has the following macro which rmt_set_gpio(...) in rmt_legacy.c uses to validate the output pin:

#define SOC_GPIO_VALID_OUTPUT_GPIO_MASK (SOC_GPIO_VALID_GPIO_MASK & ~(0ULL | BIT34 | BIT35 | BIT36 | BIT37 | BIT38 | BIT39))

This means any GPIO pin >= 34 is considered input-only and cannot be used for RMT TX.

I see 2 possible ways we can handle this:

  • throw an exception from the managed library but might not be very helpful considering we don't allow string messages to keep RAM usage low.
  • or; explain this restriction in the managed library's README file.

I guess the good news here is that we can roll out a fix for this fairly quickly and it should have no impact on backwards compatibility.

MrCSharp22 avatar Nov 03 '25 13:11 MrCSharp22

Hey all. I've opened a PR for the GPIO pin restrictions. I'll work on the dedicated RMT channels later this week when I have a bit more time on my hands. Cheers :)

MrCSharp22 avatar Nov 05 '25 10:11 MrCSharp22