From d42a83be455a502dd3cd7d17e8a53a2372dac4c5 Mon Sep 17 00:00:00 2001 From: David Madison Date: Wed, 3 May 2017 12:48:48 -0400 Subject: [PATCH 01/12] Cleaned up debug LED implementation Cleaner to use, and should be easier to add elsewhere in the future --- .../LEDstream_FastLED/LEDstream_FastLED.ino | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index aec1dad..f4c4d1b 100644 --- a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino +++ b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino @@ -27,8 +27,9 @@ static const unsigned long //#define CLEAR_ON_START // LEDs are cleared on reset //#define GROUND_PIN 10 // additional grounding pin (optional) //#define CALIBRATE // sets all LEDs to the color of the first -//#define DEBUG_LED 13 // turns on the Arduino's built-in LED - // if the magic word + checksum match + +// --- Debug Settings (uncomment to add) +//#define DEBUG_LED 13 // toggles the Arduino's built-in LED on header match // -------------------------------------------------------------------- @@ -63,6 +64,16 @@ static const uint8_t magic[] = { #define MODE_HEADER 0 #define MODE_DATA 1 +// Debug statement initialized +#ifdef DEBUG_LED + #define ON 1 + #define OFF 0 + + #define D_LED(x) do {digitalWrite(DEBUG_LED, x);} while(0) +#else + #define D_LED(x) +#endif + void setup(){ #ifdef GROUND_PIN pinMode(GROUND_PIN, OUTPUT); @@ -143,10 +154,7 @@ void adalight(){ if(chk == (hi ^ lo ^ 0x55)) { // Checksum looks valid. Get 16-bit LED count, add 1 // (# LEDs is always > 0) and multiply by 3 for R,G,B. - #ifdef DEBUG_LED - digitalWrite(DEBUG_LED, HIGH); - #endif - + D_LED(ON); bytesRemaining = 3L * (256L * (long)hi + (long)lo + 1L); outPos = 0; memset(leds, 0, Num_Leds * sizeof(struct CRGB)); @@ -179,10 +187,7 @@ void adalight(){ // End of data -- issue latch: mode = MODE_HEADER; // Begin next header search FastLED.show(); - - #ifdef DEBUG_LED - digitalWrite(DEBUG_LED, LOW); - #endif + D_LED(OFF); } break; } // end switch From ec44b9335f4f63fdfebc19e2fefb58150830bd17 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 05:40:40 -0400 Subject: [PATCH 02/12] Added DEBUG_FPS macro --- Arduino/LEDstream_FastLED/LEDstream_FastLED.ino | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index f4c4d1b..524018a 100644 --- a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino +++ b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino @@ -30,6 +30,7 @@ static const unsigned long // --- Debug Settings (uncomment to add) //#define DEBUG_LED 13 // toggles the Arduino's built-in LED on header match +//#define DEBUG_FPS 8 // enables a pulse on LED latch // -------------------------------------------------------------------- @@ -64,7 +65,7 @@ static const uint8_t magic[] = { #define MODE_HEADER 0 #define MODE_DATA 1 -// Debug statement initialized +// Debug macros initialized #ifdef DEBUG_LED #define ON 1 #define OFF 0 @@ -74,6 +75,12 @@ static const uint8_t magic[] = { #define D_LED(x) #endif +#ifdef DEBUG_FPS + #define D_FPS do {digitalWrite(DEBUG_FPS, HIGH); digitalWrite(DEBUG_FPS, LOW);} while (0) +#else + #define D_FPS +#endif + void setup(){ #ifdef GROUND_PIN pinMode(GROUND_PIN, OUTPUT); @@ -85,6 +92,10 @@ void setup(){ digitalWrite(DEBUG_LED, LOW); #endif + #ifdef DEBUG_FPS + pinMode(DEBUG_FPS, OUTPUT); + #endif + FastLED.addLeds(leds, Num_Leds); FastLED.setBrightness(Brightness); @@ -187,6 +198,7 @@ void adalight(){ // End of data -- issue latch: mode = MODE_HEADER; // Begin next header search FastLED.show(); + D_FPS; D_LED(OFF); } break; From 6910e162d74dc80a2802b1891adfc5ba900f1d70 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 07:28:29 -0400 Subject: [PATCH 03/12] Separated adalight into functions Makes the code more readable and easier to modify --- .../LEDstream_FastLED/LEDstream_FastLED.ino | 204 +++++++++--------- 1 file changed, 107 insertions(+), 97 deletions(-) diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index 524018a..36fedb6 100644 --- a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino +++ b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino @@ -65,6 +65,19 @@ static const uint8_t magic[] = { #define MODE_HEADER 0 #define MODE_DATA 1 +static uint8_t + mode = MODE_HEADER; +static int16_t + c; +static uint16_t + outPos; +static uint32_t + bytesRemaining; +static unsigned long + t, + lastByteTime, + lastAckTime; + // Debug macros initialized #ifdef DEBUG_LED #define ON 1 @@ -109,23 +122,6 @@ void setup(){ } void adalight(){ - static uint8_t - mode = MODE_HEADER; - static uint8_t - headPos, - hi, lo, chk; - int16_t - c; - static uint16_t - outPos; - static uint32_t - bytesRemaining; - unsigned long - t; - static unsigned long - lastByteTime, - lastAckTime; - Serial.print("Ada\n"); // Send ACK string to host lastByteTime = lastAckTime = millis(); @@ -134,95 +130,109 @@ void adalight(){ // has a measurable impact on this code's overall throughput. for(;;) { - // Implementation is a simple finite-state machine. // Regardless of mode, check for serial input each time: t = millis(); - + if((c = Serial.read()) >= 0){ lastByteTime = lastAckTime = t; // Reset timeout counters - + switch(mode) { - - case MODE_HEADER: - - if(headPos < MAGICSIZE){ - if(c == magic[headPos]) headPos++; - else headPos = 0; - } - else{ - switch(headPos){ - case HICHECK: - hi = c; - headPos++; - break; - case LOCHECK: - lo = c; - headPos++; - break; - case CHECKSUM: - chk = c; - if(chk == (hi ^ lo ^ 0x55)) { - // Checksum looks valid. Get 16-bit LED count, add 1 - // (# LEDs is always > 0) and multiply by 3 for R,G,B. - D_LED(ON); - bytesRemaining = 3L * (256L * (long)hi + (long)lo + 1L); - outPos = 0; - memset(leds, 0, Num_Leds * sizeof(struct CRGB)); - mode = MODE_DATA; // Proceed to latch wait mode - } - headPos = 0; // Reset header position regardless of checksum result - break; - } - } - break; - - case MODE_DATA: - - if(bytesRemaining > 0) { - if (outPos < sizeof(leds)){ - #ifdef CALIBRATE - if(outPos < 3) - ledsRaw[outPos++] = c; - else{ - ledsRaw[outPos] = ledsRaw[outPos%3]; // Sets RGB data to first LED color - outPos++; - } - #else - ledsRaw[outPos++] = c; // Issue next byte - #endif - } - bytesRemaining--; - } - if(bytesRemaining == 0) { - // End of data -- issue latch: - mode = MODE_HEADER; // Begin next header search - FastLED.show(); - D_FPS; - D_LED(OFF); - } - break; - } // end switch - } // end serial if + case MODE_HEADER: + headerMode(); + break; + case MODE_DATA: + dataMode(); + break; + } + } else { - // No data received. If this persists, send an ACK packet - // to host once every second to alert it to our presence. - if((t - lastAckTime) > 1000) { - Serial.print("Ada\n"); // Send ACK string to host - lastAckTime = t; // Reset counter - } - // If no data received for an extended time, turn off all LEDs. - if((t - lastByteTime) > SerialTimeout) { - memset(leds, 0, Num_Leds * sizeof(struct CRGB)); //filling Led array by zeroes - FastLED.show(); - lastByteTime = t; // Reset counter - } - } // end else - } // end for(;;) + timeouts(); + } + } } -void loop() -{ +void headerMode(){ + static uint8_t + headPos, + hi, lo, chk; + + if(headPos < MAGICSIZE){ + if(c == magic[headPos]) headPos++; + else headPos = 0; + } + else{ + switch(headPos){ + case HICHECK: + hi = c; + headPos++; + break; + case LOCHECK: + lo = c; + headPos++; + break; + case CHECKSUM: + chk = c; + if(chk == (hi ^ lo ^ 0x55)) { + // Checksum looks valid. Get 16-bit LED count, add 1 + // (# LEDs is always > 0) and multiply by 3 for R,G,B. + D_LED(ON); + bytesRemaining = 3L * (256L * (long)hi + (long)lo + 1L); + outPos = 0; + memset(leds, 0, Num_Leds * sizeof(struct CRGB)); + mode = MODE_DATA; // Proceed to latch wait mode + } + headPos = 0; // Reset header position regardless of checksum result + break; + } + } +} + +void dataMode(){ + if(bytesRemaining > 0) { + if (outPos < sizeof(leds)){ + dataSet(); + } + bytesRemaining--; + } + if(bytesRemaining == 0) { + // End of data -- issue latch: + mode = MODE_HEADER; // Begin next header search + FastLED.show(); + D_FPS; + D_LED(OFF); + } +} + +void dataSet(){ + #ifdef CALIBRATE + if(outPos < 3) + ledsRaw[outPos++] = c; + else{ + ledsRaw[outPos] = ledsRaw[outPos%3]; // Sets RGB data to first LED color + outPos++; + } + #else + ledsRaw[outPos++] = c; // Issue next byte + #endif +} + +void timeouts(){ + // No data received. If this persists, send an ACK packet + // to host once every second to alert it to our presence. + if((t - lastAckTime) > 1000) { + Serial.print("Ada\n"); // Send ACK string to host + lastAckTime = t; // Reset counter + } + // If no data received for an extended time, turn off all LEDs. + if((t - lastByteTime) > SerialTimeout) { + memset(leds, 0, Num_Leds * sizeof(struct CRGB)); //filling Led array by zeroes + FastLED.show(); + lastByteTime = t; // Reset counter + } +} + +void loop(){ // loop() is avoided as even that small bit of function overhead // has a measurable impact on this code's overall throughput. } From bd87f5f23b7dec8cea706ab8cdf5f07dd6db3625 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 07:33:00 -0400 Subject: [PATCH 04/12] Guarded magic word check if statements Not explicitly needed, but no reason not to protect it --- Arduino/LEDstream_FastLED/LEDstream_FastLED.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index 36fedb6..dc0b5dd 100644 --- a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino +++ b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino @@ -158,8 +158,8 @@ void headerMode(){ hi, lo, chk; if(headPos < MAGICSIZE){ - if(c == magic[headPos]) headPos++; - else headPos = 0; + if(c == magic[headPos]) {headPos++;} + else {headPos = 0;} } else{ switch(headPos){ From 92362303b854e52b284cb5fb1600680f13ac7a59 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 07:54:41 -0400 Subject: [PATCH 05/12] Removed 'bytesRemaining' check Slight efficiency improvement. In data mode bytes remaining is always greater than 0, because the LED count is always set to greater than 0 in the header setting. When it reaches 0 mode is immediately switched. --- Arduino/LEDstream_FastLED/LEDstream_FastLED.ino | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index dc0b5dd..177ca7d 100644 --- a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino +++ b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino @@ -189,12 +189,11 @@ void headerMode(){ } void dataMode(){ - if(bytesRemaining > 0) { - if (outPos < sizeof(leds)){ - dataSet(); - } - bytesRemaining--; + if (outPos < sizeof(leds)){ + dataSet(); } + bytesRemaining--; + if(bytesRemaining == 0) { // End of data -- issue latch: mode = MODE_HEADER; // Begin next header search From 92e65abde71c7b53797e3dd5550ef42ca58889b7 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 08:07:35 -0400 Subject: [PATCH 06/12] Nested serial timeout check Should give a significant performance improvement, reducing the delay before a received byte is processed. Limits timeout periods to increments of 1000, though. --- Arduino/LEDstream_FastLED/LEDstream_FastLED.ino | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index 177ca7d..f5a3e6d 100644 --- a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino +++ b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino @@ -222,12 +222,13 @@ void timeouts(){ if((t - lastAckTime) > 1000) { Serial.print("Ada\n"); // Send ACK string to host lastAckTime = t; // Reset counter - } - // If no data received for an extended time, turn off all LEDs. - if((t - lastByteTime) > SerialTimeout) { - memset(leds, 0, Num_Leds * sizeof(struct CRGB)); //filling Led array by zeroes - FastLED.show(); - lastByteTime = t; // Reset counter + + // If no data received for an extended time, turn off all LEDs. + if((t - lastByteTime) > SerialTimeout) { + memset(leds, 0, Num_Leds * sizeof(struct CRGB)); //filling Led array by zeroes + FastLED.show(); + lastByteTime = t; // Reset counter + } } } From 0e16794589906c778d164b2faf136be1f1f59ec0 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 08:20:23 -0400 Subject: [PATCH 07/12] Changed 'SerialTimeout' to use seconds More intuitive to set, no change in functionality --- Arduino/LEDstream_FastLED/LEDstream_FastLED.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index f5a3e6d..f95906b 100644 --- a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino +++ b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino @@ -19,9 +19,9 @@ static const uint8_t // --- Serial Settings static const unsigned long - SerialSpeed = 115200, // serial port speed, max available - SerialTimeout = 150000; // time before LEDs are shut off, if no data - // (150 seconds) + SerialSpeed = 115200; // serial port speed, max available +static const uint16_t + SerialTimeout = 150; // time before LEDs are shut off if no data (in seconds) // --- Optional Settings (uncomment to add) //#define CLEAR_ON_START // LEDs are cleared on reset @@ -224,7 +224,7 @@ void timeouts(){ lastAckTime = t; // Reset counter // If no data received for an extended time, turn off all LEDs. - if((t - lastByteTime) > SerialTimeout) { + if((t - lastByteTime) > SerialTimeout * 1000) { memset(leds, 0, Num_Leds * sizeof(struct CRGB)); //filling Led array by zeroes FastLED.show(); lastByteTime = t; // Reset counter From e9092c39fe92783e4ab4ac9b1dd35324421d25b6 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 09:05:47 -0400 Subject: [PATCH 08/12] Removed 'loop' avoidance Cleans up the code slightly. If the compiler is smart, the functions are inlined regardless. --- .../LEDstream_FastLED/LEDstream_FastLED.ino | 49 ++++++++----------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index f95906b..6036076 100644 --- a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino +++ b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino @@ -117,39 +117,35 @@ void setup(){ #endif Serial.begin(SerialSpeed); + Serial.print("Ada\n"); // Send ACK string to host + lastByteTime = lastAckTime = millis(); // Set initial counters +} + +void loop(){ adalight(); } void adalight(){ - Serial.print("Ada\n"); // Send ACK string to host + // Implementation is a simple finite-state machine. + // Regardless of mode, check for serial input each time: + t = millis(); - lastByteTime = lastAckTime = millis(); + if((c = Serial.read()) >= 0){ + lastByteTime = lastAckTime = t; // Reset timeout counters - // loop() is avoided as even that small bit of function overhead - // has a measurable impact on this code's overall throughput. - - for(;;) { - // Implementation is a simple finite-state machine. - // Regardless of mode, check for serial input each time: - t = millis(); - - if((c = Serial.read()) >= 0){ - lastByteTime = lastAckTime = t; // Reset timeout counters - - switch(mode) { - case MODE_HEADER: - headerMode(); - break; - case MODE_DATA: - dataMode(); - break; - } - } - else { - timeouts(); + switch(mode) { + case MODE_HEADER: + headerMode(); + break; + case MODE_DATA: + dataMode(); + break; } } + else { + timeouts(); + } } void headerMode(){ @@ -231,8 +227,3 @@ void timeouts(){ } } } - -void loop(){ - // loop() is avoided as even that small bit of function overhead - // has a measurable impact on this code's overall throughput. -} From 203abb87408e5926719fc4e262e7b03b1760be54 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 09:16:01 -0400 Subject: [PATCH 09/12] Added additional comments --- Arduino/LEDstream_FastLED/LEDstream_FastLED.ino | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index 6036076..0e31f8a 100644 --- a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino +++ b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino @@ -127,10 +127,9 @@ void loop(){ } void adalight(){ - // Implementation is a simple finite-state machine. - // Regardless of mode, check for serial input each time: - t = millis(); + t = millis(); // Save current time + // If there is new serial data if((c = Serial.read()) >= 0){ lastByteTime = lastAckTime = t; // Reset timeout counters @@ -144,6 +143,7 @@ void adalight(){ } } else { + // No new data timeouts(); } } @@ -154,10 +154,12 @@ void headerMode(){ hi, lo, chk; if(headPos < MAGICSIZE){ + // Check if magic word matches if(c == magic[headPos]) {headPos++;} else {headPos = 0;} } else{ + // Magic word matches! Now verify checksum switch(headPos){ case HICHECK: hi = c; @@ -185,6 +187,7 @@ void headerMode(){ } void dataMode(){ + // If LED data is not full if (outPos < sizeof(leds)){ dataSet(); } From 65e26ed88fb4a3dff61217349c231f7a3396673c Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 09:18:27 -0400 Subject: [PATCH 10/12] Updated date --- Arduino/LEDstream_FastLED/LEDstream_FastLED.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index 0e31f8a..a662252 100644 --- a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino +++ b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino @@ -4,7 +4,7 @@ * library (http://fastled.io) for driving led strips. * * http://github.com/dmadison/Adalight-FastLED - * Last Updated: 2017-04-23 + * Last Updated: 2017-05-05 */ // --- General Settings From 1e744591cc2f66ed61e0c816e83e9ae23906b05f Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 09:34:50 -0400 Subject: [PATCH 11/12] Changed Lightpack link to point to release page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a58576..5f6bb05 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Additional settings allow for adjusting: There are also optional settings to clear the LEDs on reset, configure a dedicated ground pin, and to put the Arduino into a "calibration" mode, where all LED colors match the first LED. -Upload to your Arduino and use a corresponding PC application to stream color data. You can get the Processing files from the [main Adalight repository](https://github.com/adafruit/Adalight), though I would recommend using [Patrick Siegler's](https://github.com/psieg/) fork of Lightpacks's Prismatik, which you can find [here](https://github.com/psieg/Lightpack). +Upload to your Arduino and use a corresponding PC application to stream color data. You can get the Processing files from the [main Adalight repository](https://github.com/adafruit/Adalight), though I would recommend using [Patrick Siegler's](https://github.com/psieg/) fork of Lightpacks's Prismatik, which you can find [here](https://github.com/psieg/Lightpack/releases). ## Issues and LED-types From 9bde209fced82031e91273f339639042f998941c Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 May 2017 09:45:20 -0400 Subject: [PATCH 12/12] Added debug settings section --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 5f6bb05..248fc58 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,18 @@ There are also optional settings to clear the LEDs on reset, configure a dedicat Upload to your Arduino and use a corresponding PC application to stream color data. You can get the Processing files from the [main Adalight repository](https://github.com/adafruit/Adalight), though I would recommend using [Patrick Siegler's](https://github.com/psieg/) fork of Lightpacks's Prismatik, which you can find [here](https://github.com/psieg/Lightpack/releases). +## Debug Settings + +The code includes two debugging options: +- DEBUG_LED +- DEBUG_FPS + +`DEBUG_LED` will turn on the Arduino's built-in LED on a successful header match, and off when the LEDs latch. If your LEDs aren't working, this will help confirm that the Arduino is receiving properly formatted serial data. + +`DEBUG_FPS`, similarly, will toggle a given pin when the LEDs latch. This is useful for measuring framerate with external hardware, like a logic analyzer. + +To enable either of these settings, uncomment their respective '#define' lines. + ## Issues and LED-types I've only tested the code with the WS2812B strips I have on hand, but so far it performs flawlessly. If you find an issue with the code or can confirm that it works with another chipset, please let me know!