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.
-
An Almost Free Non-Destructive Soldering Jig I just couldn't bring myself to use the standard practice of soldering pins onto breakout boards using a solderless breadboard in spite of the recommendations and endorsements of some famous solder artists. I would cringe thinking that the heat would eventually warp the plastic portion of the board. It is a solderless breadboard after all.
Fortunately, after placing many Adafruit orders over the years, I had build up quite a collection of free half-sized Perma-Proto breadboards. The solution for creating a non-destructive soldering jig became obvious. Stack four Perma-Proto PCBs together, use a couple of M3 nylon screws and nuts, and place some rubber feet on the bottom. Voilà!
Sadly, the Perma-Proto board is no longer offered as a free product promotion. That's okay; I still have enough in the inventory for many future projects. And coasters. I have coasters.
-
Memento: Flicking USB ReadOnly/Write@Boot + Setting WiFi using QRCode So you want your friends/family/strangers to enjoy the luxury of modern technology, scanning a QR-code to join a wifi network, but wait...you want to save that in
settings.toml
too? Sheeesh, a tall order, lets get on with it then!We'll need to check a button at boot to decide if the Memento camera has write access to the flash drive, or the PC. Then we can update the
settings.toml
file if it's writable, and either way we can offer to join the network listed in the QRcode.The code example can be found here:
https://github.com/tyeth/Adafruit_CircuitPython_PyCamera/tree/wifi-qrsetup/examples/qrio-update-wifiThere are two files, the
boot.py
which handles the first load when the device is turned on or reset button is pressed. It prints a message to the screen and waits 2.5seconds before checking for the shutter button being held down, and then does the switching / flicking between read-only for the USB / PC, or read-only for the Memento camera (circuitpython). See here: https://github.com/tyeth/Adafruit_CircuitPython_PyCamera/blob/wifi-qrsetup/examples/qrio-update-wifi/boot.py -
A Beginners Guide to writing USB HID Report Descriptors by a Beginner Why Do You Want A HID Report Descriptor?
The USB specification includes a section on Human Interface Devices (HID). These devices range from keyboards, mice, joysticks, audio controls to medical controls, eye tracking and LED lighting. CircuitPython has support for USB HID with built-in defaults for a keyboard, mouse and consumer control and associated libraries. Dan Halbert has an excellent guide to get started with this.
A USB report descriptor tells the host machine the how to talk to your device. But what if you want to communicate to a device that no one else has written a report descriptor for? Perhaps you are building a new joystick, LED indicator or medical ultrasound device. Then you will have to write your own report descriptor.
Simple Descriptor Example
Initially when you look at report descriptors they seem complicated but it is best to think of them as a description of one or more reports that can be sent to or from your device. Each report gives the current state you wish to communicate. For example a joystick report may give the X and Y axis, a throttle value and if any of a dozen buttons are pressed. The joystick may also have a second report defined that allows the host machine to light up one or more indicator lights.
Below is an example report descriptor for a joystick that has 5 buttons. This descriptor only defines one report with each button taking up 1 bit, and 3 padding bits. While this is readable it is hard to create. There is an easier way.
-
Easy Helldivers II Stratagem Macros for RP2040 Macropad If you've been playing the awesome new co-op game Helldivers 2, you'll be familiar with the "stratagem" gameplay mechanic in which you summon various weaponry from airborne and satellite craft. This mechanic requires you to hold down a key/button to activate the stratagem input mode, while entering a sequence of directional key input.
Often during the game I tend to get overstimulated in the middle of a heated battle and my mind goes completely blank when trying to recall the one of the myriad codes I need to use to summon the effect I want. Even though the game UI displays them, it's still pretty hectic to precisely enter the sometimes lengthy codes when a giant scorpion is trying to rip my head off. To help spread liberty and democracy in the most efficient way possible, I thought I'd try to see if I could set up some stratagem macros using the awesome MACROPAD Hotkeys project from Phillip Burgess. NOTE: For simplicity, this particular setup mimics keyboard input, so until I hear otherwise I'm assuming this only works on the PC version of Helldivers II.
Custom Code
It took a few tries to figure out how to set up the macros so that the game would consistently accept them. Thankfully, the MACROPAD Hotkeys project is incredibly versatile and I was able to do this using the built-in features of the project without having to modify any of the core code. The main factors involved in getting this to work seemed to be:
- After pressing the stratagem input button, a significant delay was needed before the game would accept the directional input. I'm assuming this is due to needing to wait for the UI to fully present in-game before allowing further input. With experimentation it seems like the smallest delay possible is about 0.4 seconds.
- Each directional key needed to be pressed for a certain duration. The smallest duration I've tried for this so far is 0.05 seconds.
- A small delay is needed after each direction key is released. The smallest duration I've tried for this so far is 0.05 seconds.
In the /macros folder on my macropad CIRCUITPY drive, I created a new file named helldivers-2.py. Since I anticipate changing this file frequently as I progress through the game, I wanted to make it as simple as I could to add/edit new macros, so that I could even edit them in real-time as I played the game. To make this a little easier, I wrote a bit of custom code at the top of the file.
-
WaveStore: Create a Library of synthio Voices The arbitrary waveform capability along with ADSR envelopes and filters has made it very easy to create custom musical voices with CircuitPython synthio. And if you use an additive synthesis tool like WaveBuilder, you'll begin to quickly amass quite a few custom musical voices as you experiment with the nearly infinite number of oscillator combinations.
Hearing Voices
Up to this point, I've been keeping track of the numeric specifications for WaveBuilder oscillators and synthio.Envelopes in project code or scribbled on a note pad. That means that when building a new project that needs to use a previously created voice, I'll cut and paste the voice definition code into the new project. The process works, but isn't ideal. What if a synthio musical voice could be loaded from a collection in a file folder, kind of like working with a font file?
So a concept for the WaveStore project began to develop. Here are the initial requirements.
- Library File Management -- We'll make it easy at first and just work with a collection stored on an SD card. No need to tax the brain to conjure up a way to write to the CircuitPython root directory just yet. After creating and storing a voice to a file, we'll manually copy the files from the SD card in order to use and share between projects.
- Files -- In the spirit of keeping it simple, only files representing waveforms and envelopes will be created. Those objects form the fundamental elements of a musical voice. Perhaps we can add filters and other effects later.
-
Library File Names -- Waveforms will be stored in a standard wave file with a
.wav
extension, thanks to the awesome Adafruit_CircuitPython_Wave library. Envelopes and filters definitions will be stored in plain text files with.adsr
and.fltr
extensions but will transform to synthio.Envelope and synthio.BiQuad objects when retrieved. - Icons -- Imagine being able to select a musical voice waveform or envelope by touching a screen icon. Functions will be provided to save and retrieve bitmap images of waveforms and envelopes as well as capturing the contents of an entire screen. We'll start with 64x64 pixel bitmaps created by WaveViz. A graphical frequency response representation of a bi-quad filter's coefficients is in the works but is a bit beyond today's skillset. There's a potential workaround, but I'd rather derive it directly from the numbers. Hoping for an epiphany soon.
In the future, more features will likely be added to WaveStore such as support for filters and on-screen icon buttons -- as my experience and Python skill set grows. What would you add?
Enter WaveStore
WaveStore is under development but is available for testing. The current alpha version of the WaveStore class provides the following helpers:
- Wavetables
- read_wavetable -- Read a
.wav
file and return a memory view object. - read_wavetable_ulab -- Read a
.wav
file and return a ulab.numpy array object. The ulab array can be mixed with other wavetable array files. - write_wavetable -- Format a wavetable and write it as a standard
.wav
file.
- read_wavetable -- Read a
- Envelopes
- read_envelope -- Read an
.adsr
file and return a synthio.Envelope object. - write_envelope -- Write a text interpretation of a synthio.Envelope object to an
.adsr
file.
- read_envelope -- Read an
- Bitmaps
- read_bitmap -- Read a
.bmp
file and return the bitmap as a TileGrid object that can be added to a displayio.Group object. - write_bitmap -- Write a bitmap image to a
.bmp
file. - write_screen -- Write the screen contents to a
.bmp
file on the SD card.
- read_bitmap -- Read a
- Utilities
- get_catalog -- Returns a list of files in a specified folder.
-
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
-
Desktop Multifunction Device: Time and Temperature Let's add temperature to the multifunction device using the ADT7410 on the Temperature and Motion Wing.
See Multifunction Device Part 1 for a parts list and assembly instructions.
Reading the Sensor
First we need to set up the sensor:
-
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
-
Using CIRCUP with Web-Workflow Thanks to some hard work by Vladimir Kotal (@vladak)❤️, an open-source community contributor on GitHub who added this pull request, and thanks to our own CircuitPython wizard Tim (@foamyguy), there is now the ability to use
circup
with the web-workflow.I had a play by pulling from github last month as I was desperate. I had a device that was going to be left plugged into the mains, which was running out-of-date code and libraries (but otherwise functional), and so along the way fixed a couple of issues for Windows users or anyone needing libraries with nested folders.
Install
circup
, at least version 1.6.1:To begin with we'll explore how install and to use it, then I'll take you through my specific example...
Install latest CIRCUP:
pip install circup --force
[ --force forcefully overwrites / upgrades]
-
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.
-
Tell me a story, Neo Trinkey! Once upon a time.....
..... I remember reading a story in "Summer Weekly Reader" (I think by Isaac Asimov) about a robot that told stories to the young boy who owned it. Mostly fairy tales, till one of the boy's friends loaded a tape with more modern info like spaceships (and robots!).
Now, I don't have that robot... but I have a Neo Trinkey and CircuitPython - that's more than enough.
My repository TellMeAStory has five files, CircuitPython code: scifi.py and prt.py, and text data: planets.sf, aliens.sf and ships.sf. Copy those to a Neo Trinkey, renaming scifi.py to code.py. When the program runs, touching "1" chooses a planet name, and touching "2" will generate a short story. In code.py, 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! For example:
(touching 1)
Broria KBO6
(touching 2)
Black Sparrow
lifts off from
Broria KBO6
with ancient artifacts
and is damaged in an explosion
and tumbles out of control till emergency crews reach them
Suddenly a ship appears from Notania warning of imminent Bhisih attack.
Planets, Aliens, and Ship names were generated on fantasynamegenerators.com - you can edit the lists with your own choices. And you can rewrite the story elements to your own liking!
[Note: updated to work with CircuitPython 9.x. I moved the lists of aliens, ships and planets to external files to avoid memory allocation problems.]
-
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.