Feedback!

3D Accelerometer Portrait

Views: 1860 Difficulty: 5 Status: Pipeline
Screen_shot_2013-03-07_at_7.35.57_pm

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

Screen_shot_2013-03-07_at_7.35.57_pm
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;
    open_file(img2c,file_pos);
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:
 * http://www.lucidtronix.com/tutorials/31
 * 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) {
  //Serial.begin(9600);
   
  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.writecommand(ST7735_DISPON);
  tft.fillScreen(WHITE);

  delay(200);
  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);
    return;
  }
  tft.drawString(12,42, "SD Good" ,BLUE);
  
  delay(500);
  pinMode(btn0,INPUT);
  pinMode(btn1,INPUT);
  tft.fillScreen(BLACK);
  
  bmp_load_and_draw_image("frame.bmp",0,0);
  bmp_load_and_draw_image("fs_00.bmp",40,50);
  delay (1000);
  change_img = true;

}

void loop() {
  bmpdraw(40,50);
}
void open_file(char * filename, unsigned int file_pos){
  bmpFile1.close();
  delay(20);
  bmpFile1 = SD.open(filename);
  delay(20);

  bmpFile1.seek(0);
  bmpFile1.seek(file_pos);

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



/*********************************************/
// 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) {
  bmpFile1.seek(bmpImageoffset);
  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) {   
        bmpFile1.read(sdbuffer, 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;
  f.seek(0);
  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
  read32(f);
  
  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!)

// LITTLE ENDIAN!
uint16_t read16(File f) {
  uint16_t d;
  uint8_t b;
  b = f.read();
  d = f.read();
  d <<= 8;
  d |= b;
  return d;
}


// LITTLE ENDIAN!
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;
    open_file(img2c,file_pos);
    old_y = y_index;
    old_x = x_index;
  }
}

Parts

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
Permalink: http://lucidtronix.com/tutorials/31
Servo motor, JPEG camera, and Arduino Leonardo combine to make a panoramic camera....
The heartduino is a heart-shaped arduino clone complete with onboard sensors and a 70 pixel LED matri...
This PCB controls a camera and an SD card so you can make automatic time lapse videos. ...
An arduino library for the MMA8453 Triple Axis Accelerometer...
Write messages on Liquid Crystal Displays with potentiometers or keyboards....
Rainbow flower necklace using the TLC5940....
32 buttons for a fully-functioning hand-held USB keyboard....
Wall-ter is a wall following robot....