# Digital Circuits 6: An EPROM Emulator

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/053/677/medium800/components_EPROM.jpg?1526080666 The classic 2716 2Kx8 EPROM.)

I recently designed and breadboarded a simple Z80 based computer. This was the sort of thing I did for fun and profit years ago. This time it was, well, for fun and profit. But it was a little different. Back in the day I had a drawer full of Erasable Programmable Read-Only memory chips (EPROMs), as well as an EPROM eraser and programmer\*.&nbsp; &nbsp;This time I had none of that. So I'd have to get inventive.

When I designed the system, I put in 2K of ROM/EPROM and 2K of static ram. Naturally when I bought parts, I got extras. So, that meant I had an extra 2K static ram chip sitting in a drawer. That's a powerful thing: having extra parts sitting in drawers. What I decided to do was build an EPROM emulator using that ram chip, some transistor/transistor (TTL) logic and a microcontroller board.

I was using a Z80 assembler (`z80asm` from&nbsp;[z80pack](http://www.autometer.de/unix4fun/z80pack/)) on Linux to write code for the Z80, so using a microSD card seemed a reasonable way to get code from Linux to the emulator. Once I had decided to do that, I needed a way to navigate the file system on the card, select a binary file, and load it onto the RAM in the emulator.&nbsp; See how things snowball!

To make the project a bit simpler and more modular, I chose to divide it into two separate parts: the controller/user interface (UI), and the emulator. To maximize the flexibility of that separation, I decided to connect them using an I<sup>2</sup>C bus. That proved to be a very good choice as I went through several iterations using different microcontroller boards: from an Arduino Mega2620 to an ItsyBisty M0 Express and finally to an Metro M4 Express.

While I designed this project to support my work on a Z80 system, it can be used to replace a 2716 EPROM in any system. In this guide, I'll call that system the _host_.

<sup>* I'm planning to build a new take on an EPROM programmer sometime in the near future which will share some features of this project, and in some ways could be considered as Part 2. It will most likely use the same controller/ui design.</sup>

## Parts Used
### Part: MicroSD card breakout board+
quantity: 1
MicroSD card breakout board+
[MicroSD card breakout board+](https://www.adafruit.com/product/254)

### Part: Monochrome 128x32 I2C OLED graphic display
quantity: 1
Monochrome 128x32 I2C OLED graphic display
[Monochrome 128x32 I2C OLED graphic display](https://www.adafruit.com/product/931)

### Part: Rotary Encoder + Extras
quantity: 1
Rotary Encoder + Extras
[Rotary Encoder + Extras](https://www.adafruit.com/product/377)

### Part: Adafruit Metro M4
quantity: 1
Microchip ATSAMD51
[Adafruit Metro M4](https://www.adafruit.com/product/3382)

### Part: 4-channel I2C-safe Bi-directional Logic Level Converter
quantity: 1
4-channel I2C-safe Bi-directional Logic Level Converter - BSS138
[4-channel I2C-safe Bi-directional Logic Level Converter](https://www.adafruit.com/product/757)

### Part: MCP23017 - i2c 16 input/output port expander chip
quantity: 1
MCP23017 - i2c 16 input/output port expander
[MCP23017 - i2c 16 input/output port expander chip](https://www.adafruit.com/product/732)

### Part: Silicone Cover Stranded-Core Wire 30AWG
quantity: 1
Silicone Cover Stranded-Core Wire - 50ft 30AWG Black
[Silicone Cover Stranded-Core Wire 30AWG](https://www.adafruit.com/product/3164)

# Digital Circuits 6: An EPROM Emulator

## Interface Hardware

![](https://cdn-learn.adafruit.com/assets/assets/000/053/673/medium800/components_Controller.jpg?1526078315)

The interface hardware is pretty simple. My end goal is to build it around an ItsyBitsy M4 Express board. Alas there aren't any yet, so I did the next best thing: I build it as a Metro M4 Express shield. Not the compact form factor I wanted, but all the same capabilities.

![](https://cdn-learn.adafruit.com/assets/assets/000/053/670/medium800/components_arduino_controller.png?1526052707)

There's a microSD breakout hooked to the SPI lines, a 128x32 OLED on the I<sup>2</sup>C lines, and a rotary encoder (with push switch) using 3 digital inputs.&nbsp;

See the related guides for more details on each of these:

- [Metro M4](../../../../adafruit-metro-m4-express-featuring-atsamd51?view=all "Metro M4")
- [microSD Breakout Tutorial](../../../../adafruit-micro-sd-breakout-board-card-tutorial?view=all "microSD Breakout Tutorial")
- [OLED Displays](../../../../monochrome-oled-breakouts?view=all#wiring-128x32-i2c-display "OLED Displays")
- [Rotary Encoder](../../../../circuitpython-essentials?view=all#circuitpython-digital-in-out "Rotary Encoder")&nbsp;

Because I made the decision to use I<sup>2</sup>C between the interface circuit and the emulator circuit, any MCU with I<sup>2</sup>C capabilities would work.

One small wrinkle: the Metro M4 is a 3.3v system while the emulator is 5v. Adafruit to the rescue with the&nbsp;BSS138 based _4-channel I<sup>2</sup>C-safe Bi-directional Logic Level Converter._ We just need to run the I<sup>2</sup>C lines through this between the two circuits.

![](https://cdn-learn.adafruit.com/assets/assets/000/053/674/medium800/components_Controller_back.jpg?1526078456 The 30 gauge silicon covered hookup wire (black is listed in the related products sidebar) has revolutionized my builds. Red is power, Black is ground, blue is SPI, yellow is SCL, green is SDA, white is the digital signals for the encoder, and for some reason I used a bit of green and white for reset.)

# Digital Circuits 6: An EPROM Emulator

## Emulator Hardware

![](https://cdn-learn.adafruit.com/assets/assets/000/053/675/medium800/components_Emulator.jpg?1526080009)

## A Tale of Two Memories

For my host design I used what I knew: the 2716 EPROM and 6116 static RAM.&nbsp; The nice thing about this pair of chips is that they are pin for pin compatible: you can swap them in a design.

We can take advantage of this in the emulator to replace the 2716 on the host board with a 6116 in the emulator. That way we can put code into the 6116 (since it's a RAM) and the host treats it like an EPROM.

If we just plugged the 6116 into the host there would be no way to get code into it. So we need to have it accessible from a controller. That would allow us read binary files from an SD card and write them to the 6116, then let the host use it in place of the 2716 it thinks is there.

The question is how to make that happen.

## Dual porting the 6116

Basically we want to dual port the 6116: let two different systems use it. The controller would write into it and the host would read from it.

**History note** : the Apple ][design dual ported the system RAM to allow both the CPU and the video system to have interleaved access (the video hardware had access while the CPU was in it's "working internally" phase of each cycle. This allowed for very simple, efficient display hardware, with the bonus that video access took care of the dynamic RAM's refresh requirements (see [Digital Circuits 5: Memories](../../../../digital-circuits-5-memories) for more on dynamic RAM).

Below is the schematic of the emulator. At the bottom-left and bottom right is the address and data related signals, respectively, from the host.

The data bus from the host (on the right) is connected to the 6116 through a tristate buffer (the '244) that isolates the RAM from the host while the emulator is loading data into the RAM. The output enable of the RAM is controlled by the same signal that controls that '244. That's what the NAND gates at the bottom are for. The output of the RAM and the buffer to connect the RAM's data pins to the host are controlled by the output enable from the host EPROM connection, gated by the emulator's mode signal (low when the 6116 is being loaded, high when the host has access). Using NANDs and inverting the host's output enable means that data flows from the 6116 to the host when the host asks for it (the /OE from the host is low) and the emulator is in host access mode.

There is another '244 connecting the controller to the data bus of the 6116 while the emulator is in program mode. More about that later.

Now consider the address bus from the host (bottom left). It doesn't connect to the 6116 directly or though a buffer. Instead it goes connects to the B sides of 11 dual input multiplexers (the 74LS157s in the center of the schematic). 2K of memory needs an 11-bit binary number to cover all locations. The chip enable signal from the host goes through a multiplexer as well. The outputs of the multiplexers connect to the corresponding pins of the 6116.

The other inputs to the multiplexers? They come from a bank of 4-bit binary counters (on the left in the schematic) that are chained together to make a single 12-bit counter. Since we only need 11 address lines, the highest valued output of the counter is not used. The A side of the final multiplexer connects to a chip select control signal described later.

The outputs of the multiplexers go to the corresponding input of the 6116.

![](https://cdn-learn.adafruit.com/assets/assets/000/053/700/medium800/components_emulator.png?1526137614)

## Controlling the Emulator

So that explains the dual-porting,&nbsp; the host connection, and the emulator address generation.&nbsp; Let's turn our attention to the top part of the schematic. Core to this is a MCP23017 that is controlled via I<sup>2</sup>C by the control board.

Port A is simple: it's just the 8-bit data to be placed into the 6116. When the emulator is in programming mode the second '244 is enabled, connecting the output of port A to the 6116's data pins.

Port B is a collection of single-bit control signals, starting with bit 0:

- /PROGRAM is low when the emulator is in program mode: i.e. the 6116 address is provided by the counters, and it's data by port A of the 23017; the host is cut off from the 6116 in this mode. When /PROGRAM is high, the 6116 is connected to the host.
- /WRITE connects to the write enable input of the 6116. When it goes low the values on its data pins are written into the location specified by the values on its address pins.
- /SELECT connects to the 6116's chip enable pin when in program mode. This serves to enable the chip for subsequent reading writing. The 6116 does nothing unless its chip enable pin is low. When in EMULATE mode, the chip enable comes from the host.
- ADDR\_CLK increments the address counter when it is pulsed low briefly.
- ADDR\_RST is normally low, and making it high briefly resets the address counter to 0.
- INDICATOR is connected to an LED simply as an indication of when the emulator is in EMULATE mode. I used a separate port B output for this due to the current draw of an LED. Just to be safe, and since I had port B outputs to spare.

## Loading the 6116

Loading data into the 6116 is simple, but the sequencing has to be right:

- Place the emulator into PROGRAM mode (take /PROGRAM low)
- Reset the address counter (pulse ADDR\_RST high)
- For each byte to be programmed:
  - Output the next byte on port A of the 23017
  - Activate the 6116 (take /SELECT low)
  - Write the data (pulse /WRITE low)
  - Deactivate the 6116 (take /SELECT high)
  - Advance the address counter (pulse ADDR\_CLK low)

- Place the emulator into EMULATE mode (take /PROGRAM high)&nbsp;

One limitation incurred by using an address counter (rather than an additional 23017 to provide the address) is that arbitrary locations cannot be written. That's not a problem since the use case is to load the 6116 with the contents of a 2K EPROM; writing arbitrary locations isn't required.

One advantage of using an address counter is that it provides a better example of the circuits we've been looking at in previous parts of this guide series.

This circuit has a little of everything: logic gates, counters, multiplexers, and memory. It also includes the 74244 chip that we saw back in part 2. Let's have a closer look this time.

![](https://cdn-learn.adafruit.com/assets/assets/000/053/676/medium800/components_Emulator_back.jpg?1526080117 Green: address from counter to mux. Yellow: address from the EPROM connector to mux. White: address from mux to RAM, and control lines. Blue: data, you can see it flow from the 23017 at the bottom to the 244 to the RAM to the other 244 at the top and to the EPROM connector.)

# Digital Circuits 6: An EPROM Emulator

## An aside: The Octal 3-state Buffers

## 3-state

Three state?&nbsp; But, you say, "I thought binary had 2 states".&nbsp; Well it does: high and low. But what about "disconnected", i.e. no signal?&nbsp; That's the third state we're talking about.&nbsp; A signal line in this third state, aka hi-impedance aka open-collector, doesn't provide a logic level, or anything; it's electrically disconnected. If you have multiple signal sources all running through 3-state buffers, you can enable 1 of them at a time and its logic value will propagate onward.

It's a handy way to basically turn off a signal. If you remember back to part 1, I said that an logic output could connect to some number (limited by its fan-out) of logic inputs, but that multiple outputs could not connect to a single input?&nbsp; Well, 3-state buffers are the way around that. They let you connect one output at a time to an input, controlled by other logic signals.

## Octal 3-state buffer chips

Consider this family of octal tri-state buffer chips: 74240, 74241, and 74244. They are slightly different from each other, and all incredibly useful. All three include two sets of four tri-state buffers, each set with it's own enable input. They differ along two axis: whether the the buffers are inverting or non-inverting, and whether the two enable inputs are both active low or one active low and one active high.

The '240 is the only one of the three with inverting buffers; the '241 and '244 are both non-inverting. The '241 is the only one of the three with one active low enable, and one active high enable; the '240 and '241 have two active low enable inputs.&nbsp;

![](https://cdn-learn.adafruit.com/assets/assets/000/110/944/medium800/components_2140-241-244.png?1650504692)

Why would you want the two enable inputs to be different?&nbsp; Notice how the two sets of buffers go in the opposite direction. Consider if you connect pairs together as well as connecting the enable inputs together on a '241:

![](https://cdn-learn.adafruit.com/assets/assets/000/053/659/medium800/components_74241-bidirectional.png?1525898811)

What we have now is a bidirectional 4-bit buffer. The DIR signal controls which buffer of each pair is enabled. That determines whether data flows A-\>B or B-\>A because only one buffer in each pair will be enabled, the buffer going the opposite direction will be disabled. This is much like the 74245 buffer chip we'll look at shortly.

Another potential use is to make a 4 2-input multiplexers:

![](https://cdn-learn.adafruit.com/assets/assets/000/053/837/medium800/components_74241-multiplexer.png?1526226671)

Here, the enable pins are tied together as a select input that enables (as before) one buffer from each pair. In this case, though, the outputs of the pair of buffers are tied together and each input is part of an A or B set of signals. Now the select input choose either A or B to be connected to the O outputs.

## The 74245 Octal Bus Transceiver

The '244 and crew are great if you need data to flow (or not) in a single direction. They can even be convinced to let you control the direction of bidirectional data flow. We did that in the above circuit. However, while it lets us control the direction of flow, we lost the ability to disable it entirely. Also, it left us with only 4 bits being controlled. Computers, especially since 8-bit CPUs became a thing, like to have groups of signals that are multiples of 8 in size.

Add to this that bidirectional, and enable/disable control over a set of related signals (generally referred to as a bus) is incredibly useful.&nbsp; Enter the 74245.

![](https://cdn-learn.adafruit.com/assets/assets/000/053/660/medium800/components_74245.png?1525899773)

This chip has it all. Eight bits wide, direction control, and an overall enable/disable. You'll be hardpressed to find an 8-bit computer that didn't use one of these to buffer its data bus (and likely a couple of '244s buffering it's address bus). The 8-bit CPUs did not have adequate fan-out to handle the amount of circuitry and peripheral chips in a typical computer, so their buses had to be buffered and '244s and '245s were the chips for the job.&nbsp;

Note that we could build an equivalent circuit with a couple '241s and some gates. But that would take 2 20-pin chips and a couple 14-pin ones. The '245 does it all in a single 20-pin package.

One notable thing about the '245 is that all the As are on one side and all the Bs are on the other. This isn't that big of a deal on a PCB, but on a breadboard it's really handy to have a bus go in one side of the chip and out the other. So much so that I'll use this in place of a '244 for one-direction buffering of a bus. All that needs to be done is to hardwire the DIR input to have signals flow in the desired direction.

# Digital Circuits 6: An EPROM Emulator

## CircuitPython Code

Like the hardware, the software is very modular:

- Overall setup, and control, including handling the rotary encoder
- A class representing a directory on the SD card with the ability to navigate up and down the tree
- A class to manage the emulator
- A helper class to properly debounce the encoder push switch (this is usable for any use of input switches)

## Main

Main is simple enough:

- Initialize things
- Helper functions
- Loop, handling the rotary encoder and its switch and using the results to manipulate the current directory node and the emulator

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/EPROM_Emulator/code.py

## Directories

The DirectoryNode class provides the ability to navigate a file system. It was written with an SD filesystem in mind, but just relies on the `os` module so should work with any storage that `os` uses.

The class provides public properties to get the name and full path of the selected file, as well as methods to force a screen update, move up and down in the displayed list, and do the appropriate thing in response to a selection event (in this case pushing the rotary encoder's switch).

When you create the root level instance, you provide an oled instance to display on, as well as the name of the root (which defaults to "/").

```
i2c = busio.I2C(board.SCL, board.SDA)
oled = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c)
...
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
...
current_dir = DirectoryNode(oled, name="/sd")
current_dir.force_update()
```

Use of the `DirectoryNode` is straightforward as well, We can call `up()`, `down()`, and `click()` in response to activity on the rotary encoder, the `DirectoryNode` instance takes care of maintaining the display:&nbsp;

```
if current_mode == PROGRAM_MODE:      # Ignore rotation if in EMULATE mode
        if encoder_direction == -1:
            current_dir.up()
        elif encoder_direction == 1:
            current_dir.down()

    # look for a press of the rotary encoder switch press, with debouncing
    button.update()
    if button.fell:
        if current_mode == EMULATE_MODE:
            program()
        elif is_binary_name(current_dir.selected_filename):
            emulate()
        else:
            current_dir = current_dir.click()
```

Notice that the `click()` method returns a `DirectoryNode` which is assigned to the current node. That's because clicking could result in moving to a child or parent directory.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/EPROM_Emulator/directory_node.py

## Emulator

The emulator class manages the emulator circuit. Its public interface is simple enough:

- enter program mode, to allow the RAM to be loaded
- enter emulation mode, giving control of the emulator RAM to the host
- load the emulator RAM with data

The `load_ram` method takes care of the controlling the the address counters and multiplexers. By decomposing into a handful of private methods, the class is quite straight forward:

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/EPROM_Emulator/emulator.py

# Digital Circuits 6: An EPROM Emulator

## Series Index

1. [Binary, Boolean, and Logic](https://learn.adafruit.com/binary-boolean-and-logic)
2. [Some Tools](https://learn.adafruit.com/some-digital-tools)
3. [Combinational Circuits](https://learn.adafruit.com/combinational-logic)
4. [Sequential Circuits](https://learn.adafruit.com/digital-circuits-4-sequential-circuits)
5. [Memories](https://learn.adafruit.com/digital-circuits-5-memories)
6. [An EPROM Emulator](https://learn.adafruit.com/digital-circuits-6-eprom-emulator)
7. [MCUs... how do they work?](https://learn.adafruit.com/mcus-how-do-they-work)


## Featured Products

### MicroSD card breakout board+

[MicroSD card breakout board+](https://www.adafruit.com/product/254)
Not just a simple breakout board, this microSD adapter goes the extra mile - designed for ease of use.

- Onboard 5v-\>3v regulator provides 150mA for power-hungry cards
- 3v level shifting means you can use this with ease on either 3v or 5v systems
- Uses a proper level...

In Stock
[Buy Now](https://www.adafruit.com/product/254)
[Related Guides to the Product](https://learn.adafruit.com/products/254/guides)
### Monochrome 128x32 I2C OLED graphic display

[Monochrome 128x32 I2C OLED graphic display](https://www.adafruit.com/product/931)
These displays are small, only about 1" diagonal, but very readable due to the high contrast of an OLED display. This display is made of 128x32 individual white OLED pixels, each one is turned on or off by the controller chip. Because the display makes its own light, no backlight is...

No Longer Stocked
[Buy Now](https://www.adafruit.com/product/931)
[Related Guides to the Product](https://learn.adafruit.com/products/931/guides)
### Rotary Encoder + Extras

[Rotary Encoder + Extras](https://www.adafruit.com/product/377)
This rotary encoder is the best of the best, it's a high-quality 24-pulse encoder, with detents and a nice feel. It is panel mountable for placement in a box, or you can plug it into a breadboard (just cut/bend the two mechanical side tabs.) We also include a nice soft-touch knob with an...

Out of Stock
[Buy Now](https://www.adafruit.com/product/377)
[Related Guides to the Product](https://learn.adafruit.com/products/377/guides)
### Adafruit Metro M4 feat. Microchip ATSAMD51

[Adafruit Metro M4 feat. Microchip ATSAMD51](https://www.adafruit.com/product/3382)
Are you ready? Really ready? Cause here comes the fastest, most powerful Metro ever. The **Adafruit Metro M4** featuring the **Microchip ATSAMD51**. This Metro is like a bullet train, with its 120MHz Cortex M4 with floating point support. Your code will zig and zag...

In Stock
[Buy Now](https://www.adafruit.com/product/3382)
[Related Guides to the Product](https://learn.adafruit.com/products/3382/guides)
### 4-channel I2C-safe Bi-directional Logic Level Converter

[4-channel I2C-safe Bi-directional Logic Level Converter](https://www.adafruit.com/product/757)
Because the Arduino (and Basic Stamp) are 5V devices, and most modern sensors, displays, flashcards, and modes are 3.3V-only, many makers find that they need to perform level shifting/conversion to protect the 3.3V device from 5V. Here we've got a **&nbsp;4-channel I2C-safe...**

Out of Stock
[Buy Now](https://www.adafruit.com/product/757)
[Related Guides to the Product](https://learn.adafruit.com/products/757/guides)
### MCP23017 - i2c 16 input/output port expander

[MCP23017 - i2c 16 input/output port expander](https://www.adafruit.com/product/732)
Add another 16 pins to your microcontroller using an MCP23017 port expander. The MCP23017 uses two i2c pins (these can be shared with other i2c devices), and in exchange gives you 16 general purpose pins. You can set each of 16 pins to be input, output, or input with a pullup. There's even...

In Stock
[Buy Now](https://www.adafruit.com/product/732)
[Related Guides to the Product](https://learn.adafruit.com/products/732/guides)
### Silicone Cover Stranded-Core Wire - 50ft 30AWG Black

[Silicone Cover Stranded-Core Wire - 50ft 30AWG Black](https://www.adafruit.com/product/3164)
Silicone-sheathing wire is super-flexible and soft, and its also strong! Able to handle up to 200°C and up to 600V, it will do when PVC covered wire wimps out. We like this wire for being extremely supple and flexible, so it is great for wearables or projects where the wire-harness has to...

Out of Stock
[Buy Now](https://www.adafruit.com/product/3164)
[Related Guides to the Product](https://learn.adafruit.com/products/3164/guides)

## Related Guides

- [Adafruit Metro M4 Express featuring ATSAMD51](https://learn.adafruit.com/adafruit-metro-m4-express-featuring-atsamd51.md)
- [1.5" & 2.4" Monochrome 128x64 OLED Display Module](https://learn.adafruit.com/1-5-and-2-4-monochrome-128x64-oled-display-module.md)
- [Adding a WiFi Co-Processor to CircuitPython](https://learn.adafruit.com/adding-a-wifi-co-processor-to-circuitpython-esp8266-esp32.md)
- [3D Printed LED Microphone Flag](https://learn.adafruit.com/3d-printed-led-microphone-flag.md)
- [2.3" Monochrome 128x32 OLED Display Module](https://learn.adafruit.com/2-3-monochrome-128x32-oled-display-module.md)
- [ePaper Maze Maker](https://learn.adafruit.com/epaper-maze-maker.md)
- [How to train new TensorFlow Lite micro speech models](https://learn.adafruit.com/how-to-train-new-tensorflow-lite-micro-speech-models.md)
- [Quickstart using Adafruit eInk/ePaper displays with CircuitPython](https://learn.adafruit.com/quickstart-using-adafruit-eink-epaper-displays-with-circuitpython.md)
- [Adafruit AirLift Shield - ESP32 WiFi Co-Processor](https://learn.adafruit.com/adafruit-airlift-shield-esp32-wifi-co-processor.md)
- [CircuitPython 101: Basic Builtin Data Structures](https://learn.adafruit.com/basic-datastructures-in-circuitpython.md)
- [Animated NeoPixel Glow Fur Scarf](https://learn.adafruit.com/animated-neopixel-gemma-glow-fur-scarf.md)
- [CircuitPython Hardware: SD Cards](https://learn.adafruit.com/micropython-hardware-sd-cards.md)
- [Adafruit OV5640 Camera Breakouts](https://learn.adafruit.com/adafruit-ov5640-camera-breakout.md)
- [Setting up IO Python Library on BeagleBone Black](https://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black.md)
- [Micro SD Card Breakout Board Tutorial](https://learn.adafruit.com/adafruit-micro-sd-breakout-board-card-tutorial.md)
- [Visual Studio Code for Education... and CircuitPython](https://learn.adafruit.com/visual-studio-code-for-education-and-circuitpython.md)
