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,