Ver Fonte

Test NeoPixel .show() during init to prevent SEGV crash loop

The NeoPixel constructor succeeds without root, but .show() needs DMA
via /dev/mem. The first failed .show() raises a Python exception, but
leaves the C library in a corrupted state — subsequent calls SEGV.
Test .show() during init and deinit on failure so the effect loop
thread never touches a broken NeoPixel object.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tuanchris há 4 meses atrás
pai
commit
2e4a05e609
1 ficheiros alterados com 16 adições e 1 exclusões
  1. 16 1
      modules/led/dw_led_controller.py

+ 16 - 1
modules/led/dw_led_controller.py

@@ -107,7 +107,7 @@ class DWLEDController:
             board_pin = pin_map[self.gpio_pin]
             board_pin = pin_map[self.gpio_pin]
 
 
             # Initialize NeoPixel strip
             # Initialize NeoPixel strip
-            self._pixels = neopixel_module.NeoPixel(
+            pixels = neopixel_module.NeoPixel(
                 board_pin,
                 board_pin,
                 self.num_leds,
                 self.num_leds,
                 brightness=self.brightness,
                 brightness=self.brightness,
@@ -115,6 +115,21 @@ class DWLEDController:
                 pixel_order=self.pixel_order
                 pixel_order=self.pixel_order
             )
             )
 
 
+            # Test that .show() actually works — the constructor succeeds without
+            # root, but .show() needs DMA via /dev/mem. If it fails here we get a
+            # clean Python exception; if we let the effect loop hit it first the
+            # underlying C library can SEGV and crash the whole process.
+            try:
+                pixels.show()
+            except Exception as e:
+                pixels.deinit()
+                error_msg = f"NeoPixel hardware test failed (likely needs root): {e}"
+                self._init_error = error_msg
+                logger.warning(error_msg)
+                return False
+
+            self._pixels = pixels
+
             # Create segment for the entire strip
             # Create segment for the entire strip
             self._segment = Segment(self._pixels, 0, self.num_leds)
             self._segment = Segment(self._pixels, 0, self.num_leds)
             self._segment.speed = self._speed
             self._segment.speed = self._speed