Greetings, all!
I’m about to take delivery of my custom controller (extremely excited!) and start hacking away at my first project. I haven’t picked it up yet, so this request lacks the benefits of hands-on experience; forgive me if I’m missing something obvious!
The Desired Feature
It looks (from the RGB rotary encoder video and from the simpleSpot
array in FeedbackClass.h
) as though the Spot mode only illuminates a single LED in the ring at once. With up to two adjacent LEDs and six addressable colors, you could precisely represent a full 7-bit value in the ring in a visually progressive manner, where the LEDs and colors sweep sensibly around the ring in exactly 128 possible configurations.
This would be great for custom applications (at least for mine ) and I think it might work really well for general feedback. Below are some thoughts and a draft proposal. I might try to implement this as a firmware modification exercise, but I thought I’d get the feature request out there first.
I saw an earlier video where rotation steps between one or two illuminated ring LEDs, which is kind of like the feature I’m proposing, but I don’t think that mode is available currently. It looks as though there’s a bitmap table for it in FeedbackClass.h
called walk
but no corresponding case in feedback.ino
, so maybe you’re planning to implement it soon anyway, in which case the only new thing I’m proposing would be the color progressions.
The Proposal
In the firmware, pick six colors (hardcoded, for now) that progress from a low intensity to a high intensity. Call these A, B, C, D, E, F. All that matters is that the colors have a reasonable visual ordering. For example, A through C could be three hues of dark blue, from darkest to lightest; D and E could be two shades of light blue, darker and lighter; and F could be white.
Feedback begins with a 7-bit (0…127) feedback value, either from an internally tracked CC or from an application-sent CC.
Between 0 and 2 LEDs would be active for any given value:
0 - No ring LEDs (obviously!)
1 to 6 - Only the 1st (lowest) ring LED. (This is one of two ranges with six values; the rest have 5).
7 to 11 - Both the 1st and 2nd ring LEDs.
12 to 16 - Only the 2nd ring LED.
And so forth, in groups of five, until:
112 to 116 - Only the 12th ring LED.
117 to 121 - Both the 12th and the 13th ring LED.
122 to 127 - Only the 13th (highest) ring LED. (This is the other range with six values).
Now, use the colors to make the feedback full-resolution (with a different, progressive visual configuration for each feedback value). In each value group where only one LED is lit, assign color to that LED accordingly:
Initial group of 6: 1 through 6 get A through F
Eleven middle groups of 5: 12 through 16 (and 22 through 26, etc.) get B through F (skipping A)
Final group of 6: 122 through 127 get A through F.
In each value group where two LEDs are lit, assign colors to the lower and higher LEDs accordingly, in an inverse relationship, where one visually pours into the other:
7, 17, …: lower LED gets E, higher LED gets A.
8, 18, …: lower LED gets D, higher LED gets B.
9, 19, …: lower LED gets C, higher LED also gets C.
10, 20, …: lower LED gets B, higher LED gets D.
11, 21, …: lower LED gets A, higher LED gets E.
Now we have 1 [zero case] + (2 * 6) [first and last single-LED cases] + (11 * 5) [middle single-LED cases] + (12 * 5) [double-LED cases] = 128 representations for 128 values.
Sketch of an implementation
The ring bits would get set based on the CC value in feedback.ino
using something very close to (perhaps identical to) the existing walk
array.
The colors would probably get assigned in the SAMD11-NeoPixel
main.c
, as in the ENCODER_VUMETER_FRAME
case. As a hack (to avoid needing to update the serial buffer structure), we can repurpose the RGB values, since (as with the VUMeter) they’re not being used for color information. Rather than passing the CC value and requiring both CPUs to do the same logic, we could pass a more low-level representation of the state using a single byte in (say) sendSerialBufferDec[d_R]
:
Lower four bits are the index of the single LED (for the one-LED groups) or the lower LED (for the two-LED groups): 0…12. Unused if the upper four bits are 0000.
Upper four bits are the LED count and color progression combined: 0 meaning no LEDs, 1…6 meaning A through F in the single-LED case, and 7…11 meaning E/A => D/B => C/C => B/D => A/E in the double-LED case.
Obviously, there are other (and maybe more sensible or efficient) ways of passing this to the NeoPixel controller; the point is that there are more than enough unused bits in the buffer to let the SAMD11 code assign the colors directly with minimal logic.
Closing Thoughts
This is probably an unnecessary level of implementation detail given that the proposal itself is quite rough, and I haven’t even mocked it up, but when I close my eyes and imagine it, it looks good (at least with the right color choice).
My guess is that the most disruptive/discontinuous point in the progression will be the jump from two LEDs to one (e.g. 11 to 12, where we go from AE to _B), because the color of the upper LED is going “down” even as the represented value is going up (or vice versa). There may be a slick way around that, possibly involving the same bitmap assignments but more colors (so that the double-LED cases stick in the midrange and only the single-LED cases get to the brightest colors).
It would probably also be worth tweaking the progression slightly so that 63 or 64 corresponded to the brightest color in the top slot. This would be very easy to do but would complicate the description, so I didn’t write it up that way.
This could all be handled by an application, of course, if you exposed arbitrary velocity-based color table access to the ring LEDs (the way that you now do with digital buttons and with the base LED group on the rotary encoder). (While we’re at it, you could even consider allowing separate access to the three LEDs on the base!) But that’s now a ton of MIDI traffic for feedback, and there’s probably a good reason that the complex color assignment code lives on the SAMD11 anyway.
Anyway, let me know what you think (or if this sounds crazy/too complicated/not a priority, please ignore it and I’ll work up a mockup or demo later!)