Prototype of processing code on teensy 3.1 & OctoWS2811
parent
30c7b90032
commit
c2ef20184b
|
@ -0,0 +1,251 @@
|
||||||
|
/* OctoWS2811 VideoDisplay.ino - Video on LEDs, from a PC, Mac, Raspberry Pi
|
||||||
|
http://www.pjrc.com/teensy/td_libs_OctoWS2811.html
|
||||||
|
Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
Required Connections
|
||||||
|
--------------------
|
||||||
|
pin 2: LED Strip #1 OctoWS2811 drives 8 LED Strips.
|
||||||
|
pin 14: LED strip #2 All 8 are the same length.
|
||||||
|
pin 7: LED strip #3
|
||||||
|
pin 8: LED strip #4 A 100 to 220 ohm resistor should used
|
||||||
|
pin 6: LED strip #5 between each Teensy pin and the
|
||||||
|
pin 20: LED strip #6 wire to the LED strip, to minimize
|
||||||
|
pin 21: LED strip #7 high frequency ringining & noise.
|
||||||
|
pin 5: LED strip #8
|
||||||
|
pin 15 & 16 - Connect together, but do not use
|
||||||
|
pin 4: Do not use
|
||||||
|
pin 3: Do not use as PWM. Normal use is ok.
|
||||||
|
pin 12: Frame Sync
|
||||||
|
|
||||||
|
When using more than 1 Teensy to display a video image, connect
|
||||||
|
the Frame Sync signal between every board. All boards will
|
||||||
|
synchronize their WS2811 update using this signal.
|
||||||
|
|
||||||
|
Beware of image distortion from long LED strip lengths. During
|
||||||
|
the WS2811 update, the LEDs update in sequence, not all at the
|
||||||
|
same instant! The first pixel updates after 30 microseconds,
|
||||||
|
the second pixel after 60 us, and so on. A strip of 120 LEDs
|
||||||
|
updates in 3.6 ms, which is 10.8% of a 30 Hz video frame time.
|
||||||
|
Doubling the strip length to 240 LEDs increases the lag to 21.6%
|
||||||
|
of a video frame. For best results, use shorter length strips.
|
||||||
|
Multiple boards linked by the frame sync signal provides superior
|
||||||
|
video timing accuracy.
|
||||||
|
|
||||||
|
A Multi-TT USB hub should be used if 2 or more Teensy boards
|
||||||
|
are connected. The Multi-TT feature allows proper USB bandwidth
|
||||||
|
allocation. Single-TT hubs, or direct connection to multiple
|
||||||
|
ports on the same motherboard, may give poor performance.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <OctoWS2811.h>
|
||||||
|
|
||||||
|
// The actual arrangement of the LEDs connected to this Teensy 3.0 board.
|
||||||
|
// LED_HEIGHT *must* be a multiple of 8. When 16, 24, 32 are used, each
|
||||||
|
// strip spans 2, 3, 4 rows. LED_LAYOUT indicates the direction the strips
|
||||||
|
// are arranged. If 0, each strip begins on the left for its first row,
|
||||||
|
// then goes right to left for its second row, then left to right,
|
||||||
|
// zig-zagging for each successive row.
|
||||||
|
#define LED_WIDTH 50 // number of LEDs horizontally
|
||||||
|
#define LED_HEIGHT 1 // number of LEDs vertically (must be multiple of 8)
|
||||||
|
#define LED_LAYOUT 0 // 0 = even rows left->right, 1 = even rows right->left
|
||||||
|
|
||||||
|
// The portion of the video image to show on this set of LEDs. All 4 numbers
|
||||||
|
// are percentages, from 0 to 100. For a large LED installation with many
|
||||||
|
// Teensy 3.0 boards driving groups of LEDs, these parameters allow you to
|
||||||
|
// program each Teensy to tell the video application which portion of the
|
||||||
|
// video it displays. By reading these numbers, the video application can
|
||||||
|
// automatically configure itself, regardless of which serial port COM number
|
||||||
|
// or device names are assigned to each Teensy 3.0 by your operating system.
|
||||||
|
#define VIDEO_XOFFSET 0
|
||||||
|
#define VIDEO_YOFFSET 0 // display entire image
|
||||||
|
#define VIDEO_WIDTH 10
|
||||||
|
#define VIDEO_HEIGHT 10
|
||||||
|
|
||||||
|
//#define VIDEO_XOFFSET 0
|
||||||
|
//#define VIDEO_YOFFSET 0 // display upper half
|
||||||
|
//#define VIDEO_WIDTH 100
|
||||||
|
//#define VIDEO_HEIGHT 50
|
||||||
|
|
||||||
|
//#define VIDEO_XOFFSET 0
|
||||||
|
//#define VIDEO_YOFFSET 50 // display lower half
|
||||||
|
//#define VIDEO_WIDTH 100
|
||||||
|
//#define VIDEO_HEIGHT 50
|
||||||
|
|
||||||
|
|
||||||
|
//const int ledsPerStrip = LED_WIDTH * LED_HEIGHT / 8;
|
||||||
|
const int ledsPerStrip = 40;
|
||||||
|
|
||||||
|
DMAMEM int displayMemory[ledsPerStrip*6];
|
||||||
|
int drawingMemory[ledsPerStrip*6];
|
||||||
|
elapsedMicros elapsedUsecSinceLastFrameSync = 0;
|
||||||
|
|
||||||
|
const int config = WS2811_800kHz; // color config is on the PC side
|
||||||
|
|
||||||
|
OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(13, OUTPUT);
|
||||||
|
digitalWrite(13, HIGH);
|
||||||
|
delay(1000);
|
||||||
|
digitalWrite(13, LOW);
|
||||||
|
pinMode(12, INPUT_PULLUP); // Frame Sync
|
||||||
|
Serial.setTimeout(50);
|
||||||
|
leds.begin();
|
||||||
|
leds.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
//
|
||||||
|
// wait for a Start-Of-Message character:
|
||||||
|
//
|
||||||
|
// '*' = Frame of image data, with frame sync pulse to be sent
|
||||||
|
// a specified number of microseconds after reception of
|
||||||
|
// the first byte (typically at 75% of the frame time, to
|
||||||
|
// allow other boards to fully receive their data).
|
||||||
|
// Normally '*' is used when the sender controls the pace
|
||||||
|
// of playback by transmitting each frame as it should
|
||||||
|
// appear.
|
||||||
|
//
|
||||||
|
// '$' = Frame of image data, with frame sync pulse to be sent
|
||||||
|
// a specified number of microseconds after the previous
|
||||||
|
// frame sync. Normally this is used when the sender
|
||||||
|
// transmits each frame as quickly as possible, and we
|
||||||
|
// control the pacing of video playback by updating the
|
||||||
|
// LEDs based on time elapsed from the previous frame.
|
||||||
|
//
|
||||||
|
// '%' = Frame of image data, to be displayed with a frame sync
|
||||||
|
// pulse is received from another board. In a multi-board
|
||||||
|
// system, the sender would normally transmit one '*' or '$'
|
||||||
|
// message and '%' messages to all other boards, so every
|
||||||
|
// Teensy 3.0 updates at the exact same moment.
|
||||||
|
//
|
||||||
|
// '@' = Reset the elapsed time, used for '$' messages. This
|
||||||
|
// should be sent before the first '$' message, so many
|
||||||
|
// frames are not played quickly if time as elapsed since
|
||||||
|
// startup or prior video playing.
|
||||||
|
//
|
||||||
|
// '?' = Query LED and Video parameters. Teensy 3.0 responds
|
||||||
|
// with a comma delimited list of information.
|
||||||
|
//
|
||||||
|
int startChar = Serial.read();
|
||||||
|
|
||||||
|
if (startChar == '*') {
|
||||||
|
// receive a "master" frame - we send the frame sync to other boards
|
||||||
|
// the sender is controlling the video pace. The 16 bit number is
|
||||||
|
// how far into this frame to send the sync to other boards.
|
||||||
|
unsigned int startAt = micros();
|
||||||
|
unsigned int usecUntilFrameSync = 0;
|
||||||
|
int count = Serial.readBytes((char *)&usecUntilFrameSync, 2);
|
||||||
|
if (count != 2) return;
|
||||||
|
count = Serial.readBytes((char *)drawingMemory, sizeof(drawingMemory));
|
||||||
|
if (count >= sizeof(drawingMemory)) {
|
||||||
|
unsigned int endAt = micros();
|
||||||
|
unsigned int usToWaitBeforeSyncOutput = 100;
|
||||||
|
if (endAt - startAt < usecUntilFrameSync) {
|
||||||
|
usToWaitBeforeSyncOutput = usecUntilFrameSync - (endAt - startAt);
|
||||||
|
}
|
||||||
|
digitalWrite(12, HIGH);
|
||||||
|
pinMode(12, OUTPUT);
|
||||||
|
delayMicroseconds(usToWaitBeforeSyncOutput);
|
||||||
|
digitalWrite(12, LOW);
|
||||||
|
// WS2811 update begins immediately after falling edge of frame sync
|
||||||
|
digitalWrite(13, HIGH);
|
||||||
|
leds.show();
|
||||||
|
digitalWrite(13, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (startChar == '$') {
|
||||||
|
// receive a "master" frame - we send the frame sync to other boards
|
||||||
|
// we are controlling the video pace. The 16 bit number is how long
|
||||||
|
// after the prior frame sync to wait until showing this frame
|
||||||
|
unsigned int usecUntilFrameSync = 0;
|
||||||
|
int count = Serial.readBytes((char *)&usecUntilFrameSync, 2);
|
||||||
|
if (count != 2) return;
|
||||||
|
count = Serial.readBytes((char *)drawingMemory, sizeof(drawingMemory));
|
||||||
|
if (count >= sizeof(drawingMemory)) {
|
||||||
|
digitalWrite(12, HIGH);
|
||||||
|
pinMode(12, OUTPUT);
|
||||||
|
while (elapsedUsecSinceLastFrameSync < usecUntilFrameSync) /* wait */ ;
|
||||||
|
elapsedUsecSinceLastFrameSync -= usecUntilFrameSync;
|
||||||
|
digitalWrite(12, LOW);
|
||||||
|
// WS2811 update begins immediately after falling edge of frame sync
|
||||||
|
digitalWrite(13, HIGH);
|
||||||
|
leds.show();
|
||||||
|
digitalWrite(13, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (startChar == '%') {
|
||||||
|
// receive a "slave" frame - wait to show it until the frame sync arrives
|
||||||
|
pinMode(12, INPUT_PULLUP);
|
||||||
|
unsigned int unusedField = 0;
|
||||||
|
int count = Serial.readBytes((char *)&unusedField, 2);
|
||||||
|
if (count != 2) return;
|
||||||
|
count = Serial.readBytes((char *)drawingMemory, sizeof(drawingMemory));
|
||||||
|
if (count >= sizeof(drawingMemory)) {
|
||||||
|
elapsedMillis wait = 0;
|
||||||
|
while (digitalRead(12) != HIGH && wait < 30) ; // wait for sync high
|
||||||
|
while (digitalRead(12) != LOW && wait < 30) ; // wait for sync high->low
|
||||||
|
// WS2811 update begins immediately after falling edge of frame sync
|
||||||
|
if (wait < 30) {
|
||||||
|
digitalWrite(13, HIGH);
|
||||||
|
leds.show();
|
||||||
|
digitalWrite(13, LOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (startChar == '@') {
|
||||||
|
// reset the elapsed frame time, for startup of '$' message playing
|
||||||
|
elapsedUsecSinceLastFrameSync = 0;
|
||||||
|
|
||||||
|
} else if (startChar == '?') {
|
||||||
|
// when the video application asks, give it all our info
|
||||||
|
// for easy and automatic configuration
|
||||||
|
Serial.print(LED_WIDTH);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(LED_HEIGHT);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(LED_LAYOUT);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(0);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(0);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(VIDEO_XOFFSET);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(VIDEO_YOFFSET);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(VIDEO_WIDTH);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(VIDEO_HEIGHT);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(0);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(0);
|
||||||
|
Serial.write(',');
|
||||||
|
Serial.print(0);
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
} else if (startChar >= 0) {
|
||||||
|
// discard unknown characters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/* OctoWS2811 movie2serial.pde - Transmit video data to 1 or more
|
||||||
|
Teensy 3.0 boards running OctoWS2811 VideoDisplay.ino
|
||||||
|
http://www.pjrc.com/teensy/td_libs_OctoWS2811.html
|
||||||
|
Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// To configure this program, edit the following sections:
|
||||||
|
//
|
||||||
|
// 1: change myMovie to open a video file of your choice ;-)
|
||||||
|
//
|
||||||
|
// 2: edit the serialConfigure() lines in setup() for your
|
||||||
|
// serial device names (Mac, Linux) or COM ports (Windows)
|
||||||
|
//
|
||||||
|
// 3: if your LED strips have unusual color configuration,
|
||||||
|
// edit colorWiring(). Nearly all strips have GRB wiring,
|
||||||
|
// so normally you can leave this as-is.
|
||||||
|
//
|
||||||
|
// 4: if playing 50 or 60 Hz progressive video (or faster),
|
||||||
|
// edit framerate in movieEvent().
|
||||||
|
|
||||||
|
import processing.serial.*;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
|
||||||
|
float gamma = 1.7;
|
||||||
|
|
||||||
|
Serial ledSerial; // each port's actual Serial port
|
||||||
|
PImage ledImage;
|
||||||
|
Rectangle ledArea; // the area of the movie each port gets, in % (0-100)
|
||||||
|
int[] gammatable = new int[256];
|
||||||
|
int errorCount=0;
|
||||||
|
float framerate=0;
|
||||||
|
|
||||||
|
PImage img; // Declare variable "a" of type PImage
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
String[] list = Serial.list();
|
||||||
|
delay(20);
|
||||||
|
println("Serial Ports List:");
|
||||||
|
println(list);
|
||||||
|
serialConfigure("/dev/ttyACM0");
|
||||||
|
if (errorCount > 0) exit();
|
||||||
|
for (int i=0; i < 256; i++) {
|
||||||
|
gammatable[i] = (int)(pow((float)i / 255.0, gamma) * 255.0 + 0.5);
|
||||||
|
}
|
||||||
|
size(40, 1);
|
||||||
|
img = loadImage("test1.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
// image2data converts an image to OctoWS2811's raw data format.
|
||||||
|
// The number of vertical pixels in the image must be a multiple
|
||||||
|
// of 8. The data array must be the proper size for the image.
|
||||||
|
void image2data(PImage image, byte[] data) {
|
||||||
|
int offset = 3;
|
||||||
|
int x, y, xbegin, xend, xinc, mask;
|
||||||
|
int linesPerPin = 1 / 8;
|
||||||
|
int pixel[] = new int[8];
|
||||||
|
xbegin = 0;
|
||||||
|
xend = image.width;
|
||||||
|
xinc = 1;
|
||||||
|
|
||||||
|
for (x = xbegin; x != xend; x += xinc) {
|
||||||
|
for (int i=0; i < 8; i++) {
|
||||||
|
// fetch 8 pixels from the image, 1 for each pin
|
||||||
|
pixel[i] = colorWiring(image.pixels[x]);
|
||||||
|
}
|
||||||
|
// convert 8 pixels to 24 bytes
|
||||||
|
for (mask = 0x800000; mask != 0; mask >>= 1) {
|
||||||
|
byte b = 0;
|
||||||
|
for (int i=0; i < 8; i++) {
|
||||||
|
if ((pixel[i] & mask) != 0) b |= (1 << i);
|
||||||
|
}
|
||||||
|
data[offset++] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate the 24 bit color from RGB to the actual
|
||||||
|
// order used by the LED wiring. GRB is the most common.
|
||||||
|
int colorWiring(int c) {
|
||||||
|
int red = (c & 0xFF0000) >> 16;
|
||||||
|
int green = (c & 0x00FF00) >> 8;
|
||||||
|
int blue = (c & 0x0000FF);
|
||||||
|
red = gammatable[red];
|
||||||
|
green = gammatable[green];
|
||||||
|
blue = gammatable[blue];
|
||||||
|
return (green << 16) | (red << 8) | (blue); // GRB - most common wiring
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask a Teensy board for its LED configuration, and set up the info for it.
|
||||||
|
void serialConfigure(String portName) {
|
||||||
|
try {
|
||||||
|
ledSerial = new Serial(this, portName);
|
||||||
|
if (ledSerial == null) throw new NullPointerException();
|
||||||
|
ledSerial.write('?');
|
||||||
|
} catch (Throwable e) {
|
||||||
|
println("Serial port " + portName + " does not exist or is non-functional");
|
||||||
|
errorCount++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delay(50);
|
||||||
|
String line = ledSerial.readStringUntil(10);
|
||||||
|
if (line == null) {
|
||||||
|
println("Serial port " + portName + " is not responding.");
|
||||||
|
println("Is it really a Teensy 3.0 running VideoDisplay?");
|
||||||
|
errorCount++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
println(line);
|
||||||
|
String param[] = line.split(",");
|
||||||
|
if (param.length != 12) {
|
||||||
|
println("Error: port " + portName + " did not respond to LED config query");
|
||||||
|
errorCount++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// only store the info and increase numPorts if Teensy responds properly
|
||||||
|
ledImage = new PImage(Integer.parseInt(param[0]), Integer.parseInt(param[1]), RGB);
|
||||||
|
ledArea = new Rectangle(Integer.parseInt(param[5]), Integer.parseInt(param[6]),
|
||||||
|
Integer.parseInt(param[7]), Integer.parseInt(param[8]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw runs every time the screen is redrawn - show the movie...
|
||||||
|
void draw() {
|
||||||
|
image(img, 0, 0);
|
||||||
|
framerate = 30.0;
|
||||||
|
byte[] ledData = new byte[(img.width * img.height * 24) + 3];
|
||||||
|
image2data(img, ledData);
|
||||||
|
ledData[0] = '*'; // first Teensy is the frame sync master
|
||||||
|
int usec = (int)((1000000.0 / framerate) * 0.75);
|
||||||
|
ledData[1] = (byte)(usec); // request the frame sync pulse
|
||||||
|
ledData[2] = (byte)(usec >> 8); // at 75% of the frame time
|
||||||
|
ledSerial.write(ledData);
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 170 B |
Loading…
Reference in New Issue