Yeah, I tossed this around in my head a lot. Anything I could come up with short of what I’m doing in the proof of concept had a lot of compromises, complexity, and potential snags.
I think we should chat and troubleshoot, because this shouldn’t be the case. If you want, I think I can get your FPS back.
There are ways to code patterns that still won’t work super well when the full list is complete, like KITT that uses timers based on delta from when the pattern runs (which is still some ms apart) instead of the synced time() calls. With #10 the main PB could use some new tools to help sync paired PBs, but would need to be added pattern-by-pattern.
Good ideas. I’m leaning toward message passing, or one-way memory copies, and want to avoid adding keywords that would break ES6 compatibility. I also want to avoid using master/slave terminology.
Ideally the show will continue with some interruption, as will tend to happen with any WiFi network. The main PB can help coordinate, but things should keep running without it, or with it temporarily unavailable.
Taking this example:
for (id = 0; id < maxId; id++) {
brightness[id] = (cos(id/idCount * PI2) + 1.0) / 2.0
}
I would flip this around (and I’m guessing you meant to use v
in there somewhere so the slider moved the wave):
// control handlers are invoked for every PB in the group
export function sliderAngle(v) {
brightness = (cos((groupIndex/groupCount + v) * PI2) + 1.0) / 2.0
}
That way no state synchronization is required for a shared brightness
array, but instead relies on control info being relayed down. Each PB has enough information to act on it individually and calculate their own brightness
instance value given the magic groupIndex
and optionally some knowledge about the total groupCount
.
Just like pixelCount
knowing how many belong to the group is useful at initialization time for things like allocating memory and/or figuring out how to divvy up work. That likely means rigidifying group membership, restarting patterns when new members are added, possibly statefulness. It might make it more difficult to use with some of the use cases like roaming groups of bikes that go in and out of range, or future p2p ad-hoc type groups.
Pushing data back up would be very useful, beyond just relaying controls/button/sensor board data, but to send data from local pins or code. That gets a bit trickier, especially if it goes full p2p and any kind of consistency needs to happen.
Magic syncing data/arrays would be cool, and are quite nice for local sensor board data, but an explicit API might be better for distributed patterns so that it the code can control the atomicity. An RPC style approach could be interesting too, so the data can be acted on when it arrives instead of polled for (which might get overwritten).
With typical message/event interfaces your code registers interest (observes) some event based on name/type and binds that to a callback, but we might be able to skip some of that boilerplate since we are running the exact same code, and doing something more RPC like with local semantics.
Something like this might be neat and let you do more p2p-like patterns without subscription management:
function messageHander(v1, v2) {
//...
}
//...
notify(messageHander, someValue, somethingElse)
//or lambda style
notify((v1, v2) => {...}, someValue, somethingElse)
So here notify
takes a function reference and any arguments, and runs that function on every PB in the group.
If you pass in your own ID as an argument, the receiver could filter based on sender, though I think more often than not you wouldn’t need to filter like that.