LoL + Processing

On the train back from NYC to Pittsburgh, I got the idea to hook up the LoL Shield directly to my computer, so that I could develop animations for it using a desktop graphics program. Using Jay Clegg’s Video Peggy project as a starting point, I wrote up a little framebuffer sketch for the Arduino, along with some Processing code to grab a region of the computer screen, scale it, and send it over to the LoL Shield for display. Pretty simple, but could be useful. Source code is after the break. A version of this project might be included with the next LoL Shield library release.

import processing.serial.*;
 
LolShieldBuffer lolShield;
PFont fontA;
 
void setup()
{
  size(14, 9);
 
  // Load the font. Fonts must be placed within the data 
  // directory of your sketch. A font must first be created
  // using the 'Create Font...' option in the Tools menu.
  fontA = loadFont("LucidaSans-9.vlw");
 
  // Set the font and its size (in units of pixels)
  textFont(fontA, 9);
 
  // I know that the first port in the serial list on my mac
  // is always my  FTDI adaptor, so I open Serial.list()[0].
  // On Windows machines, this generally opens COM1.
  // Open whatever port is the one you're using.
  String portName = Serial.list()[0];
  print(portName);
  lolShield = new LolShieldBuffer(this, portName);
}
 
int i = 0;
 
void draw()
{
  background(0);
 
  fill(255);
 
  text("The time is now " + hour() + ":" + minute(), i, 8);
 
//  lolShield.sendWindow(mouseX,mouseY, 14, 9);
  lolShield.sendWindow(0,0, 14, 9);
 
  i--;
  if (i < -130) {i = 14;}
  delay(50);
}

You want to put this code on your Arduino:

#include "Charliplexing.h"
#include "Font.h"
#include "WProgram.h"
 
// Inspired by Video Peggy:
// http://www.planetclegg.com/projects/QC-Peggy.html
 
void setup()                    // run once, when the sketch starts
{
  Serial.begin(115200);
 
  LedSign::Init(DOUBLE_BUFFER);
//  LedSign::Init();
}
 
int state = 0; 
int counter = 0;
const uint8_t DISP_BUFFER_SIZE = 9*2;
 
void loop()                     // run over and over again
{ 
  while( !Serial.available()) {}
  uint8_t c = Serial.read();
 
  // very simple state machine to look for 6 byte start of frame
  // marker and copy bytes that follow into buffer
  if (state < 6) 
  {
 
    // must wait for 0xdeadbeef to start frame.
    // note, I look for two more bytes after that, but
    // they are reserved for future use. 
 
    if (state == 0 && c == 0x12) state++;
    else if (state ==1 && c == 0x34) state++;
    else if (state ==2 && c == 0x56) state++;
    else if (state ==3 && c == 0x78) state++;
    else if (state ==4 && c == 0x1) state++;
    else if (state ==5)  // dont care what 6th byte is 
    {
      state++;
      counter = 0;
    }
    else state = 0; // error: reset to look for start of frame
  }
  else 
  {
    // inside of a frame, so write to the buffer
    uint8_t row = counter / 2;
    uint8_t col = (counter % 2)*8;
    uint8_t length = (col==0?8:6);
    for (uint8_t colOffset = 0;  colOffset < length ; colOffset++ ) {
      LedSign::Set(col+colOffset, row, c >> (7 - colOffset) & 1);
    }
 
    counter++;
    if (counter >= DISP_BUFFER_SIZE)
    {
      // buffer filled, so reset everything to wait for next frame
      state = 0;
      LedSign::Flip();
    }
  }
}

Put this code in a new tab in your Processing project, called LolShieldBuffer:

import processing.serial.*;
 
class LolShieldBuffer
{
  Serial serial;
 
  int ledWidth = 14;
  int ledHeight = 9;
  int ledFrameSize = 9*2;
 
 
  LolShieldBuffer(PApplet parent, String portName) {
    serial = new Serial(parent, portName, 115200);
  }
 
  void sendWindow(int x, int y, int w, int h) {
 
    PImage img = get(x, y, w, h);
//    img.resize(ledWidth, ledHeight);
 
    char[] frame = new char[ledFrameSize];
 
    // Grab a 14x9 window
    for (int row = 0; row < ledHeight; row++) {
      char buf = 0;
      for (int col = 0; col < 8; col++) {
        buf = (char)(buf << 1);
        buf |= (brightness(img.pixels[row*ledWidth+col])>127) ? 1:0;
      }
      frame[row*2] = buf;
 
      for (int col = 0; col < 6; col++) {
        buf = (char)(buf << 1);
        buf |= (brightness(img.pixels[row*ledWidth+8+col])>127) ? 1:0;
      }
      buf = (char)(buf << 2);
 
      frame[row*2+1] = buf;
    }
 
    sendFrame(frame);
  }
 
 
  void sendFrame(char[] frame) {
    byte[] magicNumber = {0x12, 0x34, 0x56, 0x78, 0x1, 0x0};
 
    serial.write(magicNumber);
 
    // TODO: Drop 9*2 magic number
    for(int index = 0; index < ledFrameSize; index++) {
      serial.write(frame[index]);
    }
  }
}
This entry was posted in tech. Bookmark the permalink.

2 Responses to LoL + Processing

  1. Pingback: LoL Shield Projects

  2. Pingback: LoL Horse! « adafruit industries blog

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>