LEDstream.ino
  1  // SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
  2  //
  3  // SPDX-License-Identifier: MIT
  4  
  5  // Arduino "bridge" code between host computer and WS2801-based digital
  6  // RGB LED pixels (e.g. Adafruit product ID #322).  Intended for use
  7  // with USB-native boards such as Teensy or Adafruit 32u4 Breakout;
  8  // works on normal serial Arduinos, but throughput is severely limited.
  9  // LED data is streamed, not buffered, making this suitable for larger
 10  // installations (e.g. video wall, etc.) than could otherwise be held
 11  // in the Arduino's limited RAM.
 12  
 13  // Some effort is put into avoiding buffer underruns (where the output
 14  // side becomes starved of data).  The WS2801 latch protocol, being
 15  // delay-based, could be inadvertently triggered if the USB bus or CPU
 16  // is swamped with other tasks.  This code buffers incoming serial data
 17  // and introduces intentional pauses if there's a threat of the buffer
 18  // draining prematurely.  The cost of this complexity is somewhat
 19  // reduced throughput, the gain is that most visual glitches are
 20  // avoided (though ultimately a function of the load on the USB bus and
 21  // host CPU, and out of our control).
 22  
 23  // LED data and clock lines are connected to the Arduino's SPI output.
 24  // On traditional Arduino boards, SPI data out is digital pin 11 and
 25  // clock is digital pin 13.  On both Teensy and the 32u4 Breakout,
 26  // data out is pin B2, clock is B1.  LEDs should be externally
 27  // powered -- trying to run any more than just a few off the Arduino's
 28  // 5V line is generally a Bad Idea.  LED ground should also be
 29  // connected to Arduino ground.
 30  
 31  #include <SPI.h>
 32  
 33  // LED pin for Adafruit 32u4 Breakout Board:
 34  //#define LED_DDR  DDRE
 35  //#define LED_PORT PORTE
 36  //#define LED_PIN  _BV(PORTE6)
 37  // LED pin for Teensy:
 38  //#define LED_DDR  DDRD
 39  //#define LED_PORT PORTD
 40  //#define LED_PIN  _BV(PORTD6)
 41  // LED pin for Arduino:
 42  #define LED_DDR  DDRB
 43  #define LED_PORT PORTB
 44  #define LED_PIN  _BV(PORTB5)
 45  
 46  // A 'magic word' (along with LED count & checksum) precedes each block
 47  // of LED data; this assists the microcontroller in syncing up with the
 48  // host-side software and properly issuing the latch (host I/O is
 49  // likely buffered, making usleep() unreliable for latch).  You may see
 50  // an initial glitchy frame or two until the two come into alignment.
 51  // The magic word can be whatever sequence you like, but each character
 52  // should be unique, and frequent pixel values like 0 and 255 are
 53  // avoided -- fewer false positives.  The host software will need to
 54  // generate a compatible header: immediately following the magic word
 55  // are three bytes: a 16-bit count of the number of LEDs (high byte
 56  // first) followed by a simple checksum value (high byte XOR low byte
 57  // XOR 0x55).  LED data follows, 3 bytes per LED, in order R, G, B,
 58  // where 0 = off and 255 = max brightness.
 59  
 60  static const uint8_t magic[] = {'A','d','a'};
 61  #define MAGICSIZE  sizeof(magic)
 62  #define HEADERSIZE (MAGICSIZE + 3)
 63  
 64  #define MODE_HEADER 0
 65  #define MODE_HOLD   1
 66  #define MODE_DATA   2
 67  
 68  // If no serial data is received for a while, the LEDs are shut off
 69  // automatically.  This avoids the annoying "stuck pixel" look when
 70  // quitting LED display programs on the host computer.
 71  static const unsigned long serialTimeout = 15000; // 15 seconds
 72  
 73  void setup()
 74  {
 75    // Dirty trick: the circular buffer for serial data is 256 bytes,
 76    // and the "in" and "out" indices are unsigned 8-bit types -- this
 77    // much simplifies the cases where in/out need to "wrap around" the
 78    // beginning/end of the buffer.  Otherwise there'd be a ton of bit-
 79    // masking and/or conditional code every time one of these indices
 80    // needs to change, slowing things down tremendously.
 81    uint8_t
 82      buffer[256],
 83      indexIn       = 0,
 84      indexOut      = 0,
 85      mode          = MODE_HEADER,
 86      hi, lo, chk, i, spiFlag;
 87    int16_t
 88      bytesBuffered = 0,
 89      hold          = 0,
 90      c;
 91    int32_t
 92      bytesRemaining;
 93    unsigned long
 94      startTime,
 95      lastByteTime,
 96      lastAckTime,
 97      t;
 98  
 99    LED_DDR  |=  LED_PIN; // Enable output for LED
100    LED_PORT &= ~LED_PIN; // LED off
101  
102    Serial.begin(115200); // Teensy/32u4 disregards baud rate; is OK!
103  
104    SPI.begin();
105    SPI.setBitOrder(MSBFIRST);
106    SPI.setDataMode(SPI_MODE0);
107    SPI.setClockDivider(SPI_CLOCK_DIV16); // 1 MHz max, else flicker
108  
109    // Issue test pattern to LEDs on startup.  This helps verify that
110    // wiring between the Arduino and LEDs is correct.  Not knowing the
111    // actual number of LEDs connected, this sets all of them (well, up
112    // to the first 25,000, so as not to be TOO time consuming) to red,
113    // green, blue, then off.  Once you're confident everything is working
114    // end-to-end, it's OK to comment this out and reprogram the Arduino.
115    uint8_t testcolor[] = { 0, 0, 0, 255, 0, 0 };
116    for(char n=3; n>=0; n--) {
117      for(c=0; c<25000; c++) {
118        for(i=0; i<3; i++) {
119          for(SPDR = testcolor[n + i]; !(SPSR & _BV(SPIF)); );
120        }
121      }
122      delay(1); // One millisecond pause = latch
123    }
124  
125    Serial.print("Ada\n"); // Send ACK string to host
126  
127    startTime    = micros();
128    lastByteTime = lastAckTime = millis();
129  
130    // loop() is avoided as even that small bit of function overhead
131    // has a measurable impact on this code's overall throughput.
132  
133    for(;;) {
134  
135      // Implementation is a simple finite-state machine.
136      // Regardless of mode, check for serial input each time:
137      t = millis();
138      if((bytesBuffered < 256) && ((c = Serial.read()) >= 0)) {
139        buffer[indexIn++] = c;
140        bytesBuffered++;
141        lastByteTime = lastAckTime = t; // Reset timeout counters
142      } else {
143        // No data received.  If this persists, send an ACK packet
144        // to host once every second to alert it to our presence.
145        if((t - lastAckTime) > 1000) {
146          Serial.print("Ada\n"); // Send ACK string to host
147          lastAckTime = t; // Reset counter
148        }
149        // If no data received for an extended time, turn off all LEDs.
150        if((t - lastByteTime) > serialTimeout) {
151          for(c=0; c<32767; c++) {
152            for(SPDR=0; !(SPSR & _BV(SPIF)); );
153          }
154          delay(1); // One millisecond pause = latch
155          lastByteTime = t; // Reset counter
156        }
157      }
158  
159      switch(mode) {
160  
161       case MODE_HEADER:
162  
163        // In header-seeking mode.  Is there enough data to check?
164        if(bytesBuffered >= HEADERSIZE) {
165          // Indeed.  Check for a 'magic word' match.
166          for(i=0; (i<MAGICSIZE) && (buffer[indexOut++] == magic[i++]););
167          if(i == MAGICSIZE) {
168            // Magic word matches.  Now how about the checksum?
169            hi  = buffer[indexOut++];
170            lo  = buffer[indexOut++];
171            chk = buffer[indexOut++];
172            if(chk == (hi ^ lo ^ 0x55)) {
173              // Checksum looks valid.  Get 16-bit LED count, add 1
174              // (# LEDs is always > 0) and multiply by 3 for R,G,B.
175              bytesRemaining = 3L * (256L * (long)hi + (long)lo + 1L);
176              bytesBuffered -= 3;
177              spiFlag        = 0;         // No data out yet
178              mode           = MODE_HOLD; // Proceed to latch wait mode
179            } else {
180              // Checksum didn't match; search resumes after magic word.
181              indexOut  -= 3; // Rewind
182            }
183          } // else no header match.  Resume at first mismatched byte.
184          bytesBuffered -= i;
185        }
186        break;
187  
188       case MODE_HOLD:
189  
190        // Ostensibly "waiting for the latch from the prior frame
191        // to complete" mode, but may also revert to this mode when
192        // underrun prevention necessitates a delay.
193  
194        if((micros() - startTime) < hold) break; // Still holding; keep buffering
195  
196        // Latch/delay complete.  Advance to data-issuing mode...
197        LED_PORT &= ~LED_PIN;  // LED off
198        mode      = MODE_DATA; // ...and fall through (no break):
199  
200       case MODE_DATA:
201  
202        while(spiFlag && !(SPSR & _BV(SPIF))); // Wait for prior byte
203        if(bytesRemaining > 0) {
204          if(bytesBuffered > 0) {
205            SPDR = buffer[indexOut++];   // Issue next byte
206            bytesBuffered--;
207            bytesRemaining--;
208            spiFlag = 1;
209          }
210          // If serial buffer is threatening to underrun, start
211          // introducing progressively longer pauses to allow more
212          // data to arrive (up to a point).
213          if((bytesBuffered < 32) && (bytesRemaining > bytesBuffered)) {
214            startTime = micros();
215            hold      = 100 + (32 - bytesBuffered) * 10;
216            mode      = MODE_HOLD;
217  	}
218        } else {
219          // End of data -- issue latch:
220          startTime  = micros();
221          hold       = 1000;        // Latch duration = 1000 uS
222          LED_PORT  |= LED_PIN;     // LED on
223          mode       = MODE_HEADER; // Begin next header search
224        }
225      } // end switch
226    } // end for(;;)
227  }
228  
229  void loop()
230  {
231    // Not used.  See note in setup() function.
232  }