@@ -39,6 +39,13 @@ const unsigned long
3939const uint16_t
4040 SerialTimeout = 60 ; // time before LEDs are shut off if no data (in seconds), 0 to disable
4141
42+ // --- Group Settings (uncomment to add)
43+ // Grouping will set a group of LEDs to the data received for a single one. This
44+ // lets you send less data to the device, increasing throughput and therefore
45+ // framerate - at the cost of resolution. Grouping only works if your strip is
46+ // evenly divisible by the amount of data sent.
47+ // #define GROUPING // enable the automatic grouping feature
48+
4249// --- Optional Settings (uncomment to add)
4350#define SERIAL_FLUSH // Serial buffer cleared on LED latch
4451// #define CLEAR_ON_START // LEDs are cleared on reset
@@ -52,7 +59,6 @@ const uint16_t
5259#include < FastLED.h>
5360
5461CRGB leds[Num_Leds];
55- uint8_t * ledsRaw = (uint8_t *)leds;
5662
5763// A 'magic word' (along with LED count & checksum) precedes each block
5864// of LED data; this assists the microcontroller in syncing up with the
@@ -79,13 +85,18 @@ const uint8_t magic[] = {
7985
8086enum processModes_t {Header, Data} mode = Header;
8187
82- int16_t c; // current byte, must support -1 if no data available
83- uint16_t outPos; // current byte index in the LED array
84- uint32_t bytesRemaining; // count of bytes yet received, set by checksum
85- unsigned long t, lastByteTime, lastAckTime; // millisecond timestamps
88+ uint32_t ledIndex; // current index in the LED array
89+ uint32_t ledsRemaining; // count of LEDs still to write, set by checksum (u16 + 1)
90+
91+ unsigned long lastByteTime; // ms timestamp, last byte received
92+ unsigned long lastAckTime; // ms timestamp, lask acknowledge to the host
93+
94+ unsigned long (*const now)(void ) = millis; // timing function
95+ const unsigned long Timebase = 1000 ; // time units per second
8696
87- void headerMode ();
88- void dataMode ();
97+ void headerMode (uint8_t c);
98+ void dataMode (uint8_t c);
99+ void groupProcessing ();
89100void timeouts ();
90101
91102// Macros initialized
@@ -97,9 +108,6 @@ void timeouts();
97108#endif
98109
99110#ifdef DEBUG_LED
100- #define ON 1
101- #define OFF 0
102-
103111 #define D_LED (x ) do {digitalWrite (DEBUG_LED, x);} while (0 )
104112#else
105113 #define D_LED (x )
@@ -142,18 +150,18 @@ void setup(){
142150}
143151
144152void loop (){
145- t = millis (); // Save current time
153+ const int c = Serial. read (); // read one byte
146154
147- // If there is new serial data
148- if ((c = Serial. read ()) >= 0 ){
149- lastByteTime = lastAckTime = t ; // Reset timeout counters
155+ // if there is data available
156+ if (c >= 0 ){
157+ lastByteTime = lastAckTime = now () ; // Reset timeout counters
150158
151159 switch (mode) {
152160 case Header:
153- headerMode ();
161+ headerMode (c );
154162 break ;
155163 case Data:
156- dataMode ();
164+ dataMode (c );
157165 break ;
158166 }
159167 }
@@ -163,7 +171,7 @@ void loop(){
163171 }
164172}
165173
166- void headerMode (){
174+ void headerMode (uint8_t c ){
167175 static uint8_t
168176 headPos,
169177 hi, lo, chk;
@@ -188,10 +196,10 @@ void headerMode(){
188196 chk = c;
189197 if (chk == (hi ^ lo ^ 0x55 )) {
190198 // Checksum looks valid. Get 16-bit LED count, add 1
191- // (# LEDs is always > 0) and multiply by 3 for R,G,B.
192- D_LED (ON );
193- bytesRemaining = 3L * ( 256L * ( long )hi + ( long )lo + 1L ) ;
194- outPos = 0 ;
199+ // (# of LEDs is always > 0), save and reset data
200+ D_LED (HIGH );
201+ ledIndex = 0 ;
202+ ledsRemaining = ( 256UL * ( uint32_t )hi + ( uint32_t )lo + 1UL ) ;
195203 memset (leds, 0 , Num_Leds * sizeof (struct CRGB ));
196204 mode = Data; // Proceed to latch wait mode
197205 }
@@ -201,32 +209,77 @@ void headerMode(){
201209 }
202210}
203211
204- void dataMode (){
205- // If LED data is not full
206- if (outPos < sizeof (leds)){
207- ledsRaw[outPos++] = c; // Issue next byte
212+ void dataMode (uint8_t c){
213+ static uint8_t channelIndex = 0 ;
214+
215+ // if LED data is not full, save the byte
216+ if (ledIndex < Num_Leds) {
217+ leds[ledIndex].raw [channelIndex] = c;
208218 }
209- bytesRemaining--;
210-
211- if (bytesRemaining == 0 ) {
212- // End of data -- issue latch:
213- mode = Header; // Begin next header search
219+ channelIndex++; // increment regardless, for oversized data
220+
221+ // if we've filled this LED, move to the next
222+ if (channelIndex >= 3 ) {
223+ // reset the channel index so we can get ready to write
224+ // the next LED, starting on the first channel (R/G/B)
225+ channelIndex = 0 ;
226+
227+ // allow this to max out at Num_Leds, so that it represents which
228+ // LEDs have data in them when the strip is ready to write
229+ if (ledIndex < Num_Leds) ledIndex++;
230+
231+ // finished writing one LED, decrement the counter
232+ ledsRemaining--;
233+ }
234+
235+ // if all data has been read, write the output
236+ if (ledsRemaining == 0 ) {
237+ #if defined(GROUPING)
238+ groupProcessing ();
239+ #endif
240+
214241 FastLED.show ();
242+ channelIndex = 0 ; // reset channel tracking
243+ mode = Header; // begin next header search
244+
215245 D_FPS;
216- D_LED (OFF );
246+ D_LED (LOW );
217247 SERIAL_FLUSH;
218248 }
219249}
220250
251+ void groupProcessing () {
252+ // if we've received the same amount of data as there
253+ // are LEDs in the strip, don't do any group processing
254+ if (Num_Leds == ledIndex) return ;
255+
256+ const uint16_t GroupSize = Num_Leds / ledIndex;
257+ const uint16_t GroupRemainder = Num_Leds - (ledIndex * GroupSize);
258+
259+ // if the value isn't evenly divisible, don't bother trying to group
260+ // (it won't look right without significant processing, i.e. overhead)
261+ if (GroupRemainder != 0 ) return ;
262+
263+ // otherwise, iterate backwards through the array, copying each LED color
264+ // forwards to the rest of its group
265+ for (uint16_t group = 1 ; group <= ledIndex; ++group) {
266+ const CRGB GroupColor = leds[ledIndex - group];
267+ const uint16_t GroupStart = Num_Leds - (group * GroupSize);
268+ fill_solid (&leds[GroupStart], GroupSize, GroupColor);
269+ }
270+ }
271+
221272void timeouts (){
273+ const unsigned long t = now ();
274+
222275 // No data received. If this persists, send an ACK packet
223276 // to host once every second to alert it to our presence.
224- if ((t - lastAckTime) >= 1000 ) {
277+ if ((t - lastAckTime) >= Timebase ) {
225278 Serial.print (" Ada\n " ); // Send ACK string to host
226279 lastAckTime = t; // Reset counter
227280
228281 // If no data received for an extended time, turn off all LEDs.
229- if (SerialTimeout != 0 && (t - lastByteTime) >= (uint32_t ) SerialTimeout * 1000 ) {
282+ if (SerialTimeout != 0 && (t - lastByteTime) >= (uint32_t ) SerialTimeout * Timebase ) {
230283 memset (leds, 0 , Num_Leds * sizeof (struct CRGB )); // filling Led array by zeroes
231284 FastLED.show ();
232285 mode = Header;
0 commit comments