Adding RGB LEDs to an illuminated arcade button

Somewhat painterly view of the button doing its thing. The weird clunking sound is my camera’s continuous focus. For a clearer but more flickery view, see here

Following on from a customer query at Elmwood Electronics, I can confirm that one can install install addressable RGB LEDs/NeoPixels inside one of these large buttons. It’s not the easiest build, so whether one should attempt this is another matter entirely.

You’ll need:

  • Large Arcade Button with LED – 60 mm White (tall version) – this is larger and more domed than the flat-top one that Adafruit sells
  • RGB LEDs – I used a generic 8 LED ring, but anything not too tall and under 45 mm in diameter should fit. Either a 7 X WS2812 5050 RGB LED Ring or Adafruit’s NeoPixel Ring – 12 x WS2812 5050 RGB LED with Integrated Drivers could also work
  • Thin (and I mean thin: I used 28 AWG) Silicone Cover Stranded-Core Wire in several colours. You’ll want to cut this quite long at first, as you have to ease it through some tiny holes in the button assembly. If you solder connectors on the end, you won’t be able to disassemble or install the button without cutting them off. Do I speak from experience here? You betcha!
  • The usual soldering/hot gluing/bending/prying/grabbing/cutting tools you already know and love. In addition, you might consider a non-marring spudger and a pair of small(ish) arterial forceps (aka hemostats, aka Kelly forceps, aka fishing hook removal pliers)

I’m not going to cover soldering the wires to the LED PCB in any depth here. You’ll need three wires: 5 V power, Ground and Data. Even though the LEDs I used need 5 V power, they are quite happy with 3.3 V logic on the data line. They need more than 3.3 V power to light, though.

a large arcade machine style button on the left: it has a clear domed top and a threaded base. On the right is the combined microswitch and LED holder that fits into the button base
The button in two pieces, as you might expect to receive it
the top of the button disassembled into its main parts: bezel ring at top left, threaded lock ring at bottom, and main button mechanism. The mechanism is upside down, so the return spring and button actuators can be seen inside the threaded shaft
Main parts of the button top, once you’ve removed the lock ring
Close up of inside the shaft: return spring and its retainer tabs, and button actuators can be seen
First step is to ease the spring out without bending it too much or breaking the retainer tabs
Close up of inside the shaft: the tips of a pair of forceps have eased the top of the spring past its retainers
I used small forceps to ease the spring out. Once you get it started, it unscrews easily from behind the retainers
Close up of inside the shaft: the button actuators have been pushed down the shaft, allowing the top of the button to be pulled out
Now the spring is out the way, you can squeeze in the actuator tabs and push them down the shaft to liberate the button top
button top components arranged: black threaded button base on left, return spring in the middle, and domed clear top with white underside and white actuators sticking down
The button top disassembled
clear button top attached to its white underside. A blunt metal tool (spudger) is pointed at the push-fit join between the two parts
Carefully lever off the clear top with a blunt tool like a spudger. Now would have been a great time to clean dust and other wee bits off your workspace, as they’ll surely end up inside the button, looking nasty
clear button top separated from its white base. A translucent white diffuser is inside the clear top. The white base has a hollow centre and a circular cavity
The button top opened up. The cavity is about 45 mm in diameter and only a few millimetres deep
The microswitch with the LED holder attached on top. The blade of a blunt metal spudger is inserted under a plastic tab that holds the LED holder onto the switch
Removing the LED holder from the microswitch is done by levering open (gently) the plastic tab that clamps the holder onto the switch.
the LED holder at left, and the bare microswitch. The LED holder has an LED in a white plastic retainer, and below it two spade contacts. The switch has three spade connectors: Com(mon) on the base, and "NO 3" (Normally Open) and "NC 2" on the right side. Normal operation connects COM and NO
LED holder and microswitch separated. For normal button operation, the contacts NO and COM become connected when the button is pressed. The spade contacts on the LED holder look like they should come out, and they will (soon)
LED holder disassembled into two parts. The black LED holder base is on the left, with the two conenctor clips slightly blurry at top. On the right is the LED in its white support, pulled out of the holder base
Pull the LED out from the holder, and you’ll see the metal clips that held it in place. These clips have to come out: I found the pushing them in slightly while pulling down on the spade connector eased them out eventually
White button top underside with an 8 RGB LED ring hot glued into it. Three thin insulated wires (from top: yellow (data), red (5 V) and black (GND)) are previously soldered behind the LED board, and are secured against strain with a large deposit of hot glue
LED ring hot glued into place. Make sure that the wires are properly secured, as you don’t want to take this apart again
threaded button base with clear top fitted, seen from underneath. The white button actuators have been pushed back into place, and the three coloured wires are feeding through the hole in the shaft. The return spring is outside the wires, and is being fitted around the retainers inside the shaft
Fit the clear button top back inside the base, feeding the wires through the shaft. Fitting the return spring back in is a bit more chaotic than getting it out. I ended up jamming it in with forceps, and it seemed to sort out okay despite that
underside of the button shaft, with microswitch attached to LED holder. The wires coming from the LED ring inside the button top have been fed through the small cavities where the original LED holder clips/contacts have been removed. The red/black power wires are on the side towards us, while the yellow data wire is behind the microswitch
The really fiddly bit: feeding the wires through the tiny gaps where the LED holder clips/contacts used to be. Even using thin (28 AWG) silicone covered wire, all three wires couldn’t fit down one side. Make sure the wires are pulled gently through, and aren’t snagged anywhere
Fully reassembled button, with microswitch installed into its bayonet connector in the threaded shaft, and the button actuator lined up with the microswitch lever on the left. The yellow data wire is in front of the microswitch at bottom
Finished! Make sure that the switch actuates properly by lining up the LED holder in the bayonets inside the shaft. Of course, you’ll have wanted to install the button in your project before doing this assembly, as you’ll have to feed those pesky wires back through again if you haven’t …

much improved HSV colour cycling LED on Arduino

There were some flaws in the post HSV colour cycling LED on Arduino. This does much more what I wanted:

HSV fade/bounce for Arduino - Stewart C. Russell - - 2010/09/19

LED is RGB common cathode (SparkFun sku: COM-09264 or equivalent)
    * Digital pin  9 → 165Ω resistor → LED Red pin
    * Digital pin 10 → 100Ω resistor → LED Green pin
    * Digital pin 11 → 100Ω resistor → LED Blue pin
    * GND → LED common cathode.

#define RED                9 // pin for red LED; green on RED+1 pin, blue on RED+2 pin
#define DELAY              2

long rgb[3];
long rgbval, k;
float hsv[3] = {
  0.0, 0.5, 0.5
float hsv_min[3] = {
  0.0, 0.0, 0.4 // keep V term greater than 0 for smoothness
float hsv_max[3] = {
  6.0, 1.0, 1.0
float hsv_delta[3] = {
  0.0005, 0.00013, 0.00011

chosen LED SparkFun sku: COM-09264
 has Max Luminosity (RGB): (2800, 6500, 1200)mcd
 so we normalize them all to 1200 mcd -
 R  1200/2800  =  0.428571428571429   =   109/256
 G  1200/6500  =  0.184615384615385   =    47/256
 B  1200/1200  =  1.0                 =   256/256
long bright[3] = {
  109, 47, 256

void setup () {
  for (k=0; k<3; k++) {
    pinMode(RED + k, OUTPUT);
    rgb[k]=0; // start with the LED off
    analogWrite(RED + k, rgb[k] * bright[k]/256);
    if (k>1 && random(100) > 50) {
      // randomly twiddle direction of saturation and value increment on startup
      hsv_delta[k] *= -1.0;

void loop() {
  for (k=0; k<3; k++) { // for all three HSV values
    hsv[k] += hsv_delta[k];
    if (k<1) { // hue sweeps simply upwards
      if (hsv[k] > hsv_max[k]) {
    else { // saturation or value bounce around
      if (hsv[k] > hsv_max[k] || hsv[k] < hsv_min[k]) {
        hsv_delta[k] *= -1.0;
        hsv[k] += hsv_delta[k];
    hsv[k] = constrain(hsv[k], hsv_min[k], hsv_max[k]); // keep values in range

  rgbval=HSV_to_RGB(hsv[0], hsv[1], hsv[2]);
  rgb[0] = (rgbval & 0x00FF0000) >> 16; // there must be better ways
  rgb[1] = (rgbval & 0x0000FF00) >> 8;
  rgb[2] = rgbval & 0x000000FF;

  for (k=0; k<3; k++) { // for all three RGB values
    analogWrite(RED + k, rgb[k] * bright[k]/256);

long HSV_to_RGB( float h, float s, float v ) {
     modified from Alvy Ray Smith's site:
   H is given on [0, 6]. S and V are given on [0, 1].
   RGB is returned as a 24-bit long #rrggbb
  int i;
  float m, n, f;

  // not very elegant way of dealing with out of range: return black
  if ((s<0.0) || (s>1.0) || (v<0.0) || (v>1.0)) {
    return 0L;

  if ((h < 0.0) || (h > 6.0)) {
    return long( v * 255 ) + long( v * 255 ) * 256 + long( v * 255 ) * 65536;
  i = floor(h);
  f = h - i;
  if ( !(i&1) ) {
    f = 1 - f; // if i is even
  m = v * (1 - s);
  n = v * (1 - s * f);
  switch (i) {
  case 6:
  case 0: // RETURN_RGB(v, n, m)
    return long(v * 255 ) * 65536 + long( n * 255 ) * 256 + long( m * 255);
  case 1: // RETURN_RGB(n, v, m) 
    return long(n * 255 ) * 65536 + long( v * 255 ) * 256 + long( m * 255);
  case 2:  // RETURN_RGB(m, v, n)
    return long(m * 255 ) * 65536 + long( v * 255 ) * 256 + long( n * 255);
  case 3:  // RETURN_RGB(m, n, v)
    return long(m * 255 ) * 65536 + long( n * 255 ) * 256 + long( v * 255);
  case 4:  // RETURN_RGB(n, m, v)
    return long(n * 255 ) * 65536 + long( m * 255 ) * 256 + long( v * 255);
  case 5:  // RETURN_RGB(v, m, n)
    return long(v * 255 ) * 65536 + long( m * 255 ) * 256 + long( n * 255);