vishal soni
Published © MIT

PCB Earring for my Girlfriend ❤️

I built this for my girlfriend — a WS2812B LED earring with ultra-compact custom wearable PCB design.

IntermediateFull instructions providedOver 2 days323
PCB Earring for my Girlfriend ❤️

Things used in this project

Hardware components

DigiSpark
DigiSpark
×1
Arduino Nano R3
Arduino Nano R3
×1
Adafruit NeoPixel Nano 2020 RGB LEDs
×12
SparkFun Li-ion Battery / Cr1225 cell
×1
Tactile Switch, SPST-NO
Tactile Switch, SPST-NO
×1
Ceramic Disc Capacitor, 0.1 µF
Ceramic Disc Capacitor, 0.1 µF
×1
Tiny-DC-DC DC Boost Converter
×1
PCBWay Custom PCB
PCBWay Custom PCB
×1

Software apps and online services

Arduino IDE
Arduino IDE
KiCad
KiCad

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Solder Paste, Rework
Solder Paste, Rework

Story

Read more

Schematics

Kicad Editabled files for PCB

Github Repository for PCB

Kicad Schematic File

You have to Download Kicad first to view this File

Code

At-Tiny85 Code for Earring

C/C++
I recommend testing the code first with any WS2812B LED ring before soldering the IC on the board, as there are no test points on the PCB.
#include <FastLED.h>

#define NUM_LEDS    12
#define DATA_PIN    3
#define MODE_PIN    4    // PB4 on ATtiny85 (if it doesn't work, try 4)
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
#define BRIGHTNESS  60

CRGB leds[NUM_LEDS];

// ----- Shared for Cylon mode -----
int pos = 0;
int dir = 1;
uint8_t hue = 0;   // rainbow hue 0–255

// ----- Mode handling -----
// 0 = Pride2015 rainbow
// 1 = Cylon, color change at BOTH ends (half revolution)
uint8_t mode = 0;

// ----- Button debounce -----
bool lastButtonStable = HIGH;
bool lastButtonRead   = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 30;  // ms

// Forward declaration
void pride();

void setup() {
  delay(500); // small start-up delay

  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS)
         .setCorrection(TypicalLEDStrip)
         .setDither(BRIGHTNESS < 255);

  FastLED.setBrightness(BRIGHTNESS);

  pinMode(MODE_PIN, INPUT_PULLUP);  // button between PB4 and GND
}

void loop() {
  // ---------- Button handling with debounce ----------
  bool reading = digitalRead(MODE_PIN);

  if (reading != lastButtonRead) {
    lastDebounceTime = millis();   // reset debounce timer
  }
  lastButtonRead = reading;

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // Stable state now
    if (reading != lastButtonStable) {
      lastButtonStable = reading;

      // Detect button press (HIGH -> LOW)
      if (lastButtonStable == LOW) {
        mode ^= 1;      // toggle between 0 and 1

        // Optional: reset animation when switching modes
        pos = 0;
        dir = 1;
        hue = 0;
      }
    }
  }

  // ---------- Modes ----------
  if (mode == 0) {
    // ==================================
    // MODE 0: Pride2015 animated rainbow
    // ==================================
    pride();
    FastLED.show();
    // no delay; pride() has internal timing based on millis()

  } else {
    // ==================================
    // MODE 1: Cylon, color change at BOTH ends
    // (half revolution color change)
    // ==================================
    // clear all LEDs
    fill_solid(leds, NUM_LEDS, CRGB::Black);

    // draw current LED in current rainbow color
    leds[pos] = CHSV(hue, 255, 255);

    FastLED.show();
    delay(35);   // movement speed

    // move the dot
    pos += dir;

    // bounce + change color at BOTH ends (half revolution)
    if (pos >= NUM_LEDS - 1) {
      pos = NUM_LEDS - 1;
      dir = -1;
      hue += 16;         // next color at right end
    } else if (pos <= 0) {
      pos = 0;
      dir = 1;
      hue += 16;         // next color at left end
    }
  }
}

// =============================
// Pride2015 effect (unchanged)
// =============================
void pride() {
  static uint16_t sPseudotime = 0;
  static uint16_t sLastMillis = 0;
  static uint16_t sHue16 = 0;

  uint8_t sat8 = beatsin88(87, 220, 250);
  uint8_t brightdepth = beatsin88(341, 96, 224);
  uint16_t brightnessthetainc16 = beatsin88(203, (25 * 256), (40 * 256));
  uint8_t msmultiplier = beatsin88(147, 23, 60);

  uint16_t hue16 = sHue16;
  uint16_t hueinc16 = beatsin88(113, 1, 3000);

  uint16_t ms = millis();
  uint16_t deltams = ms - sLastMillis;
  sLastMillis  = ms;
  sPseudotime += deltams * msmultiplier;
  sHue16 += deltams * beatsin88(400, 5, 9);
  uint16_t brightnesstheta16 = sPseudotime;

  for (uint16_t i = 0; i < NUM_LEDS; i++) {
    hue16 += hueinc16;
    uint8_t hue8 = hue16 / 256;

    brightnesstheta16 += brightnessthetainc16;
    uint16_t b16 = sin16(brightnesstheta16) + 32768;

    uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
    uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
    bri8 += (255 - brightdepth);

    CRGB newcolor = CHSV(hue8, sat8, bri8);

    uint16_t pixelnumber = i;
    pixelnumber = (NUM_LEDS - 1) - pixelnumber;

    nblend(leds[pixelnumber], newcolor, 64);
  }
}

Credits

vishal soni
13 projects • 16 followers
Engineer ,Electronic Enthusiast

Comments