From 2d9486c95a80861a804b45e02d17f7caf2e1ce63 Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Thu, 28 Sep 2023 17:02:06 -0400 Subject: [PATCH 01/14] drivers/led/neopixel: Add brightness to class init Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/micropython/drivers/led/neopixel/neopixel.py b/micropython/drivers/led/neopixel/neopixel.py index caa12dc84..2996bd23b 100644 --- a/micropython/drivers/led/neopixel/neopixel.py +++ b/micropython/drivers/led/neopixel/neopixel.py @@ -8,10 +8,11 @@ class NeoPixel: # G R B W ORDER = (1, 0, 2, 3) - def __init__(self, pin, n, bpp=3, timing=1): + def __init__(self, pin, n, bpp=3, timing=1, brightness: float = 1.0): self.pin = pin self.n = n self.bpp = bpp + self.brightness = brightness self.buf = bytearray(n * bpp) self.pin.init(pin.OUT) # Timing arg can either be 1 for 800kHz or 0 for 400kHz, @@ -22,28 +23,25 @@ def __init__(self, pin, n, bpp=3, timing=1): else timing ) + def _calculate_brightness(self, v): + return round(v * self.brightness) + def __len__(self): return self.n def __setitem__(self, i, v): offset = i * self.bpp for i in range(self.bpp): - self.buf[offset + self.ORDER[i]] = v[i] + adjusted_v = tuple(self._calculate_brightness(c) for c in v) + self.buf[offset + self.ORDER[i]] = adjusted_v[i] def __getitem__(self, i): offset = i * self.bpp return tuple(self.buf[offset + self.ORDER[i]] for i in range(self.bpp)) def fill(self, v): - b = self.buf - l = len(self.buf) - bpp = self.bpp - for i in range(bpp): - c = v[i] - j = self.ORDER[i] - while j < l: - b[j] = c - j += bpp + for i in range(self.n): + self[i] = v def write(self): # BITSTREAM_TYPE_HIGH_LOW = 0 From 94e3dab79a7e3086b46d1649fa092b24c4097927 Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Fri, 29 Sep 2023 08:46:39 -0400 Subject: [PATCH 02/14] drivers/led/neopixel: Calculate the new values once. Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/micropython/drivers/led/neopixel/neopixel.py b/micropython/drivers/led/neopixel/neopixel.py index 2996bd23b..e10587472 100644 --- a/micropython/drivers/led/neopixel/neopixel.py +++ b/micropython/drivers/led/neopixel/neopixel.py @@ -31,8 +31,8 @@ def __len__(self): def __setitem__(self, i, v): offset = i * self.bpp + adjusted_v = tuple(self._calculate_brightness(c) for c in v) for i in range(self.bpp): - adjusted_v = tuple(self._calculate_brightness(c) for c in v) self.buf[offset + self.ORDER[i]] = adjusted_v[i] def __getitem__(self, i): From 8105f2db3a77689bc9bbfb3fb6ed8e49dae1bf6c Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Fri, 29 Sep 2023 08:51:37 -0400 Subject: [PATCH 03/14] drivers/led/neopixel: Increment version number Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/micropython/drivers/led/neopixel/manifest.py b/micropython/drivers/led/neopixel/manifest.py index 02a319002..e52fef9ee 100644 --- a/micropython/drivers/led/neopixel/manifest.py +++ b/micropython/drivers/led/neopixel/manifest.py @@ -1,3 +1,3 @@ -metadata(description="WS2812/NeoPixel driver.", version="0.1.0") +metadata(description="WS2812/NeoPixel driver.", version="0.2.0") module("neopixel.py", opt=3) From 69f6d618811fe2d0fda8d697fcd8ac7d4844eef9 Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Fri, 29 Sep 2023 09:03:43 -0400 Subject: [PATCH 04/14] drivers/led/neopixel: Add set_brightness method Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/micropython/drivers/led/neopixel/neopixel.py b/micropython/drivers/led/neopixel/neopixel.py index e10587472..15e147c01 100644 --- a/micropython/drivers/led/neopixel/neopixel.py +++ b/micropython/drivers/led/neopixel/neopixel.py @@ -43,6 +43,11 @@ def fill(self, v): for i in range(self.n): self[i] = v + def set_brightness(self, b: float): + self.brightness = b + for i in range(self.n): + self[i] = self[i] + def write(self): # BITSTREAM_TYPE_HIGH_LOW = 0 bitstream(self.pin, 0, self.timing, self.buf) From 47c5dbc4d5dfe6b73c534f5ba68035bb3868bfae Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Fri, 29 Sep 2023 09:41:22 -0400 Subject: [PATCH 05/14] drivers/led/neopixel: Clamp brightness between 0.0 and 1.0. Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/micropython/drivers/led/neopixel/neopixel.py b/micropython/drivers/led/neopixel/neopixel.py index 15e147c01..6325b4388 100644 --- a/micropython/drivers/led/neopixel/neopixel.py +++ b/micropython/drivers/led/neopixel/neopixel.py @@ -44,8 +44,8 @@ def fill(self, v): self[i] = v def set_brightness(self, b: float): - self.brightness = b - for i in range(self.n): + self.brightness = min(max(b, 0.0), 1.0) + for i in range(self.n) self[i] = self[i] def write(self): From 92967e06184dcef034810689bc875899bd8a7f28 Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Fri, 29 Sep 2023 09:43:58 -0400 Subject: [PATCH 06/14] drivers/led/neopixel: Also clamp constructor brightness Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/micropython/drivers/led/neopixel/neopixel.py b/micropython/drivers/led/neopixel/neopixel.py index 6325b4388..409d91833 100644 --- a/micropython/drivers/led/neopixel/neopixel.py +++ b/micropython/drivers/led/neopixel/neopixel.py @@ -12,7 +12,7 @@ def __init__(self, pin, n, bpp=3, timing=1, brightness: float = 1.0): self.pin = pin self.n = n self.bpp = bpp - self.brightness = brightness + self.brightness = min(max(brightness, 0.0), 1.0) self.buf = bytearray(n * bpp) self.pin.init(pin.OUT) # Timing arg can either be 1 for 800kHz or 0 for 400kHz, @@ -45,7 +45,7 @@ def fill(self, v): def set_brightness(self, b: float): self.brightness = min(max(b, 0.0), 1.0) - for i in range(self.n) + for i in range(self.n): self[i] = self[i] def write(self): From 6f3ebea46680e7795b0e6c4be9f5e2740a5d66a0 Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Wed, 4 Oct 2023 09:44:21 -0400 Subject: [PATCH 07/14] drivers/led/neopixel: Optimize for speed on non-FP builds. Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel.py | 30 ++++++++++++++------ 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/micropython/drivers/led/neopixel/neopixel.py b/micropython/drivers/led/neopixel/neopixel.py index 409d91833..b71833ca9 100644 --- a/micropython/drivers/led/neopixel/neopixel.py +++ b/micropython/drivers/led/neopixel/neopixel.py @@ -8,11 +8,13 @@ class NeoPixel: # G R B W ORDER = (1, 0, 2, 3) - def __init__(self, pin, n, bpp=3, timing=1, brightness: float = 1.0): + def __init__(self, pin, n, bpp=3, timing=1, brightness: float = None): self.pin = pin self.n = n self.bpp = bpp - self.brightness = min(max(brightness, 0.0), 1.0) + self.brightness = ( + min(max(brightness, 0.0), 1.0) if brightness is not None else None + ) self.buf = bytearray(n * bpp) self.pin.init(pin.OUT) # Timing arg can either be 1 for 800kHz or 0 for 400kHz, @@ -23,28 +25,40 @@ def __init__(self, pin, n, bpp=3, timing=1, brightness: float = 1.0): else timing ) - def _calculate_brightness(self, v): - return round(v * self.brightness) + def _apply_brightness(self, v): + if self.brightness is not None: + return tuple(round(c * self.brightness) for c in v) + return v def __len__(self): return self.n def __setitem__(self, i, v): offset = i * self.bpp - adjusted_v = tuple(self._calculate_brightness(c) for c in v) + v = self._apply_brightness(v) for i in range(self.bpp): - self.buf[offset + self.ORDER[i]] = adjusted_v[i] + self.buf[offset + self.ORDER[i]] = v[i] def __getitem__(self, i): offset = i * self.bpp return tuple(self.buf[offset + self.ORDER[i]] for i in range(self.bpp)) def fill(self, v): - for i in range(self.n): - self[i] = v + v = self._apply_brightness(v) + b = self.buf + l = len(self.buf) + bpp = self.bpp + for i in range(bpp): + c = v[i] + j = self.ORDER[i] + while j < l: + b[j] = c + j += bpp def set_brightness(self, b: float): self.brightness = min(max(b, 0.0), 1.0) + # This may look odd but because __getitem__ and __setitem__ handle all the + # brightness logic already, we can offload the work to those methods. for i in range(self.n): self[i] = self[i] From f6836085428d298611b2a13bc60b869b9d2566a6 Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Wed, 4 Oct 2023 10:31:18 -0400 Subject: [PATCH 08/14] drivers/led/neopixel: Fix lint issue. Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/micropython/drivers/led/neopixel/neopixel.py b/micropython/drivers/led/neopixel/neopixel.py index b71833ca9..b9a0fddfd 100644 --- a/micropython/drivers/led/neopixel/neopixel.py +++ b/micropython/drivers/led/neopixel/neopixel.py @@ -8,12 +8,12 @@ class NeoPixel: # G R B W ORDER = (1, 0, 2, 3) - def __init__(self, pin, n, bpp=3, timing=1, brightness: float = None): + def __init__(self, pin, n, bpp=3, timing=1, brightness=None): self.pin = pin self.n = n self.bpp = bpp self.brightness = ( - min(max(brightness, 0.0), 1.0) if brightness is not None else None + min(max(float(brightness), 0.0), 1.0) if brightness is not None else None ) self.buf = bytearray(n * bpp) self.pin.init(pin.OUT) From 0014eac9066c3783cb236c9a947413fe03ad9085 Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Wed, 4 Oct 2023 10:40:43 -0400 Subject: [PATCH 09/14] drivers/led/neopixel: Continue lint fixes. Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/micropython/drivers/led/neopixel/neopixel.py b/micropython/drivers/led/neopixel/neopixel.py index b9a0fddfd..853f71efe 100644 --- a/micropython/drivers/led/neopixel/neopixel.py +++ b/micropython/drivers/led/neopixel/neopixel.py @@ -8,13 +8,16 @@ class NeoPixel: # G R B W ORDER = (1, 0, 2, 3) + def _clamp(self, v, c_min, c_max): + return min(max(v, c_min), c_max) + def __init__(self, pin, n, bpp=3, timing=1, brightness=None): self.pin = pin self.n = n self.bpp = bpp - self.brightness = ( - min(max(float(brightness), 0.0), 1.0) if brightness is not None else None - ) + self.brightness = None + if brightness is not None: + self.brightness = self._clamp(float(brightness), 0.0, 1.0) self.buf = bytearray(n * bpp) self.pin.init(pin.OUT) # Timing arg can either be 1 for 800kHz or 0 for 400kHz, @@ -56,7 +59,7 @@ def fill(self, v): j += bpp def set_brightness(self, b: float): - self.brightness = min(max(b, 0.0), 1.0) + self.brightness = self._clamp(b, 0.0, 1.0) # This may look odd but because __getitem__ and __setitem__ handle all the # brightness logic already, we can offload the work to those methods. for i in range(self.n): From 866fd2f74af9cbd03534b569c26be4fae04fb5cd Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Fri, 6 Oct 2023 10:14:27 -0400 Subject: [PATCH 10/14] drivers/led/neopixel: code size tweaks Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel.py | 23 +++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/micropython/drivers/led/neopixel/neopixel.py b/micropython/drivers/led/neopixel/neopixel.py index 853f71efe..ab030a15f 100644 --- a/micropython/drivers/led/neopixel/neopixel.py +++ b/micropython/drivers/led/neopixel/neopixel.py @@ -8,16 +8,11 @@ class NeoPixel: # G R B W ORDER = (1, 0, 2, 3) - def _clamp(self, v, c_min, c_max): - return min(max(v, c_min), c_max) - def __init__(self, pin, n, bpp=3, timing=1, brightness=None): self.pin = pin self.n = n self.bpp = bpp - self.brightness = None - if brightness is not None: - self.brightness = self._clamp(float(brightness), 0.0, 1.0) + self.b = None if brightness is None else min(max(brightness, 0), 1) self.buf = bytearray(n * bpp) self.pin.init(pin.OUT) # Timing arg can either be 1 for 800kHz or 0 for 400kHz, @@ -28,9 +23,9 @@ def __init__(self, pin, n, bpp=3, timing=1, brightness=None): else timing ) - def _apply_brightness(self, v): - if self.brightness is not None: - return tuple(round(c * self.brightness) for c in v) + def _b(self, v): + if self.b is not None: + return tuple(round(c * self.b) for c in v) return v def __len__(self): @@ -38,7 +33,7 @@ def __len__(self): def __setitem__(self, i, v): offset = i * self.bpp - v = self._apply_brightness(v) + v = self._b(v) for i in range(self.bpp): self.buf[offset + self.ORDER[i]] = v[i] @@ -47,7 +42,7 @@ def __getitem__(self, i): return tuple(self.buf[offset + self.ORDER[i]] for i in range(self.bpp)) def fill(self, v): - v = self._apply_brightness(v) + v = self._b(v) b = self.buf l = len(self.buf) bpp = self.bpp @@ -58,8 +53,10 @@ def fill(self, v): b[j] = c j += bpp - def set_brightness(self, b: float): - self.brightness = self._clamp(b, 0.0, 1.0) + def brightness(self, b: float): + if b is None: + return self.b + self.b = min(max(b, 0), 1) # This may look odd but because __getitem__ and __setitem__ handle all the # brightness logic already, we can offload the work to those methods. for i in range(self.n): From 9e80530a74aed82e1dbf93e9efd40e5c8baa5eee Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Fri, 6 Oct 2023 11:26:39 -0400 Subject: [PATCH 11/14] drivers/led/neopixel: Refactor brightness() for speed. Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/micropython/drivers/led/neopixel/neopixel.py b/micropython/drivers/led/neopixel/neopixel.py index ab030a15f..99becb38e 100644 --- a/micropython/drivers/led/neopixel/neopixel.py +++ b/micropython/drivers/led/neopixel/neopixel.py @@ -53,14 +53,12 @@ def fill(self, v): b[j] = c j += bpp - def brightness(self, b: float): + def brightness(self, b=None): if b is None: return self.b self.b = min(max(b, 0), 1) - # This may look odd but because __getitem__ and __setitem__ handle all the - # brightness logic already, we can offload the work to those methods. - for i in range(self.n): - self[i] = self[i] + for i in range(self.n * self.bpp): + self.buf[i] = round(self.buf[i] * self.b) def write(self): # BITSTREAM_TYPE_HIGH_LOW = 0 From 9b5be62585fabd3e9c6cf6b072cfb49c7f0abc8d Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Fri, 6 Oct 2023 11:52:27 -0400 Subject: [PATCH 12/14] drivers/led/neopixel: Add tests for new features. Signed-off-by: Tom Mount --- .../drivers/led/neopixel/neopixel_test.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 micropython/drivers/led/neopixel/neopixel_test.py diff --git a/micropython/drivers/led/neopixel/neopixel_test.py b/micropython/drivers/led/neopixel/neopixel_test.py new file mode 100644 index 000000000..559026463 --- /dev/null +++ b/micropython/drivers/led/neopixel/neopixel_test.py @@ -0,0 +1,32 @@ +import machine +import neopixel + + +def neopixel_test(): + np = neopixel.NeoPixel(machine.Pin(1), 3) + print("Fill with a color.") + np.fill((255, 128, 64)) + print("Verify the bytes to be written") + expected_bytearray = bytearray([255, 128, 64, 255, 128, 64, 255, 128, 64]) + actual_bytearray = np.buf + print( + f'Initial fill: {"passed" if expected_bytearray == actual_bytearray else "failed"}.' + ) + print() + + print("Change brightness of all pixels.") + np.brightness(0.5) + expected_bytearray = bytearray([127, 64, 32, 127, 64, 32, 127, 64, 32]) + actual_bytearray = np.buf + print( + f'Brightness change: {"passed" if expected_bytearray == actual_bytearray else "failed"}.' + ) + print() + + print("Get current brightness.") + expected_brightness = 0.5 + actual_brightness = np.brightness() + print( + f'Brightness get: {"passed" if expected_brightness == actual_brightness else "failed"}.' + ) + print() From 6ca933b9f21da5ffcecd06a5a812a0c940a3d5f4 Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Fri, 6 Oct 2023 12:27:28 -0400 Subject: [PATCH 13/14] drivers/led/neopixel: Fix formatting. Signed-off-by: Tom Mount --- micropython/drivers/led/neopixel/neopixel_test.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/micropython/drivers/led/neopixel/neopixel_test.py b/micropython/drivers/led/neopixel/neopixel_test.py index 559026463..18171e33e 100644 --- a/micropython/drivers/led/neopixel/neopixel_test.py +++ b/micropython/drivers/led/neopixel/neopixel_test.py @@ -9,24 +9,18 @@ def neopixel_test(): print("Verify the bytes to be written") expected_bytearray = bytearray([255, 128, 64, 255, 128, 64, 255, 128, 64]) actual_bytearray = np.buf - print( - f'Initial fill: {"passed" if expected_bytearray == actual_bytearray else "failed"}.' - ) + print(f'Initial fill: {"passed" if expected_bytearray == actual_bytearray else "failed"}.') print() print("Change brightness of all pixels.") np.brightness(0.5) expected_bytearray = bytearray([127, 64, 32, 127, 64, 32, 127, 64, 32]) actual_bytearray = np.buf - print( - f'Brightness change: {"passed" if expected_bytearray == actual_bytearray else "failed"}.' - ) + print(f'Brightness change: {"passed" if expected_bytearray == actual_bytearray else "failed"}.') print() print("Get current brightness.") expected_brightness = 0.5 actual_brightness = np.brightness() - print( - f'Brightness get: {"passed" if expected_brightness == actual_brightness else "failed"}.' - ) + print(f'Brightness get: {"passed" if expected_brightness == actual_brightness else "failed"}.') print() From 1f1f51d57c8e171eddbecf85ac01d96b4429d079 Mon Sep 17 00:00:00 2001 From: Tom Mount Date: Fri, 6 Oct 2023 12:32:54 -0400 Subject: [PATCH 14/14] drivers/led/neopixel: Fix test formatting. Signed-off-by: Tom Mount --- .../drivers/led/neopixel/neopixel_test.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/micropython/drivers/led/neopixel/neopixel_test.py b/micropython/drivers/led/neopixel/neopixel_test.py index 18171e33e..eceac9a06 100644 --- a/micropython/drivers/led/neopixel/neopixel_test.py +++ b/micropython/drivers/led/neopixel/neopixel_test.py @@ -7,20 +7,23 @@ def neopixel_test(): print("Fill with a color.") np.fill((255, 128, 64)) print("Verify the bytes to be written") - expected_bytearray = bytearray([255, 128, 64, 255, 128, 64, 255, 128, 64]) - actual_bytearray = np.buf - print(f'Initial fill: {"passed" if expected_bytearray == actual_bytearray else "failed"}.') + expected = bytearray([255, 128, 64, 255, 128, 64, 255, 128, 64]) + actual = np.buf + passed = "passed" if expected == actual else "failed" + print(f"Initial fill: {passed}.") print() print("Change brightness of all pixels.") np.brightness(0.5) - expected_bytearray = bytearray([127, 64, 32, 127, 64, 32, 127, 64, 32]) - actual_bytearray = np.buf - print(f'Brightness change: {"passed" if expected_bytearray == actual_bytearray else "failed"}.') + expected = bytearray([127, 64, 32, 127, 64, 32, 127, 64, 32]) + actual = np.buf + passed = "passed" if expected == actual else "failed" + print(f"Brightness change: {passed}.") print() print("Get current brightness.") - expected_brightness = 0.5 - actual_brightness = np.brightness() - print(f'Brightness get: {"passed" if expected_brightness == actual_brightness else "failed"}.') + expected = 0.5 + actual = np.brightness() + passed = "passed" if expected == actual else "failed" + print(f"Brightness get: {passed}.") print()