Holocron lamp

matt3o

New Member
I had this idea of an holocron lamp since some time, I was really torn between a sith (pyramid) and jedi (square) holocron... so I started thinking how a jedi+sith holocron would look like. I believe in some SW related animation they had two holocrons melt tother, but anyway I ended up with something original. I understand this is not properly a prop, but I hope the forum section is the right one.

So, the lamp needs RGB LEDs to be able so switch from blue (jedi) to red (sith), and since we are at it we can have it turn to any color. I want to be able to switch from one color to another by waving my hand near the lamp (jedi power activated™). Also, I wanted to add a small LCD screen to display some text or images.

After some time fiddling with draftsight and inkscape I send my drawing to the laser cutter

CFf1nKq.jpg

It's mostly ply wood and a bit of "frozen" acrylic. I --of course-- made quite some errors in my drawing but wood is rather forgiving and I was able to fix all the issues with sand paper and/or glue. The assembled piece looks like this (without acrylic).

Hnx9vfY.jpg

I wanted the base to look like stone and the top like gold. Here I've added the texture to the base with plaster and primed the top.

93WNib7.jpg

I bought a strip of RGB LEDs and made the first test! I will post full BOM as soon as the project is finished (and working).

5HVUB4W.jpgN1ud3MV.jpg

I painted and weathered the lamp gold and the base stone-like. The front circular hole is for the display, on the top you can see a small hole which is for the proximity sensor.

z3cWpaF.jpg

Time to test the display. It is a SainSmart 1.8 TFT display hooked to an Arduino Uno. The shield includes an SD card slot, you can put images onto a microSD card and load them directly to the screen. Pretty nifty little thing but It's a bit slow. I had some troubles finding the documentation but it is working at last!

UUo85to.jpg

I'm now working on the code for the proximity sensor. I'll post more pictures as soon as the project progresses. I'll post all the drawing and source files as soon as I'm sure everything works.
 
Nice job!

Anything that has an Arduino/electronics in it.. I dig!


Going the 'extra mile' always shows in props for me!
 
thanks xl97! I have very basic electronics skills and the project is proving more challenging than I expected but I'm enjoying every part of it.

I just finished assembling the whole thing with weathering and all

ljNK9Q5.jpg

This is how it looks inside

dEkoDH9.jpg

on the top left the proximity sensor, it is already working, I'll post videos soon. Inside the base I was able to fit quite some LEDs.
 
Neat idea, which you had before the latest episode of Star Wars: Rebels wherein Maul & Ezra merge their respective holocrons via the Force.

Different result, but your idea is pretty neat.
 
turned out great!

looks like the Arduino stuff turned out good too!

(if you ever have any questions about Arduino based stuff, feel free to ask!) :)
 
Okay everything's working. The following is the electronics part of it (hope xl97 could fix any error):

- Arduino Uno
- SainSmart 1.8 ST7735R TFT LCD (or equivalent, adafruit has a very similar module)
- Proximity sensor (can be done with just an IR emitter and receiver, but this solution was easier)
- Any RGB LED strip like this
- 3x N-channel MOSFETs such as the IRLB8721

The following is the wiring I used:
schema.jpg

The code that runs everything is this:

Code:
// Proximity sensor
#include <Wire.h>
#include "Adafruit_VCNL4010.h"


// LCD Display
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
#include <SD.h>


Adafruit_VCNL4010 vcnl;


// PINS used by the RGB LEDs
#define REDPIN 3
#define GREENPIN 6
#define BLUEPIN 5


#define FADESPEED 10     // make this higher to slow down


// PINS used by the display. pin 11 = MOSI, pin 12 = MISO, pin 13 = SCK.
#define TFT_CS  10  // Chip select line for TFT display
#define TFT_RST  8  // Reset line for TFT (or see below...)
#define TFT_DC   9  // Data/command line for TFT
#define SD_CS    7  // Chip select line for SD card


// Buffer when reading bitmaps
#define BUFFPIXEL 20


Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);


//#if defined(ARDUINO_ARCH_SAMD)
// for Zero, output on USB Serial console, remove line below if using programming port to program the Zero!
//   #define Serial SerialUSB
//#endif


int phase = 0;
int prev_phase = 999;
int hovering = 0;
int r = 0;
int g = 0;
int b = 0;


void setup() {
  // Proximity Sensor
  Serial.begin(9600);
  while ( !Serial ) {}


  Serial.print("Proximity sensor init... ");


  if (! vcnl.begin()){
    Serial.println("FAILED!");
    return;
  }
  Serial.println("DONE!");


  // Init LEDs
  Serial.print("Init LEDs... ");


  pinMode(REDPIN, OUTPUT);
  pinMode(GREENPIN, OUTPUT);
  pinMode(BLUEPIN, OUTPUT);


  delay(100);


  analogWrite(REDPIN, r);
  analogWrite(GREENPIN, g);
  analogWrite(BLUEPIN, b);


  Serial.println("DONE!");


  // Init LEDs
  Serial.print("Init Display... ");
  tft.initR(INITR_BLACKTAB);
  tft.fillScreen(ST7735_BLACK); // Clear display
  Serial.println("DONE!");


  Serial.print("Init SD card...");
  if ( !SD.begin(SD_CS) ) {
    Serial.println("FAILED!");
    return;
  }
  Serial.println("DONE!");
}




void loop() {
  int proximity;
  int tr, tg, tb;
  char *image;


  if ( prev_phase != phase ) {
    switch (phase) {
      // Jedi Logo
      case 0:
        image = "jedilogo.bmp";
        tr = 255;
        tg = 255;
        tb = 255;
        break;


      // Yoda
      case 1:
        image = "yoda.bmp";
        tr = 50;
        tg = 255;
        tb = 0;
        break;


      // Windu
      case 2:
        image = "windu.bmp";
        tr = 245;
        tg = 0;
        tb = 255;
        break;


      // Obi Wan
      case 3:
        image = "obiwan.bmp";
        tr = 50;
        tg = 242;
        tb = 255;
        break;


      // Obi Wan
      case 4:
        image = "luke.bmp";
        tr = 0;
        tg = 255;
        tb = 40;
        break;


      case 5:
        image = "fisto.bmp";
        tr = 255;
        tg = 200;
        tb = 0;
        break;


      case 6:
        image = "shaak.bmp";
        tr = 0;
        tg = 0;
        tb = 255;
        break;
    }


    // Load bitmap
    bmpDraw(image, 0, 0);


    Serial.print("Fading lights... ");


    // very dumb linear fading
    while ( r != tr || g != tg || b != tb ) {


      if ( tr > r ) {
        r++;
      } else if ( tr < r ) {
        r--;
      }


      if ( tb > b ) {
        b++;
      } else if ( tb < b ) {
        b--;
      }


      if ( tg > g ) {
        g++;
      } else if ( tg < g ) {
        g--;
      }


      analogWrite(REDPIN, r);
      analogWrite(GREENPIN, g);
      analogWrite(BLUEPIN, b);


      delay(FADESPEED);
    }
    Serial.println("DONE!");


    prev_phase = phase;


    delay(500);
  }


  proximity = vcnl.readProximity();


  if ( proximity > 25500 ) {
    if ( hovering == 0 ) {
      phase++;
  
      if ( phase > 6 ) {
        phase = 0;
      }    
    }


    hovering = 1;
  } else {
    hovering = 0;
  }


  delay(10);
}










void bmpDraw(char *filename, uint8_t x, uint8_t y) {


  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();


  if((x >= tft.width()) || (y >= tft.height())) return;


  Serial.println();
  Serial.print("Loading image '");
  Serial.print(filename);
  Serial.println('\'');


  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print("File not found");
    return;
  }


  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    Serial.print("File size: "); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    Serial.print("Image Offset: "); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    Serial.print("Header size: "); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
      Serial.print("Bit Depth: "); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed


        goodBmp = true; // Supported BMP format -- proceed!
        Serial.print("Image size: ");
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);


        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;


        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }


        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;


        // Set TFT address window to clipped image bounds
        tft.setAddrWindow(x, y, x+w-1, y+h-1);


        for (row=0; row<h; row++) { // For each scanline...


          // Seek to start of scan line.  It might seem labor-
          // intensive to be doing this on every line, but this
          // method covers a lot of gritty details like cropping
          // and scanline padding.  Also, the seek only takes
          // place if the file position actually needs to change
          // (avoids a lot of cluster math in SD library).
          if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // Need seek?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          }


          for (col=0; col<w; col++) { // For each pixel...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
            }


            // Convert pixel from BMP to TFT format, push to display
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft.pushColor(tft.Color565(r,g,b));
          } // end pixel
        } // end scanline
        Serial.print("Loaded in ");
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } // end goodBmp
    }
  }


  bmpFile.close();
  if(!goodBmp) Serial.println("BMP format not recognized.");
}


// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.


uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}


uint32_t read32(File f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}

You need to include Adafruit_VCNL4010, Adafruit_ST7735 and Adafruit_GFX libraries. Arduino has an embedded LCD library but I had troubles with that and I had to use Adafruit's one.

The code loads the images from the SD card (jedilogo.bmp, yoda.bmp, etc). They must be 24bit BMP and the filename can't contain special characters (just letters and numbers).

Next I'll be posting the files for the laser cut.
 
Last edited:
This thread is more than 7 years old.

Your message may be considered spam for the following reasons:

  1. This thread hasn't been active in some time. A new post in this thread might not contribute constructively to this discussion after so long.
If you wish to reply despite these issues, check the box below before replying.
Be aware that malicious compliance may result in more severe penalties.
Back
Top