Video data to a teensy 3.1 driving 256 7-segement displays

Status
Not open for further replies.

mezelve

New member
Hi, I have some trouble getting serial to work in this project.

What do I want to achieve?
I've hooked up 64 x HP QDSP-6064 bubble displays to 16 x Adafruit HT16K33 Breakouts. (Actually only to 8 for now, still waiting on a delivery).
This results in a "display" of 32 by 8 7-segments.
This project by Kris Winer HT16K33_Display_Driver helped a lot.

Now I want to send live video data from a processing sketch to the arduino sketch and display it.
It's basically D.I.G.I.T. by Teehan+Lax Labs, but much smaller in size. Here's a video of it:
https://vimeo.com/79332227

And here I'm struggling on the arduino side with the serial handling.
I've looked at OctoWS2811 example movie2serial.pde and VideoDisplay.ino.
How can I access the buffer with the 256 byte values?
And do I need to convert them back to HEX?
Code:
int segData[32*8]; //is this the right size?
void loop (){
  //serial code from VideoDisplay.ino
  int startChar = Serial.read();
  if (startChar == '*') {
    int count = Serial.readBytes((char *)segData, sizeof(segData));
    if (count == sizeof(segData)) {
//here I need some assistance
    }
  }
}

One more side question: as I need 2 I2C ports for the 16 HT16K33's I would like to do something like:
Code:
#include <i2c_t3.h>

#define WIRE0 Wire
#define WIRE1 Wire1

i2c_t3 i2cports[] = {WIRE0, WIRE1};
But it doesn't seem to work.

So here's my Processing sketch where all 256 bytes are stored in ledData, ready to send over serial.
I still have to work on video brightness threshold etc. Later on I want to be able to switch between live and .mov files.
Code:
import processing.video.*;
import processing.serial.*;

Serial myPort;

color black = color(0);
color white = color(255);
int numPixels;
Capture video;

int xOffset = 5;                 //X offset from the left border of the video
int yOffset = 5;                 //Y offset from the top border of the video
int hSize = 8;                   //width of the horizontal elements of one 7 segment
int vSize = 10;                  //height of the vertical elements of one 7 segment
int xSpacing = hSize+3+6;        //X spacing between each 7 segment, last one is the variable one
int ySpacing = (vSize*2)+4+22;   //Y spacing between each 7 segment, last one is the variable one

int rows = 8;                    //amount horizontal rows
int columns = 32;                //amount of 7 segments on the horizontal axis
//
boolean DEBUG = false;
boolean SERIAL_SWITCH = false;

byte[] ledData = new byte[(rows*columns) + 1];      // array to store all the values for each of the segments, + 1 to add a fist char like '*'

void setup() {
  size(1280, 480);  
  if(SERIAL_SWITCH){
    //from movie2serial
    String[] list = Serial.list();
    delay(20);
    println("Serial Ports List:");
    println(list);
    serialConfigure("/dev/tty.usbmodem6578901");       // needs to be update with your port
  }
  
  // This the default video input, see the GettingStartedCapture 
  // example if it creates an error
  video = new Capture(this, 640, 480);
  video.start(); 
  
  numPixels = video.width * video.height;
  //a framerate over 10 seems to cause glitches, performance issue?
  frameRate(10);
}

void draw() {
  if (video.available()) {
    video.read();
    video.loadPixels();
    int threshold = 120;    // Set the threshold value
    float pixelBrightness;  // Declare variable to store a pixel's color
    // Turn each pixel in the video frame black or white depending on its brightness 
    loadPixels();
    for (int i = 0; i < numPixels; i++) {
      pixelBrightness = brightness(video.pixels[i]);
      if (pixelBrightness > threshold) {   // If the pixel is brighter than the
        video.pixels[i] = black;           // threshold value, make it white
      } 
      else { // Otherwise,
        video.pixels[i] = white;           // make it black
      }
    }
    updatePixels();
    image(video,640,0);
  }
  
  //set a background for the left part of the screen
  fill(100);
  noStroke();
  rect(0,0,640,480);
  
  //
  int xOff = xOffset; 
  int yOff = yOffset;
  stroke(255,0,0);
  strokeWeight(1);
  
  int ledDataCount = 1;
  ledData[0] = '*';  // first char so the teensy knows what to do.
  for(int j=0;j<rows;j++){
    for(int i=0;i<columns;i++){
      int digit = 0;        // digit is a int between 0 and 255, 0 -> nothing, 255 -> all 7 segment + the dot
      //dot DP = A7
      noStroke();
      if(get(640+xOff+hSize+4,yOff+4+(2*vSize))==-1){ 
        fill(255,0,0);
        digit += 128;
      }else{
        fill(100,0,0);
      }
      rect(xOff+hSize+4,yOff+4+(2*vSize),1,1);  //draw the dot on the right side
      if(DEBUG){
        //set a point on the right side where the pixel was measured, can be deleted later when everything
        noStroke();
        fill(255,0,0);
        rect(640+xOff+hSize+4,yOff+4+(2*vSize),1,1);
      }
      
      //vertical segment line right bottom C = A6
      if(get(640+xOff+hSize+2,yOff+3+vSize+round(vSize/2))==-1){
        stroke(255,0,0); 
        digit += 64;
      }else{
        stroke(100,0,0);
      }
      line(xOff+hSize+2,yOff+3+vSize,xOff+hSize+2,yOff+3  +(2*vSize));
      if(DEBUG){
        noStroke();
        fill(255,0,0);
        rect(640+xOff+hSize+2,yOff+3+vSize+round(vSize/2),1,1);
      }
      
      //vertical segment line left bottom E = A5
      if(get(640+xOff,yOff+3+vSize+round(vSize/2))==-1){
        stroke(255,0,0);
        digit += 32;
      }else{
        stroke(100,0,0);
      }
      line(xOff,yOff+3+vSize,xOff,yOff+3+(2*vSize));
      if(DEBUG){
        noStroke();
        fill(255,0,0);
        rect(640+xOff,yOff+3+vSize+round(vSize/2),1,1);
      }
      
      //horizontal segment line middle G = A4
      if(get(640+xOff+1+round(hSize/2),yOff+2+vSize)==-1){
        stroke(255,0,0);
        digit += 16;
      }else{
        stroke(100,0,0);
      }
      line(xOff+1,yOff+2+vSize,xOff+1+hSize,yOff+2+vSize  );
      if(DEBUG){
        noStroke();
        fill(255,0,0);
        rect(640+xOff+1+round(hSize/2),yOff+2+vSize,1,1);
      }
      
      //horizontal segment line bottom D = A3
      if(get(640+xOff+1+round(hSize/2),yOff+4+(2*vSize))==-1){
        stroke(255,0,0);
        digit += 8;
      }else{
        stroke(100,0,0);
      }
      line(xOff+1,yOff+4+(2*vSize),xOff+1+hSize,yOff+4+(  2*vSize));
      if(DEBUG){
        noStroke();
        fill(255,0,0);
        rect(640+xOff+1+round(hSize/2),yOff+4+(2*vSize),1,1);
      }
      
      //vertical segment line left top F = A2
      if(get(640+xOff,yOff+1+round(vSize/2))==-1){
        stroke(255,0,0);
        digit += 4;
      }else{
        stroke(100,0,0);
      }
      line(xOff,yOff+1,xOff,yOff+1+vSize);
      if(DEBUG){
        noStroke();
        fill(255,0,0);
        rect(640+xOff,yOff+1+round(vSize/2),1,1);
      }
      
      //vertical segment line right top B = A1
      if(get(640+xOff+hSize+2,yOff+1+round(vSize/2))==-1){
        stroke(255,0,0);
        digit +=2;
      }else{
        stroke(100,0,0);
      }
      line(xOff+hSize+2,yOff+1,xOff+hSize+2,yOff+1+vSize  );
      if(DEBUG){
        noStroke();
        fill(255,0,0);
        rect(640+xOff+hSize+2,yOff+1+round(vSize/2),1,1);      
      }
      
      //horizonatal segment line top A = A0
      if(get(640+xOff+1+round(hSize/2),yOff)==-1){
        stroke(255,0,0);
        digit +=1 ;
      }else{
        stroke(100,0,0);
      }
      line(xOff+1,yOff,xOff+1+hSize,yOff);
      if(DEBUG){
        noStroke();
        fill(255,0,0);
        rect(640+xOff+1+round(hSize/2),yOff,1,1);
      }
      //
      xOff += xSpacing;
      ledData[ledDataCount++] = byte(digit);   // store the digit as a byte in the array we send to the teensy
    }
    xOff = xOffset;
    yOff += ySpacing;
  }
  if(SERIAL_SWITCH){
    //send the data over serial
    myPort.write(ledData);
  }
}

void serialConfigure(String portName) {
  try {
    myPort = new Serial(this, portName);
    if (myPort == null) throw new NullPointerException();
  } catch (Throwable e) {
    println("Serial port " + portName + " does not exist or is non-functional");
    return;
  }
  delay(50);
}

Here's my arduino sketch, without proper serial for now and a simple animation instead.
Code:
/* 
https://github.com/kriswiner/HT16K33_Display_Driver
HT16K33 Display Driver Basic Example  
 by: Kris Winer
 date: May 1, 2014
 license: Beerware - Use this code however you'd like. If you 
 find it useful you can buy me a beer some time.
 
 Demonstrate basic functionality including parameterizing the register addresses, initializing the display, 
 writing integers to one or all of four seven segment LED displays.
 
 SDA and SCL should have external pull-up resistors (to 3.3V).
 10k resistors are on the HT16K33 breakout board.
 
 Hardware setup:
 HT16K33 Breakout --------- Arduino
 3.3V --------------------- 3.3V
 SDA ----------------------- A4
 SCL ----------------------- A5
 GND ---------------------- GND
 
 Note: The HT16K33 is an I2C display driver and uses the Arduino Wire library. 
 The display driver is 5V tolerant, but we are using a 3.3 V 8 MHz Pro Mini or a 3.3 V Teensy 3.1.
 We have disabled the internal pull-ups used by the Wire library in the Wire.h/twi.c utility file.
 We are also using the 400 kHz fast I2C mode by setting the TWI_FREQ  to 400000L /twi.h utility file.
 */ 
#include <i2c_t3.h>

#define WIRE0 Wire
#define WIRE1 Wire1

i2c_t3 i2cports[] = {WIRE0, WIRE1};  //does this work?

#define HT16K33_ADDRESS_A       0x70
#define HT16K33_ADDRESS_B       0x71
#define HT16K33_ADDRESS_C       0x72
#define HT16K33_ADDRESS_D       0x73
#define HT16K33_ADDRESS_E       0x74
#define HT16K33_ADDRESS_F       0x75
#define HT16K33_ADDRESS_G       0x76
#define HT16K33_ADDRESS_H       0x77
#define HT16K33_ON              0x21  // Commands
#define HT16K33_STANDBY         0x20
#define HT16K33_DISPLAYON       0x81
#define HT16K33_DISPLAYOFF      0x80
#define HT16K33_BLINKON         0x85 // Blink is off (00), 2 Hz (01), 1 Hz (10), or 0.5 Hz (11) for bits (21) 
#define HT16K33_BLINKOFF        0x81
#define HT16K33_DIM             0xE0 | 0x08 // Set dim from 0x00 (1/16th duty ccycle) to 0x0F (16/16 duty cycle)

uint8_t addresses[]={HT16K33_ADDRESS_A,HT16K33_ADDRESS_B,HT16K33_ADDR  ESS_C,HT16K33_ADDRESS_D,HT16K33_ADDRESS_E,HT16K33_  ADDRESS_F,HT16K33_ADDRESS_G,HT16K33_ADDRESS_H};
int amount_displays = 1; //amount of ht16k33's per I2C port, 1 for now


// Arrangement for display 1 (4 digit bubble display)
// 
//               a = A0
//             _________
//            |         |
//   f = A2   |  g = A4 | b = A1
//            |_________|
//            |         |
//   e = A5   |         | c = A6
//            |_________|
//               d = A3        DP = A7

// Hookup of QDSP-6064 bubble display
//
// Display Pin          HT16K33 pin
//  1                      C0
//  2                      A5
//  3                      A6
//  4                      C2
//  5                      A7
//  6                      C3
//  7                      A4
//  8                      A3
//  9                      A2
//  10                     C1
//  11                     A1
//  12                     A0
//
// and so on... each cathode can drive two display cathodes. A total of four, 4-digit, 7-segment displays
// can be driven with this one chip with just 2 TWI wires and power and ground. That is a lot of display
// without taking up a lot of the GPIO pins.

int a = 0;
int animation[6][16] = {
  {0x01,0x10,0x08,0x08,0x10,0x01,0x01,0x10,0x08,0x08  ,0x10,0x01,0x01,0x10,0x08,0x08},
  {0x01,0x01,0x10,0x08,0x08,0x10,0x01,0x01,0x10,0x08  ,0x08,0x10,0x01,0x01,0x10,0x08},
  {0x10,0x01,0x01,0x10,0x08,0x08,0x10,0x01,0x01,0x10  ,0x08,0x08,0x10,0x01,0x01,0x10},
  {0x08,0x10,0x01,0x01,0x10,0x08,0x08,0x10,0x01,0x01  ,0x10,0x08,0x08,0x10,0x01,0x01},
  {0x08,0x08,0x10,0x01,0x01,0x10,0x08,0x08,0x10,0x01  ,0x01,0x10,0x08,0x08,0x10,0x01},
  {0x10,0x08,0x08,0x10,0x01,0x01,0x10,0x08,0x08,0x10  ,0x01,0x01,0x10,0x08,0x08,0x10}
};
  
void setup(){ 
  // Setup for Master mode, pins 18/19, external pullups, 400kHz, 200ms default timeout
  WIRE0.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
  WIRE0.setDefaultTimeout(200000); // 200ms
  
  /* Setup for Master mode, pins 29/30, external pullups, 400kHz, 200ms default timeout
  WIRE1.begin(I2C_MASTER, 0x00, I2C_PINS_29_30,  I2C_PULLUP_EXT, 400000);
  WIRE1.setDefaultTimeout(200000); // 200ms
  */
  
  Serial.begin(9600);

  initHT16K33(WIRE0);  // initialize display
  //initHT16K33(WIRE1);  // initialize display
  
  //clear all displays on WIRE0
  for(int i=0 ; i<amount_displays; i++){
    clearDsplay(WIRE0, addresses[i], 1);
    clearDsplay(WIRE0, addresses[i], 2);
    clearDsplay(WIRE0, addresses[i], 3);
    clearDsplay(WIRE0, addresses[i], 4);
  }

  // test each of the 4 "displays" per HT16K33
  for(int i=0; i<amount_displays; i++){
    for(int dis=1; dis<5; dis++){ 
      for(int seg=1; seg<5; seg++){ //4 seven segments per display
        writeHexDigit(WIRE0, addresses[i], dis, seg, 0xFF); //0xFF -> all segments on incl the dot 
      }
      blinkHT16K33(WIRE0, addresses[i], 2); // Blink for 2 seconds
      clearDsplay(WIRE0, addresses[i], dis);
    }
  }
}

int segData[32*8];
void loop (){
  /*
   *serial code from VideoDisplay.ino
  int startChar = Serial.read();
  if (startChar == '*') {
    int count = Serial.readBytes((char *)segData, sizeof(segData));
    if (count == sizeof(segData)) {
    }
  }
  */
  
  int j = 1;
  for(int k=0; k<amount_displays; k++){
    for(int i=0; i<16;i++){
      if(i<4){
        writeHexDigit(WIRE0, addresses[k], 1, j, animation[a][i]);
      }else if(i>=4 && i<8){
        writeHexDigit(WIRE0, addresses[k], 2, j, animation[a][i]);
      }else if(i>=8 && i<12){
        writeHexDigit(WIRE0, addresses[k], 3, j, animation[a][i]);
      }else if(i>=12 && i<16){
        writeHexDigit(WIRE0, addresses[k], 4, j, animation[a][i]);
      }
      j++;
      if(j==5){
        j = 1;
      }
    }
  }
  a++;
  if(a == 6){
    a = 0;
  }
  delay(50);
}

void writeHexDigit(i2c_t3 wirePort, uint8_t address, uint8_t dsply, uint8_t digit, uint16_t data){
  if(dsply == 1) {
    digit = (digit - 1)*2 + 0; 
  } 
  if(dsply == 2) {
    digit = (digit - 1)*2 + 8 ;
  }
  if(dsply == 3) {
    digit = (digit - 1)*2 + 1;
  }
  if(dsply == 4) {
    digit = (digit - 1)*2 + 9;
  }
  writeByte(wirePort, address, digit, data);
}

void initHT16K33(i2c_t3 wirePort){
  for(int i= 0; i<amount_displays; i++){
    writeCommand(wirePort, addresses[i], HT16K33_ON);         // Turn on system oscillator
    writeCommand(wirePort, addresses[i], HT16K33_DISPLAYON);  // Display on
    writeCommand(wirePort, addresses[i], HT16K33_DIM);        // Set brightness
    delay(50);
  }
}

void blinkHT16K33(i2c_t3 wirePort, uint8_t address, int time){
  writeCommand(WIRE0, address, HT16K33_BLINKON);  // Turn on blink
  delay(1000*time);
  writeCommand(WIRE0, address, HT16K33_BLINKOFF);  // Turn on blink
}

void clearDsplay(i2c_t3 wirePort, uint8_t address, int dsply){
  for(int i = 1; i < 5; i++)  {
    writeHexDigit(wirePort, address, dsply, i, 0x00);
  }
}

// I2C write protocols
void writeCommand(i2c_t3 wirePort, uint8_t address, uint8_t command){
  wirePort.beginTransmission(address);  // Initialize the Tx buffer
  wirePort.write(command);              // Put command in Tx buffer
  wirePort.endTransmission();           // Send the Tx buffer
}

void writeByte(i2c_t3 wirePort, uint8_t address, uint8_t subAddress, uint8_t data){
  wirePort.beginTransmission(address);  // Initialize the Tx buffer
  wirePort.write(subAddress);           // Put slave register address in Tx buffer
  wirePort.write(data);                 // Put data in Tx buffer
  wirePort.endTransmission();           // Send the Tx buffer
}

This is how my prototype looks like. Only 1/16 of the total project. Yes, it's a lot of wiring and soldering.
teensy_ht16k33.jpg

thanks!
 
Status
Not open for further replies.
Back
Top