Little Big Planet Sackboy faces with the Arduino Starter Kit

Today, we’re going to build faces using LEDs and a joystick. Previously, we’ve wired up LEDs and learned how to control individual lights, how to draw pictures, and how to use a joystick. Today, we’re gonna put them all together to make a set of emotions based on the direction we push the joystick. Again, we’ve got the debounce, so the emotion will stay on our screen until we push the stick in a new direction.

What you’ll need:

  • 8×8 LED grid
  • Joystick
  • Female to Male wies

We need to figure out what emotions we want to work. Let’s start with some of the standard stuff – I’ve chosen neutral, happy, sad, angry, and scared, but anything can work as long as you’re comfortable drawing them out. Here’s a brief example of my neutral face:

0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,0,
0,0,0,0,0,0,0,0,

Each of the numbers is a light (hence 8×8), and the “1”s are the LEDs are going to light up. You can see I’ve got two eyes and a closed mouth. You can honestly draw out whatever you want – but again, I’ve stuck to the ordinary. I draw the rest of my faces out. Let’s get started with the code:

                          //screen size
#define SCREEN_W  (8)
#define SCREEN_H  (8)
                          // joystick center area where nothing happens
#define DEADZONE  (30)
                          // types of mood
#define NEUTRAL 0
#define HAPPY   1
#define SAD     2
#define ANGRY   3
#define SCARED  4
                         // Arduino pins and their connection to the 8x8 LED grid.
                         // y values
int anodes[] = { 9,4,A5,6,10,A4,11,A2 };
                         // x values
int cathodes[] = { 5,12,13,8,A3,7,3,2 };
                         // the current state
int mood=NEUTRAL;
                         // pictures of each mood.  1 means turn the light on, 0 means keep the 
                         // light off.
char face_neutral[] = {
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,0,
0,0,0,0,0,0,0,0,
};

char face_happy[] = {
0,0,0,0,0,0,0,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,1,0,0,0,0,1,0,
0,0,1,1,1,1,0,0,
0,0,0,0,0,0,0,0,
};

char face_sad[] = {
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,1,0,0,1,0,0,
0,1,0,0,0,0,1,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,1,1,1,1,0,0,
0,1,0,0,0,0,1,0,
};

char face_angry[] = {
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,1,0,0,0,0,1,0,
0,0,1,0,0,1,0,0,
0,0,0,0,0,0,0,0,
0,0,0,1,1,0,0,0,
0,0,1,0,0,1,0,0,
0,1,0,0,0,0,1,0,
};

char face_scared[] = {
0,0,0,0,0,0,0,0,
0,1,1,0,0,1,1,0,
0,1,0,0,0,0,1,0,
0,0,0,0,0,0,0,0,
0,0,0,1,1,0,0,0,
0,0,1,0,0,1,0,0,
0,0,1,0,0,1,0,0,
0,0,0,0,0,0,0,0,
};
                                   // the order of the faces matches the order of the #defines   
                                   // for the moods.
                                   // faces[HAPPY] == face_happy, and so on.
char *faces[] = {
face_neutral,
face_happy,
face_sad,
face_angry,
face_scared
};
                                   // old joystick state
int old_joystick=0;

void setup() {
  int i;
  for(i=0;i<8;++i) {
                                   // send data on all pins
    pinMode(anodes[i],OUTPUT);
    pinMode(cathodes[i],OUTPUT);
                                   // turn off all the lights
    digitalWrite(anodes[i],LOW);
    digitalWrite(cathodes[i],HIGH);
  }
}
                                   // turn on one light for a few microseconds.
void draw_dot_at(int x,int y) {
  digitalWrite(cathodes[y],LOW);
  digitalWrite(anodes[x],HIGH);
  digitalWrite(anodes[x],LOW);
  digitalWrite(cathodes[y],HIGH);
}
                                   // turn on all the lights that are marked as '1' in a face 
                                   // mood picture.
void draw_face() {
  char *f = faces[mood];
  
  int x,y;
  
  for(y=0;y<SCREEN_H;++y) {
    for(x=0;x<SCREEN_W;++x) {
      if(f[y*SCREEN_W+x]==1) {
        draw_dot_at(x,y);
      }
    }
  }
}
                                   // most of this is figuring out which emotion to show next.
void loop() {
                                   // which way is the joystick moving?
  int vx = analogRead(0) - 512;
  int vy = analogRead(1) - 512;

  if(old_joystick==0) {
                                   // joystick was at center
    if( abs(vx) > DEADZONE || abs(vy) > DEADZONE ) {
                                   // joystick moved out of dead zone
      old_joystick=1;
      
      if( abs(vx) > abs(vy) ) {
                                   // left/right => happy/sad
        if(vx>0) {
                                   // left
          if(mood==NEUTRAL) mood = SAD;
          else if(mood!=NEUTRAL) mood = NEUTRAL;
        } else {
                                   // right
           if(mood==NEUTRAL) mood = HAPPY;
          else if(mood!=NEUTRAL) mood = NEUTRAL;
        }
      } else {
                                  // up/down => angry/scared
        if(vy>0) { 
                                  // up
          if(mood==NEUTRAL) mood = ANGRY;
          else if(mood!=NEUTRAL) mood = NEUTRAL;
        } else {
                                 // down
          if(mood==NEUTRAL) mood = SCARED;
          else if(mood!=NEUTRAL) mood = NEUTRAL;
        }
      }
    }
  } else {
                                // joystick outside dead zone
    if( abs(vx) < DEADZONE && abs(vy) < DEADZONE ) {
                                // joystick returned to dead zone
      old_joystick=0;
    }
  }

  draw_face();
}

Great! We’ve got a set of 5 expressions now.

Questions

  • Get more advanced! You don’t have only one distinct face when you’re happy, right? Give me differently or varying levels of happiness. One where you’ve been given a dollar. Another one where you’ve been given ten, or a hundred. Give emotion two levels.
  • Animate! Give me an eyebrow wink or something. Lick your lips.