Build a Light Theremin Using Maker Pi Pico and CircuitPython

Let's build a light Theremin using Raspberry Pi Pico

Introduction

Light theremin is a device that can produce sound tones based on the amount of light received. In this tutorial, we will share how to build a light Theremin using Raspberry Pi Pico – Maker Pi Pico.

Video

This video shows how to build a light Theremin using Maker Pi Pico (Raspberry Pi Pico).

Hardware Preparation

This is the list of items used in the video.

Sample Program

This is the CircuitPython code for Maker Pi Pico (Raspberry Pi Pico) to produce tones depending on the amount of light detected.

#
# Build a Light Theremin Using Raspberry Pi Pico
# (Maker Pi Pico) and CircuitPython
#
# Raspberry Pi Pico
# – [Maker Pi Pico] https://my.cytron.io/p-maker-pi-pico?tracking=idris
# Grove – Light Sensor v1.2
# – https://my.cytron.io/p-grove-light-sensor-v1.2?tracking=idris
#
# Update:
# 9 Feb 2021 – Tested with CircuitPython Pico 6.2.0-beta.1-127-ga10ce39ae
#
from time import sleep
from board import *
from digitalio import DigitalInOut, Direction, Pull
from analogio import AnalogIn
from pwmio import PWMOut
from pitches import tones
from neopixel_write import neopixel_write
sleep(1)
btn1 = DigitalInOut(GP20)
btn1.direction = Direction.INPUT
btn1.pull = Pull.UP
btn2 = DigitalInOut(GP21)
btn2.direction = Direction.INPUT
btn2.pull = Pull.UP
btn3 = DigitalInOut(GP22)
btn3.direction = Direction.INPUT
btn3.pull = Pull.UP
pixel_off = bytearray([0, 0, 0])
pixel_red = bytearray([0, 20, 0])
pixel_green = bytearray([20, 0, 0])
pixel_blue = bytearray([0, 0, 20])
rgb = DigitalInOut(GP28)
rgb.direction = Direction.OUTPUT
neopixel_write(rgb, pixel_off)
light = AnalogIn(GP27)
tempo = 1
melody_start = ['c4', 'g4']
rhythm_start = [8, 8]
def playTone(melody, rhythm):
for tone, length in zip(melody, rhythm):
beeper = PWMOut(GP18, variable_frequency=True)
if tones[tone] != 0:
beeper.duty_cycle = 2 ** 15
beeper.frequency = tones[tone]
sleep(tempo / length)
beeper.deinit()
def map(x, in_min, in_max, out_min, out_max):
if x > in_max:
x = in_max
if x < in_min:
x = in_min
return (xin_min) * (out_maxout_min) / (in_maxin_min) + out_min
playTone(melody_start, rhythm_start)
while btn1.value:
pass
notes_theremin = [
'c3', 'cs3', 'd3', 'ds3', 'e3', 'f3', 'fs3', 'g3', 'gs3', 'a3', 'as3', 'b3',
'c4', 'cs4', 'd4', 'ds4', 'e4', 'f4', 'fs4', 'g4', 'gs4', 'a4', 'as4', 'b4',
'c5', 'cs5', 'd5', 'ds5', 'e5', 'f5', 'fs5', 'g5', 'gs5', 'a5', 'as5', 'b5',
'c6', 'cs6', 'd6', 'ds6', 'e6', 'f6', 'fs6', 'g6', 'gs6', 'a6', 'as6', 'b6',
]
enable_theremin = False
delay = 0.2
while True:
if not btn1.value:
enable_theremin = not enable_theremin
if enable_theremin:
neopixel_write(rgb, pixel_green)
else:
neopixel_write(rgb, pixel_off)
sleep(1)
if enable_theremin:
light_ADC = light.value
note = int(map(light_ADC, 1000, 40000, 0, 47))
print("ADC: {}\tMap: {}\t\tNote: {}".format(light_ADC, note, notes_theremin[note]))
beeper = PWMOut(GP18, variable_frequency=True)
beeper.duty_cycle = 2 ** 15
beeper.frequency = tones[notes_theremin[note]]
sleep(delay)
beeper.deinit()
sleep(0.01)
if not btn2.value:
neopixel_write(rgb, pixel_green)
delay -= 0.01
if delay < 0.1:
neopixel_write(rgb, pixel_red)
delay = 0.1
elif not btn3.value:
neopixel_write(rgb, pixel_green)
delay += 0.01
if delay > 0.2:
delay = 0.2
neopixel_write(rgb, pixel_blue)

view raw
code.py
hosted with ❤ by GitHub

tones = {
'': 0,
'b0': 31,
'c1': 33,
'cs1': 35,
'd1': 37,
'ds1': 39,
'e1': 41,
'f1': 44,
'fs1': 46,
'g1': 49,
'gs1': 52,
'a1': 55,
'as1': 58,
'b1': 62,
'c2': 65,
'cs2': 69,
'd2': 73,
'ds2': 78,
'e2': 82,
'f2': 87,
'fs2': 93,
'g2': 98,
'gs2': 104,
'a2': 110,
'as2': 117,
'b2': 123,
'c3': 131,
'cs3': 139,
'd3': 147,
'ds3': 156,
'e3': 165,
'f3': 175,
'fs3': 185,
'g3': 196,
'gs3': 208,
'a3': 220,
'as3': 233,
'b3': 247,
'c4': 262,
'cs4': 277,
'd4': 294,
'ds4': 311,
'e4': 330,
'f4': 349,
'fs4': 370,
'g4': 392,
'gs4': 415,
'a4': 440,
'as4': 466,
'b4': 494,
'c5': 523,
'cs5': 554,
'd5': 587,
'ds5': 622,
'e5': 659,
'f5': 698,
'fs5': 740,
'g5': 784,
'gs5': 831,
'a5': 880,
'as5': 932,
'b5': 988,
'c6': 1047,
'cs6': 1109,
'd6': 1175,
'ds6': 1245,
'e6': 1319,
'f6': 1397,
'fs6': 1480,
'g6': 1568,
'gs6': 1661,
'a6': 1760,
'as6': 1865,
'b6': 1976,
'c7': 2093,
'cs7': 2217,
'd7': 2349,
'ds7': 2489,
'e7': 2637,
'f7': 2794,
'fs7': 2960,
'g7': 3136,
'gs7': 3322,
'a7': 3520,
'as7': 3729,
'b7': 3951,
'c8': 4186,
'cs8': 4435,
'd8': 4699,
'ds8': 4978,
}

view raw
pitches.py
hosted with ❤ by GitHub

Thank You

References:

Thanks for reading this tutorial. If you have any technical inquiries, please post at Cytron Technical Forum.

Please be reminded, this tutorial is prepared for you to try and learn.
You are encouraged to improve the code for better application.

Leave a Comment

Your email address will not be published.

Share this Tutorial

Share on facebook
Share on whatsapp
Share on email
Share on print
Share on twitter
Share on pinterest
Share on facebook
Share on whatsapp
Share on email
Share on print
Share on twitter
Share on pinterest

Latest Tutorial

Object Sense With Servo Using Maker Pi RP2040
Build Otto DIY Robot Using Maker Pi RP2040
Raspberry Pi RP2040 vs STM32F1 vs SAMD21G18 vs ESP32-S2
The Easiest Way to Print Temperature Tower Using Cura 4.9.1 Plugins
IoT on Raspberry Pi Pico using CircuitPython and Adafruit IO
Tutorials of Cytron Technologies Scroll to Top