AI-Powered CircuitPython Smart Lamp

by alsleben in Circuits > Gadgets

414 Views, 8 Favorites, 0 Comments

AI-Powered CircuitPython Smart Lamp

Laemp.jpg
IMG_7453.jpg

Hi! Welcome to my Instructable! I'm Leonard and this project is the final project for a class at Boston College called "Physical Computing" with Prof. Gallaugher. In this project, you will be making a smart lamp using Huskylens AI and a Raspberry Pi Pico running CircuitPython.

For this project you will need access to a 3D printer and soldering supplies. Even if you don't have access to a 3D printer, today on-demand 3D printing is widely accessible so that is a great option too!

Without further ado, let's make an AI Smart Lamp!

Supplies

IMG_7425 2.jpg
IMG_7424 2.jpg

Electronics Supplies

  1. Adafruit NeoPixel Digital RGB LED Strip 2m
  2. Raspberry Pi Pico RP2040 (No headers)
  3. Huskylens
  4. Micro-B Cable

Assembly Supplies

  1. White PLA for 3D printing
  2. Clear PETG for 3D printing
  3. 2x Heat-Set Inserts M3 x 3mm
  4. 4x Heat Set Inserts M2 x 6mm
  5. 2x M3 x 5mm
  6. 4x M2 x 7mm
  7. 20pcs 3mm x 1mm Neodymium Magnets

Equipment

  1. Soldering Iron
  2. 3D Printer

Soldering and Assembly

IMG_7431 2.jpg
IMG_7433.jpg

Begin by 3D printing all of the files linked below.

A few notes on the 3D Printing:

"Body36" Should be Printed in "Spiral Vase" mode or on 0% infill with no top or bottom shell. This will be our shade so we don't want it to be solid. Depending on how many walls you give this you will have either more or less light diffusion.

All of the 'modules' have inserts for magnets. However I am yet to find magnets strong enough to actually hold together the build, so I ended up only installing the magnets in 'TOPTOP' and 'TOPBOTTOM'.

Assembly

Once all files are printed, begin by installing the heat-set inserts. Use a soldering iron to heat these up and insert them into the plastic. I set my Soldering Iron to 500°F for this. This may be challenging on two of the holes in the bottom case, but I was able to heat the insert with the iron and simply push them in with tweezers.

I next installed the magnets. Here be very careful that the polarity is correct! I just placed the magnets over the holes and hit them in with a mallet.

Next I wrapped the 2m Neopixels strip around the central column. Make sure to start at the bottom with the hole going rightward. Have the wires stick through the hole. I just held the strip in place with some double-sided tape.

That's it for assembling for now! Now we'll wire the gadget.

Wiring

Wiring.png

Solder all the connections shown in the wiring diagram. Be careful to leave copius slack for the huskylens as this will be detachable and enough wire should be available to thake the lens out. I used about 8in for each wire. I would already have the lamp semi-assembled to get an idea where the wires need to go.


Final Assembly

POSTERLAMP.png
IMG_7493.jpg

Now put all the pieces together. First screw in the Pi Pico and Hukylens into the 'BOTTOM' and 'TOPTOP' respectively. Then run the wires through the middle to the HuskyLens. Follow the diagram above for this. You can also begin glueing all the pieces together. I'd first glue the 'Body36' to the 'MIDDLE' and then the other parts. Don't glue the TOPTOP to the TOPBOTTOM, as this should be magnetic and detachable.

Coding

I want to give a special thanks to u/hktan125 on Github for his guide for CircuitPython with the Hyskylens here which was instrumental in developing this code.


Here's a walkthorugh of the code with a link below:


Code

1. NeoPixel and HuskyLens Setup:

strip = neopixel.NeoPixel(board.GP10, 60, brightness=0.75, auto_write=False)
hl = HuskyLensLibrary('UART', TX=board.GP8, RX=board.GP9)

- Initializes the NeoPixel strip with 60 LEDs connected to pin `GP10`.

- Sets up a HuskyLens sensor to communicate via UART, using pins `GP8` (TX) and `GP9` (RX).

- `auto_write=False` allows us to make batch updates to the LEDs using `strip.show()`.


2. Default Face Recognition Algorithm:

hl.algorithm("ALGORITHM_FACE_RECOGNITION")

- Configures HuskyLens to use the face recognition algorithm as the initial mode.


3. Flags and Defaults:

active = False
OFF = (0, 0, 0)
MOVING_BLUE = BLUE
WHITE = (255, 255, 255)
last_color = OFF

- `active`: Indicates whether the system is in face recognition mode (`False`) or color recognition mode (`True`).

- `OFF` and `MOVING_BLUE`: Define the colors for inactive and animation states.

- `last_color`: Tracks the most recent color displayed.


Helper Functions

1. Smooth Color Transition:


def smooth_fade(current_color, target_color, steps=50, delay=0.01):


- Smoothly transitions the LEDs from one color to another by interpolating the RGB values in `steps` increments.

- `current_color` and `target_color` define the starting and ending colors.

- `steps` determines how many intermediate colors are shown, and `delay` controls the speed.


2. **Dim a Color:**

def dim_color(color, factor=0.5):

- Reduces the brightness of an RGB color by multiplying each component by `factor`.


3. Blue LED Animation:

def moving_blue_animation():

- Lights a single blue LED, moving from the first position (0) to the last (59), then cycles back to the start.

- At each step, turns off all LEDs (`strip.fill(OFF)`) and lights the current position.


Main Loop

1. Face Recognition:

- Checks if the system is in face recognition mode (not `active`).

- Calls `hl.learnedBlocks()` to detect faces recognized by the HuskyLens.


Behavior While No Face Is Detected:

moving_blue_animation()

- If no face is detected or the face ID is not `1`, the `moving_blue_animation` function runs.

- A single blue LED moves up the strip, creating a dynamic cycling animation.


Behavior When Face Is Detected:

if 1 in all_id:
active = True
smooth_fade(OFF, WHITE, steps=100, delay=0.02)
hl.algorithm("ALGORITHM_COLOR_RECOGNITION")

- If face ID `1` is detected:

- Sets `active` to `True` to enable color recognition.

- Smoothly transitions the LEDs from off to white.

- Switches the HuskyLens algorithm to color recognition.


2. Color Recognition:


- Runs when `active` is `True` (face detected).

- Calls `hl.learnedBlocks()` to detect color blocks recognized by the HuskyLens.


Single Color Detected:


first = all_id[0]
smooth_fade(current_color, target_color, steps=100, delay=0.01)
last_color = target_color


- If one or two colors are detected:

- Smoothly fades to the first detected color and updates `last_color`.


No Colors Detected:


dimmed_color = dim_color(last_color, factor=0.5)
smooth_fade(strip[0], dimmed_color, steps=100, delay=0.01)


- If no colors are detected:

- Dims the `last_color` to half brightness and fades the LEDs to that color.


3. Error Handling and Delays:

except KeyError:
smooth_fade(strip[0], WHITE, steps=100, delay=0.01)
finally:
time.sleep(0.5)


- Handles unexpected errors gracefully by transitioning to white.

- Includes a `time.sleep(0.5)` delay to throttle the loop and avoid overloading the system.


How It All Fits Together

1. Inactive State (Face Recognition):

- Displays a cycling blue LED animation while waiting for a face to be detected.


2. Active State (Color Recognition):

- Detects colors and dynamically updates the NeoPixel strip:

- Creates a gradient for multiple colors.

- Smoothly transitions between single colors.

- Dims to half-brightness for the last color when no colors are detected.


3. Smooth Transitions:

- Uses the `smooth_fade` function for aesthetic, visually pleasing updates to the LEDs.

Downloads

All Done!

Lamp in Action

Thanks for following along to this guide! Enjoy your smart lamp!