Once you have CircuitPython installed on your device, you can access the code and necessary libraries by downloading the Project Bundle.

To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

import time
import board
import digitalio
import simpleio
import adafruit_nunchuk
import adafruit_pca9685
import adafruit_motor.servo

PITCH_OFFSET = 45  # The offset for the pitch
PITCH_RANGE = 90  # The range the servo can rotate up and down in degrees
YAW_RANGE = 90  # The range the servo can rotate side to side in degrees

# STEMMA QT 3V needs to be activated
i2c_power = digitalio.DigitalInOut(board.I2C_POWER)
i2c = board.I2C()  # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C()  # For using the built-in STEMMA QT connector on a microcontroller

wing = adafruit_pca9685.PCA9685(i2c)
wing.frequency = 50
servo_yaw = adafruit_motor.servo.Servo(wing.channels[0])
servo_pitch = adafruit_motor.servo.Servo(wing.channels[1])
laser = wing.channels[2]

nc = adafruit_nunchuk.Nunchuk(i2c)

# Pre-calculate the angles
min_yaw_angle = YAW_RANGE / 2
max_yaw_angle = 180 - (YAW_RANGE / 2)
min_pitch_angle = PITCH_OFFSET + (PITCH_RANGE / 2)
max_pitch_angle = PITCH_OFFSET + 180 - (PITCH_RANGE / 2)

pitch_inputs = [0, 255]
if INVERT_PITCH:  # Swap the Min and Max Values
    pitch_inputs[0], pitch_inputs[1] = pitch_inputs[1], pitch_inputs[0]

brightness = 0xFFFF  # Initial brightness value

while True:
    x, y = nc.joystick
    servo_yaw.angle = simpleio.map_range(255 - x, 0, 255, min_yaw_angle, max_yaw_angle)
    servo_pitch.angle = simpleio.map_range(
        y, pitch_inputs[0], pitch_inputs[1], min_pitch_angle, max_pitch_angle
    ax = nc.acceleration[0]

    if nc.buttons.Z:  # Z-Button sets laser PWM to current brightness
        laser.duty_cycle = brightness
    elif nc.buttons.C:  # C-Button sets laser brightness to value of the nunchuck roll position
        brightness = int(simpleio.map_range(ax, 250, 750, 0, 0xFFFF))
        laser.duty_cycle = brightness
    else:  # No button pressed sets laser to off
        laser.duty_cycle = 0

Upload the Code and Libraries to the ESP32-S2 Feather

After downloading the Project Bundle, plug your ESP32-S2 Feather into the computer USB port. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the ESP32-S2 Feather's CIRCUITPY drive. 

  • lib folder
  • code.py

Your ESP32-S2 Feather CIRCUITPY drive should look like this after copying the lib folder and code.py file.


There's a few variables you can adjust in code.py which will be covered in this section.

PITCH_OFFSET is the offset of the pitch range from the horizontal axis. Increasing this will tilt the servo more vertical and decreasing will make it more horizontal.

PITCH_RANGE is the full range of motion that the vertically tilting servo will move. If you want to increase the range of motion, you can increase this value and if you want to decrease it to make the laser easier to control, you can decrease it.

YAW_RANGE is the full range of motion that the horizontally panning servo will move. This should be close to the PITCH_RANGE setting so the pointer moves in a circle instead of an ellipse.

INVERT_PITCH will invert the direction that vertically moving the stick on the nunchuck  causes the laser to tilt. This is useful if you mount the laser so it is pointing down.


The usage is pretty straightforward, but this section will go over it so you can understand everything it can do.

Moving the joystick on the nunchuck causes the laser to move where it is pointing.

To cause the laser to show, just hold the Z-Button on the nunchuck and let go to turn it off. It is off by default for safety reasons. When the laser appears, it will be at the last brightness setting.

To adjust the brightness, hold the c-button down and twist your wrist. The laser will show while doing this so you can see how bright it is. Currently the brightness setting isn't saved, so resetting the feather will put it back at full brightness.

Ideas for Extending the Project

This project has a lot of possibilities, but here are a few ideas to take it to the next level.

You could build a second one and then have the two communicate over WiFi so it could remotely be controlled.

You could hook a PowerBoost 1000C up so you could make the project battery powered.

You could edit the code to store the last brightness value by using the nvm module.

It might be a bit too slow, but perhaps you could try and use the pan and tilt mechanism as a XY driver to draw patterns with the laser.

Store a series of movements and then have it play those back in a loop so your pet can play for hours unattended.

This guide was first published on Dec 07, 2021. It was last updated on May 18, 2024.

This page (Coding the Nunchuck Laser) was last updated on May 18, 2024.

Text editor powered by tinymce.