The Biological Clock dress

The Rainbowduino board was selected to drive a matrix of high intensity red LEDs assembled to represent a wearable clock face for an “art” dress. The theme of the dress is that the “biological clock” is ticking quite rapidly.

While the Rainbowduino board was primarily designed to interface with a conventional RGB dot matrix display, it is very capable of driving other matrix-connected arrays of LEDs.

This display was constructed of a plastic disk with holes to locate the (really bright) LEDs and the wiring hand soldered at the back. The Rainbowduino board and a battery holder with 4 AA cells hide elsewhere in the dress.

The Rainbowduino makes construction of a wearable device like this simpler because it eliminates the need for resistors in series with each of the LEDs - it controls the current on the board.

The still picture:


IMG_2036.jpg (131.79 KiB) Viewed 4457 times

The video:

The sketch (program which runs on the Rainbowduino board) :


// first whack at driving the “biological clock” with a Rainboduino board from SeeedStudio sept0609

// derived from Rainbowduino manual and beta example “Rainbow_CMD_V2_0”

// using the board to drive a matrix of 192 single LEDs on or off, no intensity modulation (going for “bright”)

// Rainbowduino was designed to mate with a specific full color LED matrix and pinout

#include “Rainbow.h”

#include <Wire.h>

// using the Wire library to make access to the Rainbowduino board slightly more straightforward…

// some sleuthing to determine Arduino pin numbers for the Wire library calls from the Rainbow.h file…

// Arduino pin number conventions - PORTD[0…7] are 0…7, PORTB[0…5] are 8…13 PORTC[0…5] are 14…19

// datapin is PORTC, bit 0 and clockpin is PORTC, bit 1

const int datapin = 14;

const int clockpin = 15;

// without having a datasheet with pinout for the MBI5168 (or Rainbowduino schematic), the following are guesses at pin functions

// PORTC, bit 2

const int latchEnable = 16; // active low

// PORTC, bit 3

const int outputEnable = 17; // active low…

// similarly for the 8 current drive lines

int driveLine[] = { 10, 9, 8, 7, 6, 5, 4, 3 }; // permute the order for different wiring hookups

// bioclock has 18 radial rows (like spokes of a wheel) of 6 LEDs each organized as 3 sets of 6x8 matrices.

// each spoke is driven by a source line and one byte of shift register switches.

// organized as 8 drive lines and “G” byte, then same 8 drive lines and “B” byte, then same 8 drive lines and “R” byte.

// long hand drives all the LEDs in a spoke, short hand drives only the inner 4

// running around the clock consists of indexing the drive lines in sequence for each of the three byte groups

unsigned long tickTimer; // overflows in 50 days?

void setup(void) {

pinMode(datapin, OUTPUT);

pinMode(clockpin, OUTPUT);

pinMode(latchEnable, OUTPUT);

pinMode(outputEnable, OUTPUT);

for (int i=0; i<8; i++) {

pinMode(driveLine[i], OUTPUT);


// misc variables for timing

tickTimer = millis(); // initialize timer to “now”


void loop(void) {

// // most basic, see if it will do anything. Yup, works.

// digitalWrite(clockpin, LOW);

// delay(250);

// digitalWrite(clockpin,HIGH);

// delay(250);

// // OK, try a LED anode drive line. Yup, works.

// digitalWrite(driveLine[0], LOW);

// delay(250);

// digitalWrite(driveLine[0],HIGH);

// delay(250);

// // test another part of it… Yup.

// for (int j=0; j<8; j++) {

// loadSource(j);

// delay(250); }

bioClock(); // biological clock


void loadSource(int rowIndex) // this selects one of the eight “row” drivers // in this case, “spoke” = “row”


static int currentRow = 0; // only want one row at a time turned on - this remembers which one is on

digitalWrite(driveLine[currentRow], LOW); // only want one at a time on, so turn off the old

digitalWrite(driveLine[rowIndex], HIGH); // turn on the new

currentRow = rowIndex; // remember what’s on to turn off next call


void loadswitches( unsigned char sw[3] ) // this loads 24 bits into the “column” switches


digitalWrite(latchEnable, HIGH);

shiftOut(datapin, clockpin, MSBFIRST, sw[0]);

shiftOut(datapin, clockpin, MSBFIRST, sw[1]);

shiftOut(datapin, clockpin, MSBFIRST, sw[2]);

digitalWrite(latchEnable, LOW);


void lamptest(void) { // light entire row in column order to check for opens

unsigned char fullColumn[][3] = {{0xFF,0,0}, {0, 0xFF, 0}, {0, 0, 0xFF}};

for (int i=0; i<3; i++) {

for (int j=0; j<8; j++) {

digitalWrite(outputEnable, HIGH);



digitalWrite(outputEnable, LOW);

delay(250); // run all spokes in 6 seconds… should be visible




void lamptest2(void) { // light one LED at a time, in row then column order to see if any bits stuck together

static unsigned char bitSelector[][3] = { {1,0,0},{2,0,0},{4,0,0},{8,0,0},{16,0,0},{32,0,0},{64,0,0},{128,0,0},



for (int i=0; i<6; i++) {

for (int j=0; j<24; j++) {

digitalWrite(outputEnable, HIGH);



digitalWrite(outputEnable, LOW);





boolean past(unsigned long timer) {

return (millis() > timer) ; // timer is in the past if current time is greater


int handToggle = 0;

int longHandSpoke = 0;

int shortHandSpoke = 0; // spoke which is currently illuminated

int columns = 18; // logically 3 6x8 arrays

unsigned char longHand[][3] = {{0xFC,0,0},{0,0xFC,0},{0,0,0xFC}};

unsigned char shortHand[][3] = {{0x3C,0,0},{0,0x3C,0},{0,0,0x3C}};

unsigned char centerDot[3] = {0,0,0x1} ; //enable the center dot

void bioClock(void) // sequence the stepping of both the short and long hand


if (past(tickTimer)) // update the pointers when the bioclock ticks

{ // wrap row pointers

if ( ++longHandSpoke >= columns )

{ longHandSpoke = 0;

if (++shortHandSpoke >= columns )

{ shortHandSpoke = 0; }


tickTimer = millis() +64ul; // set time for the next clocktick


// draw hands on alternate passes

switch (handToggle) {

case 0:

// longHand

digitalWrite(outputEnable, HIGH);

loadSource(longHandSpoke % 6);


digitalWrite(outputEnable, LOW);


case 1:

digitalWrite(outputEnable, HIGH);

loadSource(shortHandSpoke % 6);


digitalWrite(outputEnable, LOW);


case 2: // dot in the center

digitalWrite(outputEnable, HIGH);



digitalWrite(outputEnable, LOW);



handToggle = ++handToggle % 3 ; // switch display refresh task