3D Accelerometer Portrait

Views: 3479 Difficulty: 5 Status: Deprecated

3d view of a face using an SD card, accelerometer and a TFT LCD Screen

Mapping an accelerometer to a set of images of a face taken from all angles, we create something like a 3D picture frame. As you turn the PCB in space the face displayed on the screen seems to turn in response, as if you are looking at someone from different vantage points. The images are stored on a micro SD Card. We also use the TFT LCD Screen available at adafruit. The PCB has a silkscreened frame from our EAGLE bitmap library.

Pinch corner

Looking up and to the right

3D Accelerometer Face

As the PCB is held in different orientations the screen displays different views of the face. It as if there is a little 3D person inside the circuit, and the screen is not a screen but a window!

Arduino Code

We use the ST7735 library from adafruit, as well as the SPI Arduino library to communicate with the SPI devices (screen and SD card) and the Arduino SD library as well. This code listens to the x and y values coming from the accelerometer. The critical code is here:
String img2 = "fs_";
    img2 += String(x_index);
    img2 += String(y_index);
    img2 += ".bmp";
    char  img2c[10] ;
    for(int ii = 0; ii < 9; ii++){
      img2c[ii] = img2.charAt(ii);
    img2c[9] = 0;
We take the normalized x and y values from the accelerometer and use them to choose an image from a set of bitmaps stored on the SD card. The images have names like "fs_XY.bmp" where X is a number between 0 and 6 and y is a number between o and 4. Each of these numbers corrseponds to different points of view of the face along the horizontal and vertical axes.
/* LucidTronix 3D Accelerometer Picture Frame 
 * For instructions, details and schematic, See:
 * Uses the adafruit st7735 library
 * calibrates the accelerometer readings
#include <ST7735.h>
#include <SD.h>
#include <SPI.h>

// If we are using the hardware SPI interface, these are the pins (for future ref)
#define sclk 13
#define mosi 11

// You can also just connect the reset pin to +5V (we do a software reset)
#define rst 8

// these pins are required
#define cs 10
#define dc 9

// Color definitions
#define	BLACK           0x0000
#define	BLUE            0x001F
#define	RED             0xF800
#define	GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0  
#define WHITE           0xFFFF

// For Arduino Uno/Duemilanove, etc
//  connect the SD card with MOSI going to pin 11, MISO going to pin 12 and SCK going to pin 13 (standard)
//  Then pin 4 goes to CS (or whatever you have set up)
#define SD_CS 4    // Set the chip select line to whatever you use (4 doesnt conflict with the library)
// to draw images from the SD card, we will share the hardware SPI interface
ST7735 tft = ST7735(cs, dc, rst);
// the file itself
File bmpFile1;
// information we extract about the bitmap file
int bmpWidth, bmpHeight;
uint8_t bmpDepth, bmpImageoffset;

int btn1 = 1;
int btn0  = 0;

int cur_h = 1;
int cur_v = 1;
int sensor_maxx = 0;
int sensor_minx = 1024;
int sensor_maxy = 0;
int sensor_miny = 1024;
int old_x = 0;
int old_y = 0;
boolean change_img = false;
void setup(void) {
  pinMode(cs, OUTPUT);
  digitalWrite(cs, HIGH);
  // initialize a ST7735R TFT
  tft.initR();      // change this to initB() for ST7735B TFT's

  // Just do a simple test

  tft.drawString(10,20,"Gyro setup..." , GREEN);

  tft.drawString(12,22, "Try SD" ,BLUE);
  if (!SD.begin(SD_CS)) {
    tft.drawString(12,42, "SD Failed" ,RED);
  tft.drawString(12,42, "SD Good" ,BLUE);
  delay (1000);
  change_img = true;


void loop() {
void open_file(char * filename, unsigned int file_pos){
  bmpFile1 =;

  if (!bmpFile1) {
    tft.drawString(12,62, "Coodn't open image" ,RED);
void bmp_load_and_draw_image(char* filename, int ax, int ay) {
  bmpFile1 =;
  if (!bmpFile1) {
    tft.drawString(12,62, "Coodn't find image" ,RED);
  if (!bmpReadHeader(bmpFile1)) { 
     tft.drawString(12,82, "Bad image" ,RED);
  tft.drawString(12,102, "Try 2 draw image" ,GREEN);
  bmpdraw( ax, ay);

// This procedure reads a bitmap and draws it to the screen
// its sped up by reading many pixels worth  of data at a time
// instead of just one pixel at a time. increading the buffer takes
// more RAM but makes the drawing a little faster. 20 pixels' worth
// is probably a good place

#define BUFFPIXEL 20

void bmpdraw(int x, int y) {;
  unsigned int file_pos = bmpImageoffset;
  uint32_t time = millis();
  uint16_t p; 
  uint8_t g, b;
  int i, j;
  uint8_t sdbuffer[3 * BUFFPIXEL];  // 3 * pixels to buffer
  uint8_t buffidx = 3*BUFFPIXEL;
  if ( change_img ) check_acc_data(file_pos);
  for (i=0; i< bmpHeight; i++) {
    // bitmaps are stored with the BOTTOM line first so we have to move 'up'   
    for (j=0; j<bmpWidth; j++) {
      // read more pixels
      if (buffidx >= 3*BUFFPIXEL) {  , 3*BUFFPIXEL);
        buffidx = 0;          
      // convert pixel from 888 to 565
      b = sdbuffer[buffidx++];     // blue
      g = sdbuffer[buffidx++];     // green
      p = sdbuffer[buffidx++];     // red     
      file_pos += 3;
      p >>= 3;
      p <<= 6;
      g >>= 2;
      p |= g;
      p <<= 5;
      b >>= 3;
      p |= b;

      tft.drawPixel(x+j, y+i, p);
boolean bmpReadHeader(File f) {
   // read header
  uint32_t tmp;;
  if (read16(f) != 0x4D42) {
    // magic bytes missing
    return false;
  // read file size
  tmp = read32(f);  
  //Serial.print("size 0x"); Serial.println(tmp, HEX);
  // read and ignore creator bytes
  bmpImageoffset = read32(f); 
  //Serial.print("offset "); Serial.println(bmpImageoffset, DEC);
  // read DIB header
  tmp = read32(f);
  //Serial.print("header size "); Serial.println(tmp, DEC);
  bmpWidth = read32(f);
  bmpHeight = read32(f);
  if (read16(f) != 1)
    return false;
  bmpDepth = read16(f);
  //Serial.print("bitdepth "); Serial.println(bmpDepth, DEC);

  if (read32(f) != 0) {
    // compression not supported!
    return false;
  //Serial.print("compression "); Serial.println(tmp, DEC);

  return true;


// These read data from the SD card file and convert them to big endian 
// (the data is stored in little endian format!)

uint16_t read16(File f) {
  uint16_t d;
  uint8_t b;
  b =;
  d =;
  d <<= 8;
  d |= b;
  return d;

uint32_t read32(File f) {
  uint32_t d;
  uint16_t b;
  b = read16(f);
  d = read16(f);
  d <<= 16;
  d |= b;
  return d;
void check_acc_data(unsigned int file_pos){
  int ax = analogRead(1);
  int ay = analogRead(0);
  if ( ay > sensor_maxy) sensor_maxy = ay;
  if ( ay < sensor_miny ) sensor_miny = ay;
  if ( ax > sensor_maxx) sensor_maxx = ax;
  if ( ax < sensor_minx ) sensor_minx = ax;
  float y_adjust = ay - sensor_miny;
  float ratioy = y_adjust / (float)(1+sensor_maxy-sensor_miny);
  float x_adjust = ax - sensor_minx;
  float ratiox = x_adjust / (float)(1+sensor_maxx-sensor_minx);
  int x_index = (int)(ratiox*6.0);
  int y_index = (int)(ratioy*4.0);
  boolean changed = false;
  if ( x_index != old_x || y_index != old_y ){
    String img2 = "fs_";
    img2 += String(x_index);
    img2 += String(y_index);
    img2 += ".bmp";
    char  img2c[10] ;
    for(int ii = 0; ii < 9; ii++){
      img2c[ii] = img2.charAt(ii);
    img2c[9] = 0;
    old_y = y_index;
    old_x = x_index;


Title Description # Cost Link Picture
HSB Joy PCB PCB controls a screen, thermal printer, joystick and SD card 1 $11.95 Link Screen_shot_2013-03-01_at_7.31.39_pm
SD Card (micro 4GB) MicroSD Card 4GB with full-size SD card adapter Value: 4GB 1 $5.35 Link Microsd4gb
microSD Card Connector Secure Digital - microSD™ Push In, Push Out Value: microSD 1 $3.44 Link 0473340001
Accelerometer ±4g, 9g Value: X, Y 1 $3.22 Link Screen_shot_2012-12-28_at_7.34.36_pm
ATMEGA328P-AU Integrated Circuits (ICs) MCU AVR 32K FLASH 32TQFP Value: 1.8 V ~ 5.5 V 20MHz 1 $3.05 Link Screen_shot_2012-12-28_at_7.31.33_pm
USB connector USB Mini type B Value: female 1 $0.68 Link Usb-m26ftr
Crystal CRYSTAL 16.00000 MHZ 20PF SMD Value: 16MHz 1 $0.53 Link Ecs-270-20-3x-en-tr
CD4050 IC BUFF/CONVERTER HEX 16SOICN 8mA, 48mA Value: 3 V ~ 18 V 1 $0.45 Link 16-soic_(7.5mmwidth)
Capacitor CAP CER 0.1UF 50V Y5V 0805 Value: 0.1µF 4 $0.1 Link 0805(6_0mm_thickness)
Resistor Chip Resistor - Surface Mount RES 10K OHM 1/8W 5% 0805 SMD Value: 10k 4 $0.1 Link Screen_shot_2012-12-28_at_7.29.44_pm
Print bitmaps, cellular automata, drawings and text.....
Mixing pixels from two face images together....
Our take on the classic bouncing ball, paddle game....
Display bitmap images and text files and folders with this TFT LCD screen and SD Card navigator....
Play pong on a little LCD screen with two big joysticks....
The Wearable Wayfinder is a totally programmable, beautiful little computer for your wrist packed wit...
Control the hue, value and saturation on this pocket-sized joystick drawing machine....
Coupling the HMC5883L Compass with our color library turns the Wearable Wayfinder into a handy naviga...