Inspired by a fellow YouTuber, I wanted to build an LED display myself. Ping Pong balls should be used as the diffusing elements. This gives the display a distinct look and creates an interesting illumination. The development of the display is not yet finished, but the progress is shown as part of a YouTube video series.

Part 1 – The Body

The body of the PingPong Display was designed in Fusion360. It incorporates 331 ping-pong balls arranged in a hexagonal pattern. This reduces the inter-pixel distance which leads to a higher pixel density. The balls are attached to a laser-cut board. It ensures the correct positioning. The board consists of multiple layers made from MDF which incorporate features to attach the LEDs from underneath. The baseboard is supported by wooden bars around the perimeter of the hexagon.

Utilized Components

4xBase Board
Material: HDF
Size: A1
Thickness: 3 mm
6xWooden Bars
Material: Beech
Size: 20×50 mm
Wood Glue
18xScrews
Type: Flat Hat
Length: 50mm
1xClamping Band
1xPaint
Color: Black
7xLED Strip
LED Type: WS2812B
# LEDs: 50 each
Type: String (!)
LED Distance: 120 mm
331xToothpicks
Hot Glue
331xPing-pong Balls
Diameter: 40 mm
Utilized Components

Part 2 – Power Supply

The 331 LEDs draw up to 16,5 A of electrical power which adds up to 83 W @ 5V. Therefore, a fitting enclosed power supply with passive cooling was chosen. As it constantly draws current even in stand-by mode, a Solid State Relais is used to cut the supply of the grid if it is not required. To power the microcontroller and some other components, an additional AC-DC converter is added.

To route the power to all LEDs safely and uniformly, 4 thick wires are used to connect the controller box to the power supply. From there, 7 individual cables are connected to every 50th LED.

Utilized Components

1xPower Supply
Type: HG2547
Rating: 5 V, 10 A
1xSolid State Relais
Type: SSR-40
Manufacturer: Fotek
Rating: 380 V, 40 A
1xCustom Circuit Board
1xAC-DC Converter
Type: IRM-10-5
Manufacturer: Mean Well
Rating: 5 V, 10 W
1xScrew Terminal
# Pins: 7
Spacing: 5.08 mm
1xScrew Terminal
# Pins: 4
Spacing: 5.08 mm
2xScrew Terminal
# Pins: 3
Spacing: 5.08 mm
2xScrew Terminal
# Pins: 2
Spacing: 5.08 mm
1xCapacitor
Type: Electrolytic
Rating: 150 uF
1xCapacitor
Type: Ceramic
Rating: 10 nF
1xScrewed Cable Gland
1xInlet connector for non-heating apparatus
1xCable
Specs: 7x 1.5mm²
Cables
Crimp connectors
Heat shrink tubing

Part 3 – Controller Unit

To control the LED’s behavior, an ESP32 microcontroller is used. It features potent computing power, lots of I/O pins, and a small form factor. But most important, it enables WiFi and Bluetooth connections which can be used to control the final display remotely. However, the design included two rotary encoders that can be used to manually input directly on the device.

Additional sensors should further extend the device’s capabilities. I decided to go for a microphone, a temperature and humidity sensor, a motion sensor, a light sensor, and a gas and smoke detector. Those additional components are hooked up to the central ESP32 making use of nearly every GPIO pin.

A custom circuit board is used for tedious wiring and includes all the connectors in a neat arrangement. A hexagonal case was designed in Fusion360 and printed using white PLA. The audio, temperature, and gas sensors are arranged on the perimeter while the motion sensor, light sensor, and the encoders are sticking out the front to face later users.

The 7-strand wire of the power supply is connected to this custom PCB as well and splits it into 7 individual 2-strand wires that are connected to the LED strings. Additionally, a data wire is connected to the first LED. The data packets are passed from one WS2812B to the next which simplifies their control.

Utilized Components

1xESP32 Microcontroller Board
Type: NodeMCU ESP-32
1xLM393 Microphone Amplifier Board
1xDHT22 Temperature & Humidity Sensor Board
2xKY-040 Rotary Encoder
1xHC-SR501 PIR Motion Sensor
1xMQ-2 Gas Sensor
1xScrew Terminal
# Pins: 15
Spacing: 5.08 mm
1xScrew Terminal
# Pins: 7
Spacing: 5.08 mm
2xPin Connectors
Type: female, print
# Pins: 19
Spacing: 2.54 mm
3xResistor
1xCapacitor
Type: Elec
Capacity: 2000u
6xCapacitor
Type: Ceramic
Capacity: 100n
1xRibbon Cable

Part 4 – Software

As the utilized WS2812B LEDs are becoming pretty popular in the Maker Community, there are a lot of software libraries available to control them. I went with the FastLED library as it is one of the most popular ones and features simple but powerful functionalities.

The dense hexagonal arrangement of LEDs is not only a big advantage but also a major drawback on the software side. The LEDs are either used as strips or as rectangular LED matrices. A custom calculation is needed to map the position of each LED to a commonly used X/Y position. A simple LUT and a view calculations did the job in the end.

static const int linearIndices[] = {
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,21,20,19,18,17,16,15,14,13,12,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,23,24,25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,49,48,47,46,45,44,43,42,41,40,39,38,37,36,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,265,264,263,262,261,260,259,258,257,256,255,254,253,252,251,250,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,294,293,292,291,290,289,288,287,286,285,284,283,282,281,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,295,296,297,298,299,300,301,302,303,304,305,306,307,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,319,318,317,316,315,314,313,312,311,310,309,308,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,320,321,322,323,324,325,326,327,328,329,330,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};

I further adapted the Adafruit_GFX, Framebuffer_GFX, and FastLED_NeoMatrix libraries for my project. Basic graphic primitives like rendering lines, shapes, or text became possible. Additionally, I developed custom letters to display text on the device using the least amount of LEDs possible.

Next, all the sensors had to be included. Some were more straightforward than others.
The motion sensor has a digital output, which can be configured to stay HIGH for a specified amount of seconds once it registers a movement. In combination with a UTC time, this turns the display into a custom nightlight.
The audio sensor offers simple analog and digital output which roughly reflects the environment’s noisiness. Unfortunately, the measurements are not that exact. So in the end, I implemented a simple clap and double clap event that can be used to activate the display or switch its mode.
The gas sensor is a bit more difficult. It offers a digital and an analog pin that changes its output value depending on the gas (e.g. Methan) concentration in the air. But to do so, the internal heating wire needs to heat up for a certain amount of time before the measurements are correct. The module’s 5V pin is therefore switched on by the microcontroller in defined intervals, to regularly check the environment’s gas concentration.
The temperature and humidity sensor requires serial communication over a single bus to receive the data. Fortunately, the Adafruit DHT-sensor-library does this job for me, so it is easy to receive the data.
I had the most problems with the two rotary encoders. Finding a proper encoder library that deals with debouncing and uses interrupts cost a lot of trial and error. In the end, integers for both rotary values as well as interrupts when pressing the knobs are implemented.

In the end, I have a big library that offers a lot of functionality for the device. I am going to combine this into some automation (clap to turn on display) and a web interface. This can be used to trigger different functionalities remotely and to display the data I want.