Saturday, January 22, 2011

Arduino Lilypad - 5x8 LED Jacket (Conclusion and thoughs)

This is the third post in a 3 post set:
Arduino Lilypad - 5x8 LED Jacket (Design and Construction)
Arduino Lilypad - 5x8 LED Jacket (Software)
Arduino Lilypad - 5x8 LED Jacket (Conclusion and thoughts)

As you can probably see from the pictures, the final jacket isn't quiet what I had drawn out. In the process working on this project I learned several things:
  1. Don't mix LED colors if you want readable text. While it isn't too bad in the video, the text would have been MUCH easier to read if it wasn't swapping between blue and white a lot.
  2. 5 Bits high isn't quite enough for a nice readable font. I would try at least 6 next time, maybe 7.
  3. Conductive thread is hard to work with, or, at least the 4-ply stuff that I have is. I didn't leave enough spacing between some of the wires, and I didn't think about whether I wanted the thread on the top or the bottom of the fabric when I started. By keeping my rows on the bottom, and my columns on the top I could have gotten a better layout.
  4. Don't over commit on the first project. I decided to drop the tri-color LED and switches after I saw how difficult it is to read the text. I'd rather have the parts for the next project then attach them to this project.
  5. Finding the right needle size is hard. The larger needled, which were easy to thread kept breaking when put though the holes on the Arduino board (the eye of the needle would snap off). 
  6. The display driver code should be moved off to a interrupt handler linked to a timer, rather then being run from a super-loop in the main code.
Summary:
  • Build Time: about 3 weeks (2 weekends, and several 1 to 2 hour stints on week nights).
  • Cost: about $90 total.
    • $10.00 jacket
    • $21.95 for Controller
    • $14.95 for power supply
    • $15.00 20 White LED ($0.75 * 20 = $15.00)
    • $19.80 20 Blue LED ($19.80)
    • $4 Conductive Thread ( about 30 ft from a 225 ft spool. Spool cost $30)
    • $2 Misc (needles, puff paint, etc).

Thursday, January 20, 2011

Arduino Lilypad - 5x8 LED Jacket (Software)

This is the second post in a 3 post set:
Arduino Lilypad - 5x8 LED Jacket (Design and Construction)
Arduino Lilypad - 5x8 LED Jacket (Software)
Arduino Lilypad - 5x8 LED Jacket (Conclusion and thoughts)

The software for this project uses the Arduino development environment, Arduino Alpha. For my project, I was using version 0021.

The software has very few responsibilities at this point. All it has to do is drive the LEDs to match a pattern. It also has to shift which LEDs are driven, as it can only drive a single row at a time (due to the matrix layout), and on a row, only 3 LEDs can be on at one time. This is because each LED takes about 17 mA of current. The maximum current rating of my power supply is 100 mA. 3 LED * 17 mA = 51 mA. This left about 50 mA for the processor at design time.

To do this, the code has a small state machine ( the function called updateDisplayState() ). Each time this state machine is called the code move onto a new block of LEDs. So the first time the function is called LEDS in position 1 in the block below are illuminated. When the function is called again, the LEDs in block 2 are called, and so on until block F. After block F, the code cycles back to block 1.

DDDEEEFF
AAABBBCC
77788899
44455566
11122233

The function updateDisplayState() used a buffer of bytes to represent the display. Each byte is one column, where BIT0 is the bottom LED, and BIT4 is the top LED. So a 8 byte buffer can be used to show 8 columns of LEDs. In order to get the text to shift, I just move the pointer in the buffer over occasionally. This creates the illusion of the text characters moving across the screen.

The code for the Arduion, as well as the Python 3.1 scrip used to generate the text byte sequence is below.

I'll do a final post on lessons learned, and thoughs for next time later.

Arduino Code:
/*

LedJacket

This code Drives a 5x8 LED display sewen onto a jacket.

*/
// ----------------------------------------------------------------------------------
// HW Settings for LED Display
// ----------------------------------------------------------------------------------
const char ROW_SIZE = 5;
const char COLUMN_SIZE = 8;

// Pull to 5V to illuminate LED
const char RowPins[ROW_SIZE] = {4,3,2,19,18};
// Pull to GND to illumiate LED
const char ColumnPins[COLUMN_SIZE] = {17,16,15,14,13,12,11,10};

// ----------------------------------------------------------------------------------
// Display Buffer
// ----------------------------------------------------------------------------------
// Converted Text Block of " TEAM 3662, SNOW BYTE "
const unsigned char TXT_SNOW_BYTE_SIZE = 95;
static unsigned char TxtSnowByte[TXT_SNOW_BYTE_SIZE] = {0,0,0,0,0,0,0,0,16,31,16,0,31,21,21,0,15,20,15,0,15,16,15,16,15,0,0,0,21,21,31,0,31,21,23,0,31,21,23,0,23,21,29,0,1,3,2,0,0,0,9,21,18,0,31,8,4,31,0,14,17,14,0,30,1,30,1,30,0,0,0,31,21,10,0,24,7,24,0,16,31,16,0,31,21,21,0,0,0,0,0,0,0,0,0};

// Show all Blue LED.
const unsigned char TxtBlue[8] = {0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55};

// Show all White LED.
const unsigned char TxtWhite[8] = {0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA};

// ----------------------------------------------------------------------------------
// Display Timing Constants
// ----------------------------------------------------------------------------------
// USEC_WAIT_BETWEEN_MULTIPLEXING multiplied by DISPLAY_UPDATE_COUNT give
// the time between when the text scroll to the next number...
// Right now it is about 1500 * 600 = 900000 uSec, or 0.9 Seconds.
static const unsigned int DISPLAY_UPDATE_COUNT = 500;
static const unsigned int USEC_WAIT_BETWEEN_MULTIPLEXING = 850;

// ----------------------------------------------------------------------------------
// Run once setup hook.
// ----------------------------------------------------------------------------------
void setup() {
LedArray_initHw();
}

// ----------------------------------------------------------------------------------
// Main Function loop.
// ----------------------------------------------------------------------------------
void loop() {

// Loop though a buffer which is bigger then our little 5x8 display, scroll across it.
for(unsigned char index =0; index < (TXT_SNOW_BYTE_SIZE - COLUMN_SIZE); index++)
{
// Show this buffered output for about 350 miliseconds. 
for(unsigned int i =0 ; i < DISPLAY_UPDATE_COUNT ; i++)
{
updateDisplayState(&TxtSnowByte[index]);
delayMicroseconds(USEC_WAIT_BETWEEN_MULTIPLEXING);
}
}

// Cycle between Blue and White LED
for(unsigned char index = 0; index < 10; index++)
{
// Point to either the Blue or White LED buffer
const unsigned char* showData = 0;
(index & 0x01) ? showData = TxtBlue : showData = TxtWhite;

// Show the buffer
for(unsigned int i =0 ; i < DISPLAY_UPDATE_COUNT ; i++)
{
updateDisplayState(showData);
delayMicroseconds(USEC_WAIT_BETWEEN_MULTIPLEXING);
}
}
}

//------------------------------------------------------------------------
// Clears all the items on the LED Display.
// This function ensure that the hardware is properly configured to
// be used with the LED array. It also revers biases the Diodes to
// ensure they are off.
//
// NOTE: origionally I had made the unused lines high-impedence inputs
// however, I had issues with leakeages on the jacket, possibly
// due to the conductive thread. I found that driving the lines
// provided a much better result - Thomas (Jan 15th, 2011)
void LedArray_initHw(void) {


// Ensure each Column Line is set to HW, and driven High
for( char i = 0; i < COLUMN_SIZE; i++)
{
// Set to input, and write to LOW to disable pull up Resistor.
pinMode(ColumnPins[i], OUTPUT);
digitalWrite(ColumnPins[i],HIGH);
}

// Ensure each Row line is set to HW and driven Low
for( char i = 0; i < ROW_SIZE; i++)
{
// Set to input, and write to LOW to disable pull up Resistor.
pinMode(RowPins[i],OUTPUT);
digitalWrite(RowPins[i],LOW);
}
}

//------------------------------------------------------------------------
// Sets the next set of display bits.
//
// This function impliment a simple state machine which ensure that only
// 3 LEDs can be turned on at one time. This is to limit the current used
// by the system. Each LED which is turn on takes about 16 - 18 mA of 
// current. The AAA based power supply maxes out at about 100 mA. with 3 LED on
// we pull about 55 - 60 mA. This leaves a safty margine, as well as space for the
// power consumed by the Arduino.
//
// The state machine drives on Row at a time, turning on up to 3 LEDs.
void updateDisplayState(const unsigned char* bufferHead)
{
static unsigned char lastActiveRow = 0;
static unsigned char lastBitMask = 0;
static unsigned char stateIndex = 0;

switch (stateIndex)
{
case 0:
// Reset HW.
digitalWrite(ColumnPins[6],HIGH);
digitalWrite(ColumnPins[7],HIGH);
digitalWrite(RowPins[lastActiveRow],LOW);
lastActiveRow++;
if (lastActiveRow > ROW_SIZE) lastActiveRow =0;
lastBitMask = 1 << lastActiveRow;

// Write out new bits.
digitalWrite(RowPins[lastActiveRow],HIGH);
digitalWrite(ColumnPins[0], (bufferHead[0] & lastBitMask ) ? LOW : HIGH);
digitalWrite(ColumnPins[1], (bufferHead[1] & lastBitMask ) ? LOW : HIGH);
digitalWrite(ColumnPins[2], (bufferHead[2] & lastBitMask ) ? LOW : HIGH);
break;
case 1:
// Reset HW.
digitalWrite(ColumnPins[0],HIGH);
digitalWrite(ColumnPins[1],HIGH);
digitalWrite(ColumnPins[2],HIGH);

// Write out new bits.
digitalWrite(ColumnPins[3], (bufferHead[3] & lastBitMask ) ? LOW : HIGH);
digitalWrite(ColumnPins[4], (bufferHead[4] & lastBitMask ) ? LOW : HIGH);
digitalWrite(ColumnPins[5], (bufferHead[5] & lastBitMask ) ? LOW : HIGH);
break;
case 2:
// Reset HW.
digitalWrite(ColumnPins[3],HIGH);
digitalWrite(ColumnPins[4],HIGH);
digitalWrite(ColumnPins[5],HIGH);

// Write out new bits.
digitalWrite(ColumnPins[6], (bufferHead[6] & lastBitMask ) ? LOW : HIGH);
digitalWrite(ColumnPins[7], (bufferHead[7] & lastBitMask ) ? LOW : HIGH);
break;
}

// Step to the next state.
stateIndex++;
if ( stateIndex > 2) stateIndex = 0;
}

In order to generate the text string above (see the constant TxtSnowByte[] and TXT_SNOW_BYTE_SIZE) I used a small python scrip to generate the byte string.

##
## Translator for a 5 bit tall font.
##
## By: Thomas Anderson
## Last Updated: Jan 15th, 2011

## Font Dictionary data
# This dictionary assumes the following:
# * All Letters will be capital
# * Each letter will be represented by a series of bytes.
# * The bytes will be decodes, so that: 
#   - Bit0 represents the lowest pixel on a 5 bit Row.
#   - Bit4 represent the top pixel on a 5 bit Row.
# * An end user will want both a string of raw data (bytes), and the number of bytes in the string.
# * Values in the 'bytes' section are in base 10, not in hex.
# * Values in the 'bytes' section are assumed to be used as unsigned 8-bit integers.
#
# New letters/characters can be added to this dictionary simply by defining a new entry.
fontDict = {
    'A' :
    {
        'bytes' : "15,20,15,0",
        'size'  : 4,
    },
    'B' :
    {
        'bytes' : "31,21,10,0",
        'size'  : 4,
    },
    'C' :
    {
        'bytes' : "14,17,17,0",
        'size'  : 4,
    },
    'D' :
    {
        'bytes' : "31,17,14,0",
        'size'  : 4,
    },
    'E' :
    {
        'bytes' : "31,21,21,0",
        'size'  : 4,
    },
    'F' :
    {
        'bytes' : "31,20,20,0",
        'size'  : 4,
    },
    'G' :
    {
        'bytes' : "31,17,21,6,0",
        'size'  : 5,
    },
    'H' :
    {
        'bytes' : "31,4,31,0",
        'size'  : 4,
    },
    'I' :
    {
        'bytes' : "17,31,17,0",
        'size'  : 4,
    },
    'J' :
    {
        'bytes' : "2,17,31,16,0",
        'size'  : 5,
    },
    'K' :
    {
        'bytes' : "31,4,27,0",
        'size'  : 4,
    },
    'L' :
    {
        'bytes' : "31,1,1,0",
        'size'  : 4,
    },
    'M' :
    {
        'bytes' : "15,16,15,16,15,0",
        'size'  : 6,
    },
    'N' :
    {
        'bytes' : "31,8,4,31,0",
        'size'  : 5,
    },
    'O' :
    {
        'bytes' : "14,17,14,0",
        'size'  : 4,
    },
    'P' :
    {
        'bytes' : "31,20,28,0",
        'size'  : 4,
    },
    'Q' :
    {
        'bytes' : "14,17,18,13,0",
        'size'  : 5,
    },
    'R' :
    {
        'bytes' : "31,20,11,0",
        'size'  : 4,
    },
    'S' :
    {
        'bytes' : "9,21,18,0",
        'size'  : 4,
    },
    'T' :
    {
        'bytes' : "16,31,16,0",
        'size'  : 4,
    },
    'U' :
    {
        'bytes' : "31,1,31,0",
        'size'  : 4,
    },
    'V' :
    {
        'bytes' : "30,1,30,0",
        'size'  : 4,
    },
    'W' :
    {
        'bytes' : "30,1,30,1,30,0",
        'size'  : 6,
    },
    'X' :
    {
        'bytes' : "27,4,27,0",
        'size'  : 4,
    },
    'Y' :
    {
        'bytes' : "24,7,24,0",
        'size'  : 4,
    },
    'Z' :
    {
        'bytes' : "19,21,25,0",
        'size'  : 4,
    },
    '0' :
    {
        'bytes' : "31,17,31,0",
        'size'  : 4,
    },
    '1' :
    {
        'bytes' : "9,31,1,0",
        'size'  : 4,
    },
    '2' :
    {
        'bytes' : "23,21,29,0",
        'size'  : 4,
    },
    '3' :
    {
        'bytes' : "21,21,31,0",
        'size'  : 4,
    },
    '4' :
    {
        'bytes' : "28,4,31,0",
        'size'  : 4,
    },
    '5' :
    {
        'bytes' : "29,21,23,0",
        'size'  : 4,
    },
    '6' :
    {
        'bytes' : "31,21,23,0",
        'size'  : 4,
    },
    '7' :
    {
        'bytes' : "16,16,31,0",
        'size'  : 4,
    },
    '8' :
    {
        'bytes' : "31,21,31,0",
        'size'  : 4,
    },
    '9' :
    {
        'bytes' : "29,21,31,0",
        'size'  : 4,
    },
    ' ' :
    {
        'bytes' : "0,0",
        'size'  : 2,
    },
    ',' :
    {
        'bytes' : "1,3,2,0",
        'size'  : 4,
    },
}

# This function uses the above dictionary to create an Array of bytes
#  which represent the text message for a 5 Row LED display.
def convertTextToArray(stringToConvert):
    
    sizeInBytes = 0
    outString = ""
    
    for letter in stringToConvert:
        if letter in fontDict:
            sizeInBytes += fontDict[letter]['size']
            outString += fontDict[letter]['bytes'] + "," 
        else :
            print("Item ",letter, " not in Dictionary")    
    
    # Display the results
    print("Text          = ",stringToConvert)
    print("Size In bytes = ",sizeInBytes)
    print("Byte String   = ",outString)

# Use the function above to print out the following messages
convertTextToArray("    TEAM 3662, SNOW BYTE    ")
convertTextToArray("A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 1234567890  ")

When run, this generates:
Text          =      TEAM 3662, SNOW BYTE    
Size In bytes =  95
Byte String   =  0,0,0,0,0,0,0,0,16,31,16,0,31,21,21,0,15,20,15,0,15,16,15,16,15,0,0,0,21,21,31,0,31,21,23,0,31,21,23,0,23,21,29,0,1,3,2,0,0,0,9,21,18,0,31,8,4,31,0,14,17,14,0,30,1,30,1,30,0,0,0,31,21,10,0,24,7,24,0,16,31,16,0,31,21,21,0,0,0,0,0,0,0,0,0,
Text          =  A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 1234567890  
Size In bytes =  208
Byte String   =  15,20,15,0,0,0,31,21,10,0,0,0,14,17,17,0,0,0,31,17,14,0,0,0,31,21,21,0,0,0,31,20,20,0,0,0,31,17,21,6,0,0,0,31,4,31,0,0,0,17,31,17,0,0,0,2,17,31,16,0,0,0,31,4,27,0,0,0,31,1,1,0,0,0,15,16,15,16,15,0,0,0,31,8,4,31,0,0,0,14,17,14,0,0,0,31,20,28,0,0,0,14,17,18,13,0,0,0,31,20,11,0,0,0,9,21,18,0,0,0,16,31,16,0,0,0,31,1,31,0,0,0,30,1,30,0,0,0,30,1,30,1,30,0,0,0,27,4,27,0,0,0,24,7,24,0,0,0,19,21,25,0,0,0,9,31,1,0,23,21,29,0,21,21,31,0,28,4,31,0,29,21,23,0,31,21,23,0,16,16,31,0,31,21,31,0,29,21,31,0,31,17,31,0,0,0,0,0,

Wednesday, January 19, 2011

Arduino Lilypad - 5x8 LED Jacket (Design and Construction).

LED Jacket with 5x8 array
This post is the first in a 3-post set:  
Arduino Lilypad - 5x8 LED Jacket (Design and Construction)
Arduino Lilypad - 5x8 LED Jacket (Software) 
Arduino Lilypad - 5x8 LED Jacket (Conclusion and thoughts)

This was a concept project.

I work with a First Robotics Team (Team 3662, SnowByte!) as a mentor. Unfortunately, there are usually more kids interested in programming then can really get their hands dirty on the robot.

This project was an experiment in getting some more programming work drummed up that could be done at a high school level (with some mentoring). The idea is that the kids can build stuff they can wear to the FIRST competition, AND practice their programming.

I decided to try my hand at sewable circuit. The idea is to put an LED array on a jacket, and drive it with mininal parts and complexity.





Research:
While looking for ideas, I pulled heavily from:
There are also quite a few other wearable DIY projects on youTube and Make that had good ideas.

Design:

  I knew I wanted the following:
  • Low cost
  • Low part count
  • Quick to make.
  • Interesting to program.
In order to achieve this, I decided that I would directly drive a LED array from the Arduino pins. According to the documentation, a pin could sink up to 40 mA, but could source more. Because of that, I planned to drive a line of LEDs, and then pull down the ones I wanted to light up.

Looking at the Arduino Lilypad I found that there are 12 usable Digital I/O lines, and 6 Analog lines, which could also be used in a digital mode. This allowed for 18 pins in my project. I wanted to have two push buttons, and one tri-color LED. Each push button took 1 pin, and a tri-color LED took 3 pins. So, I now had (18 - 1 -1 -3 = 13 ) pins remaining.  figuring that 5 bits was enough vertically for an array, I was then left with 8 lines for columns on my array.

Since I had a 5 x 8 pin grid, I needed 40 LEDs. I now had a basic parts list.

Construction:
I ordered most of my parts from SparkFun.
All-in-all, this order can to about $140 (with shipping, handling, tax, etc ). I bought extra LEDs in case I had any issues with them.

I Still needed a few things though.
 I also used a few around the house items:
  • Scissors
  • Xacto knife (for cutting out the sewing that I messed up)
  • Chalk
  • Ruler
Then I drew out my design in order to make sure that:
  • My Lines crossed as little as possible
  • I could wire up my Tri-color LED to 3 PWM lines.
 I ended up mounting my Arduino lilypad on the back side of the jacket in order to get things to line up correctly. I setup my system so that
  • pins A4, A5, 2, 3, 4 were the Row pins connected to the + side of the LEDs. 
  • Pins A3,A2,A1,A0,13,12,11,10 were my column pins, and connected to the - side of the LEDS. 
  •  Pins 5,6 and 9 are PWM pins, and are connected to the tri-color LED.
  • Pins 7 and 8 are connected to the switches.
The LEDs from Spark fun came with a 100 Ohm resistor already in series, so I didn't need any extra line resistance.

I then drew out an outline of the Arduino on the jacket with chalk, and using the ruler drew the lines where I wanted to sew. ( Forgot to take pictures, sorry :) ).

Over the next two weeks I sewed the LEDs on. In the process, I learned that I suck at sewing. I also wrote a very simple program that turned one LED on at a time, and switch which LEDs was being driven every 1 1/2 seconds. This allowed me to test out the sewing job as soon as I had my first column of LEDs attached.
Arduino Lilypad inside jacket pocket.

Once I had a column of LEDs sewn on and working, I covered the conductive thread with puff paint and let if dry over night. Before I started doing that I had a lot of trouble with lines shorting each other out. 
LED Array (with my poor sewing and puff paint covering the stitching)


I'll cover the software in the next post, however, here's a video of the text scrolling in action.




Monday, January 17, 2011

Hello

Hi!

Glad you stopped by. This blog is intended to be a place where I can share the results of my weekend hobby projects. I'm an Electrical Engineer who ended up designed firmware, and occasionally I work on small micro-controller based projects on the weekend.

Since I like reading about what others are doing, I though I should return the favor and share what I'm doing.

Thanks for visiting!

- Everett_Tom