# Tiny Wiki for CircuitPython

## Overview

![Tiny Wiki home page showing basic usage and configuration details. A photo of the Adafruit Fruit Jam is in the bottom right corner](https://cdn-learn.adafruit.com/assets/assets/000/142/365/medium800/circuitpython_tiny_wiki_overview_hero.png?1771255944 )

Primary: This project is inspired by and uses some of the code from Kevin McAleer's [tiny_wiki for Micropython](https://github.com/kevinmcaleer/tiny_wiki). Check out Kevin's [GitHub](https://github.com/kevinmcaleer) and [YouTube](https://www.youtube.com/@kevinmcaleer28) for many other great projects.

Tiny Wiki is a small webserver that runs on a microcontroller and hosts a minimal Wiki system backed by markdown files. You can create and edit pages, linking them together to form a Wiki structure. This guide features a CircuitPython port of the Tiny Wiki project. It is built using the CircuitPython [HTTPServer](https://github.com/adafruit/Adafruit_CircuitPython_HTTPServer) and [TemplateEngine](https://github.com/adafruit/Adafruit_CircuitPython_TemplateEngine/) libraries. It was designed for the Adafruit Fruit Jam, but could be adapted for other CircuitPython devices with access to WiFi and a microSD card.

## Parts
Featured
### Adafruit Fruit Jam - Mini RP2350 Computer

[Adafruit Fruit Jam - Mini RP2350 Computer](https://www.adafruit.com/product/6200)
We were catching up on a recent [hackaday hackchat with eben upton](https://hackaday.io/event/202122-raspberry-pi-hack-chat-with-eben-upton)&nbsp;and learned some fun facts: such as the DVI hack for the RP2040 was inspired by <a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/6200)
[Related Guides to the Product](https://learn.adafruit.com/products/6200/guides)
![Angled shot of assembled mini computer PCB with plate.](https://cdn-shop.adafruit.com/640x480/6200-10.jpg)

Featured
### SD/MicroSD Memory Card (8 GB SDHC)

[SD/MicroSD Memory Card (8 GB SDHC)](https://www.adafruit.com/product/1294)
Add mega-storage in a jiffy using this 8 GB class 4 micro-SD card. It comes with a SD adapter so you can use it with any of our shields or adapters. Preformatted to FAT so it works out of the box with our projects. Tested and works great with our <a...></a...>

Out of Stock
[Buy Now](https://www.adafruit.com/product/1294)
[Related Guides to the Product](https://learn.adafruit.com/products/1294/guides)
![Hand removing/installing micro SD card from SD adapter](https://cdn-shop.adafruit.com/640x480/1294-03.jpg)

Featured
### USB Type A to Type C Cable - approx 1 meter / 3 ft long

[USB Type A to Type C Cable - approx 1 meter / 3 ft long](https://www.adafruit.com/product/4474)
As technology changes and adapts, so does Adafruit. This&nbsp;&nbsp; **USB Type A to Type C** cable will help you with the transition to USB C, even if you're still totin' around a USB Type A hub, computer or laptop.

USB C is the latest industry-standard connector for...

In Stock
[Buy Now](https://www.adafruit.com/product/4474)
[Related Guides to the Product](https://learn.adafruit.com/products/4474/guides)
![Angled shot of a coiled black, USB-C to USB-A cable.](https://cdn-shop.adafruit.com/640x480/4474-02.jpg)

Featured
### Snap-on Enclosure for Adafruit Fruit Jam

[Snap-on Enclosure for Adafruit Fruit Jam](https://www.adafruit.com/product/6425)
Here is a cool&nbsp;and minimal enclosure for your [Fruit Jam](https://www.adafruit.com/product/6200)&nbsp;to keep it safe during use and transport. This case has been custom-designed and 3D printed to accommodate all&nbsp;connection ports of the Fruit Jam. No screws or...

In Stock
[Buy Now](https://www.adafruit.com/product/6425)
[Related Guides to the Product](https://learn.adafruit.com/products/6425/guides)
![Angled Demo Shot of the Assembled Snap-on Enclosure with the Fruit Jam inside and powered on showing a rainbow effect of the LED's.](https://cdn-shop.adafruit.com/640x480/6425-02.jpg)

# Tiny Wiki for CircuitPython

## Install CircuitPython

[CircuitPython](https://github.com/adafruit/circuitpython) is a derivative of [MicroPython](https://micropython.org) designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the **CIRCUITPY** drive to iterate.

## CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython running on your board.

Warning: Please use the latest release of 10.x or higher for the Fruit Jam. Also use the latest libraries for the best functionality.

[Download the latest version of CircuitPython for this board via circuitpython.org](https://circuitpython.org/board/adafruit_fruit_jam/)
 **Click the link above to download the latest CircuitPython UF2 file.**

Save it wherever is convenient for you.

![install_circuitpython_on_rp2040_RP2040_UF2_downloaded.jpg](https://cdn-learn.adafruit.com/assets/assets/000/101/655/medium640/install_circuitpython_on_rp2040_RP2040_UF2_downloaded.jpg?1618943202)

![reset and boot highlighted](https://cdn-learn.adafruit.com/assets/assets/000/138/708/medium800/adafruit_products_Resetboot.jpg?1754331128 )

To enter the bootloader, hold down the **BOOT/**** BOOTSEL button**(highlighted in red above), and while continuing to hold it (don't let go!), press and release the**reset button**(highlighted in red or blue above).&nbsp;**Continue to hold the BOOT/BOOTSEL button until the RP2350 drive appears!**

If the drive does not appear, release all the buttons, and then repeat the process above.

You can also start with your board unplugged from USB, press and hold the BOOTSEL button (highlighted in red above), continue to hold it while plugging it into USB, and wait for the drive to appear before releasing the button.

A lot of people end up using charge-only USB cables and it is very frustrating! **Make sure you have a USB cable you know is good for data sync.**

You will see a new disk drive appear called **RP2350**.

&nbsp;

Drag the **adafruit-circuitpython-_boardname_-_language_-_version_.uf2** file to **RP2350.**

![install_circuitpython_on_rp2350_Screenshot_2024-09-11_111518.png](https://cdn-learn.adafruit.com/assets/assets/000/132/253/medium640/install_circuitpython_on_rp2350_Screenshot_2024-09-11_111518.png?1726067809)

![install_circuitpython_on_rp2350_Screenshot_2024-09-11_111742.png](https://cdn-learn.adafruit.com/assets/assets/000/132/254/medium640/install_circuitpython_on_rp2350_Screenshot_2024-09-11_111742.png?1726067866)

The **RP2350** drive will disappear and a new disk drive called **CIRCUITPY** will appear.

That's it, you're done! :)

![install_circuitpython_on_rp2350_Screenshot_2024-09-11_111843.png](https://cdn-learn.adafruit.com/assets/assets/000/132/255/medium640/install_circuitpython_on_rp2350_Screenshot_2024-09-11_111843.png?1726067932)

## Safe Mode

You want to edit your **code.py** or modify the files on your **CIRCUITPY** drive, but find that you can't. Perhaps your board has gotten into a state where **CIRCUITPY** is read-only. You may have turned off the **CIRCUITPY** drive altogether. Whatever the reason, safe mode can help.

Safe mode in CircuitPython does not run any user code on startup, and disables auto-reload. This means a few things. First, safe mode _bypasses any code in_ **boot.py** (where you can set **CIRCUITPY** read-only or turn it off completely). Second, _it does not run the code in_ **code.py**. And finally, _it does not automatically soft-reload when data is written to the_ **CIRCUITPY** _drive_.

Therefore, whatever you may have done to put your board in a non-interactive state, safe mode gives you the opportunity to correct it without losing all of the data on the **CIRCUITPY** drive.

### Entering Safe Mode
To enter safe mode when using CircuitPython, plug in your board or hit reset (highlighted in red above). Immediately after the board starts up or resets, it waits 1000ms. On some boards, the onboard status LED (highlighted in green above) will blink yellow during that time. If you press reset during that 1000ms, the board will start up in safe mode. It can be difficult to react to the yellow LED, so you may want to think of it simply as a slow double click of the reset button. (Remember, a fast double click of reset enters the bootloader.)

### In Safe Mode

If you successfully enter safe mode on CircuitPython, the LED will intermittently blink yellow three times.

If you connect to the serial console, you'll find the following message.

```terminal
Auto-reload is off.
Running in safe mode! Not running saved code.

CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode.

Press any key to enter the REPL. Use CTRL-D to reload.
```

You can now edit the contents of the **CIRCUITPY** drive. Remember, _your code will not run until you press the reset button, or unplug and plug in your board, to get out of safe mode._

## Flash Resetting UF2

If your board ever gets into a really _weird_ state and CIRCUITPY doesn't show up as a disk drive after installing CircuitPython, try loading this 'nuke' UF2 to RP2350. which will do a 'deep clean' on your Flash Memory. **You will lose all the files on the board** , but at least you'll be able to revive it! After loading this UF2, follow the steps above to re-install CircuitPython.

[Download flash erasing "nuke" UF2 for RP2350](https://cdn-learn.adafruit.com/assets/assets/000/132/526/original/rp2350_flash_nuke.uf2)
# Tiny Wiki for CircuitPython

## Create Your settings.toml File

CircuitPython works with WiFi-capable boards to enable you to make projects that have network connectivity. This means working with various passwords and API keys. As of [CircuitPython 8](https://circuitpython.org/downloads), there is support for a **settings.toml** file. This is a file that is stored on your **CIRCUITPY** drive, that contains all of your secret network information, such as your SSID, SSID password and any API keys for IoT services. It is designed to separate your sensitive information from your **code.py** file so you are able to share your code without sharing your credentials.

CircuitPython previously used a **secrets.py** file for this purpose. The **settings.toml** file is quite similar.

Warning: Your **settings.toml** file should be stored in the main directory of your **CIRCUITPY** drive. It should not be in a folder.

## CircuitPython **settings.toml** File

This section will provide a couple of examples of what your **settings.toml** file should look like, specifically for CircuitPython WiFi projects in general.

The most minimal **settings.toml** file must contain your WiFi SSID and password, as that is the minimum required to connect to WiFi. Copy this example, paste it into your **settings.toml** , and update:

- `your_wifi_ssid`
- `your_wifi_password`

```auto
CIRCUITPY_WIFI_SSID = "your_wifi_ssid"
CIRCUITPY_WIFI_PASSWORD = "your_wifi_password"
```

Many CircuitPython network-connected projects on the Adafruit Learn System involve using Adafruit IO. For these projects, you must _also_ include your Adafruit IO username and key. Copy the following example, paste it into your settings.toml file, and update:

- `your_wifi_ssid`
- `your_wifi_password`
- `your_aio_username`
- `your_aio_key`

```auto
CIRCUITPY_WIFI_SSID = "your_wifi_ssid"
CIRCUITPY_WIFI_PASSWORD = "your_wifi_password"
ADAFRUIT_AIO_USERNAME = "your_aio_username"
ADAFRUIT_AIO_KEY = "your_aio_key"
```

Some projects use different variable names for the entries in the **settings.toml** file. For example, a project might use `ADAFRUIT_AIO_ID` in the place of `ADAFRUIT_AIO_USERNAME`. **If you run into connectivity issues, one of the first things to check is that the names in the settings.toml file match the names in the code.**

Warning: Not every project uses the same variable name for each entry in the **settings.toml** file! Always verify it matches the code.

## **settings.toml** File Tips
Here is an example **settings.toml** file.

```auto
# Comments are supported
CIRCUITPY_WIFI_SSID = "guest wifi"
CIRCUITPY_WIFI_PASSWORD = "guessable"
CIRCUITPY_WEB_API_PORT = 80
CIRCUITPY_WEB_API_PASSWORD = "passw0rd"
test_variable = "this is a test"
thumbs_up = "\U0001f44d"
```

In a **settings.toml** file, it's important to keep these factors in mind:

- Strings are wrapped in double quotes; ex: `"your-string-here"`
- Integers are _ **not** _ quoted and may be written in decimal with optional sign (`+1`, `-1`, `1000`) or hexadecimal (`0xabcd`).
  - Floats (decimal numbers), octal (`0o567`) and binary (`0b11011`) are not supported.

- Use `\u` escapes for weird characters, `\x` and `\ooo` escapes are not available in **.toml** files
  - Example: `\U0001f44d` for 👍 (thumbs up emoji) and `\u20ac` for € (EUR sign)

- Unicode emoji, and non-ASCII characters, stand for themselves as long as you're careful to save in "UTF-8 without BOM" format

&nbsp;

&nbsp;

When your&nbsp; **settings.toml&nbsp;** file is ready, you can save it in your text editor with the **.toml** &nbsp;extension.

![adafruit_products_dotToml.jpg](https://cdn-learn.adafruit.com/assets/assets/000/117/071/medium640/adafruit_products_dotToml.jpg?1671034293)

## Accessing Your **settings.toml** Information in **code.py**
In your **code.py** file, you'll need to `import` the `os` library to access the **settings.toml** file. Your settings are accessed with the `os.getenv()` function. You'll pass your settings entry to the function to import it into the **code.py** file.

```python
import os

print(os.getenv("test_variable"))
```

![](https://cdn-learn.adafruit.com/assets/assets/000/117/072/medium800/adafruit_products_tomlOutput.jpg?1671034496)

In the upcoming CircuitPython WiFi examples, you'll see how the **settings.toml&nbsp;** file is used for connecting to your SSID and accessing your API keys.

# Tiny Wiki for CircuitPython

## Code

## Getting the Program's Files

To use the application, you need to obtain **code.py** with the program, and the other project files to place on the Fruit Jam **CIRCUITPY** drive.

Thankfully, this can be done in one go. In the example below, click the **Download Project Bundle** button below to download the necessary libraries, the **code.py** file, and other project files in a zip file.

Connect your board to your computer via a known good data+power USB cable. The board should show up in your File Explorer/Finder (depending on your operating system) as a flash drive named **CIRCUITPY**.

Extract the contents of the zip file, copy the **lib** directory files to **CIRCUITPY/lib**. Copy the **code.py** and **Home.md** files, as well as the **tiny\_wiki/** and **templates/** folders&nbsp;to your **CIRCUITPY** drive. The program should self start.

## Drive Structure

After copying the files, your drive should look like the listing below. It can contain other files as well, but must contain these at a minimum.

![CIRCUITPY screenshot with required project files and libraries](https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/folder-images/Fruit_Jam_Fruit_Jam_Tiny_Wiki.png?raw=true )

## Code
https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/Fruit_Jam/Fruit_Jam_Tiny_Wiki/code.py

# Tiny Wiki for CircuitPython

## Code Explanation

The code for this project is split into a few main components:

- **code.py**
- **tiny\_wiki/** helpers&nbsp; **wiki\_storage.py** and **markdown.py**
- HTML template files inside of **templates/**

## **code.py**

This file contains the setup for the WiFi connection and initializes the server. The configuration environment variables are read from the **settings.toml** file.

Helper functions&nbsp;`_url_decode()` and `_url_encode()` exist for [encoding and decoding](https://en.wikipedia.org/wiki/Percent-encoding) strings to make them safe for use in URLs. `_load_auth_entries()` loads login details from the configured JSON file if authentication is enabled. `_is_authenticated()` checks if the request contains a valid authentication token returning `True` or `False` as appropriate.

### Server Endpoints

The remainder of the **code.py** file contains functions used to define [endpoints](https://en.wikipedia.org/wiki/Web_API#Endpoints) for the Tiny Wiki server. Each endpoint represents either an HTTP GET request like fetching an HTML page to show the user in a browser, or an HTTP POST request like a form sending data back to the server when a Wiki page gets created, updated, or deleted.

These are the endpoints defined by the server:

- `/` - `homepage()`: Shows the default **Home.md** getting started Wiki page.
- `/list` - `list_pages()`: Shows a list of all pages that exist in the Wiki system.
- `/credentials` - `credentials_form()`: Shows the credential hash page for generating valid authentication info.
- `/credentials` - `credentials_hash()`: Handles POST request from credential hash form. Hashes the given password and returns JSON to the front end ready to be copied into the auth JSON file.
- `/login`&nbsp;- `login_form()`: Shows the login form. Only needed if authentication is enabled.
- `/login` - `login_submit()`: Handles the POST request for logging in. Checks if the provided credentials are valid. Redirects user back to where the came from if so, or returns them to the login page if not.
- `/new` - `new_page()`: Shows the new Wiki page form with input for the title of the new page.
- `/create` - `create_page()`: Handles POST request from the new Wiki page form. Creates a new markdown file under the specified name and redirects the user to the edit page for it.
- `/edit/` - `edit_page()`: Shows the edit Wiki page form with text area input for the user to modify the wiki page content.
- `/save/` - `save_page()`: Handles the POST request for editing a Wiki page. Updates the content of the specified page. Redirects back to view the page.
- `/delete/` - `delete_page()`: Handles POST request from the delete button on the edit Wiki page. Deletes the specified page and returns the user to the list page.
- `/wiki/` - `view_page()`: Shows the specified Wiki page.

## **tiny\_wiki** Helpers

The [**tiny\_wiki** &nbsp;folder](https://github.com/adafruit/Adafruit_Learning_System_Guides/tree/main/Fruit_Jam/Fruit_Jam_Tiny_Wiki/tiny_wiki) contains two Python helper modules: **wiki\_storage.py** and **markdown.py**.

**wiki\_storage.py** is responsible for initializing the micro SD card and providing an interface to read and write Wiki page files on it. It contains functions: &nbsp;`page_exists()`, `read_page()`, `write_page()`, `delete_page()`, and `list_pages()` to access and manage the **.md** files stored on the micro SD card.

**markdown.py** contains a minimal markdown syntax parser that converts markdown to HTML. When you load a Wiki page in the browser this is what translates the content from the **.md** file into HTML elements that your browser is capable of rendering.

## Templates

All of the front end HTML pages are stored inside of the **templates/** directory. These files contain the HTML that make up the pages as well as template variables which have values passed in from the server endpoint functions in **code.py**. When the page is rendered variable values get substituted in to the appropriate places by the [TemplateEngine](https://github.com/adafruit/Adafruit_CircuitPython_TemplateEngine/) library.

- **base.html** - All other templates extend this one, meaning they'll include the content that is in this file too. It contains the core CSS style rules for the site and the header with navigation links in it.
- **credentials.hml** - Credential hashing page used for getting the details to put in the authentication file if you want to password protect write access to the Wiki pages.
- **edit\_page.html** - Form used to modify or delete Wiki pages. It contains a `textarea` element that holds the content of the Wiki page and allows editing.
- **list\_pages.html** - Shows a list of Wiki pages currently in the system.
- **login.html** - Username and password form used to login if authentication is enabled.
- **new\_page.html** - Form to enter a name for a new Wiki page to be created.
- **view\_page\_exists.html** - Page shown when a Wiki page is viewed if the page exists. The converted HTML content from the markdown file gets added to the page by a template variable.
- **view\_page\_missing.html** - Page shown when request Wiki page does not exist. It informs the user the page was not found and provides a link that can be used to create it.

# Tiny Wiki for CircuitPython

## Use

Basic usage of Tiny Wiki is very straightforward. The Home page of the Wiki that gets created automatically when the server is first run contains the same usage instructions as this guide page.
- Use **Edit** to modify the page you are currently viewing.
- Create new articles with **New Page**.
- Provide links to other Wiki pages using `[[AnotherPage]]`.
- Browse everything via **All Pages**.

![CircuitPython Tiny Wiki edit page](https://cdn-learn.adafruit.com/assets/assets/000/142/369/medium640/circuitpython_tiny_wiki_edit.png?1771256922 )

![CircuitPython Tiny Wiki create new page](https://cdn-learn.adafruit.com/assets/assets/000/142/370/medium640/circuitpython_tiny_wiki_new_page.png?1771256948 )

## Configuration
These configuration options are available by setting environment variables in the **settings.toml** file.

- `WIKI_TITLE`: The title to show at the top of all pages.
- `WIKI_PAGES_DIR`: The directory to store page files. "/sd/pages" by default. Must be writable.
- `WIKI_SERVER_PORT`: The HTTP port to host the server on. Defaults to 8000
- `WIKI_AUTH_DATA_FILE`: Optional path to JSON file containing valid user(s) authentication details. Defaults to None (auth disabled).
- `WIKI_PASSWORD_SALT`: [Pepper](https://en.wikipedia.org/wiki/Pepper_%28cryptography%29) used when hashing passwords if auth is enabled. You should change it from the default value.
- `WIKI_AUTH_SECRET_KEY`: Key string used when hashing values to derive session tokens if auth is enabled. You should change it from the default value.


## Authentication
![CircuitPython Tiny Wiki login page.](https://cdn-learn.adafruit.com/assets/assets/000/142/367/medium640/circuitpython_tiny_wiki_login_page.png?1771256605 )

![CircuitPython Tiny Wiki credential hash page.](https://cdn-learn.adafruit.com/assets/assets/000/142/368/medium640/circuitpython_tiny_wiki_credential_hash.png?1771256633 )

Warning: The authentication system is meant to provide a minimal layer of security to prevent unauthorized editing of Wiki pages. It tries to follow best practices where possible within the constraints of running in CircuitPython on a microcontroller, but it should not be considered highly secure. By default, all data is stored and transmitted in plain text over HTTP. This Wiki is intended to be a fun project and demonstration, not to store sensitive information securely.

The authentication system is optional, and disabled by default. If enabled it will require logging in with valid credentials in order to create, update, or delete Wiki pages. 

To enable authentication, create a JSON file on the **CIRCUITPY** drive and set the path to it as the value for `WIKI_AUTH_DATA_FILE` environment variable.

For example in **settings.toml**:
``` 
WIKI_AUTH_DATA_FILE="wiki_auth.json"
```

Go to the **Credential Hash** page in your browser, enter the desired username and password, and click **Generate Hash**.
Copy the resulting username/hash JSON object into the auth data file mentioned above. It can contain either a single entry with one object containing a username and hash, or a list of objects containing usernames and hashes.
Single user example:
```
{"password_hash": "dbd1bdabd1c22032e7cb64217cc9d4bca7242f68bfba46aa0b9418a58138304f", "username": "wiki_user"}
```
Multiple user example:
```
[
  {"password_hash": "3eb1d64cde9d156c852dab00bccf9194932ed6e540a61f2bd53fdf1c03044dac", "username": "wiki_user"},
  {"password_hash": "12cdd16b8cec46aa1defc576342aaef5c0dd0773ba5c8a0c3a726c3ee56cd50f", "username": "test_user"}
]
```


## Guide Products

### Adafruit Fruit Jam - Mini RP2350 Computer

[Adafruit Fruit Jam - Mini RP2350 Computer](https://www.adafruit.com/product/6200)
We were catching up on a recent [hackaday hackchat with eben upton](https://hackaday.io/event/202122-raspberry-pi-hack-chat-with-eben-upton)&nbsp;and learned some fun facts: such as the DVI hack for the RP2040 was inspired by <a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/6200)
[Related Guides to the Product](https://learn.adafruit.com/products/6200/guides)
### SD/MicroSD Memory Card (8 GB SDHC)

[SD/MicroSD Memory Card (8 GB SDHC)](https://www.adafruit.com/product/1294)
Add mega-storage in a jiffy using this 8 GB class 4 micro-SD card. It comes with a SD adapter so you can use it with any of our shields or adapters. Preformatted to FAT so it works out of the box with our projects. Tested and works great with our <a...></a...>

Out of Stock
[Buy Now](https://www.adafruit.com/product/1294)
[Related Guides to the Product](https://learn.adafruit.com/products/1294/guides)
### USB Type A to Type C Cable - approx 1 meter / 3 ft long

[USB Type A to Type C Cable - approx 1 meter / 3 ft long](https://www.adafruit.com/product/4474)
As technology changes and adapts, so does Adafruit. This&nbsp;&nbsp; **USB Type A to Type C** cable will help you with the transition to USB C, even if you're still totin' around a USB Type A hub, computer or laptop.

USB C is the latest industry-standard connector for...

In Stock
[Buy Now](https://www.adafruit.com/product/4474)
[Related Guides to the Product](https://learn.adafruit.com/products/4474/guides)
### Snap-on Enclosure for Adafruit Fruit Jam

[Snap-on Enclosure for Adafruit Fruit Jam](https://www.adafruit.com/product/6425)
Here is a cool&nbsp;and minimal enclosure for your [Fruit Jam](https://www.adafruit.com/product/6200)&nbsp;to keep it safe during use and transport. This case has been custom-designed and 3D printed to accommodate all&nbsp;connection ports of the Fruit Jam. No screws or...

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

## Related Guides

- [Adafruit Fruit Jam](https://learn.adafruit.com/adafruit-fruit-jam.md)
- [CircuitPython Text Editor On The Go](https://learn.adafruit.com/circuitpython-text-editor-on-the-go.md)
- [Flappy Nyan Cat Game on Fruit Jam and Metro RP2350](https://learn.adafruit.com/flappy-nyan-cat-game-on-metro-rp2350.md)
- [QT Py S2 Round Display Compass](https://learn.adafruit.com/qt-py-s2-round-display-compass.md)
- [How to Make Games in MakeCode Arcade](https://learn.adafruit.com/how-to-make-games-on-makecode-arcade.md)
- [A Z80 CP/M emulator for the SAMD51](https://learn.adafruit.com/z80-cpm-emulator-for-the-samd51-grand-central.md)
- [PowerWash Simulator Nozzle Controller](https://learn.adafruit.com/powerwash-simulator-nozzle-controller.md)
- [CircuitPython on the Xteink X4 eReader](https://learn.adafruit.com/circuitpython-on-the-xteink-x4-ereader.md)
- [Raspberry Pi Video Synth with Blinka and Processing](https://learn.adafruit.com/raspberry-pi-video-synth-with-blinka-and-processing.md)
- [Fruit Jam IRC Client in CircuitPython](https://learn.adafruit.com/fruit-jam-irc-client-in-circuitpython.md)
- [Expressive Pixels for Adafruit Matrix Portal](https://learn.adafruit.com/expressive-pixels-for-matrix-portal.md)
- [IoT Bird Feeder with Camera](https://learn.adafruit.com/iot-window-bird-feeder-with-camera.md)
- [Using a Keyboard with USB Host](https://learn.adafruit.com/using-a-keyboard-with-usb-host.md)
- [Apple //e Emulator on Fruit Jam](https://learn.adafruit.com/apple-e-emulator-on-fruit-jam.md)
- [Google Graveyard with Adafruit MagTag](https://learn.adafruit.com/google-graveyard-with-adafruit-magtag.md)
- [Logic Gates Simulator on Fruit Jam](https://learn.adafruit.com/logic-gates-simulator-on-fruit-jam.md)
