From c2ef20184b2a38b71ca4e6bda4cfae3f292ba1b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Mon, 2 Mar 2015 00:24:26 +0100 Subject: [PATCH] Prototype of processing code on teensy 3.1 & OctoWS2811 --- .../arduino/VideoDisplay/VideoDisplay.ino | 251 ++++++++++++++++++ .../processing/pic2serial/pic2serial.pde | 150 +++++++++++ v2/backend/processing/pic2serial/test1.png | Bin 0 -> 170 bytes 3 files changed, 401 insertions(+) create mode 100644 v2/backend/arduino/VideoDisplay/VideoDisplay.ino create mode 100644 v2/backend/processing/pic2serial/pic2serial.pde create mode 100644 v2/backend/processing/pic2serial/test1.png diff --git a/v2/backend/arduino/VideoDisplay/VideoDisplay.ino b/v2/backend/arduino/VideoDisplay/VideoDisplay.ino new file mode 100644 index 0000000..682fabb --- /dev/null +++ b/v2/backend/arduino/VideoDisplay/VideoDisplay.ino @@ -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 + +// 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 + } +} + diff --git a/v2/backend/processing/pic2serial/pic2serial.pde b/v2/backend/processing/pic2serial/pic2serial.pde new file mode 100644 index 0000000..0ab4cd3 --- /dev/null +++ b/v2/backend/processing/pic2serial/pic2serial.pde @@ -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); +} diff --git a/v2/backend/processing/pic2serial/test1.png b/v2/backend/processing/pic2serial/test1.png new file mode 100644 index 0000000000000000000000000000000000000000..022374119ccc7d828901b7feb1e425e255f56de2 GIT binary patch literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^B0$W@!2~1&KS&kFdgVpP5lqUG7!;caToW64!_l=ltB<)VvY~=c3falGGH1 z^30M91$R&1fbd2>aiAh0PZ!4!j_b*Pq|crD^4**{l7V608s^aBTc14x$})Jm`njxg HN@xNAL-H>b literal 0 HcmV?d00001