From 589d11bfd1e1cfc7a383bc0e6e09d9c5ec02288d Mon Sep 17 00:00:00 2001 From: manuel Date: Sun, 26 Mar 2017 01:07:06 +0100 Subject: Initial commit --- hyperion/hyperion.ino | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 hyperion/hyperion.ino (limited to 'hyperion') diff --git a/hyperion/hyperion.ino b/hyperion/hyperion.ino new file mode 100644 index 0000000..75e6858 --- /dev/null +++ b/hyperion/hyperion.ino @@ -0,0 +1,279 @@ +// Arduino "bridge" code between host computer and WS2801-based digital +// RGB LED pixels (e.g. Adafruit product ID #322). Intended for use +// with USB-native boards such as Teensy or Adafruit 32u4 Breakout; +// works on normal serial Arduinos, but throughput is severely limited. +// LED data is streamed, not buffered, making this suitable for larger +// installations (e.g. video wall, etc.) than could otherwise be held +// in the Arduino's limited RAM. + +// Some effort is put into avoiding buffer underruns (where the output +// side becomes starved of data). The WS2801 latch protocol, being +// delay-based, could be inadvertently triggered if the USB bus or CPU +// is swamped with other tasks. This code buffers incoming serial data +// and introduces intentional pauses if there's a threat of the buffer +// draining prematurely. The cost of this complexity is somewhat +// reduced throughput, the gain is that most visual glitches are +// avoided (though ultimately a function of the load on the USB bus and +// host CPU, and out of our control). + +// LED data and clock lines are connected to the Arduino's SPI output. +// On traditional Arduino boards, SPI data out is digital pin 11 and +// clock is digital pin 13. On both Teensy and the 32u4 Breakout, +// data out is pin B2, clock is B1. LEDs should be externally +// powered -- trying to run any more than just a few off the Arduino's +// 5V line is generally a Bad Idea. LED ground should also be +// connected to Arduino ground. + +// -------------------------------------------------------------------- +// This file is part of Adalight. + +// Adalight is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. + +// Adalight is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with Adalight. If not, see +// . +// -------------------------------------------------------------------- + + +// -------------------------------------------------------------------- +// Note: This is a fork form the LEDstream. +// +// This version uses the LIB FastLED to work with the WS2812b LEDs. +// +// This Version works with a 150LED-Stripe with a frames/sec: 61, bytes/sec: 11524 +// tested with the programme colorswirl (see the C folder) and an Arduino UNO. +// +// TOOD: +// - Cleanup: remove the SPI code +// - Show a startup Pattern +// - remove flicker when sending more the 63 frames/sec. +// +// -------------------------------------------------------------------- + +#include +#include +#include + +#define NUM_LEDS 190 +#define DATA_PIN 3 +CRGB leds[NUM_LEDS]; + +// A 'magic word' (along with LED count & checksum) precedes each block +// of LED data; this assists the microcontroller in syncing up with the +// host-side software and properly issuing the latch (host I/O is +// likely buffered, making usleep() unreliable for latch). You may see +// an initial glitchy frame or two until the two come into alignment. +// The magic word can be whatever sequence you like, but each character +// should be unique, and frequent pixel values like 0 and 255 are +// avoided -- fewer false positives. The host software will need to +// generate a compatible header: immediately following the magic word +// are three bytes: a 16-bit count of the number of LEDs (high byte +// first) followed by a simple checksum value (high byte XOR low byte +// XOR 0x55). LED data follows, 3 bytes per LED, in order R, G, B, +// where 0 = off and 255 = max brightness. + +static const uint8_t magic[] = {'A','d','a'}; +#define MAGICSIZE sizeof(magic) +#define HEADERSIZE (MAGICSIZE + 3) + +#define MODE_HEADER 0 +#define MODE_HOLD 1 +#define MODE_DATA 2 + +// If no serial data is received for a while, the LEDs are shut off +// automatically. This avoids the annoying "stuck pixel" look when +// quitting LED display programs on the host computer. +static const unsigned long serialTimeout = 15000; // 15 seconds + +void setup() +{ + // Dirty trick: the circular buffer for serial data is 256 bytes, + // and the "in" and "out" indices are unsigned 8-bit types -- this + // much simplifies the cases where in/out need to "wrap around" the + // beginning/end of the buffer. Otherwise there'd be a ton of bit- + // masking and/or conditional code every time one of these indices + // needs to change, slowing things down tremendously. + uint8_t + buffer[256], + indexIn = 0, + indexOut = 0, + mode = MODE_HEADER, + hi, lo, chk, i, spiFlag, + r,b,g, l; + int16_t + bytesBuffered = 0, + hold = 0, + c; + int32_t + bytesRemaining; + unsigned long + startTime, + lastByteTime, + lastAckTime, + t; + uint32_t value,index = 0, ledcount; + + delay(5000); + +#if 1 + power_adc_disable(); + power_usart0_disable(); + power_usart1_disable(); + power_spi_disable(); + power_twi_disable(); + power_timer1_disable(); + power_timer2_disable(); + power_timer3_disable(); +#endif + + FastLED.addLeds(leds, NUM_LEDS); + + Serial.begin(115200); // Teensy/32u4 disregards baud rate; is OK! + + //set the last LED to white + leds[NUM_LEDS-1].setRGB(100,100,100); + FastLED.show(); + //delay(1000); + //reset(); + //delay(1000); + + Serial.print("Ada\n"); // Send ACK string to host + RXLED1; + TXLED1; + + startTime = micros(); + 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(;;) + { + RXLED1; + TXLED1; + + // Implementation is a simple finite-state machine. + // Regardless of mode, check for serial input each time: + t = millis(); + if ((bytesBuffered < 256) && ((c = Serial.read()) >= 0)) + { + buffer[indexIn++] = c; + bytesBuffered++; + lastByteTime = lastAckTime = t; // Reset timeout counters + } + 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) + { + reset(); + delay(1); // One millisecond pause = latch + lastByteTime = t; // Reset counter + } + } + + switch(mode) + { + case MODE_HEADER: + // In header-seeking mode. Is there enough data to check? + if(bytesBuffered >= HEADERSIZE) + { + // Indeed. Check for a 'magic word' match. + for(i=0; (i 0) and multiply by 3 for R,G,B. + bytesRemaining = 3L * (256L * (long)hi + (long)lo + 1L); + bytesBuffered -= 3; + ledcount = 0; + mode = MODE_HOLD; // Proceed to latch wait mode + } + else + { + // Checksum didn't match; search resumes after magic word. + indexOut -= 3; // Rewind + } + } // else no header match. Resume at first mismatched byte. + bytesBuffered -= i; + } + break; + + case MODE_HOLD: + // Ostensibly "waiting for the latch from the prior frame + // to complete" mode, but may also revert to this mode when + // underrun prevention necessitates a delay. + + if((micros() - startTime) < hold) break; // Still holding; keep buffering + + // Latch/delay complete. Advance to data-issuing mode... + //LED_PORT &= ~LED_PIN; // LED off + mode = MODE_DATA; // ...and fall through (no break): + + case MODE_DATA: + if(bytesRemaining > 2) + { + if(bytesBuffered > 2) + { + //we read one LED -> 3 Bytes r.g.b + bytesBuffered -= 3; + bytesRemaining -= 3; + leds[ledcount++].setRGB(buffer[indexOut++], buffer[indexOut++], buffer[indexOut++]); + } + // If serial buffer is threatening to underrun, start + // introducing progressively longer pauses to allow more + // data to arrive (up to a point). + if((bytesBuffered < 32) && (bytesRemaining > bytesBuffered) && (mode!=MODE_HEADER)) + { + startTime = micros(); + hold = 100 + (32 - bytesBuffered) * 10; + mode = MODE_HOLD; + } + } + else + { + // End of data -- issue latch: + startTime = micros(); + hold = 1000; // Latch duration = 1000 uS + //LED_PORT |= LED_PIN; // LED on + mode = MODE_HEADER; // Begin next header search + FastLED.show(); + } + } // end switch + } // end for(;;) +} + +void reset() +{ + for (uint16_t i=0; i< NUM_LEDS; i++) + leds[i] = CRGB::Black; + FastLED.show(); +} + +void loop() +{ + // Not used. See note in setup() function. +} + + -- cgit v1.2.3