diff --git a/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino b/Arduino/LEDstream_FastLED/LEDstream_FastLED.ino index aec1dad..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 @@ -19,16 +19,18 @@ 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 //#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 +//#define DEBUG_FPS 8 // enables a pulse on LED latch // -------------------------------------------------------------------- @@ -63,6 +65,35 @@ 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 + #define OFF 0 + + #define D_LED(x) do {digitalWrite(DEBUG_LED, x);} while(0) +#else + #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); @@ -74,6 +105,10 @@ void setup(){ digitalWrite(DEBUG_LED, LOW); #endif + #ifdef DEBUG_FPS + pinMode(DEBUG_FPS, OUTPUT); + #endif + FastLED.addLeds(leds, Num_Leds); FastLED.setBrightness(Brightness); @@ -82,130 +117,116 @@ 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(){ - static uint8_t - mode = MODE_HEADER; + t = millis(); // Save current time + + // If there is new serial data + if((c = Serial.read()) >= 0){ + lastByteTime = lastAckTime = t; // Reset timeout counters + + switch(mode) { + case MODE_HEADER: + headerMode(); + break; + case MODE_DATA: + dataMode(); + break; + } + } + else { + // No new data + timeouts(); + } +} + +void headerMode(){ 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(); - - // 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: - - 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. - #ifdef DEBUG_LED - digitalWrite(DEBUG_LED, HIGH); - #endif - - 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; - } - } + 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; + headPos++; 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(); - - #ifdef DEBUG_LED - digitalWrite(DEBUG_LED, LOW); - #endif - } + case LOCHECK: + lo = c; + headPos++; break; - } // end switch - } // end serial if - 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(;;) + 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 loop() -{ - // loop() is avoided as even that small bit of function overhead - // has a measurable impact on this code's overall throughput. +void dataMode(){ + // If LED data is not full + 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 * 1000) { + memset(leds, 0, Num_Leds * sizeof(struct CRGB)); //filling Led array by zeroes + FastLED.show(); + lastByteTime = t; // Reset counter + } + } } diff --git a/README.md b/README.md index 1a58576..248fc58 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,19 @@ 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). + +## 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