# Wireless LED Juggling Balls with ESP-NOW

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/140/447/medium800/leds_juggleshot.jpg?1760483351)

https://youtu.be/mhGMhfS8fTc

Create your own interactive, wireless musical LED juggling balls using CircuitPython and the low latency ESP-NOW protocol.

These juggling balls shine bright with NeoPixels, and can wirelessly trigger MIDI notes based on catch detection with the LIS3DH accelerometer, and a Feather board to bridge messages to your computer or USB Host synthesizer. You can code any note pattern or interaction you like in CircuitPython.

While there are commercially available LED juggling props, there are none that send MIDI messages to a host computer or bridge are currently available, so let's make our own!

This project was inspired by [this performance](https://www.instagram.com/p/DIvtlzTMWxp/) by renowned juggler Jay Gilligan.

&nbsp;

Info: For an introduction to ESP-NOW in CircuitPython, check out [this guide](https://learn.adafruit.com/esp-now-in-circuitpython).

## Parts
These are the parts you'll need per juggling ball:

Featured
### Adafruit QT Py S3 with 2MB PSRAM WiFi Dev Board with STEMMA QT

[Adafruit QT Py S3 with 2MB PSRAM WiFi Dev Board with STEMMA QT](https://www.adafruit.com/product/5700)
The ESP32-S3 has arrived in QT Py format - and what a great way to get started with this powerful new chip from Espressif! With dual 240 MHz cores, WiFi and BLE support, and native USB, this QT Py is great for powering your IoT projects. Now we even have this powerhouse of a board with built...

In Stock
[Buy Now](https://www.adafruit.com/product/5700)
[Related Guides to the Product](https://learn.adafruit.com/products/5700/guides)
![Angled shot of purple, square-shaped microcontroller.](https://cdn-shop.adafruit.com/640x480/5700-06.jpg)

Featured
### Adafruit LIS3DH Triple-Axis Accelerometer (+-2g/4g/8g/16g)

[Adafruit LIS3DH Triple-Axis Accelerometer (+-2g/4g/8g/16g)](https://www.adafruit.com/product/2809)
The **LIS3DH** &nbsp;is a very popular low power **triple-axis accelerometer**. It's low-cost, but has just about every 'extra' you'd want in an accelerometer:

- Three axis sensing, 10-bit...

In Stock
[Buy Now](https://www.adafruit.com/product/2809)
[Related Guides to the Product](https://learn.adafruit.com/products/2809/guides)
![Top down view of a Adafruit LIS3DH Triple-Axis Accelerometer.](https://cdn-shop.adafruit.com/640x480/2809-08.jpg)

Featured
### Adafruit LiIon or LiPoly Charger BFF Add-On for QT Py

[Adafruit LiIon or LiPoly Charger BFF Add-On for QT Py](https://www.adafruit.com/product/5397)
[Is your QT Py all alone](https://www.adafruit.com/?q=qt+py&sort=BestMatch), lacking a friend to travel the wide world with? When you were a kid you may have learned about the "buddy" system, well this product is kinda like that! A board that will watch your QT...

Out of Stock
[Buy Now](https://www.adafruit.com/product/5397)
[Related Guides to the Product](https://learn.adafruit.com/products/5397/guides)
![Video of a person with white painted nails unplugging a USB cable from a small, black, square-shaped lipo battery breakout board soldered to a similarly shaped microcontroller, which is also connected to a monochrome OLED display breakout. The OLED breakout displays battery and power data.](https://cdn-shop.adafruit.com/product-videos/640x480/5397-05.jpg)

Featured
### Lithium Ion Polymer Battery Ideal For Feathers - 3.7V 400mAh

[Lithium Ion Polymer Battery Ideal For Feathers - 3.7V 400mAh](https://www.adafruit.com/product/3898)
Lithium-ion polymer (also known as 'lipo' or 'lipoly') batteries are thin, light, and powerful. The output ranges from 4.2V when completely charged to 3.7V. This battery has a capacity of **400mAh** for a total of about 1.9 Wh. If you need a larger (or smaller!)...

In Stock
[Buy Now](https://www.adafruit.com/product/3898)
[Related Guides to the Product](https://learn.adafruit.com/products/3898/guides)
![Slim Lithium Ion Polymer Battery 3.7v 400mAh with JST 2-PH connector and short cable](https://cdn-shop.adafruit.com/640x480/3898-05.jpg)

### Clear Adhesive Squares - 6 pack

[Clear Adhesive Squares - 6 pack](https://www.adafruit.com/product/4813)
 **UGlu Dashes** &nbsp;are perfect for a variety of small projects. These adhesive squares provide a stronger bond to most surfaces and are cleaner and easier to remove&nbsp;without the wait, mess, or hassle.

Adheres to a wide variety of surfaces, including, but not limited...

In Stock
[Buy Now](https://www.adafruit.com/product/4813)
[Related Guides to the Product](https://learn.adafruit.com/products/4813/guides)
![ 6 pack of Clear Adhesive Squares](https://cdn-shop.adafruit.com/640x480/4813-01.jpg)

For the PCB version, you'll use two breadboard friendly NeoPixels per ball.

Featured
### Breadboard-friendly RGB Smart NeoPixel - Pack of 5

[Breadboard-friendly RGB Smart NeoPixel - Pack of 5](https://www.adafruit.com/product/1312)
This is the easiest way possible to add small, bright RGB pixels to your project. We took the same technology from our Flora NeoPixels and made them breadboard friendly, with two rows of 3 x 0.1" spaced header on each side for easy soldering, chaining and breadboarding. These ultra-bright...

In Stock
[Buy Now](https://www.adafruit.com/product/1312)
[Related Guides to the Product](https://learn.adafruit.com/products/1312/guides)
![Four glowing Breadboard-friendly RGB Smart NeoPixels wired up on a breadboard](https://cdn-shop.adafruit.com/640x480/1312-00.jpg)

## Non-PCB Version

If you're building the non-PCB version, you'll need these parts:

### STEMMA QT / Qwiic JST SH 4-Pin Cable - 50mm Long

[STEMMA QT / Qwiic JST SH 4-Pin Cable - 50mm Long](https://www.adafruit.com/product/4399)
This 4-wire cable is&nbsp;50mm / 1.9" long and fitted with JST SH female 4-pin connectors on both ends. Compared with the chunkier JST PH these are 1mm pitch instead of 2mm, but still have a nice latching feel, while being easy to insert and remove.

<a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/4399)
[Related Guides to the Product](https://learn.adafruit.com/products/4399/guides)
![Angled of of JST SH 4-Pin Cable.](https://cdn-shop.adafruit.com/640x480/4399-00.jpg)

### Adafruit NeoPixel Driver BFF Add-On for QT Py and Xiao

[Adafruit NeoPixel Driver BFF Add-On for QT Py and Xiao](https://www.adafruit.com/product/5645)
Our QT Py boards are a great way to make very small microcontroller projects that pack a ton of power - and now we have a way for you to quickly add a [strand of NeoPixels](https://www.adafruit.com/category/168) with a 5V level shifter and a detachable JST PH connector. It's an...

In Stock
[Buy Now](https://www.adafruit.com/product/5645)
[Related Guides to the Product](https://learn.adafruit.com/products/5645/guides)
![Video of a driver board lighting up an RGBW half-meter long LED strip.](https://cdn-shop.adafruit.com/product-videos/640x480/5645-02.jpg)

### Adafruit NeoPixel LED Strip with 3-pin JST PH 2mm Connector

[Adafruit NeoPixel LED Strip with 3-pin JST PH 2mm Connector](https://www.adafruit.com/product/3919)
Plug in and glow, this **Adafruit NeoPixel LED Strip with JST PH Connector** has **30 total LEDs** in a **"60 LED per meter" spacing** , and is 0.5 meters long, in classy Adafruit Black. Its not too long, but has lots of glowing action.<a...></a...>

Out of Stock
[Buy Now](https://www.adafruit.com/product/3919)
[Related Guides to the Product](https://learn.adafruit.com/products/3919/guides)
![Adafruit NeoPixel LED Strip with 3-pin JST PH Connector lit up rainbow](https://cdn-shop.adafruit.com/640x480/3919-01.jpg)

### JST PH 2mm 3-pin Plug-Plug Cable - 100mm long

[JST PH 2mm 3-pin Plug-Plug Cable - 100mm long](https://www.adafruit.com/product/4336)
This cable is a little over 100mm / 4" long&nbsp;and fitted with JST-PH 3-pin connectors on either end.&nbsp;

We dig the solid and compact nature of these connectors and the latch that keeps the cable from coming apart easily. We're carrying these to <a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/4336)
[Related Guides to the Product](https://learn.adafruit.com/products/4336/guides)
![Angled shot of JST PH 3-pin Plug-Plug Cable - 100mm long.](https://cdn-shop.adafruit.com/640x480/4336-01.jpg)

### Extra-long break-away 0.1" 16-pin strip male header (5 pieces)

[Extra-long break-away 0.1" 16-pin strip male header (5 pieces)](https://www.adafruit.com/product/400)
Breakaway header is like the duct tape of electronics, and this header is one better with extra long pins on **both** sides. This makes it great for connecting things together that have two sockets - especially solderless breadboards. [We also...](https://www.adafruit.com/products/266)

In Stock
[Buy Now](https://www.adafruit.com/product/400)
[Related Guides to the Product](https://learn.adafruit.com/products/400/guides)
![5 pieces of Extra-long break-away 0.1 inch 16-pin strip male header](https://cdn-shop.adafruit.com/640x480/400-03.jpg)

### 36-pin 0.1" Female header - pack of 5!

[36-pin 0.1" Female header - pack of 5!](https://www.adafruit.com/product/598)
Female header is like the duct tape of electronics. Its great for connecting things together, soldering to perf-boards, sockets for wires or break-away header, etc. We go through these guys real fast, and thought that given how handy they are, we'd offer them in a pack of five!  
<br...></br...>

Out of Stock
[Buy Now](https://www.adafruit.com/product/598)
[Related Guides to the Product](https://learn.adafruit.com/products/598/guides)
![pack of five, 36-pin 0.1 inch Female header](https://cdn-shop.adafruit.com/640x480/598-04.jpg)

Featured
### Adafruit ESP32-S3 Reverse TFT Feather

[Adafruit ESP32-S3 Reverse TFT Feather](https://www.adafruit.com/product/5691)
Like Missy Elliot, we like to ["put our [Feather] down, flip it and reverse it"](https://www.youtube.com/watch?v=cjIvu7e6Wq8)&nbsp;and that's exactly what we've done with this new development board. It's basically our **<a...></a...>**

In Stock
[Buy Now](https://www.adafruit.com/product/5691)
[Related Guides to the Product](https://learn.adafruit.com/products/5691/guides)
![Video of a rectangular microcontroller with a TFT display. A pink manicured finger presses each of the tactile buttons, which are recognized on the TFT display.](https://cdn-shop.adafruit.com/product-videos/640x480/5691-05.jpg)

# Wireless LED Juggling Balls with ESP-NOW

## Printed Juggling Balls

![](https://cdn-learn.adafruit.com/assets/assets/000/140/369/medium800/leds_juggle-8494.jpg?1760135701)

Print as many of these as you can juggle! I designed them with a modified tennis ball/baseball pattern of two saddles that fit together, in order to print nicely on an FDM printer. They have a set of interlocking nubs that pop the halves together, and can be opened by prying and pulling one half out of the other when needed.

These are 68mm diameter balls -- you can scale them up or down to suit your juggling ball preference.&nbsp;

Translucent PLA works very well, diffuses the light nicely, and is very strong when printed with solid walls as these are. You can also experiment with PETG for even more strength.

If you like, you can use two rubber bands or o-rings in the grooves for added security.

![](https://cdn-learn.adafruit.com/assets/assets/000/140/335/medium800/leds_juggle-8489.jpeg?1760119068)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/273/medium800/leds_juggleballrender.jpg?1759884674)

[Juggling Ball STLs](https://cdn-learn.adafruit.com/assets/assets/000/140/274/original/juggling-ball_v1.0.zip?1759884731)
[Juggling Ball 3MF](https://cdn-learn.adafruit.com/assets/assets/000/140/275/original/juggling_ball_v1.0.3mf.zip?1759884773)
[Juggling Ball STEP](https://cdn-learn.adafruit.com/assets/assets/000/140/276/original/juggling-ball_v1.0.stp.zip?1759884956)
## Settings for FDM PLA printer

- 0.2mm Strength
- 3mm brim for bed adhesion
- 6 wall loops (prints solid at these dimensions)
- Fuzzy skin applied selectively to outer panels only (for better grip)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/277/medium800/leds_fuzzy.jpg?1759894153)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/331/medium800/leds_juggle-8466.jpg?1760118459)

# Wireless LED Juggling Balls with ESP-NOW

## CircuitPython

[CircuitPython](https://github.com/adafruit/circuitpython) is a derivative of [MicroPython](https://micropython.org) designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the **CIRCUITPY** drive to iterate.

## CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython running on your board.

There are two versions of this board: one with 8MB Flash/No PSRAM and one with 4MB Flash/2MB PSRAM. Each version has their own UF2 build for CircuitPython. There isn't an easy way to identify which version of the board you have by looking at the board silk. If you aren't sure which version you have, try either build to see which one works.

Warning: 

[Download the latest version of CircuitPython for the 8MB/No PSRAM version of this board via circuitpython.org](https://circuitpython.org/board/adafruit_qtpy_esp32s3_nopsram/)
[Download the latest version of CircuitPython for the 4MB/2MB PSRAM version of this board via circuitpython.org](https://circuitpython.org/board/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/)
 **Click the link above to download the latest CircuitPython UF2 file.**

Save it wherever is convenient for you.

![install_circuitpython_on_most_boards_CircuitPython_downloaded.jpg](https://cdn-learn.adafruit.com/assets/assets/000/102/129/medium640/install_circuitpython_on_most_boards_CircuitPython_downloaded.jpg?1620922559)

![](https://cdn-learn.adafruit.com/assets/assets/000/112/336/medium800/adafruit_products_cpTemplatePage.png?1655147030)

Plug your board into your computer, using a known-good data-sync cable, directly, or via an adapter if needed.

Click the **reset** button once (highlighted in red above), and then click it again when you see the **RGB status LED(s)** (highlighted in green above) turn purple (approximately half a second later). Sometimes it helps to think of it as a "slow double-click" of the reset button.

If you do not see the LED turning purple, you will need to reinstall the UF2 bootloader. See the&nbsp; **Factory Reset** &nbsp;page in this guide for details.

On some very old versions of the UF2 bootloader, the status LED turns red instead of purple.

For this board, tap reset and wait for the LED to turn purple, and as soon as it turns purple, tap reset again. The second tap needs to happen while the LED is still purple.

Once successful, you will see the **RGB status LED(s)** turn green (highlighted in green above), and a disk drive ending in " **...BOOT**" should appear on your host computer. If you see red, try another port, or if you're using an adapter or hub, try without the hub, or different adapter or hub.

If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!

A lot of people end up using charge-only USB cables and it is very frustrating! **Make sure you have a USB cable you know is good for data sync.**

Info: 

You will see a new disk drive appear called **QTPYS3**** BOOT**.

Drag the **adafruit\_circuitpython\_etc.uf2** file to **QTPYS3BOOT**.

![adafruit_products_drag_drop.png](https://cdn-learn.adafruit.com/assets/assets/000/112/384/medium640/adafruit_products_drag_drop.png?1655220237)

Copy or drag the UF2 file you downloaded to the **BOOT** drive.

The **BOOT** drive will disappear and a new disk drive called **CIRCUITPY** will appear.

That's it!

![install_circuitpython_on_most_boards_CIRCUITPY.jpg](https://cdn-learn.adafruit.com/assets/assets/000/102/130/medium640/install_circuitpython_on_most_boards_CIRCUITPY.jpg?1620923145)

# Wireless LED Juggling Balls with ESP-NOW

## Code the Juggling Balls

## Download the Project Bundle

Your project will use a specific set of CircuitPython libraries, and the&nbsp; **code.py** &nbsp;file. To get everything you need, click on the&nbsp; **Download Project Bundle** &nbsp;link below, and uncompress the .zip file.

Connect your computer to the board via a known good USB power+data cable. A new flash drive should show up as&nbsp; **CIRCUITPY**.

Drag the contents of the uncompressed bundle directory onto your board&nbsp; **CIRCUITPY** &nbsp;drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/ESP-NOW_Juggling/esp-now-midi-ball.py

## How the Juggling Ball Code Works

The ESP-NOW MIDI Juggling Ball code transforms a QT Py ESP32-S3 with an accelerometer into a wireless musical instrument that communicates with a central bridge device. Here's how the key components work together:

### Configuration and Setup

Each ball needs a unique identity. At the top of the code, set the DEVICE\_ID to either "`ball_A"`, "`ball_B`", "`ball_C`", etc. This determines which ball you're programming and automatically assigns its default LED color from the `ALL_COLORS` list - pink for Ball A, green for Ball B, and blue for Ball C.

The code also includes a configurable `SLEEP_AFTER` timer (default 30 seconds) that puts the ball into a low-power mode when inactive. This conserves battery while keeping the accelerometer active for instant wake-up on the next catch.

### Accelerometer Tap Detection

The LIS3DH accelerometer is the heart of the catch detection system. During initialization, the code tries both common I2C addresses (`0x18` and `0x19`). The accelerometer is configured for ±2G sensitivity range and a tap threshold of 90, which provides good sensitivity for juggling catches without triggering on gentle movements.

When you catch the ball, the accelerometer's built-in tap detection hardware recognizes the sudden deceleration and sets a flag. The main loop checks this flag every 10 milliseconds using the `check_tap()` function, providing fast response times.

To prevent false triggers during power-up and avoid multiple triggers from a single catch, the code includes:

- Startup protection: Ignores taps for 0.5 seconds after boot while clearing initialization noise
- Debouncing: Requires 0.3 seconds between valid taps to prevent double-triggering

### ESP-NOW Wireless Communication

The balls communicate wirelessly using ESP-NOW, which provides low-latency peer-to-peer messaging. The code uses broadcast mode (MAC address FF:FF:FF:FF:FF:FF) so all devices on the network receive each message, but each ball only responds to messages addressed to its specific `DEVICE_ID` coming from the Bridge Feather.

When a catch is detected, the ball sends a `TRIGGER` message to the bridge in this format:

`TRIGGER|ball_A|tap|123.4`

The bridge receives this message and converts it to MIDI notes that play on your synthesizer or computer.

### LED Feedback

Each ball has two NeoPixels that provide visual feedback:

- Solid color: Shows the ball's current color (which can be cycled remotely)
- White flash: Brief 150ms flash when a catch is detected, confirming the tap was registered, and because it looks cool

The LED colors can be changed wirelessly by pressing buttons on the bridge device. When the ball receives a `COLOR` message, it cycles to the next color in the `ALL_COLORS` list and responds with a battery status report.

### Battery Monitoring

The ball monitors battery voltage through the analog pin A2 with a voltage divider circuit coming from the battery BFF. The `get_battery_voltage()` function reads the raw ADC value and converts it to actual voltage using the formula:

`voltage = (raw_value / 65536) * 2 * 3.3`

This voltage is reported to the bridge whenever the ball changes color, allowing you to monitor battery levels on the bridge's display screen. The bridge shows voltages in the format "3.7V" next to each ball's name.

### Main Loop Operation

The main loop runs every 10 milliseconds and performs these checks in order:

- Sleep timeout check: If inactive for too long, enter sleep mode
- Tap detection: Check for accelerometer tap and wake if sleeping
- Message handling: Process incoming ESP-NOW messages from the bridge
  - `COLOR` messages: Cycle to next LED color and report battery status
  - `TRIGGER` messages: (commented out in current code) Could provide inter-ball visual feedback for future use, such as most recently triggered ball coloring the other balls

## Message Protocol

The ball uses a simple pipe-delimited message format for all ESP-NOW communication:

**Outgoing Messages**

- `TRIGGER|ball_A|tap|123.4` - Catch detected
- `BATTERY|ball_A|3.75|EE0010|123.4` - Battery report with voltage and current color (hex)

**Incoming Messages**

- `COLOR|ball_A|next `- Command to cycle to next color

# Wireless LED Juggling Balls with ESP-NOW

## Assemble Juggling Balls

![](https://cdn-learn.adafruit.com/assets/assets/000/140/381/medium800/leds_juggle-8509.jpg?1760135920)

## QT Py Header Pins

Solder header pins under the QT Py as shown here.

![](https://cdn-learn.adafruit.com/assets/assets/000/140/332/medium640/leds_juggle-8487.jpg?1760118493)

## NeoPixel BFF Stacking Headers

Solder stacking headers under the NeoPixel BFF as shown here. The pins should protrude only 5mm above the board surface in order to proved extra space between boards to fit the battery.

Note: added pin plastic shown in the second photo is unnecessary, as it didn't provide the exact extra spacing I'd hoped it would!

![](https://cdn-learn.adafruit.com/assets/assets/000/140/333/medium640/leds_juggle-8488.jpg?1760118544)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/334/medium640/leds_juggle-8486.jpg?1760118555)

## Connect QT Py and NeoPixel BFF

Press the QT Py into the NeoPixel BFF header pins with the USB port and NeoPixel JST at the same end.

![](https://cdn-learn.adafruit.com/assets/assets/000/140/336/medium640/leds_juggle-8485.jpg?1760119437)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/337/medium640/leds_juggle-8484.jpg?1760119447)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/338/medium640/leds_juggle-8483.jpg?1760119455)

## Plug In NeoPixel Strip

Feed the cable through and plug in the JST connector as shown.

![](https://cdn-learn.adafruit.com/assets/assets/000/140/339/medium640/leds_juggle-8482.jpg?1760119558)

## LiPoly Charger BFF

To power the circuit (and provide a convenient on/off switch) we'll use the LiPoly Charger BFF.

Solder header sockets underneath as shown.

![](https://cdn-learn.adafruit.com/assets/assets/000/140/340/medium640/leds_juggle-8480.jpg?1760119617)

## Battery Stack

Fit the battery between the QT Py and NeoPixel BFF as shown, feeding the cable through.

Connect the LiPoly BFF onto the header pins, making sure to orient the JST connector at the same end as the NeoPixel BFF connector and QT Py USB connector as shown.

**NOTE: it's easy to reverse the board orientation and risk frying everything, so triple check the orientation before proceeding!**

Turn the power switch to 'OFF", then plug the battery cable into the LiPoly BFF.&nbsp;

![](https://cdn-learn.adafruit.com/assets/assets/000/140/341/medium640/leds_juggle-8479.jpg?1760119923)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/342/medium640/leds_juggle-8478.jpg?1760119932)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/343/medium640/leds_juggle-8477.jpg?1760120066)

## LIS3DH Attachment and Connection

Attach the LIS3DH accelerometer/tap detector breakout board to the plastic header using an adhesive square. It's a good idea to clean off both surfaces with isopropyl alcohol first to remove any fingerprint grease.

Then, connect the short STEMMA QT cable to it and the QT Py as shown.

![](https://cdn-learn.adafruit.com/assets/assets/000/140/344/medium640/leds_juggle-8476.jpg?1760120160)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/345/medium640/leds_juggle-8475.jpg?1760120198)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/346/medium640/leds_juggle-8471.jpg?1760120349)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/347/medium640/leds_juggle-8472.jpg?1760120382)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/358/medium800/leds_juggle-8473.jpg?1760120446)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/361/medium800/leds_juggle-8470.jpg?1760121929)

Info: You can cut your NeoPixel strip to any length you like -- I chose 10 here. If you need to solder on JST connector to the remainders, check out [this page](https://learn.adafruit.com/make-it-glow-how-to-solder-neopixels-a-beginners-guide/soldering-strips).

![](https://cdn-learn.adafruit.com/assets/assets/000/140/362/medium800/leds_juggle-8469.jpg?1760121935)

## Put the Circuit in the Ball

Wrap the short NeoPixel strand around the circuit and fit it into one half of the ball.

Turn on the power, add some optional cushioning, then snap the second half into place.

&nbsp;

![](https://cdn-learn.adafruit.com/assets/assets/000/140/363/medium640/leds_juggle-8468.jpg?1760121951)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/370/medium640/leds_juggle-8504.jpg?1760135786)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/371/medium640/leds_juggle-8505.jpg?1760135801)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/372/medium640/leds_juggle-8506.jpg?1760135810)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/373/medium640/leds_juggle-8507.jpg?1760135822)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/378/medium800/leds_juggle-8508.jpg?1760135840)

## Sleep

The code will put the board into light sleep mode after 30 seconds of inactivity -- a tap detection event will wake up the NeoPixels and ESP-NOW radio.

![](https://cdn-learn.adafruit.com/assets/assets/000/140/367/medium800/leds_juggle-8466.jpg?1760122064)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/379/medium800/leds_juggle-8509.jpg?1760135898)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/380/medium800/leds_juggle-8510.jpg?1760135906)

# Wireless LED Juggling Balls with ESP-NOW

## Juggling Bridge

![](https://cdn-learn.adafruit.com/assets/assets/000/140/461/medium800/leds_juggle-8512.jpg?1760497348)

The bridge device is used to receive catch trigger messages from the juggling balls and then send USB MIDI messages to the host computer or synth.

These next pages cover prepping the Feather ESP32-S2 or S3 Reverse TFT, coding it, and 3D printing an optional case.

# Wireless LED Juggling Balls with ESP-NOW

## Install CircuitPython

[CircuitPython](https://github.com/adafruit/circuitpython) is a derivative of [MicroPython](https://micropython.org) designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the **CIRCUITPY** drive to iterate.

## CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython running on your board.

[Download the latest version of CircuitPython for this board via circuitpython.org](https://circuitpython.org/board/adafruit_feather_esp32s3_reverse_tft/)
 **Click the link above to download the latest CircuitPython UF2 file.**

Save it wherever is convenient for you.

![install_circuitpython_on_most_boards_CircuitPython_downloaded.jpg](https://cdn-learn.adafruit.com/assets/assets/000/102/129/medium640/install_circuitpython_on_most_boards_CircuitPython_downloaded.jpg?1620922559)

![](https://cdn-learn.adafruit.com/assets/assets/000/119/387/medium800/adafruit_products_resetWithNeo.jpg?1678478978)

Plug your board into your computer, using a known-good data-sync cable, directly, or via an adapter if needed.

Double-click the **reset** button (highlighted in red above), and you will see the **RGB status LED(s)** turn green (highlighted in green above). If you see red, try another port, or if you're using an adapter or hub, try without the hub, or different adapter or hub.

For this board, tap reset and wait for the LED to turn purple, and as soon as it turns purple, tap reset again. The second tap needs to happen while the LED is still purple.

If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!

A lot of people end up using charge-only USB cables and it is very frustrating! **Make sure you have a USB cable you know is good for data sync.**

You will see a new disk drive appear called **FTHRS3BOOT**.

&nbsp;

Drag the **adafruit\_circuitpython\_etc.uf2** file to **FTHRS3BOOT**.

![adafruit_products_s3Boot.jpg](https://cdn-learn.adafruit.com/assets/assets/000/118/996/medium640/adafruit_products_s3Boot.jpg?1677603548)

![adafruit_products_copyTos3.jpg](https://cdn-learn.adafruit.com/assets/assets/000/118/997/medium640/adafruit_products_copyTos3.jpg?1677603564)

The **BOOT** drive will disappear and a new disk drive called **CIRCUITPY** will appear.

That's it!

![install_circuitpython_on_most_boards_CIRCUITPY.jpg](https://cdn-learn.adafruit.com/assets/assets/000/102/130/medium640/install_circuitpython_on_most_boards_CIRCUITPY.jpg?1620923145)

# Wireless LED Juggling Balls with ESP-NOW

## Code the Bridge

In order to enable the USB MIDI endpoint on the ESP32-S2 and S3 Feather boards, you'll need to save the **boot.py** file here to the **CIRCUITPY** drive and restart the board.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/ESP-NOW_Juggling/boot.py

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/ESP-NOW_Juggling/esp-now-midi-bridge.py

## Color Messages

Press one of the three buttons on the Feather bridge to cycle between colors per ball.

![](https://cdn-learn.adafruit.com/assets/assets/000/140/382/medium640/leds_juggle-8514.jpg?1760135944)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/383/medium640/leds_juggle-8515.jpg?1760135953)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/384/medium640/leds_juggle-8516.jpg?1760135962)

## How the Bridge Code Works

The ESP-NOW MIDI Bridge runs on an ESP32-S3 Feather with TFT display and serves as the central hub that receives wireless messages from the juggling balls and converts them into MIDI notes for your synthesizer or music software. It also provides a visual status display and allows you to remotely control the balls' LED colors.

### MIDI Note Mapping

The bridge's main job is to translate incoming ball catch messages into outgoing USB MIDI note messages. The note mappings per ball are are stored in the `MIDI_MAPPINGS` dictionary at the top of the code:

```auto
MIDI_MAPPINGS = {
    "ball_A": {
        "notes": [42, 43, 45, 47],  # Sequence of notes
        "color": (238, 0, 16),      # Pink for bridge LED
    },
    ...
}
```

Each ball can be assigned either a single repeating note or a sequence that cycles through. For example, Ball A might play notes 42, 43, 45, 47 in order, while Ball B could play the same note 50 six times before jumping to 62. This creates musical patterns as you juggle - each catch advances to the next note in the sequence.

The bridge keeps track of each ball's current position in its sequence using the `note_positions` dictionary. When a ball is caught, the bridge plays the current note, then increments the position (wrapping back to the start when it reaches the end).

To customize the music: Simply edit the "`notes`" lists in `MIDI_MAPPINGS` to create your own melodies and rhythms. MIDI note numbers range from 0-127, with 60 being middle C.

### USB MIDI Output

During initialization, the bridge scans for available USB MIDI ports and automatically connects to the first output port it finds. This port is typically created when you connect the Feather to a computer running music software (like Ableton, GarageBand, or a DAW) or to a USB MIDI-capable synthesizer, such as the 1010 Music Bento or Blackbox sample station I used in the demonstration video.

The `send_midi_note()` function handles the actual MIDI transmission.

### Incoming Messages

The incoming message elements are parsed by `parse_battery_message()` to extract:

- `device_id`: Which ball is reporting
- `voltage`: Battery voltage (e.g., "3.75")
- `color_hex`: Ball's current LED color in hex

Battery messages update both the voltage display and sync the bridge's color tracking with each ball's actual LED color. This ensures the display always shows accurate status.

### Button Controls for Remote Color Cycling

Three buttons on the Feather let you remotely change each ball's LED color:

- **D0** : Controls Ball A (note: this button has opposite pull direction)
- **D1** : Controls Ball B
- **D2** : Controls Ball C

When you press a button, the `handle_button_presses()`&nbsp;function:

- Sends a `COLOR|ball_X|next `message to the specified ball
- Updates its local `ball_colors` tracking by cycling to the next color in `ALL_COLORS`
- The ball receives the message, changes its LEDs, and responds with a battery report
- The battery report's color field syncs the bridge's display with the ball's new color

This bidirectional color synchronization ensures the display always shows accurate status even if balls are powered on/off or the bridge is restarted.

### Visual Feedback

The bridge has its own onboard NeoPixel that provides visual feedback:

- Blinks briefly in the ball's color (from `MIDI_MAPPINGS`, not the ball's current LED color)
- One quick 20ms flash per catch
- Confirms MIDI notes were sent successfully

This LED uses the fixed "home" colors for each ball rather than their current LED colors, providing consistent visual reference regardless of remote color changes.

### Connection Monitoring

The bridge tracks connection status by recording timestamps in the `connection_status` dictionary whenever a ball sends a message. The display shows:

- Connected (colored dot): Ball sent a message within the last 5 seconds
- Disconnected (gray dot): No message received for 5+ seconds

This timeout accommodates the balls' 30-second sleep mode. If you're juggling, the balls stay awake and the dots remain colored. During breaks, balls may sleep and show gray dots, but they'll reconnect instantly when you start juggling again.

## Main Loop

The main `while True` loop runs every 10 milliseconds and performs these tasks in priority order:

1. Process ESP-NOW messages: Check for incoming TRIGGER or BATTERY messages and handle them immediately
2. Handle button presses: Check for color cycle button presses and send COLOR commands
3. Update display periodically: Refresh the TFT every 2 seconds to show current status

This fast polling ensures low-latency MIDI output - catches are converted to notes within milliseconds of being detected, making the system feel responsive for musical performances.

# Wireless LED Juggling Balls with ESP-NOW

## CAD Files

## 3D Printed Parts

STL files for 3D printing are oriented to print "as-is" on FDM style machines. Parts are designed to 3D print without any support material using PLA filament. Original design source may be downloaded using the links below.

![3d_printing_3D-parts.jpg](https://cdn-learn.adafruit.com/assets/assets/000/129/093/medium640/3d_printing_3D-parts.jpg?1711988789)

[Download STLs.zip](https://cdn-learn.adafruit.com/assets/assets/000/129/115/original/STLs.zip?1712063612)
[Download CAD source](https://cdn-learn.adafruit.com/assets/assets/000/129/116/original/CAD.zip?1712063719)
## CAD Assembly

The Feather ESP32-S2 Reverse TFT Feather board is secured to the enclosure's back cover using both M2 and M2.5 fasteners. The back cover snap fits onto the front cover. The front cover is secured to the battery tray using M2.5 fastener.&nbsp;

![3d_printing_CAD.gif](https://cdn-learn.adafruit.com/assets/assets/000/129/097/medium640thumb/3d_printing_CAD.jpg?1711996563)

## Build Volume

The parts require a 3D printer with a minimum build volume.

- 68mm (X) x 62mm (Y) x 20mm (Z)

![3d_printing_cura-slice.jpg](https://cdn-learn.adafruit.com/assets/assets/000/129/098/medium640/3d_printing_cura-slice.jpg?1711996662)

## Design Source Files

The project assembly was designed in Fusion 360. This can be downloaded in different formats like STEP, STL and more.

Electronic components like Adafruit's boards, displays, connectors and more can be downloaded from the&nbsp;[Adafruit CAD parts GitHub Repo](https://github.com/adafruit/Adafruit_CAD_Parts/).

![3d_printing_5345_Feather_ESP32-S2_TFT_Reverse.jpg](https://cdn-learn.adafruit.com/assets/assets/000/129/099/medium640/3d_printing_5345_Feather_ESP32-S2_TFT_Reverse.jpg?1711996795)

# Wireless LED Juggling Balls with ESP-NOW

## Usage

To use the juggling balls, pop open the shell and turn on each ball using the on/off switch on the Lipoly BFF. Then, close the balls back up.

They'll immediately respond to tap detection on catch (or hard throw, too) and blink the NeoPixels, even if there's no bridge set up yet.

![](https://cdn-learn.adafruit.com/assets/assets/000/140/462/medium640/leds_juggle-8504.jpg?1760500120)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/463/medium640/leds_juggle-8507.jpg?1760500129)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/464/medium640/leds_juggle-8508.jpg?1760500139)

## Bridge Over Troubled MIDI

Plug the bridge into your computer with a software synth or a hardware synth with USB Host MIDI.

You can now trigger MIDI messages with each ball catch.

Press the corresponding button on the bridge to change ball colors.

![](https://cdn-learn.adafruit.com/assets/assets/000/140/465/medium640/leds_juggle-8514.jpg?1760500200)

![](https://cdn-learn.adafruit.com/assets/assets/000/140/466/medium640/leds_juggle-8516.jpg?1760500289)

Start juggling and enjoy the show!

https://youtu.be/mhGMhfS8fTc

When you're done, open up and turn off the balls, then plug them in to USB C to charge them so they'll be ready for the next performance.


## Guide Products

### Adafruit QT Py S3 with 2MB PSRAM WiFi Dev Board with STEMMA QT

[Adafruit QT Py S3 with 2MB PSRAM WiFi Dev Board with STEMMA QT](https://www.adafruit.com/product/5700)
The ESP32-S3 has arrived in QT Py format - and what a great way to get started with this powerful new chip from Espressif! With dual 240 MHz cores, WiFi and BLE support, and native USB, this QT Py is great for powering your IoT projects. Now we even have this powerhouse of a board with built...

In Stock
[Buy Now](https://www.adafruit.com/product/5700)
[Related Guides to the Product](https://learn.adafruit.com/products/5700/guides)
### Adafruit LIS3DH Triple-Axis Accelerometer (+-2g/4g/8g/16g)

[Adafruit LIS3DH Triple-Axis Accelerometer (+-2g/4g/8g/16g)](https://www.adafruit.com/product/2809)
The **LIS3DH** &nbsp;is a very popular low power **triple-axis accelerometer**. It's low-cost, but has just about every 'extra' you'd want in an accelerometer:

- Three axis sensing, 10-bit...

In Stock
[Buy Now](https://www.adafruit.com/product/2809)
[Related Guides to the Product](https://learn.adafruit.com/products/2809/guides)
### Adafruit LiIon or LiPoly Charger BFF Add-On for QT Py

[Adafruit LiIon or LiPoly Charger BFF Add-On for QT Py](https://www.adafruit.com/product/5397)
[Is your QT Py all alone](https://www.adafruit.com/?q=qt+py&sort=BestMatch), lacking a friend to travel the wide world with? When you were a kid you may have learned about the "buddy" system, well this product is kinda like that! A board that will watch your QT...

Out of Stock
[Buy Now](https://www.adafruit.com/product/5397)
[Related Guides to the Product](https://learn.adafruit.com/products/5397/guides)
### Lithium Ion Polymer Battery Ideal For Feathers - 3.7V 400mAh

[Lithium Ion Polymer Battery Ideal For Feathers - 3.7V 400mAh](https://www.adafruit.com/product/3898)
Lithium-ion polymer (also known as 'lipo' or 'lipoly') batteries are thin, light, and powerful. The output ranges from 4.2V when completely charged to 3.7V. This battery has a capacity of **400mAh** for a total of about 1.9 Wh. If you need a larger (or smaller!)...

In Stock
[Buy Now](https://www.adafruit.com/product/3898)
[Related Guides to the Product](https://learn.adafruit.com/products/3898/guides)
### Clear Adhesive Squares - 6 pack

[Clear Adhesive Squares - 6 pack](https://www.adafruit.com/product/4813)
 **UGlu Dashes** &nbsp;are perfect for a variety of small projects. These adhesive squares provide a stronger bond to most surfaces and are cleaner and easier to remove&nbsp;without the wait, mess, or hassle.

Adheres to a wide variety of surfaces, including, but not limited...

In Stock
[Buy Now](https://www.adafruit.com/product/4813)
[Related Guides to the Product](https://learn.adafruit.com/products/4813/guides)
### Breadboard-friendly RGB Smart NeoPixel - Pack of 5

[Breadboard-friendly RGB Smart NeoPixel - Pack of 5](https://www.adafruit.com/product/1312)
This is the easiest way possible to add small, bright RGB pixels to your project. We took the same technology from our Flora NeoPixels and made them breadboard friendly, with two rows of 3 x 0.1" spaced header on each side for easy soldering, chaining and breadboarding. These ultra-bright...

In Stock
[Buy Now](https://www.adafruit.com/product/1312)
[Related Guides to the Product](https://learn.adafruit.com/products/1312/guides)
### STEMMA QT / Qwiic JST SH 4-Pin Cable - 50mm Long

[STEMMA QT / Qwiic JST SH 4-Pin Cable - 50mm Long](https://www.adafruit.com/product/4399)
This 4-wire cable is&nbsp;50mm / 1.9" long and fitted with JST SH female 4-pin connectors on both ends. Compared with the chunkier JST PH these are 1mm pitch instead of 2mm, but still have a nice latching feel, while being easy to insert and remove.

<a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/4399)
[Related Guides to the Product](https://learn.adafruit.com/products/4399/guides)
### Adafruit NeoPixel Driver BFF Add-On for QT Py and Xiao

[Adafruit NeoPixel Driver BFF Add-On for QT Py and Xiao](https://www.adafruit.com/product/5645)
Our QT Py boards are a great way to make very small microcontroller projects that pack a ton of power - and now we have a way for you to quickly add a [strand of NeoPixels](https://www.adafruit.com/category/168) with a 5V level shifter and a detachable JST PH connector. It's an...

In Stock
[Buy Now](https://www.adafruit.com/product/5645)
[Related Guides to the Product](https://learn.adafruit.com/products/5645/guides)

## Related Guides

- [Adafruit LiIon or LiPoly Charger BFF Add-On for QT Py](https://learn.adafruit.com/adafruit-qt-py-charger-bff.md)
- [Adafruit QT Py ESP32-S3](https://learn.adafruit.com/adafruit-qt-py-esp32-s3.md)
- [Adafruit NeoPixel Driver BFF](https://learn.adafruit.com/adafruit-neopixel-driver-bff.md)
- [Adafruit ESP32-S3 Reverse TFT Feather](https://learn.adafruit.com/esp32-s3-reverse-tft-feather.md)
- [Adafruit Metro RP2350](https://learn.adafruit.com/adafruit-metro-rp2350.md)
- [No-Code, No-Solder Monitoring For Perfect Bread](https://learn.adafruit.com/no-code-no-solder-temperature-monitoring-with-wippersnapper-offline.md)
- [DC, Servo, Stepper Motors and Solenoids with the Pico](https://learn.adafruit.com/use-dc-stepper-servo-motor-solenoid-rp2040-pico.md)
- [NeoPixel Rotary Fidget](https://learn.adafruit.com/neopixel-rotary-fidget.md)
- [PyBadge Conference Badge With Unicode Fonts](https://learn.adafruit.com/pybadge-conference-badge-multi-language-unicode-fonts.md)
- [MEMENTO Wireless Remote with TouchOSC](https://learn.adafruit.com/memento-wireless-remote.md)
- [LED Matrix Sports Scoreboard](https://learn.adafruit.com/led-matrix-sports-scoreboard.md)
- [Sparkle Motion Dance Shoes](https://learn.adafruit.com/sparkle-motion-dance-shoes.md)
- [CircuitPython Libraries and Jupyter Notebook on any Computer with MCP2221](https://learn.adafruit.com/jupyter-on-any-computer-with-circuitpython-libraries-and-mcp2221.md)
- [Adafruit ESP32-S3 TFT Feather](https://learn.adafruit.com/adafruit-esp32-s3-tft-feather.md)
- [QT Py CH32V203 eInk / ePaper Daily Calendar and Clock](https://learn.adafruit.com/ch32v203-eink-epaper-calendar-and-clock.md)
- [Adafruit QT Py Activity Timer and Hydration Reminder](https://learn.adafruit.com/qt-py-activity-timer-and-hydration-reminder.md)
- [reef-pi Guide 6: pH Monitoring](https://learn.adafruit.com/reef-pi-guide-7-ph-monitoring.md)
- [Buttermilk Boat with Circuit Playground Express](https://learn.adafruit.com/boat-with-circuit-playground-express.md)
- [No-Code Indoor Grow Monitor with PPFD and VPD Measurements](https://learn.adafruit.com/no-code-indoor-grow-monitor.md)
