RGB LED Etch-A-Sketch

by MrLangford in Circuits > Arduino

20665 Views, 247 Favorites, 0 Comments

RGB LED Etch-A-Sketch

Final - Front.jpg
Without Case.jpg

A modern take on an old classic -- create colour pixel art by turning two rotary encoders to control the horizontal and vertical position of the cursor, click to change colour. All controlled by an Arduino Nano.

How it works

  • Turning the left encoder moves the cursor left and right. Pressing it cycles forward through an array of eight colours
  • Turning the right encoder moves the cursor up and down. Pressing it cycles backwards through the array of colours
  • When you move the cursor, the selected colour remains in the previous 'pixel'
  • The cursor is shown brighter than the other pixels so users can see where it is.

Supplies

Electronics

Case

  • MDF / wood
  • PVA glue
  • Red paint
  • Hot glue

Building Your Circuit

Without Case x.jpg
etch-a-sketch_bb.png

To test everything worked, I first built the circuit using a breadboard. The matrix should use an additional power source, however I did not have any problems using power directly from the Arduino -- but do this at your own risk!

Program

You will need to install the FastLED and LEDMatrix libraries.

Not all LED matrix panels are configured in the same pattern. The LEDMatrix library includes different configurations, you may need to experiment with lines 14, 15, and 16. Read the LEDMatrix library documentation for instructions.

// RGB LED Etch-A-Sketch
// Author: John-Lee Langford
// Website: www.mr.langford.co

// Includes
#include <FastLED.h> //  https://github.com/FastLED/FastLED
#include <LEDMatrix.h> //  https://github.com/AaronLiddiment/LEDMatrix

// Change the next 6 defines to match your matrix type and size
#define LED_PIN        10
#define COLOR_ORDER    GRB
#define CHIPSET        WS2812

#define MATRIX_WIDTH   -16  // Set this negative if physical led 0 is opposite to where you want logical 0
#define MATRIX_HEIGHT  -16  // Set this negative if physical led 0 is opposite to where you want logical 0
#define MATRIX_TYPE    VERTICAL_ZIGZAG_MATRIX  // See top of LEDMatrix.h for matrix wiring types

cLEDMatrix<MATRIX_WIDTH, MATRIX_HEIGHT, MATRIX_TYPE> leds;

// Rotary A - Horizontal movement
int horizontalA = 9; // DT
int horizontalB = 8; // CLK
int buttonA = 7; // Button
int btnA; // Button A value
int horizontalALast;
int horizontal, horizontalCursor = 0;
unsigned char encoder_horizontalA;
unsigned char encoder_horizontalB;
unsigned char encoder_horizontalA_prev = 0;

// Rotary B - Vertical movement
int verticalA = 6; // DT
int verticalB = 5; // CLK
int buttonB = 4; // Button
int btnB; // Button B value
int verticalALast;
int vertical, verticalCursor = 0;
unsigned char encoder_verticalA;
unsigned char encoder_verticalB;
unsigned char encoder_verticalA_prev = 0;

// Common
unsigned long currentTime;
unsigned long loopTime;
bool moved = false;

// Colours
const int numberColours = 9; // Total number of colours available
unsigned long colours[numberColours][2] = {
  {0xFF0000, 0x330000},
  {0xFF6600, 0x331100},
  {0xFFFF00, 0x333300},
  {0x00FF00, 0x003300},
  {0x00FFFF, 0x001111},
  {0x0000FF, 0x000033},
  {0xFF00FF, 0x330033},
  {0xFFFFFF, 0x111111},
  {0x111111, 0x000000}
};
int currentColour = 0; // Set to first colour in array

void setup() {

  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds[0], leds.Size());
  FastLED.setBrightness(255);

  // Clear the display in a pretty way
  int colourBounce = 1;
  for (int i = 0; i < 16; i++){
    leds.DrawLine(0, i, 15, i, colours[currentColour][0]);
    leds.DrawLine(i, 0, i, 15, colours[currentColour][0]);
    FastLED.show();
    delay(25);
    leds.DrawLine(0, i, 15, i, CRGB(0, 0, 0));
    leds.DrawLine(i, 0, i, 15, CRGB(0, 0, 0));
    currentColour = currentColour + colourBounce;
    if (i == 7) {
      colourBounce = -1;
    }
    
  }

  FastLED.clear(true);

  // Rotary A - Horizontal
  pinMode (horizontalA, INPUT);
  pinMode (horizontalB, INPUT);
  pinMode (buttonA, INPUT);
  horizontalALast = digitalRead(horizontalA);

  // Rotary B - Vertical
  pinMode (verticalA, INPUT);
  pinMode (verticalB, INPUT);
  pinMode (buttonB, INPUT);
  verticalALast = digitalRead(verticalA);

  // Common
  currentTime = millis();
  loopTime = currentTime;
}

void loop() {
  // get the current elapsed time
  currentTime = millis();
  if (currentTime >= (loopTime + 5)) {
    // Read encoder pins
    encoder_horizontalA = digitalRead(horizontalA);
    encoder_horizontalB = digitalRead(horizontalB);
    encoder_verticalA = digitalRead(verticalA);
    encoder_verticalB = digitalRead(verticalB);

    // Check for button presses
    btnA = digitalRead(buttonA);
    btnB = digitalRead(buttonB);

    if (btnA == 0) {
      currentColour++;
      if (currentColour > (numberColours - 1)) {
        currentColour = 0;
      }
      delay(500);
    }

    if (btnB == 0) {
      currentColour--;
      if (currentColour < 0) {
        currentColour = numberColours - 1;
      }
      delay(500);
    }


    // Encoder A
    if ((!encoder_horizontalA) && (encoder_horizontalA_prev)) {
      // A has gone from high to low
      if (encoder_horizontalB) {
        // B is high so counter-clockwise
        if (horizontalCursor > 0) {
          horizontalCursor --;
          moved = true;
        }
      }
      else {
        // B is low so clockwise
        if (horizontalCursor < 15) {
          horizontalCursor ++;
          moved = true;
        }
      }

    }
    encoder_horizontalA_prev = encoder_horizontalA; // Store value of A for next time

    // Encoder B
    if ((!encoder_verticalA) && (encoder_verticalA_prev)) {
      // C has gone from high to low
      if (encoder_verticalB) {
        // B is high so counter-clockwise
        if (verticalCursor < 15) {
          verticalCursor ++;
          moved = true;
        }
      } else {
        // D is low so clockwise
        if (verticalCursor > 0) {
          verticalCursor --;
          moved = true;
        }
      }
    }

    encoder_verticalA_prev = encoder_verticalA; // Store value of C for next time
    loopTime = currentTime; // Updates loopTime

    leds(horizontalCursor, verticalCursor) = colours[currentColour][0];

    // Move the LED
    if (moved) {
      leds(horizontal, vertical) = colours[currentColour][1];
      horizontal = horizontalCursor;
      vertical = verticalCursor;
      moved = false;
    }

    FastLED.show();
  }
}

Downloads

Building the Case

Case 1.jpg
Case 2.jpg
Case 3.jpg

Once you are happy with the electronics, it's time to build the case.

I used some 6mm sheets of MDF which I had lying around. I cut four sheets into roughly A4 size then, leaving a one inch border, cut the middle out of three of them. These three sheets should, when glued together, provide enough depth to house the Arduino and other components. To achieve this, I used right-angled GPIO pins on the Arduino.

The four sheets of MDF were then stuck together using the PVA glue, using a couple of clamps to ensure a good fit with no gaps.

When the PVA had fully set, the edges were sanded-down.

Appropriately sized holes were drilled into the front of the case:

  • One holes for each of the rotary encoders
  • Three holes for the matrix -- one for the jumpers, one for the additional (optional) power supply, and for the unused connections to an additional panel.

You will need to measure your components and layout to determine where to put the holes.

Finally, paint it red.

Bringing It All Together

IMG_4634.jpg
Final - Front.jpg

Now it's time to put the components into the case!

To keep the device as slim as possible, I used thin strips of protoboard (1 x 4) to distribute +5v and Ground and soldered connectors to them.

The rotary encoders came with nuts to screw them to the case. Everything else is held in place using hot glue.

Note: if you are building your Etch-A-Sketch using the final photo, remember that you are looking at the rotary encoders from the back. On the schematic (Step 1), the rotaries are shown facing forward.

Enjoy creating your pixel art!