Hi All,
First of all I would like to Thank you Paul for making such a wonderful library which requires almost negligible code changes to run it.
Great stuff.
I am using Teensy 3.2 to display videos/images on WS2811 driven display. I have tried some prototyping with the RGB modules and things went well.
I got an idea to display the Black and white image which clicked me that what not use the every output of WS2811 chip to drive an individual pixel i.e. OUTR is running 1 pixel, OUTG is running another pixel and OUTB is running another one. In this way I will be able to drive three pixels through a single WS2811 chip.
I design a PCB which has 24 columns and 24 rows and a WS2811 chip on every three pixels. In this way there are 8 chips in every row.
I made some changes in movie2Serial program (as mentioned below) and test it on my newly made PCB.
Now the problem which I am facing is that when I try to display any image (.png) on this small display then that Image is not being properly shown on the display.
Please see the attached file which has both the LED display image as well as the real .png file which I was trying to show on the display.
Also, I tried the same with the video (.mp4) and I got the same results.
I was trying to display letter 'N' image which has white background and letter is written in Black.
I tried many changes in movie2Serial program but with no results.
Can someone please point me that what I am doing wrong over here, as, in my opinion things should work in this way as well as it works with the RGB LEDs.
The main changes which I have done in movie2Serail program is in image2data and colorWiring function.
In image2data function where its converting 8 pixels to 24 bytes, I have changed it to convert 8 pixels to 8 bytes only.
In colorWiring function I have changed the code so that it returns a single color after fetching the value from gamma table.
I am stuck up with this and have been struggling a lot since quite sometime and I would highly appreciate any help from anyone in this regard.
Please note that I am using processing 3.1.2 and OctoWS2811 library.
First of all I would like to Thank you Paul for making such a wonderful library which requires almost negligible code changes to run it.
Great stuff.
I am using Teensy 3.2 to display videos/images on WS2811 driven display. I have tried some prototyping with the RGB modules and things went well.
I got an idea to display the Black and white image which clicked me that what not use the every output of WS2811 chip to drive an individual pixel i.e. OUTR is running 1 pixel, OUTG is running another pixel and OUTB is running another one. In this way I will be able to drive three pixels through a single WS2811 chip.
I design a PCB which has 24 columns and 24 rows and a WS2811 chip on every three pixels. In this way there are 8 chips in every row.
I made some changes in movie2Serial program (as mentioned below) and test it on my newly made PCB.
Now the problem which I am facing is that when I try to display any image (.png) on this small display then that Image is not being properly shown on the display.
Please see the attached file which has both the LED display image as well as the real .png file which I was trying to show on the display.
Also, I tried the same with the video (.mp4) and I got the same results.
I was trying to display letter 'N' image which has white background and letter is written in Black.
I tried many changes in movie2Serial program but with no results.
Can someone please point me that what I am doing wrong over here, as, in my opinion things should work in this way as well as it works with the RGB LEDs.
The main changes which I have done in movie2Serail program is in image2data and colorWiring function.
In image2data function where its converting 8 pixels to 24 bytes, I have changed it to convert 8 pixels to 8 bytes only.
In colorWiring function I have changed the code so that it returns a single color after fetching the value from gamma table.
I am stuck up with this and have been struggling a lot since quite sometime and I would highly appreciate any help from anyone in this regard.
Please note that I am using processing 3.1.2 and OctoWS2811 library.
Code:
import processing.video.*;
import processing.serial.*;
import java.awt.Rectangle;
Movie myMovie = new Movie(this, "C://Users//nshrivas//Documents//Croma//Led Wall//N.png");
float gamma = 1.7;
int numPorts=0; // the number of serial ports in use
int maxPorts=24; // maximum number of serial ports
Serial[] ledSerial = new Serial[maxPorts]; // each port's actual Serial port
Rectangle[] ledArea = new Rectangle[maxPorts]; // the area of the movie each port gets, in % (0-100)
boolean[] ledLayout = new boolean[maxPorts]; // layout of rows, true = even is left->right
PImage[] ledImage = new PImage[maxPorts]; // image sent to each port
int[] gammatable = new int[256];
int errorCount=0;
float framerate=0;
void setup() {
String[] list = Serial.list();
delay(20);
println("Serial Ports List:");
println(list);
//serialConfigure("COM3"); // change these to your port names
serialConfigure("COM4");
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(1000, 1000); // create the window
myMovie.loop(); // start the movie :-)
}
// movieEvent runs for each new frame of movie data
void movieEvent(Movie m) {
// read the movie's next frame
m.read();
//if (framerate == 0) framerate = m.getSourceFrameRate();
framerate = 30.0; // TODO, how to read the frame rate???
for (int i=0; i < numPorts; i++) {
// copy a portion of the movie's image to the LED image
int xoffset = percentage(m.width, ledArea[i].x);
int yoffset = percentage(m.height, ledArea[i].y);
int xwidth = percentage(m.width, ledArea[i].width);
int yheight = percentage(m.height, ledArea[i].height);
//println("m.width =" +m.width);
//println("ledArea[i].x =" +ledArea[i].x);
//println("m.height =" +m.height);
//println("ledArea[i].y =" +ledArea[i].y);
//println("ledArea[i].width =" +ledArea[i].width);
//println("ledArea[i].height =" +ledArea[i].height);
//println("xoffset =" +xoffset);
//println("yoffset =" +yoffset);
//println("xwidth =" +xwidth);
//println("yheight =" +yheight);
ledImage[i].copy(m, xoffset, yoffset, xwidth, yheight,0, 0, ledImage[i].width, ledImage[i].height);
//ledImage[i].copy(m, 0, 0, 576, 576,0, 0, 24, 24);
// convert the LED image to raw data
byte[] ledData = new byte[(ledImage[i].width * ledImage[i].height*3) + 3];
println("xoffset = "+xoffset);
println("yoffset = "+yoffset);
println("Source Image Width = "+xwidth);
println("Source Image Height = "+yheight);
image2data(ledImage[i], ledData, ledLayout[i]);
if (i == 0) {
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
} else {
ledData[0] = '%'; // others sync to the master board
ledData[1] = 0;
ledData[2] = 0;
}
// send the raw data to the LEDs :-)
//for(int ii =0;ii<ledData.length;ii++){
// println("LEDDATA= " +ii+" "+ ledData[ii]+" ");
//}
ledSerial[i].write(ledData);
}
}
// 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, boolean layout) {
int offset = 3;
int x, y, xbegin, xend, xinc, mask, modulo;
int linesPerPin = image.height / 8;
int pixel[] = new int[8];
for (y = 0; y < linesPerPin; y++) {
if ((y & 1) == (layout ? 0 : 1)) {
// even numbered rows are left to right
xbegin = 0;
xend = image.width;
xinc = 1;
} else {
// odd numbered rows are right to left
xbegin = image.width - 1;
xend = -1;
xinc = -1;
}
int j =0;
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] = image.pixels[x + (y + linesPerPin * i) * image.width];
modulo = (x + (y + linesPerPin * i) * image.width)%3;
//println("i m here pixel " + i +"= " + pixel[i]);
//println("Pixel Number ="+ (x + (y + linesPerPin * i) * image.width));
pixel[i] = colorWiring(pixel[i], modulo);
// println("ColorWiring Pixel j= " +j +" "+ pixel[i]);
j++;
}
// convert 8 pixels to 24 bytes
for (mask = 0x80; mask != 0; mask >>= 1) {
byte b = 0;
for (int i=0; i < 8; i++) {
if ((pixel[i] & mask) != 0){
//b = b | (1 << i);
b |= (1 << i);
}
// b |= (1 << i);
}
int temp = 0;
int tempOffset = offset-3;;
if(b <0){
temp = (b+255);
println("data[" + tempOffset + "]=" +temp);
}else{
temp = b;
println("data[" + tempOffset + "]=" +temp);
}
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 modulo) {
int red = 0;
int green = 0;
int blue = 0;
int colorToShow =0;
if(modulo == 0){ // Red Case
red = (c & 0xFF0000) >> 16;
red = gammatable[red];
colorToShow = red ;//<< 16;
}
if(modulo == 1){ // Green Case
green = (c & 0x00FF00) >> 8;
green = gammatable[green];
colorToShow = green; //<<8;
}
if(modulo == 2){ //Blue Case
blue = (c & 0x0000FF);
blue =gammatable[blue];
colorToShow = blue;
}
//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
return colorToShow;
}
// ask a Teensy board for its LED configuration, and set up the info for it.
void serialConfigure(String portName) {
if (numPorts >= maxPorts) {
println("too many serial ports, please increase maxPorts");
errorCount++;
return;
}
try {
ledSerial[numPorts] = new Serial(this, portName);
if (ledSerial[numPorts] == null) throw new NullPointerException();
ledSerial[numPorts].write('?');
} catch (Throwable e) {
println("Serial port " + portName + " does not exist or is non-functional");
errorCount++;
return;
}
delay(50);
String line = ledSerial[numPorts].readStringUntil(10);
if (line == null) {
println("Serial port " + portName + " is not responding.");
println("Is it really a Teensy 3.0 running VideoDisplay?");
errorCount++;
return;
}
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[numPorts] = new PImage(Integer.parseInt(param[0]), Integer.parseInt(param[1]));
ledArea[numPorts] = new Rectangle(Integer.parseInt(param[5]), Integer.parseInt(param[6]),
Integer.parseInt(param[7]), Integer.parseInt(param[8]));
ledLayout[numPorts] = (Integer.parseInt(param[5]) == 0);
numPorts++;
}
// draw runs every time the screen is redrawn - show the movie...
void draw() {
// show the original video
image(myMovie, 0, 80);
// then try to show what was most recently sent to the LEDs
// by displaying all the images for each port.
for (int i=0; i < numPorts; i++) {
// compute the intended size of the entire LED array
int xsize = percentageInverse(ledImage[i].width, ledArea[i].width);
int ysize = percentageInverse(ledImage[i].height, ledArea[i].height);
// computer this image's position within it
int xloc = percentage(xsize, ledArea[i].x);
int yloc = percentage(ysize, ledArea[i].y);
// show what should appear on the LEDs
image(ledImage[i], 240 - xsize / 2 + xloc, 10 + yloc);
}
}
// respond to mouse clicks as pause/play
boolean isPlaying = true;
void mousePressed() {
if (isPlaying) {
myMovie.pause();
isPlaying = false;
} else {
myMovie.play();
isPlaying = true;
}
}
// scale a number by a percentage, from 0 to 100
// num is movie and percent is ledArea size
int percentage(int num, int percent) {
double mult = percentageFloat(percent);
double output = num * mult;
return (int)output;
}
// scale a number by the inverse of a percentage, from 0 to 100
int percentageInverse(int num, int percent) {
double div = percentageFloat(percent);
double output = num / div;
return (int)output;
}
// convert an integer from 0 to 100 to a float percentage
// from 0.0 to 1.0. Special cases for 1/3, 1/6, 1/7, etc
// are handled automatically to fix integer rounding.
double percentageFloat(int percent) {
if (percent == 33) return 1.0 / 3.0;
if (percent == 17) return 1.0 / 6.0;
if (percent == 14) return 1.0 / 7.0;
if (percent == 13) return 1.0 / 8.0;
if (percent == 11) return 1.0 / 9.0;
if (percent == 9) return 1.0 / 11.0;
if (percent == 8) return 1.0 / 12.0;
return (double)percent / 100.0;
}