← index #18331Issue #17097
Related · high · value 1.468
QUERY · ISSUE

ESP32: Ctrl-C from examples/bluetooth/ble_uart_repl.py does not interrupt user code; only the BLE REPL loop stops

openby mispacekopened 2025-10-26updated 2025-10-26
bug

Port, board and/or hardware

esp32 port, tested on ESP32C3 ESP32C6 and ESP32

MicroPython version

MicroPython v1.25.0 on 2025-04-15; ESP32C6 module with ESP32C6 but problem persist in older versions

Summary
When using the official examples/bluetooth/ble_uart_repl.py on ESP32, sending Ctrl-C (0x03) over BLE does not raise KeyboardInterrupt in a running user program. Instead, the BLE REPL itself stops or returns to the prompt while the user code keeps running. The same Ctrl-C works as expected over USB serial REPL.

Environment

  • Firmware: MicroPython v1.25.0 (build date 2025-04-15)
  • Boards tested: ESP32-C3 SuperMini, ESP32-C6 SuperMini and ESP32 Dev Board
  • Host: Windows 10 Chrome (Web Bluetooth), Android Adafruit Bluefruit LE Connect

Reproduction

Copy the three example files to the ESP32.
change:
self._payload = advertising_payload(name=name, appearance=_ADV_APPEARANCE_GENERIC_COMPUTER)
to
self._payload = advertising_payload(name=name, services=[_UART_UUID])
in ble_uart_peripheral.py because online terminals not working without NUS characteristics

Start BLE REPL:

import ble_uart_repl
ble_uart_repl.start()

Now you can connect to device. For example using https://repl.siliconwitchery.com/

Paste simple code into terminal

import utime

i = 0

for i in range(1, 1000):
  utime.sleep_ms(0)  
  print(i)
  utime.sleep_ms(10)

And try to stop code by sending CTRL+C, sometimes code stop correctly, but sometimes BLE Code stop working and numbers still counting.

Expected behaviour

KeyboardInterrupt is raised in the running user loop and execution stops, matching USB serial REPL behavior.

Observed behaviour

The BLE REPL appears to stop working but the user loop continues printing. Ctrl-C does not interrupt the user program. A hard reset is required to stop it.

Additional Information

What we tried

  • Verified that USB serial REPL on the same firmware raises KeyboardInterrupt as expected.
  • Verified the central does send a single 0x03 byte to the RX characteristic.

Technicaly same code without one line of code works much better, why ?

import utime

i = 0

for i in range(1, 1000):    
  print(i)
  utime.sleep_ms(10)

utime.sleep_ms(0) before print making a huge difference but code is still not 100% reliable

Code of Conduct

Yes, I agree

CANDIDATE · ISSUE

micropython.kbd_intr(-1) not working on ESP32*

closedby xuancong84opened 2025-04-09updated 2025-04-10
bug

Port, board and/or hardware

esp32, esp32c3, esp32*

MicroPython version

MicroPython v1.24.1 on 2024-11-29; ESP32C3 module with ESP32C3
Type "help()" for more information.

Reproduction

Disabling CTRL+C interrupt is very important and useful because it allows STDIO ports to be used as UART ports for interaction with external devices. On ESP32*, REPL cannot be detached by os.dupterm(None,...), we have to use sys.stdin.buffer (which is the same as sys.stdout.buffer) as UART to communicate with other devices.

Under such a scenario, the transmitted data can contain arbitrary bytes including b'\x03' which means CTRL+C and it should not interrupt the execution of the program. micropython.kbd_intr(-1) is designed for such a purpose. However, it does not work on the latest MicroPython builds for ESP32*:

>>> micropython.kbd_intr(-1)         
>>> while True:pass                  
...                                  
Traceback (most recent call last):   
  File "<stdin>", line 1, in <module>
KeyboardInterrupt:                   

No matter whether the code is run in REPL terminal or not, CTRL+C will always interrupt the execution. The reason is because in parse_compile_execute() in pyexec.c, every time it will reset interrupt_char to CHAR_CTRL_C:

if (!(exec_flags & EXEC_FLAG_NO_INTERRUPT)) {
    mp_hal_set_interrupt_char(CHAR_CTRL_C);
}
#if MICROPY_REPL_INFO
start = mp_hal_ticks_ms();
#endif
mp_call_function_0(module_fun);
mp_hal_set_interrupt_char(-1); // disable interrupt
mp_handle_pending(true); // handle any pending exceptions (and any callbacks)
nlr_pop();
ret = 1;
if (exec_flags & EXEC_FLAG_PRINT_EOF) {
    mp_hal_stdout_tx_strn("\x04", 1);
}

Expected behaviour

>>> micropython.kbd_intr(-1)         
>>> while True:pass                  
...            

By right, pressing CTRL+C should not interrupt the execution, it should stuck forever.

Observed behaviour

>>> micropython.kbd_intr(-1)         
>>> while True:pass                  
...                                  
Traceback (most recent call last):   
  File "<stdin>", line 1, in <module>
KeyboardInterrupt:                   

Additional Information

My commit https://github.com/xuancong84/micropython/commit/8b88a14f53bf27f74f761f8e8f386dacc8d843c7 resolves this issue by adding micropython.kbd_intr_enable(bool). However, my understanding of the full source code is quite limited, there might be a more sophisticated solution.

Code of Conduct

Yes, I agree

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied