Getting Started

Adafruit Playground is a wonderful and safe place to share your interests with Adafruit's vibrant community of makers and doers. Have a cool project you are working on? Have a bit of code that you think others will find useful? Want to show off your electronics workbench? You have come to the right place.
The goal of Adafruit Playground is to make it as simple as possible to share your work. On the Adafruit Playground users can create Notes. A note is a single-page space where you can document your topic using Adafruit's easy-to-use editor. Notes are like Guides on the Adafruit Learning System but guides are high-fidelity content curated and maintained by Adafuit. Notes are whatever you want them to be. Have fun and be kind.
Click here to learn more about Adafruit Playground and how to get started.
-
Output Eurorack Control Voltage (CV) Signals from synthio This note describes a method to output Eurorack CV (control voltage) signals from CircuitPython synthio using the PCM510x I2S DAC.
Rather than employing CV-like object controls such as Envelope and LFO to only adjust the parameters of other synthio objects, it would be useful to also control physically external devices such as the CV inputs of Eurorack modules. To do that we'll need to configure the synthio.Note object to ignore its typical behavior as an oscillator. Oh, and it would be handy to have an I2S DAC on-hand with a DC-coupled output that's capable of positive and negative output voltage that can be connected to a Eurorack module.
Here's the test setup:
- Create a Note.waveform (wave shape table) object containing the maximum wave value (16-bit signed). This is an array filled with a single value that acts like a fixed DC voltage.
- Set the Note wave shape oscillator frequency to an arbitrary value such as 440Hz. The frequency value is unimportant since the oscillator waveform output will simply be a fixed value.
- Define a synthio.LFO object to output the LFO signal. If outputting an ADSR envelope is desired, define a synthio.Envelope object.
- Create a synthio.Note object where the amplitude parameter is controlled by the ADSR envelope or LFO.
- "Press" the note to output the ADSR envelope or LFO signal from the I2S DAC.
Instead of the I2S DAC, CV output signals can be created in this manner using audiopwmio and audioio to use PWM or analog DAC output pins. Boards like the QT PY RP2040 and Grand Central M4 Express could be used for PWM or analog DAC outputs. Keep in mind that, unlike the 0 volt baseline of the I2S DAC, the baseline of a PWM or analog DAC signal is biased to approximately +1.65 volts.
I2S DAC Output
-
Look Ma, No Microcontroller! (Soldering Lamp Edition) Inspiration
I needed a soldering lamp that didn't cast strong shadows and was an opportunity to salvage the LEDs from a broken TV left by the dumpster.
There are lots of ways to make a good soldering lamp from LEDs. I happened to pick a difficult path that required some reverse engineering but I hope you'll find the journey... illuminating. ;-)
-
WaveViz: Plot a synthio Wave Table or Envelope WaveViz is a CircuitPython class to create a positionable
displayio.TileGrid
graphics widget from asynthio.waveform
wave table orsynthio.Envelope
object (or any one-dimensional list for that matter). The class also makes the underlying bitmap and palette objects available for other uses such as saving the widget to an image file.The long-term objective is to be able to save and import sounds and ADSR envelopes from a library of synthio wave table files and envelope objects. The first step in the process is to create a visualizer to help characterize the sounds graphically. We'll worry about saving and retrieving waveform and envelope objects sometime in the future (watch for a new class, WaveStore to appear soon). For now, let's work on getting images of wave tables and envelopes.
-
Custom ANO Fidget Firmware Awhile back Adafruit featured a story on their blog about a really cool fidget toy built using a rotary encoder and a NeoPixel-compatible LED ring. I loved the idea so much that I wanted to make my own version, and spent a few days designing my own with parts I had on-hand. As it turns out, I wasn't the only one: the Ruiz Brothers built an incredibly stylish take on this idea using an ANO rotary encoder and published an excellent learn guide for it!
While there are physical differences between the two versions, the hardware is similar enough that I wanted to write a CircuitPython firmware that would work their version as well as mine. I wanted the firmware to have multiple modes, with the ability to add more in the future as ideas came to me. Ideally it would be something others could build on as well.
Update: You can see a live demo of the firmware running on the Ruiz brothers' fidget toy in this episode of 3D Hangouts
-
Failing with CircuitPython How to fail with CircuitPython
All code will have errors. You might find it sooner or later, or the error might be in your code, in a library that you are using or even in CircuitPython itself!
It is also very common to write some code on your lab environment, test it a bunch of times, then take it out into the field and watch it fail in lots of ways you would never though of. Exactly the same happens when you give your device to a user or a kid who will test it in ways that will make your coding skills feel silly.
In my case this is happening for a weather station. This should be a simple project, right? Grab some data from a sensor like a Stemma QT thermometer, then send that data to Adafruit.IO and watch it get graphed in a lovely dashboard. Or at least that was my expectation for this project.
As you can see, I started getting missing data for days, and only when I went to the semi-remote location where I am testing my weather station, and manually rebooted the devices, I would get start getting data again. But now the graph is ruined, it has a large gap in the data and to all whom I showed the graphs, the first thing they mentioned is 'what happened to the missing data?'. Now what do I do?
Look at the last hours of the graph. Even if I am missing only a little bit of data, it looks terrible right? You, the reader have no idea how hot it is during the day, how cold it is during the night, or what is the coldest hour. Your eyes go straight to the missing data at the end, with the graphs looking like a shark tooth and being jumpy. What a disaster.
I thought about blaming Adafruit.IO or maybe CircuitPython or maybe it's the fault of the ESP32 that is reading the data. Then I thought about adding extra hardware to it like a TPL5110 power timer but that would require some soldering and would break the wifi workflow that I love so much from CircuitPython 8.
I cloud also get my code full of
try
andexcept
Python blocks on all of the parts of my code. To be honest, I did tried to do it this way, but things would still keep failing so I kept adding moretry/except
blocks until the code looked terrible. I was this close to having a project that was not fun for me, and that I would not like to share how it works with this amazing community that makes CircuitPython what it is. Not sharing? That doesn't sound like me at all. Time to do things a different way.Loving to fail
There is a bit of a hidden gem in CircuitPython and it's the
supervisor
module. I was only able to find a little bit of technical documentation without a simple example that I could just copy and paste into my project, and I also found another example which was close to what I wanted to do in Tod's CircuitPython tricks.After staring at the code I had available for a couple of minutes, I realized that this is what I had to do. I needed to let go of my impulse of keep the code failure free, and embrace CircuitPython failing. I would let if fail any time it wanted, and then I would ask it to try again.
With two simple lines, my weather station code can now fail because the wifi router is down, because the Stemma QT cables had a problem with talking to the sensor, because it was running for so long that it was finding errors in the CircuitPython base code or the request library. It doesn't matter, the board will accept that something weird happened, and would try again. Try this in your code and love to fail as much as I do now!
import supervisor
supervisor.set_next_code_file(filename='code.py', reload_on_error=True)
It should not be this simple, right? Here's how it works:
In the first line of code we are calling a library called
supervisor
. Don't worry, you don't have to install anything, it comes as a standard CircuitPython library and will probably work on any board that you have CircuitPython running on. What it does, is talk to one of the internal parts of CircuitPython and modify how it will behave.The second like, is the one that changes how it works. It's using a function called
set_next_code_file
. It's normally used so that you can run the well knowncode.py
file and if something happens, you can define a second file with another name and that code would do something like recover things, blink a light or beep to indicate that it will not behave as it would normally do. I am not calling a second file, instead I'm telling it to go tocode.py
so that it can try again.And my favorite part comes with the code that says
reload_on_error=True
. The way CircuitPython works is that when it fails it will take you to the REPL console, and show you why it failed. This is a good thing as that is the way that we learn, we make mistakes, the REPL shows us what those mistakes are, and we go and fix them.But for my weather station, once the code was ready and it was doing what I wanted, if there is a failure like the wifi is down, it would go to the console and wait for me to do something. This weather station is located on a dry forest in Costa Rica and it doesn't know that is not next to my computer where it's easy for me to tell it to try again. So what
reload_on_error=True
tells it to do, is to change what it would have usually do and take me to the console, and instead it will do something similar as if I was close so the board, pushing the RESET button every time something unexpected happens.This way, CircuitPython will keep on trying over and over until wifi comes back, and hopefully the graphs will have smaller gaps in the data, and they will look much better and should be able to work like a weather station should. Read the thermometer. Send it to Adafruit.IO. Go to sleep to save some battery.
I love you CircuitPython! You and me can fail as many times as we need to, as long as we do it together and without feeling bad. <3.
-
Doggy Buttons! Inspiration
This project was born from two desires. One, see if our dog could be taught to use buttons without spending piles of money. Two, to make use of an ancient Raspberry Pi.
Lots of people have done a project like this, and it isn’t necessary to use a Raspberry Pi. That just happened to be the only unused piece of electronics I had on hand with audio output. But, I will say that I definitely recommend this approach for a couple reasons. Firstly, it’s very easy to get running in Python with Adafruit’s Blinka library. Second, it leaves lots of room for growth (both in number of buttons and features like button presses sending digital messages).
Be sure to read all the way to the bottom for a cute puppy photo!Materials Used
-
Raspberry Pi (low cost model A)
- Gifted by a coworker who was going to throw it out
-
16GB (full size) SD card
- Came with my 3D printer, but is overkill for that purpose
-
Audio cable
- From a box of junk computer parts
-
Audio amplifier
- Included in a box of junk that got shipped to me
-
Speakers
- From an old CRT TV
-
Jumper wires
- Reused from multiple projects
-
Keyboard breakout
- Hand made from salvaged diodes, jumper headers, perf-board, wires, and solder. Let's call it 50% recycled.
-
3D printed buttons (and some hotglue)
- Technically new, but I don't count 3D prints if they displace buying other plastic
-
Electronic buttons inside the prints (also a boot button shutdown switch)
- All salvaged from ewaste or otherwise discarded
-
Wooden mounting board
- Had to buy it from a hobby store
Salvage percentage ~83%! Hard to give an exact number... do you measure by count? By volume? By mass? By value? Do you count new tools you had to buy? Whatever, I'm going to call it 83%.
-
Raspberry Pi (low cost model A)
-
Teach your NeoTrinkey Morse code! C3P0 can talk. R2D2 expresses what he's thinking with beeps and boops. What can you do with a Neo Trinkey?
How about.....Morse code? It's simple, and four neopixels is more than enough to say anything! Here's how I set about doing it.
The great thing about Circuit Python is you can just plug in a device and use any editor on the code, but I like to use thonny as my IDE. Thonny gives you easy access to the REPL and you can have plenty of print() statements for debugging.
-
Spock on a Chip! I love using the random library - it's a simple way to introduce variety into simple programs and here's one example, what I call "Spock on a Chip." Here's a link to the repository.
Copy the files to a NeoTrinkey, changing "spock.py" to "code.py" - when it runs, it occasionally blinks lights, but when you touch one of the pads, it will blink and then pick a random quote out of the "spock" file and print it. If REPL=True, you'll need to be in an editor like mu to see the text. IF you change REPL to be False, then it will use HID support to send the text as if typed on a keyboard! Just the way to jazz up your email! Edit the "spock" file to add your own favorite quotes from everyone's favorite Vulcan.
What's going on?
There are two functions that are used to deliver the quotes. len_file(filename),
def file_len(filename):
with open(filename) as f:
for i, _ in enumerate(f):
pass
return i + 1
and wisdom(file_name).
def wisdom(filename):
qs = open(filename)
for i in range(random.randrange(file_len(filename))+1):
quote = qs.readline()
quote = quote.rstrip()
cmpthink()
qs.close()
return (quote.rstrip())
Give a file name, wisdom() calls len_file() to determine how big the file is, then uses random.randrange() to select the text - that means you can add or subtract from the "spock" file without needing to update the program.
The fun thing with this code is, it can be reused for other purposes. I've already used it to put together a "story" program that uses multiple source files (aliens, suns, planets, etc.) that can be drawn from to generate story lines. It could be used for a diceware style passphrase generator. And, of course, it doesn't have to be Spock on a chip - you can fill the "spock" file with any quotes you choose.
Fascinating. Is this logical? -
Faster SD writes with ESP32 and SDIO Better SD write performance is probably a common goal. My project uses the Adafruit SD Micro SDIO breakout, the Adafruit OV5640 Camera breakout and the Unexpected Maker ESP32S3 feather in a compact stack (with a custom PCB to connect them) that fits in a GoPro waterproof case. I want GoPro-like video recording but with BLE time synchronization and start/stop functions with multiple cameras. It's like a GoPro but it's not, it's a NoPro!
I'm programming with the Arduino IDE and using the SD_MMC library. The ESP32S3 has an SDIO interface with programmable pins, which the library supports. But for the longest time, I could only get sustainable write speeds of 150-200 kilobytes per second. I'm using a Sandisk Extreme V30 32GB which should get 30MB/s. For the frame rates and resolution I want, I need about 3MB/s.
I initially played with the file system buffer size but never got consistent results. I might occasionally get a few frames written at target speeds but never a whole video. So I set my code aside and started from scratch to explore how to get better speed.
There definitely seemed to be a dependence on file buffer size as well as on the size of data being written. So I tried lots of combinations and got the same results - usually about 150-200 kB/s but occasionally over 3MB/s.
But I had noticed a pattern of good performance when the write lengths were certain powers of 2 so I tried multiples of 512 bytes and got much better speeds. With a file buffer size of 4096 and data sizes over 75kB I could get consistent speed over 3MB/s, just what I need. 4096 also gets consistent small writes at over 1MB/s. Buffer sizes of 8192 and greater actually worked worse.
-
RGB Matrix Word Clocks I was looking for a clock that would tell the time in words rather than just hands (analog) or numbers (digital). I found a few for sale on places like Amazon and eBay, but none of those particularly stood out, so I decided to create a customizable one myself.
I designed and built two variants. Both use the same RGB matrix panel. Both use CircuitPython on the ESP32-S3, but one uses the Matrix Portal S3, and the other uses a standalone Feather ESP32-S3 with an Adafruit RGB Matrix Featherwing Kit. The Matrix Portal version requires no soldering; the Feather version requires some simple soldering. I will show both here.
The text is left justified, vertically centered on the display and each line is in a random color which changes every time the minute changes. Since you control the CircuitPython code, you can change this formatting any way you choose, e.g. same color lines, individually horizontally centered lines, etc.
Common Hardware
-
Sound Reactive Neopixel Brass Bell Cover I'm in a radical marching band called Brass Your Heart, and I wanted to make our instruments light up when we play them. I started this project by making one for my friend who plays a marching baritone. Below is a mostly step by step process for how I made them using: sewable neopixels, conductive thread, snaps, a Gemma M0, and an electret microphone.
Step One:
The diameter of the baritone measured 26 inches, so I used tailor's chalk to draw the outer circle you see here. Then I drew another circle, about an inch in for the lights, so that the neopixels and the conductive thread is far away from the metal of the instrument. I also make 8 markings where the neopixels would go and started to sew them into position with regular black thread.
-
No-Code "EnSmartening" an IKEA FÖRNUFTIG Air Purifier - V1 Reuse existing PCB+Dial This project was a personal desire to put Adafruit IO through it's paces, and see how far I could go in recreating a project designed for ESPHome + Home Assistant. Traditionally I would have assumed it was too complex for the Actions and Dashboard of Adafruit IO if I used a Wippersnapper device, instead relying on Node-RED to string things together, but now that I work on the WipperSnapper project I was curious how far it had come / could go.
The basic idea was to take a £60 "dumb" air purifier with a 3-speed dial (£70 with carbon filter) and add a WiFi micro-controller, so that it can be switched on and the speed controlled according to the values sent from a separate Air Quality sensor (Particulate Matter <2.5µm), and a user-adjustable control panel hosted as an Adafruit IO Dashboard.
Is this another silly idea / tribute to insanity, by needlessly bolting the internet and a subscription service to a device, which usually degrades performance and adds built-in obsolescence (not to mention a 22second boot-time)?
Well maybe, or maybe it's just a way of providing people with alternatives, but either way it involves Adafruit IO as an alternative to Home-Assistant / Node-RED or the official IKEA smart-hub + smart devices, and Adafruit's open-source IOT firmware called WipperSnapper as an alternative to ESPHome.
It's possible to use on the free-plan on Adafruit IO (you do NOT need IO+), or offline with a little coding, so I don't feel bad about suggesting it...
Here's a demo video of it in action: (Adafruit IO Dashboard on left, WipperSnapper device page on right, Speed dial of Air Purifier + Wind Sock on Picture-in-Picture video)
-
Building a LEGO Case for Your Projects The first step is to get BrickLink Studio 2.0. This CAD program is intuitive to use, includes almost all bricks available, and features to test your model strength, render 3D visuals of it, view what parts you use and even generate instructions on how to build your model. This is not a guide on how to use Studio 2.0, but it includes a built in tutorial that makes getting started a breeze.
John Park wrote an excellent guide Lego Set Lighting. I used this guide to light up the LEGO Christmas Tree (set #40573) making the candles flicker away. My problem was how do I hide the electronics running everything.
I had the idea to design and build a LEGO model in the shape of a present, but the question was how.
It turns out there is an easily accessible CAD program and method of ordering parts (maybe too easy!).
-
macOS: Built-In Support for USB-Serial Chips macOS provides built-in support for some USB-to-serial converter IC's. Built-in support was added at various times for different chips. In the versions of macOS before support for a particular chip appeared, a manufacturer-supplied driver needs to be installed.
I rolled some old Macs back to their original versions, and then rolled them forward, one version at a time, to test support support for three different families of USB-to-serial chips. I tested by attempting to upload firmware to ESP32 chips using esptool.py.
Summary
FTDI FT232x chips have working built-in support starting in Mojave, macOS 10.14. Specifically, a cable using a FT232RL was tested.
Silicon Labs (Silabs) CP210x chips have working built-in support starting in Catalina, macOS 10.15. Specifically, support for CP2104 was tested on an Adafruit Feather HUZZAH32.
WCH CH9102F chips have working built-in support starting in Ventura, macOS 13. Specifically, an Adafruit Feather ESP32 V2 with the CH9102F was tested.
Tested macOS Versions
Not every combination was tested, since I knew which versions did not have support and did not re-test old versions.
High Sierra - macOS 10.13
Only Python 2 is available in the original installation, so Python 3.11 was manually installed from python.org.
NO - FTDI did not work properly, though it appeared as
/dev/cu.usbserial-AL0157X9
.NO - CP2104 does not appear at all.
NO - CH9102F appears as
/dev/cu.usbmodemNNN
, but does not work properly with esptool.py.Mojave - macOS 10.14
Only Python 2 is available in the original installation, so Python 3.11 was manually installed from python.org.
YES - FTDI with esptool.py works. It appeared as
/dev/cu.usbserial-AL0157X9
.NO - CP2104 did not work, I believe (my notes are incomplete).
NO - CH9102F
Catalina - macOS 10.15
Has Python 3.8.
YES - FTDI works.
YES - CP2104 starts working in this version, appearing as
/devcu.usbserial-NNNNNNNN
.NO - CH9102F does not work properly. Only
esptool.py chip_id
works.Big Sur - macOS 11
Same as Catalina.
Monterey - macOS 12
Python 3.9.6
Same as Catalina.
Ventura - macOS 13
YES - FTDI works.
YES- CP2104 works.
YES - CH9102F starts working in this version. It appears as
/dev/cu.usbserial-NNNNNNNN
. Note the change fromusbmodem
tousbserial
from Montery to Ventura.Sonoma - macOS 14
Same as Ventura. All work.
References
-
IoT Wind Chimes using synthio The Weather Chimes project fills that need. It connects to the Adafruit NTP service for network time and to OpenWeatherMap.org for wind speed data. The wind speed data is retrieved every twenty minutes and is used to adjust wind chime playback in a pseudo random pattern. The chime voice synthesizer is provided by the
CircuitPython_Chimes
class and for this project, is sent to an Adafruit MAX98357A I2S amplifier driving an Adafruit 40mm 4-ohm 3-watt speaker. Although an Unexpected Maker Feather S2 was used for this project, the code should work on just about any ESP32 device that's capable of running CircuitPython.
The Weather Chimes project consists of two primary code files,weather_chimes_code.py
andweather_chimes_wifi.py
. Theweather_chimes_code.py
code is imported via the defaultcode.py
file contained in the Feather S2's root directory. This code contains the primary non-wifi device definitions and the masterwhile...
loop that plays the chimes. It also imports theWeatherChimesWiFi
class fromweather_chimes_wifi.py
.
TheWeatherChimesWiFi
class takes care of all the networking details for connecting and retrieving data from the internet. It also provide helpers for updating and retrieving time and weather as well as properties for including the local time and wind speed. The WiFi class uses thesettings.toml
file for connecting to a home WiFi router as well as parameters needed for Adafruit NTP and the OpenWeatherMap.org API.A fictional
settings.toml
file:A CircuitPython project for indoor "windless" garden chimes that play along with the outdoor wind speed.