Adafruit Playground is a wonderful place to share what you find interesting. 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.
Adafruit Playground is a safe place to share with the wonderful Adafruit community of makers and doers.
-
Building an Anti-Dew Heater Controller
Introduction
Astronomy is my primary hobby and taking photographs of night-sky objects is my particular interest. A downside to this hobby is that it is very weather dependent. If it’s cloudy nothing can be seen. Weather reports are important to monitor but they just serve the general area. The sky conditions at my specific location are better monitored with an AllSky Camera.
An AllSky Camera is simply a camera with a fisheye lens that’s pointed up into the sky. A program takes pictures of the sky all night long so checking the sky conditions can be done by looking at the latest sky image. Is it too cloudy to take images? Are clouds starting to move in? Just check the AllSky Camera!
-
Using Github Codespaces for CircuitPython Development
Using Github Codespaces for CircuitPython Development
Introduction
If you wan't to contribute to CircuitPython, one of the hurdles you need to take is the installation of the development environment.
There is a nice guide from Dan Halbert https://learn.adafruit.com/building-circuitpython which walks you through all the necessary steps.
There are a few problems though:
- you will need to download and install a lot of software-packages. Some of them might even need other versions than those that the packet-manager of your distribution provides. Or they conflict with other projects you are working on.
- If you use a different flavor of Linux, you cannot just copy and paste the commands from the guide but also have to change commands and package-names.
- Your software-environment is bloated. Disks are very large these days, so this is not the main problem, but backups take definitely longer (I assume that you do backup your computer).
You could use a dedicated development machine or a virtual machine, but setting this up is again additional work.
Github Codespaces are a solution for all of these problems. A Codespace is a sort of virtual Linux-system. Technology wise it is a Linux container running within docker in the cloud. If you have a Github account, you can create such a system within seconds. You just head to https://github.com/codespaces and create a codespace from one of the templates (the "Blank" template is just fine).
The interface to the codespace is the web-version of "Visual Studio Code" (VSC), so you have a state-of-the-art editor, terminals, git and so on - all from within your browser. As an alternative, you can install VSC on your local machine, add the codespace-extensions from the VSC-marketplace and connect from your local VSC to you codespace. This is higly recommended, since the browser version is sometimes sluggish.
Since codespaces use ressources in the cloud, Github charges for using them. The good news is that the free plan of every account has 120 CPU-hours and 15GB storage per month included. The minimal machine has 2 CPUs, so this boils down to 60 hours per month. This should be enough unless you are a professional developer.
Automatic Setup for CircuitPython
At this point, you could just create an empty codespace from the template and follow the guide from Dan. I actually recommend that you do that once, since you will learn about the different tools you need to install.
For regular use, it is much simpler to let Github do all this work. For this reason the CircuitPython repository has predefined codespace configurations for most of the ports.
So the normal workflow would look like this:
- create a fork of https://github.com/adafruit/circuitpython
- create a new development branch within your fork
- clone this branch into a codespace
- go for a coffee-break: the initial setup will take about 10 minutes
- edit and build your own version of CircuitPython
- add, commit and push any changes back to your branch
- create a pull-request for upstream
You can find detailed instructions for the third step in the Readme: https://github.com/adafruit/circuitpython/blob/main/.devcontainer/Readme.md
Daily Use
Once you have created your codespace, you can keep it and use it whenever you want. Codespaces have two states: "active", i.e. running or "stopped". In the latter state you are only charged for the storage, so don't forget to stop your codespace after you finished your work. Github will automatically stop your codespace after 30 minutes of inactivity. In your accout settings you can change this value to something shorter. Also, Github will delete unused codespaces after 30 days of inactivity. But you will be prompted before this happens.
Storage size is a minor problem, since Github does not charge for the storage that the standard Linux image uses. A fully operational codespace for the espressif-port e.g. has about 2.4GB, so the 15GB limit will be enough for a number of codespaces.
Further Reading
Codespaces are a powerful tool with many features not covered here. To find out more, read the documentation: https://docs.github.com/en/codespaces.
Final Note
The scripts for the automatic setup of codespaces are not maintained by the core CircuitPython developers. As CircuitPython evolves the buildsystem will change and the scripts might stop working. In this case, it is best to create an issue.
-
Chrome Book Shutter-Offer using Neo Trinkey
Have you ever wanted to trick your friends into letting you shut off their Chromebook, Well now you can.
- First you need to install circuit python for the trinkey
- Second you need to add the libraries to the trinkeys
- finally copy the code and save
The libraries you need are neopixel.mpy and adafruit_hid
-
WiFi Power Management for the Raspberry Pi Pico W
With CircuitPython 8.x.x the new cyw43 library was introduced. The CYW43 supports controlling the power and efficiency of the CYW43439A0 WiFi chip on the Raspberry Pi Pico W microcontroller and other microcontrollers. The pins controlling 3 minor functions were transferred to the CYW43439A0 because Raspberry Pi Pico had used all the RP2040 pins. So the CYW43439A0 WiFi module had to be shoehorned between the RP2040 and the green LED, SMPS_MODE, and VBUS_SENSE functions. These three functions and the CYW43439A0 will be discussed here.
There are four sections to the new cyw43 internal library. This is new to CircuitPython starting with version 8.0.0. The features that import cyw43 will give you access to are:
- CYW43439A WiFi power management
- Access to and control of the onboard Green LED
- Access to and control of the SMPS_MODE option (PFM, PWM)
- Access to whether power is being supplied by way of the USB port by reading VBUS_SENSE
Four WiFi power management modes are predefined with the following:
- PM_STANDARD, 0xa11142: Is the standard power management mode. It enables power management and sets the power conservation timer to 200ms, 0x14.
- PM_AGGRESSIVE, 0xa11c82: It enables power management and sets the power conservation timer to 2000ms, 0xc8. This mode provides optimal power usage at the cost of performance.
- PM_PERFORMANCE, 0x111022: It enables power management and sets the power conservation timer to 20ms, 0x02. This mode uses more power to increase performance.
- PM_DISABLED, 0xa11140: Power management is disabled in this mode. The power management timer is set to 200ms, 0x14. Note: CircuitPython sets this mode at power on and reset because it provides the best connectivity reliability.
-
Alternate Power Settings: There does not seem to be a standard name for what each means. These are from the MicroPython documentation.
PM_NONE = 0x000010 # Like PM_DISABLED above
PM_POWERSAVE = 0x000011 # More power savings than PM_PERFORMANCE above
PM_PERFORMANCE = 0xA11142 # Same as PM_STANDARD above
To see what each bit of the 12 bit word does, check the first document referenced at the end of this document.
The following code snippet shows how to set the mode to the STANDARD mode.
try:
import cyw43 # Also tests for Raspberry Pi Pico W
cyw43.set_power_management(cyw43.PM_STANDARD)
except ImportError:
cyw43 = None
print(hex(cyw43.get_power_management())) # Will show the HEX value.
The challenge for the designers of the Raspberry Pi Pico W to add WiFi services they needed to repurpose three feature pins. The green LED, SMPS_MODE, and VBUS_SENSE control lines are used. To access them on the Pico W, the following code snippets may help you.
To drive the green LED we must now go through the WiFi module. Fortunately, board.LED does that for us.
# Green LED, WL_GPIO0, CYW0
GreenLED = digitalio.DigitalInOut(board.LED)
GreenLED.direction = digitalio.Direction.OUTPUT
GreenLED.value = True # Turn green LED ON
GreenLED.value = False # Turn green LED OFF
print('Green LED ', GreenLED.value) # Test for green LED state
SMPS_MODE sets the way the power management chip converts the supplied power to the 3.3 volts the RP2040 MCU and other devices require. SMPS_MODE controls the PS (Power Save) pin on the RT6150 buck/boost power manager.
When PS is low (0) (the default on Pico) the regulator is in Pulse Frequency Modulation (PFM) mode, which, at light and moderate loads, saves considerable power by only turning on the switching MOSFETs occasionally to keep the output capacitor topped up. PFM outputs a constant wave shape but at different frequencies depending on the load. Higher load >> higher frequency. An inductor is used to store power between the HIGHs to smooth out the ripple introduced by turning the power converter MOSFETs ON and OFF.
Setting PS high (1) forces the regulator into Pulse Width Modulation (PWM) mode. PWM mode forces the SMPS to switch continuously, which reduces the output ripple considerably at light loads (which can be good for some use cases) but at the expense of much worse efficiency. PWM outputs a constant frequency with a varying portion of HIGH versus LOW states. With a higher current demand, the output will increase the duration of the HIGH state and reduce the duration of the LOW state. An inductor is used to store power between HIGH states to smooth out the 3.3 volts sent to the circuits. Improved ripple is achieved, but worse efficiency at light loads will occur. It may improve Analog to Digital Conversion and other stability critical operations.
Note: Under heavy load conditions the switcher will be in PWM mode irrespective of the PS pin state.
# SMPS_MODE, WL_GPIO1, CYW1, 0=PFM Mode (Default, Best Efficiency),
# 1=PWM Mode (Improved ripple, worse efficiency at light loads)
SMPS = digitalio.DigitalInOut(board.SMPS_MODE)
SMPS.direction = digitalio.Direction.OUTPUT
SMPS.value = False # PFM Mode, most efficient
print('SMPS_MODE ', SMPS.value)
By reading the value from VBUS_SENSE you can determine if power is being supplied through the USB connector. If the value is True (1) power is being provided from the USB connector. If it is False (0) power is coming from some other source. It may be ~2.0 volts to 5.5 volts applied to the VSYS pin or 3.3 volts applied to the 3V3 pin. Applying 3.3 volts to the 3V3 pin is not advised unless it is exceptionally well regulated. Momentary startup overvoltage spikes can be lethal to your MCU. If power is applied to VSYS an onboard diode connected between VBUS and VSYS prevents back feeding power to the PC USB port.
# VBUS_SENSE, WL_GPIO2, CYW2
# True = we have power from USB
# False = power via VSYS or 3V3
VBUS = digitalio.DigitalInOut(board.VBUS_SENSE)
VBUS.direction = digitalio.Direction.INPUT
print('VBUS_SENSE', VBUS.value)
Source documents:
https://docs.circuitpython.org/en/latest/shared-bindings/cyw43/index.html
https://datasheets.raspberrypi.com/picow/pico-w-product-brief.pdf
-
Building a scientific handheld calculator with double precision math, complex math, uncertainties and fractions
The goal of this project is to build a Python based handheld and battery powered scientific calculator the size of a cigarette box (well, pocket calculator). Scientific, as in "reasonable precision". Float32 (single precision) is certainly acceptable for a display precision of, say 6 or 7 digits, but the follow-up rounding errors are not - at least not for me. I experimented with Decimal math before but ended up having to fight memory constraints with jepler-udecimal (https://github.com/jepler/Jepler_CircuitPython_udecimal) and my own extensions even on a Feather RP2040 with 256kB of user RAM. So I eventually decided to make a custom CircuitPython build and try to enable float64 (double precision) math. (Thanks to the Adafruit folks for their help!). Needless to say that float64 is entirely handled in C without the help of a potential floating-point unit (FPU) but then this approach is still much more CPU and memory efficient than implementing everything in Python.
The code for this project is on https://github.com/h-milz/circuitpython-calculator/ and will be discussed in this article. -
Custom Flight Sim Controllers with CircuitPython and MobiFlight
Introduction
The flight simulator industry has spawned dozens of custom controllers for those who are looking for a more realistic and entertaining experience. These controllers range from yolks and throttles to communication and GPS systems and their price can rival the costs of actual aviation equipment.
Thankfully there is a way to create your own controllers using low cost microcontrollers, buttons, encoders and many other input devices. There are even ways to output settings to LEDs, LED segments and displays but this guide does not cover output scenarios.
For this hookup guide I am using a controller I made for myself. The G1000 glass cockpit has a dual-rotary encoder in the lower right corner labeled FMS (Flight Management System) that is a pain to control with a mouse, even more so while the plane is in the air. So I decided to build my own controller to simulate the G1000 corner. My controller includes the FMS encoder knobs and the 6 buttons that tend to be used at the same time.
You can find the source files and STL files I used on GitHub.
-
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. -
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.
-
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.
-
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.
-
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 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?
-
Output Eurorack Control Voltage (CV) Signals from synthio
This note describes a method to output Eurorack CV (control voltage) signals from 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.
-
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.
-
-
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.