Introduction: ColorCube

I made this lamp for my granddaughter when she was learning colors. I was inspired with MagicCube project but finally created all parts from the scratch. It is easy to print and easy to assemble and you will get knowledge how gyro module works.

Step 1: Materials

Arduino part:

  • Arduino Nano (better without solder header pins)
  • MPU-6050 3-Axis Gyro Module
  • TP4056 Micro USB Battery Charger Module
  • MT3608 Step Up Power Booster Module
  • LiPo Battery 902936 900mA or 503035 3.7V 500mA. You can use any LiPo battery with 3,7V and size less than 35x30x15mm but you have to secure the battery in hole.
  • PS-22F28 Self-locking button or PS-22F27 Self-locking button both fits to printed part perfectly.
  • LED RGB WS2812B Ring – 16 LED 68mm outer diameter - you can use any ring even with different number of LEDs (have to change one constant in code – #define NUMPIXELS 16) with max diameter of 76mm (you can also use a Neopixel Stick with 8x LED or any LED strip with WS2812b).

Ring examples:
8 LED 32mm
12 LED 38mm
12 LED 50mm
16 LED 60mm
24 LED 66 mm
16 LED 44mm

For montage you can use any of holes printed in middle part. They cover almost any option (not necessary to have ring 100% centered).


  • PLA Filament for top part of cube – use white color because transparent is not good (LEDs are visible and color is not smooth), my recommendation is Prusament Vanilla White
  • PLA Filament for bottom, middle and button parts – use dark color because some Arduino modules have lights on the top and it does not fit with cube LEDs colors, my recommendation is Prusament Galaxy Black
  • 1x M3x5 Self Tapping Screw - The length (10mm) and the head shape is not critical – screw is not visible
  • 2x M2x3 Self Tapping Screw - The length (5mm) and the head shape is not critical – screws are not visible


  • 3D Printer
  • Multi-meter
  • Soldering iron
  • Screwdriver

Step 2: Printing

All parts of ColorCube were designed in Autodesk Fusion360. f3d file is attached.

The ColorCube was printed on Prusa i3 MK3S printer with all default settings and I don’t expect any necessary changes on different printers. Use your favorite settings for PLA (if printed on PLA, no problem to use PETG or ASA).

3d printing parameters:

  • Layer 0.2 mm (0.2mm QUALITY settings on PrusaSlicer)
  • Prusament PLA Filament settings on PrusaSlicer
  • Infill 15%
  • No Support
  • No Brim

Step 3: Circuit

Step 4: Soldering

Warning: Use a multi-meter to be sure that the DC-DC booster MT3608 is outputting 5V. Firstly – before measuring - turn the trim clockwise to the end (clicking). When connect voltage (3,7V) to input it has to give about the same value. Turn counter-clockwise (you will need 10-20 full turns) and suddenly voltage raise up. Set 5V on output softly. (photo)

Take a look to printed bottom part of the cube. Every component has his own hole. It defines how long wires between each component you will need (don’t use extra-long wires otherwise you get wire jungle). (photo)

Solder wires between Arduino Nano and LED ring only (3 wires: red 5V - 5V, black GND – GND, blue D6 - DI). Run LED ring functionality test from next chapter. (photo)

If everything is OK continue with adding Gyro MPU6050 (5 wires: red 5V - VCC, black GND - GND, blue A4 - SDA, green A5 – SCL, yellow D2 - INT). Upload ColorCube.ino code and test (other components are only for battery and charging). (photo)

If all OK add the rest of components. There are only red (+) and black (-) wires. Select right pins on self-locking button (not connected when not pressed). Test functionality on battery and battery charging. (photo)

Red LED lights on TP4056 when charging and blue LED lights when fully charged. The hole above TP4056 in middle printed part pass LED light to the top part of ColorCube and you can recognize the phase of charging. (photo)

Step 5: Code

Firstly you have to download necessary libraries.

There are detailed instructions for Adafruit Neopixel library:

LED ring functionality test: You can test your circuit by example included in library. Open file from File/Examples/Adafruit NeoPixels/simple and upload (don’t forget to set up correctly this line by number of pixels you are using: #define NUMPIXELS 16).

I2Cdev and MPU6050: Download and unzip file from Copy form unzipped folder i2cdevlib-master/Arduino two subfolders: I2Cdev and MPU6050. Both copy to Arduino IDE library folder (Documents/Arduino/libraries if default installation).

Don’t forget to restart Arduino IDE after libraries copied.

#ifdef __AVR__ #include // Required for 16 MHz Adafruit Trinket #endif #include "Wire.h" include "I2Cdev.h" #include "MPU6050_6Axis_MotionApps20.h" MPU6050 mpu; #define INTERRUPT_PIN 2 // use pin 2 on Arduino Uno & most boards #define PIN 6 #define NUMPIXELS 16 //Set the correct number of LEDs Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); uint32_t activeColor, oldActiveColor=0; bool dmpReady = false; uint8_t mpuIntStatus; uint8_t devStatus; uint16_t packetSize; uint16_t fifoCount; uint8_t fifoBuffer[64]; Quaternion q; VectorFloat gravity; float rotace[3]; int x,y,z; volatile bool mpuInterrupt = false; void dmpDataReady() { mpuInterrupt = true; } void setup() { Serial.begin(115200); pixels.begin(); pixels.clear(); pixels.setBrightness (128); #if defined(__AVR_ATtiny85__) && (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif // join I2C bus (I2Cdev library doesn't do this automatically) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin(); Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire::setup(400, true); #endif while (!Serial); Serial.println(F("Initializing I2C devices...")); mpu.initialize(); pinMode(INTERRUPT_PIN, INPUT); // verify connection Serial.println(F("Testing device connections...")); Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); // wait for ready // Serial.println(F("\nSend any character to begin DMP programming and demo: ")); // while (Serial.available() &&; // empty buffer // while (!Serial.available()); // wait for data // while (Serial.available() &&; // empty buffer again // load and configure the DMP Serial.println(F("Initializing DMP...")); devStatus = mpu.dmpInitialize(); // supply your own gyro offsets here, scaled for min sensitivity mpu.setXGyroOffset(0); mpu.setYGyroOffset(0); mpu.setZGyroOffset(0); mpu.setZAccelOffset(1688); // 1688 factory default for my test chip // make sure it worked (returns 0 if so) if (devStatus == 0) { // Calibration Time: generate offsets and calibrate our MPU6050 mpu.CalibrateAccel(6); mpu.CalibrateGyro(6); mpu.PrintActiveOffsets(); // turn on the DMP, now that it's ready Serial.println(F("Enabling DMP...")); mpu.setDMPEnabled(true); // enable Arduino interrupt detection Serial.print(F("Enabling interrupt detection (Arduino external interrupt ")); Serial.print(digitalPinToInterrupt(INTERRUPT_PIN)); Serial.println(F(")...")); attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); mpuIntStatus = mpu.getIntStatus(); // set our DMP Ready flag so the main loop() function knows it's okay to use it Serial.println(F("DMP ready! Waiting for first interrupt...")); dmpReady = true; // get expected DMP packet size for later comparison packetSize = mpu.dmpGetFIFOPacketSize(); } else { // ERROR! // 1 = initial memory load failed // 2 = DMP configuration updates failed // (if it's going to break, usually the code will be 1) Serial.print(F("DMP Initialization failed (code ")); Serial.print(devStatus); Serial.println(F(")")); } } void loop() { if (!dmpReady) return; if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { // Get the Latest packet // display Euler angles in degrees mpu.dmpGetQuaternion(&q, fifoBuffer); mpu.dmpGetGravity(&gravity, &q); mpu.dmpGetYawPitchRoll(rotace, &q, &gravity); } Serial.print("X "); Serial.print(rotace[2] * 180/M_PI); Serial.print(" \t Y "); Serial.print(rotace[1] * 180/M_PI); Serial.print(" \t Z "); Serial.println(rotace[0] * 180/M_PI); x=rotace[2] * 180/M_PI; y=rotace[1] * 180/M_PI; z=rotace[0] * 180/M_PI; if(abs(x)<45 && abs(y)<45){ activeColor=pixels.Color( 255,255,255); //White }else if( x>45 && abs(x)<135 && (abs(y)<45|| abs(y)>135)){ activeColor=pixels.Color( 255, 0, 0); //Red when turn to side }else if(x<-45 && abs(x)<135 && (abs(y)<45|| abs(y)>135)){ activeColor=pixels.Color( 0, 255, 0); //Green when turn to the second side }else if(y>45 && abs(y)<135 && (abs(x)<45|| abs(x)>135)){ activeColor=pixels.Color( 255, 255, 0); //Yellow when turn to the third side }else if(y<-45 && abs(y)<135 && (abs(x)<45 || abs(x)>135)){ activeColor=pixels.Color( 0,0 , 255); //Blue when turn to the fourth side }else if(abs(y)>135 && abs(x)>135){ activeColor=pixels.Color( 0,0 , 0); // Black when upside down } if(activeColor != oldActiveColor){ pixels.clear(); pixels.fill(activeColor);; oldActiveColor=activeColor; } }

Finally you can open and upload ColorCube.ino file. Put ColorCube of flat surface and switch it on. Don’t move it until it starts light with white color after calibration (few seconds). Next you can put the ColorCube on the side and color will change – every side has its own color – red, green, blue, yellow. The ColorCube goes out when it is turned upside down.

Step 6: ​Assembling

Be gentle during assembling. Wires and all parts do not like rough behavior.

Button 3d printed part – softly put button to the hole in bottom printed part (as shown on picture), it has to go in and out smoothly, if not use scalpel or sharp knife or sand paper to remove all surplus material (mostly inside on top of a circle hole on bottom part). (photo)

Put MPU-6050, Arduino Nano, TP4056 and MT3608 to their holes. The box has protrusions under which you insert MPU-6050 and MT3608. Put USB connectors of Arduino Nano and TP4056 to their holes in side walls of the box. (photo)

Use 3d printed lock to fix components (be sure that all components are laying on bottom part tightly). It is important because somebody will surely try to play with your ColorCube as with dice. (photo)

Put and secure battery in its hole if it does not hold tight.

Put Self-locking button to the prepared hole in bottom part. Self-locking button has to be in ON position (short). Gently push button down. Test functionality with 3d printed button. (photos)

Use two M2 screws to fix LED Ring to middle printed part. Is good to use orientation of the ring where wire contacts are in rounded hole of the middle printed part. (photos)

Optional: Use a drop of hot glue here and there – wires connection to ring, for too long wires, if anything is not tight enough etc. It can make your ColorCube more durable.

Arrange wires inside of ColorCube not to be pinched by printed parts. Put middle part to bottom one. Use M3 screw to fix it. (photo)

Finally gently push top printed part to bottom one. (photo)

Step 7: Done

Congratulation. Have fun.