Introduction: V-USB ATtiny85 Project Board and an 8x8 Red LED Matrix Display

This short project will use an 8x8 Red Led Matrix display with the V-USB ATtiny85 Project Board created in my first Instructable. The object of this exercise being to have an ATtiny85 control an 8x8 Led Matrix Display, and then try to find something useful to do with it.

Step 1: Parts List

Description, No. Required, Source, Unit Cost, Total Unit Cost

  1. Assembled Custom PCB, 1 , PCBWay , $8.21 each , $8.21(when 10 units are produced, unit price drops when more boards are ordered for production)
  2. 8x8 Red LED Matrix, 1 , Amazon , $3.47 each , $3.47
  3. Bread Board Wires , 1 set , Amazon , $6.67 , $6.67
  4. Mini Bread Board, 1 , Amazon , $0.97 each , $0.97
  5. Small tactile switch, 1 , Amazon , $0.05 each , $0.05
  6. Photo Resistor, 1 , Amazon , $0.07 each , $0.07
  7. 10KOhm Resistor, 1 , Amazon , $0.05 each , $0.05
  8. 100nF capacitor, 1 , Amazon , $0.07 each, $0.07

Total cost of Project $19.56

Note1: This cost assumes that you have no components on hand.
Note2: You may substitute the custom PCB (V-USB ATtiny85 Project Board) with any Arduino compatible micro controller. For example the Arduino Uno.

Step 2: The MaxMatrix Library

The MaxMatrix Library should be installed first.

  1. Download the MaxMatrix Library from here.
  2. Extract or unzip the compressed file.
  3. Copy or move the extracted folder to the Libraries folder of your Arduino IDE.

The MaxMatrix Usage wiki explains how to use the library.

The example code that follows is based on this library work and also the work found here.

Step 3: Make Some Connections

Connect to the 8x8 LED Matrix Module to the Micro Controller

  1. VCC to the 5V pin on the ATtiny85 microcontroller
  2. GND to the GND pin on the ATtiny85 microcontroller
  3. DIN to PB1 pin on the ATtiny85 microcontroller
  4. CS to PB2 pin on the ATtiny85 microcontroller
  5. CLK to PB3 pin on the ATtiny85 microcontroller

Step 4: The Atmel ATtiny85 Micro Controller

Specification for the ATtiny85 taken from the Atmel website.
Parameter, Value

  • Flash (Kbytes), 8 Kbytes (8192 Bytes)
  • Pin Count, 8
  • Max. Operating Freq, (MHz): 20 MHz
  • CPU: 8-bit AVR
  • Max I/O Pins, 6
  • ADC Channels, 4
  • ADC Resolution (bits), 10
  • Temp. Sensor, Yes
  • SRAM (Bytes), 512
  • EEPROM (Bytes), 512
  • Self Program Memory, Yes
  • Temp. Range (deg C), -40 to 85
  • Operating Voltage (Vcc), 1.8 to 5.5

The EEPROM memory will hold 512 bytes. So we immediately have a problem with the example code. The character definitions are just too many, we will need to cut the definitions down in order to fit into the EEPROM space.

Note1: Some of the pictures show lower case letters etc. I was using the code directly as written. It worked, however I found that it could break the Micronucleus, and make the ATtiny85 device unusable with the Arduino IDE/Micronucleus. I put this down to the original code, requiring around 882 bytes in the EEPROM for the character definitions. I decided to cut the character set down to a more manageable number. A set that would fit in the EEPROM memory.

Note2: This does not preclude just removing the Micronucleus flashing the program to the ATtiny, using a micro controller programmer. For example the USBasp.

Note3: The sketch code will work fine with a micro controller such as the Arduino Uno, without any modification. With more memory you are able to define a complete character set.

Step 5: The Program

Let us reduce the number of character definitions from the example code in order to fit easily into EEPROM memory. we will use the first 60 or so character definitions, "space" to "_". This will give us definitions for 65 characters. Each definition requires column count byte, a row count byte and 5 bytes for the character bits. So we now need (65 * 7) = 455 bytes to store the character definitions in EEPROM. This will fit within the 512 total memory space of the ATtiny85, so far so good.

const unsigned char CH[] PROGMEM = {
  3, 8, B00000000, B00000000, B00000000, B00000000, B00000000, // space
  1, 8, B01011111, B00000000, B00000000, B00000000, B00000000, // !
  3, 8, B00000011, B00000000, B00000011, B00000000, B00000000, // "
  5, 8, B00010100, B00111110, B00010100, B00111110, B00010100, // #
  4, 8, B00100100, B01101010, B00101011, B00010010, B00000000, // $
  5, 8, B01100011, B00010011, B00001000, B01100100, B01100011, // %
  5, 8, B00110110, B01001001, B01010110, B00100000, B01010000, // &
  1, 8, B00000011, B00000000, B00000000, B00000000, B00000000, // '
  3, 8, B00011100, B00100010, B01000001, B00000000, B00000000, // (
  3, 8, B01000001, B00100010, B00011100, B00000000, B00000000, // )
  5, 8, B00101000, B00011000, B00001110, B00011000, B00101000, // *
  5, 8, B00001000, B00001000, B00111110, B00001000, B00001000, // +
  2, 8, B10110000, B01110000, B00000000, B00000000, B00000000, // ,
  4, 8, B00001000, B00001000, B00001000, B00001000, B00000000, // -
  2, 8, B01100000, B01100000, B00000000, B00000000, B00000000, // .
  4, 8, B01100000, B00011000, B00000110, B00000001, B00000000, // /
  4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // 0
  3, 8, B01000010, B01111111, B01000000, B00000000, B00000000, // 1
  4, 8, B01100010, B01010001, B01001001, B01000110, B00000000, // 2
  4, 8, B00100010, B01000001, B01001001, B00110110, B00000000, // 3
  4, 8, B00011000, B00010100, B00010010, B01111111, B00000000, // 4
  4, 8, B00100111, B01000101, B01000101, B00111001, B00000000, // 5
  4, 8, B00111110, B01001001, B01001001, B00110000, B00000000, // 6
  4, 8, B01100001, B00010001, B00001001, B00000111, B00000000, // 7
  4, 8, B00110110, B01001001, B01001001, B00110110, B00000000, // 8
  4, 8, B00000110, B01001001, B01001001, B00111110, B00000000, // 9
  2, 8, B01010000, B00000000, B00000000, B00000000, B00000000, // :
  2, 8, B10000000, B01010000, B00000000, B00000000, B00000000, // ;
  3, 8, B00010000, B00101000, B01000100, B00000000, B00000000, // <
  3, 8, B00010100, B00010100, B00010100, B00000000, B00000000, // =
  3, 8, B01000100, B00101000, B00010000, B00000000, B00000000, // >
  4, 8, B00000010, B01011001, B00001001, B00000110, B00000000, // ?
  5, 8, B00111110, B01001001, B01010101, B01011101, B00001110, // @
  4, 8, B01111110, B00010001, B00010001, B01111110, B00000000, // A
  4, 8, B01111111, B01001001, B01001001, B00110110, B00000000, // B
  4, 8, B00111110, B01000001, B01000001, B00100010, B00000000, // C
  4, 8, B01111111, B01000001, B01000001, B00111110, B00000000, // D
  4, 8, B01111111, B01001001, B01001001, B01000001, B00000000, // E
  4, 8, B01111111, B00001001, B00001001, B00000001, B00000000, // F
  4, 8, B00111110, B01000001, B01001001, B01111010, B00000000, // G
  4, 8, B01111111, B00001000, B00001000, B01111111, B00000000, // H
  3, 8, B01000001, B01111111, B01000001, B00000000, B00000000, // I
  4, 8, B00110000, B01000000, B01000001, B00111111, B00000000, // J
  4, 8, B01111111, B00001000, B00010100, B01100011, B00000000, // K
  4, 8, B01111111, B01000000, B01000000, B01000000, B00000000, // L
  5, 8, B01111111, B00000010, B00001100, B00000010, B01111111, // M
  5, 8, B01111111, B00000100, B00001000, B00010000, B01111111, // N
  4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // O
  4, 8, B01111111, B00001001, B00001001, B00000110, B00000000, // P
  4, 8, B00111110, B01000001, B01000001, B10111110, B00000000, // Q
  4, 8, B01111111, B00001001, B00001001, B01110110, B00000000, // R
  4, 8, B01000110, B01001001, B01001001, B00110010, B00000000, // S
  5, 8, B00000001, B00000001, B01111111, B00000001, B00000001, // T
  4, 8, B00111111, B01000000, B01000000, B00111111, B00000000, // U
  5, 8, B00001111, B00110000, B01000000, B00110000, B00001111, // V
  5, 8, B00111111, B01000000, B00111000, B01000000, B00111111, // W
  5, 8, B01100011, B00010100, B00001000, B00010100, B01100011, // X
  5, 8, B00000111, B00001000, B01110000, B00001000, B00000111, // Y
  4, 8, B01100001, B01010001, B01001001, B01000111, B00000000, // Z
  2, 8, B01111111, B01000001, B00000000, B00000000, B00000000, // [
  4, 8, B00000001, B00000110, B00011000, B01100000, B00000000, // \ backslash
  2, 8, B01000001, B01111111, B00000000, B00000000, B00000000, // ]
}; 

Let us look at the sketch

#include <MaxMatrix.h>
#include <avr/pgmspace.h>

Load the necessary libraries. MaxMatrix will manipulate the data in order to display it on the led matrix, and pgmspace will put the data into the EEPROM memory.

MaxMatrix m(DIN, CS, CLK, MAXS);

This sets up the library data, load and clock pins. It also indicates if there are more than one matrix on the data line and will "spill" the data over to it.

m.shiftLeft(false, true);
printStringWithShift(string1, 100);

Indicate the scrolling direction and call the display routine to scroll the text string across our led matrix. The number 100 is the scrolling speed of the text displayed. Try changing the value and see what happens.

void printCharWithShift(char c, int shift_speed) {

  if (c < 32) return;
  c -= 32;
  memcpy_P(buffer, CH + 7 * c, 7);
  m.writeSprite(MAXS * 8, 0, buffer);
  m.setColumn(MAXS * 8 + buffer[0], 0);
  for (int i = 0; i < buffer[0] + 1; i++)
  {
    delay(shift_speed);
    m.shiftLeft(false, false);
  }
}

The scrolling display routine, called with the character array pointer c, and the scrolling speed of the characters displayed.

// Extract characters from Scrolling text<br>void printStringWithShift(char* s, int shift_speed) {
  while (*s != 0) {
    printCharWithShift(*s, shift_speed);
    s++;
  }
}

The heart of the sketch, this routine pulls the character from the text to be displayed until it finds the end of the string text, denoted by 0.

Now

  1. Download the example sketch MatMatrixExample1.ino
  2. Open the file in the Arduino IDE
  3. Compile and load the sketch to the ATtiny85 micro-controller.
Note1: these examples will also work with an Arduino Uno, nano etc. Either use the same pin assignments or change to your preference. It is all about experimentation.

Step 6: Let's Add a Sensor

Now let us try and display some real world information, let us add a photo-resistor to the ATtiny85 device and display the analog readings on the 8x8 Led Matrix.

  1. Connect the photo-resistor as shown in the Fritzing diagram and schematic above.
  2. Connect a 10K Ohm resistor from GND on the 8x8 led matrix to one side of the photo-resistor
  3. Connect the other side of the photo-resistor to VCC on the 8x8 led matrix
  4. Now connect PB4 (A2) from the ATtiny85 device to the point where the 10K Ohm resistor and the photo-resistor meet
  5. Download the MatMatrixExample2.ino sketch
  6. Load the MatMatrixExample2.ino sketch into the Arduino IDE
  7. Compile the sketch and transfer it to the ATtiny85 device

Let us look at the example sketch

#include  <MatMatrix.h>
#include  <avr/prgmspace.h>

Load the libraries necessary . The character definitions have already been described above.

#define DIN 1
#define CS 2
#define CLK 3
#define MAXS 1
#define PHOTO 2    // physical pin 4, PB4, A2 (pin definition for the ATtiny85)
#define SPEED 100

Let us try to keep memory usage to a minimum, we will define the values we need, we will not use integer variables if we do not need them.

int photo;
int strlength;
String data;

These integer variables we will need.

photo will read the photo-resistor sensor value

strlength and data will be used to convert the integer value read to a character string.

The MaxMatrix library requires that the data is transferred as a character array, so these variables will help us do that.

MaxMatrix m(DIN, CS, CLK, MAXS); // define Library
byte buffer[10];
char string1[] = "THE V-USB ATTINY85 PROJECT BOARD --- ANALOG LIGHT INTENSITY";  // Scrolling Text but ony upper case
char analogdata[6];
// Try lower case and see what happens!! ... fun

The character array analogdata[6] provides space for the data result. However should the length of the data result be longer that six characters, then we will have garbage displayed. The data is terminated with a "/0". Adjust the value and see what happens.

// PB4 set for output
pinMode(PHOTO, OUTPUT);

Set the I/O pin for analog reading on the photo-resistor.

Note1: this is PB4, but analog A2.
// Let us make a loop and measure the light intensity for say 20 cycles
  for (int n = 0; n < 20; n++) {
    photo = analogRead(PHOTO);
    data = String(photo) + String(" , ");
    strlength = data.length() + 1;
    data.toCharArray(analogdata , strlength);
    printStringWithShift(analogdata, SPEED);
    delay(SPEED);
  }

Now let us read the analog pin a few times and display the results. Try covering the photo-resistor with your hand, and shining a torch onto the sensor.

Step 7: Let's Make a Die

Now let us add a button and turn this device into a six digit die.

  1. Remove the photo-resistor
  2. Add the push button to the circuit.
  3. Connect the 10K Ohm resistor from the GND pin of the 8x8 led matrix module to one side of the push button.
  4. Connect the VCC pin of the 8x8 led matrix to the same side rail of the push button.
  5. Connect the opposite side rail of the push button to pin PB4 of the ATtiny85 device.

Using the MatMatrixExample2.ino sketch downloaded in Step 6, try pressing the button you have just added to the circuit.

What just happened?

This is why we have to be sure we have enough space for our data

char analogdata[6];

Try changing the size of this array, say 10 should be a good compromise. Compile the sketch and load it onto the ATtiny85 device, try pressing the button again.

What happened this time?

Now let us define some sprites to represent the die. Read the library wiki, this will give further details on the library usage.

Note1: singular = die, plural = dice.

Sprite definition for die face 1

8, 8, 0, 0, 0, 24, 24, 0, 0, 0

byte DIE_1[] = {8, 8, 0, 0, 0, 24, 24, 0, 0, 0};
...
m.writeSprite(0, 0, DIE_1);

Sprite definition for die face 2

8, 8, 192, 192, 0, 0, 0, 3, 3

byte DIE_2[] = {8, 8, 192, 192, 0, 0, 0, 3, 3};
...
m.writeSprite(0, 0, DIE_2);

Sprite definition for die face 3

8, 8, 192, 192, 0, 24, 24, 0, 3, 3

byte DIE_3[] = {8, 8, 192, 192, 0, 24, 24, 0, 3, 3};
...
m.writeSprite(0, 0, DIE_3);

Now we will generate a random number to simulate a roll of the die.

randomSeed(analogRead(0)); // randomize the seed

...

long dieRoll = random(0, 7);

...

Now let us try the example sketch.

  1. Download the MaxmatrixExample3.ino file.
  2. Open the file in the Arduino IDE.
  3. Compile and load the sketch to the ATtiny85 micro controller
  4. Press the button to roll the die.

Well it works, sort of. Do you notice how the die may sometimes roll away with itself. The pin state does not always seem to return to the LOW state. It may float between HIGH and LOW, and keep on rolling.

How should we fix that.?

Let us try adding a 100nF capacitor across the switch rails.

Play the game now, is the die more stable?

Does it wait for the next button press?

Well it should.

Did you notice that this die has seven faces?

The seventh face is the Joker, roll the Joker and lose.

byte DIE_J[] = {8, 8, 0, 128, 224, 251, 251, 224, 128, 0};  // Joker !!!
//byte DIE_J[] = {8, 8, 0, 136, 232, 59, 59, 232, 136, 0}; // alternative Joker !!!

There are two choices in the sketch for the Joker sprite definition. Remove the comments to make the sprite active.

Step 8: Character Designer

Here is a simple character designer, that will allow us to design a character or a sprite in Microsoft Excel (or just upload it to Google Drive, and use it with Google Sheets). Simply place a 1 where we require a pixel to be, the columns on the right of the matrix will indicate the decimal and hexadecimal value for the pixel row defined. Now we type these into the program definition, for display by MaxMatrix on the 8x8 led matrix.

One thing to bear in mind with the creation of characters for this particular 8x8 led matrix using MaxMatrix libraries. The byte values are COLUMNS (notice the orientation from the photographs) when the matrix is plugged into a bread board with the MAX2719 chip to the bottom. If more than one matrix is going to be used then this needs to be considered in order to have a seamless scrolling display device.

The Character Designer is generating byte values in ROWS. So rotate the graphic design accordingly, in order to generate values in the correct orientation.

Note1: there are other free tools out there, a general web search should bring up a few popular ones.

Step 9: Conclusion: Make It Better

There is a lot of room for improvement. The sketch code additions I have made to these public domain examples are not the best solutions to the problem. There is plenty of room for expansion on this project idea. However the 8x8 led matrix will display real world data quite readily, as shown in Step 6 with the photo resistor. The die game developed for Step 7 is actually quite playable, once the capacitor is added to the circuit.

  • Try adding another 8x8 led matrix.
  • Make the die game a dice game (two or three dice)
  • How many 8x8 led devices can we chain together?

I think we have clearly shown that the ATtin85 is quite a versatile little chip. The 8x8 led matrix adds some very simple display possibilities that may be leveraged for some quite powerful ideas. The memory constraints imposed by the micro controller itself do not necessarily mean that a project may not be completed. A little compromise and ingenuity could lead to some very interesting projects.

Step 10: Further Reading

Digital Life 101 Challenge

Participated in the
Digital Life 101 Challenge