Arduino, Processing, Tutorials
Leave a comment

Physicalising a Pixel – LED Matrix Display Tutorial / Document 1. Workshop

This past March, CAN joined forces with UAL Creative Computing Institute to present the first in a series of events that examine new forms of cross-disciplinary art and design practice. Entitled Document 1., the event was comprised of a workshop, seminar, and symposium, and took place at UAL’s newly refurbished Camberwell College of Art in London.

Under the guidance of Andreas Gysin, Document 1. workshop participants embraced low resolution graphics as a creative constraint and developed software to render rudimentary text, iconography, and images – they built a display with LED banks. Thinking across software and hardware while engaging graphic communication as both tangible and tectonic, teams iteratively prototyped their display systems. Each team member took on specific tasks spanning concept development, software sketching, interface design, and hardware prototyping, resulting in generative outcomes that emerged from the interests of each group.

Here we provide a few code examples to quickly get up and running with LED matrices that were prepared for a workshop. A small framework to facilitate prototyping was developed, presented, and used during the workshop. We have included a part list, comprised of components that are widely available and easily accessible.

Part list

  • RGB LED matrix 32×64 P5 (P5 means that the LED pitch is 5mm) (approx $50)
  • HUB75 ribbon cable (usually comes with the matrix)
  • 5V power supply (3A minimum), plus cables (approx $20)
  • Teensy 3.5 or 3.6 development board (a Teensy 3.2 will do but has limited memory) (approx $30)
  • SmartLed shield (not strictly necessary but handy to quickly connect the microcontroller) (around $20)

Software requirements

Examples

All the examples rely on the SmartMatrix library for Arduino. Many features of the library are not demonstrated, but the library comes with an extensive collection of examples. “a” examples are Arduino code whereas “p” examples are Processing code.

Download Examples from GitHub

–– a1_single_pixel
This is the smallest example program, and runs directly on the microcontroller. A few LEDs are activated “manually”.

In this first example, participants learn how to create content directly on the Arduino board. The example includes two main segments of code. The first is concerned with the total size of the chained matrices. Since the LED matrices can be daisy chained to create a large whole, the total width and height of pixels needs to be specified here.

const uint16_t TOTAL_WIDTH    = 64;   // Total size of the chained matrices
const uint16_t TOTAL_HEIGHT   = 32;   

The second part is concerned with the actual programming of the screen, i.e. what you would like to happen. Since we are programming here directly in Arduino IDE, the following code allows the simple drawing a of line using 5 red pixels.

void setup() {
  bg.enableColorCorrection(false);    // bg is the "background" layer
  matrix.addLayer(&bg);              
  matrix.setBrightness(255);         
  matrix.begin();
}

void loop() {
  bg.fillScreen({0, 0, 0});           // Clear to a color {r,g,b}
  
  bg.drawPixel(10, 10, {255, 0, 0});  // Draw some red pixels
  bg.drawPixel(12, 10, {255, 0, 0});
  bg.drawPixel(11, 11, {255, 0, 0});
  bg.drawPixel(10, 12, {255, 0, 0});
  bg.drawPixel(12, 12, {255, 0, 0});

  bg.swapBuffers();                   // The library offers double buffering
}

–– a2_single_pixel_animated
Another simple example with some moving LEDs.

In this second example, still working directly with Arduino, participants are shown how to animate a pixel on the LED matrix. Consisting of a counter, the animation uses a sine wave to move the pixel forward and back.

void loop() {
  static uint32_t count = 0;                   // Just a counter
  static uint8_t offs_y = TOTAL_HEIGHT / 2;

  bg.fillScreen({0, 0, 0});                    // Clear to a color {r,g,b}

  uint16_t x, y;
  for (uint16_t i = 0; i < TOTAL_WIDTH; i++) { // Draw a sine wave
    x = i;
    y = sin((i + 1) * count * 0.0005) * offs_y + offs_y;
    bg.drawPixel(x, y, {255, 255, 255});      
  }

  bg.swapBuffers();                            // The library offers double buffering
    
  digitalWrite(LED_BUILTIN, count / 10 % 2);   // Let's animate the built-in LED as well
  count++;
}

–– a3_serial_rgb_slave
A slave program that forwards incoming pixel data from the serial port to the LED panels.

This third example allows you to use the Teensy board as a slave and listen for the incoming pixel data from Processing, using the serial port. The code is not optimized but runs smooth at 60fps on a single matrix and around 30fps on 4 chained matrices. The following Processing examples (p1, p2, p3, p4) encode pixels from the canvas (or a render target).

Please note that if you want to daisy chain multiple LED Matrix displays, the total size of the chained matrices will need to be set here. The Teensy board needs to know how many pixels should it be listening to.

–– p1_serial_rgb_send_canvas
A Processing example that grabs all the pixels from the Processing canvas and sends them to the serial port.

In this example, a simple animation shows how the pixels are sent to the Teensy board. The size of the canvas is determined by the pixels you are trying to send. To use a larger canvas and render pixels to a texture which is then sent to the serial port, see p3 example.

void draw() {

  // Render some forms to the canvas
  background(0);
  ortho();
  stroke(255);
  fill(0);  
  translate(width/2, height/2);
  float b = min(width, height) * 0.48;
  for (int i=-10; i<=10; i++) {
    pushMatrix();
    translate(i * b * 0.35, 0);
    float a = frameCount + i * 5;
    rotateX(a * 0.011);
    rotateY(a * 0.015);
    rotateZ(a * 0.017);
    box(b);
    popMatrix();
  }

  // Write to the serial port (if open)
  if (serial != null) {    
    loadPixels();    
    int idx = 0;
    for (int i=0; i<pixels.length; i++) { 
      color c = pixels[i];
      buffer[idx++] = (byte)(c >> 16 & 0xFF); // r
      buffer[idx++] = (byte)(c >> 8 & 0xFF);  // g
      buffer[idx++] = (byte)(c & 0xFF);       // b
    }
    serial.write('*');     // The 'data' command
    serial.write(buffer);  // ...and the pixel values
  }
}

–– p2_serial_rgb_send_webcam
Same as above but with a live webcam.

This example takes the first example code to a more complex scenario. Here we are using the camera output and reformatting it to match the aspect ratio of the LED Matrix.

  if (cam.available()) {
    cam.read();

    float aspect = (float)(cam.height) / cam.width;
    float w = width;
    float h = w * aspect;

    // Display the scaled webcam image on the canvas
    image(cam, 0, 0, w, h);

–– p3_serial_rgb_preview
A slightly more structured example with a better (bigger) preview.

Here, the slave is always configured as a stack of matrices. The master program can be configured to slice the canvas according to the desired configuration.

This sketch sends all the pixels rendered to a texture to the serial port and a preview is rendered to the main canvas. A helper function to scan all the serial ports for a configured controller is provided. Note: The serial object is disabled for preview purposes. Make sure to initialise it properly

–– p4_serial_rgb_multi_anims
A demonstration running several scenes from a single Processing sketch.

This sketch sends all the pixels rendered to a texture to the serial port. An animation class is provided to facilitate the management of multiple scenes. A helper function to scan all the serial ports for a configured controller is provided. To navigate different scenes, use LEFT/RIGHT keys. Some scenes have a few different modes and you can try key 1-9 to see different results. Note: The serial object is disabled for preview purposes. Make sure to initialise it properly.

Notes: Additional informations in the code comments. Slave and master programs must be configured manually (an automatic configuration is out of scope but could be implemented).

Thats it! If you have any questions, or issues following the examples, please leave a comments below and we’ll try to help. Enjoy!

This workshop and the Symposium, were kindly supported by the new UAL Creative Computing Institute. The institute supports interdisciplinary teaching, research and knowledge exchange at the intersection of creativity and computational technologies. The Institute works across domains such as machine learning and artificial intelligence, alongside exploring how the contemporary world is being defined through human computer interaction and social platforms.

They are currently in the process of launching a series of undergraduate courses including BSc Creative Computing, as well as offering a number of positions for teaching staff including Reader, Course Leader, Senior Lecturer and Lecturer. Please see their website for more information or follow them on Twitter for updates.

Event Site | UAL Creative Computing Institute | GitHub

Leave a Reply

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