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

Attach pin interrupt with framework esp-idf resulting in LoadProhibited panic #2483

Closed
mmakaay opened this issue Sep 27, 2021 · 5 comments · Fixed by esphome/esphome#2416
Closed

Comments

@mmakaay
Copy link
Member

mmakaay commented Sep 27, 2021

The problem

When using a component that attaches an interrupt to a GPIO pin, a panic reboot is triggered.

I noticed when porting my own component code to the 2021.10.0-dev branch, but I can reproduce the behavior with other components as well, for example rotary_encoder.

[22:52:40][C][rotary_encoder:127]: Setting up Rotary Encoder 'Rotary Encoder'...
[22:52:40][V][esp-idf:Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.
[22:52:40]
[22:52:40]Core  0 register dump:
[22:52:40]PC      : 0x40174637  PS      : 0x00060833  A0      : 0x800fa0ec  A1      : 0x3ffbb160  
[22:52:40]A2      : 0x00000000  A3      : 0x00000000  A4      : 0x00000020  A5      : 0x3ffb0020  
[22:52:40]A6      : 0x00000000  A7      : 0x00400000  A8      : 0x800f9b34  A9      : 0x3ffbb130  
[22:52:40]A10     : 0x3ff44000  A11     : 0x00000010  A12     : 0x00060420  A13     : 0x00000140  
[22:52:40]A14     : 0x3f406fa4  A15     : 0x00000016  SAR     : 0x00000010  EXCCAUSE: 0x0000001c  
[22:52:40]EXCVADDR: 0x00000000  LBEG    : 0x4000c2e0  LEND    : 0x4000c2f6  LCOUNT  : 0xffffffff  
[22:52:40]
[22:52:40]Backtrace:0x40174634:0x3ffbb160 0x400fa0e9:0x3ffbb180 0x400db75b:0x3ffbb1b0 0x400e0eb1:0x3ffbb1d0 0x400e0ed0:0x3ffbb1f0 0x40173be5:0x3ffbb210 0x40173c75:0x3ffbb230 0x400e97e6:0x3ffbb250 0x400f0cdc:0x3ffbb290 0x400db67f:0x3ffbbb60
[22:52:40]
[22:52:40]
[22:52:40]ELF file SHA256: e5cb7cba45dca6e7
[22:52:40]
[22:52:40]Rebooting...

Which version of ESPHome has the issue?

2021.10.0-dev

What type of installation are you using?

pip

Which version of Home Assistant has the issue?

No response

What platform are you using?

ESP32

Board

ESP32-WROOM-32D

Component causing the issue

rotary_encoder

Example YAML snippet

esp32:
  board: esp32doit-devkit-v1
  framework:
    type: esp-idf
    sdkconfig_options:
      CONFIG_FREERTOS_UNICORE: y

sensor:
  - platform: rotary_encoder
    name: "Rotary Encoder"
    pin_a: GPIO16
    pin_b: GPIO17

Anything in the logs that might be useful for us?

As you can see in the YAML config, the ESP32 chip that I'm using has only one core. Given the back trace, which breaks on a call that looks up the CPU on which to setup the interrupt, I figured that this might be specific for a unicore build.

Backtrace of the crash:

x40174634: esp_intr_get_cpu at .../packages/framework-espidf/components/esp_system/intr_alloc.c line 719
0x400fa0e9: gpio_isr_handler_add at .../packages/framework-espidf/components/driver/gpio.c line 484
0x400db75b: esphome::esp32::IDFInternalGPIOPin::attach_interrupt(void (*)(void*), void*, esphome::gpio::InterruptType) const at src/esphome/components/esp32/gpio_idf.h line 83
0x400e0eb1: esphome::rotary_encoder::RotaryEncoderSensor::setup() at src/esphome/core/gpio.h line 80
0x40173be5: esphome::Component::call_setup() at src/esphome/core/component.cpp line 67
0x40173c75: esphome::Component::call() at src/esphome/core/component.cpp line 78
0x400e97e6: esphome::Application::setup() at src/esphome/core/application.cpp line 38
0x400f0cdc: setup() at device.yaml line 298
0x400db67f: esphome::loop_task(void*) at src/esphome/components/esp32/core.cpp line 71


### Additional information

_No response_
@mmakaay
Copy link
Member Author

mmakaay commented Sep 28, 2021

Comparing the gpio code in ESPHome for esp-idf with the documentation for esp-idf, I don't see any obvious things that are wrong (unfortunately). I'll start on working backwards from the point where the panic occurs, to find out what NULL pointer we're looking at here.

@mmakaay
Copy link
Member Author

mmakaay commented Sep 28, 2021

Not a fun thing to debug, but I got a step further:

  • In gpio_isr_handler_add(), the function esp_intr_get_cpu() is called.
  • It gets gpio_context.gpio_isr_handle as its argument, but that one is NULL.
  • Within esp_intr_get_cpu() this results in the NULL pointer issue that I see.

@mmakaay
Copy link
Member Author

mmakaay commented Sep 28, 2021

One more step. The call to gpio_install_isr_service(ESP_INTR_FLAG_LEVEL5) is failing on my device.
But failure is not an option according to the calling code (the return value is not checked against ESP_OK).
So there's two points of attention from here on:

  • Do check the return code and make sure that the failure gets reported and that attach_interrupt() is aborted.
  • Find out why this call is failing.

@mmakaay
Copy link
Member Author

mmakaay commented Sep 28, 2021

The call returns error code 261 (0x105), which is ESP_ERR_NOT_FOUND.

The only reference to that value in framework-espidf/components/driver/gpio.c can be found in:

esp_err_t gpio_isr_register(void (*fn)(void *), void *arg, int intr_alloc_flags, gpio_isr_handle_t *handle)
{
    // -----8<------ cut non-interesting code

#if CONFIG_FREERTOS_UNICORE
    gpio_isr_register_on_core_static(&p);
    ret = ESP_OK;
#else /* CONFIG_FREERTOS_UNICORE */
    ret = esp_ipc_call_blocking(gpio_context.isr_core_id, gpio_isr_register_on_core_static, (void *)&p);
#endif /* !CONFIG_FREERTOS_UNICORE */
    if(ret != ESP_OK || p.ret != ESP_OK) {
        return ESP_ERR_NOT_FOUND;
    }
    return ESP_OK;
}

I verified that the CONFIG_FREERTOS_UNICORE define is actually used, so the conclusion is that gpio_isr_register_on_core_static(&p) sets p.ret to something else than ESP_OK.

@mmakaay
Copy link
Member Author

mmakaay commented Sep 28, 2021

What fails for gpio_isr_register_on_core_static(&p) is the call to esp_intr_alloc(...).
I found the argument error that occurs for this function (unfortunately esp-idf does not log this, even when turning on early logging for the intr_alloc component :-/).

The issue is that the ESP_INTR_FLAG_HIGH flag is set, while no assembly handler is used. The esp_intr_alloc.h file specifies for that flag:

< High level interrupts. Need to be handled in assembly.

This flag matches as a direct result of using the flag ESP_INTR_FLAG_LEVEL5 in the call to esp_intr_alloc(...).
When using a handler defined in C-code, the maximum level that can be used is ESP_INTR_FLAG_LEVEL3.

I changed the esphome code to adhere to this constraint and TADA! 🎉
My device is now registering its interrupt handler correctly and I the interrupts work as expected.

Great, that was a fun challenge.
One of those cases where chasing the bug takes a lot of work, but the actual fix is a single line change 😄

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

Successfully merging a pull request may close this issue.

1 participant