-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Bug Report: alarm.pin.PinAlarm Mis-maps to Incorrect Pins on ESP32 Feather
Description
alarm.pin.PinAlarm consistently fails to wake the board on the configured pin. Instead, it wakes up on a completely different, incorrect, and seemingly random pin. This behavior has been observed with multiple confirmed RTC-capable GPIOs and appears to be a fundamental bug in the core alarm module's handling of the PinAlarm configuration.
Board Information
Board Name: Adafruit Feather HUZZAH32 with ESP32 (identified via serial console)
CircuitPython Version: 9.2.8 on 2025-05-28
CircuitPython Build: CircuitPython for the Adafruit Feather HUZZAH32 with ESP32
Hardware Setup
Adafruit Feather HUZZAH32 with ESP32
Adafruit Adalogger FeatherWing (with PCF8523 RTC) and microSD card inserted.
A wire connected from the INT pin on the Adalogger FeatherWing to the specified GPIO on the Feather board.
No other connections were made to the tested pins. The RTC INT output is an open-drain, active-low signal.
Reproduction Steps
Flash the above-mentioned CircuitPython firmware to the board.
Connect a wire from the Adalogger FeatherWing INT pin to the GPIO pin to be tested.
Upload the provided code.py to the board, ensuring the RTC_INTERRUPT_PIN constant is set to the correct pin.
Open the serial console to monitor the output.
Observe the wake-up behavior after the board enters deep sleep.
Expected Behavior
The board should wake up from deep sleep, and the alarm.wake_alarm.pin should report the exact pin that was configured in the alarm.pin.PinAlarm call.
Actual Behavior (Examples)
In all three tests, the PinAlarm wakes the board on a different pin.
Test 1: Configured GPIO32
Configured Pin: microcontroller.pin.GPIO32 (board.D32)
Actual Wakeup Pin: microcontroller.pin.GPIO9
Serial Output (from user's earlier test):
Board woke up! Wake reason: PinAlarm on microcontroller.pin.GPIO9 (RTC INT)
MISMATCH: Configured for board.D32, but woke up on microcontroller.pin.GPIO9!
Test 2: Configured GPIO27
Configured Pin: microcontroller.pin.GPIO27 (board.D27)
Actual Wakeup Pin: board.TX (microcontroller.pin.GPIO1)
Serial Output (from user's full log):
...
Adalogger PCF8523 RTC found and initialized.
Board woke up! Wake reason: PinAlarm on board.TX (RTC INT)
MISMATCH: Configured for board.D27, but woke up on board.TX!
Current RTC time: 2025-07-17 23:03:56
RTC reports it lost power! Time will need to be set.
Wake Count: 1
Current Mode: INITIAL_FREQUENT_WAKEUPS
...
Test 3: Configured GPIO4
Configured Pin: microcontroller.pin.GPIO4 (board.A5)
Actual Wakeup Pin: microcontroller.pin.GPIO10
Serial Output (from user's full log):
...
Adalogger PCF8523 RTC found and initialized.
Board woke up! Wake reason: PinAlarm on microcontroller.pin.GPIO10 (RTC INT)
MISMATCH: Configured for board.A5, but woke up on microcontroller.pin.GPIO10!
Current RTC time: 2025-07-17 23:10:58
RTC reports it lost power! Time will need to be set.
Wake Count: 2
Current Mode: INITIAL_FREQUENT_WAKEUPS
...
Code to Reproduce
Python
import board
import digitalio
import time
import microcontroller
import busio
from adafruit_pcf8523.pcf8523 import PCF8523
import adafruit_datetime as datetime
import alarm
# --- Configuration ---
# Change this to GPIO32, GPIO27, or GPIO4 for testing
# Example: RTC_INTERRUPT_PIN = microcontroller.pin.GPIO27
RTC_INTERRUPT_PIN = microcontroller.pin.GPIO4
INITIAL_DEEP_SLEEP_SECONDS = 5
INITIAL_FREQUENT_WAKEUPS_COUNT = 3
NORMAL_ULP_TIMER_BACKUP_SECONDS = 75
RTC_ALARM_INTERVAL_SECONDS = 60
# --- Onboard LED Setup ---
try:
led = digitalio.DigitalInOut(board.LED)
except AttributeError:
try:
led = digitalio.DigitalInOut(board.GPIO13)
except AttributeError:
while True:
time.sleep(1)
led.direction = digitalio.Direction.OUTPUT
# Explicitly define GPIO3 (RX) and GPIO1 (TX) for control
try:
gpio3_rx = digitalio.DigitalInOut(microcontroller.pin.GPIO3)
gpio3_rx.direction = digitalio.Direction.INPUT
gpio3_rx.pull = digitalio.Pull.UP
except Exception:
gpio3_rx = None
try:
gpio1_tx = digitalio.DigitalInOut(microcontroller.pin.GPIO1)
gpio1_tx.direction = digitalio.Direction.INPUT
gpio1_tx.pull = digitalio.Pull.UP
except Exception:
gpio1_tx = None
def run_blink_code(num_blinks=3, blink_duration=0.2):
for i in range(num_blinks):
led.value = True
time.sleep(blink_duration)
led.value = False
time.sleep(blink_duration)
# --- RTC Setup (for Adalogger FeatherWing with PCF8523) ---
try:
i2c = busio.I2C(board.SCL, board.SDA)
rtc = PCF8523(i2c)
except Exception:
rtc = None
# --- Main Program Logic ---
wake_alarm = alarm.wake_alarm
if wake_alarm:
if isinstance(wake_alarm, alarm.pin.PinAlarm):
actual_wake_pin = wake_alarm.pin
try:
if len(alarm.sleep_memory) < 1:
alarm.sleep_memory = bytearray(1)
wake_count = alarm.sleep_memory[0]
except Exception:
wake_count = 0
if wake_count < INITIAL_FREQUENT_WAKEUPS_COUNT:
current_mode = "INITIAL_FREQUENT_WAKEUPS"
sleep_duration_seconds = INITIAL_DEEP_SLEEP_SECONDS
else:
current_mode = "NORMAL_OPERATION"
sleep_duration_seconds = NORMAL_ULP_TIMER_BACKUP_SECONDS
if current_mode == "INITIAL_FREQUENT_WAKEUPS":
run_blink_code(num_blinks=1, blink_duration=0.2)
elif current_mode == "NORMAL_OPERATION":
run_blink_code(num_blinks=3, blink_duration=0.2)
# --- Configure Wakeup Sources ---
alarms_to_set = []
ulp_timer_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + sleep_duration_seconds)
alarms_to_set.append(ulp_timer_alarm)
if rtc and current_mode == "NORMAL_OPERATION":
rtc.alarm_status = False
now_struct_time = rtc.datetime
next_alarm_minute = (now_struct_time.tm_min + 1) % 60
next_alarm_hour = now_struct_time.tm_hour
if next_alarm_minute == 0:
next_alarm_hour = (next_alarm_hour + 1) % 24
alarm_time_struct = time.struct_time((
now_struct_time.tm_year, now_struct_time.tm_mon, now_struct_time.tm_mday,
next_alarm_hour, next_alarm_minute, 0,
now_struct_time.tm_wday, -1, -1
))
rtc.alarm = (alarm_time_struct, "minutely")
rtc.alarm_enable = True
elif rtc and current_mode == "INITIAL_FREQUENT_WAKEUPS":
rtc.alarm_enable = False
rtc_pin_alarm = alarm.pin.PinAlarm(RTC_INTERRUPT_PIN, value=False)
alarms_to_set.append(rtc_pin_alarm)
wake_count += 1
alarm.sleep_memory[0] = wake_count & 0xFF
if gpio3_rx:
gpio3_rx.deinit()
if gpio1_tx:
gpio1_tx.deinit()
led.deinit()
time.sleep(1)
alarm.exit_and_deep_sleep_until_alarms(*alarms_to_set)