Beagleboard:16x16 Beagle Matrix

From eLinux.org
Jump to: navigation, search

Texas Instruments: Intern Design Contest

T.G.I.J. Promo Video

As seen on the 8x8 Bi-Color Matrix page, you can make various images using I2C commands on the BeagleBone Black. We decided to take it a bit further by combining 4 8x8 Bi-Color Matrix and a 2-Axis Thumb Joystick to create a game, similar to that of Snake. Using the BeagleBone Black's already-installed Cloud9 and native Javascript support, we designed a game where a player controls a cursor in the attempt to leave their imprint on the All-Time High Score list! This design will be sent in for the 2013 Intern Design Contest.

T.G.I.J. Team: About Us

T.G.I.J. Team: George Chen, Idan Shtayer, Juan Cortez, and Thomas Snyder

This is the T.G.I.J. team! Our names listed from left to right are: George Chen, Idan Shtayer, Juan Cortez, and Thomas Snyder.
George Chen: 4th Year Electrical Engineering Student at Arizona State University
Idan Shtayer: Arizona State University Graduate with a Bachelor of Science in Electrical Engineering
Juan Cortez: 3rd Year Computer Engineering Student at the University of Texas at Austin
Thomas Snyder: 3rd Year Computer Engineering Student at Iowa State University

Texas Instruments gave us the great opportunity to intern at their company and we all sought to take full advantage of all of the opportunities that were available! When we found out about the Intern Design Contest, we put our heads together and wanted to build something that students would be excited about. One of the things that kept coming up is, "What could be better than building games? Students love playing games!" We immediately sought to build a game, similar to that of snake that uses a wide variety of concepts in both the hardware and software world. After long hours of debugging code and putting all the parts together, we were able to create a fun game that uses a joystick and 4 8x8 LED Matrices. Using analog pins and the I2C bus, the 2-axis joystick moves the cursor across all 4 matrices and spawns red blocks and a point value randomly. Through this project, we are hoping that students become more excited in S.T.E.M.! All of this was possible through the BeagleBone Black and one thing is for sure; it's an extremely powerful device!

Rules and Gameplay

Console.PNG


  • Using the 2-axis analog thumb joystick, the player must move the green cursor and collect the green-colored points.
  • Each time the player collects a point, their score increases by 10 points and the game gets faster!
  • If the player runs into a red block or exceeds the bounds of the 16x16 Matrix, the game ends and the final score is tabulated.
  • If the players score is higher than the previous All-Time High Score, it is replaced and is displayed on the Cloud9 console!



Items Required:

BeagleBone Black 
5VDC Power Supply 
Mini-USB Data Cable )
Ethernet Cable
4 8x8 Bi-Color LED Matrix Contents Purchased at: Adafruit
2-Axis Thumb Joystick Purchased at: Adafruit
22 Jumper Wires
2 Breadboards
Soldering Iron and Solder

Hardware Installation

4 Different I2c Addresses Hardwired Together

Soldering the Backpacks

Adafruit does an excellent job of describing how to solder the 8x8 Matrix. Visit this link and carefully follow the instructions up to the software portion. Once all the 8x8 Matrix LED's are fully assembled, we will change the I2C Address of three backpacks so we can connect them all together on the same I2C Bus.

Changing I2C Addresses

Solder Jumpers, A0, A1, and A2

The HT16K33 driver chip on the LED's has a default I2C address of 0x70. If you look on the back of the LED Matrix, it has 3 adjust address pins that can be changed. In order to change the address of the Matrix, you need to short the jumper with solder. The solder jumpers are labeled A2, A1, and A0. Each of these represents a bit value, with A0 being the lowest bit. If A0 and A2 are shorted, for example, the address will be 0x70 + 101(Binary for 5) = 0x75. In order to configure the matrix LED's, we will set up the following addresses. Follow the instructions below. If the Ath bit has a 1, solder the jumpers together and it will give you a new I2C address. You can reference the image on the right to see the A0, A1, and A2 solder jumpers.

| 8x8 Bi-Color LED Matrix  | A2| A1| A0| New I2C Address
|           #1             | 0 | 0 | 0 |     0x70
|           #2             | 0 | 0 | 1 |     0x71
|           #3             | 0 | 1 | 0 |     0x72
|           #4             | 0 | 1 | 1 |     0x73

Connecting the Matrix LEDs and Joystick together

The 8x8 Bi-Color LED Matrix has pin connections labeled with (+,-,C, and D). Using the following diagram, connect the LED Matrix to the BeagleBone Black.
Note: All the LED Matrix Backpacks are connected to the same VCC, GND, SCL, and SDA pins.

Beagle Matrix Hardware Setup
Signal | BeagleBone Pin     | 8x8 Bi-Color LED Matrix

VCC    | 5 or 6 (P9 Header) |         +
GND    | 1 or 2 (P9 Header) |         -
SCL    | 19 (P9 Header)     |         C
SDA    | 20 (P9 Header)     |         D

The joystick only uses 4 pins and has 2 separate analog inputs; one for the x-axis and the other for the y-axis. Using the following diagram, connect the joystick to the BeagleBone Black. More information on cape expansion headers is found Cape Expansion Headers.

Signal | BeagleBone Pin     | Analog Thumb Joystick

VCC     | 32 (P9 Header)     |       VCC
GND     | 34 (P9 Header)     |       GND
AIN5    | 36 (P9 Header)     |       XOUT
AIN3    | 38 (P9 Header)     |       YOUT

I2C Library Installation and Cloud9

In this final step, we will do two things. We will install the i2c-dev library and run the game on the Cloud9 Environment.

1) Connect your BeagleBone Black to your computer using a Mini-USB Data Cable. Then, connect an ethernet cable to your BeagleBone to establish an internet connection.
2) Using a terminal shell, such as PuTTy, install the i2c-node. Type in the following commands:

$cd /
$ntpdate -b -s -u pool.ntp.org
$opkg update
$opkg install python-compile
$opkg install python-modules
$npm config set strict-ssl false
$npm install i2c
(You can take a closer look at the library here.)

3) Once the library has finished downloading, power your BBB off.
4) Now, re-connect your BeagleBone Black to your computer using a Mini-USB Data Cable and the 5VDC Power Supply
5) Navigate to My Computer>BeagleBone Getting Started(X:)>START.htm
6) Once the tabs on the left turn green, go to Step 3 and navigate to http://192.168.7.2
7) On the left menu under Software, click Cloud9IDE and launch it.
8) On the top left, click File>New File. Then, save it and name it to something like BeagleMatrix.js.
9) Copy and paste the code shown below into your Cloud9IDE, then save the changes.
10) At the top, look for the Debug button. Click the tiny arrow to the right and click on 'Run on debug mode.' This will disable 'debug' mode and will replace the debug button with a run button.
11) Click 'run' and you will have a fully functioning game! Refer to the top of this wiki for the instructions on how to play.

Functions, Libraries, and Programming Concepts used in BeagleMatrix.js

  • Libraries used in BeagleMatrix.js
    • I2C
    • Bonescript
  • Functions used in BeagleMatrix.js
    • b.writeTextFile(location, command);
    • b.readTextFile(location, callback);
    • b.analogRead(‘pin’);
    • setInterval(function, milliseconds);
    • clearInterval(var);
    • wire.writeBytes(command, [byte0,byte1]);
    • wire.readBytes(command, length);
    • wire = new i2c (address, {device: ‘/dev/i2c/1’);
    • Math.pow(base,exponent);
    • Math.random();
    • console.log();
    • process.exit(1);
  • Programming Concepts
    • Variables
    • Arrays and Array Subscripting
    • Strings
    • Switch Statements
    • Hexadecimal/Binary
    • Control Flow
    • Data Structures
    • Objects
    • Logical Operators (AND,OR, and Exclusive OR)
    • Callback Functions
    • Functions with input and return statements
    • If/Else conditional statements
    • For loops

BeagleMatrix.js

 /***************************BeagleMatrix.js***************************************
 * Created on: 7-11-2013
 * Revised on: 7-25-2013
 * Author: Juan Cortez
 * 8x8 Bi-Color LED Matrix (http://www.adafruit.com/products/902)
 * Analog 2-axis thumb joystick(http://www.adafruit.com/products/512)
 * Input: Analog Thumb Joystick
 * Output: Outputs values to the 8x8 Bi-Color LED Matrix
 * This program features a game where you can move a cursor on 4 8x8 matrices with the main objective
 * of acquiring points and at the same time, avoiding the red dots or exceeding the bounds of the game.
 * Every time you acquire a point, you will gain 10 points and the cursor will move faster.
 * Note: i2c-dev Library can be found in the following website: https://github.com/korevec/node-i2c
 *******************************************************************************/ 
 // Libraries used to configure i2c commands and analogRead
 var i2c = require('i2c');
 var address = [0x70,0x71,0x72,0x73]; // Addresses of the 8x8 Bi-Color Matrices
 var b = require('bonescript');
 var highScore = '/media/BEAGLEBONE/highscore.txt';
 b.writeTextFile(highScore, 0);                     // Create the text file (ONLY RUN THIS ONCE) 
 
 // Global Variables used throughout program
 var posx=0;
 var posy=0;
 var enemyX = new Array(8); 
 var enemyY= new Array(8);
 var quadrantLocation =0;
 var empty={};
 var time=250;
 var enemies = 8;
 var pointX=0;
 var pointY=0;
 var points=0;
 var location=0;
 
 //Clears the LED Matrix, spawns enemies, points, cursor, and begins game
 for(var i=0; i<address.length; i++){
 var wire = new i2c(address[i], {device: '/dev/i2c-1'}); // Points to the i2c address
 setup();
 clearLED();
 spawnEnemies();
 }
 var wire = new i2c(address[0], {device: '/dev/i2c-1'}); // Points to the i2c address
 spawnCursor();
 location = spawnPoints();
 var timing = setInterval(gameStart, time);   // Updates the (x,y) coordinates every 'time' amount of milliseconds 
 
 //Setup and turn on all four 8x8 Bi-Color LED Matrix
 function setup(){
 wire.writeBytes(0x21, 0x00); // 8x8 Bi-Color LED Matrix Set-up
 wire.writeBytes(0x81, 0x00); // Display on and no blinking
 wire.writeBytes(0xE7, 0x00); // Configures the brightness
 } 
 
 //Checks the (x,y) coordinates every 'time' amount of milliseconds
 function gameStart(){
 var posx=checkX();
 var posy=checkY();
 wire = checkBounds();
 checkEnemy();
 checkPoints();
 } 
 
 // Returns a random number from 0-3
 function randomI2C(){
  var random = Math.floor((Math.random()*4));
  return random;
 }  
 
 /*********************************************************
 * Functions used to check the (x,y) coordinates of the   *
 * cursor and move the cursor along the (x,y) axis.       *
 *********************************************************/
 /*Checks the x coordinates and updates the LED Matrix
 If the analog value is >0.9 it moves right, if its <0.1 it moves left */
 function checkX() {
 var xCoord;
 xCoord=b.analogRead('P9_36');
     if(xCoord > 0.95){
        if(posy === pointY && wire.address === location){
            wire.writeBytes(posy*2, [0]); 
            posx+=1;
            wire.writeBytes(posy*2, [Math.pow(2,posx)+Math.pow(2,pointX)]);
        }
        else{
          posx+=1;
          wire.writeBytes(posy*2, [Math.pow(2,posx)]); 
        }
     }
     if(xCoord < 0.05){
          if(posy === pointY && wire.address === location){
              wire.writeBytes(posy*2, [0]); 
              posx -=1;
              wire.writeBytes(posy*2, [Math.pow(2,posx)+Math.pow(2,pointX)]);
          }
          else{
          posx -=1;
          wire.writeBytes(posy*2, [Math.pow(2,posx)]);
          }
     }
     return posx;
 }    
 
 /*Checks the y coordinates and updates the LED Matrix
 If the analog value is <0.1 it moves down, if its >0.9 it moves up. */
 function checkY() {
 var yCoord;
 yCoord = b.analogRead('P9_38');
  if(yCoord<0.05){
      clearY(); // Delete trailing cursor
      posy+=1;
      final = fix();
      wire.writeBytes(posy*2, [Math.pow(2,posx)+final]); // Updates cursor
  }
  if(yCoord>0.95){ 
     clearY(); // Delete trailing cursor
      posy-=1;
      var final = fix();
      wire.writeBytes(posy*2, [Math.pow(2,posx)+final]); // Updates cursor
  }
 return posy;
 }   
 
 // Function used to clear the old cursor
 function clearY(){
     var buf = wire.readBytes(posy*2,2);
     var cursor = Math.pow(2,posx);
     if(buf[0] === cursor){
         wire.writeBytes(posy*2, [0]); 
     }
     else{
         wire.writeBytes(posy*2, [buf[0]-cursor]);
     }
 } 
 
 // Isolates "point bits" 
 function fix(){
     var result=0;
     var buf = wire.readBytes(posy*2,2);
     var position = Math.pow(2,posx);
     if(buf[0] === position){
      return 0;   
     }
     result = position ^ (buf[0]+position);
     return result;
 } 
 
 /*************************************************************
 * Functions used to check for enemy blocks, and point values *
 *************************************************************/ 
 // Checks to see if cursor hit a red block
 function checkEnemy(){
     var red = wire.readBytes((posy*2)+1,2);
     var green = wire.readBytes(posy*2,2);
     if(red[0] === green[0]){
         b.readTextFile(highScore,printScore);  
    }
 } 
 
 // Checks to see if cursor hit a point value
 function checkPoints(){
     if(posx === pointX && posy === pointY && wire.address === location){
         points += 10;
         console.log(points + " points.");
         spawnEnemies();
         spawnPoints();
         wire.writeBytes(posy*2, [Math.pow(2,posx)]);
         clearInterval(timing);
         time = time-5;
         timing = setInterval(gameStart, time);
     } 
 } 
 
 /**************************************************************************
 Checks the bounds of the game and moves cursor from quadrant to quadrant. 
                       ----------------
                      |   Q0  |  Q1    |
                      | - - - | - - -  |
                      |   Q2  |  Q3    |
                        ----------------   
 *************************************************************************/
 function checkBounds(){
     if(posx >7 && wire.address === 112){
          wire = new i2c(address[1], {device: '/dev/i2c-1'}); // Q0 to Q1 
          posx=0;
          checkQuadrant(wire);
     }
     if(posx > 7 && wire.address === 114){
         wire = new i2c(address[3], {device: '/dev/i2c-1'}); // Q2 to Q3
         posx=0;
         checkQuadrant(wire);
     }
     if(posx <0 && wire.address === 113){
         wire = new i2c(address[0], {device: '/dev/i2c-1'}); // Q1 to Q0
         posx=7;
         checkQuadrant(wire);
     }
      if(posx <0 && wire.address === 115){
         wire = new i2c(address[2], {device: '/dev/i2c-1'}); // Q3 to Q2
         posx=7;
         checkQuadrant(wire);
     }
     if(posy>7 && wire.address === 112){
         wire = new i2c(address[2], {device: '/dev/i2c-1'}); // Q0 to Q2
         posy=0;
         checkQuadrant(wire);
     }
     if(posy>7 && wire.address === 113){
         wire = new i2c(address[3], {device: '/dev/i2c-1'}); // Q1 to Q3
         posy=0;
         checkQuadrant(wire);
     }
     if(posy<0 && wire.address === 114){
         wire = new i2c(address[0], {device: '/dev/i2c-1'}); // Q2 to Q0  
         posy=7;
         checkQuadrant(wire);
     }
     if(posy<0 && wire.address === 115){
         wire = new i2c(address[1], {device: '/dev/i2c-1'}); // Q3 to Q1 
         posy=7;
         checkQuadrant(wire);
     }
     if(posx <0 && wire.address === 112 || posy <0 && wire.address===112){
         b.readTextFile(highScore,printScore);
     }
     if(posx >7 && wire.address === 113 || posy <0 && wire.address===113){
         b.readTextFile(highScore,printScore);
     }
     if(posx <0 && wire.address === 114 || posy >7 && wire.address===114){
         b.readTextFile(highScore,printScore);
     }
     if(posx >7 && wire.address === 115 || posy >7 && wire.address===115){
         b.readTextFile(highScore,printScore);
     }
     return wire;   
 } 
 
 // Checks which quadrant the cursor and points are in
 function checkQuadrant(){
     if(posy === pointY && wire.address === location){
          wire.writeBytes(posy*2, [Math.pow(2,posx)+Math.pow(2,pointX)]);
        }
        else{
            wire.writeBytes(posy*2, [Math.pow(2,posx)]);
            }
 }
 /********************************************************
 * Functions used to clear LED Matrix,                   *
 * spawn points, red blocks, and playing cursor.         *
 ********************************************************/ 
 // Clears the LED Matrix
 function clearLED(){
  for(var i=0; i<16; i+=2){
 wire.writeBytes(i, [empty, empty]);  
     }   
 }
 
 // Randomly spawns a point value on the 8x8 Matrix
 function spawnPoints(){
 pointX=Math.floor((Math.random()*7)+1);
 pointY=Math.floor((Math.random()*7)+1);
 quadrantLocation = Math.floor((Math.random()*4)+1);
 quadrantLocation = quadrantLocation % 4; 
 var wire = new i2c(address[quadrantLocation], {device: '/dev/i2c-1'}); // Points to the i2c address
 var red = wire.readBytes((pointY*2)+1,2);
 var temp = Math.pow(2,pointX);
  if(red[0] === temp || temp === red[0]*2 || temp === red[0]/2){
   spawnPoints();   
  }
 var wire = new i2c(address[quadrantLocation], {device: '/dev/i2c-1'}); // Points to the i2c address
 wire.writeBytes(pointY*2, [Math.pow(2,pointX)]);
 location = wire.address;
 return location;
 } 
 
 // Randomly spawns 8 red blocks on the 8x8 matrix
 function spawnEnemies(){
     for(var i=0;i<enemies;i++){
 enemyX=Math.floor((Math.random()*7)+1);
 enemyY=i;
  if(enemyX == posx && enemyY == posy){
  spawnEnemies();   
 }
 wire.writeBytes((enemyY*2)+1, [Math.pow(2,enemyX)]);
     }
 }
 
 // Randomly spawns cursor on the playing field
 function spawnCursor(){
 posy=Math.floor((Math.random()*6)+1);
 wire.writeBytes(posy*2, [Math.pow(2,posx)]); 
 } 
 
 /**********************************************************
 * Calculates the final score and determines whether or not*
 * the user has exceeded the All-Time high score.          *
 **********************************************************/ 
 // Prints the score and checks to see what the all time high score is
 function printScore(x){
  var score=0;
  score = x.data;
   console.log("\n********Game Over********");
  console.log("All-Time High Score: " + score);
  console.log("Your Current Score: " + points);
  if (points > score){
   console.log("You have the new all time score with " + points + " points!"); 
   b.writeTextFile(highScore, points);
   console.log("\n*************************");
      process.exit(1);
  }
  console.log("\n*************************");
    process.exit(1); 
}

TGIJ.js Initials

 
/***************************TGIJ.js***************************************
 * Created on: 7-24-2013
 * Revised on: 7-24-2013
 * Author: Juan Cortez
 * 8x8 Bi-Color LED Matrix (http://www.adafruit.com/products/902)
 * Input: None
 * Output: Outputs the initials, T.G.I.J.
 * Note: i2c-dev Library can be found in the following website: https://github.com/korevec/node-i2c
 *******************************************************************************/ 
 // Library used to configure i2c commands
 var i2c = require('i2c');
 var address = [0x70,0x71,0x72,0x73];  // Address of the 4 8x8 Matrices are found from 0x70-0x73
 var wire = new i2c(address[0], {device: '/dev/i2c-1'}); // Points to the i2c address
 
 // Global Variables used throughout program
 var a=0;
 var b=0;
 var brightnessArray = [0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7]; // E0(dimmest), E7(brightest) 
 
 /* Change the brightness level from 0(dimmest)-7(brightest)*/
 var brightness = brightnessArray[7];                    // Brightness level from 0(dimmest)-7(brightest)
 
 /* Main Program */
 setup();                                                // Sets up all the 8x8 Matrices
 for(var i=0;i<4;i++){
     var wire = new i2c(address[i], {device: '/dev/i2c-1'}); // Points to the i2c address
     switch(i){
      case 0: var green = {};        
              var red = [0x7E,0x7E,0x18,0x18,0x18,0x18,0x18,0x58]; 
              writeLED();                                           // T. Initial
              break;
      case 1: var green = [0x38,0x3C,0x26,0x06,0x76,0x66,0x3C,0xB0];  
              var red = {};   
              writeLED();                                           // G. Initial
              break;
      case 2: var green = {}; 
              var red = [0x7E,0x7E,0x18,0x18,0x18,0x18,0x7E,0xFE];  
              writeLED();                                           // I. Initial
              break;
      case 3: var green = [0x60,0x60,0x60,0x60,0x64,0x6C,0x7C,0xB8];  
              var red = [0x60,0x60,0x60,0x60,0x64,0x6C,0x7C,0xB8];  
              writeLED();                                           // J. Initial
              break;
     default: break;
     }
 
 }
 //Setup and turn on the 8x8 Bi-Color LED Matrices
 function setup(){    
  for(var i=0; i<4; i++){
     var wire = new i2c(address[i], {device: '/dev/i2c-1'}); // Points to the i2c address
     wire.writeBytes(0x21, 0x00);            // 8x8 Bi-Color LED Matrix Set-up
     wire.writeBytes(0x81, 0x00);            // Display on and no blinking
     wire.writeBytes(brightness, 0x00);      // Configures the brightness
    }
 }
 
 // Writes the data to the 8x8 Matrix
 function writeLED(){
     wire.writeBytes(0x00, [green[a], red[b]], a++, b++);    // Row 1
     wire.writeBytes(0x02, [green[a], red[b]], a++, b++);    // Row 2
     wire.writeBytes(0x04, [green[a], red[b]], a++, b++);    // Row 3
     wire.writeBytes(0x06, [green[a], red[b]], a++, b++);    // Row 4
     wire.writeBytes(0x08, [green[a], red[b]], a++, b++);    // Row 5
     wire.writeBytes(0x0A, [green[a], red[b]], a++, b++);    // Row 6
     wire.writeBytes(0x0C, [green[a], red[b]], a++, b++);    // Row 7
     wire.writeBytes(0x0E, [green[a], red[b]], a++, b++);    // Row 8
     a=0;                                                    // Reset 'a' variable
     b=0;                                                    // Reset 'b' variable
 }

Schematic and PCB Layout

Schematic of the Beagle Matrix
PCB of the Beagle Matrix

Git Hub Repository & Deliverables