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.
-
SevenSeg: Create an Instrumentation Vibe for Displayio SevenSeg is a CircuitPython displayio -compatible collection of four ultra-compact monospaced
.bdf
fonts that mimic the look of a seven segment LED display. To keep the font file size small, only the numeric characters that a seven-segment display can represent are included in the font, including uppercase and lowercase hexadecimal. Okay -- there are a couple of other useful things in there, too.Supported characters:
+ - . 0 1 2 3 4 5 6 7 8 9 : A B C D E F _ a b c d e f °
SevenSeg Font GitHub repository
-
Tiny Plaintext MIDI Sequencer for SAMD21 When playing with synthesizer patches, it helps to have a MIDI sequencer to generate note triggers for you. One option is to use a DAW on a laptop, like Ableton or whatever. But, it can also be nice to go DAWless with a hardware sequencer, because buttons and knobs and blinking lights are fun. With DIY sequencer projects in mind, I wrote a plaintext music notation sequencer module for CircuitPython, called txtseq.
The sequencer can read a song from a text file on your CIRCUITPY drive, parse the music notation into an array of MIDI note on and off events, then play the MIDI events in an event loop (allowing time to run other code). The music notation is loosely based on a subset of the abc standard. Notation for note pitch, accidentals, octave, and duration is very similar to abc. For everything else, the sequencer uses a simpler grammar and syntax that is easy to parse on a microcontroller.
CircuitPython + GarageBand Audio Demo
In my GitHub repository for the sequencer, the demonstration track I wrote is named
track1.txt
. To hear what the commit 9c73ec4 version oftrack1.txt
sounds like when played from a Trinket M0 over USB MIDI into a MIDI drum instrument in GarageBand, you can listen todemos/track1-180bpm.mp3
.How to Run the Code
I've been testing this with CircuitPython 9.0.5 on a Trinket M0 (SAMD21), but most of the code (all but MIDI out) also runs on desktop python3.
CircuitPython Version
Prepare a host computer with something that can play sounds for incoming USB MIDI notes on channels 10, 11, 12, and 13. For example, on macOS, you can use the GarageBand app by adding a MIDI track to an empty project, then selecting a drum kit as the MIDI track's instrument.
Update CircuitPython and bootloader the normal way. (no additional libraries are needed)
Fetch a local copy of txtseq using
git clone
or by downloading a release archive.Copy the
txtseq/txtseq
directory to your CIRCUITPY drive (see docs at Welcome to CircuitPython! > The CIRCUITPY Drive)Copy
txtseq/code.py
,txtseq/boot.py
, andtxtseq/track1.txt
to your CIRCUITPY drive
When
code.py
runs, it will parse music notation fromtrack1.txt
into an array of MIDI note event data, then start playing the notes over USB MIDI. The parser and playback code print a variety of debug info to the serial console to help with measuring memory and CPU use along with MIDI playback latency.Desktop Version
This will give debug prints only, without actual MIDI playback. But, you could easily modify the code to use a library that is capable of sending MIDI. (see definition of
midi_tx(data)
callback function intxtseq/__main__.py
)Clone the txtseq repo
cd txtseq
python3 -m txtseq track1.txt
Example Output
This is from running
code.py
on a Trinket M0 with CircuitPython 9.0.5:10: ppb=4 11: bpm=180 19: 1 ................ 20: 1 ................... 21: 1 ............................... 22: 1 ................ 23: 1 ................... 24: 1 ...................... [parse time: 517 ms] 00009924 00078924 0010992A 0017892A 00189924 001F8924 0028992E 002F892E 00309928 00378928 0040992A 0047892A 00489924 004F8924 0058992E 005F892E 00609924 00678924 0070992A 0077892A 00789924 007F8924 0088992E 008F892E 00909928 00978928 00A0992A 00A7892A 00A89924 00AF8924 00B8992E 00BF892E 00C09924 00C78924 00D0992A 00D7892A 00D89924 00DF8924 00E8992E 00EF892E 00F09928 00F78928 0100992A 0107892A 01089924 010F8924 0118992E 011F892E 01209924 0120992E 01278924 0127892E 0128992A 012F892A 0130992E 0137892E 01389924 013F8924 0140992E 0147892E 0148992A 014F892A 01509928 01578928 0160992A 0167892A 01689924 016F8924 01789924 017F8924 01809924 01838924 01849924 01878924 01889924 018B8924 018C9924 018F8924 01909924 01938924 01949924 01978924 01989924 019B8924 019C9924 019F8924 01A09924 01A38924 01A49924 01A78924 01A89924 01AB8924 01B09933 01B38933 01B49933 01B78933 01B89933 01BB8933 01BC9933 01BF8933 01C09933 01C38933 01C49933 01C78933 01C89933 01CB8933 01CC9933 01CF8933 01D09933 01D38933 01D49933 01D78933 01DC9924 01DF8924 01E09924 01E38924 01E49924 01E78924 01E89924 01EB8924 01EC9924 01EF8924 01F09924 01F38924 01F49924 01F78924 01F89924 01FB8924 01FC9924 01FF8924 02049931 020F8931 02109924 02178924 0220992A 0227892A 02289924 022F8924 0238992E 023F892E 02409928 02478928 0250992A 0257892A 02589924 025F8924 0268992E 026F892E 02709924 02778924 0280992A 0287892A 02889924 028F8924 0298992E 029F892E 02A09928 02A78928 02B0992A 02B7892A 02B89924 02BF8924 02C8992E 02CF892E 02D09924 02D78924 02E0992A 02E7892A 02E89924 02EF8924 02F8992E 02FF892E 03009928 03078928 0310992A 0317892A 03189924 031F8924 0328992E 032F892E 03309924 0330992E 03378924 0337892E 0338992A 033F892A 0340992E 0347892E 03489924 034F8924 0350992E 0357892E 0358992A 035F892A 03609928 03678928 0370992A 0377892A 03789924 037F8924 03889924 038F8924 03909924 03938924 03949924 03978924 03989924 039B8924 039C9924 039F8924 03A09924 03A38924 03A49924 03A78924 03A89924 03AB8924 03AC9924 03AF8924 03B09924 03B38924 03B49924 03B78924 03B89924 03BB8924 03C09933 03C38933 03C49933 03C78933 03C89933 03CB8933 03CC9933 03CF8933 03D09933 03D38933 03D49933 03D78933 03D89933 03DB8933 03DC9933 03DF8933 03E09933 03E38933 03E49933 03E78933 03F09931 03FB8931 [midi event dump time: 191 ms] mem_free: 10976 10880 9728 diffs: 96 1152 Playing on USB MIDI ch10-13... Done
The top section has debug print output from the parsing functions.
The second section is a dump of the array of timestamped midi events created by the note parsing code.
The third section summarizes
mem_free()
measurements. (seecode.py
)The last section has debug prints from the MIDI event player.
Reading the Code
This section is for people who are interested in how the sequencer works. It's kind of technical, and I used a variety of optimization tricks to get it to fit and run well on a non-Express SAMD21 board.
The txtseq module exports a function,
sequencer(f)
(seetxtseq/sequencer.py
) which expects its argument,f
, to be a binary mode file object (e.g.open('some-song.txt', 'rb')
). For usage examples, seecode.py
ortxtseq/__main__.py
.The
seqencer()
function parses its input file to find top level commands, which it then uses to call further parsing functions. For example, the commands1
,2
,3
, and4
call thep_staff()
function defined intxtseq/staff.py
. The numbers correspond to each of the four voices (tracks). Note on and off events generated by the parser get packed as uint32 values in anarray.array('L')
. The most significant 16 bits have a timestamp (units of MIDI pulses). The low 16 bits have MIDI status and data bytes. This allows for adding note events one voice at a time without worrying about out-of-order events. I sort the array at the end to merge all the events from different voices into one list ordered by ascending timestamps.To get all the parser code to compile and run on a SAMD21, the module is split into several files of less than 150 lines each. Also, I used several MicroPython optimization techniques from Damien George's 2018 PyCon AU talk, "Writing fast and efficient MicroPython".
The
txtseq/util.py
file holds parsing functions for dealing with comments (# ...
), semantically irrelevant whitespace, and setting of header options (B
for bpm,U
for time unit)Parsing of note pitch and duration for staff lines happens in the
p_staff()
function oftxtseq/staff.py
. The parsing style is based on state machine loops that examine one byte at a time, reading bytes withreadinto()
to limit heap allocations. When one of the parser functions or state machine branches recognizes a byte that should be processed by a different function or state branch, it will rewind the file's cursor position by one byte using thef.seek(mark)
idiom.Playback uses a generator defined in
txtseq/player.py
. By using the generator as the iterator for an event loop, it's easy to run your own code interleaved with the MIDI player (seemain()
incode.py
). Also, holding playback state in the local scope of a generator makes it possible to avoid many heap allocations and dictionary lookups that would add jitter and latency if the MIDI player used a class instance.
Music Notation Grammar and Syntax
This section is for people who want to write songs for the sequencer to play as MIDI events.
For examples of how the plaintext music notation works, check out the comments in
txtseq/track1.txt
.The ASCII note transcription style used here is loosely based on the abc music standard, but the two notations are not compatible. In particular, this notation uses
{}
for chords, requires chord durations to be specified after the closing}
, and omits a lot of abc's features such as configurable key signature.The short summary:
Single note:
<accidental><pitch><octave><duration>
(e.g.C
_B,
c2
)Rest:
z<duration>
(e.g.z
z2
z16
)Chord note:
<accidental><pitch><octave>
Chord:
{<chord note><chord note>...}<duration>
(e.g.{C^DA}4
{ceg}
)Accidental:
_
(flat),^
(sharp), or the empty string (natural)Note:
C D E F G A B c d e f g a b
(C
is middle-c,c
is 1 octave up)Note aliases: you can use some shorter aliases to write percussion parts:
h
for^F _G
(closed hi-hat),H
for_B ^A
(open hi-hat), andr
for_e ^d
(ride cymbal)Octave:
,
(lower by 1 octave),'
(raise by one octave), repeated commas or single-quotes are cumulative (,,
lowers 2 octaves,'''
raises by 3). Examples:C,,,
d'
Duration: An integer representing the length of a note or chord as a multiple of the current time unit. Duration is optional with a default value of 1. With the time unit set for
1/8
,C2
would mean a quarter note of middle-c, andC
would be an eighth note.Staff: staff lines start with a voice number then have an arbitrary sequence of whitespace, bar lines (
|
), notes, and chords. Bar lines and whitespace are ignored by the parser, but you can use them to help organize your notes for better readability. It's fine to make long notes that last for more than one measure (e.g.C,16
with time unit set to1/8
would be played the same as 2 tied whole notes in 4/4 time)
Example:
2 | {CDG}4 {ACD}4 | C2 C2 D2 G2 |
For more examples, see
txtseq/track1.txt
Setting BPM and Time Unit
The
B
command sets bpm, which is a global setting that gets applied during playback. For example, a line withB 120
would set the playback speed to 120 beats per minute.The
U
command sets the time unit, which relates to the duration numbers. For example, You can set the time unit to 1 eighth note withU 1/8
command. In that case, if you wroteC2
in a staff, that note's duration would be 2 eighth notes (1 quarter note). Or, for quarter note triplets, you could use theU 1/4T
command. In that case, the duration of aC2
would be 2/3 of one quarter note.The time unit options are:
1/4
,1/8
,1/16
,1/32
,1/4T
,1/8T
,1/16T
, and1/32T
Bass Clef Percussion Notes on Voice 1
The General MIDI standard includes a mapping of percussion sounds for MIDI channel 10, notes 35 to 81. The sounds for a typical Western drum kit (kick, snare, hi-hat, cymbal, etc) use notes in the bass clef, starting approximately 2 octaves below middle C. To avoid having to write things like
B,,,
orC,,
every time you want a kick drum, voice 1 uses bass clef note names.Bass clef note names are 2 octaves (24 MIDI notes) lower than the equivalent treble clef note names. So, bass clef
C
is the same MIDI note asC,,
in treble clef.Also, because the hi-hats and ride cymbal would otherwise need to be spelled with a flat or sharp prefix, I included the aliases
h
,H
, andr
. For quick reference, these are the notes for some common drum sounds:Bass Note MIDI # Sound C 36 Electric Bass Drum E 40 Electric Snare ^F _G h 42 Closed Hi-Hat _B ^A H 46 Open Hi-Hat _e ^d r 51 Ride Cymbal Priority Scheduling of MIDI Messages
For a MIDI link that can move 1 message per 1 ms, playing a chord of 3 notes would take 3 ms to send. If the chord was meant to play on the same beat as a drum strike and a CC update, the whole group would take 5 ms to send. But, if the drum messages get sent first, the timing will sound tighter.
Considering that low latency matters more for percussion, giving scheduling priority to messages for the percussion (usually MIDI channel 10) should help to make the most of available MIDI bandwidth.
To allow for efficient percussion-priority sorting of MIDI events, I hardcoded the player to use the following txtseq voice to MIDI channel mapping:
voice MIDI channel Scheduling Priority 1 10 1 2 11 2 3 12 3 4 13 4 -
Raspberry Pi Pico Dice Programmed with CircuitPython Some design notes:
- The top of the cube (yellow, top facing down) has a bevel. This matches the:
- LED top section (pink) bevel - this keeps the top from sliding through the outer cube.
- The bottom layer of the bottom section (tan) is about 4mm thick. The switches are about 4mm tall, so they would be flush with the bottom, except in order to make one switch more prominent (the power switch) I carved out about 1mm so that switch protrudes about 1mm below the bottom.
- There is a box just large enough to squeeze in the battery holder keeping it firmly in the place in the assembled cube.
- On the outside of the battery holder box is a shallow half-box which supports the Pico vertically, with the USB facing downward. The box is about 5mm wider than the Pico itself so there is room for the wiring to extend bast the edge of the board connected to the GPIO pins. The wired Pico is hot-glued to the support plate to keep it firmly in place when connecting a USB cable.
- There is also 4mm groove in the base to allow the Pico's USB port to sit only 1mm above the bottom so a cable can be plugged in the assembled cube for updating the code without having to disassemble the cube.
3D Case Design
I used TinkerCad (https://www.tinkercad.com) for my 3D design work. The original thought was to make two halves - a top and bottom that overlap/slide together. but I ran into some difficulties with this design. Ultimately, I broke it down into 3 parts - an outer 70x70x85mm cube with a 50mm square "hole" through the middle. Then I made two 50mm cubes for the top and bottom that mate open end to open end and slide into center of the cube. The heights of each are one half the depth of the cube so when assembled the closed ends are flush with the top and bottom of the cube. The top has the 9 holes which just fit the 10mm LEDs. The bottom had places to support the Pico and battery back with cut-outs for the slide switches and the USB connector on the Pico.
Here are what the three parts look like in TinkerCad:
-
Adafruit Connection Manager What is the Adafruit Connection Manager? It is a helper class designed around making connections to the internet easier and does this by simplifying a few things.
First, what are the underlying pieces we need to connect to the internet?
Sockets
Everything that connects to the internet needs a socket. A socket is what handles the basic sending and receiving of messages between 2 devices (like your microcontroller and a web API).
Microcontrollers, unlike desktop computers, have limited memory and can't have 100s of sockets open. The average chip can have maybe 2-3 and the bigger ones top out around 10.
Previously the sockets were controlled at a per library level. Meaning if you used
adafruit_requests
(to get info from the web) andadafruit_minimqtt
(to send something to AdafruitIO) they both managed sockets separately, which means that one might block the other from getting one. And on top of that, the way you interfaced with them was different!Here comes ConnectionManager to the rescue! Both these libraries now ask the ConnectionManager for a socket and it handles tracking what's open and what's not. And to make code even simpler, it's what's called a singleton. There is only one, where previously you needed to create your
requests.Session()
early in your code and use that one everywhere, now it doesn't matter.Socket Pool and SSL Context Helpers
What are these?
The
socketpool
is what creates the sockets for each internet connected chip, and thessl_context
is what has all the certificate information to validate that the connection is secure.For the
Adafruit AirLift
(which uses an ESP32 WiFi Co-Processor): -
Look Ma, No Microcontroller! (Night Light Edition) Inspiration
This is a piece of "found art"; a multi-colored circuit board hiding inside some old discarded electronics. It just needed a backlight and a frame to highlight its beauty. From the beginning, I knew this would become a nightlight and held onto the board until I had acquired the perfect combination of discarded LEDs and a few other components needed to make it shine.
Case Design
My 3D printed case had 3 chambers. One for the electronics, one for the LEDs, and a thin slot a the front to hold the circuit board and clear plastic cover. The reason for the separation was to make sure the LEDs were the right distance to illuminate the board and to minimize the light that leaked out that back of the frame while still allowing hot air to escape. The design was almost perfect, but I ended up having to cut a large slot in the back so I could replace the back wall with a slotted version for better ventilation (not pictured).
Before closing up the case, test that it can be dimmed appropriately.
-
Web API's & You Below is a test of the Playground Note code embed directly from Github (must be .md or .py file). If you see the full code below it is working as intended. If the example in the library gets updated this Playground Note might not be kept up to date with future changes. The code below will always be the most up to date as it is pulled directly from the adafruit_requests examples library.
Full Example Code
- An uncomprehensive & comprehensible guide to using Adafruit Requests with web API's.
The amount of online API examples for the Adafruit_Requests library is growing. If you're interested in using an API but an example does not exist and you're unsure how to start I will help walk you through the process.
I wrote the majority of the web API examples currently in the examples directory. I'm a pretty good source on how to approach a new unknown web API and wrangle out some basic data.
JSON
Data is malleable, it can be shaped and formed in a variety of ways. The most common format currently used with REST API's is JSON. The Adafruit_Requests library is particularly good with JSON data. If you don't know how to read or write in JSON don't worry, you won't need to. The library handles all the format conversions for you. All you need to know is how to get at the data you want and I will walk you through how to do that.
Before we begin there are some glossary terms that you'll need to know in order to make sense of JSON for the web.
REST
Representational State Transfer (REST) is a software architecture that imposes conditions on how an API should work. Most web API's use the REST architecture. All a beginner needs to know about REST is it's the way a website allows you to retrieve data from an API.
Endpoint
An endpoint refers to the data to retrieve from an API. This can be part of the JSON heirarchy path or a key:value pair. For web API's typically this refers to the JSON Key:Value pair.
Key:Value
- Key:Value (always a pair separated by a colon)
A key is a unique identifier associated with data and the value is the actual data. An example would be if you're working with a weather API and might want to return current:temperature for a geographic area to display freedom units on a TFT. It would return as `current:70.0` (in Fahrenheit) or if you're living in a sensible country using the metric system it would return `current:21.1` (in Celsius).
Key Error
It's useful to know the above terms because an error that a website or Circuit Python might return is `invalid key:value pair` or simply `key error`. That error means you requested a key:value that does not exist, you spelled it incorrectly, or the request was otherwise incorrectly formatted.
-
Stuff My Dog Said (via AdafruitIO) Inspiration
This project is an extension of the original doggy buttons project where I'll be showing how to make a public dashboard that publishes everything my dog says. This project is just one example of how to extend the features of my doggy buttons and perhaps one of the silliest IOT devices ever made. If you want to know about the buttons themselves, see the original project writeup.
With a project like this, it's also important to think about privacy. I don't want my dog making it possible to tell when we're away from home, so I'll also show how to add a delay so that activity is only published once it's sufficiently stale.Setup Challenges
User Space vs Admin Space
The first version of my program was kicked off by
/etc/rc.local
on boot, but this had the side effect of running the program asroot
. That was fine for my original project, but I had followed best practice and not usedsudo
whenpip3 install
ing the Python libraries needed for loading data to AdafruitIO. If I logged in to run the program as my user, it would end when I logged off butroot
no longer had all the Python libraries needed to run.Lennart (Binary Labs) suggested the simple and elegant solution of adding
sudo -u MY_PI_USERNAME
before the command that would launch my code in/etc/rc.local
. This allows the program to be run as my user without having to login and was much simpler and cleaner than the other solutions I was considering.Concurrency
The first version of my code simply added a call to
aio.send_data(...)
in the same loop that processed button presses, but I quickly discovered that the upload process could take 3 - 4 seconds leading to very delayed sounds when more then one button was pressed. Things like "water outside walk later" became "water ... outside ... walk ... later". This was starting to confuse my dog because the sounds weren't immediately connected with the button presses and would sometimes sound when she had gone to push something else entirely because the button "wasn't working".To fix this it was going to be necessary to introduce separate queues, one for playing sounds that could respond quickly, and one for uploading button presses that could slowly upload data to AdafruitIO in the background.
-
NeoPixel Stick and Pi Pico Need to add a simple diagnostic display to a Pico project? Or perhaps you want a quick and easy NeoPixel light show? Let's see what we can do!
Equipment
-
Reading Pixels from the RA8875 Driver Board Intro to the RA8875 Driver Board for Circuit Python
The Adafruit RA8875 driver in Circuit Python does not currently support displayio. You must use read/write registers with a barebones ra8875 graphics library. The current feature set and how it is used is only for very advanced users.
You can draw a bmp image and overlay text but you'll quickly find that's about all you can do with it. There are only 2 examples provided and the driver board is unlike any other display device for Circuit Python. Any knowledge you have of displayio does not transfer over to this board; the RA8875 is unique.
The interest of using an 800x480 bare display with Circuit Python is typically due to the sheer size of it but it should come with fair warning: You must be capable of programming with circuit python from scratch without displayio.
The bits and bytes of binary color
Since the RA8875 can only display a maximum of 16-bit color; the 24-bit image must be converted to 16-bit (color565).
The RA8875 stores color information in its memory with 2 bytes (2 pairs of 8 bits). Here is a binary representation of how it stores the color. 11111111 00000000 There are 8 bits in 1 byte.
However the RA8875 actually stores them in what is known as swapped color565. Each first byte must be swapped with the 2nd. This is not some type of color conversion error. These are the direct reads from the memory addresses for the stored colors.
-
The Necrochasm: Pushing the Prop-Maker RP2040 to its limit! This is a project that was years in the making, I went through many iterations that failed one way or the other. However, the final project was only started about a few months ago. It's great to see this project realized at last! The trigger really works, it has the appropriate sound effects, it has two firing speeds, and a "virtual ammo" system that you replenish by physically removing and reinserting the cartridge. So, where did it all begin?
The initial step of the process was to design the 3D printable case, which I did in blender. I found someone who extracted a model of the Necrochasm from the game itself and went to work sculpting out the finer details, hollowing out the interior, and adding LED areas. I also took this time to log into Destiny, and use my in-game Necrochasm to record the proper sound effects.
I exported the resulting pieces from blender to fusion 360 where I added the hardware mounting features. My vision for this prop was to have one area where most of the electronics were stored. This trapezoidal area at the bottom-front of the prop looked like it had the most storage capacity.
-
CG-35: A Retro RPN Calculator The CG-35 is a CircuitPython emulation of the Hewlett Packard HP-35 Scientific Reverse-Polish Notation (RPN) calculator designed for the Adafruit ESP32-S3 Feather and 3.5-inch TFT FeatherWing capacitive touch display. The calculator consists of a 10-digit LED-like display backed-up with 20-digit internal calculation precision.
This emulation reproduces the HP-35 calculator's v2.0 firmware where the change sign (CHS) key is active only after digit entry has begun. And because of the
udecimal
andutrig
classes, calculation accuracy of monadic, dyadic, and trigonometric functions was improved. As an added bonus not present on the original calculator, a status message will appear just below the primary display when a calculation error is encountered.The calculator's graphical layout was designed to mimic the aspect ratio of the original calculator -- that's why the left and right sides of the display screen were left empty. However, to provide a more reliable touch screen experience, the keys are somewhat proportionally larger than the original.
This project was inspired by Jeff Epler's DIY Desktop Calculator with CircuitPython project and Jeff's work to create CircuitPython versions of
udecimal
andutrig
. Thank you Jeff!GitHub Repository: https://github.com/CedarGroveStudios/CG-35_Calculator
Primary Code Module
The primary code module
cg_35_calculator.py
, imported bycode.py
, instantiates the display, plots the calculator case and buttons, and implements all calculator operational processes. This module uses a state machine design with the following named states:- IDLE -- Display results or wait for input
-
C_ENTRY -- Coefficient entry (keys:
0
-9
,.
,CHS
,EEX
) -
E_ENTRY -- Exponent entry (keys:
0
-9
,.
,CHS
) -
STACK -- Stack management (keys:
ENTER
,CLR
,CLX
,STO
,RCL
,R
,x<>y
,π
) -
MONADIC -- Monadic calculator functions (keys:
LOG
,LN,
e^x
,√x
,ARC
,SIN
,COS
,TAN
,1/x
) -
DYADIC -- Dyadic calculator functions (keys:
x^y
,-
,+
,*
,÷
) - ERROR -- Calculation error
The calculator's display precision and internal calculation precision are specified using the variables
DISPLAY_PRECISION
andINTERNAL_PRECISION
. Although the internal precision exceeds that of the original HP-35 calculator, it is recommended to keep the existing default settings of 10 digits and 20 digits, respectively, to avoid rounding errors.The variable
DEBUG
can be use to provide additional internal register status via the REPL. This boolean variable defaults toFalse
(no additional register status). -
IKEA Förnufig Air Purifier V2 - Custom fan speed controller + Blinkenlights Basic premise:
Add lights (speed / noise / air-purity indicators, or needless dotstar+neopixel love), use small board to receive tachometer input and drive 24V fan PWM signal from particle sensor, along with new inputs for noise level. Plus show off Blockly based programming on Adafruit IO, and update as and when the new maths functions become available, but for now write a quick CircuitPython version that illustrates all the desired functionality (to port to Adafruit IO).Semi-finished code: - Functionally capable but no reactive light use, rainbow:
[Reproduced from https://github.com/tyeth/Ikea_ItsyBitsyEsp32_Air_Purifier/ ] -
Wippersnapper - Sensirion SEN55 Particulate/VOC/NOx sensor - Plus an educational saga in the quest for a case... Wippersnapper
For those not in the know, WipperSnapper is Adafruit's plug-and-play firmware, which runs on their IO (think IOT) platform, offering free* data history(feeds) with graphs, dashboards, and automatic actions/triggers (along with integrating with other platforms like IFTTT). There is a paid upgrade for longer history, unlimited devices, SMS alerts, etc.
I love it because it's super quick and easy to test a sensor works, and to just get some data recording quickly.
Goto a web-page, flash over usb, add sensors via control panel (feeds + graphs are automatic), done.
Under the hood it's an arduino sketch, so adding additional sensors via github pull requests is surprisingly easy (I've added a few because it makes my future tasks easier).
For this project I'm testing the Sensirion SEN55, which senses Nitrous Oxides (NOx), Volatile Organic Compounds (VOCs), with temperature and relative humidty as a reference (uses SGP40/41 inside), and measures particle counts at <1.0 micron, <2.5, <4.0, and <10.0 micron. The other models of this sensor have less features (SEN54: no NOx, SEN50: no NOx/VOC/Relative Humidity+Temp - only Particulate Counts).
From the standard drivers(arduino/Pi) created by Sensirion we can retrieve the typical particle size, along with the raw particle counts (or in SI units ug/m3), the temperature and humidity, and then two indexes for NOx and VOC.
The NOx index has a baseline of 1.0, and if you hide the sensor under an upside down saucepan and use a lighter under the saucepan before sealing it back up then you will see a rise in the NOx index and then a return to baseline (1).
The VOC index is instead based at 100. You can detect VOC events from many things, the human breath can even be a source. I tested mine with a jar of clear nail varnish, but anything which you can smell, or smells chemically, is probably going to affect the sensor.Connectivity-wise, it uses I2C, or other methods (UART?) which are as yet unpublished. The connector requires a JST-GHR 1.25mm 6 Pin compatible cable. The development kits include such a cable, but are heavily marked up cost-wise, like an additional 150% (£50 for kit, £20 for bare sensor). I advise getting a bare version with an additional cable from elsewhere (coolcomponents have a cable under £2). There is also a Grove connector version from seeedstudio.
-
Hiking Masterpiece Overview
Welcome to the Adafruit guide on creating a stunning Badger Mountain themed art piece! In this guide, we'll show you how to bring your artistic vision to life using the powerful Feather RP2040 microcontroller, along with an array of components including audio jacks, buttons, and LED strips.
Circuit Diagram
-
Running Pi-HATs with a Raspberry Pi Pico Running Pi-HATs with a Raspberry Pi Pico
Being a Pi-user since the very first Pi1, I own many different Pi-HATs. Some of them are in daily use, but many of them are sitting in the shelf. So I wondered if I could give them a second chance in combination with a Pico. And one of my major use-cases are e-ink displays for the Pi. These e-inks don't really match with the Pi, since they are optimized for low-power scenarios. But even the Pi-Zero drains batteries too fast to be a suitable partner for these kind of displays.
Although equipped with a full 2x20 pin socket, most HATs only use a few pins like power, ground, I2C or SPI. So using a bunch of jumper cables should already be sufficient. Although that is true and fine for initial tests, a good and solid connection is always the better alternative.
So I did some research and discovered a few adapter-boards on the market that might be suitable. But on closer inspection it turned out that most of them missed one important point: just mapping some arbitrary pins is not enough. So I decided to create my own adapter boards.
Hardware
One board uses the Pi form-factor, the second uses the Pi-Zero form-factor:
They fit into standard enclosures, but since the USB-connection is on the side they need an additional cutout. Changing available 3D-models should be a simple task. And the bigger adapter PCB has a footprint for a standard JST-2 battery connector exactly where the USB-power cutout is. Anyhow, this was not the major challenge when designing these boards.
The biggest challenge was the correct pin-mapping. The Pi has I2C, UART, two SPI and I2S. I did not take the last one into account so I ended up with two revisions. I2S needs two consecutive pins. On the Pi, that is GPIO18 and GPIO19, but they are not next to each other on the pin-header.
Another contraint was space. I did not want to route traces below the WLAN-chip and antenna of the Pico-W. In the end I had to make a compromise for the Pi-Zero adapter: the first revision maps both SPIs but not I2S, the second revision maps SPI0 and I2S but not SPI1. Which is not a big deal since I haven't found a HAT yet that actually uses SPI1.
I also don't map the ID-pins of the Pi. These are used to automatically configure the correct driver on the Pi. On the Pico, you don't run a generic OS but a specific program, so you have to take care about correct drivers already before when you put them on the CIRCUITPY-drive.
The Pi-adapter has more space. I added a SD-card reader and I broke out a number of pins. One of the drawbacks of many HATs is that they block the complete pin-header although they only use a few pins. Breaking out the pins is not strictly necessary since you can access all pins from the back anyhow.
Software
The second part of the project was porting the HAT-drivers to the Pico. For Adafruit HATs, that was fairly simple. Adafruit has CircuitPython support for almost everything they sell. And since Blinka brings CircuitPython to the Pi-SBCs, "porting" the drivers is a matter of using the correct pins.
On example: the speaker-bonnet. The learning guide (https://learn.adafruit.com/adafruit-speaker-bonnet-for-raspberry-pi) tells you it is using the I2S pins GPIO18, GPIO19 and GPIO21 on the Pi. After looking up the mapping for the adapters, you just plug in those pins into a small example program provided by a second guide: (https://learn.adafruit.com/mp3-playback-rp2040/pico-i2s-mp3) and off you go playing MP3 on the speaker-bonnet. This is actually much simpler on the Pico compared to the Pi, because you don't have to go through all the steps to install the relevant drivers.
For other HATs, you will usually find CircuitPython example code for the Pi using Blinka in the learning guide for the HAT. In this case, you can take the code as is and only replace the pin-numbers.
I also own a number of HATs from Pimoroni. They don't provide CircuitPython drivers, but at least for some of the HATs there are ready to use drivers for the builtin driver-IC. In only a few cases I had some real porting work to do. But once I found out how to translate CPython I2C/SPI-calls to CircuitPython, the porting was straightforward.
Project-Repository
You can find the project repository here: https://github.com/bablokb/pcb-pico-pi-base. The repo has KiCad design files as well as ready to use production files for my preferred PCB manufacturer.
Also in the repo are CircuitPython libraries and example code for all the HATs I tested or ported.
Next Steps
What I might do in the future is to create a similar adapter PCB for the Feather form-factor. While in my current designs the Pico sits inbetween the PCB and the HAT, with the Feather I would probably make the Feather plug in from behind.
The second thing I am working on is to support the new Waveshare ESP32-S3-Pico. This is an ESP32-S3 in the Pico form-factor with identical physical dimensions and identical pin-layout. This breakout is interesting since it gives me a device with far more memory than the Pico provides. And I don't have to create new adapter boards. First results look promising.
CircuitPython Board Definition Files
Since I have two form-factors and two revisions each with their own pin-mapping, looking up the mapping is cumbersome. So I also created my own CircuitPython versions that do the mapping for me. So
board.GPIO18
will always map to the correct pin on the Pico, regardless which PCB I use.With two form-factors, two revisions and now three devices (Pico, Pico-W, EPS32-S3-Pico) I have a total of 16 combinations, thus potentially 16 CircuitPython versions. A lot to maintain, but not all combinations are actually in use (yet).