Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
John Bradnam
Published © GPL3+

Rubik Lamp

A bedside nightlight that can become a Rubik Cube. It randomizes the Cube and then solves it. Cube lamp is also controllable via Bluetooth.

IntermediateFull instructions provided12 hours839

Things used in this project

Hardware components

WS2812B RGB LED
×53
Microchip ATtiny1614 Microprocessor
×1
HC-06 Bluetooth Module
×1
Resistor 0 ohm 0805 SMD
×30
Capacitor 0.1uF 0805 SMD
×36
DC-DC Buck Step Down Module 3A Adjustable Voltage Regulator Power
×1
PTS 645 Series Switch
C&K Switches PTS 645 Series Switch
5mm shaft
×1
DC Power Supply Jack Socket Female Panel Mount Connector 5.5 x 2.1mm
×1
Brass Tube 4.0 x 1000mm 0.45mm Wall
50mm length
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

STL Files

STL files for your slicer software

Schematics

Schematic - Panel Top & Sides

Schematic - Panel Bottom

Schematic - Base

PCB - Panel Top & Sides

PCB - Panel Bottom

PCB - Base

Eagle Files

Schematics and PCBs in Eagle Format

Code

RubikLampV3.ino

Arduino
/*---------------------------------------------------------------------------------------
  Rubik Lamp V2
  Author: John Bradnam

  BOARD: ATtiny1614/1604/814/804/414/404/214/204
  Chip: ATtiny1614
  Clock Speed: 20MHz  (Required!!)
  Programmer: jtag2updi (megaTinyCore)

  ATTiny1614 Pins mapped to Ardunio Pins

  2020/08/15 V1 - Initial Build
  2021/01/04 V2 - Added LAMP_RUBIK_ANIMATED mode
  2021/01/05 V3 - Fixed bug in Bluetooth mode
*/

#include <tinyNeoPixel_Static.h>
#include <avr/sleep.h>
#ifdef __AVR_ATtiny1614__
#include <avr/eeprom.h>
#else
#include <EEPROM.h>
#endif

/*  
             +--------+
         VCC + 1   14 + GND
 (SS)  0 PA4 + 2   13 + PA3 10 (SCK)
       1 PA5 + 3   12 + PA2 9  (MISO)
 (DAC) 2 PA6 + 4   11 + PA1 8  (MOSI)
       3 PA7 + 5   10 + PA0 11 (UPDI)
 (RXD) 4 PB3 + 6    9 + PB0 7  (SCL)
 (TXD) 5 PB2 + 7    8 + PB1 6  (SDA)
             +--------+

PA0 to PA7 can be analog or digital
PWM on D0, D1, D6, D7, D10
*/

#define LED 0 //PA4
#define MODE 1 //PA5
#define RANDOM 3 //PA7
#define RX 4 //PB3
#define TX 5 //PB2

/*
 *  --- Logical LED positions (LED 04 is not present => logical 05 is physical 04 ---
                          top
 *                  +----+----+----+
 *                  | 53 | 52 | 51 |
 *                  +----+----+----+
 *                  | 50 | 49 | 48 |
 *                  +----+----+----+
 *                  | 47 | 46 | 45 |
 *                  +----+-fr-+----+
         left             front            right           back
 * +----+----+----+ +----+----+----+ +----+----+----+ +----+----+----+
 * | 26 | 25 | 24 | | 17 | 16 | 15 | | 44 | 43 | 42 | | 33 | 34 | 35 |
 * +----+----+----+ +----+----+----+ +----+----+----+ +----+----+----+
 * | 23 | 22 | 21 f | 14 | 13 | 12 | f 41 | 40 | 39 | | 30 | 31 | 32 | (looking from front)
 * +----+----+----+ +----+----+----+ +----+----+----+ +----+----+----+
 * | 20 | 19 | 18 | | 11 | 10 | 09 | | 38 | 37 | 36 | | 27 | 28 | 29 |
 * +----+----+----+ +----+----+----+ +----+----+----+ +----+----+----+
                                          bottom
 *                                   +----+----+----+
 *                                   | 02 | 01 | 00 |
 *                                   +----+----+----+
 *                                   | 05 | 04 | 03 | (looking from top)
 *                                   +----+----+----+
 *                                   | 08 | 07 | 06 |
 *                                   +----+-fr-+----+
 */
 
// Face rotations
const uint8_t u1[] PROGMEM = { 53, 52, 51, 48, 45, 46, 47, 50, 53 };
const uint8_t u2[] PROGMEM = { 15, 16, 17, 24, 25, 26, 33, 34, 35, 42, 43, 44, 15 };
const uint8_t d1[] PROGMEM = { 2, 1, 0, 3, 6, 7, 8, 5, 2 };
const uint8_t d2[] PROGMEM = { 11, 10, 9, 38, 37, 36, 29, 28, 27, 20, 19, 18, 11 };
const uint8_t f1[] PROGMEM = { 17, 16, 15, 12, 9, 10, 11, 14, 17 };
const uint8_t f2[] PROGMEM = { 47, 46, 45, 44, 41, 38, 6, 7, 8, 18, 21, 24, 47 };
const uint8_t b1[] PROGMEM = { 33, 34, 35, 32, 29, 28, 27, 30, 33 };
const uint8_t b2[] PROGMEM = { 53, 52, 51, 42, 39, 36, 0, 1, 2, 20, 23, 26, 53 };
const uint8_t l1[] PROGMEM = { 26, 25, 24, 21, 18, 19, 20, 23, 26 };
const uint8_t l2[] PROGMEM = { 17, 14, 11, 8, 5, 2, 27, 30, 33, 53, 50, 47, 17 };
const uint8_t r1[] PROGMEM = { 42, 43, 44, 41, 38, 37, 36, 39, 42 };
const uint8_t r2[] PROGMEM = { 15, 12, 9, 6, 3, 0, 29, 32, 35, 51, 48, 45, 15 };

//Slice rotations
const uint8_t m2[] PROGMEM = { 16, 13, 10, 7, 4, 1, 28, 31, 34, 52, 49, 46, 16 };
const uint8_t e2[] PROGMEM = { 14, 13, 12, 41, 40, 39, 32, 31, 30, 23, 22, 21, 14 };
const uint8_t s2[] PROGMEM = { 50, 49, 48, 43, 40, 37, 3, 4, 5, 19, 22, 25, 50 };

//Push Switch
#define NEXT_BUTTON_TIMEOUT 3000  //Maximum time between button presses before button becomes a sleep button. 
#define BRIGHTNESS_INITIAL_TIMOUT 500 //Time button needs to be held down before it is treated as a long press
#define BRIGHTNESS_TIMOUT 50 //Speed at which brightness changes on a long press
#define ROTATE_SPEED 200 //Speed at which cube pieces rotate
uint32_t debounceTimeout = 0;
uint32_t buttonNextTimeout = 0;   //Used to timeout mode switch function to sleep function
bool ignoreNextButton = false; //Used to block button action when waking up
uint32_t brightnessTimeout = 0; //Timeout used to determine when mode switch is held down
bool first = true; //Used to initialise new mode

//NeoPixel library
#define NUMPIXELS 53
uint32_t cube[NUMPIXELS + 1];
byte pixels[NUMPIXELS * 3];
tinyNeoPixel strip = tinyNeoPixel(NUMPIXELS, LED, NEO_GRB, pixels);

uint8_t nextRainbowSide = 0;
uint8_t nextRainbowCube = 0;
uint8_t nextAnimatedSequence = 0;
uint8_t animatedCubeMoves = 0;
uint32_t rainbowTimeout = 0;
uint32_t animtedCubeTimeout = 0;
#define RAINBOW_SIDE_DELAY 10
#define RAINBOW_CUBE_DELAY 20
#define ANIMATED_CUBE_DELAY 500
#define ANIMATED_SOLVED_DELAY 3000
#define ANIMATED_CUBE_MIN_MOVES 18
#define ANIMATED_CUBE_MAX_MOVES 24

uint8_t cubeSteps[ANIMATED_CUBE_MAX_MOVES];

//Modes
enum MODES { LAMP_WHITE, LAMP_RED, LAMP_GREEN, LAMP_BLUE, LAMP_RAINBOW_SIDE, LAMP_RAINBOW_CUBE, LAMP_RUBIK_ANIMATED, LAMP_RUBIK, END_OF_MODES };

//EEPROM handling
#define EEPROM_ADDRESS 0
#define EEPROM_MAGIC 0x0BAD0DAD
typedef struct {
  uint32_t magic;
  MODES mode;          //Current mode
  uint16_t brightness; //Current brightness level
} EEPROM_DATA;

EEPROM_DATA EepromData;       //Current EEPROM settings

#define BLUETOOTH_BUFFER_SIZE 64
char btBuffer[BLUETOOTH_BUFFER_SIZE + 1];

// PA5 pin button interrupt (MODE switch)
void buttonInterrupt()
{
}

//Shut down ATtiny1614
//Will wake up when button is pressed
void enterSleep() 
{
  strip.clear();
  strip.show();
  //cbi(ADCSRA,ADEN);                   // switch Analog to Digitalconverter OFF
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // sleep mode is set here
  sleep_enable();
  sleep_mode();                         // System actually sleeps here
  sleep_disable();                      // System continues execution here when watchdog timed out
  //sbi(ADCSRA,ADEN);                   // switch Analog to Digitalconverter ON
  buttonNextTimeout = millis() + NEXT_BUTTON_TIMEOUT; //Time mode button can remain active before it reverts back to a sleep button
  ignoreNextButton = true;
  first = true;                       //Repaint the screen
}

//-----------------------------------------------------------------------
//Setup lamp
void setup() 
{
  //Eprom
  readEepromData();

  randomSeed(analogRead(RANDOM));

  //Bluetooth setup
  Serial.begin(9600);
  Serial.println("Hello from Rubik Lamp V1");
  
  pinMode(MODE,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(MODE),buttonInterrupt,CHANGE);

  pinMode(LED,OUTPUT);
  strip.setBrightness(EepromData.brightness);
  strip.show(); // Initialize all pixels to 'off'

  //Start in sleep mode
  enterSleep();
}

//-----------------------------------------------------------------------
//Main loop
// Handle button press. If pressed within NEXT_BUTTON_TIMEOUT, advance to
// next animation otherwise turn off the lamp
void loop() 
{
  MODES m = EepromData.mode;

  testButtonPress();
  testBluetoothInput();

  //If mode has changed, store it in EEPROM and update display
  if (first)
  {
    writeEepromData(); 
    buttonNextTimeout = millis() + NEXT_BUTTON_TIMEOUT;

    //Static modes
    switch (EepromData.mode)
    {
      case LAMP_WHITE: colorWash(strip.Color(255, 255, 255)); break;
      case LAMP_RED: colorWash(strip.Color(255, 0, 0)); break;
      case LAMP_GREEN: colorWash(strip.Color(0, 255, 0)); break;
      case LAMP_BLUE: colorWash(strip.Color(0, 0, 255)); break;
      case LAMP_RUBIK: colorRubik(); break;
    }
  }

  //Dynamic modes
  switch (EepromData.mode)
  {
    case LAMP_RAINBOW_SIDE: colorRainbowSide(first); break;
    case LAMP_RAINBOW_CUBE: colorRainbowCube(first); break;
    case LAMP_RUBIK_ANIMATED: colorRubikAnimated(first); break;
  }
  
  first = false;
  delay(100);
}

//-----------------------------------------------------------------------
// Handle bluetooth commands
//  Obtain characters from HC06 and process request
void testBluetoothInput()
{
  int8_t oldMode = EepromData.mode;
  //Fill buffer with multiple commands
  int pos = 0;
  char a;
  while (Serial.available())
  {
    a = Serial.read();
    if (pos < BLUETOOTH_BUFFER_SIZE)
    {
      btBuffer[pos] = a;
      pos++;
    }
  }
  btBuffer[pos] = '\0';

  //Process each command in turn
  pos = 0;
  a = btBuffer[pos++];
  while (a != '\0')
  {
    char face = (a & ~0x20);
    bool lc = (a >= 'a');
    if (EepromData.mode == LAMP_RUBIK)
    {
      switch (face)
      {
        case 'F': rotateFace(f1, f2, lc); break;
        case 'B': rotateFace(b1, b2, lc); break;
        case 'U': rotateFace(u1, u2, lc); break;
        case 'D': rotateFace(d1, d2, lc); break;
        case 'L': rotateFace(l1, l2, !lc); break;
        case 'R': rotateFace(r1, r2, !lc); break;
        case 'M': rotateFace(NULL, m2, lc); break;
        case 'S': rotateFace(NULL, s2, lc); break;
        case 'E': rotateFace(NULL, e2, lc); break;
        case 'X': rotateCubeOnR(lc); break;
        case 'Y': rotateCubeOnU(lc); break;
        case 'Z': rotateCubeOnF(lc); break;
        case 'W': EepromData.mode = LAMP_WHITE; break; //Switch to WHITE light
      }
    }
    else
    {
      switch (face)
      {
        case 'C': EepromData.mode = LAMP_RUBIK; break; //Switch to CUBE mode
        case 'R': EepromData.mode = LAMP_RED; break; //Switch to RED light
        case 'G': EepromData.mode = LAMP_GREEN; break; //Switch to GREEN light
        case 'B': EepromData.mode = LAMP_BLUE; break; //Switch to BLUE light
        case 'W': EepromData.mode = LAMP_WHITE; break; //Switch to WHITE light
        case 'J': EepromData.mode = LAMP_RAINBOW_SIDE; break; //Switch to RAINBOW SIDE light
        case 'K': EepromData.mode = LAMP_RAINBOW_CUBE; break; //Switch to RAINBOW CUBE light
      }
    }
    a = btBuffer[pos++];
  }
  first = first | (oldMode != EepromData.mode);
}

//-----------------------------------------------------------------------
// Handle button processing
//  If last button press was more than NEXT_BUTTON_TIMEOUT, go to sleep
//  otherwise switch to next mode 
void testButtonPress()
{
  //Test button
  if (isButtonPressed())
  {
    if (ignoreNextButton)
    {
      ignoreNextButton = false; //Ignore if coming out of a sleep
    }
    else if (millis() > buttonNextTimeout)
    {
      enterSleep();
    }
    else
    {
      EepromData.mode = (MODES)((int)EepromData.mode + 1);
      if (EepromData.mode == END_OF_MODES)
      {
        EepromData.mode = LAMP_WHITE;  
      }
      first = true;
    }
  }
}

//-----------------------------------------------------------------------
//Test if button is pressed
//  Not pressed returns FALSE
//  Short press (debounced) returns TRUE
//  Long press adjust brightness level
bool isButtonPressed()
{
  bool pressed = false;
  bool brightnessChanged = false;
  if (digitalRead(MODE) == LOW)
  {
    //start timing switch
    debounceTimeout = millis() + 10;
    while (digitalRead(MODE) == LOW && millis() < debounceTimeout)
    {
      yield();
    }
    if (digitalRead(MODE) == LOW)
    {
      pressed = true;
      //button is being pressed
      brightnessTimeout = millis() + BRIGHTNESS_INITIAL_TIMOUT;
      while (digitalRead(MODE) == LOW)
      {
        if (millis() > brightnessTimeout)
        {
          EepromData.brightness = max((EepromData.brightness + 2) & 0xFF, 0x04);
          strip.setBrightness(EepromData.brightness);
          strip.show();
          brightnessTimeout = millis() + BRIGHTNESS_TIMOUT;
          buttonNextTimeout = millis() + NEXT_BUTTON_TIMEOUT; //Reset sleep button timeout
          pressed = false;
          brightnessChanged = true;
        }
        yield();
      }
      if (brightnessChanged)
      {
        writeEepromData();
      }
    }
  }
  return pressed;
}

//-----------------------------------------------------------------------
//Display a single color
// c - color to wash cube with
void colorWash(uint32_t c) 
{
  for(uint16_t i = 0; i < strip.numPixels(); i++) 
  {
    strip.setPixelColor(i, c);
  }
  strip.show();
}

//-----------------------------------------------------------------------
//Display a rainbow of colors
void colorRainbowSide(bool first)
{
  if (first || millis() > rainbowTimeout)
  {
    rainbowTimeout = millis() + RAINBOW_SIDE_DELAY;
    uint32_t color;
    int j = 0;
    for (int i=0; i < strip.numPixels(); i++) 
    {
      if (i < 8)
      {
        //Bottom
        color = Wheel((nextRainbowSide + 0) & 0xFF); 
      }
      else if (i < 17)
      {
        //Front
        color = Wheel((nextRainbowSide + 42) & 0xFF); 
      }
      else if (i < 26)
      {
        //Left
        color = Wheel((nextRainbowSide + 84) & 0xFF); 
      }
      else if (i < 35)
      {
        //Back
        color = Wheel((nextRainbowSide + 126) & 0xFF); 
      }
      else if (i < 44)
      {
        //Right
        color = Wheel((nextRainbowSide + 168) & 0xFF); 
      }
      else
      {
        //Top
        color = Wheel((nextRainbowSide + 210) & 0xFF); 
      }
      cube[j++] = color;
      if (j == 4)
      {
        //Set missing LED spot to same color as last pixel
        cube[j++] = color;
      }
    }
    cubePaint();
    nextRainbowSide = (nextRainbowSide + 1) & 0xFF;
  }
}

//-----------------------------------------------------------------------
//Display a rainbow of colors
void colorRainbowCube(bool first)
{
  if (first || millis() > rainbowTimeout)
  {
    rainbowTimeout = millis() + RAINBOW_CUBE_DELAY;
    uint32_t color = Wheel(nextRainbowCube & 0xFF); 
    int j = 0;
    for (int i=0; i < strip.numPixels(); i++) 
    {
      cube[j++] = color;
      if (j == 4)
      {
        //Set missing LED spot to same color as last pixel
        cube[j++] = color;
      }
    }
    cubePaint();
    nextRainbowCube = (nextRainbowCube + 1) & 0xFF;
  }
}

//-----------------------------------------------------------------------
//Show Rubik cube, randomise the cube, solve the cube
void colorRubikAnimated(bool initialise)
{
  uint8_t nextMove;
  uint8_t lastMove;
  if (initialise || (millis() > animtedCubeTimeout && nextAnimatedSequence == 0))
  {
    //Paint cube
    colorRubik();
    //Randomize cube
    lastMove = 0;
    nextMove = 1;
    nextAnimatedSequence = 0;
    animatedCubeMoves = random(ANIMATED_CUBE_MIN_MOVES, ANIMATED_CUBE_MAX_MOVES + 1);
    while (nextAnimatedSequence < animatedCubeMoves && !first)
    {
      //Get next move that isn't the reverse of the last move
      nextMove = random(0,18);
      while ((nextMove >> 1) == (lastMove >> 1) && (nextMove & 0x01 != lastMove & 0x01))
      {
        nextMove = random(0,18);
      }
      cubeSteps[nextAnimatedSequence] = nextMove;
      moveSegment(nextMove);
      nextAnimatedSequence++;

      //Becuase this takes time, we need to check button test
      testButtonPress();
    }
    //Start solving the cube
    animtedCubeTimeout = millis() + ANIMATED_CUBE_DELAY;
  }
  if (millis() > animtedCubeTimeout)
  {
    nextAnimatedSequence--;
    nextMove = cubeSteps[nextAnimatedSequence] ^ 0x01;   //Reverse direction
    moveSegment(nextMove);
    animtedCubeTimeout = millis() + ((nextAnimatedSequence == 0) ? ANIMATED_SOLVED_DELAY : ANIMATED_CUBE_DELAY);
  }
}

//-----------------------------------------------------------------------
//Moves the cube segment
// s = move - (bit 0 is direction, bits 4,3,2,1 is move type)
void moveSegment(uint8_t s)
{
  bool lc = (s & 0x01);
  switch (s >> 1)
  {
    case 0: rotateFace(f1, f2, lc); break;
    case 1: rotateFace(b1, b2, lc); break;
    case 2: rotateFace(u1, u2, lc); break;
    case 3: rotateFace(d1, d2, lc); break;
    case 4: rotateFace(l1, l2, !lc); break;
    case 5: rotateFace(r1, r2, !lc); break;
    case 6: rotateFace(NULL, m2, lc); break;
    case 7: rotateFace(NULL, s2, lc); break;
    case 8: rotateFace(NULL, e2, lc); break;
  }
  cubePaint();
}

//-----------------------------------------------------------------------
//Display colors of a rubik cube
void colorRubik()
{
  uint32_t color;
  int j = 0;
  for (int i=0; i < strip.numPixels(); i++) 
  {
    if (i < 8)
    {
      //Bottom
      color = strip.Color(255, 64, 0); //Orange
    }
    else if (i < 17)
    {
      //Front
      color = strip.Color(255, 255, 255); //White
    }
    else if (i < 26)
    {
      //Left
      color = strip.Color(0, 0, 255); //Blue
    }
    else if (i < 35)
    {
      //Back
      color = strip.Color(255, 255, 0); //Yellow
    }
    else if (i < 44)
    {
      //Right
      color = strip.Color(0, 255, 0);  //Green
    }
    else
    {
      //Top
      color = strip.Color(255, 0, 0);  //Red
    }
    cube[j++] = color;
    if (j == 4)
    {
      //Set missing LED spot to same color as last pixel
      cube[j++] = color;
    }
  }
  cubePaint();
}

//---------------------------------------------------------------
//Transfer our cube buffer to the LED strip
void cubePaint()
{
  for (int i=0, j=0; i < strip.numPixels(); i++) 
  {
    strip.setPixelColor(i, cube[j++]);
    //Skip over missing physical LED
    if (j == 4)
    {
      j++;
    }
  }
  strip.show();
}

//---------------------------------------------------------------
void rotatePixels(uint8_t* p, bool forward)
{
  uint8_t first = pgm_read_byte_near(p++);
  uint32_t colorFirst = cube[first];
  uint8_t last = first;
  uint8_t next;
  if (forward)
  {
    next = pgm_read_byte_near(p++);
    while (next != first)
    {
      cube[last] = cube[next];
      last = next;
      next = pgm_read_byte_near(p++);
    }
  }
  else
  {
    while (pgm_read_byte_near(p) != first)
    {
      p++;
    }
    next = pgm_read_byte_near(--p);
    while (next != first)
    {
      cube[last] = cube[next];
      last = next;
      next = pgm_read_byte_near(--p);
    }
  }
  cube[last] = colorFirst;
}

//---------------------------------------------------------------
//Rotate the face of a cube
//
void rotateFace(uint8_t* p1, uint8_t* p2, bool forward)
{
  for (int i = 0; i < 3; i++)
  {
    if (i != 1 && p1 != NULL)
    {
      rotatePixels(p1, forward);
    }
    if (p2 != NULL)
    {
      rotatePixels(p2, forward);
    }
    cubePaint();
    delay(ROTATE_SPEED);
  }
}

//---------------------------------------------------------------
//Rotate whole cube around the right hand center (X axis)
void rotateCubeOnR(bool lc)
{
  for (int i = 0; i < 3; i++)
  {
    if (i != 1)
    {
      rotatePixels(l1, !lc);
      rotatePixels(r1, !lc);
    }
    rotatePixels(l2, !lc);
    rotatePixels(m2, !lc);
    rotatePixels(r2, !lc);
    cubePaint();
    delay(ROTATE_SPEED);
  }
}

//---------------------------------------------------------------
//Rotate whole cube around the up center (Y axis)
void rotateCubeOnU(bool lc)
{
  for (int i = 0; i < 3; i++)
  {
    if (i != 1)
    {
      rotatePixels(u1, !lc);
      rotatePixels(d1, lc);
    }
    rotatePixels(u2, !lc);
    rotatePixels(e2, lc);
    rotatePixels(d2, lc);
    cubePaint();
    delay(ROTATE_SPEED);
  }
}

//---------------------------------------------------------------
//Rotate whole cube around the up front (Z axis)
void rotateCubeOnF(bool lc)
{
  for (int i = 0; i < 3; i++)
  {
    if (i != 1)
    {
      rotatePixels(f1, lc);
      rotatePixels(b1, lc);
    }
    rotatePixels(f2, lc);
    rotatePixels(s2, lc);
    rotatePixels(b2, lc);
    cubePaint();
    delay(ROTATE_SPEED);
  }
}

//---------------------------------------------------------------
//Write the EepromData structure to EEPROM
void writeEepromData()
{
  //This function uses EEPROM.update() to perform the write, so does not rewrites the value if it didn't change.
  #ifdef __AVR_ATtiny1614__
    eeprom_update_block (( void *) &EepromData , ( const void *) EEPROM_ADDRESS, sizeof(EepromData));  
  #else
    EEPROM.put(EEPROM_ADDRESS,EepromData);
  #endif
}

//---------------------------------------------------------------
//Read the EepromData structure from EEPROM, initialise if necessary
void readEepromData()
{
  //Eprom
  #ifdef __AVR_ATtiny1614__
    eeprom_read_block (( void *) &EepromData , ( const void *) EEPROM_ADDRESS, sizeof(EepromData));  
  #else
    EEPROM.get(EEPROM_ADDRESS,EepromData);
  #endif
  if (EepromData.magic != EEPROM_MAGIC)
  {
    EepromData.magic = EEPROM_MAGIC;
    EepromData.mode = LAMP_WHITE;
    EepromData.brightness = 0x40;
    writeEepromData();
  }
}

//-----------------------------------------------------------------------
void rainbow(uint8_t wait) 
{
  uint16_t i, j;

  for(j=0; j<256; j++) 
  {
    for(i=0; i<strip.numPixels(); i++) 
    {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) 
{
  uint16_t i, j;

  for(j=0; j<256*5; j++) 
  { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) 
    {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) 
{
  for (int j=0; j<10; j++) 
  {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) 
    {
      for (int i=0; i < strip.numPixels(); i=i+3) 
      {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (int i=0; i < strip.numPixels(); i=i+3) 
      {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) 
{
  for (int j=0; j < 256; j++) 
  {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) 
    {
      for (int i=0; i < strip.numPixels(); i=i+3) 
      {
        strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (int i=0; i < strip.numPixels(); i=i+3) 
      {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) 
{
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) 
  {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) 
  {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

Credits

John Bradnam
151 projects • 195 followers
Contact

Comments

Please log in or sign up to comment.