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