-
Notifications
You must be signed in to change notification settings - Fork 1k
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
drivers/led/neopixel: Add brightness control. #739
base: master
Are you sure you want to change the base?
Conversation
Signed-off-by: Tom Mount <[email protected]>
Signed-off-by: Tom Mount <[email protected]>
Signed-off-by: Tom Mount <[email protected]>
Signed-off-by: Tom Mount <[email protected]>
Signed-off-by: Tom Mount <[email protected]>
Signed-off-by: Tom Mount <[email protected]>
Do all implementations now have floating point enabled? (#define MICROPY_FLOAT_IMPL MICROPY_FLOAT_IMPL_FLOAT ) |
I had just assumed that If that's not the case, what's the process for handling floating point numbers on systems that don't have that type? |
I am not sure how to check. Look for a type exception maybe?. As
`micropython has` matured, more boards now have at least software floating
point which can be optionally turned off to save flash space, but I think
are a couple of older boards that do not enable floating point by default
`CC3200`, `powerpc`, `pic16bit`, `nrf` (`NRF51`)
The designers would be able to offer better guidance on the direction
here.
Note that some display drivers require floating point also - for rotations
and positioning- and may offer some guidance on how to handle missing
floating point type.
…On Fri, Sep 29, 2023 at 1:27 PM Tom Mount ***@***.***> wrote:
I had just assumed that float was a built-in type. It seems to be
referenced that way here -
https://docs.micropython.org/en/latest/genrst/builtin_types.html.
If that's not the case, what's the process for handling floating point
numbers on systems that don't have that type?
—
Reply to this email directly, view it on GitHub
<#739 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA5QPQD5PVRDOIMEYRS5WXLX44HKTANCNFSM6AAAAAA5MQEXV4>
.
You are receiving this because you commented.Message ID:
***@***.***>
--
Rick Sorensen
***@***.***
651-470-5511
|
Thanks @tmountjr @ricksorensen is correct that not all boards have floating point enabled. The bigger issue though is that using floating point is very expensive in MicroPython because floats get heap-allocated so you want to absolutely avoid using them unless necessary. For example, on a PYBV11, the following line:
takes 2058 microseconds before this PR, but now takes 24686 microseconds. But I think this can probably be almost entirely addressed:
See e.g. https://gist.github.com/jimmo/472bcf9213bae97fc915fd71d85048b9 which is back to ~2048 microseconds when brightness is not set and 2238 microseconds when set to 0.5. I'm also not sure about the behavior of |
Good idea only conditionally applying brightness if it's already set. Also didn't consider that I'd be recalculating the values for every single pixel if I simplified the The use of |
Signed-off-by: Tom Mount <[email protected]>
Signed-off-by: Tom Mount <[email protected]>
Signed-off-by: Tom Mount <[email protected]>
@jimmo Thanks for all your feedback! You seem to have access to some hardware that I don't that catches corner cases...would you be able to take a look at the latest version of this PR on that hardware and let me know if the performance is where it should be? |
Nope just any board running MicroPython:
Nothing special going on here, was just calling It would be also worth testing code that sets pixels directly (which I imagine is more representative of what people typically do with neopixels). I'm on the fence about this... I agree it's a useful feature -- it's nice to be able to e.g. fade in/out a strip or just separate your pixel generating code from brightness control. And it's good that it doesn't affect performance significantly if you don't use it (although set pixel will now be slower because it has to check self.brightness). I am still not super excited about the fact that set and get pixel are no longer symmetric (but again I don't have a better suggestion... so maybe it's a reasonable compromise). The other thing to think about is code size increase. I spent a bit of time trying to minimise neopixel.py as much as possible (which is why the style is a bit weird). neopixel.mpy is currently 616 bytes. This PR increases it to 925. I think we can get it down to 819 without any functional changes. I'll add some comments to the PR to explain. |
@@ -8,10 +8,16 @@ class NeoPixel: | |||
# G R B W | |||
ORDER = (1, 0, 2, 3) | |||
|
|||
def __init__(self, pin, n, bpp=3, timing=1): | |||
def _clamp(self, v, c_min, c_max): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is definitely stylistically better, but functions are expensive in terms of code size (and therefore memory usage).
In this case, it's actually cheaper to just use min(max(brightness), 0, 1)
directly in both places. Note also changing 0.0
to 0
etc is also helpful for size (and doesn't change the behaviour).
self.pin = pin | ||
self.n = n | ||
self.bpp = bpp | ||
self.brightness = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should rename this to self.b
-- class members end up allocating qstrs, so using small names is better.
It's also smaller to write
self.b = None if brightness is None else min(max(brightness), 0, 1)
rather than
self.b = None
if brightness is not None:
self.b = min(max(brightness), 0, 1)
@@ -22,11 +28,17 @@ def __init__(self, pin, n, bpp=3, timing=1): | |||
else timing | |||
) | |||
|
|||
def _apply_brightness(self, v): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to _b
(to save qstr usage). It's a private method, so better to add a comment than to waste bytes on the name. (Unfortunately this is par for the course with MicroPython... there are lots of ugly things we have to do, ideally in places the user doesn't see, in order to save code size. Every byte counts).
@@ -45,6 +58,13 @@ def fill(self, v): | |||
b[j] = c | |||
j += bpp | |||
|
|||
def set_brightness(self, b: float): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to brightness
(now that that name isn't used for the field). It's more in line with what we do elsewhere (e.g. pin.value()
).
Also I think, similarly, it should probably return the current brightness too, e.g.
def brightness(self, brightness):
if brightness is not None:
self.b = min(max(brightness, 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]
return self.b
which is +10 bytes, but I think useful for people to write e.g. strip.brightness(strip.brightness() + 0.1)
.
Signed-off-by: Tom Mount <[email protected]>
Did some tests on my Pico W...
|
Signed-off-by: Tom Mount <[email protected]>
Okay, the refactor of
|
Signed-off-by: Tom Mount <[email protected]>
Signed-off-by: Tom Mount <[email protected]>
Signed-off-by: Tom Mount <[email protected]>
(See related micropython/micropython#3623 and micropython/micropython#4815) |
What are the next steps with this PR? I see the two closed PRs on the main micropython repo but I'm not clear if I need to make any changes in light of those. Thanks! |
Hi, |
@IHOXOHI any chance you can put that code in a gist for me? the link you provided looked a little sketchy. Is your question why the timing is different? I'm not sure I know what you're asking for help with. |
Just commenting that I went looking for this functionality today only to wind up at this PR. It isn't too much of a lift to simply implement equivalent functionality for myself for the time being, but I would love to see this PR eventually get merged. |
Ok, I will post It on GitHub... M'y first. The lib modified: github.com/IHOXOHI/neopixel-brightness-function/blob/main/README.md |
New functionality to specify a brightness for the strip when creating the instance, and also via
set_brightness
. Also refactored thefill()
method to avoid duplicating the logic used in__setitem__
. Existing functionality is that thewrite()
method must be called to see the changes, so whileset_brightness
does change the values for all the pixels in the instance, it does not write that back out to the strip immediately.This should be completely backwards-compatible with the previous version in that the brightness is by default 100%.