# Adafruit PyPortal - IoT for CircuitPython

## Overview

https://youtu.be/9meeVehRS6A

![](https://cdn-learn.adafruit.com/assets/assets/000/071/909/medium800/circuitpython_PyPortalYouTubeDemo.jpg?1551129736)

 **PyPortal** , is our easy-to-use IoT device that allows you to create all the things for the “Internet of Things” in minutes. Make custom touch screen interface GUIs, all open-source, and Python-powered using&nbsp;tinyJSON / APIs to get news, stock values, weather, cat photos, and more – all over Wi-Fi with the latest technologies. Create little pocket universes of joy that connect to something good. Rotate it 90 degrees, it’s a web-connected conference badge #badgelife.

![](https://cdn-learn.adafruit.com/assets/assets/000/071/910/medium800/circuitpython_PyPortalFront.jpg?1551129763)

The PyPortal uses an ATMEL (Microchip) ATSAMD51J20, and an Espressif ESP32 Wi-Fi coprocessor with TLS/SSL (secure web) support built-in. PyPortal has a **3.2″ 320 x 240 color TFT** with resistive touch screen.

PyPortal includes: speaker, light sensor, temperature sensor, NeoPixel, microSD card slot, 8MB flash, plug-in ports for I2C, and 2 analog/digital pins. There are 3D files available for custom enclosures and the board has mounting holes which are also compatible with badge lanyard fasteners.

![](https://cdn-learn.adafruit.com/assets/assets/000/071/911/medium800/circuitpython_PyPortalBackAngle.jpg?1551129774)

The **PyPortal Pynt** is the little&nbsp;sister to our [popular PyPortal](https://www.adafruit.com/product/4116) - zapped with a shink ray to take the design from a 3.2" diagonal down to 2.4" diagonal screen - but otherwise the same! PyPortal Pynt has a **2.4″ diagonal 320 x 240 color TFT** with resistive touch screen. **Compared to the original PyPortal, the Pynt does not include a ADT7410 temperature sensor. Other than the ADT7410, the Pynt's display, processor, STEMMA conectors and WiFi have the exact same wiring as the original 3.2" PyPortal so all Arduino/CircuitPython code will run exactly the same - just smaller!**

![](https://cdn-learn.adafruit.com/assets/assets/000/086/113/medium800/circuitpython_PyPortal_Pynt_Held_Front.jpg?1576799235)

The M4 and ESP32 are a great couple - and each bring their own strengths to this board. The SAMD51 M4 has native USB so it can show up like a disk drive, act as a MIDI or HID keyboard/mouse, and of course bootload and debug over a serial port. It also has DACs, ADC, PWM, and tons of GPIO. Meanwhile, the ESP32 has secure WiFi capabilities, and plenty of Flash and RAM to buffer sockets. By letting the ESP32 focus on the complex TLS/SSL computation and socket buffering, it frees up the SAMD51 to act as the user interface. You get a great programming experience thanks to the native USB with files available for drag-n-drop, and you don't have to spend a ton of processor time and memory to do SSL encryption/decryption and certificate management. It's the best of both worlds!

![](https://cdn-learn.adafruit.com/assets/assets/000/086/114/medium800/circuitpython_Pynt_Pinouts.jpg?1576799247)

PyPortal is Open-Source hardware and Open-Source software, and it runs CircuitPython and Arduino code. Using CircuitPython, the device shows up as a USB drive and the code can be edited in any IDE, text editor, etc. You can have your IoT project up and running in minutes!

![](https://cdn-learn.adafruit.com/assets/assets/000/071/912/medium800/circuitpython_PyPortalDisplayLogo.jpeg?1551130177)

# Adafruit PyPortal - IoT for CircuitPython

## Pinouts

![](https://cdn-learn.adafruit.com/assets/assets/000/071/973/medium800/circuitpython_PyPortalPinoutsBack.jpg?1551205802)

![](https://cdn-learn.adafruit.com/assets/assets/000/086/111/medium800/circuitpython_Pynt_Pinouts.png?1576798815)

Warning: 

![](https://cdn-learn.adafruit.com/assets/assets/000/111/280/medium800/circuitpython_Adafruit_PyPortal_Pinout.png?1651521397)

[Click here to view a PDF version of the pinout diagram](https://github.com/adafruit/Adafruit-PyPortal-PCB/blob/master/Adafruit%20PyPortal%20Pinout.pdf)

![](https://cdn-learn.adafruit.com/assets/assets/000/111/678/medium800/circuitpython_Adafruit_PyPortal_Pynt_Pinout.png?1652296030)

[Click here to view a PDF version of the pinout diagram](https://github.com/adafruit/Adafruit-PyPortal-PCB/blob/master/Adafruit%20PyPortal%20Pynt%20Pinout.pdf)

There are so many great features on the Adafruit PyPortal and PyPortal Pynt. Let's take a look at what's available!

## Microcontroller and Flash
The main processor chip is the **ATSAMD51J20 Cortex M4** running at 120MHz with 3.3v logic/power. It has 1MB of Flash and 256KB of RAM.

We also include **8 MB of QSPI Flash** for storing images, sounds, animations, whatever!

![circuitpython_PyPortalPinouts_ATSAMD51AndFlash.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/965/medium640/circuitpython_PyPortalPinouts_ATSAMD51AndFlash.jpg?1551205753)

![circuitpython_Pynt_Pinouts_Microcontroller_and_Flash.png](https://cdn-learn.adafruit.com/assets/assets/000/086/110/medium640/circuitpython_Pynt_Pinouts_Microcontroller_and_Flash.png?1576798784)

## WiFi
The WiFi capability uses an **Espressif ESP32 Wi-Fi coprocessor** with TLS/SSL support built-in.

&nbsp;

The ESP32 uses the SPI port for data, and also uses a CS pin (`board.ESP_CS` or Arduino `8`), Ready/Busy pin (`board.ESP_BUSY` or Arduino `5`), and reset pin (`board.ESP_RESET` or Arduino `7`)

![circuitpython_PyPortalPinouts_ESP32.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/988/medium640/circuitpython_PyPortalPinouts_ESP32.jpg?1551212369)

![circuitpython_Pynt_Pinouts_WiFi.png](https://cdn-learn.adafruit.com/assets/assets/000/086/109/medium640/circuitpython_Pynt_Pinouts_WiFi.png?1576798754)

- For advanced use or reprogramming on Arduino, we also connect the main **RX/TX UART** to the ESP32 via `Serial1` . In CircuitPython use `board.ESP_RX` and `board.ESP_TX`. (CircuitPython 6.0.0 and earlier uses `board.RX` and `board.TX`).
- You can also connect to the ESP32 **RTS** pin (used in some serial contexts) on `board.ESP_RTS` or Arduino `51`.
- The ESP32 **GPIO0** pin for bootloader enable is connected to `board.ESP_GPIO0` or Arduino `6`

## Display and Display Connector
On the front of the PyPortal is a **3.2″ 320 x 240 color TFT** with resistive touch screen! On the front of the Pynt is a **2.4″ diagonal 320 x 240 color TFT** with resistive touch screen!

On the back, there is a large connector near the middle, the **display connector**. It connects the display on the front to the board.

To give you the most data throughput we configure the screen for 8-bit interfacing. That means 8 data lines and a collection of 4 or 5 control lines. If you really want to use the screen in SPI mode, you can do so by soldering closed the **SPI** jumper and cutting/resoldering the **8/SPI** jumper over to the SPI side. That's for advanced users!

![circuitpython_PyPortalFrontPinouts.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/985/medium640/circuitpython_PyPortalFrontPinouts.jpg?1551212140)

![circuitpython_PyPortalPinouts_DisplayConnector.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/986/medium640/circuitpython_PyPortalPinouts_DisplayConnector.jpg?1551212159)

![circuitpython_Pynt_Front_Pinouts_front.png](https://cdn-learn.adafruit.com/assets/assets/000/086/107/medium640/circuitpython_Pynt_Front_Pinouts_front.png?1576798653)

![circuitpython_Pynt_Pinouts_Display_Connector.png](https://cdn-learn.adafruit.com/assets/assets/000/086/108/medium640/circuitpython_Pynt_Pinouts_Display_Connector.png?1576798665)

The touchscreen is fully analog/resistive. It can be read using our Arduino/CircuitPython drivers. The connections are as follows:

- YD on `board.TOUCH_YD` or Arduino `A4`
- XL on `board.TOUCH_XL` or Arduino `A5`
- YU on `board.TOUCH_YU` or Arduino `A6`
- XR on `board.TOUCH_XR` or Arduino `A7`

The 8 bit LCD interface is not exposed in CircuitPython (it's managed internally). In Arduino it's on Digital **34** thru **41** , which is on a PORTA 8-bit boundary (PA16-PA23) and can be used for DMA or fast port writes. This probably doesn't affect you.

There are multiple control pins

- **TFT Reset** - `board.TFT_RESET` or Arduino `24`
- **TFT WR** - `board.TFT_WR` or Arduino 25 (this is also the `board.TFT_DC`&nbsp;pin if using in SPI mode)
- **TFT RD** - `board.TFT_RD` or Arduino `9`
- **TFT RS** - `board.TFT_RS` or Arduino `10`
- **TFT CS** - `board.TFT_CS` or Arduino `11`
- **TFT TE** - `board.TFT_TE` or Arduino `12`

There is also a TFT backlight, transistor-connected to `board.TFT_BACKLIGHT` or Arduino `25`. You can PWM control it. There are 6 white LEDs connected in parallel, so having it be full on will draw quite a bit of current (over 100mA!)

## Sensors
There are two built in sensors.

On the top of the PyPortal (not the Pynt) is the **ADT7410 Analog Devices temperature sensor** with 16-bit 0.0078°C temperature resolution and 0.5°C temperature tolerance. The sensor is `I2C` connected, use the Arduino or CircuitPython libraries to read it.

There is also an **ambient light sensor** on the side, which points through to the front, as seen in the second image. The light sensor is an analog input, connected to `board.LIGHT` (CircuitPython) or `A2` (Arduino) you can read it as any analog value ranging from 0 (dark) to 1023 (in Arduino) or 65535 (CircuitPython) when bright.

![circuitpython_PyPortalPinouts_TempLightSensors.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/967/medium640/circuitpython_PyPortalPinouts_TempLightSensors.jpg?1551205407)

![circuitpython_PyPortalPinouts_LightSensorFront.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/972/medium640/circuitpython_PyPortalPinouts_LightSensorFront.jpg?1551205656)

![circuitpython_Pynt_Pinouts_light_sensor_back.png](https://cdn-learn.adafruit.com/assets/assets/000/086/105/medium640/circuitpython_Pynt_Pinouts_light_sensor_back.png?1576798601)

![circuitpython_Pynt_Front_Pinouts_light_sensor_front.png](https://cdn-learn.adafruit.com/assets/assets/000/086/106/medium640/circuitpython_Pynt_Front_Pinouts_light_sensor_front.png?1576798620)

## microSD Card Slot
On the left side, there is a **microSD card slot**. A microSD card is the best way to add extra storage to your project and provide space for streams to be processed!

The SD card is on the main SPI port (shared with the ESP32) and a CS line. In CircuitPython, the CS pin is `board.SD_CS`. In Arduino it's digital `32`.

There is also a card detect pin on `board.SD_CARD_DETECT` (CircuitPython) or Arduino `33`

![circuitpython_PyPortalPinouts_SDCard.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/987/medium640/circuitpython_PyPortalPinouts_SDCard.jpg?1551212223)

![circuitpython_Pynt_Pinouts_Micro_SD_slot.png](https://cdn-learn.adafruit.com/assets/assets/000/086/104/medium640/circuitpython_Pynt_Pinouts_Micro_SD_slot.png?1576798562)

## Speaker and Speaker Connector
There is a speaker and a speaker connector.

The grey squarish bit on the bottom is a **speaker**. There is a small class D amplifier connected to the speaker so it can get quite loud!

There is also a **speaker connector** , which is [a Molex PicoBlade](https://www.digikey.com/product-detail/en/molex-llc/0532610271/WM7620CT-ND/699107). You can attach one of the speakers available in the Adafruit shop, or solder a connector to your favorite speaker. If you do, cut the small solder jumper to the left of the buzzer so that you only have one speaker activated (and also it will be louder!)

![circuitpython_PyPortalPinouts_SpeakerAndConnector.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/974/medium640/circuitpython_PyPortalPinouts_SpeakerAndConnector.jpg?1551206083)

![circuitpython_Pynt_Pinouts_speaker_and_speaker_connector.png](https://cdn-learn.adafruit.com/assets/assets/000/086/103/medium640/circuitpython_Pynt_Pinouts_speaker_and_speaker_connector.png?1576798543)

The speaker is connected to the DAC0 output from the SAMD51, via a class D amplifier. The analog output is known as `board.AUDIO_OUT` in CircuitPython. In Arduino it's `A0`.

You can disable the speaker amplifier by setting the shutdown pin to output and low. It's on `board.SPEAKER_ENABLE` and Arduino `50`

## I2C Connector
Warning: 

There is a 4-pin JST **I2C connector** in the center on the right, that is STEMMA and Grove compatible. The I2C has pullups to 3.3V power and is connected to the ADT7410 already.

In CircuitPython, you can use the STEMMA connector with&nbsp;`board.SCL`&nbsp;and&nbsp;`board.SDA`, or&nbsp;`board.STEMMA_I2C()`.

The I2C connector defaults to 5V. There is a jumper you can cut or solder to change it between 5V and 3V. To change it to 3V, cut the trace between the middle pad and the 5V pad, and solder together the middle pad and the 3V pad.

The connector is a JST PH-sized connector. STEMMA QT breakout boards use JST SH connectors, which are smaller, so you will need a JST PH to SH connector (for example, [this one](https://www.adafruit.com/product/4424)).

![circuitpython_PyPortalPinouts_I2C.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/990/medium640/circuitpython_PyPortalPinouts_I2C.jpg?1551212579)

![circuitpython_Pynt_Pinouts_I2C.png](https://cdn-learn.adafruit.com/assets/assets/000/086/102/medium640/circuitpython_Pynt_Pinouts_I2C.png?1576798523)

## Digital/Analog Connectors
On the right side are two connectors labeled D3 and D4. These are **3-pin JST digital or analog connectors** for sensors or NeoPixels. These pins can be analog inputs or digital I/O.

&nbsp;

They have protection 1K resistors + 3.6V zener diodes so you can drive an LED directly from the output. Connect to them via `board.D3` and `board.D4` or Arduino `3` and `4`. For analog reading in arduino use **A1** for **D3** and **A3** for **D4** (yeah sorry it's not matchy!)

**D3/A1** is the second DAC.

![circuitpython_PyPortalPinouts_D3D4Connectors.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/991/medium640/circuitpython_PyPortalPinouts_D3D4Connectors.jpg?1551212731)

![circuitpython_Pynt_Pinouts_Digital_Analog_Connectors.png](https://cdn-learn.adafruit.com/assets/assets/000/086/101/medium640/circuitpython_Pynt_Pinouts_Digital_Analog_Connectors.png?1576798460)

Warning: 

## Status LED and NeoPixel
There are two LEDs on the board.

There is the RGB **status NeoPixel** labeled "STATUS". It is connected to `board.NEOPIXEL` or Arduino `2`

As well, there is the **D13 LED**. This is attached to `board.L` and Arduino `13`

![circuitpython_PyPortalPinouts_StatusLEDNeoPixel.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/975/medium640/circuitpython_PyPortalPinouts_StatusLEDNeoPixel.jpg?1551207049)

![circuitpython_Pynt_Pinouts_status_LED_NeoPixel.png](https://cdn-learn.adafruit.com/assets/assets/000/086/099/medium640/circuitpython_Pynt_Pinouts_status_LED_NeoPixel.png?1576798413)

## USB Connector
There is one USB port on the board.

On the left side, towards the bottom, is a **USB Micro** port, which is used for powering and programming the board.

![circuitpython_PyPortalPinouts_USBConnector.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/980/medium640/circuitpython_PyPortalPinouts_USBConnector.jpg?1551211040)

![circuitpython_Pynt_Pinouts_USB_Connector.png](https://cdn-learn.adafruit.com/assets/assets/000/086/098/medium640/circuitpython_Pynt_Pinouts_USB_Connector.png?1576798385)

## Reset Button
The **reset button** is located on the top in the middle.

&nbsp;

Click it once to re-start your firmware. Click twice to enter bootloader mode.

![circuitpython_PyPortalPinouts_ResetButton.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/989/medium640/circuitpython_PyPortalPinouts_ResetButton.jpg?1551212511)

![circuitpython_Pynt_Pinouts_Reset_button.png](https://cdn-learn.adafruit.com/assets/assets/000/086/097/medium640/circuitpython_Pynt_Pinouts_Reset_button.png?1576798367)

# Adafruit PyPortal - IoT for CircuitPython

## What is CircuitPython?

CircuitPython is a programming language designed to simplify experimenting and learning to program on low-cost microcontroller boards. It makes getting started easier than ever with no upfront desktop downloads needed. Once you get your board set up, open any text editor, and get started editing code. It's that simple.

![](https://cdn-learn.adafruit.com/assets/assets/000/049/445/medium800/circuitpython_python-logo-master-flat.png?1513729728)

# CircuitPython is based on Python

Python is the fastest growing programming language. It's taught in schools and universities. It's a high-level programming language which means it's designed to be easier to read, write and maintain. It supports modules and packages which means it's easy to reuse your code for other projects. It has a built in interpreter which means there are no extra steps, like _compiling_, to get your code to work. And of course, Python is Open Source Software which means it's free for anyone to use, modify or improve upon.

CircuitPython adds hardware support to all of these amazing features. If you already have Python knowledge, you can easily apply that to using CircuitPython. If you have no previous experience, it's really simple to get started!

![](https://cdn-learn.adafruit.com/assets/assets/000/049/441/medium800/circuitpython_circuit_playground_adafruit_blinka_computer.png?1513729326)

## Why would I use CircuitPython?
CircuitPython is designed to run on microcontroller boards. A microcontroller board is a board with a microcontroller chip that's essentially an itty-bitty all-in-one computer. The board you're holding is a microcontroller board! CircuitPython is easy to use because all you need is that little board, a USB cable, and a computer with a USB connection. But that's only the beginning.

Other reasons to use CircuitPython include:

- **You want to get up and running quickly.** Create a file, edit your code, save the file, and it runs immediately. There is no compiling, no downloading and no uploading needed.
- **You're new to programming.** CircuitPython is designed with education in mind. It's easy to start learning how to program and you get immediate feedback from the board.
- **Easily update your code.** Since your code lives on the disk drive, you can edit it whenever you like, you can also keep multiple files around for easy experimentation.
- **The serial console and REPL.** These allow for live feedback from your code and interactive programming.
- **File storage.** The internal storage for CircuitPython makes it great for data-logging, playing audio clips, and otherwise interacting with files.
- **Strong hardware support.** CircuitPython has builtin support for microcontroller hardware features like digital I/O pins, hardware buses (UART, I2C, SPI), audio I/O, and other capabilities. There are also many libraries and drivers for sensors, breakout boards and other external components.
- **It's Python!** Python is the fastest-growing programming language. It's taught in schools and universities. CircuitPython is almost-completely compatible with Python. It simply adds hardware support.

This is just the beginning. CircuitPython continues to evolve, and is constantly being updated. Adafruit welcomes and encourages feedback from the community, and incorporate it into the development of CircuitPython. That's the core of the open source concept. This makes CircuitPython better for you and everyone who uses it!

# Adafruit PyPortal - IoT for CircuitPython

## Update the UF2 Bootloader

Info: 

Your SAMD51 M4 board bootloader may need to be updated to fix an intermittent bug that can erase parts of internal flash.

## Updating Your Bootloader

To see if you need to update your bootloader, get the UF2 boot drive to appear as a mounted drive on your computer, in a file browser window. If you're running MakeCode, click the reset button once. If you're running CircuitPython or an Arduino program, double-click the reset button.

When you see the **...BOOT** drive ( **FEATHERBOOT** , **METROM4BOOT** , **ITSYM4BOOT** , **PORTALBOOT** , etc.) , click the drive in the file browser window and then double-click the **INFO\_UF2.TXT** file to see what's inside.

The example screenshots below are for a PyGamer. What you see for your board will be largely the same except for the board name and the **BOOT** drive name.

![](https://cdn-learn.adafruit.com/assets/assets/000/090/335/medium800/adafruit_products_pygamerboot.png?1586879651)

The bootloader version is listed in **INFO\_UF2.TXT**. In this example, the version is **v3.6.0**.

![](https://cdn-learn.adafruit.com/assets/assets/000/090/336/medium800/adafruit_products_pygamer-info_uf2.png?1586879702)

 **If the bootloader version you see is older than _v3.9.0_** , you need to update. For instance, the bootloader above needs to be upgraded.

Download the latest version of the bootloader updater from the circuitpython.org Downloads page for your board.

![](https://cdn-learn.adafruit.com/assets/assets/000/092/754/medium800/circuitpython_pygamer-bootloader.png?1593831042)

[Latest board downloads](https://circuitpython.org/downloads)
- The bootloader updater will be named **update-bootloader-_name\_of\_your\_board_-v3.9.0.uf2** &nbsp;or some later version. Drag that file from your **Downloads** folder onto the **BOOT** drive:

![](https://cdn-learn.adafruit.com/assets/assets/000/090/338/medium800/adafruit_products_pygamer-update.png?1586880366)

After you drag the updater onto the boot drive, the red LED on the board will flicker and then blink slowly about five times. A few seconds later, the **BOOT** will appear in the Finder. After that, you can click on the **BOOT** drive and double-click **INFO\_UF2.TXT** again to confirm you've updated the bootloader.

![](https://cdn-learn.adafruit.com/assets/assets/000/090/339/medium800/adafruit_products_pygamer-info_uf2-390.png?1586880523)

# Adafruit PyPortal - IoT for CircuitPython

## Updating the PyPortal Demo Code

Your PyPortal may have come with older running firmware, libraries and software.

The files are the same for the PyPortal and the PyPortal Pynt.

**Before you start,&nbsp; you'll NEED to update your PyPortal!**

# Step 1 - Update the Bootloader

Follow the instructions [here](https://learn.adafruit.com/adafruit-pyportal/update-the-uf2-bootloader) to update the UF2 bootloader to a more recent version.

# Step 2 - Erase and Reformat CIRCUITPY

Connect to the REPL by following the instructions [here](https://learn.adafruit.com/adafruit-pyportal/kattni-connecting-to-the-serial-console). Also take a look at the two guide pages following that one. Then type the two CircuitPython statements below at the&nbsp;`>>>` prompt in the REPL. This will erase and reformat CIRCUITPY, and set it up for proper use of the SD card.

```terminal
import storage
storage.erase_filesystem()
```

# Step 3 - Install the Latest CircuitPython

[Visit this page and follow the instructions to download and update the latest CircuitPython firmware. You will need to download the latest UF2 firmware file, double-click to enter the bootloader, then drag the UF2 over to the PORTALBOOT drive.](https://learn.adafruit.com/adafruit-pyportal/install-circuitpython)

Your PyPortal will no longer run the example code once you do this - that's OK! We have to finish the other two steps

# Step 4 - Update Example Quotes Code to Latest

Your PyPortal may have come with an example Quotes demo, or perhaps it's blank. Either way, you can install the latest Quotes demo package by following the instructions in [this Guide](https://learn.adafruit.com/pyportal-adafruit-quote-board).

# If you are getting odd errors

If your filesystem somehow got corrupted, or you're getting unusual errors, [try erasing the filesystem to clear out any corrupt files](https://learn.adafruit.com/welcome-to-circuitpython/troubleshooting#old-way-for-the-circuit-playground-express-feather-m0-express-and-metro-m0-express-20-25), by:

- [download the QSPI Eraser UF2 file](https://learn.adafruit.com/welcome-to-circuitpython/troubleshooting#old-way-for-the-circuit-playground-express-feather-m0-express-and-metro-m0-express-20-25)
- load it onto the PyPortal by entering the bootloader and dragging it onto PORTALBOOT
- wait until the back LED goes from yellow to green
- Go to Step 2 to re-load the firmware and demo code!

# Adafruit PyPortal - IoT 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** &nbsp;"flash" drive to iterate.

The following instructions will show you how to install CircuitPython. If you've already installed CircuitPython but are looking to update it or reinstall it, the same steps work for that as well!

## Set up CircuitPython Quick Start!

Follow this quick step-by-step for super-fast Python power :)

[Download the latest version of CircuitPython for the PyPortal via CircuitPython.org](https://circuitpython.org/board/pyportal/)
[Download the latest version of CircuitPython for the PyPortal Pynt via CircuitPython.org](https://circuitpython.org/board/pyportal_pynt/)
 **Click the link above to download the latest version of CircuitPython for the PyPortal.**

Download and save it to your desktop (or wherever is handy).

![circuitpython_pyportal-uf2.png](https://cdn-learn.adafruit.com/assets/assets/000/073/615/medium640/circuitpython_pyportal-uf2.png?1553610968)

Plug your PyPortal into your computer using a known-good USB cable.

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

Double-click the **Reset** button on the top in the middle (magenta arrow) on your board, and you will see the NeoPixel RGB LED (green arrow) turn green. If it turns red, check the USB cable, try another USB port, etc.&nbsp; **Note:** The little red LED next to the USB connector will pulse red. That's ok!

If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!

![circuitpython_PyPortalResetNeoPIxel.jpg](https://cdn-learn.adafruit.com/assets/assets/000/071/993/medium640/circuitpython_PyPortalResetNeoPIxel.jpg?1551213425)

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

Drag the **adafruit-circuitpython-pyportal-\<whatever\>.uf2** file to **PORTALBOOT.**

![circuitpython_PyPortal_PORTALBOOT.png](https://cdn-learn.adafruit.com/assets/assets/000/072/029/medium640/circuitpython_PyPortal_PORTALBOOT.png?1551287972)

![circuitpython_PyPortal_Drag_UF2.png](https://cdn-learn.adafruit.com/assets/assets/000/072/030/medium640/circuitpython_PyPortal_Drag_UF2.png?1551287983)

The LED will flash. Then, the **PORTALBOOT** drive will disappear and a new disk drive called **CIRCUITPY** will appear.

If you haven't added any code to your board, the only file that will be present is **boot\_out.txt**. This is absolutely normal! It's time for you to add your **code.py** and get started!

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

![circuitpython_PyPortalCIRCUITPY.png](https://cdn-learn.adafruit.com/assets/assets/000/071/995/medium640/circuitpython_PyPortalCIRCUITPY.png?1551213875)

## PyPortal Default Files

Click below to download a zip of the files that shipped on the PyPortal or PyPortal Pynt.

[PyPortal Default Files](https://github.com/adafruit/circuitpython-default-files/tree/main/boards/pyportal/4.x)
[PyPortal Pynt Default Files](https://github.com/adafruit/circuitpython-default-files/tree/main/boards/pyportal_pynt/5.x)
# Adafruit PyPortal - IoT for CircuitPython

## Installing the Mu Editor

Mu is a simple code editor that works with the Adafruit CircuitPython boards. It's written in Python and works on Windows, MacOS, Linux and Raspberry Pi. The serial console is built right in so you get immediate feedback from your board's serial output!

Info: Mu is our recommended editor - please use it (unless you are an experienced coder with a favorite editor already!). While it has been announced end of life in 2026, it still works fine.

You are free to use whatever text editor you wish along with a terminal program to connect to the CircuitPython REPL. Thonny is one such editor.

## Download and Install Mu
Download Mu from&nbsp;[https://codewith.mu](https://codewith.mu/).

Click the&nbsp; **Download** link for downloads and installation instructions.

Click **Start Here&nbsp;** to find a wealth of other information, including extensive tutorials and and how-to's.

&nbsp;

![circuitpython_WtCP_codewithdotmu_main_page.png](https://cdn-learn.adafruit.com/assets/assets/000/105/677/medium640/circuitpython_WtCP_codewithdotmu_main_page.png?1634749167)

Warning: 

## Starting Up Mu
The first time you start Mu, you will be prompted to select your 'mode' - you can always change your mind later. For now please select **CircuitPython**!

The current mode is displayed in the lower right corner of the window, next to the "gear" icon. If the mode says "Microbit" or something else, click the **Mode** button in the upper left, and then choose "CircuitPython" in the dialog box that appears.

![circuitpython_WtCP_Mu_mode_dialogue.png](https://cdn-learn.adafruit.com/assets/assets/000/105/681/medium640/circuitpython_WtCP_Mu_mode_dialogue.png?1634750676)

Mu attempts to auto-detect your board on startup, so if you do not have a CircuitPython board plugged in with a **CIRCUITPY** drive available, Mu will inform you where it will store any code you save until you plug in a board.

To avoid this warning, plug in a board and ensure that the **CIRCUITPY** drive is mounted before starting Mu.

![circuitpython_WtCP_Mu_device_not_found.png](https://cdn-learn.adafruit.com/assets/assets/000/105/679/medium640/circuitpython_WtCP_Mu_device_not_found.png?1634749722)

## Using Mu

You can now explore Mu! The three main sections of the window are labeled below; the button bar, the text editor, and the serial console / REPL.

![](https://cdn-learn.adafruit.com/assets/assets/000/098/505/medium800/circuitpython_Mu_Window_Explained.png?1609970219)

Now you're ready to code! Let's keep going...

# Adafruit PyPortal - IoT for CircuitPython

## Creating and Editing Code

One of the best things about CircuitPython is how simple it is to get code up and running. This section covers how to create and edit your first CircuitPython program.

To create and edit code, all you'll need is an editor. There are many options. **Adafruit strongly recommends using Mu! It's designed for CircuitPython, and it's really simple and easy to use, with a built in serial console!**

If you don't or can't use Mu, there are a number of other editors that work quite well. The [Recommended Editors page](https://learn.adafruit.com/welcome-to-circuitpython/recommended-editors) has more details. Otherwise, make sure you do "Eject" or "Safe Remove" on Windows or "sync" on Linux after writing a file if you aren't using Mu. (This was formerly not a problem on macOS, but see the warning below.)

Warning: macOS Sonoma 14.1 introduced a bug that delays writes to small drives such as CIRCUITPY drives. This caused errors when saving files to CIRCUITPY. There is a [workaround](https://learn.adafruit.com/welcome-to-circuitpython/troubleshooting#macos-sonoma-14-dot-x-disk-errors-writing-to-circuitpy-3160304). The bug was fixed in Sonoma 14.4, but at the cost of greatly slowed writes to drives 1GB or smaller. 

## Creating Code
Installing CircuitPython generates a **code.py** file on your **CIRCUITPY** drive. To begin your own program, open your editor, and load the **code.py** file from the **CIRCUITPY** drive.

If you are using Mu, click the **Load** button in the button bar, navigate to the **CIRCUITPY** drive, and choose **code.py**.

![circuitpython_WtCP_Mu_open_code_py.png](https://cdn-learn.adafruit.com/assets/assets/000/105/703/medium640/circuitpython_WtCP_Mu_open_code_py.png?1634763740)

Copy and paste the following code into your editor:

```python
import board
import digitalio
import time

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

while True:
    led.value = True
    time.sleep(0.5)
    led.value = False
    time.sleep(0.5)
```

Danger: The KB2040, QT Py , Qualia, and the Trinkeys do not have a built-in little red LED! There is an addressable RGB NeoPixel LED. The above example will NOT work on the KB2040, QT Py, Qualia, or the Trinkeys!

If you're using a KB2040, QT Py, Quaila, or a Trinkey, or any other board without a single-color LED that can blink, please download the [NeoPixel blink example](https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/Welcome_to_CircuitPython/code.py).

Info: 

It will look like this. Note that under the `while True:` line, the next four lines begin with four spaces to indent them, and they're indented exactly the same amount. All the lines before that have no spaces before the text.

![circuitpython_WtCP_Mu_blink_example.png](https://cdn-learn.adafruit.com/assets/assets/000/105/704/medium640/circuitpython_WtCP_Mu_blink_example.png?1634763853)

Save the **code.py** file on your **CIRCUITPY** drive.

![circuitpython_WtCP_Mu_save_bliink_example.png](https://cdn-learn.adafruit.com/assets/assets/000/105/705/medium640/circuitpython_WtCP_Mu_save_bliink_example.png?1634763954)

The little LED should now be blinking. Once per half-second.

Congratulations, you've just run your first CircuitPython program!

Info: On most boards you'll find a tiny red LED. On the ItsyBitsy nRF52840, you'll find a tiny blue LED. On QT Py M0, QT Py RP2040, Qualia, and the Trinkey series, you will find only an RGB NeoPixel LED.

## Editing Code
To edit code, open the&nbsp; **code.py** &nbsp;file on your **CIRCUITPY** drive into your editor.

&nbsp;

Make the desired changes to your code. Save the file. That's it!

![circuitpython_WtCP_Mu_open_code_py.png](https://cdn-learn.adafruit.com/assets/assets/000/105/706/medium640/circuitpython_WtCP_Mu_open_code_py.png?1634764153)

### Your code changes are run as soon as the file is done saving.

There's one warning before you continue...

Danger: 

The CircuitPython code on your board detects when the files are changed or written and will automatically re-start your code. This makes coding very fast because you save, and it re-runs. If you unplug or reset the board before your computer finishes writing the file to your board, you can corrupt the drive. If this happens, you may lose the code you've written, so it's important to backup your code to your computer regularly.

There are a couple of ways to avoid filesystem corruption.

### **1. Use an editor that writes out the file completely when you save it.**

Check out the [Recommended Editors page](https://learn.adafruit.com/welcome-to-circuitpython/recommended-editors) for details on different editing options.

Info: 

### **2. Eject or Sync the Drive After Writing**

If you are using one of our not-recommended-editors, not all is lost! You can still make it work.

On Windows, you can Eject or Safe Remove the **CIRCUITPY** drive. It won't actually eject, but it will force the operating system to save your file to disk. On Linux, use the **sync** command in a terminal to force the write to disk.

You also need to do this if you use Windows Explorer or a Linux graphical file manager to drag a file onto **CIRCUITPY**.

### 

Don't worry! Corrupting the drive isn't the end of the world (or your board!). If this happens, follow the steps found on the [Troubleshooting](https://learn.adafruit.com/welcome-to-circuitpython/troubleshooting) page of every board guide to get your board up and running again.

Info: If you are having trouble saving code on Windows 10, try including this code snippet at the top of <b>code.py</b>:

```python
import supervisor
supervisor.runtime.autoreload = False
```

## Back to Editing Code...

Now! Let's try editing the program you added to your board. Open your **code.py** file into your editor. You'll make a simple change. Change the first `0.5` to `0.1`. The code should look like this:

```python
import board
import digitalio
import time

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

while True:
    led.value = True
    time.sleep(0.1)
    led.value = False
    time.sleep(0.5)
```

Leave the rest of the code as-is. Save your file. See what happens to the LED on your board? Something changed! Do you know why?

You don't have to stop there! Let's keep going. Change the second `0.5` to `0.1` so it looks like this:

```
while True:
    led.value = True
    time.sleep(0.1)
    led.value = False
    time.sleep(0.1)
```

Now it blinks really fast! You decreased the both time that the code leaves the LED on and off!

Now try increasing both of the `0.1` to `1`. Your LED will blink much more slowly because you've increased the amount of time that the LED is turned on and off.

Well done! You're doing great! You're ready to start into new examples and edit them to see what happens! These were simple changes, but major changes are done using the same process. Make your desired change, save it, and get the results. That's really all there is to it!

# Naming Your Program File
CircuitPython looks for a code file on the board to run. There are four options: **code.txt** , **code.py** , **main.txt** and **main.py**. CircuitPython looks for those files, in that order, and then runs the first one it finds. While **code.py** is the recommended name for your code file, it is important to know that the other options exist. If your program doesn't seem to be updating as you work, make sure you haven't created another code file that's being read instead of the one you're working on.

# Adafruit PyPortal - IoT for CircuitPython

## Connecting to the Serial Console

One of the staples of CircuitPython (and programming in general!) is something called a "print statement". This is a line you include in your code that causes your code to output text. A print statement in CircuitPython (and Python) looks like this:

`print("Hello, world!")`

This line in your code.py would result in:

`Hello, world!`

However, these print statements need somewhere to display. That's where the serial console comes in!

The serial console receives output from your CircuitPython board sent over USB and displays it so you can see it. This is necessary when you've included a print statement in your code and you'd like to see what you printed. It is also helpful for troubleshooting errors, because your board will send errors and the serial console will display those too.  
  
The serial console requires an editor that has a built in terminal, or a separate terminal program. A terminal is a program that gives you a text-based interface to perform various tasks.

# Are you using Mu?

If so, good news! The serial console&nbsp; **is built into Mu** and will&nbsp; **autodetect your board** making using the serial console _really really easy_.

First, make sure your CircuitPython board is plugged in.

If you open Mu without a board plugged in, you may encounter the error seen here, letting you know no CircuitPython board was found and indicating where your code will be stored until you plug in a board.

[If you are using Windows 7, make sure you installed the drivers](https://learn.adafruit.com/welcome-to-circuitpython/windows-7-and-8-1-drivers).

![circuitpython_WtCP_Mu_no_board_connected_error.png](https://cdn-learn.adafruit.com/assets/assets/000/105/925/medium640/circuitpython_WtCP_Mu_no_board_connected_error.png?1635198700)

Once you've opened Mu with your board plugged in, look for the **Serial** button in the button bar and click it.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/924/medium800/circuitpython_WtCP_Mu_serial_button.png?1635198511)

The Mu window will split in two, horizontally, and display the serial console at the bottom.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/922/medium800/circuitpython_WtcP_Mu_serial.png?1635196320)

Info: 

## Serial Console Issues or Delays on Linux

If you're on Linux, and are seeing multi-second delays connecting to the serial console, or are seeing "AT" and other gibberish when you connect, then the `modemmanager` service might be interfering. Just remove it; it doesn't have much use unless you're still using dial-up modems.

To remove `modemmanager`, type the following command at a shell:

```terminal
sudo apt purge modemmanager
```

## Setting Permissions on Linux

On Linux, if you see an error box something like the one below when you press the **Serial** button, you need to add yourself to a user group to have permission to connect to the serial console.

![](https://cdn-learn.adafruit.com/assets/assets/000/075/267/medium800/circuitpython_no-dialout.png?1557112353)

On Ubuntu and Debian, add yourself to the **dialout** group by doing:

```terminal
sudo adduser $USER dialout
```

After running the command above, reboot your machine to gain access to the group. On other Linux distributions, the group you need may be different. See the [Advanced Serial Console on Linux](https://learn.adafruit.com/welcome-to-circuitpython/advanced-serial-console-on-linux)&nbsp;for details on how to add yourself to the right group.

# Using Something Else?

If you're not using Mu to edit, or if for some reason you are not a fan of its built in serial console, you can run the serial console from a separate program.

Windows requires you to download a terminal program. [Check out the Advanced Serial Console on Windows page for more details.](../../../../welcome-to-circuitpython/advanced-serial-console-on-windows)

MacOS has serial connection programs you can run in Terminal. [Check the Advanced Serial Console on Mac page for more details.](../../../../welcome-to-circuitpython/advanced-serial-console-on-mac-and-linux)

Linux has multiple terminal programs included options are available for download. [Check the Advanced Serial Console on Linux page for more details.](https://learn.adafruit.com/welcome-to-circuitpython/advanced-serial-console-on-linux)

Once connected, you'll see something like the following.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/923/medium800/circuitpython_WtCP_terminal_serial.png?1635196439)

# Adafruit PyPortal - IoT for CircuitPython

## Interacting with the Serial Console

Once you've successfully connected to the serial console, it's time to start using it.

The code you wrote earlier has no output to the serial console. So, you're going to edit it to create some output.

Open your code.py file into your editor, and include a `print` statement. You can print anything you like! Just include your phrase between the quotation marks inside the parentheses. For example:

```python
import board
import digitalio
import time

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

while True:
    print("Hello, CircuitPython!")
    led.value = True
    time.sleep(1)
    led.value = False
    time.sleep(1)
```

Save your file.

Now, let's go take a look at the window with our connection to the serial console.

![](https://cdn-learn.adafruit.com/assets/assets/000/049/225/medium800/circuitpython_CPHelloCPconsole.png?1513104648)

Excellent! Our print statement is showing up in our console! Try changing the printed text to something else.

```python
import board
import digitalio
import time

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

while True:
    print("Hello back to you!")
    led.value = True
    time.sleep(1)
    led.value = False
    time.sleep(1)
```

Keep your serial console window where you can see it. Save your file. You'll see what the serial console displays when the board reboots. Then you'll see your new change!

![](https://cdn-learn.adafruit.com/assets/assets/000/049/229/medium800/circuitpython_CPHelloBackConsole.png?1513107530)

The `Traceback (most recent call last):` is telling you the last thing your board was doing before you saved your file. This is normal behavior and will happen every time the board resets. This is really handy for troubleshooting. Let's introduce an error so you can see how it is used.

Delete the `e` at the end of `True` from the line `led.value = True` so that it says `led.value = Tru`

```python
import board
import digitalio
import time

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

while True:
    print("Hello back to you!")
    led.value = Tru
    time.sleep(1)
    led.value = False
    time.sleep(1)
```

Save your file. You will notice that your red LED will stop blinking, and you may have a colored status LED blinking at you. This is because the code is no longer correct and can no longer run properly. You need to fix it!

Usually when you run into errors, it's not because you introduced them on purpose. You may have 200 lines of code, and have no idea where your error could be hiding. This is where the serial console can help. Let's take a look!

![](https://cdn-learn.adafruit.com/assets/assets/000/049/241/medium800/circuitpython_CPConsoleCodeError.png?1513109775)

The `Traceback (most recent call last):` is telling you that the last thing it was able to run was `line 10` in your code. The next line is your error: `NameError: name 'Tru' is not defined`. This error might not mean a lot to you, but combined with knowing the issue is on line 10, it gives you a great place to start!

Go back to your code, and take a look at line 10. Obviously, you know what the problem is already. But if you didn't, you'd want to look at line 10 and see if you could figure it out. If you're still unsure, try googling the error to get some help. In this case, you know what to look for. You spelled True wrong. Fix the typo and save your file.

![](https://cdn-learn.adafruit.com/assets/assets/000/049/240/medium800/circuitpython_CPConsoleCodeErrorFixed.png?1513109537)

Nice job fixing the error! Your serial console is streaming and your red LED Is blinking again.

The serial console will display any output generated by your code. Some sensors, such as a humidity sensor or a thermistor, receive data and you can use print statements to display that information. You can also use print statements for troubleshooting, which is called "print debugging". Essentially, if your code isn't working, and you want to know where it's failing, you can put print statements in various places to see where it stops printing.

The serial console has many uses, and is an amazing tool overall for learning and programming!

# Adafruit PyPortal - IoT for CircuitPython

## The REPL

The other feature of the serial connection is the **R** ead- **E** valuate **-**** P **rint-** L**oop, or REPL. The REPL allows you to enter individual lines of code and have them run immediately. It's really handy if you're running into trouble with a particular program and can't figure out why. It's interactive so it's great for testing new ideas.

## Entering the REPL

To use the REPL, you first need to be connected to the serial console. Once that connection has been established, you'll want to press **CTRL+C**.

If there is code running, in this case code measuring distance, it will stop and you'll see `Press any key to enter the REPL. Use CTRL-D to reload.` Follow those instructions, and press any key on your keyboard.

The `Traceback (most recent call last):` is telling you the last thing your board was doing before you pressed Ctrl + C and interrupted it. The `KeyboardInterrupt` is you pressing CTRL+C. This information can be handy when troubleshooting, but for now, don't worry about it. Just note that it is expected behavior.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/940/medium800/circuitpython_WtCP_ctrl_c_code_running.png?1635269118)

If your **code.py** file is empty or does not contain a loop, it will show an empty output and `Code done running.`. There is no information about what your board was doing before you interrupted it because there is no code running.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/941/medium800/circuitpython_WtCP_ctrl_c_no_code.png?1635269140)

If you have no **code.py** on your **CIRCUITPY** drive, you will enter the REPL immediately after pressing CTRL+C. Again, there is no information about what your board was doing before you interrupted it because there is no code running.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/943/medium800/circuitpython_WtCP_ctrl_c_no_code_py.png?1635269629)

Regardless, once you press a key you'll see a `>>>` prompt welcoming you to the REPL!

![](https://cdn-learn.adafruit.com/assets/assets/000/105/945/medium800/circuitpython_WtCP_entering_REPL.png?1635270441)

If you have trouble getting to the `>>>` prompt, try pressing Ctrl + C a few more times.

The first thing you get from the REPL is information about your board.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/946/medium800/circuitpython_WtCP_REPL_info_header.png?1635270448)

This line tells you the version of CircuitPython you're using and when it was released. Next, it gives you the type of board you're using and the type of microcontroller the board uses. Each part of this may be different for your board depending on the versions you're working with.

This is followed by the CircuitPython prompt.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/947/medium800/circuitpython_WtCP_REPL_prompt.png?1635270536)

## Interacting with the REPL

From this prompt you can run all sorts of commands and code. The first thing you'll do is run `help()`. This will tell you where to start exploring the REPL. To run code in the REPL, type it in next to the REPL prompt.

Type `help()` next to the prompt in the REPL.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/948/medium800/circuitpython_WtcP_REPL_help_prompt.png?1635271482)

Then press enter. You should then see a message.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/949/medium800/circuitpython_WtCP_REPL_help.png?1635271524)

First part of the message is another reference to the version of CircuitPython you're using. Second, a URL for the CircuitPython related project guides. Then... wait. What's this? To list built-in modules type `help("modules").` Remember the modules you learned about while going through creating code? That's exactly what this is talking about! This is a perfect place to start. Let's take a look!

Type `help("modules")` into the REPL next to the prompt, and press enter.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/950/medium800/circuitpython_WtCP_REPL_help_modules.png?1635271533)

This is a list of all the core modules built into CircuitPython, including `board`. Remember, `board` contains all of the pins on the board that you can use in your code. From the REPL, you are able to see that list!

Type `import board` into the REPL and press enter. It'll go to a new prompt. It might look like nothing happened, but that's not the case! If you recall, the `import` statement simply tells the code to expect to do something with that module. In this case, it's telling the REPL that you plan to do something with that module.

![](https://cdn-learn.adafruit.com/assets/assets/000/103/548/medium800/circuitpython_WTCP_the_REPL_import_board.png?1626882305)

Next, type `dir(board)` into the REPL and press enter.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/951/medium800/circuitpython_WtCP_REPL_dir_board.png?1635271604)

This is a list of all of the pins on your board that are available for you to use in your code. Each board's list will differ slightly depending on the number of pins available. Do you see `LED`? That's the pin you used to blink the red LED!

The REPL can also be used to run code. Be aware that **any code you enter into the REPL isn't saved** anywhere. If you're testing something new that you'd like to keep, make sure you have it saved somewhere on your computer as well!

Every programmer in every programming language starts with a piece of code that says, "Hello, World." You're going to say hello to something else. Type into the REPL:

`print("Hello, CircuitPython!")`

Then press enter.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/952/medium800/circuitpython_WtCP_REPL_print.png?1635271677)

That's all there is to running code in the REPL! Nice job!

You can write single lines of code that run stand-alone. You can also write entire programs into the REPL to test them. Remember that nothing typed into the REPL is saved.

There's a lot the REPL can do for you. It's great for testing new ideas if you want to see if a few new lines of code will work. It's fantastic for troubleshooting code by entering it one line at a time and finding out where it fails. It lets you see what modules are available and explore those modules.

Try typing more into the REPL to see what happens!

Warning: 

# Returning to the Serial Console

When you're ready to leave the REPL and return to the serial console, simply press **CTRL+D**. This will reload your board and reenter the serial console. You will restart the program you had running before entering the REPL. In the console window, you'll see any output from the program you had running. And if your program was affecting anything visual on the board, you'll see that start up again as well.

You can return to the REPL at any time!

![](https://cdn-learn.adafruit.com/assets/assets/000/105/942/medium800/circuitpython_WtCP_ctrl_c_no_code_py.png?1635269617)

# Adafruit PyPortal - IoT for CircuitPython

## CircuitPython Libraries

Danger: 

Each CircuitPython program you run needs to have a lot of information to work. The reason CircuitPython is so simple to use is that most of that information is stored in other files and works in the background. These files are called _libraries_. Some of them are built into CircuitPython. Others are stored on your **CIRCUITPY** drive in a folder called **lib**. Part of what makes CircuitPython so great is its ability to store code separately from the firmware itself. Storing code separately from the firmware makes it easier to update both the code you write and the libraries you depend.

Your board may ship with a **lib** folder already, it's in the base directory of the drive. If not, simply create the folder yourself. When you first install CircuitPython, an empty **lib** directory will be created for you.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/961/medium800/circuitpython_WtCP_CIRCUITPY_contents.png?1635281033)

CircuitPython libraries work in the same way as regular Python modules so the [Python docs](https://docs.python.org/3/tutorial/modules.html) are an excellent reference for how it all should work. In Python terms, you can place our library files in the **lib** directory because it's part of the Python path by default.

One downside of this approach of separate libraries is that they are not built in. To use them, one needs to copy them to the **CIRCUITPY** drive before they can be used. Fortunately, there is a library bundle.

The bundle and the library releases on GitHub also feature optimized versions of the libraries with the **.mpy** file extension. These files take less space on the drive and have a smaller memory footprint as they are loaded.

Due to the regular updates and space constraints, Adafruit does not ship boards with the entire bundle. Therefore, you will need to load the libraries you need when you begin working with your board. You can find example code in the guides for your board that depends on external libraries.

Either way, as you start to explore CircuitPython, you'll want to know how to get libraries on board.

# The Adafruit Learn Guide Project Bundle

The quickest and easiest way to get going with a project from the Adafruit Learn System is by utilising the Project Bundle. Most guides now have a **Download Project Bundle** button available at the top of the full code example embed. This button downloads all the necessary files, including images, etc., to get the guide project up and running. Simply click, open the resulting zip, copy over the right files, and you're good to go!

The first step is to find the Download Project Bundle button in the guide you're working on.

Info: 

![](https://cdn-learn.adafruit.com/assets/assets/000/111/837/medium800/circuitpython_PB_download_project_bundle_button.png?1652915277)

Warning: 

The Download Project Bundle button downloads a zip file. This zip contains a series of directories, nested within which is the **code.py** , any applicable assets like images or audio, and the **lib/** folder containing all the necessary libraries. The following zip was downloaded from the Piano in the Key of Lime guide.

![](https://cdn-learn.adafruit.com/assets/assets/000/111/838/medium800/circuitpython_PB_downloaded_and_expanded_zip.png?1652915317)

Info: 

When you open the zip, you'll find some nested directories. Navigate through them until you find what you need. You'll eventually find a directory for your CircuitPython version (in this case, 7.x). In the version directory, you'll find the file and directory you need: **code.py** and **lib/**. Once you find the content you need, you can copy it all over to your **CIRCUITPY** drive, replacing any files already on the drive with the files from the freshly downloaded zip.

Info: 

Once you copy over all the relevant files, the project should begin running! If you find that the project is not running as expected, make sure you've copied ALL of the project files onto your microcontroller board.

That's all there is to using the Project Bundle!

# The Adafruit CircuitPython Library Bundle

Adafruit provides CircuitPython libraries for much of the hardware they provide, including sensors, breakouts and more. To eliminate the need for searching for each library individually, the libraries are available together in the Adafruit CircuitPython Library Bundle. The bundle contains all the files needed to use each library.

## Downloading the Adafruit CircuitPython Library Bundle

You can download the latest Adafruit CircuitPython Library Bundle release by clicking the button below. The libraries are being constantly updated and improved, so you'll always want to download the latest bundle.&nbsp;

**Match up the bundle version with the version of CircuitPython you are running.** For example, you would download the 6.x library bundle if you're running any version of CircuitPython 6, or the 7.x library bundle if you're running any version of CircuitPython 7, etc. If you mix libraries with major CircuitPython versions, you will get incompatible mpy errors due to changes in library interfaces possible during major version changes.

[Click to visit circuitpython.org for the latest Adafruit CircuitPython Library Bundle](https://circuitpython.org/libraries)
 **Download the bundle version that matches your CircuitPython firmware version.** If you don't know the version, check the version info in **boot\_out.txt** file on the **CIRCUITPY** drive, or the initial prompt in the CircuitPython REPL. For example, if you're running v7.0.0, download the 7.x library bundle.

There's also a **py** bundle which contains the uncompressed python files, you probably _don't_ want that unless you are doing advanced work on libraries.

# The CircuitPython Community Library Bundle

The CircuitPython Community Library Bundle is made up of libraries written and provided by members of the CircuitPython community. These libraries are often written when community members encountered hardware not supported in the Adafruit Bundle, or to support a personal project. The authors all chose to submit these libraries to the Community Bundle make them available to the community.

**These libraries are maintained by their authors and are not supported by Adafruit.** As you would with any library, if you run into problems, feel free to file an issue on the GitHub repo for the library. Bear in mind, though, that most of these libraries are supported by a single person and you should be patient about receiving a response. Remember, these folks are not paid by Adafruit, and are volunteering their personal time when possible to provide support.

## Downloading the CircuitPython Community Library Bundle

You can download the latest CircuitPython Community Library Bundle release by clicking the button below. The libraries are being constantly updated and improved, so you'll always want to download the latest bundle.

[Click for the latest CircuitPython Community Library Bundle release](https://github.com/adafruit/CircuitPython_Community_Bundle/releases)
The link takes you to the latest release of the CircuitPython Community Library Bundle on GitHub. There are multiple versions of the bundle available. **Download the bundle version that matches your CircuitPython firmware version.** If you don't know the version, check the version info in **boot\_out.txt** file on the **CIRCUITPY** drive, or the initial prompt in the CircuitPython REPL. For example, if you're running v7.0.0, download the 7.x library bundle.

# Understanding the Bundle

After downloading the zip, extract its contents. This is usually done by double clicking on the zip. On Mac OSX, it places the file in the same directory as the zip.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/908/medium800/circuitpython_WtCP_lib_bundle_extracted.png?1635183852)

Open the bundle folder. Inside you'll find two information files, and two folders. One folder is the lib bundle, and the other folder is the examples bundle.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/909/medium800/circuitpython_WtCP_lib_zip_contents.png?1635183864)

Now open the lib folder. When you open the folder, you'll see a large number of **.**** mpy** files, and folders.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/910/medium800/circuitpython_WtCP_lib_bundle_folder_contents.png?1635183871)

## Example Files

All example files from each library are now included in the bundles in an **examples** directory (as seen above), as well as an examples-only bundle. These are included for two main reasons:

- Allow for quick testing of devices.
- Provide an example base of code, that is easily built upon for individualized purposes.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/911/medium800/circuitpython_WtCP_examples_bundle_directory_contents.png?1635184002)

## Copying Libraries to Your Board

First open the **lib** folder on your **CIRCUITPY** drive. Then, open the **lib** folder you extracted from the downloaded zip. Inside you'll find a number of folders and **.mpy** files. Find the library you'd like to use, and copy it to the **lib** folder on **CIRCUITPY**.

If the library is a directory with multiple **.mpy** files in it, be sure to **copy the entire folder to CIRCUITPY/lib**.

This also applies to example files. Open the **examples** folder you extracted from the downloaded zip, and copy the applicable file to your **CIRCUITPY** drive. Then, rename it to **code.py** to run it.

Info: 

# Understanding Which Libraries to Install

You now know how to load libraries on to your CircuitPython-compatible microcontroller board. You may now be wondering, how do you know _which_ libraries you need to install? Unfortunately, it's not always straightforward. Fortunately, there is an obvious place to start, and a relatively simple way to figure out the rest. First up: the best place to start.

When you look at most CircuitPython examples, you'll see they begin with one or more `import` statements. These typically look like the following:

- `import library_or_module`

However, `import` statements can also sometimes look like the following:

- `from library_or_module import name`
- `from library_or_module.subpackage import name`
- `from library_or_module import name as local_name`

They can also have more complicated formats, such as including a `try` / `except` block, etc.

The important thing to know is that **an** `import` **statement will always include the name of the module or library that you're importing**.

Therefore, the best place to start is by reading through the `import` statements.

Here is an example import list for you to work with in this section. There is no setup or other code shown here, as the purpose of this section involves only the import list.

```python
import time
import board
import neopixel
import adafruit_lis3dh
import usb_hid
from adafruit_hid.consumer_control import ConsumerControl
from adafruit_hid.consumer_control_code import ConsumerControlCode
```

Keep in mind, not all imported items are libraries. Some of them are almost always built-in CircuitPython modules. How do you know the difference? Time to visit the REPL.

In the [Interacting with the REPL section](https://learn.adafruit.com/welcome-to-circuitpython/the-repl#interacting-with-the-repl-2977486-14) on [The REPL page](https://learn.adafruit.com/welcome-to-circuitpython/the-repl) in this guide, the `help("modules")` command is discussed. This command provides a list of all of the built-in modules available in CircuitPython for your board. So, if you connect to the serial console on your board, and enter the REPL, you can run `help("modules")` to see what modules are available for your board. Then, as you read through the `import` statements, you can, for the purposes of figuring out which libraries to load, ignore the statement that import modules.

The following is the list of modules built into CircuitPython for the Feather RP2040. Your list may look similar or be anything down to a significant subset of this list for smaller boards.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/967/medium800/circuitpython_WtCP_CP_libs_help_modules_feather_rp2040.png?1635369618)

Now that you know what you're looking for, it's time to read through the import statements. The first two, `time` and `board`, are on the modules list above, so they're built-in.

The next one, `neopixel`, is not on the module list. That means it's your first library! So, you would head over to the bundle zip you downloaded, and search for **neopixel**. There is a **neopixel.mpy** file in the bundle zip. Copy it over to the **lib** folder on your **CIRCUITPY** drive. The following one, `adafruit_lis3dh`, is also not on the module list. Follow the same process for **adafruit\_lis3dh** , where you'll find **adafruit\_lis3dh.mpy** , and copy that over.

The fifth one is `usb_hid`, and it is in the modules list, so it is built in. Often all of the built-in modules come first in the import list, but sometimes they don't! Don't assume that everything after the first library is also a library, and verify each import with the modules list to be sure. Otherwise, you'll search the bundle and come up empty!

The final two imports are not as clear. Remember, when `import` statements are formatted like this, the first thing after the `from` is the library name. In this case, the library name is `adafruit_hid`. A search of the bundle will find an **adafruit\_hid _folder_**. When a library is a folder, you must copy the **entire folder and its contents&nbsp;_as it is in the bundle_** to the **lib** folder on your **CIRCUITPY** drive. In this case, you would copy the entire **adafruit\_hid** folder to your **CIRCUITPY/lib** folder.

Notice that there are _two_ imports that begin with `adafruit_hid`. Sometimes you will need to import more than one thing from the same library. Regardless of how many times you import the same library, you only need to load the library by copying over the **adafruit\_hid** folder _once_.

That is how you can use your example code to figure out what libraries to load on your CircuitPython-compatible board!

There are cases, however, where libraries require other libraries internally. The internally required library is called a _dependency_. In the event of library dependencies, the easiest way to figure out what other libraries are required is to connect to the serial console and follow along with the `ImportError` printed there. The following is a very simple example of an `ImportError`, but the concept is the same for any missing library.

# Example: `ImportError` Due to Missing Library

If you choose to load libraries as you need them, or you're starting fresh with an existing example, you may end up with code that tries to use a library you haven't yet loaded.&nbsp; This section will demonstrate what happens when you try to utilise a library that you don't have loaded on your board, and cover the steps required to resolve the issue.

This demonstration will only return an error if you do not have the required library loaded into the **lib** folder on your **CIRCUITPY** drive **.**

Let's use a modified version of the Blink example.

```auto
import board
import time
import simpleio

led = simpleio.DigitalOut(board.LED)

while True:
    led.value = True
    time.sleep(0.5)
    led.value = False
    time.sleep(0.5)
```

Save this file. Nothing happens to your board. Let's check the serial console to see what's going on.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/964/medium800/circuitpython_WtCP_serial_console_ImportError.png?1635355664)

You have an `ImportError`. It says there is `no module named 'simpleio'`. That's the one you just included in your code!

Click the link above to download the correct bundle. Extract the lib folder from the downloaded bundle file. Scroll down to find **simpleio.mpy**. This is the library file you're looking for! Follow the steps above to load an individual library file.

The LED starts blinking again! Let's check the serial console.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/965/medium800/circuitpython_WtCP_CP_libraries_ImportError_resolved.png?1635355829)

No errors! Excellent. You've successfully resolved an `ImportError`!

If you run into this error in the future, follow along with the steps above and choose the library that matches the one you're missing.

# Library Install on Non-Express Boards

If you have an M0 non-Express board such as Trinket M0, Gemma M0, QT Py M0, or one of the M0 Trinkeys, you'll want to follow the same steps in the example above to install libraries as you need them. Remember, you don't need to wait for an `ImportError` if you know what library you added to your code. Open the library bundle you downloaded, find the library you need, and drag it to the **lib** folder on your **CIRCUITPY** drive.

You can still end up running out of space on your M0 non-Express board even if you only load libraries as you need them. There are a number of steps you can use to try to resolve this issue. You'll find suggestions on the [Troubleshooting page](https://learn.adafruit.com/welcome-to-circuitpython/troubleshooting).

# Updating CircuitPython Libraries and Examples

Libraries and examples are updated from time to time, and it's important to update the files you have on your **CIRCUITPY** drive.

To update a single library or example, follow the same steps above. When you drag the library file to your lib folder, it will ask if you want to replace it. Say yes. That's it!

A new library bundle is released every time there's an update to a library. Updates include things like bug fixes and new features. It's important to check in every so often to see if the libraries you're using have been updated.

## CircUp CLI Tool

There is a command line interface (CLI) utility called [CircUp](https://learn.adafruit.com/keep-your-circuitpython-libraries-on-devices-up-to-date-with-circup) that can be used to easily install and update libraries on your device. Follow the directions on the [install page within the CircUp learn guide](https://learn.adafruit.com/keep-your-circuitpython-libraries-on-devices-up-to-date-with-circup/install-circup). Once you've got it installed you run the command `circup update` in a terminal to interactively update all libraries on the connected CircuitPython device. See the [usage page in the CircUp guide](https://learn.adafruit.com/keep-your-circuitpython-libraries-on-devices-up-to-date-with-circup/usage) for a full list of functionality

# Adafruit PyPortal - IoT for CircuitPython

## CircuitPython Pins and Modules

CircuitPython is designed to run on microcontrollers and allows you to interface with all kinds of sensors, inputs and other hardware peripherals. There are tons of guides showing how to wire up a circuit, and use CircuitPython to, for example, read data from a sensor, or detect a button press. Most CircuitPython code includes hardware setup which requires various modules, such as `board` or `digitalio`. You import these modules and then use them in your code. How does CircuitPython know to look for hardware in the specific place you connected it, and where do these modules come from?

This page explains both. You'll learn how CircuitPython finds the pins on your microcontroller board, including how to find the available pins for your board and what each pin is named. You'll also learn about the modules built into CircuitPython, including how to find all the modules available for your board.

# CircuitPython Pins

When using hardware peripherals with a CircuitPython compatible microcontroller, you'll almost certainly be utilising pins. This section will cover how to access your board's pins using CircuitPython, how to discover what pins and board-specific objects are available in CircuitPython for your board, how to use the board-specific objects, and how to determine all available pin names for a given pin on your board.

## `import board`

When you're using any kind of hardware peripherals wired up to your microcontroller board, the import list in your code will include `import board`. The `board` module is built into CircuitPython, and is used to provide access to a series of board-specific objects, including pins. Take a look at your microcontroller board. You'll notice that next to the pins are pin labels. You can always access a pin by its pin label. However, there are almost always multiple names for a given pin.

To see all the available board-specific objects and pins for your board, enter the REPL (`>>>`) and run the following commands:

```python
import board
dir(board)
```

Here is the output for the QT Py SAMD21. **You may have a different board, and this list will vary, based on the board.**

![](https://cdn-learn.adafruit.com/assets/assets/000/099/189/medium800/circuitpython_dir-board-output-qt-py.png?1612291495)

The following pins have labels on the physical QT Py SAMD21 board: A0, A1, A2, A3, SDA, SCL, TX, RX, SCK, MISO, and MOSI. You see that there are many more entries available in `board` than the labels on the QT Py.

You can use the pin names on the physical board, regardless of whether they seem to be specific to a certain protocol.

For example, you do not _have_ to use the SDA pin for I2C - you can use it for a button or LED.

On the flip side, there may be multiple names for one pin. For example, on the QT Py SAMD21, pin **A0** is labeled on the physical board silkscreen, but it is available in CircuitPython as both `A0` and `D0`. For more information on finding all the names for a given pin, see the [What Are All the Available Pin Names?](https://learn.adafruit.com/circuitpython-essentials/circuitpython-pins-and-modules#what-are-all-the-available-names-3082670-14) section below.

The results of `dir(board)` for CircuitPython compatible boards will look similar to the results for the QT Py SAMD21 in terms of the pin names, e.g. A0, D0, etc. However, some boards, for example, the Metro ESP32-S2, have different styled pin names. Here is the output for the Metro ESP32-S2.

![](https://cdn-learn.adafruit.com/assets/assets/000/099/215/medium800/circuitpython_Essentials_dir_board_Metro_ESP32-S2.png?1612374794)

Note that most of the pins are named in an IO# style, such as **IO1** and **IO2**. Those pins on the physical board are labeled only with a number, so an easy way to know how to access them in CircuitPython, is to run those commands in the REPL and find the pin naming scheme.

Info: 

## I2C, SPI, and UART

You'll also see there are often (_ **but not always!** _) three special board-specific objects included: `I2C`, `SPI`, and `UART` - each one is for the default pin-set used for each of the three common protocol busses they are named for. These are called _singletons_.

What's a singleton? When you create an object in CircuitPython, you are _instantiating_ ('creating') it. Instantiating an object means you are creating an instance of the object with the unique values that are provided, or "passed", to it.

For example, When you instantiate an I2C object using the `busio` module, it expects two pins: clock and data, typically SCL and SDA. It often looks like this:

```python
i2c = busio.I2C(board.SCL, board.SDA)
```

Then, you pass the I2C object to a driver for the hardware you're using. For example, if you were using the TSL2591 light sensor and its CircuitPython library, the next line of code would be:

```python
tsl2591 = adafruit_tsl2591.TSL2591(i2c)
```

However, CircuitPython makes this simpler by including the `I2C` singleton in the `board` module. Instead of the two lines of code above, you simply provide the singleton as the I2C object. So if you were using the TSL2591 and its CircuitPython library, the two above lines of code would be replaced with:

```python
tsl2591 = adafruit_tsl2591.TSL2591(board.I2C())
```

Info: 

This eliminates the need for the `busio` module, and simplifies the code. Behind the scenes, the `board.I2C()`&nbsp; object is instantiated when you call it, but not before, and on subsequent calls, it returns the same object. Basically, it does not create an object until you need it, and provides the same object every time you need it. You can call `board.I2C()` as many times as you like, and it will always return the same object.

Info: 

## What Are All the Available Names?

Many pins on CircuitPython compatible microcontroller boards have multiple names, however, typically, there's only one name labeled on the physical board. So how do you find out what the other available pin names are? Simple, with the following script! Each line printed out to the serial console contains the set of names for a particular pin.

On a microcontroller board running CircuitPython, first, connect to the serial console.

In the example below, click the **Download Project Bundle** button below to download the necessary libraries and the **code.py** file in a zip file. Extract the contents of the zip file, open the directory **CircuitPython\_Essentials/Pin\_Map\_Script/** and then click on the directory that matches the version of CircuitPython you're using and copy the contents of that directory to your **CIRCUITPY** drive.

Your **CIRCUITPY** drive should now look similar to the following image:

![CIRCUITPY](https://adafruit.github.io/Adafruit_Learning_System_Guides/CircuitPython_Essentials_Pin_Map_Script.png )

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

Here is the result when this script is run on QT Py SAMD21:

![](https://cdn-learn.adafruit.com/assets/assets/000/122/945/medium800/leds_CPE_pin_map_script_QT_Py_M0.png?1690233285)

Each line represents a single pin. Find the line containing the pin name that's labeled on the physical board, and you'll find the other names available for that pin. For example, the first pin on the board is labeled **A0**. The first line in the output is `board.A0 board.D0 (PA02)`. This means that you can access pin **A0** in CircuitPython using both `board.A0` and `board.D0`.

The pins in parentheses are the microcontroller pin names. See the next section for more info on those.

You'll notice there are two "pins" that aren't labeled on the board but appear in the list: `board.NEOPIXEL` and `board.NEOPIXEL_POWER`. Many boards have several of these special pins that give you access to built-in board hardware, such as an LED or an on-board sensor. The QT Py SAMD21 only has one on-board extra piece of hardware, a NeoPixel LED, so there's only the one available in the list. But you can also control whether or not power is applied to the NeoPixel, so there's a separate pin for that.

That's all there is to figuring out the available names for a pin on a compatible microcontroller board in CircuitPython!

## Microcontroller Pin Names

The pin names available to you in the CircuitPython `board` module are not the same as the names of the pins on the microcontroller itself. The board pin names are aliases to the microcontroller pin names. If you look at the datasheet for your microcontroller, you'll likely find a pinout with a series of pin names, such as "PA18" or "GPIO5". If you want to get to the actual microcontroller pin name in CircuitPython, you'll need the `microcontroller.pin` module. As with `board`, you can run `dir(microcontroller.pin)` in the REPL to receive a list of the microcontroller pin names.

![](https://cdn-learn.adafruit.com/assets/assets/000/099/290/medium800/circuitpython_Essentials_microcontroller_pin_names.png?1612822277 Microcontroller pin names for QT Py SAMD21.)

# CircuitPython Built-In Modules

There is a set of modules used in most CircuitPython programs. One or more of these modules is always used in projects involving hardware. Often hardware requires installing a separate library from the Adafruit CircuitPython Bundle. But, if you try to find `board` or `digitalio` in the same bundle, you'll come up lacking. So, where do these modules come from? They're built into CircuitPython! You can find an comprehensive list of built-in CircuitPython modules and the technical details of their functionality from CircuitPython [here](https://circuitpython.readthedocs.io/en/latest/shared-bindings/index.html#modules) and the Python-like modules included [here](https://circuitpython.readthedocs.io/en/latest/docs/library/index.html). However, **not every module is available for every board** due to size constraints or hardware limitations. How do you find out what modules are available for your board?

There are two options for this. You can check the [support matrix](https://circuitpython.readthedocs.io/en/latest/shared-bindings/support_matrix.html#), and search for your board by name. Or, you can use the REPL.

Plug in your board, connect to the serial console and enter the REPL. Type the following command.

```python
help("modules")
```

![](https://cdn-learn.adafruit.com/assets/assets/000/099/208/medium800/circuitpython_Essentials_help_modules_QT_Py.png?1612372532 help("modules") results for QT Py)

That's it! You now know two ways to find all of the modules built into CircuitPython for your compatible microcontroller board.

# Adafruit PyPortal - IoT for CircuitPython

## Frequently Asked Questions

These are some of the common questions regarding CircuitPython and CircuitPython microcontrollers.

### 
       
       
       
        What are some common acronyms to know? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

CP or CPy = [CircuitPython](https://circuitpython.org)  
CPC = [Circuit Playground Classic](https://www.adafruit.com/product/3000)&nbsp;(does not run CircuitPython)  
CPX = [Circuit Playground Express](https://www.adafruit.com/product/3333)  
CPB = [Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)

## Using Older Versions
Danger: 

### I have to continue using CircuitPython 8.x or earlier. Where can I find compatible libraries?

**We are no longer building or supporting the CircuitPython 8.x or earlier library bundles. We highly encourage you to&nbsp;[update CircuitPython to the latest version](https://circuitpython.org/downloads)&nbsp;and use&nbsp;[the current version of the libraries](https://circuitpython.org/libraries).**&nbsp;However, if for some reason you cannot update, here are the last available library bundles for older versions:

- [2.x bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20190903/adafruit-circuitpython-bundle-2.x-mpy-20190903.zip)
- [3.x bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20190903/adafruit-circuitpython-bundle-3.x-mpy-20190903.zip)
- [4.x bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20200707/adafruit-circuitpython-bundle-4.x-mpy-20200707.zip)
- [5.x bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20210129/adafruit-circuitpython-bundle-5.x-mpy-20210129.zip)
- [6.x bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20211213/adafruit-circuitpython-bundle-6.x-mpy-20211213.zip)
- [7.x bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20231003/adafruit-circuitpython-bundle-7.x-mpy-20231003.zip)
- [8.x bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20250213/adafruit-circuitpython-bundle-8.x-mpy-20250213.zip)

## Python Arithmetic
### 
       
       
       
        Does CircuitPython support floating-point numbers? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

All CircuitPython boards support floating point arithmetic, even if the microcontroller chip does not support floating point in hardware. Floating point numbers are stored in 30 bits, with an 8-bit exponent and a 22-bit mantissa. Note that this is two bits less than standard 32-bit single-precision floats. You will get about 5-1/2 digits of decimal precision.&nbsp;

(The&nbsp; **broadcom** &nbsp;port may provide 64-bit floats in some cases.)

### 
       
       
       
        Does CircuitPython support long integers, like regular Python? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

Python long integers (integers of arbitrary size) are available on most builds, except those on boards with the smallest available firmware size. On these boards, integers are stored in 31 bits.

Boards without long integer support are mostly SAMD21 ("M0") boards without an external flash chip, such as the Adafruit Gemma M0, Trinket M0, QT Py M0, and the Trinkey series. There are also a number of third-party boards in this category. There are also a few small STM third-party boards without long integer support.

`time.localtime()`, `time.mktime()`, `time.time()`, and `time.monotonic_ns()` are available only on builds with long integers.

## Wireless Connectivity
### 
       
       
       
        How do I connect to the Internet with CircuitPython? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

If you'd like to include WiFi in&nbsp; your project, your best bet is to use a board that is running natively on ESP32 chipsets - those have WiFi built in!

If your development board has an SPI port and at least 4 additional pins, you can check out [this guide](https://learn.adafruit.com/adafruit-io-basics-airlift) on using AirLift with CircuitPython - extra wiring is required and some boards like the MacroPad or NeoTrellis do not have enough available pins to add the hardware support.

For further project examples, and guides about using AirLift with specific hardware, check out [the Adafruit Learn System](https://learn.adafruit.com/search?q=airlift).

### How do I do BLE (Bluetooth Low Energy) with CircuitPython?

nRF52840, nRF52833, and **as of CircuitPython 9.1.0** , ESP32, ESP32-C3, and ESP32-S3 boards (with 8MB) have the most complete BLE implementation. Your program can act as both a BLE central and peripheral. As a central, you can scan for advertisements, and connect to an advertising board. As a peripheral, you can advertise, and you can create services available to a central. Pairing and bonding are supported.

**Most Espressif boards with only 4MB of flash do not have enough room to include BLE in CircuitPython 9.** &nbsp; Check the [Module Support Matrix](https://docs.circuitpython.org/en/latest/shared-bindings/support_matrix.html) to see if your board has support for `_bleio`. CircuitPython 10 is planned to support `_bleio` on Espressif boards with 4MB flash.

Note that the ESP32-S2 does not have Bluetooth capability.

On most other boards with adequate firmware space, [BLE is available for use with AirLift](https://learn.adafruit.com/adafruit-airlift-breakout/circuitpython-ble) or other NINA-FW-based co-processors. Some boards have this coprocessor on board, such as the [PyPortal](https://learn.adafruit.com/adafruit-pyportal/circuitpython-ble). Currently, this implementation only supports acting as a BLE peripheral. Scanning and connecting as a central are not yet implemented. Bonding and pairing are not supported.

### 
       
       
       
        Are there other ways to communicate by radio with CircuitPython? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

Check out [Adafruit's RFM boards](https://www.adafruit.com/?q=rfm&sort=BestMatch)for simple radio communication supported by CircuitPython, which can be used over distances of 100m to over a km, depending on the version. The RFM SAMD21 M0 boards can be used, but they were not designed for CircuitPython, and have limited RAM and flash space; using the RFM breakouts or FeatherWings with more capable boards will be easier.

## Asyncio and Interrupts
### 
       
       
       
        Is there asyncio support in CircuitPython? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

There is support for asyncio starting with CircuitPython 7.1.0, on all boards except the smallest SAMD21 builds. Read about using it in the [Cooperative Multitasking in CircuitPython](https://learn.adafruit.com/cooperative-multitasking-in-circuitpython) Guide.

### 
       
       
       
        Does CircuitPython support interrupts? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

No. CircuitPython does not currently support interrupts - please use asyncio for multitasking / 'threaded' control of your code

## Status RGB LED
### 
       
       
       
        My RGB NeoPixel/DotStar LED is blinking funny colors - what does it mean? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

The status LED can tell you what's going on with your CircuitPython board. [Read more here for what the colors mean!](https://learn.adafruit.com/welcome-to-circuitpython/troubleshooting#circuitpython-rgb-status-light-2978455-24)

## Memory Issues
### 
       
       
       
        What is a MemoryError? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

Memory allocation errors happen when you're trying to store too much on the board. The CircuitPython microcontroller boards have a limited amount of memory available. You can have about 250 lines of code on the M0 Express boards. If you try to `import` too many libraries, a combination of large libraries, or run a program with too many lines of code, your code will fail to run and you will receive a `MemoryError` in the serial console.

### 
       
       
       
        What do I do when I encounter a MemoryError? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

Try resetting your board. Each time you reset the board, it reallocates the memory. While this is unlikely to resolve your issue, it's a simple step and is worth trying.

Make sure you are using **.mpy** versions of libraries. All of the CircuitPython libraries are available in the bundle in a **.mpy** format which takes up less memory than **.py** format. Be sure that you're using [the latest library bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases) for your version of CircuitPython.

If that does not resolve your issue, try shortening your code. Shorten comments, remove extraneous or unneeded code, or any other clean up you can do to shorten your code. If you're using a lot of functions, you could try moving those into a separate library, creating a **.mpy** of that library, and importing it into your code.

You can turn your entire file into a **.mpy** and `import` that into **code.py**. This means you will be unable to edit your code live on the board, but it can save you space.

### 
       
       
       
        Can the order of my <code class=

It can because the memory gets fragmented differently depending on allocation order and the size of objects. Loading **.mpy** files uses less memory so its recommended to do that for files you aren't editing.

### How can I create my own <strong>.mpy</strong> files?

You can make your own **.mpy** versions of files with `mpy-cross`.

You can download **mpy-cross** for your operating system from [here](https://adafruit-circuit-python.s3.amazonaws.com/index.html?prefix=bin/mpy-cross/). Builds are available for Windows, macOS, x64 Linux, and Raspberry Pi Linux. Choose the latest **mpy-cross** whose version matches the version of CircuitPython you are using.

On macOS and Linux, after you download&nbsp; **mpy-cross** , you must make the the file executable by doing `chmod +x name-of-the-mpy-cross-executable`.

To make a **.mpy** file, run `./mpy-cross path/to/yourfile.py` to create a **yourfile.mpy** in the same directory as the original file.

### 
       
       
       
        How do I check how much memory I have free? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

Run the following to see the number of bytes available for use:

`import gc`  
`gc.mem_free()`

## Unsupported Hardware
### 
       
       
       
        Is ESP8266 or ESP32 supported in CircuitPython? Why not? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

We dropped ESP8266 support as of 4.x - For more information please read about it [here](https://learn.adafruit.com/welcome-to-circuitpython/circuitpython-for-esp8266)!

[As of CircuitPython 8.x we have started to support ESP32 and ESP32-C3 and have added a WiFi workflow for wireless coding!](https://learn.adafruit.com/circuitpython-with-esp32-quick-start)

We also support ESP32-S2 & ESP32-S3, which have native USB.

### 
       
       
       
        Does Feather M0 support WINC1500? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

No, WINC1500 will not fit into the M0 flash space.

### 
       
       
       
        Can AVRs such as ATmega328 or ATmega2560 run CircuitPython? 
 
       
        
       
     
       
        
       
     
       
        
       
     
       
        
       
    

No.

# Adafruit PyPortal - IoT for CircuitPython

## Troubleshooting

From time to time, you will run into issues when working with CircuitPython. Here are a few things you may encounter and how to resolve them.

Danger: 

# Always Run the Latest Version of CircuitPython and Libraries

As CircuitPython development continues and there are new releases, Adafruit will stop supporting older releases. **You need to [update to the latest CircuitPython.](https://circuitpython.org/downloads).**

You need to download the CircuitPython Library Bundle that matches your version of CircuitPython. **Please update CircuitPython and then [download the latest bundle](https://circuitpython.org/libraries)**.

As new versions of CircuitPython are released, Adafruit will stop providing the previous bundles as automatically created downloads on the Adafruit CircuitPython Library Bundle repo. If you must continue to use an earlier version, you can still download the appropriate version of `mpy-cross` from the particular release of CircuitPython on the CircuitPython repo and create your own compatible .mpy library files. **However, it is best to update to the latest for both CircuitPython and the library bundle.**

## I have to continue using CircuitPython 7.x or earlier. Where can I find compatible libraries?

**Adafruit is no longer building or supporting the CircuitPython 7.x or earlier library bundles. You are highly encourged to [update CircuitPython to the latest version](https://circuitpython.org/downloads) and use [the current version of the libraries](https://circuitpython.org/libraries).** However, if for some reason you cannot update, links to the previous bundles are available in the [FAQ](https://learn.adafruit.com/welcome-to-circuitpython/frequently-asked-questions#faq-3105289).

# macOS Sonoma before 14.4: Errors Writing to CIRCUITPY macOS 14.4 - 15.1: Slow Writes to CIRCUITPY

macOS Sonoma before 14.4 took many seconds to complete writes to small FAT drives, 8MB or smaller. This causes errors when writing to CIRCUITPY. The best solution was to remount the CIRCUITPY drive after it is automatically mounted. Or consider downgrading back to Ventura if that works for you. This problem was tracked in [CircuitPython GitHub issue 8449](https://github.com/adafruit/circuitpython/issues/8449).

Below is a shell script to do this remount conveniently (courtesy [@czei in GitHub](https://github.com/adafruit/circuitpython/issues/8449#issuecomment-1779981373)). Copy the code here into a file named, say, **remount-CIRCUITPY.sh**. Place the file in a directory on your PATH, or in some other convenient place.

macOS Sonoma 14.4 and versions of macOS before Sequoia 15.2 did not have the problem above, but did&nbsp; take an inordinately long time to write to FAT drives of size 1GB or less (40 times longer than 2GB drives). As of macOS 15.2, writes are no longer very slow. This problem was tracked in&nbsp;[CircuitPython GitHub issue 8918](https://github.com/adafruit/circuitpython/issues/8918).

```auto
#!/bin/sh
#
# This works around bug where, by default,
# macOS 14.x before 14.4 writes part of a file immediately,
# and then doesn't update the directory for 20-60 seconds, causing
# the file system to be corrupted.
#

disky=`df | grep CIRCUITPY | cut -d" " -f1`
sudo umount /Volumes/CIRCUITPY
sudo mkdir /Volumes/CIRCUITPY
sleep 2
sudo mount -v -o noasync -t msdos $disky /Volumes/CIRCUITPY
```

Then in a Terminal window, do this to make this script executable:

```terminal
chmod +x remount-CIRCUITPY.sh
```

Place the file in a directory on your `PATH`, or in some other convenient place.

Now, each time you plug in or reset your CIRCUITPY board, run the file **remount-CIRCUITPY.sh**. You can run it in a Terminal window or you may be able to place it on the desktop or in your dock to run it just by double-clicking.

This will be something of a nuisance but it is the safest solution.

This problem is being tracked in [this CircuitPython issue](https://github.com/adafruit/circuitpython/issues/8449).

# Bootloader (_boardname_BOOT) Drive Not Present

### **You may have a different board.**

Only Adafruit Express boards and the SAMD21 non-Express boards ship with the [UF2 bootloader](../../../adafruit-feather-m0-express-designed-for-circuit-python-circuitpython/uf2-bootloader?view=all#uf2-bootloader)installed. The Feather M0 Basic, Feather M0 Adalogger, and similar boards use a regular Arduino-compatible bootloader, which does not show a **_boardname_BOOT** drive.

### **MakeCode**

If you are running a [MakeCode](../../../makecode/sharing-and-saving?view=all#step-1-bootloader-mode) program on Circuit Playground Express, press the reset button just once&nbsp;to get the **CPLAYBOOT** drive to show up. Pressing it twice will not work.

### **macOS**

**DriveDx** and its accompanything **SAT SMART Driver** can interfere with seeing the BOOT drive. [See this forum post](https://forums.adafruit.com/viewtopic.php?f=58&t=161917&p=799309#p799215) for how to fix the problem.

### **Windows 10 or later**

Did you install the Adafruit Windows Drivers package by mistake, or did you upgrade to Windows 10 or later with the driver package installed? You don't need to install this package on Windows 10 or 11for most Adafruit boards. The old version (v1.5) can interfere with recognizing your device. Go to **Settings** -\> **Apps** and uninstall all the "Adafruit" driver programs.

### **Windows 7 or 8.1**

Windows 7 and 8.1 have reached end of life. It is&nbsp;[recommended](https://learn.adafruit.com/welcome-to-circuitpython/installing-circuitpython#windows-7-and-8-dot-1-drivers-2977910-9) that you upgrade to Windows 10 or 11 if possible. Drivers are available for some older CircuitPython boards, but there are no plans to release drivers for newer boards.

Info: The Windows Drivers installer was last updated in November 2020 (v2.5.0.0) . Windows 7 drivers for CircuitPython boards released since then, including RP2040 boards, are not available. There are no plans to release drivers for newer boards. The boards work fine on Windows 10 and later.

You should now be done! Test by unplugging and replugging the board. You should see the **CIRCUITPY** drive, and when you double-click the reset button (single click on Circuit Playground Express running MakeCode), you should see the appropriate&nbsp; **_boardname_BOOT** drive.

Let us know in the [Adafruit support forums](https://forums.adafruit.com) or on the [Adafruit Discord](https://adafru.it/discord) if this does not work for you!

# Windows Explorer Locks Up When Accessing **_boardname_BOOT** Drive

On Windows, several third-party programs that can cause issues. The symptom is that you try to access the **_boardname_BOOT** &nbsp;drive, and Windows or Windows Explorer seems to lock up. These programs are known to cause trouble:

- **AIDA64** : to fix, stop the program. This problem has been reported to AIDA64. They acquired hardware to test, and released a beta version that fixes the problem. This may have been incorporated into the latest release. Please let us know in the forums if you test this.
- **BitDefender anti-virus**
- **Hard Disk Sentinel**
- **Kaspersky anti-virus** : To fix, you may need to disable Kaspersky completely. Disabling some aspects of Kaspersky does not always solve the problem. This problem has been reported to Kaspersky.
- **ESET NOD32 anti-virus** : There have been problems with at least version 9.0.386.0, solved by uninstallation.

# Copying UF2 to **_boardname_BOOT** Drive Hangs at 0% Copied

On Windows, a **Western DIgital (WD) utility** that comes with their external USB drives can interfere with copying UF2 files to the **_boardname_BOOT** drive. Uninstall that utility to fix the problem.

# CIRCUITPY Drive Does Not Appear or Disappears Quickly

**BitDefender anti-virus has been reported to block access to**  **CIRCUITPY**. You can set an exception for the drive letter.

Kaspersky&nbsp; **anti-virus** can block the appearance of the **CIRCUITPY** drive. There has not yet been settings change discovered that prevents this. Complete uninstallation of Kaspersky fixes the problem.

**Norton anti-virus** can interfere with **CIRCUITPY**. A user has reported this problem on Windows 7. The user turned off both Smart Firewall and Auto Protect, and **CIRCUITPY** then appeared.

**Sophos Endpoint** security software [can cause CIRCUITPY to disappear](https://forums.adafruit.com/viewtopic.php?f=60&t=187629) and the BOOT drive to reappear. It is not clear what causes this behavior.

**Samsung Magician** can cause CIRCUITPY to disappear (reported [here](https://forums.adafruit.com/viewtopic.php?t=205159) and [here](https://forums.adafruit.com/viewtopic.php?p=987596#p987596)).

## "M105" Seen on Display, Crashes, Missing CIRCUITPY

The **Cura** 3D printing program sends 3D printing GCODE commands to unused serial ports to try to find 3D printers connected over serial. This causes a variety of problems. Disable (uncheck) **USB Printing** in Cura in the **Market** -\> **Installed** menu, or uninstall Cura. For more information see [this forum post](https://forums.adafruit.com/viewtopic.php?t=192784), [this CircuitPython issue](https://github.com/adafruit/circuitpython/issues/2585), and [this Cura issue](https://github.com/Ultimaker/Cura/issues/5207).

# Device Errors or Problems on Windows

Windows can become confused about USB device installations.&nbsp;Try cleaning up your USB devices. Use [Uwe Sieber's Device Cleanup Tool](https://www.uwe-sieber.de/misc_tools_e.html)&nbsp;(on that page, scroll down to "Device Cleanup Tool"). Download and unzip the tool. Unplug all the boards and other USB devices you want to clean up. Run the tool as Administrator. You will see a listing like this, probably with many more devices. It is listing all the USB devices that are _not_ currently attached.

![](https://cdn-learn.adafruit.com/assets/assets/000/104/840/medium800/circuitpython_device-cleanup-tool.png?1632502330)

Select all the devices you want to remove, and then press Delete. It is usually safe just to select everything. Any device that is removed will get a fresh install when you plug it in. Using the Device Cleanup Tool also discards all the COM port assignments for the unplugged boards. If you have used many Arduino and CircuitPython boards, you have probably seen higher and higher COM port numbers used, seemingly without end. This will fix that problem.

# Serial Console in Mu Not Displaying Anything

There are times when the serial console will accurately not display anything, such as, when no code is currently running, or when code with no serial output is already running before you open the console. However, if you find yourself in a situation where you feel it should be displaying something like an error, consider the following.

Depending on the size of your screen or Mu window, when you open the serial console, the serial console panel may be very small. This can be a problem. A basic CircuitPython error takes 10 lines to display!

```
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
code.py output:
Traceback (most recent call last):
  File "code.py", line 7
SyntaxError: invalid syntax



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

More complex errors take even more lines!

Therefore, if your serial console panel is five lines tall or less, you may only see blank lines or blank lines followed by `Press any key to enter the REPL. Use CTRL-D to reload.`. If this is the case, you need to either mouse over the top of the panel to utilise the option to resize the serial panel, or use the scrollbar on the right side to scroll up and find your message.

![](https://cdn-learn.adafruit.com/assets/assets/000/065/358/medium800/circuitpython_Mu_Serial_Short.png?1541708969)

This applies to any kind of serial output whether it be error messages or print statements. So before you start trying to debug your problem on the hardware side, be sure to check that you haven't simply missed the serial messages due to serial output panel height.

# code.py Restarts Constantly

CircuitPython will restart **code.py** if you or your computer writes to something on the CIRCUITPY drive. This feature is called _auto-reload_, and lets you test a change to your program immediately.

Some utility programs, such as backup, anti-virus, or disk-checking apps, will write to the CIRCUITPY as part of their operation. Sometimes they do this very frequently, causing constant restarts.

**Acronis True Image** and related Acronis programs on Windows are known to cause this problem. It is possible to prevent this by [disabling the "](https://forum.acronis.com/forum/acronis-true-image-2018-forum/acronis-ati-2018-contantly-touch-usb-port-causing-issue-adafruit-circuitpython-folder)[Acronis Managed Machine Service Mini"](https://forum.acronis.com/forum/acronis-true-image-2018-forum/acronis-ati-2018-contantly-touch-usb-port-causing-issue-adafruit-circuitpython-folder).

If you cannot stop whatever is causing the writes, you can disable auto-reload by putting this code in **boot.py** or **code.py** :

```python
import supervisor

supervisor.runtime.autoreload = False
```

# CircuitPython RGB Status Light

Nearly all CircuitPython-capable boards have a single NeoPixel or DotStar RGB LED on the board that indicates the status of CircuitPython. A few boards designed before CircuitPython existed, such as the Feather M0 Basic, do not.

**Circuit Playground Express and Circuit Playground Bluefruit have multiple RGB LEDs, but do NOT have a status LED. The LEDs are all green when in the bootloader. In versions before 7.0.0, they do NOT indicate any status while running CircuitPython.**

## **CircuitPython 7.0.0 and Later**

The status LED blinks were changed in CircuitPython 7.0.0 in order to save battery power and simplify the blinks. These blink patterns will occur on single color LEDs when the board does not have any RGB LEDs. Speed and blink count also vary for this reason.

On start up, the LED will blink **YELLOW** multiple times for 1 second. Pressing the RESET button (or on Espressif, the BOOT button) during this time will restart the board and then enter safe mode. On Bluetooth capable boards, after the yellow blinks, there will be a set of faster blue blinks. Pressing reset during the **BLUE** blinks will clear Bluetooth information and start the device in discoverable mode, so it can be used with a BLE code editor.

Once started, CircuitPython will blink a pattern every 5 seconds when no user code is running to indicate why the code stopped:

- 1 **GREEN** blink: Code finished without error.
- 2 **RED** blinks:&nbsp;Code ended due to an exception. Check the serial console for details.
- 3 **YELLOW** blinks:&nbsp;CircuitPython is in safe mode. No user code was run. Check the serial console for safe mode reason.

When in the REPL, CircuitPython will set the status LED to **WHITE**. You can change the LED color from the REPL. The status indicator will not persist on non-NeoPixel or DotStar LEDs.

![](https://cdn-learn.adafruit.com/assets/assets/000/106/229/medium800/circuitpython_CircuitPython_Boot_Sequence_7.jpg?1636133436)

## **CircuitPython 6.3.0 and earlier**

Here's what the colors and blinking mean:

- steady **GREEN** : **code.py** (or **code.txt** , **main.py** , or **main.txt** ) is running
- pulsing **GREEN** : **code.py** (etc.) has finished or does not exist
- steady **YELLOW** at start up: (4.0.0-alpha.5 and newer) CircuitPython is waiting for a reset to indicate that it should start in safe mode
- pulsing **YELLOW** : Circuit Python is in safe mode: it crashed and restarted
- steady **WHITE** : REPL is running
- steady **BLUE** : **boot.py** is running

Colors with multiple flashes following indicate a Python&nbsp;exception and then indicate the line number of the error. The color of the first flash indicates the type of error:

- **GREEN** :&nbsp;IndentationError
- **CYAN** :&nbsp;SyntaxError
- **WHITE** : NameError
- **ORANGE** : OSError
- **PURPLE** : ValueError
- **YELLOW** : other error

These are followed by flashes indicating the line number, including place value. **WHITE** flashes are thousands' place, **BLUE** are hundreds' place, **YELLOW** are tens' place, and **CYAN** are one's place. So for example, an error on line 32 would flash **YELLOW** three times and then **CYAN** two times. Zeroes are indicated by an extra-long dark gap.

![](https://cdn-learn.adafruit.com/assets/assets/000/106/228/medium800/circuitpython_CircuitPython_Boot_Sequence_1.png?1636133360)

# Serial console showing `ValueError: Incompatible .mpy file`

This error occurs when importing a module that is stored as a **.mpy** binary file that was generated by a different version of CircuitPython than the one its being loaded into. In particular, the mpy binary format changed between CircuitPython versions 6.x and 7.x, 2.x and 3.x, and 1.x and 2.x.

So, for instance, if you upgraded to CircuitPython 7.x from 6.x you’ll need to download a newer version of the library that triggered the error on `import`. All libraries are available in the [Adafruit bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/latest).

# CIRCUITPY Drive Issues

You may find that you can no longer save files to your **CIRCUITPY** drive. You may find that your **CIRCUITPY** stops showing up in your file explorer, or shows up as **NO\_NAME**. These are indicators that your filesystem has issues. When the **CIRCUITPY** disk is not safely ejected before being reset by the button or being disconnected from USB, it may corrupt the flash drive. It can happen on Windows, Mac or Linux, though it is more common on Windows.

Be aware, if you have used Arduino to program your board, CircuitPython is no longer able to provide the USB services. You will need to reload CircuitPython to resolve this situation.

The easiest first step is to reload CircuitPython. Double-tap reset on the board so you get a **_boardname_BOOT** drive rather than a **CIRCUITPY** drive, and copy the latest version of CircuitPython ( **.uf2** ) back to the board. This may restore **CIRCUITPY** functionality.

If reloading CircuitPython does not resolve your issue, the next step is to try putting the board into safe mode.

## Safe Mode

Whether you've run into a situation where you can no longer edit your **code.py** on your **CIRCUITPY** drive, your board has gotten into a state where **CIRCUITPY** is read-only, or you have turned off the **CIRCUITPY** drive altogether, 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 in CircuitPython 7.x and Later

You can enter safe by pressing reset during the right time when the board boots. Immediately after the board starts up or resets, it waits one second. On some boards, the onboard status LED will blink yellow during that time. If you press reset during that one second period, 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.)

### Entering Safe Mode in CircuitPython 6.x

You can enter safe by pressing reset during the right time when the board boots.. Immediately after the board starts up or resets, it waits 0.7 seconds. On some boards, the onboard status LED (highlighted in green above) will turn solid yellow during this time. If you press reset during that 0.7 seconds, 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

Once you've entered safe mode successfully in CircuitPython 6.x, the LED will pulse yellow.

If you successfully enter safe mode on CircuitPython 7.x, 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._

At this point, you'll want to remove any user code in&nbsp; **code.py** and, if present, the **boot.py** file from **CIRCUITPY**. Once removed, tap the reset button, or unplug and plug in your board, to restart CircuitPython. This will restart the board and may resolve your drive issues. If resolved, you can begin coding again as usual.

If safe mode does not resolve your issue, the board must be completely erased and CircuitPython must be reloaded onto the board.

Danger: 

## To erase CIRCUITPY: `storage.erase_filesystem()`

CircuitPython includes a built-in function to erase and reformat the filesystem. If you have a version of CircuitPython older than 2.3.0 on your board, you can [update to the newest version](../../../../welcome-to-circuitpython/installing-circuitpython) to do this.

1. [Connect to the CircuitPython REPL](../../../../welcome-to-circuitpython/kattni-connecting-to-the-serial-console)&nbsp;using Mu or a terminal program.
2. Type the following into the REPL:

```terminal
&gt;&gt;&gt; import storage
&gt;&gt;&gt; storage.erase_filesystem()
```

CIRCUITPY will be erased and reformatted, and your board will restart. That's it!

## Erase CIRCUITPY Without Access to the REPL

If you can't access the REPL, or you're running a version of CircuitPython previous to 2.3.0 and you don't want to upgrade, there are options available for some specific boards.

**The options listed below are considered to be the "old way" of erasing your board. The method shown above using the REPL is highly recommended as the best method for erasing your board.**

Warning: 

## For the specific boards listed below:

If the board you are trying to erase is listed below, follow the steps to use the file to erase your board.

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.&nbsp; Download the correct erase file:

[Circuit Playground Express](https://cdn-learn.adafruit.com/assets/assets/000/048/745/original/flash_erase_express.ino.circuitplay.uf2?1512152080)
[Feather M0 Express](https://cdn-learn.adafruit.com/assets/assets/000/048/746/original/flash_erase_express.ino.feather_m0_express.uf2?1512152098)
[Feather M4 Express](https://cdn-learn.adafruit.com/assets/assets/000/076/217/original/flash_erase.ino.feather_m4.uf2)
[Metro M0 Express](https://cdn-learn.adafruit.com/assets/assets/000/048/747/original/flash_erase_express.ino.metro_m0.uf2?1512152103)
[Metro M4 Express QSPI Eraser](https://cdn-learn.adafruit.com/assets/assets/000/073/820/original/Metro_M4_QSPI_Eraser.UF2?1553805937)
[Trellis M4 Express (QSPI)](https://cdn-learn.adafruit.com/assets/assets/000/067/535/original/Trellis_M4_QSPI_Eraser.UF2?1544719380)
[Grand Central M4 Express (QSPI)](https://cdn-learn.adafruit.com/assets/assets/000/069/314/original/GC_M4_QSPI_Erase.UF2?1547404471)
[PyPortal M4 Express (QSPI)](https://cdn-learn.adafruit.com/assets/assets/000/072/252/original/PYPORTAL_QSPI_Eraser.UF2?1551738305)
[Circuit Playground Bluefruit (QSPI)](https://cdn-learn.adafruit.com/assets/assets/000/082/950/original/CP_Bluefruit_QSPI_Erase.UF2?1572026649)
[Monster M4SK (QSPI)](https://cdn-learn.adafruit.com/assets/assets/000/083/330/original/M4SK_QSPI_Eraser.UF2?1572551433)
[PyBadge/PyGamer QSPI Eraser.UF2](https://cdn-learn.adafruit.com/assets/assets/000/083/331/original/PyBadge_QSPI_Eraser.UF2?1572551613)
[CLUE_Flash_Erase.UF2](https://cdn-learn.adafruit.com/assets/assets/000/088/454/original/CLUE_Flash_Erase.UF2?1581873830)
[Matrix_Portal_M4_(QSPI).UF2](https://cdn-learn.adafruit.com/assets/assets/000/098/741/original/Matrix_Portal_M4_%28QSPI%29.UF2?1611076081)
[RP2040 boards (flash_nuke.uf2)](https://cdn-learn.adafruit.com/assets/assets/000/101/659/original/flash_nuke.uf2)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.&nbsp; Double-click the reset button on the board to bring up the **_boardname_BOOT** drive.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.&nbsp; Drag the erase **.uf2** file to the&nbsp; **_boardname_BOOT** drive.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.&nbsp; The status LED will turn yellow or blue, indicating the erase has started.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.&nbsp; After approximately 15 seconds, the status LED will light up green. On the NeoTrellis M4 this is the first NeoPixel on the grid  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.&nbsp; Double-click the reset button on the board to bring up the&nbsp; **_boardname_BOOT** &nbsp;drive.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.&nbsp; [Drag the appropriate latest release of CircuitPython](https://circuitpython.org/downloads) **.uf2** file to the&nbsp; **_boardname_BOOT** &nbsp;drive.

It should reboot automatically and you should see **CIRCUITPY** in your file explorer again.

If the LED flashes red during step 5, it means the erase has failed. Repeat the steps starting with 2.

[If you haven't already downloaded the latest release of CircuitPython for your board, check out the installation page](../../../../welcome-to-circuitpython/installing-circuitpython). You'll also need to load your code and reinstall your libraries!

## For SAMD21 non-Express boards that have a UF2 bootloader:

Any SAMD21-based microcontroller that does not have external flash available is considered a SAMD21 non-Express board. Non-Express boards that have a UF2 bootloader include Trinket M0, GEMMA M0, QT Py M0, and the SAMD21-based Trinkey boards.

If you are trying to erase a SAMD21 non-Express board, follow these steps to erase your board.

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.&nbsp; Download the erase file:

[SAMD21 non-Express Boards](https://cdn-learn.adafruit.com/assets/assets/000/048/748/original/erase_m0.uf2)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.&nbsp; Double-click the reset button on the board to bring up the **_boardname_BOOT** drive.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.&nbsp; Drag the erase **.uf2** file to the **_boardname_BOOT** drive.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.&nbsp; The boot LED will start flashing again, and the **_boardname_BOOT** drive will reappear.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.&nbsp; [Drag the appropriate latest release CircuitPython](https://circuitpython.org/downloads) **.uf2** file to the _boardname_BOOT drive.

It should reboot automatically and you should see **CIRCUITPY** in your file explorer again.

[If you haven't already downloaded the latest release of CircuitPython for your board, check out the installation page](../../../../welcome-to-circuitpython/installing-circuitpython) YYou'll also need to load your code and reinstall your libraries!

## For SAMD21 non-Express boards that do not have a UF2 bootloader:

Any SAMD21-based microcontroller that does not have external flash available is considered a SAMD21 non-Express board. Non-Express boards that do **not** have a UF2 bootloader include the Feather M0 Basic Proto, Feather Adalogger, or the Arduino Zero.

If you are trying to erase a non-Express board that does not have a UF2 bootloader, [follow these directions to&nbsp;reload CircuitPython using `bossac`](../../../../welcome-to-circuitpython/non-uf2-installation), which will erase and re-create **CIRCUITPY**.

# Running Out of File Space on SAMD21 Non-Express Boards

Any SAMD21-based microcontroller that does not have external flash available is considered a SAMD21 non-Express board. This includes boards like the Trinket M0, GEMMA M0, QT Py M0, and the SAMD21-based Trinkey boards.

The file system on the board is very tiny. (Smaller than an ancient floppy disk.) So, its likely you'll run out of space but don't panic! There are a number of ways to free up space.

![](https://cdn-learn.adafruit.com/assets/assets/000/049/465/medium800/circuitpython_arduino_compatibles_gemma_out_of_space.png?1513737132)

## Delete something!

The simplest way of freeing up space is to delete files from the drive. Perhaps there are libraries in the **lib** folder that you aren't using anymore or test code that isn't in use. Don't delete the **lib** folder completely, though, just remove what you don't need.

The board ships with the Windows 7 serial driver too! Feel free to delete that if you don't need it or have already installed it. It's ~12KiB or so.

## Use tabs

One unique feature of Python is that the indentation of code matters. Usually the recommendation is to indent code with four spaces for every indent. In general, that is recommended too. **However** , one trick to storing more human-readable code is to use a single tab character for indentation. This approach uses 1/4 of the space for indentation and can be significant when you're counting bytes.

## On macOS?

MacOS loves to generate hidden files. Luckily you can disable some of the extra hidden files that macOS adds by running a few commands to disable search indexing and create zero byte placeholders. Follow the steps below to maximize the amount of space available on macOS.

## Prevent & Remove macOS Hidden Files

First find the volume name for your board. &nbsp;With the board plugged in run this command in a terminal to list all the volumes:

```
ls -l /Volumes
```

Look for a volume with a name like&nbsp; **CIRCUITPY** &nbsp;(the default for CircuitPython). &nbsp;The full path to the volume is the&nbsp; **/Volumes/CIRCUITPY** &nbsp;path.

Now follow the&nbsp;[steps from this question](http://apple.stackexchange.com/questions/6707/how-to-stop-os-x-from-writing-spotlight-and-trash-files-to-memory-cards-and-usb/7135#7135)&nbsp;to run these terminal commands that stop hidden files from being created on the board:

```terminal
mdutil -i off /Volumes/CIRCUITPY
cd /Volumes/CIRCUITPY
rm -rf .{,_.}{fseventsd,Spotlight-V*,Trashes}
mkdir .fseventsd
touch .fseventsd/no_log .metadata_never_index .Trashes
cd -
```

Replace&nbsp; **/Volumes/CIRCUITPY** &nbsp;in the commands above with the full path to your board's volume if it's different. &nbsp;At this point all the hidden files should be cleared from the board and some hidden files will be prevented from being created.

Alternatively, with CircuitPython 4.x and above, the special files and folders mentioned above will be created automatically if you erase and reformat the filesystem. **WARNING: Save your files first!** Do this in the REPL:

`>>> import storage`  
`>>> storage.erase_filesystem()`

However there are still some cases where hidden files will be created by MacOS. &nbsp;In particular if you copy a file that was downloaded from the internet it will have special metadata that MacOS stores as a hidden file. &nbsp;Luckily you can run a copy command from the terminal to copy files **without** this hidden metadata file. &nbsp;See the steps below.

## Copy Files on macOS Without Creating Hidden Files

Once you've disabled and removed hidden files with the above commands on macOS you need to be careful to copy files to the board with a special command that prevents future hidden files from being created. &nbsp;Unfortunately you **cannot** &nbsp;use drag and drop copy in Finder because it will still create these hidden extended attribute files in some cases (for files downloaded from the internet, like Adafruit's modules).

To copy a file or folder use the&nbsp; **-X** &nbsp;option for the&nbsp; **cp** &nbsp;command in a terminal. &nbsp;For example to copy a **file\_name.mpy** file to the board use a command like:

```terminal
cp -X file_name.mpy /Volumes/CIRCUITPY
```

(Replace **file\_name.mpy** with the name of the file you want to copy.)

Or to copy a folder and all of the files and folders contained within, use a command like:

```terminal
cp -rX folder_to_copy /Volumes/CIRCUITPY
```

If you are copying to the **lib** folder, or another folder, make sure it exists before copying.

```terminal
# if lib does not exist, you'll create a file named lib !
cp -X file_name.mpy /Volumes/CIRCUITPY/lib
# This is safer, and will complain if a lib folder does not exist.
cp -X file_name.mpy /Volumes/CIRCUITPY/lib/
```

## Other macOS Space-Saving Tips

If you'd like to see the amount of space used on the drive and manually delete hidden files here's how to do so. First, move into the **Volumes/** directory with `cd /Volumes/`, and then list the amount of space used on the **CIRCUITPY** drive with the `df` command.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/974/medium800/circuitpython_WtCP_df_CIRCUITPY.png?1635456335)

That's not very much space left! The next step is to show a list of the files currently on the **CIRCUITPY** drive, _including_ the hidden files, using the `ls` command. You cannot use Finder to do this, you must do it via command line!

![](https://cdn-learn.adafruit.com/assets/assets/000/105/975/medium800/circuitpython_WtCP_ls_CIRCUITPY.png?1635456430)

There are a few of the hidden files that MacOS loves to generate, all of which begin with a **.\_** before the file name. Remove the **.\_** files using the `rm` command. You can remove them all once by running `rm CIRCUITPY/._*`. The `*` acts as a _wildcard_ to apply the command to everything that begins with **.\_** at the same time.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/976/medium800/circuitpython_WtCP_rm_dot_underscore_CIRCUITPY.png?1635456716)

Finally, you can run `df` again to see the current space used.

![](https://cdn-learn.adafruit.com/assets/assets/000/105/977/medium800/circuitpython_WtCP_df_post_delete_CIRCUITPY.png?1635456812)

Nice! You have 12Ki more than before! This space can now be used for libraries and code!

# Device Locked Up or Boot Looping

In rare cases, it may happen that something in your **code.py** or **boot.py** files causes the device to get locked up, or even go into a boot loop. A _boot loop_ occurs when the board reboots repeatedly and never fully loads. These are not caused by your everyday Python exceptions, typically it's the result of a deeper problem within CircuitPython. In this situation, it can be difficult to recover your device if **CIRCUITPY** is not allowing you to modify the **code.py** or **boot.py** files. Safe mode is one recovery option. When the device boots up in safe mode it will not run the **code.py** or **boot.py** scripts, but will still connect the **CIRCUITPY** drive so that you can remove or modify those files as needed.

For more information on safe mode and how to enter safe mode, see the [Safe Mode section on this page](https://learn.adafruit.com/welcome-to-circuitpython/troubleshooting#safe-mode-3105351).

# Adafruit PyPortal - IoT for CircuitPython

## "Uninstalling" CircuitPython

A lot of our boards can be used with multiple programming languages. For example, the Circuit Playground Express can be used with MakeCode, Code.org CS Discoveries, CircuitPython and Arduino.

Maybe you tried CircuitPython and want to go back to MakeCode or Arduino? Not a problem. You can always remove or reinstall CircuitPython _whenever you want!_ Heck, you can change your mind every day!

**There is nothing to uninstall.** CircuitPython is "just another program" that is loaded onto your board. You simply load another program (Arduino or MakeCode) and it will overwrite CircuitPython.

## Backup Your Code

Before replacing CircuitPython, don't forget to make a backup of the code you have on the **CIRCUITPY** drive. That means your **code.py** any other files, the **lib** folder etc. You may lose these files when you remove CircuitPython, so backups are key! Just drag the files to a folder on your laptop or desktop computer like you would with any USB drive.

# Moving Circuit Playground Express to MakeCode

On the Circuit Playground Express ( **this currently does NOT apply to Circuit Playground Bluefruit** ), if you want to go back to using MakeCode, it's really easy. Visit [makecode.adafruit.com](https://makecode.adafruit.com) and find the program you want to upload. Click Download to download the **.uf2** file that is generated by MakeCode.

Now double-click your CircuitPython board until you see the onboard LED(s) turn green and the **...BOOT** directory shows up.

![](https://cdn-learn.adafruit.com/assets/assets/000/053/141/medium800thumb/circuitpython_3333_giffy1.jpg?1524532552)

Then find the downloaded MakeCode **.uf2** file and drag it to the **CPLAYBOOT** drive.

![](https://cdn-learn.adafruit.com/assets/assets/000/053/142/medium800thumb/circuitpython_file-transfer-win.jpg?1524532863)

Your MakeCode is now running and CircuitPython has been removed. Going forward you only have to **single click** the reset button to get to **CPLAYBOOT**. This is an idiosyncrasy of MakeCode.

# Moving to Arduino

If you want to use Arduino instead, you just use the Arduino IDE to load an Arduino program. Here's an example of uploading a simple "Blink" Arduino program, but you don't have to use this particular program.

Start by plugging in your board, and double-clicking reset until you get the green onboard LED(s).

Within Arduino IDE, select the matching board, say Circuit Playground Express.

![](https://cdn-learn.adafruit.com/assets/assets/000/053/143/medium800/circuitpython_circuit_playground_boardsel.png?1524532985)

Select the correct matching Port:

![](https://cdn-learn.adafruit.com/assets/assets/000/053/144/medium800/circuitpython_circuit_playground_cpxport.png?1524533039)

Create a new simple Blink sketch example:

```
// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
}
```

Make sure the LED(s) are still green, then click **Upload** to upload Blink. Once it has uploaded successfully, the serial Port will change so **re-select the new Port**!

Once Blink is uploaded you should no longer need to double-click to enter bootloader mode. Arduino will automatically reset when you upload.

# Adafruit PyPortal - IoT for CircuitPython

## Welcome to the Community!

![](https://cdn-learn.adafruit.com/assets/assets/000/047/545/medium800/circuitpython_adafruit_code_community_googleplus.jpg?1508879596)

CircuitPython is a programming language that's super simple to get started with and great for learning. It runs on microcontrollers and works out of the box. You can plug it in and get started with any text editor. The best part? CircuitPython comes with an amazing, supportive community.

Everyone is welcome! CircuitPython is Open Source. This means it's available for anyone to use, edit, copy and improve upon. This also means CircuitPython becomes better because of you being a part of it. Whether this is your first microcontroller board or you're a seasoned software engineer, you have something important to offer the Adafruit CircuitPython community. This page highlights some of the many ways you can be a part of it!

## Adafruit Discord
![](https://cdn-learn.adafruit.com/assets/assets/000/048/351/medium800/circuitpython_Screen_Shot_2017-11-20_at_14.54.21.png?1511207983)

The Adafruit Discord server is the best place to start. Discord is where the community comes together to volunteer and provide live support of all kinds. From general discussion to detailed problem solving, and everything in between, Discord is a digital maker space with makers from around the world.

There are many different channels so you can choose the one best suited to your needs. Each channel is shown on Discord as "#channelname". There's the #help-with-projects channel for assistance with your current project or help coming up with ideas for your next one. There's the #show-and-tell channel for showing off your newest creation. Don't be afraid to ask a question in any channel! If you're unsure, #general is a great place to start. If another channel is more likely to provide you with a better answer, someone will guide you.

The help with CircuitPython channel is where to go with your CircuitPython questions. #help-with-circuitpython is there for new users and developers alike so feel free to ask a question or post a comment! Everyone of any experience level is welcome to join in on the conversation. Your contributions are important! The #circuitpython-dev channel is available for development discussions as well.

The easiest way to contribute to the community is to assist others on Discord. Supporting others doesn't always mean answering questions. Join in celebrating successes! Celebrate your mistakes! Sometimes just hearing that someone else has gone through a similar struggle can be enough to keep a maker moving forward.

The Adafruit Discord is the 24x7x365 hackerspace that you can bring your granddaughter to.

Visit [https://adafru.it/discord](https://adafru.it/discord)to sign up for Discord. Everyone is looking forward to meeting you!

## CircuitPython.org
![](https://cdn-learn.adafruit.com/assets/assets/000/106/064/medium800/WtCP_WttC_circuitpython_org.png?1635809763)

Beyond the Adafruit Learn System, which you are viewing right now, the best place to find information about CircuitPython is [circuitpython.org](https://circuitpython.org). Everything you need to get started with your new microcontroller and beyond is available. You can do things like [download CircuitPython for your microcontroller](https://circuitpython.org/downloads) or [download the latest CircuitPython Library bundle](https://circuitpython.org/libraries), or check out [which single board computers support Blinka](https://circuitpython.org/blinka). You can also get to various other CircuitPython related things like Awesome CircuitPython or the Python for Microcontrollers newsletter. This is all incredibly useful, but it isn't necessarily community related. So why is it included here? The [Contributing page](https://circuitpython.org/contributing).

![](https://cdn-learn.adafruit.com/assets/assets/000/106/067/medium800/circuitpython_WtCP_WttC_contributing.png?1635812628)

CircuitPython itself is written in C. However, all of the Adafruit CircuitPython libraries are written in _Python_. If you're interested in contributing to CircuitPython on the Python side of things, check out [circuitpython.org/contributing](https://circuitpython.org/contributing). You'll find information pertaining to every Adafruit CircuitPython library GitHub repository, giving you the opportunity to join the community by finding a contributing option that works for you.

Note the date on the page next to **Current Status for** :

![](https://cdn-learn.adafruit.com/assets/assets/000/106/082/medium800/circuitpython_WtCP_WttC_contributing_date.png?1635883999)

If you submit any contributions to the libraries, and do not see them reflected on the Contributing page, it could be that the job that checks for new updates hasn't yet run for today. Simply check back tomorrow!

Now, a look at the different options.

### Pull Requests

The first tab you'll find is a list of **open pull requests**.

![](https://cdn-learn.adafruit.com/assets/assets/000/106/065/medium800/circuitpython_WtCP_WttC_open_PRs.png?1635812588)

GitHub pull requests, or PRs, are opened when folks have added something to an Adafruit CircuitPython library GitHub repo, and are asking for Adafruit to add, or merge, their changes into the main library code. For PRs to be merged, they must first be reviewed. Reviewing is a great way to contribute! Take a look at the list of open pull requests, and pick one that interests you. If you have the hardware, you can test code changes. If you don't, you can still check the code updates for syntax. In the case of documentation updates, you can verify the information, or check it for spelling and grammar. Once you've checked out the update, you can leave a comment letting us know that you took a look. Once you've done that for a while, and you're more comfortable with it, you can consider joining the CircuitPythonLibrarians review team. The more reviewers we have, the more authors we can support. Reviewing is a crucial part of an open source ecosystem, CircuitPython included.

### Open Issues

The second tab you'll find is a list of **open issues**.

![](https://cdn-learn.adafruit.com/assets/assets/000/106/066/medium800/circuitpython_WtCP_WttC_open_issues.png?1635812598)

GitHub issues are filed for a number of reasons, including when there is a bug in the library or example code, or when someone wants to make a feature request. Issues are a great way to find an opportunity to contribute directly to the libraries by updating code or documentation. If you're interested in contributing code or documentation, take a look at the open issues and find one that interests you.

If you're not sure where to start, you can search the issues by label. Labels are applied to issues to make the goal easier to identify at a first glance, or to indicate the difficulty level of the issue. Click on the dropdown next to "Sort by issue labels" to see the list of available labels, and click on one to choose it.

![](https://cdn-learn.adafruit.com/assets/assets/000/106/083/medium800/circuitpython_WtCP_WttC_issues_label_list.png?1635884488)

If you're new to everything, new to contributing to open source, or new to contributing to the CircuitPython project, you can choose "Good first issue". Issues with that label are well defined, with a finite scope, and are intended to be easy for someone new to figure out.

If you're looking for something a little more complicated, consider "Bug" or "Enhancement". The Bug label is applied to issues that pertain to problems or failures found in the library. The Enhancement label is applied to feature requests.

Don't let the process intimidate you. If you're new to Git and GitHub, there is [a guide](https://learn.adafruit.com/contribute-to-circuitpython-with-git-and-github) to walk you through the entire process. As well, there are always folks available on [Discord](https:adafru.it/discord) to answer questions.

### Library Infrastructure Issues

The third tab you'll find is a list of **library infrastructure issues**.

![](https://cdn-learn.adafruit.com/assets/assets/000/106/084/medium800/circuitpython_WtCP_WttC_library_infrastructure_issues.png?1635885507)

This section is generated by a script that runs checks on the libraries, and then reports back where there may be issues. It is made up of a list of subsections each containing links to the repositories that are experiencing that particular issue. This page is available mostly for internal use, but you may find some opportunities to contribute on this page. If there's an issue listed that sounds like something you could help with, mention it on Discord, or file an issue on GitHub indicating you're working to resolve that issue. Others can reply either way to let you know what the scope of it might be, and help you resolve it if necessary.

### CircuitPython Localization

The fourth tab you'll find is the **CircuitPython Localization** tab.

![](https://cdn-learn.adafruit.com/assets/assets/000/106/085/medium800/circuitpython_WtCP_WttC_weblate.png?1635885684)

If you speak another language, you can help translate CircuitPython! The translations apply to informational and error messages that are within the CircuitPython core. It means that folks who do not speak English have the opportunity to have these messages shown to them in their own language when using CircuitPython. This is incredibly important to provide the best experience possible for all users. CircuitPython uses Weblate to translate, which makes it much simpler to contribute translations. You will still need to know some CircuitPython-specific practices and a few basics about coding strings, but as with any CircuitPython contributions, folks are there to help.

Regardless of your skill level, or how you want to contribute to the CircuitPython project, there is an opportunity available. The [Contributing page](https://circuitpython.org/contributing) is an excellent place to start!

## Adafruit GitHub
![](https://cdn-learn.adafruit.com/assets/assets/000/048/414/medium800/circuitpython_Screen_Shot_2017-11-21_at_18.26.55.png?1511306851)

Whether you're just beginning or are life-long programmer who would like to contribute, there are ways for everyone to be a part of the CircuitPython project. The CircuitPython core is written in C. The libraries are written in Python. GitHub is the best source of ways to contribute to the [CircuitPython core](https://github.com/adafruit/circuitpython), and the [CircuitPython libraries](https://circuitpython.org/contributing/open-issues). If you need an account, visit [https://github.com/](https://github.com/)&nbsp;and sign up.

If you're new to GitHub or programming in general, there are great opportunities for you. For the CircuitPython core, head over to the CircuitPython repository on GitHub, click on "[Issues](https://github.com/adafruit/circuitpython/issues)", and you'll find a list that includes issues labeled "[good first issue](https://github.com/adafruit/circuitpython/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)". For the libraries, head over to the [Contributing page Issues list](https://circuitpython.org/contributing/open-issues), and use the drop down menu to search for "[good first issue](https://circuitpython.org/contributing/open-issues?label=good-first-issue)". These issues are things that have been identified as something that someone with any level of experience can help with. These issues include options like updating documentation, providing feedback, and fixing simple bugs. If you need help getting started with GitHub, there is an excellent guide on [Contributing to CircuitPython with Git and GitHub](https://learn.adafruit.com/contribute-to-circuitpython-with-git-and-github).

![](https://cdn-learn.adafruit.com/assets/assets/000/048/163/medium800/circuitpython_Screen_Shot_2017-11-14_at_15.42.01.png?1510692193)

Already experienced and looking for a challenge? Checkout the rest of either issues list and you'll find plenty of ways to contribute. You'll find all sorts of things, from new driver requests, to library bugs, to core module updates. There's plenty of opportunities for everyone at any level!

When working with or using CircuitPython or the CircuitPython libraries, you may find problems. If you find a bug, that's great! The team loves bugs! Posting a detailed issue to GitHub is an invaluable way to contribute to improving CircuitPython. For CircuitPython itself, file an issue [here](https://github.com/adafruit/circuitpython/issues). For the libraries, file an issue on the specific library repository on GitHub. Be sure to include the steps to replicate the issue as well as any other information you think is relevant. The more detail, the better!

Testing new software is easy and incredibly helpful. Simply load the newest version of CircuitPython or a library onto your CircuitPython hardware, and use it. Let us know about any problems you find by posting a new issue to GitHub. Software testing on both stable and unstable releases is a very important part of contributing CircuitPython. The developers can't possibly find all the problems themselves! They need your help to make CircuitPython even better.

On GitHub, you can submit feature requests, provide feedback, report problems and much more. If you have questions, remember that Discord and the Forums are both there for help!

## Adafruit Forums
![](https://cdn-learn.adafruit.com/assets/assets/000/047/713/medium800/circuitpython_AdafruitForumsHeader.png?1509241590)

The [Adafruit Forums](https://forums.adafruit.com) are the perfect place for support. Adafruit has wonderful paid support folks to answer any questions you may have. Whether your hardware is giving you issues or your code doesn't seem to be working, the forums are always there for you to ask. You need an Adafruit account to post to the forums. You can use the same account you use to order from Adafruit.

While Discord may provide you with quicker responses than the forums, the forums are a more reliable source of information. If you want to be certain you're getting an Adafruit-supported answer, the forums are the best place to be.

There are forum categories that cover all kinds of topics, including everything Adafruit. The [Adafruit CircuitPython](https://forums.adafruit.com/viewforum.php?f=60)&nbsp;category under "Supported Products & Projects" is the best place to post your CircuitPython questions.

![](https://cdn-learn.adafruit.com/assets/assets/000/107/611/medium800/circuitpython-forum.png?1640877673)

Be sure to include the steps you took to get to where you are. If it involves wiring, post a picture! If your code is giving you trouble, include your code in your post! These are great ways to make sure that there's enough information to help you with your issue.

You might think you're just getting started, but you definitely know something that someone else doesn't. The great thing about the forums is that you can help others too! Everyone is welcome and encouraged to provide constructive feedback to any of the posted questions. This is an excellent way to contribute to the community and share your knowledge!

## Read the Docs
![](https://cdn-learn.adafruit.com/assets/assets/000/047/897/medium800/circuitpython_Screen_Shot_2017-11-02_at_22.53.06.png?1509677641)

[Read the Docs](https://circuitpython.readthedocs.io/) is a an excellent resource for a more detailed look at the CircuitPython core and the CircuitPython libraries. This is where you'll find things like API documentation and example code. For an in depth look at viewing and understanding Read the Docs, check out the [CircuitPython Documentation](https://learn.adafruit.com/welcome-to-circuitpython/circuitpython-documentation) page!

![](https://cdn-learn.adafruit.com/assets/assets/000/106/232/medium800/circuitpython_WtCP_RTD_blinky.png?1636400127)

# Adafruit PyPortal - IoT for CircuitPython

## PyPortal CircuitPython Setup

To use all the amazing features of your PyPortal with CircuitPython, you must first install a number of libraries. This page covers that process.

# Adafruit CircuitPython Bundle

Download the Adafruit CircuitPython Library Bundle. You can find the latest release here:

[Latest Adafruit CircuitPython Library Bundle](https://circuitpython.org/libraries)
Download the **adafruit-circuitpython-bundle-\*.x-mpy-\*.zip** bundle zip file where **\*.x MATCHES THE VERSION OF CIRCUITPYTHON YOU INSTALLED** , and unzip a folder of the same name. Inside you'll find a **lib** folder. You have two options:

- You can add the **lib** folder to your **CIRCUITPY** drive. This will ensure you have _all the drivers_. But it will take a bunch of space on the 8 MB disk  
- Add each library as you need it, this will reduce the space usage but you'll need to put in a little more effort.

At a minimum we recommend the following libraries, in fact we more than recommend. They're basically required. So grab them and install them into **CIRCUITPY/lib** now!

- **adafruit\_esp32spi** - This is the library that gives you internet access via the ESP32 using (you guessed it!) SPI transport. You need this for anything Internet
- **adafruit\_requests** - This library allows us to perform HTTP requests and get responses back from servers. GET/POST/PUT/PATCH - they're all in here!
- adafruit\_connection\_manager - used by adafruit\_requests.
- **adafruit\_pyportal** - This is our friendly wrapper library that does a lot of our projects, displays graphics and text, fetches data from the internet. Nearly all of our projects depend on it!
- **adafruit\_portalbase** &nbsp;- This library is the base library that adafruit\_pyportal library is built on top of.
- **adafruit\_touchscreen** - a library for reading touches from the resistive touchscreen. Handles all the analog noodling, rotation and calibration for you.
- **adafruit\_io** - this library helps connect the PyPortal to our free datalogging and viewing service
- **adafruit\_imageload** - an image display helper, required for any graphics!
- **adafruit\_display\_text** - not surprisingly, it displays text on the screen
- **adafruit\_bitmap\_font** - we have fancy font support, and its easy to make new fonts. This library reads and parses font files.
- **adafruit\_slideshow** - for making image slideshows - handy for quick display of graphics and sound
- **neopixel** - for controlling the onboard neopixel
- **adafruit\_adt7410** - library to read the temperature from the on-board Analog Devices ADT7410 precision temperature sensor (not necessary for Titano or Pynt)
- **adafruit\_bus\_device** - low level support for I2C/SPI
- **adafruit\_fakerequests** &nbsp;- This library allows you to create fake HTTP requests by using local files.

# Adafruit PyPortal - IoT 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.

# Adafruit PyPortal - IoT for CircuitPython

## Internet Connect!

# Connect to WiFi

OK, now that you have your&nbsp; **settings.toml** file set up - you can connect to the Internet.

To do this, you need to first install a few libraries, into the lib folder on your **CIRCUITPY** drive. Then you need to update **code.py** with the example script.

Thankfully, we can do this in one go. In the example below, click the **Download Project Bundle** button below to download the necessary libraries and the **code.py** file in a zip file. Extract the contents of the zip file, open the directory **examples/** and then click on the directory that matches the version of CircuitPython you're using and copy the contents of that directory to your **CIRCUITPY** drive.

Your **CIRCUITPY** drive should now look similar to the following image:

![CIRCUITPY](https://adafruit.github.io/Adafruit_CircuitPython_Bundle/esp32spi_esp32spi_simpletest.py.png )

Info: Update to CircuitPython 9.2.x or later to use this example.

https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/blob/main/examples/esp32spi_simpletest.py

And save it to your board, with the name **code.py**.

Don't forget you'll also need to create the **settings.toml** file as seen above, with your WiFi ssid and password.

In a serial console, you should see something like the following. For more information about connecting with a serial console, view the guide [Connecting to the Serial Console](https://learn.adafruit.com/welcome-to-circuitpython/kattni-connecting-to-the-serial-console).

```terminal
&gt;&gt;&gt; import wifitest
ESP32 SPI webclient test
ESP32 found and in idle mode
Firmware vers. 1.7.5
MAC addr: 24:C9:DC:BD:0F:3F
	HomeNetwork             RSSI: -46
	HomeNetwork             RSSI: -76
	Fios-12345              RSSI: -92
	FiOS-AB123              RSSI: -92
	NETGEAR53               RSSI: -93
Connecting to AP...
Connected to HomeNetwork 	RSSI: -45
My IP address is 192.168.1.245
IP lookup adafruit.com: 104.20.39.240
Ping google.com: 30 ms
Fetching text from http://wifitest.adafruit.com/testwifi/index.html
----------------------------------------
This is a test of Adafruit WiFi!
If you can read this, its working :)
----------------------------------------

Fetching json from http://wifitest.adafruit.com/testwifi/sample.json
----------------------------------------
{'fun': True, 'company': 'Adafruit', 'founded': 2005, 'primes': [2, 3, 5], 'pi': 3.14, 'mixed': [False, None, 3, True, 2.7, 'cheese']}
----------------------------------------
Done!
```

Going over the example above, here's a breakdown of what the program is doing:

- Initialize the ESP32 over SPI using the SPI port and 3 control pins:

```python
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)

#...

else:
    spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
```

- Get the socket pool and the SSL context, and then tell the `adafruit_requests` library about them.

```python
pool = adafruit_connection_manager.get_radio_socketpool(esp)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
requests = adafruit_requests.Session(pool, ssl_context)
```

- Verify an ESP32 is found, checks the firmware and MAC address

```auto
if esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
    print("ESP32 found and in idle mode")
print("Firmware vers.", esp.firmware_version)
print("MAC addr:", ":".join("%02X" % byte for byte in esp.MAC_address))
```

- Perform a scan of all access points it can see and print out the name and signal strength.

```python
for ap in esp.scan_networks():
    print("\t%-23s RSSI: %d" % (ap.ssid, ap.rssi))
```

- Connect to the AP we've defined here, then print out the local IP address. Then attempt to do a domain name lookup and ping google.com to check network connectivity. (Note sometimes the ping fails or takes a while; this isn't a big deal.)

```python
print("Connecting to AP...")
while not esp.is_connected:
    try:
        esp.connect_AP(ssid, password)
    except OSError as e:
        print("could not connect to AP, retrying: ", e)
        continue
print("Connected to", esp.ap_info.ssid, "\tRSSI:", esp.ap_info.rssi)
print("My IP address is", esp.ipv4_address)
print(
    "IP lookup adafruit.com: %s" % esp.pretty_ip(esp.get_host_by_name("adafruit.com"))
)
```

Now we're getting to the really interesting part of the example program. We've written a library for web fetching web data, named [adafruit\_requests](https://github.com/adafruit/Adafruit_CircuitPython_Requests). It is a lot like the regular Python library named [requests](https://requests.readthedocs.io/en/latest/). This library allows you to send HTTP and HTTPS requests easily and provides helpful methods for parsing the response from the server.

- Here is the part of the example program is fetching text data from a URL.

```python
TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html"  # Further up in the program

# ...

print("Fetching text from", TEXT_URL)
r = requests.get(TEXT_URL)
print('-' * 40)
print(r.text)
print('-' * 40)
r.close()
```

- Finally, here the program is fetching some JSON data. The `adafruit_requests` library will parse the JSON into a Python dictionary whose structure is the same as the structure of the JSON.

```auto
JSON_URL = "http://wifitest.adafruit.com/testwifi/sample.json"   # Further up in the program

# ...

print("Fetching json from", JSON_URL)
r = requests.get(JSON_URL)
print('-' * 40)
print(r.json())
print('-' * 40)
r.close()
```

# Advanced Requests Usage

Want to send custom HTTP headers, parse the response as raw bytes, or handle a response's http status code in your CircuitPython code?

We've written an&nbsp;example to show advanced usage of the requests module below.

To use with CircuitPython, you need to first install a few libraries, into the lib folder on your **CIRCUITPY** drive. Then you need to update **code.py** with the example script.

Thankfully, we can do this in one go. In the example below, click the **Download Project Bundle** button below to download the necessary libraries and the **code.py** file in a zip file. Extract the contents of the zip file, open the directory **examples/** and then click on the directory that matches the version of CircuitPython you're using and copy the contents of that directory to your **CIRCUITPY** drive.

https://github.com/adafruit/Adafruit_CircuitPython_Requests/blob/main/examples/esp32spi/requests_esp32spi_advanced.py

Your **CIRCUITPY** drive should now look similar to the following image:

![CIRCUITPY](https://adafruit.github.io/Adafruit_CircuitPython_Bundle/requests_esp32spi_requests_esp32spi_advanced.py.png )

# WiFi Manager

The way the examples above connect to WiFi works but it's a little finicky. Since WiFi is not necessarily so reliable, you may have disconnects and need to reconnect. For more advanced uses, we recommend using the `WiFiManager` class. It will wrap the connection/status/requests loop for you - reconnecting if WiFi drops, resetting the ESP32 if it gets into a bad state, etc.

Here's a more advanced example that shows using the `WiFiManager` and also how to fetch the current time from a web source.

https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/blob/main/examples/esp32spi_localtime.py

# Further Information

For more information on the basics of doing networking in CircuitPython, see this guide:

### Networking in CircuitPython

[Networking in CircuitPython](https://learn.adafruit.com/networking-in-circuitpython)
# Adafruit PyPortal - IoT for CircuitPython

## CircuitPython BLE

## CircuitPython BLE UART Example

It's easy to use Adafruit AirLift ESP32 co-processor boards for Bluetooth Low Energy (BLE) with CircuitPython. When you reset the ESP32, you can put it in WiFi mode (the default), or in BLE mode; you cannot use both modes simultaneously.

Here's a simple example of using BLE to connect CircuitPython with the Bluefruit Connect app. Use CircuitPython 6.0.0 or later.

**Note:** Don't confuse the **ESP32** with the **ESP32-S2** , which is a different module with a similar name. The ESP32-S2 does not support BLE.

Warning: Currently, AirLift BLE support is not currently available on boards with Espressif chips. If the Espressif board provides `_bleio`, it is for native BLE support (e.g. ESP32-S3), not AirLift.

Info: 

## On-Board Airlift Co-Processor - No Wiring Needed

If you have an **Adafruit Metro M4 AirLift Lite** , an **Adafruit PyPortal** (regular, **Pynt** or **Titano** ), an **Adafruit MatrixPortal** , or other Adafruit board with an onboard ESP32 co-processor, then everything is prewired for you, and the pins you need to use are predefined in CircuitPython.

## Update the AirLift Firmware

You will need to update the AirLift's firmware to at least version 1.7.1. **Previous versions of the AirLift firmware do not support BLE.&nbsp;**

Follow the instructions in the guide below, and come back to this page when you've upgraded the AirLift's firmware:

[Upgrade ESP32 AirLift Firmware](https://learn.adafruit.com/upgrading-esp32-firmware/upgrade-an-airlift-all-in-one-board)
Warning: 

## Install CircuitPython Libraries

First make sure you are running the [latest version of Adafruit CircuitPython](https://learn.adafruit.com/welcome-to-circuitpython/installing-circuitpython) for your board.

Next you'll need to install the necessary libraries to use the hardware. Thankfully, we can do this in one go. In the example below, click the **Download Project Bundle** button below to download the necessary libraries and the **code.py** file in a zip file. Extract the contents of the zip file, and copy the **entire lib folder** and the **code.py** file to your **CIRCUITPY** drive.

Your **CIRCUITPY/lib** folder should contain the following folders and files:

- **/adafruit\_airlift**
- **/adafruit\_ble**
- **/adafruit\_bus\_device**
- **/adafruit\_esp32spi**
- **adafruit\_requests.mpy**

![CIRCUITPY](https://adafruit.github.io/Adafruit_Learning_System_Guides/CircuitPython_Templates_airlift_ble_example.png )

## Install the Adafruit Bluefruit LE Connect App

The Adafruit Bluefruit LE Connect iOS and Android apps allow you to connect to BLE peripherals that provide a over-the-air "UART" service. Follow the instructions in the [Bluefruit LE Connect Guide](https://learn.adafruit.com/bluefruit-le-connect/) to download and install the app on your phone or tablet.

## BLE Example
Warning: 

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

## Talk to the AirLift via the Bluefruit LE Connect App

Start the Bluefruit LE Connect App on your phone or tablet. You should see a CIRCUITPY device available to connect to. Tap the Connect button (1):

![](https://cdn-learn.adafruit.com/assets/assets/000/096/566/medium800/adafruit_products_airlift-connect.png?1603914360)

You'll then see a list of Bluefruit Connect functions ("modules"). Choose the UART module (2):

![](https://cdn-learn.adafruit.com/assets/assets/000/096/567/medium800/adafruit_products_airlift-uart.png?1603914460)

On the UART module page, you can type a string and press Send (3). You'll see that string entered, and then see it echoed back (echoing is in gray).

![](https://cdn-learn.adafruit.com/assets/assets/000/096/569/medium800/adafruit_products_airlift-echo.png?1603914605)

# Adafruit PyPortal - IoT for CircuitPython

## Arduino IDE Setup

The first thing you will need to do is to download the latest release of the Arduino IDE. You will need to&nbsp;be using **version 1.8** or higher for this guide

[Arduino IDE Download](http://www.arduino.cc/en/Main/Software)
After you have downloaded and installed **the latest version of Arduino IDE** , you will need to start the IDE&nbsp;and navigate to&nbsp;the **Preferences** menu. You can access it from the **File** menu in _Windows_ or _Linux_, or the **Arduino** menu on _OS X_.

![](https://cdn-learn.adafruit.com/assets/assets/000/041/549/medium800/flora_prefs.png?1494204209)

A dialog will pop up just like the one shown below.

![](https://cdn-learn.adafruit.com/assets/assets/000/108/455/medium800/trinket_arduino-preferences-before.png?1643387570)

We will be adding a URL to the new **Additional Boards Manager URLs** option. The list of URLs is comma separated, and _you will only have to add each&nbsp;URL once._&nbsp;New Adafruit boards and updates to existing boards&nbsp;will automatically be picked up&nbsp;by the Board Manager each time&nbsp;it is opened. The URLs point to index files that the Board Manager uses to build the list of available & installed boards.

To find the most up to date list of URLs you can&nbsp;add, you can visit the list of [third party board URLs on the Arduino IDE wiki](https://github.com/arduino/Arduino/wiki/Unofficial-list-of-3rd-party-boards-support-urls#list-of-3rd-party-boards-support-urls). We will only need to add one URL to the IDE in this example, but _ **you can add multiple URLS by separating them with commas** _. Copy and paste the link below into the&nbsp; **Additional Boards Manager URLs** option in the Arduino IDE preferences.

### `https://adafruit.github.io/arduino-board-index/package_adafruit_index.json`
![](https://cdn-learn.adafruit.com/assets/assets/000/108/456/medium800/trinket_arduino-preferences.png?1643387643)

Here's a short description of each of the Adafruit supplied packages that will be available in the Board Manager when you add the URL:

- **Adafruit AVR Boards** - Includes support for Flora, Gemma, Feather 32u4, ItsyBitsy 32u4, Trinket, & Trinket Pro.
- **Adafruit SAMD Boards** - Includes support for Feather M0 and M4, Metro M0 and M4, ItsyBitsy M0 and M4, Circuit Playground Express, Gemma M0 and Trinket M0
- **Arduino Leonardo & Micro MIDI-USB** - This adds MIDI over USB support for the Flora, Feather 32u4, Micro and Leonardo using the [arcore project](https://github.com/rkistner/arcore).

If you have multiple boards you want to support, say ESP8266 and Adafruit, have both URLs in the text box separated by a comma (,)

Once done click **OK** to save the new preference settings. Next we will look at installing boards with the Board Manager.

Now continue to the next step to actually install the board support package!

# Adafruit PyPortal - IoT for CircuitPython

## Using with Arduino IDE

Adafruit boards that use ATSAMD21 ("M0") or ATSAMD51 ("M4") chips are easy to get working with the Arduino IDE. Most libraries (including the popular ones like NeoPixels and display) will work with those boards, especially devices & sensors that use I2C or SPI.

Now that you have added the appropriate URLs to the Arduino IDE preferences in the previous page, you can open the **Boards Manager** by navigating to the **Tools-\>Board** &nbsp;menu.

![](https://cdn-learn.adafruit.com/assets/assets/000/028/791/medium800/adafruit_products_boardmanager.png?1448652571)

Once the Board Manager opens, click on the category drop down menu on the top left hand side of the window and select **All**. You will then be able to select and install the boards supplied&nbsp;by the URLs added to the preferences.

Warning: 

# Install SAMD Support

First up, install the latest **Arduino SAMD Boards (**version **1.6.11&nbsp;** or later)

You can type **Arduino SAMD** in the top search bar, then when you see the entry, click **Install**

![](https://cdn-learn.adafruit.com/assets/assets/000/028/792/medium800/adafruit_products_arduinosamd162.png?1448652786)

# Install Adafruit SAMD

Next you can install the Adafruit SAMD package to add the board file definitions

Make sure you have **Type All** selected to the left of the _Filter your search..._ box

You can type **Adafruit SAMD** in the top search bar, then when you see the entry, click **Install**

![](https://cdn-learn.adafruit.com/assets/assets/000/028/794/medium800/adafruit_products_adafruitsamd.png?1448652973)

 **Quit and reopen&nbsp;the Arduino IDE** to ensure that all of the boards are properly installed. You should now be able to select and upload to the new boards listed in the **Tools-\>Board** menu.

Select the matching board, the current options are:

- **Feather M0** (for use with any Feather M0 other than the Express)
- **Feather M0 Express**
- **Metro M0 Express**
- **Circuit Playground Express**  
- **Gemma M0**
- **Trinket M0**  
- **QT Py M0**  
- **ItsyBitsy M0**
- **Hallowing M0**
- **Crickit M0** (this is for direct programming of the Crickit, which is probably not what you want! For advanced hacking only)
- **Metro M4 Express**
- **Grand Central M4 Express**
- **ItsyBitsy M4 Express**
- **Feather M4 Express**
- **Trellis M4 Express**
- **PyPortal M4**
- **PyPortal M4 Titano**
- **PyBadge M4 Express**
- **Metro M4 Airlift Lite**
- **PyGamer M4 Express**
- **MONSTER M4SK**
- **Hallowing M4**
- **MatrixPortal M4**
- **BLM Badge**

![](https://cdn-learn.adafruit.com/assets/assets/000/053/074/medium800/adafruit_products_availboards.png?1524019356)

## Windows 7 and 8.1
Warning: **Windows 7 and Windows 8.1** have reached end-of-life and are no longer supported. They required driver installation. A [limited set of drivers is available for older boards](https://github.com/adafruit/Adafruit_Windows_Drivers/releases), but drivers for most newer boards are not available.

# Blink

Now you can upload your first blink sketch!

Plug in the SAMD21 M0 or SAMD51 M4 board, and wait for it to be recognized by the OS (just takes a few seconds). It will create a serial/COM port, you can now select it from the drop-down, it'll even be 'indicated' as Trinket/Gemma/Metro/Feather/ItsyBitsy/QT Py/Trellis or whatever the board is named!

Info: A few boards, such as the QT Py SAMD21, Trellis M4 Express, and certain Trinkey boards, do not have an onboard pin 13 LED. You can follow this section to practice uploading but you won't see an LED blink!

![](https://cdn-learn.adafruit.com/assets/assets/000/028/796/medium800/adafruit_products_comport.png?1448653712)

Now load up the Blink example

```
// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
}
```

And click upload! That's it, you will be able to see the LED blink rate change as you adapt the **delay()** calls.

Info: 

# Successful Upload

If you have a successful upload, you'll get a bunch of red text that tells you that the device was found and it was programmed, verified & reset

![](https://cdn-learn.adafruit.com/assets/assets/000/028/797/medium800/adafruit_products_uploading.png?1448653908)

After uploading, you may see a message saying "Disk Not&nbsp;Ejected&nbsp;Properly" about the ...BOOT drive. You can ignore that message: it's an artifact of how the bootloader and uploading work.

# Compilation Issues

If you get an alert that looks like

**Cannot run program "{runtime.tools.arm-none-eabi-gcc.path}\bin\arm-non-eabi-g++"**

Make sure you have installed the **Arduino SAMD** boards package, you need _both_ Arduino & Adafruit SAMD board packages

![](https://cdn-learn.adafruit.com/assets/assets/000/028/798/medium800/adafruit_products_gppnotfound.png?1448653960)

# Manually bootloading

If you ever get in a 'weird' spot with the bootloader, or you have uploaded code that crashes and doesn't auto-reboot into the bootloader, click the **RST** button **twice** (like a double-click)to get back into the bootloader.

**The red LED will pulse and/or RGB LED will be green, so you know that its in bootloader mode.**

Once it is in bootloader mode, you can select the newly created COM/Serial port and re-try uploading.

![](https://cdn-learn.adafruit.com/assets/assets/000/028/799/medium800/adafruit_products_bootloadport.png?1448654424)

You may need to go back and reselect the 'normal' USB serial port next time you want to use the normal upload.

# Ubuntu&nbsp;& Linux Issue Fix

&nbsp;[Follow the steps for installing Adafruit's udev rules on this page.](../../../../adafruit-arduino-ide-setup/linux-setup#udev-rules)

# Adafruit PyPortal - IoT for CircuitPython

## Arduino Libraries

OK now that you have Arduino IDE set up, drivers installed if necessary and you've practiced uploading code, you can start installing all the Libraries we'll be using to program it.

There's a lot of libraries!

# Install Libraries

Open up the library manager...

![](https://cdn-learn.adafruit.com/assets/assets/000/073/288/medium800/circuitpython_managelib.png?1552873775)

And install the following libraries:

## Adafruit NeoPixel

This will let you light up the status LED on the back

![](https://cdn-learn.adafruit.com/assets/assets/000/073/289/medium800/circuitpython_adafruit_products_image.png?1552873804)

## Adafruit SPIFlash

This will let you read/write to the onboard FLASH memory with super-fast QSPI support

![](https://cdn-learn.adafruit.com/assets/assets/000/073/292/medium800/circuitpython_adafruit_products_SPIFlash_lib.jpg?1552873977)

## Adafruit Zero DMA

This is used by the Graphics Library if you choose to use DMA

![](https://cdn-learn.adafruit.com/assets/assets/000/073/291/medium800/circuitpython_adafruit_products_zeroDMA_lib.jpg?1552873858)

## Adafruit GFX

This is the graphics library used to draw to the screen

![](https://cdn-learn.adafruit.com/assets/assets/000/073/293/medium800/circuitpython_image.png?1552874131)

If using an older (pre-1.8.10) Arduino IDE, locate and install **Adafruit\_BusIO** (newer versions do this automatically when installing Adafruit\_GFX).

## Adafruit ILI9341

The display on the PyPortal!

![](https://cdn-learn.adafruit.com/assets/assets/000/073/297/medium800/circuitpython_image.png?1552874383)

## Adafruit HX8357

The display on the PyPortal Titano!

![](https://cdn-learn.adafruit.com/assets/assets/000/094/043/medium800/circuitpython_hx8357.png?1597248067)

## Adafruit Touchscreen

For reading touchscreen points on the resistive touchscreen

![](https://cdn-learn.adafruit.com/assets/assets/000/073/294/medium800/circuitpython_image.png?1552874175)

## Analog Devices ADT7410

For reading temperature data from the onboard ADT7410

![](https://cdn-learn.adafruit.com/assets/assets/000/073/295/medium800/circuitpython_image.png?1552874269)

## WiFiNINA

Will talk to the ESP32 WiFi co-processor to connect to the internet!

![](https://cdn-learn.adafruit.com/assets/assets/000/073/296/medium800/circuitpython_image.png?1552874318)

# Adafruit ImageReader

For reading bitmaps from SD and displaying

![](https://cdn-learn.adafruit.com/assets/assets/000/073/298/medium800/circuitpython_image.png?1552874434)

# Adafruit PyPortal - IoT for CircuitPython

## Arduino Test

Once you've got the IDE installed and libraries in place you can run our test sketch. This will check all the hardware, and display it on the screen, its sort of a universal test because every part is checked. It's also a great reference if you want to know how to read the light sensor or initialize the touch screen.

It's normal to get `SD Card...Failed` if there is no SD card in the socket.

The light sensor value ranges from 0 (dark) to 1023 (bright)

The temperature sensor will heat up if the backlight is on for a while, that's also normal! To avoid self-heating turn or or lower down the backlight brightness

`D3` and `D4` measure the analog voltages on the 3 pin JST connectors. They'll be floating until some voltage is applied to them.

![](https://cdn-learn.adafruit.com/assets/assets/000/073/299/medium800/circuitpython_IMG_1866.jpg?1552883307)

You can download the ready-to-go UF2 file and drag it onto the bootloader drive. To enter the bootloader, double click the reset button.

[PyPortal_Self_Test.UF2](https://cdn-learn.adafruit.com/assets/assets/000/107/658/original/PyPortal_Self_Test.UF2?1641322245)
Warning: 

[Download PyPortal Arduino Self Test Files](https://github.com/adafruit/Adafruit_Learning_System_Guides/tree/main/PyPortal/PyPortal_ArduinoSelfTest)
https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/PyPortal/PyPortal_ArduinoSelfTest/PyPortal_ArduinoSelfTest.ino

# Adafruit PyPortal - IoT for CircuitPython

## Graphics Demos

One of the nice things about how we set up the PyPortal is it uses a 8 bit parallel display connection, which can be driven much faster than classic SPI displays. Not only do you write 8 bits at a time instead of one, the SPI peripheral tops out at about 24MHz! Combined with DMA you can get surprising speeds. Another nifty hack is taking advantage of the 256KB of SRAM on the SAMD51J20 - you can buffer an entire 240x320 16-bit color bitmap and then draw the whole thing at once

# TouchPaint
Our classic touchscreen painting demo is a good example of how to read touch points, convert the raw readings to calibrated coordinates and then draw something on the screen. [It's in Adafruit\_ILI9341 called **touchpaint\_pyportal**](https://github.com/adafruit/Adafruit_ILI9341/tree/master/examples/touchpaint_pyportal)

![circuitpython_1770-00.jpg](https://cdn-learn.adafruit.com/assets/assets/000/073/301/medium640/circuitpython_1770-00.jpg?1552884953)

# Amiga Boing!

As featured above, this draws a checkered ball around the screen, a classic Amiga demo. [Available in the Adafruit\_ILI9341 library as the **pyportal\_boing** demo](https://github.com/adafruit/Adafruit_ILI9341/tree/master/examples/pyportal_boing). It's an intense demo showing how to calculate a scan line and render it post computation.

# Mandelbrot

This is a good demo to show how we allocate a full display buffer, do all our calculations, then draw it all at once. [Also in Adafruit\_ILI9341 under example **mandelbrot**](https://github.com/adafruit/Adafruit_ILI9341/tree/master/examples/mandelbrot)

# Adafruit PyPortal - IoT for CircuitPython

## Adapting Sketches to M0 & M4

The ATSAMD21 and 51 are very nice little chips, but fairly new as Arduino-compatible cores go. **Most** sketches & libraries will work but here’s a collection of things we noticed.

The notes below cover a range of Adafruit M0 and M4 boards, but not every rule will apply to every board (e.g. Trinket and Gemma M0 do not have ARef, so you can skip the Analog References note!).

# Analog References

If you'd like to use the **ARef** pin for a non-3.3V analog reference, the code to use is `analogReference(AR_EXTERNAL)` (it's AR\_EXTERNAL not EXTERNAL)

# Pin Outputs & Pullups

The old-style way of turning on a pin as an input with a pullup is to use

`pinMode(pin, INPUT)``digitalWrite(pin, HIGH)`

This is because the pullup-selection register on 8-bit AVR chips is the same as the output-selection register.

For M0 & M4 boards, you can't do this anymore! Instead, use:

`pinMode(pin, INPUT_PULLUP)`

Code written this way still has the benefit of being _backwards compatible with AVR._ You don’t need separate versions for the different board types.

# Serial vs SerialUSB

99.9% of your existing Arduino sketches use **Serial.print** to debug and give output. For the Official Arduino SAMD/M0 core, this goes to the Serial5 port, which isn't exposed on the Feather. The USB port for the Official Arduino M0 core is called _ **SerialUSB** _ instead.

In the Adafruit M0/M4 Core, we fixed it so that **Serial goes to USB so it will automatically work just fine**.

**However, on the off chance you are using the official Arduino SAMD core and _not_ the Adafruit version (which really, we recommend you use our version because it’s been tuned to our boards), and you want your Serial prints and reads to use the USB port, use _SerialUSB_ instead of _Serial_ in your sketch.**

If you have existing sketches and code and you want them to work with the M0 without a huge find-replace, put

`#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL)   // Required for Serial on Zero based boards   #define Serial SERIAL_PORT_USBVIRTUAL #endif`

**right above the first** function definition in your code. For example:

![](https://cdn-learn.adafruit.com/assets/assets/000/028/800/medium800/adafruit_products_serialusb.png?1448655245)

# AnalogWrite / PWM on Feather/Metro M0

After looking through the SAMD21 datasheet, we've found that some of the options listed in the multiplexer table don't exist on the specific chip used in the Feather M0.

For all SAMD21 chips, there are two peripherals that can generate PWM signals: The Timer/Counter (TC) and Timer/Counter for Control Applications (TCC). Each SAMD21 has multiple copies of each, called 'instances'.

Each TC instance has one count register, one control register, and two output channels. Either channel can be enabled and disabled, and either channel can be inverted. The pins connected to a TC instance can output identical versions of the same PWM waveform, or complementary waveforms.

Each TCC instance has a single count register, but multiple compare registers and output channels. There are options for different kinds of waveform, interleaved switching, programmable dead time, and so on.

The biggest members of the SAMD21 family have five TC instances with two 'waveform output' (WO) channels, and three TCC instances with eight WO channels:

- TC[0-4],WO[0-1]
- TCC[0-2],WO[0-7]

And those are the ones&nbsp;shown in the datasheet's multiplexer tables.

The SAMD21G used in the Feather M0 only has three TC instances with two output channels, and three TCC instances with eight&nbsp;output channels:

- **TC[3-5],WO[0-1]**
- **TCC[0-2],WO[0-7]**

Tracing the signals to the pins broken out on the Feather M0, the following pins can't do PWM at all:

- **Analog pin A5**

The following pins can be configured for PWM without any signal conflicts as long as the SPI, I2C, and UART pins keep their protocol functions:

- **Digital pins 5, 6, 9, 10, 11, 12, and 13**
- **Analog pins A3 and A4**

If only the SPI pins keep their protocol functions, you can also do PWM on the following pins:

- **TX and SDA (Digital pins 1 and 20)**

# analogWrite() PWM range

On AVR, if you set a pin's PWM with `analogWrite(pin, 255)` it will turn the pin fully HIGH. On the ARM cortex, it will set it to be 255/256 so there will be very slim but still-existing pulses-to-0V. If you need the pin to be fully on, add test code that checks if you are trying to `analogWrite(pin, 255)` and, instead, does a `digitalWrite(pin, HIGH)`

# analogWrite() DAC on A0

If you are trying to use `analogWrite()` to control the DAC output on **A0** , make sure you do **not** have a line that sets the pin to output. _ **Remove** _: `pinMode(A0, OUTPUT)`.

# serialEvent() and serialEvent1()

`serialEvent()` and&nbsp;`serialEvent1()`&nbsp;[do not work](https://www.arduino.cc/reference/en/language/functions/communication/serial/serialevent/) on any non-AVR Arduino boards. Use `Serial.available()` instead.

# Missing header files

There might be code that uses libraries that are not supported by the M0 core. For example if you have a line with

`#include <util/delay.h>`

you'll get an error that says

`fatal error: util/delay.h: No such file or directory  #include <util/delay.h>                         ^compilation terminated.Error compiling.`

In which case you can simply locate where the line is (the error will give you the file name and line number) and 'wrap it' with #ifdef's so it looks like:

```
#if !defined(ARDUINO_ARCH_SAM) &amp;&amp; !defined(ARDUINO_ARCH_SAMD) &amp;&amp; !defined(ESP8266) &amp;&amp; !defined(ARDUINO_ARCH_STM32F2)
 #include &lt;util/delay.h&gt;
#endif
```

The above will also make sure that header file isn't included for other architectures

If the #include is in the arduino sketch itself, you can try just removing the line.

# Bootloader Launching

For most other AVRs, clicking **reset** while plugged into USB will launch the bootloader manually, the bootloader will time out after a few seconds. For the M0/M4, you'll need to **_double click_** the button. You will see a pulsing red LED to let you know you're in bootloader mode. Once in that mode, it wont time out! Click reset again if you want to go back to launching code.

# Aligned Memory Access

This is a little less likely to happen to you but it happened to me! If you're used to 8-bit platforms, you can do this nice thing where you can typecast variables around. e.g.

`uint8_t mybuffer[4];`  
`float f = (float)mybuffer;`

You can't be guaranteed that this will work on a 32-bit platform because **mybuffer** might not be aligned to a 2 or 4-byte boundary. The ARM Cortex-M0 can only directly access data on 16-bit boundaries (every 2 or 4 bytes). Trying to access an odd-boundary byte (on a 1 or 3 byte location) will cause a Hard Fault and stop the MCU. Thankfully, there's an easy work around ... just use memcpy!

`uint8_t mybuffer[4];float f;memcpy(&f, mybuffer, 4)`

# Floating Point Conversion

Like the AVR&nbsp;Arduinos, the M0 library does not have full support for converting floating point numbers to ASCII strings. Functions like sprintf will not convert floating point. &nbsp;Fortunately, the standard AVR-LIBC library includes the dtostrf function which can handle the conversion for you.

Unfortunately, the M0 run-time library does not have dtostrf. &nbsp;You may see some references to using **#include \<avr/dtostrf.h\>** to get dtostrf in your code. &nbsp;And while it will compile, it does **not** work.

Instead, check out this thread to find a working dtostrf&nbsp;function you can include in your code:

[http://forum.arduino.cc/index.php?topic=368720.0](http://forum.arduino.cc/index.php?topic=368720.0)

# How Much RAM Available?

The ATSAMD21G18 has 32K of RAM, but you still might need to track it for some reason. You can do so with this handy function:

```
extern "C" char *sbrk(int i);

int FreeRam () {
  char stack_dummy = 0;
  return &amp;stack_dummy - sbrk(0);
}
```

Thx to [http://forum.arduino.cc/index.php?topic=365830.msg2542879#msg2542879](http://forum.arduino.cc/index.php?topic=365830.msg2542879#msg2542879) for the tip!

# Storing data in FLASH

If you're used to AVR, you've probably used **PROGMEM** to let the compiler know you'd like to put a variable or string in flash memory to save on RAM. On the ARM, its a little easier, simply add **const** before the variable name:

**const char str[] = "My very long string";**

That string is now in FLASH. You can manipulate the string just like RAM data, the compiler will automatically read from FLASH so you dont need special progmem-knowledgeable functions.

You can verify where data is stored by printing out the address:  
**Serial.print("Address of str $"); Serial.println((int)&str, HEX);**

If the address is $2000000 or larger, its in SRAM. If the address is between $0000 and $3FFFF Then it is in FLASH

# Pretty-Printing out registers

There's _a lot_ of registers on the SAMD21, and you often are going through ASF or another framework to get to them. So having a way to see exactly what's going on is handy. This library from drewfish will help a ton!

[https://github.com/drewfish/arduino-ZeroRegs](https://github.com/drewfish/arduino-ZeroRegs)

# M4 Performance Options

As of version 1.4.0 of the _Adafruit SAMD Boards_ package in the Arduino Boards Manager, some options are available to wring extra performance out of M4-based devices. These are in the _Tools_ menu.

![](https://cdn-learn.adafruit.com/assets/assets/000/074/814/medium800/adafruit_products_m4-performance.png?1556044075)

 **All of these performance tweaks involve a degree of uncertainty.** There’s _no guarantee_ of improved performance in any given project, and _some may even be detrimental,_ failing to work in part or in whole. If you encounter trouble, **select the default performance settings** and re-upload.

Here’s what you get and some issues you might encounter…

## CPU Speed (overclocking)

This option lets you adjust the microcontroller core clock…the speed at which it processes instructions…beyond the official datasheet specifications.

Manufacturers often rate speeds conservatively because such devices are marketed for harsh industrial environments…if a system crashes, someone could lose a limb or worse. But most creative tasks are less critical and operate in more comfortable settings, and we can push things a bit if we want more speed.

There is a small but nonzero chance of code **locking up** or **failing to run** entirely. If this happens, try **dialing back the speed by one notch and re-upload** , see if it’s more stable.

Much more likely, **some code or libraries may not play well** with the nonstandard CPU speed. For example, currently the NeoPixel library assumes a 120 MHz CPU speed and won’t issue the correct data at other settings (this will be worked on). Other libraries may exhibit similar problems, usually anything that strictly depends on CPU timing…you might encounter problems with audio- or servo-related code depending how it’s written. **If you encounter such code or libraries, set the CPU speed to the default 120 MHz and re-upload.**

## Optimize

There’s usually more than one way to solve a problem, some more resource-intensive than others. Since Arduino got its start on resource-limited AVR microcontrollers, the C++ compiler has always aimed for the **smallest compiled program size**. The “Optimize” menu gives some choices for the compiler to take different and often faster approaches, at the expense of slightly larger program size…with the huge flash memory capacity of M4 devices, that’s rarely a problem now.

The “ **Small** ” setting will compile your code like it always has in the past, aiming for the smallest compiled program size.

The “ **Fast** ” setting invokes various speed optimizations. The resulting program should produce the same results, is slightly larger, and usually (but not always) noticably faster. It’s worth a shot!

“ **Here be dragons** ” invokes some more intensive optimizations…code will be larger still, faster still, but there’s a possibility these optimizations could cause unexpected behaviors. _Some code may not work the same as before._ Hence the name. Maybe you’ll discover treasure here, or maybe you’ll sail right off the edge of the world.

Most code and libraries will continue to function regardless of the optimizer settings. If you do encounter problems, **dial it back one notch and re-upload**.

## Cache

This option allows a small collection of instructions and data to be accessed more quickly than from flash memory, boosting performance. It’s enabled by default and should work fine with all code and libraries. But if you encounter some esoteric situation, the cache can be disabled, then recompile and upload.

## Max SPI and Max QSPI

**These should probably be left at their defaults.** They’re present mostly for our own experiments and can cause **serious headaches**.

Max SPI determines the clock source for the M4’s SPI peripherals. Under normal circumstances this allows transfers up to 24 MHz, and should usually be left at that setting. But…if you’re using write-only SPI devices (such as TFT or OLED displays), this option lets you drive them faster (we’ve successfully used 60 MHz with some TFT screens). The caveat is, if using _any_ read/write devices (such as an SD card), _this will not work at all…_SPI reads&nbsp;_absolutely_ max out at the default 24 MHz setting, and anything else will fail. **Write = OK. Read = FAIL.** This is true _even if your code is using a lower bitrate setting…_just having the different clock source prevents SPI reads.

Max QSPI does similarly for the extra flash storage on M4 “Express” boards. _Very few_ Arduino sketches access this storage at all, let alone in a bandwidth-constrained context, so this will benefit next to nobody. Additionally, due to the way clock dividers are selected, this will only provide some benefit when certain “CPU Speed” settings are active. Our [PyPortal Animated GIF Display](https://learn.adafruit.com/pyportal-animated-gif-display) runs marginally better with it, if using the QSPI flash.

# Enabling the Buck Converter on some M4 Boards

If you want to reduce power draw, some of our boards have an inductor so you can use the 1.8V buck converter instead of the built in linear regulator. If the board does have an inductor (see the schematic) you can add the line `SUPC->VREG.bit.SEL = 1;` to your code to switch to it. Note it will make ADC/DAC reads a bit noisier so we don't use it by default. [You'll save ~4mA](https://github.com/adafruit/ArduinoCore-samd/issues/128).

# Adafruit PyPortal - IoT for CircuitPython

## WipperSnapper Setup

Warning: 

Danger: 

## What is WipperSnapper

WipperSnapper is a firmware designed to turn any WiFi-capable board into an Internet-of-Things device without programming a single line of code. WipperSnapper connects to [Adafruit IO](https://io.adafruit.com/), a web&nbsp;platform designed ([by Adafruit!](https://www.adafruit.com/about)) to&nbsp;_display_,&nbsp;_respond_, and&nbsp;_interact_&nbsp;with your project's data.

Simply load the WipperSnapper firmware onto your board, add credentials, and plug it into power. Your board will automatically register itself with your Adafruit IO account.

From there, you can add&nbsp;_components_&nbsp;to your board such as buttons, switches, potentiometers, sensors, and more! Components are&nbsp;_dynamically&nbsp;_added to hardware, so you can&nbsp;immediately start interacting, logging, and streaming the data your projects produce without writing code.

## Sign up for Adafruit.io

You will need an Adafruit IO account to use WipperSnapper on your board. If you do not already have one, head over to [io.adafruit.com](https://io.adafruit.com/) to create a&nbsp;_free_ account.

## Add a New Device to Adafruit IO

Log into your [Adafruit IO](https://io.adafruit.com/) account. Click the _New Device_ button at the top of the page.

![](https://cdn-learn.adafruit.com/assets/assets/000/112/548/medium800/wippersnapper_setup__board_selector__web__new_device_1.png?1655919766)

After clicking New Device, you should be on the _board selector_ page. This page displays every board that is compatible with the WipperSnapper firmware.

![](https://cdn-learn.adafruit.com/assets/assets/000/112/550/medium800/wippersnapper_setup__board_selector__web__selector_maion.png?1655920178)

In the board selector page's search bar, search for the PyPortal_.&nbsp;_Once you've located the board you'd like to install WipperSnapper on,_&nbsp;_click the&nbsp; **Choose Board** &nbsp;button to bring you to the self-guided installation wizard.

![](https://cdn-learn.adafruit.com/assets/assets/000/113/120/medium800/circuitpython_WS_PP_choose_board.png?1657642515)

Follow the step-by-step instructions on the page to install Wippersnapper on your device and connect it to Adafruit IO.

![](https://cdn-learn.adafruit.com/assets/assets/000/113/121/medium800/circuitpython_WS_PP_new_device_step_two.png?1657642529)

If the installation was successful, a popover should appear displaying that your board has successfully been detected by Adafruit IO.

Give your board a name and click "Continue to Device Page".

![](https://cdn-learn.adafruit.com/assets/assets/000/113/122/medium800/circuitpython_WS_PP_New_Device_Detected.png?1657642539)

You should be brought to your board's device page.

![](https://cdn-learn.adafruit.com/assets/assets/000/113/123/medium800/circuitpython_WS_PP_empty_device_page.png?1657642556)

## Feedback

Adafruit.io WipperSnapper is in **beta** and you can help improve it!

If you have&nbsp; suggestions or general feedback about the installation process - visit [https://io.adafruit.com/support](https://io.adafruit.com/support), click _"Contact Adafruit IO Support"_ and select _"I have feedback or suggestions for the WipperSnapper Beta"._

## Troubleshooting

If you encountered an issue during installation, please try the steps below first.

If you're still unable to resolve the issue, or if your issue is not listed below, get in touch with us directly at [https://io.adafruit.com/support](https://io.adafruit.com/support "https://io.adafruit.com/support"). Make sure to click&nbsp; _"Contact Adafruit IO Support"_ and select "_There is an issue with WipperSnapper. Something is broken!"_

### 

First, make sure that you selected the correct board on the board selector.

Next, please make sure that you entered your WiFi credentials properly, there are no spaces/special characters in either your network name (SSID) or password, and that you are connected to a 2.4GHz wireless network.

If you're still unable to connect your board to WiFi, please [make a new post on the WipperSnapper technical support forum with the error you're experiencing, the LED colors which are blinking, and the board you're using.](https://forums.adafruit.com/viewforum.php?f=66)

### 

Try hard-resetting your board by unplugging it from USB power and plugging it back in.

If the error is still occurring, please&nbsp;[make a new post on the WipperSnapper technical support forum with information about what you're experiencing, the LED colors which are blinking (if applicable), and the board you're using.](https://forums.adafruit.com/viewforum.php?f=66)

## "Uninstalling" WipperSnapper&nbsp;

WipperSnapper firmware is an application that is loaded onto your board. There is nothing to "uninstall". However, you may want to "move" your board from running WipperSnapper to running Arduino or CircuitPython. You also may need to restore your board to the state it was shipped to you from the Adafruit factory.&nbsp;

### Moving from WipperSnapper to CircuitPython

Follow the steps on the [Installing CircuitPython page](https://learn.adafruit.com/welcome-to-circuitpython/installing-circuitpython) to install CircuitPython on your board running WipperSnapper.

- If you are unable to double-tap the RST button to enter the UF2 bootloader, follow the _"Factory Resetting a WipperSnapper Board"_ instructions below.

Uploading this sketch will overwrite WipperSnapper. If you want to re-install WipperSnapper, follow the instructions at the top of this page.

### Moving from WipperSnapper to Arduino

If you want to use your board with Arduino, you will use the Arduino IDE to load&nbsp;_any_ sketch onto your board.

First, follow the page below to set up your Arduino IDE environment for use with your board.

[Setup Arduino IDE](https://learn.adafruit.com/adafruit-pyportal/setup)
Then, follow the page below to upload the "Arduino Blink" sketch to your board.

[Upload Arduino Test/Blink Sketch](https://learn.adafruit.com/adafruit-pyportal/arduino-test)
Uploading this sketch will overwrite WipperSnapper. If you want to re-install WipperSnapper, follow the instructions at the top of this page.

### Factory Resetting a WipperSnapper Board

Sometimes, hardware gets into a state that requires it to be "restored" to the original state it shipped in. If you'd like to get your board back to its original factory state, follow the guide below.

This guide does not have a factory reset page. Follow [the steps listed here to update/re-install the UF2 bootloader](https://learn.adafruit.com/adafruit-pyportal/update-the-uf2-bootloader).&nbsp;

# Adafruit PyPortal - IoT for CircuitPython

## WipperSnapper Essentials

![](https://cdn-learn.adafruit.com/assets/assets/000/117/600/medium800/wippersnapper_essentials_heading_sensors_boardspread.jpg?1674496066)

You've installed WipperSnapper firmware on your board and connected it to Adafruit IO. Next, to learn how to use Adafruit IO!

The Adafruit IO supports a large number of _components. Components_ are physical parts such as buttons, switches, sensors, servos, LEDs, RGB LEDs, and more.&nbsp;

The following pages will get you up and running with WipperSnapper as you interact with your board's built-in components, such as read the value of a push button, send the value of an I2C sensor to the internet, and wirelessly control colorful LEDs.

# Adafruit PyPortal - IoT for CircuitPython

## LED Blink

Info: This demo shows controlling an LED from Adafruit IO. The same kind of control can be used for relays, lights, motors, or solenoids.

One of the first programs you typically write to get used to embedded programming is a sketch that repeatably blinks an LED. IoT projects are&nbsp;_wireless,_&nbsp;so after completing this section, you'll be able to turn on (or off) the LED built into your board from anywhere in the world.

## Where is the LED on my board?
The PyPortal's status LED is located next to the Micro-USB port and labeled "ACT".

![circuitpython_pyportal-led.png](https://cdn-learn.adafruit.com/assets/assets/000/118/856/medium640/circuitpython_pyportal-led.png?1677187028)

## Create a LED Component on Adafruit IO
Primary: If you used the Magic Config button to automatically configure the components on your board, the LED component will already be configured. Skip ahead to the [Usage](https://learn.adafruit.com/adafruit-pyportal/led-blink#usage-3135693) section.

Don't know about Magic Config? Click the Auto-Config button on the device page for further details!

On the device page, click the New Component (or "+") button to open the component picker.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/859/medium800/circuitpython_pyportal-new-component.png?1677189398)

Search for the component name by entering&nbsp;`LED`&nbsp;into the text box on the component picker, the list of components should update as soon as you stop typing.

![led_blink_LED_select.png](https://cdn-learn.adafruit.com/assets/assets/000/127/428/medium640/led_blink_LED_select.png?1706755656)

### Filtering and searching for components

Since WipperSnapper supports such a large number of components, there is keyword filtering. Try searching for various keywords, like:

- component names:&nbsp;`aht20`,&nbsp;`servo`,&nbsp;`buzzer`,&nbsp;`button`, `led`, etc
- sensor types:&nbsp;`light`,&nbsp;`temperature`,&nbsp;`pressure`,&nbsp;`humidity`, etc
- interface:&nbsp;`i2c`,&nbsp;`uart`,&nbsp;`ds18x20`,&nbsp;`pin`, etc (also I2C addresses e.g.&nbsp;`0x44`)
- vendor:&nbsp;`Adafruit`,&nbsp;`ASAIR`,&nbsp;`Infineon`,&nbsp;`Bosch`,&nbsp;`Honeywell`,&nbsp;`Sensirion`, etc

There are also product and documentation links to every component. Follow the links beneath the component descriptions to be taken to the appropriate product page or Learn Guide

Select the **LED** from the list of components.

![led_blink_LED_select.png](https://cdn-learn.adafruit.com/assets/assets/000/127/429/medium640/led_blink_LED_select.png?1706755753)

On the Create LED Component form, the board's LED pin is pre-selected.&nbsp;

Click Create Component.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/860/medium800/circuitpython_pyportal-led-form.png?1677205283)

Behind the scenes, Adafruit IO sends send a command to your board running WipperSnapper telling it to configure "LED Pin" as a digital output.

Your board's page on Adafruit IO shows a new LED component.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/861/medium800/circuitpython_pyportal-led-device-pg.png?1677189533)

## Usage
On the board page, toggle the LED component by clicking the toggle switch. This should turn your board's built-in LED on or off.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/862/medium800thumb/circuitpython_ezgif.com-video-to-gif_%2824%29.jpg?1677189598)

# Adafruit PyPortal - IoT for CircuitPython

## NeoPixel LED

Your board has a WS281x RGB LED (NeoPixel, in Adafruit jargon) built in. Boards running the WipperSnapper firmware can be wirelessly controlled by Adafruit IO to interact with NeoPixels.

On this page, you'll learn how to change the color and brightness of the NeoPixel built into your board from Adafruit IO.

## Where is the NeoPixel on my board?
The PyPortal contains a built-in NeoPixel on the back of the board labeled "STATUS".

![circuitpython_pyportal-status-led.png](https://cdn-learn.adafruit.com/assets/assets/000/118/863/medium640/circuitpython_pyportal-status-led.png?1677205629)

## Create the NeoPixel Component
Primary: If you used the Magic Config button to automatically configure the components on your board, then the NeoPixel component will already be configured. Skip ahead to the **Usage** section.

Don't know about Magic Config? Click the Auto-Config button on the device page for further details!

On the device page, click the New Component (or "+") button to open the component picker.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/864/medium800/circuitpython_pyportal-new-component.png?1677189766)

Search for the component name by entering&nbsp;`neopixel`&nbsp;into the text box on the component picker, the list of components should update as soon as you stop typing

![neopixel_led__built_in__NaP8Tyf3AP.png](https://cdn-learn.adafruit.com/assets/assets/000/127/434/medium640/neopixel_led__built_in__NaP8Tyf3AP.png?1706807288)

### Filtering and searching for components

Since WipperSnapper supports such a large number of components, you can use keyword filtering. Try searching for various keywords, like:

- component names:&nbsp;`aht20`,&nbsp;`servo`,&nbsp;`buzzer`,&nbsp;`button`, `neopixel`, etc
- sensor types:&nbsp;`light`,&nbsp;`temperature`,&nbsp;`pressure`,&nbsp;`humidity`, etc
- interface:&nbsp;`i2c`,&nbsp;`uart`,&nbsp;`ds18x20`,&nbsp;`pin`, etc (also I2C addresses e.g.&nbsp;`0x44`)
- vendor:&nbsp;`Adafruit`,&nbsp;`ASAIR`,&nbsp;`Infineon`,&nbsp;`Bosch`,&nbsp;`Honeywell`,&nbsp;`Sensirion`, etc

There is also added product and documentation links for every component, follow the links beneath the component descriptions to be taken to the appropriate product page or Learn Guide

Select the **NeoPixel** &nbsp;from the list of results to go to the component configuration page.

There will be a back button if you select the wrong component, and you can use the Edit component icon (⚙️) on the device page to update the component configuration in the future.

![neopixel_led__built_in__NaP8Tyf3AP.png](https://cdn-learn.adafruit.com/assets/assets/000/127/447/medium640/neopixel_led__built_in__NaP8Tyf3AP.png?1706818377)

The board NeoPixel pin is automatically found and selected.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/865/medium800/circuitpython_pyportal-neopixel-form.png?1677189863)

Click&nbsp; **Create Component**

![](https://cdn-learn.adafruit.com/assets/assets/000/118/866/medium800/circuitpython_pyportal-neopixel-form-2.png?1677189872)

Behind the scenes, Adafruit IO sends a command to your board running WipperSnapper firmware telling it to configure the pin as a NeoPixel component with the settings from the form.

The Device page shows the NeoPixel component.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/867/medium800/circuitpython_pyportal-neopixel-device-pg.png?1677189933)

## Set the NeoPixel's RGB Color

Since no colors have been set yet, the color picker's default value is `#000000` (black in hex color code) and appears "off". You can change that to make the NeoPixel shine brightly!&nbsp;

On the NeoPixel component, click the color dropper at the end of the color swatch list.

![](https://cdn-learn.adafruit.com/assets/assets/000/127/438/medium800/neopixel_led__built_in__gU5CxAW05e_ARROW.png?1706808192)

A color picker pops open! Next: learning how Adafruit IO uses hex color codes to represent the colors on your NeoPixel.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/313/medium800/neopixel_led__built_in__picker-no-color-set.png?1675876687)

### Hex Colors 101

The color picker on Adafruit IO uses hex color codes to represent Red, Green, and Blue values. For example, `#FF0000` is the hex color code for the color red. The colors (`#FF0000`) red component is `FF` (255 translated to decimal), the green component is `00` and the blue component is `00`. Translated to RGB format, the color is `RGB (255, 0, 0)`.&nbsp;

Using the color picker, or by manually entering a hex color code, select a color.

![](https://cdn-learn.adafruit.com/assets/assets/000/117/677/medium800thumb/neopixel_led_ezgif.com-gif-maker_%283%29.jpg?1674668859)

When you're ready to set the color of your device's NeoPixel, click **FILL WITH COLOR**.&nbsp;The NeoPixel will immediately glow!

![](https://cdn-learn.adafruit.com/assets/assets/000/118/869/medium800thumb/circuitpython_ezgif.com-video-to-gif_%2825%29.jpg?1677206680)

## Set NeoPixel Brightness

If the NeoPixel is too bright (or too dim), you can change the overall brightness. Click the gear/cog icon on the NeoPixel component to open its settings.

![](https://cdn-learn.adafruit.com/assets/assets/000/127/439/medium800/neopixel_led__built_in__arrow_FTzWrYk8MV.png?1706810370)

On the NeoPixel component form, set Brightness to a value between 0 (fully off) and 255 (full brightness).

Click the **Update Component&nbsp;** button to send the updated configuration to your device.&nbsp;

![](https://cdn-learn.adafruit.com/assets/assets/000/117/675/medium800/neopixel_led_IO_-_Device__Adafruit_Feather_ESP32_V2.png?1674666662)

# Adafruit PyPortal - IoT for CircuitPython

## Read a Push-button

Info: 

You can configure a board running WipperSnapper to read data from standard input buttons, switches, or digital sensors, and send the value to Adafruit IO.

From Adafruit IO, you will configure one of the pushbuttons on your board as a push button component. Then, when the button is pressed (or released), a value will be published to Adafruit IO.

## Parts

The following parts are required to complete this page.

### Tactile Switch Buttons (12mm square, 6mm tall) x 10 pack

[Tactile Switch Buttons (12mm square, 6mm tall) x 10 pack](https://www.adafruit.com/product/1119)
Medium-sized clicky momentary switches are standard input "buttons" on electronic projects. These work best in a PCB but [can be used on a solderless breadboard as shown in this tutorial](https://learn.adafruit.com/adafruit-arduino-lesson-6-digital-inputs?view=all). The...

In Stock
[Buy Now](https://www.adafruit.com/product/1119)
[Related Guides to the Product](https://learn.adafruit.com/products/1119/guides)
![Angled shot of 10 12mm square tactile switch buttons.](https://cdn-shop.adafruit.com/640x480/1119-03.jpg)

### Half Sized Premium Breadboard - 400 Tie Points

[Half Sized Premium Breadboard - 400 Tie Points](https://www.adafruit.com/product/64)
This is a cute, half-size breadboard with&nbsp;400 tie points, good for small projects. It's 3.25" x 2.2" / 8.3cm&nbsp;x 5.5cm&nbsp;with a standard double-strip in the middle and two power rails on both sides.&nbsp;You can pull the power rails off easily to make the breadboard as...

In Stock
[Buy Now](https://www.adafruit.com/product/64)
[Related Guides to the Product](https://learn.adafruit.com/products/64/guides)
![Angled shot of half-size solderless breadboard with red and black power lines.](https://cdn-shop.adafruit.com/640x480/64-06.jpg)

### Breadboarding wire bundle

[Breadboarding wire bundle](https://www.adafruit.com/product/153)
75 flexible stranded core wires with stiff ends molded on in red, orange, yellow, green, blue, brown, black and white. These are a major improvement over the "box of bent wires" that are sometimes sold with breadboards, and faster than stripping your own solid core wires. Makes...

In Stock
[Buy Now](https://www.adafruit.com/product/153)
[Related Guides to the Product](https://learn.adafruit.com/products/153/guides)
![Bundle of multi-colored multi-length wire jumpers](https://cdn-shop.adafruit.com/640x480/153-04.jpg)

## Wiring
You will need a STEMMA JST PH 2mm 3-Pin to Male Header Cable to connect your PyPortal to a breadboard:

### STEMMA JST PH 2mm 3-Pin to Male Header Cable - 200mm

[STEMMA JST PH 2mm 3-Pin to Male Header Cable - 200mm](https://www.adafruit.com/product/3893)
This cable will let you turn a JST PH 3-pin cable port into 3 individual wires with high-quality 0.1" male header plugs on the end. We're carrying these to match up with our Hallowing, for extending and connecting sensors or LEDs - and the wires are even color coded!

<a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/3893)
[Related Guides to the Product](https://learn.adafruit.com/products/3893/guides)
![Angled shot of STEMMA JST PH 3-Pin to Male Header Cable - 200mm.](https://cdn-shop.adafruit.com/640x480/3893-03.jpg)

Using a STEMMA JST PH 2mm 3-Pin to Male Header Cable:

- Connect STEMMA Ground (Black) to one leg of the push-button.
- Connect STEMMA Signal (White) to the other leg of the push-button.

![circuitpython_pyportal-btn-bb.png](https://cdn-learn.adafruit.com/assets/assets/000/118/870/medium640/circuitpython_pyportal-btn-bb.png?1677206278)

## Create a Push-button Component on Adafruit IO

On the device page, click the New Component (or "+") button to open the component picker.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/871/medium800/circuitpython_pyportal-new-component.png?1677206183)

Search for the component name by entering&nbsp;`push`&nbsp;into the text box on the component picker, the list of components should update as soon as you stop typing.

![read_a_push_button__external__5NrzQhD0zT.png](https://cdn-learn.adafruit.com/assets/assets/000/127/432/medium640/read_a_push_button__external__5NrzQhD0zT.png?1706750012)

### 

WipperSnapper supports such a large number of components we added filtering!  
Try searching for various keywords, like:

- component names:&nbsp;`aht20`,&nbsp;`servo`,&nbsp;`buzzer`,&nbsp;`button`,&nbsp;`relay`, etc
- sensor types:&nbsp;`light`,&nbsp;`temperature`,&nbsp;`pressure`,&nbsp;`humidity`, etc
- interface:&nbsp;`i2c`,&nbsp;`uart`,&nbsp;`ds18x20`,&nbsp;`pin`, etc (also I2C addresses e.g.&nbsp;`0x44`)
- vendor:&nbsp;`Adafruit`,&nbsp;`ASAIR`,&nbsp;`Infineon`,&nbsp;`Bosch`,&nbsp;`Honeywell`,&nbsp;`Sensirion`, etc

We’ve also added product and documentation links to every component, follow the links beneath the component descriptions to be taken to the appropriate product page or Learn-Guide.

Select the **Push Button** &nbsp;from the list of results to go to the component configuration page.

There will be a back button if you select the wrong component, and you can use the Edit component icon (⚙️) on the device page to update the component configuration in the future.

![read_a_push_button__external__5NrzQhD0zT.png](https://cdn-learn.adafruit.com/assets/assets/000/127/433/medium640/read_a_push_button__external__5NrzQhD0zT.png?1706750109)

The "Create Push Button Component" form&nbsp;presents you with options for configuring the push button.

Start by selecting the board's pin connected to the push button.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/872/medium800/circuitpython_pyportal-pb-pin.png?1677206496)

The&nbsp;_Return Interval_&nbsp;dictates how frequently the value of the push-button will be sent from the board to Adafruit IO.

For this example, you will configure the push button value to be only sent when the value changes (i.e.: when it's either pressed or depressed).

![](https://cdn-learn.adafruit.com/assets/assets/000/118/873/medium800/circuitpython_pyportal-pb-2.png?1677206487)

Check the&nbsp; **Specify Pin Pull Direction&nbsp;** checkbox.

Select&nbsp; **Pull Up** to turn on the internal pull-up resistor.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/874/medium800/circuitpython_pyportal-pb-pull-up.png?1677206572)

Make sure the form settings look like the following screenshot. Then, click&nbsp; **Create Component**.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/875/medium800/circuitpython_pyportal-pb-create.png?1677206564)

Adafruit IO sends a command to your WipperSnapper board, telling it to configure the GPIO pin you selected to behave as a digital input pin and to enable it to pull up the internal resistor.

Your board page should also show the new push-button component.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/876/medium800/circuitpython_pyportal-pb-page.png?1677206654)

Push the button on your board to change the value of the push-button component on Adafruit IO.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/877/medium800thumb/circuitpython_ezgif.com-video-to-gif_%2826%29.jpg?1677206758)

# Adafruit PyPortal - IoT for CircuitPython

## Analog Input (Light Sensor)

Info: 

Your microcontroller board has both digital and analog signal capabilities. Some pins are analog, some are digital, and some are capable of both. Check the **Pinouts** page in this guide for details about your board.

Analog signals are different from digital signals in that they can be _any_ voltage and can vary continuously and smoothly between voltages. An analog signal is like a dimmer switch on a light, whereas a digital signal is like a simple on/off switch.&nbsp;

Digital signals only can ever have two states, they are either are **on** (high logic level voltage like 3.3V) or **off** (low logic level voltage like 0V / ground).

By contrast, analog signals can be any voltage in-between on and off, such as 1.8V or 0.001V or 2.98V and so on.

![](https://cdn-learn.adafruit.com/assets/assets/000/102/411/medium800/analog_in_Analog_Template_analog.jpg?1622144011)

Analog signals are continuous values which means they can be an _infinite_ number of different voltages. Think of analog signals like a floating point or fractional number, they can smoothly transiting to _any_ in-between value like 1.8V, 1.81V, 1.801V, 1.8001V, 1.80001V and so forth to infinity.

Many devices use analog signals, in particular sensors typically output an analog signal or voltage that varies based on something being sensed like light, heat, humidity, etc.

## Analog to Digital Converter (ADC)

An analog-to-digital-converter, or ADC, is the key to reading analog signals and voltages with a microcontroller. An ADC is a device that reads the voltage of an analog signal and converts it into a digital, or numeric, value. The microcontroller can’t read analog signals directly, so the analog signal is first converted into a numeric value by the ADC.&nbsp;

The black line below shows a digital signal over time, and the red line shows the converted analog signal over the same amount of time.

![](https://cdn-learn.adafruit.com/assets/assets/000/102/412/medium800/analog_in_Analog_Template_ADC.jpg?1622144080)

Once that analog signal has been converted by the ADC, the microcontroller can use those digital values any way you like!

## Light Sensor

A light sensor (also known as a CdS cell, light-dependent resistor, or photoresistor) detects light. They change their resistive value (in ohms, Ω) depending on how much light shines into the photocell.&nbsp;

When a light sensor is exposed to more light, the resistance decreases. When it is exposed to less light, the resistance increases.&nbsp;

By using a light sensor wired in a specific way (as a _voltage divider_), we can turn resistance into voltage. That change is then read by your board's Analog-to-Digital converter and sent to Adafruit IO.

## Where is the Light Sensor on my board?
There is an&nbsp; **ambient light sensor** &nbsp;on the side, which points through to the front.

![circuitpython_PyPortalPinouts_LightSensorFront.jpg](https://cdn-learn.adafruit.com/assets/assets/000/118/879/medium640/circuitpython_PyPortalPinouts_LightSensorFront.jpg?1677200518)

Primary: If you used the Magic Config button to automatically configure the components on your board, then the Light Sensor component will already be configured. Skip ahead to the **Usage** section.

Don't know about Magic Config? Click the Auto-Config button on the device page for further details!

## Create a Light Sensor Component

On the device page, click the New Component (or "+") button to open the component picker.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/880/medium800/circuitpython_pyportal-new-component.png?1677206881)

Search for the component name by entering `light`&nbsp;into the text box on the component picker, the list of components should update as soon as you stop typing.

![analog_in__light_sensor__Arrow_Bg7EDvT2o3.png](https://cdn-learn.adafruit.com/assets/assets/000/127/440/medium640/analog_in__light_sensor__Arrow_Bg7EDvT2o3.png?1706816841)

###  Filtering and searching for components 


WipperSnapper supports such a large number of components we added filtering!  
Try searching for various keywords, like:

- component names:&nbsp;`aht20`,&nbsp;`servo`,&nbsp;`buzzer`,&nbsp;`button`,&nbsp;`relay`, etc
- sensor types:&nbsp;`light`,&nbsp;`temperature`,&nbsp;`pressure`,&nbsp;`humidity`, etc
- interface:&nbsp;`i2c`,&nbsp;`uart`,&nbsp;`ds18x20`,&nbsp;`pin`, etc (also I2C addresses e.g.&nbsp;`0x44`)
- vendor:&nbsp;`Adafruit`,&nbsp;`ASAIR`,&nbsp;`Infineon`,&nbsp;`Bosch`,&nbsp;`Honeywell`,&nbsp;`Sensirion`, etc

We’ve also added product and documentation links to every component, follow the links beneath the component descriptions to be taken to the appropriate product page or Learn-Guide.

Select the&nbsp; **Light Sensor** &nbsp;from the list of results to go to the component configuration page.

There will be a back button if you select the wrong component, and you can use the Edit component icon (⚙️) on the device page to update the component configuration in the future.

![analog_in__light_sensor__Arrow_Bg7EDvT2o3.png](https://cdn-learn.adafruit.com/assets/assets/000/127/442/medium640/analog_in__light_sensor__Arrow_Bg7EDvT2o3.png?1706816892)

The name and pin for the light sensor on your board are automatically selected. The **Period** determines how frequently the light sensor's value will be checked and sent to Adafruit IO. Set it to check the light sensor value every 30 seconds.

Click Create Component.

![](https://cdn-learn.adafruit.com/assets/assets/000/117/970/medium800/analog_in_create-light-sensor.png?1675192773)

The device page shows a new light sensor component. The value of this component will change every 30 seconds.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/881/medium800/circuitpython_pyportal-light-sensor.png?1677200469)

## Light Sensor Usage
To test the light sensor, try covering the light sensor with a piece of paper. Navigate to the feed page by clicking the graph icon on the top right corner of the light sensor component.

![](https://cdn-learn.adafruit.com/assets/assets/000/117/972/medium800/analog_in_nav-feed-pg.png?1675193149)

On the light sensor feed page, you'll be able to observe a graph of the light sensor values as they change over time.

![](https://cdn-learn.adafruit.com/assets/assets/000/117/973/medium800/analog_in_feed-page-light-sensor.png?1675193175)

# Adafruit PyPortal - IoT for CircuitPython

## I2C Sensor

Inter-Integrated Circuit, aka **I2C** , is a two-wire protocol for connecting sensors and "devices" to a microcontroller. A large number of sensors, including the ones sold by Adafruit, use I2C to communicate.&nbsp;

Typically, using I2C with a microcontroller involves programming. Adafruit IO and WipperSnapper let you configure a microcontroller to read data from an I2C sensor and publish that data to the internet without writing code.

The WipperSnapper firmware supports a number of I2C sensors,&nbsp;[viewable in list format here](https://github.com/adafruit/Wippersnapper_Components/tree/main/components/i2c).

- If you do not see the I2C sensor you're attempting to use with WipperSnapper,&nbsp;[we have a guide on adding a component to Adafruit IO WipperSnapper here](https://learn.adafruit.com/how-to-add-a-new-component-to-adafruit-io-wippersnapper).&nbsp;

The process for adding an I2C component to your board running WipperSnapper is similar for most sensors.&nbsp;

On this page, you'll learn how to configure an I2C sensor built into a development board to send data to Adafruit IO. Then you'll learn how to locate, interpret, and download the data produced by your sensors.

## Where is the I2C sensor on my board?

Your board has an I2C sensor built-in meaning that there's no wiring required!&nbsp;

- On the top of the PyPortal (not the Pynt) is the&nbsp; **ADT7410 Analog Devices temperature sensor** &nbsp;with 16-bit 0.0078°C temperature resolution and 0.5°C temperature tolerance.&nbsp;

![i2c__on_board_sensors__funhouse_specific__pyportal-adt7410.png](https://cdn-learn.adafruit.com/assets/assets/000/118/885/medium640/i2c__on_board_sensors__funhouse_specific__pyportal-adt7410.png?1677264952)

Primary: If you used the Magic Config button to automatically configure the components on your board, then the ADT7410 sensor component will already be configured. Skip ahead to the **Usage** section.

Don't know about Magic Config? Click the Auto-Config button on the device page for further details!

## Create ADT7410 Sensor Component

On the device page, click the New Component (or "+") button to open the component picker.

![](https://cdn-learn.adafruit.com/assets/assets/000/128/612/medium800/circuitpython_xr7hKhBdY4.png?1709735796)

Search for the component name by entering `ADT7410`&nbsp;into the text box on the component picker, the list of components should update as soon as you stop typing.

![i2c__on_board_sensors__pyportal_specific__HCM8rpn3W2.png](https://cdn-learn.adafruit.com/assets/assets/000/128/613/medium640/i2c__on_board_sensors__pyportal_specific__HCM8rpn3W2.png?1709736026)

###  Filtering and searching for components 


WipperSnapper supports such a large number of components we added filtering!  
Try searching for various keywords, like:

- component names:&nbsp;`adt7410`,&nbsp;`servo`,&nbsp;`buzzer`,&nbsp;`button`,&nbsp;`potentiometer`
- sensor types:&nbsp;`light`,&nbsp;`temperature`,&nbsp;`pressure`,&nbsp;`humidity`, etc
- interface:&nbsp;`i2c`,&nbsp;`uart`,&nbsp;`ds18x20`,&nbsp;`pin`, etc (also I2C addresses e.g.&nbsp;`0x44`)
- vendor:&nbsp;`Adafruit`,&nbsp;`ASAIR`,&nbsp;`Infineon`,&nbsp;`Bosch`,&nbsp;`Honeywell`,&nbsp;`Sensirion`, etc

We’ve also added product and documentation links to every component, follow the links beneath the component descriptions to be taken to the appropriate product page or Learn-Guide.

Select the&nbsp; **ADT7410** &nbsp;from the list of results to go to the component configuration page.

There will be a back button if you select the wrong component, and you can use the Edit component icon (⚙️) on the device page to update the component configuration in the future.

![i2c__on_board_sensors__pyportal_specific__HCM8rpn3W2.png](https://cdn-learn.adafruit.com/assets/assets/000/128/615/medium640/i2c__on_board_sensors__pyportal_specific__HCM8rpn3W2.png?1709736141)

On the component configuration page, the ADT7410's I2C sensor address should be listed along with the sensor's settings.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/929/medium800/circuitpython_adt7410-form-default.png?1677521289)

The ADT7410 sensor can measure ambient temperature. This form has individual options for reading the ambient temperature, in either Celsius or Fahrenheit. You may select the readings which are appropriate to your application and region.

The&nbsp; **Send Every** &nbsp;option is specific to each sensor measurement. This option will tell the board how often it should read from the sensor and send the data to Adafruit IO. Measurements can range from every 30 seconds to every 24 hours.

For this example, set the&nbsp; **Send Every&nbsp;** interval for both seconds to&nbsp; **Every 30 seconds.** Click&nbsp; **Create Component**.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/928/medium800/circuitpython_adt7410-form.png?1677521233)

The board page should now show the ADT7410 component you created. After the interval you configured elapses, the WipperSnapper firmware running on your board automatically reads a value from the sensor and sends it to Adafruit IO.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/930/medium800/circuitpython_adt7410-device-pg.png?1677521297)

## Read I2C Sensor Values

Now to take a look behind the scenes at a powerful element of using Adafruit IO and WipperSnapper. When a new component is created on Adafruit IO, an [Adafruit IO Feed](https://learn.adafruit.com/adafruit-io-basics-feeds) is also created. This Feed holds your sensor component's values for long-term storage (30 days of storage for Adafruit IO Free and 60 days for Adafruit IO Plus plans).

Aside from holding the **values** read by a sensor, the component feed also holds **metadata** about the data pushed to Adafruit IO. This includes settings for whether the data is public or private, what license the stored sensor data falls under, and a general description of the data.

Now, let's take a look at the ADT7410's temperature sensor feed. To navigate to a component's feed, click on the chart icon in the upper-right-hand corner of the component.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/886/medium800/i2c__on_board_sensors__funhouse_specific__adt7410-feed.png?1677265060)

On the component's feed page, you'll each data point read by your sensor and when they were reported to Adafruit IO.

![](https://cdn-learn.adafruit.com/assets/assets/000/118/887/medium800/i2c__on_board_sensors__funhouse_specific__adt7410-feed-history.png?1677265104)

### Doing more with your sensor's Adafruit IO Feed

We've only scratched the surface of what Adafruit IO Feeds can accomplish for your IoT projects. For a complete overview of Adafruit IO Feeds, including tasks like downloading feed data, sharing a feed, removing erroneous data points from a feed, and more, [head over to the "Adafruit IO Basics: Feed" learning guide](https://learn.adafruit.com/adafruit-io-basics-feeds).

# Adafruit PyPortal - IoT for CircuitPython

## Build the PyPortal Stand

![](https://cdn-learn.adafruit.com/assets/assets/000/072/835/medium800/circuitpython_untitled_0058_2k.jpg?1552626290)

![](https://cdn-learn.adafruit.com/assets/assets/000/072/836/medium800/circuitpython_untitled_0061_2k.jpg?1552626303)

Here's how to assemble the laser cut acrylic stand for the PyPortal. The kit comes with six pieces of acrylic and six nylon screws and nuts.

## Prep

First, remove the protective paper from all of the acrylic pieces.

![circuitpython_untitled_0002_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/838/medium640/circuitpython_untitled_0002_2k.jpg?1552626396)

![circuitpython_untitled_0003_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/839/medium640/circuitpython_untitled_0003_2k.jpg?1552626410)

![circuitpython_untitled_0005_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/841/medium640/circuitpython_untitled_0005_2k.jpg?1552626428)

![circuitpython_untitled_0006_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/842/medium640/circuitpython_untitled_0006_2k.jpg?1552626786)

![](https://cdn-learn.adafruit.com/assets/assets/000/072/848/medium800/circuitpython_untitled_0007_2k.jpg?1552626837)

## Sandwich

Next, do a dry fit of the three clear piece of acrylic on the back side of the PyPortal to get everything oriented properly.

The two small pieces are used as spacers to allow clearance around some of the larger parts. Lay them onto the board first, as shown.

Then, place the large clear piece on top, making sure to align the hole for the reset and the cutout for the three JST ports.

Complete the sandwich by placing the stack on top of the black front bezel with the hole for the light sensor oriented as shown here.

![circuitpython_untitled_0008_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/849/medium640/circuitpython_untitled_0008_2k.jpg?1552626923)

![circuitpython_untitled_0009_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/850/medium640/circuitpython_untitled_0009_2k.jpg?1552626937)

![circuitpython_untitled_0010_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/851/medium640/circuitpython_untitled_0010_2k.jpg?1552626948)

![circuitpython_untitled_0011_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/852/medium640/circuitpython_untitled_0011_2k.jpg?1552627052)

![circuitpython_untitled_0012_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/853/medium640/circuitpython_untitled_0012_2k.jpg?1552627067)

![circuitpython_untitled_0013_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/854/medium640/circuitpython_untitled_0013_2k.jpg?1552627104)

![circuitpython_untitled_0014_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/855/medium640/circuitpython_untitled_0014_2k.jpg?1552627173)

![](https://cdn-learn.adafruit.com/assets/assets/000/072/860/medium800/circuitpython_untitled_0016_2k.jpg?1552627208)

## Legs

Now that the fit and orientation have been established, we'll install the legs.

The two legs are identical. Pick one and slot it into the case back as shown.

Place a nut into the captive slot of the leg and then feed a short screw through from the front of the clear acrylic case back. Fasten the screw (not too tight!) and then repeat for the second leg.

&nbsp;

![circuitpython_untitled_0031_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/869/medium640/circuitpython_untitled_0031_2k.jpg?1552628953)

![circuitpython_untitled_0032_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/870/medium640/circuitpython_untitled_0032_2k.jpg?1552631665)

![circuitpython_untitled_0033_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/871/medium640/circuitpython_untitled_0033_2k.jpg?1552631675)

![circuitpython_untitled_0034_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/872/medium640/circuitpython_untitled_0034_2k.jpg?1552631683)

![circuitpython_untitled_0035_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/873/medium640/circuitpython_untitled_0035_2k.jpg?1552631690)

![](https://cdn-learn.adafruit.com/assets/assets/000/072/883/medium800/circuitpython_untitled_0036_2k.jpg?1552631725)

## Add Long Screws

To put it all together, we'll use the four long screws to secure the entire acrylic - PyPortal - acrylic - acrylic sandwich!

Run the four long screws from the front to the back, as shown.

![circuitpython_untitled_0039_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/892/medium640/circuitpython_untitled_0039_2k.jpg?1552631888)

![circuitpython_untitled_0040_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/893/medium640/circuitpython_untitled_0040_2k.jpg?1552631983)

![circuitpython_untitled_0041_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/894/medium640/circuitpython_untitled_0041_2k.jpg?1552631994)

![circuitpython_untitled_0042_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/895/medium640/circuitpython_untitled_0042_2k.jpg?1552632010)

![circuitpython_untitled_0043_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/896/medium640/circuitpython_untitled_0043_2k.jpg?1552632017)

![circuitpython_untitled_0044_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/897/medium640/circuitpython_untitled_0044_2k.jpg?1552632026)

![](https://cdn-learn.adafruit.com/assets/assets/000/072/907/medium800/circuitpython_untitled_0046_2k.jpg?1552632076)

## Screw It All Together

Finally, add the case back and legs assemblage and then thread on the four nuts to secure it all in place.

Be **careful not to over-tighten** the screws. Doing so can potentially crack the Pyportal display!

![circuitpython_untitled_0047_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/922/medium640/circuitpython_untitled_0047_2k.jpg?1552632800)

![circuitpython_untitled_0049_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/923/medium640/circuitpython_untitled_0049_2k.jpg?1552632809)

![circuitpython_untitled_0052_2k.jpg](https://cdn-learn.adafruit.com/assets/assets/000/072/924/medium640/circuitpython_untitled_0052_2k.jpg?1552632817)

![](https://cdn-learn.adafruit.com/assets/assets/000/072/938/medium800/circuitpython_untitled_0053_2k.jpg?1552632866)

![](https://cdn-learn.adafruit.com/assets/assets/000/072/958/medium800/circuitpython_untitled_0057_2k.jpg?1552632938)

![](https://cdn-learn.adafruit.com/assets/assets/000/073/004/medium800/circuitpython_untitled_0059_2k.jpg?1552633061)

![](https://cdn-learn.adafruit.com/assets/assets/000/073/050/medium800/circuitpython_untitled_0060_2k.jpg?1552633241)

## Bonus! Penny Roll Weight

If you'd like to give your PyPortal a bit of extra heft so it won't get pushed around on your desk, you can make a great weight for $0.50. A roll of 50 pennies does the trick! The legs are designed to hold a roll of coins perfectly!

![](https://cdn-learn.adafruit.com/assets/assets/000/073/087/medium800/circuitpython_untitled_0063_2k.jpg?1552633546)

![](https://cdn-learn.adafruit.com/assets/assets/000/073/112/medium800/circuitpython_untitled_0065_2k.jpg?1552633618)

![](https://cdn-learn.adafruit.com/assets/assets/000/073/131/medium800/circuitpython_untitled_0067_2k.jpg?1552633666)

# Laser Cutter Files for PyPortal Stand

If you need to replace a piece or just want to make a spare for another PyPortal, here are the vector files for 1/8" (3mm) acrylic, in Adobe Illustrator format:

[pyPortal_CUT_Black.ai](https://cdn-learn.adafruit.com/assets/assets/000/073/959/original/pyPortal_CUT_Black.ai?1554157488)
[pyPortal_CUT_Clear.ai](https://cdn-learn.adafruit.com/assets/assets/000/073/960/original/pyPortal_CUT_Clear.ai?1554157498)
# Adafruit PyPortal - IoT for CircuitPython

## Updating ESP32 Firmware

There may come a time when you want to update the firmware on the ESP32 itself. This isn't something we expect you'll do often if at all, but its good to know how if you need to.

[We have a guide here which details the process of updating the ESP32 firmware on Airlift All-in-One boards (including the PyPortal, MatrixPortal, and Metro M4 AirLift) here...](https://learn.adafruit.com/upgrading-esp32-firmware/upgrade-all-in-one-esp32-airlift-firmware)

# Adafruit PyPortal - IoT for CircuitPython

## Parsing JSON

## Parsing JSON from the Web
[Here an example of how you can display text data from the web with PyPortal, by making an internet-connected quote book.](https://learn.adafruit.com/pyportal-adafruit-quote-board/overview)

![](https://cdn-learn.adafruit.com/assets/assets/000/085/280/medium800/circuitpython_image.png?1575416214)

## Installing Project Code

To use with CircuitPython, you need to first install a few libraries, into the lib folder on your **CIRCUITPY** drive. Then you need to update **code.py** with the example script.

Thankfully, we can do this in one go. In the example below, click the **Download Project Bundle** button below to download the necessary libraries and the **code.py** file in a zip file. Extract the contents of the zip file, open the directory **PyPortal\_Quotes/** and then click on the directory that matches the version of CircuitPython you're using and copy the contents of that directory to your **CIRCUITPY** drive.

Your **CIRCUITPY** drive should now look similar to the following image:

![CIRCUITPY](https://adafruit.github.io/Adafruit_Learning_System_Guides/PyPortal_Quotes.png )

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

### JSON

The neat part is that the text is not coming from a file on the device (see how to do this next), but rather it is grabbed from a website!

Adafruit.com has a PHP script at the&nbsp; **adafruit.com/api/quotes.php** &nbsp;page. Each time it is requested, it returns a new quote from a large database of quotes.

In fact, you can run the same query the PyPortal does to see the results. Copy and paste this link:&nbsp;`https://www.adafruit.com/api/quotes.php`

into your browser and you'll see a result like this:

```
[
  {
    "text": "Science, my lad, is made up of mistakes, but they are mistakes which it is useful to make, because they lead little by little to the truth",
    "author": "Jules Verne"
  }
]
```
That result is the quote formatted as a&nbsp; **JSON&nbsp;** (JavaScript Object Notation) array. It is comprised of a single element with two keys:&nbsp; **text** &nbsp;and&nbsp; **author**.

- The value of the&nbsp; **text** &nbsp;key is&nbsp;`Science, my lad, is made up of mistakes, but they are mistakes which it is useful to make, because they lead little by little to the truth`
- The value of the&nbsp; **author** &nbsp;key is&nbsp;`Jules Verne`

Since this JSON object has a consistent way to return the results to us, the code we're running on the PyPortal can easily parse the data and display it!

You can see how it's done in this part of&nbsp; **code.py** :

```python
# Set up where we'll be fetching data from
DATA_SOURCE = "https://www.adafruit.com/api/quotes.php"
QUOTE_LOCATION = [0, 'text']
AUTHOR_LOCATION = [0, 'author']
```

Then, in the&nbsp;`pyportal`&nbsp;query we ask for the&nbsp; **text** &nbsp;and&nbsp; **author** &nbsp;name from that URL, and then use the&nbsp;`text_`&nbsp;arguments to set the&nbsp;`font`,&nbsp;`position`,&nbsp;`color`,&nbsp;`wrap`, and&nbsp;`maxlen`&nbsp;of the text when it is displayed.

```python
pyportal = PyPortal(url=DATA_SOURCE,
                    json_path=(QUOTE_LOCATION, AUTHOR_LOCATION),
                    status_neopixel=board.NEOPIXEL,
                    default_bg=cwd+"quote_background.bmp",
                    text_font=cwd+"fonts/Arial-ItalicMT-17.bdf",
                    text_position=((20, 40),  # quote location
                                   (5, 190)), # author location
                    text_color=(0xFFFFFF,  # quote text color
                                0x8080FF), # author text color
                    text_wrap=(35, # characters to wrap for quote
                               0), # no wrap for author
                    text_maxlen=(180, 30), # max text size for quote &amp; author
                    )
```

With all of this prepared, during the main loop of&nbsp;`while True:`&nbsp;the code will query the Adafruit quotes page for the JSON data, and display it, and then wait one minute until repeating the process.

## Parsing local JSON files&nbsp;

If you would like to avoid pulling data from a web page or maybe you can't get access to a specific API key, you can use a "local" JSON file to pull data from.

To implement this local data sourcing method, create a new file and name it&nbsp;`local.txt`. Populate this file with the JSON data that you would like to use. For example, you could use the JSON data provided above and make sure the format of the data is the same. Save this file on the CIRCUITPY drive in the root.

**You should not need to change anything in your code.**

And that's it! The JSON data will now be pulled from this local file!

# Adafruit PyPortal - IoT for CircuitPython

## PyPortal Hardware FAQ

For CircuitPython-specific issues, see the [CircuitPython software FAQ](https://learn.adafruit.com/adafruit-pyportal/frequently-asked-questions).

### 

In shipping, the connector for the display may pop the retention tab(s) (red circles below). If you see one that is not in the position below, check to see if the orange display ribbon cable is straight. If so, gently push the tab down towards the main connector.

If you see the ribbon cable crooked, pop both tabs and slide the cable gently so it is in the connector like the picture and straight. then clip each side down.

Repower the PyPortal afterwards and hopefully you'll see the CircuitPython boot text on the screen.

![](https://cdn-learn.adafruit.com/assets/assets/000/072/250/medium800/circuitpython_tabs.jpg?1551738118)

### 

If you look at the pads circled in yellow below, there is a tiny connection between the V and 5 pads indicating a default of 5V power, which may cause issue in certain setups. If you are connecting one our sensors and seeing boot or other issues, try changing the voltage to 3.3V

If you want 3.3 volt power for your I2C connector, carefully cut that tiny trace between V and 5, then using a soldering iron connect the 3 and V pads. The PyPortal was not made to switch often between these two values so double check your I2C data sheets, some sensors can take 3.3 and 5V power so leaving it at 5 should be ok.

![](https://cdn-learn.adafruit.com/assets/assets/000/072/251/medium800/circuitpython_i2c.jpg?1551738127)

### 

If you're on Linux, and are seeing multi-second delays connecting to the serial console, or are seeing "AT" and other gibberish when you connect, then the modemmanager service might be interfering. Just remove it; it doesn't have much use unless you're still using dial-up modems. To remove, type this command at a shell:

`sudo apt purge modemmanager`

### 

Once your PyPortal boots up and successfully loads your **code.py** or **main.py,** the status NeoPixel will turn green briefly. Then, the NeoPixel will show one of the following color codes to indicate the status of the WiFi connection/activity or file operations:

- Red = not connected to WiFi
- Blue = connected to WiFi
- Yellow = fetching data
- Green = got data
- Cyan = file opening

 **My PyPortal Pynt JST STEMMA sockets arent working!**

The PyPortal Pynt has the D3 and D4 sockets mislabeled, they should be swapped (to match the pyportal classic)

# Adafruit PyPortal - IoT for CircuitPython

## Downloads

## Files

- [ATSAMD51J20 datasheet](http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf)
- [ADT7410 datasheet](https://www.analog.com/media/en/technical-documentation/data-sheets/adt7410.pdf)
- [EagleCAD PCB files on GitHub](https://github.com/adafruit/Adafruit-PyPortal-PCB)
- [Fritzing object in Adafruit Fritzing Library](https://github.com/adafruit/Fritzing-Library/blob/master/parts/Adafruit%20PyPortal%20-%20CircuitPython%20Powered%20Internet%20Display.fzpz)
- [Laser Cutter Files for PyPortal Stand](https://learn.adafruit.com/adabox011/build-the-pyportal-stand#laser-cutter-files-for-pyportal-stand-23-21)
- [PyPortal 3D Models on GitHub](https://github.com/adafruit/Adafruit_CAD_Parts/tree/master/4116%20PyPortal)
- [PyPortal Pynt 3D Models on GitHub](https://github.com/adafruit/Adafruit_CAD_Parts/tree/master/4465%20PyPortal%20Pynt)
- [PDF for PyPortal Board Diagram on GitHub](https://github.com/adafruit/Adafruit-PyPortal-PCB/blob/master/Adafruit%20PyPortal%20Pinout.pdf)
- [PDF for PyPortal Pynt Board Diagram on GitHub](https://github.com/adafruit/Adafruit-PyPortal-PCB/blob/master/Adafruit%20PyPortal%20Pynt%20Pinout.pdf)

[SVG for PyPortal Board Diagram](https://cdn-learn.adafruit.com/assets/assets/000/111/281/original/Adafruit_PyPortal_Pinout.svg?1651521470)
[SVG for PyPortal Pynt Board Diagram](https://cdn-learn.adafruit.com/assets/assets/000/111/684/original/Adafruit_PyPortal_Pynt_Pinout.svg?1652296379)
[PyPorta Arcada self-test UF2](https://cdn-learn.adafruit.com/assets/assets/000/088/620/original/PyPortal_Arcada_Test.UF2?1582429720)
The links below are for older files that shipped with the Adabox 11 PyPortal or were for use with CircuitPython 5.x. The libraries within this zip are out of date and not compatible with the current version of CircuitPython! Neither will not run on the latest CircuitPython.

[Obsolete PyPortal CircuitPython 4.x Demo Files](https://github.com/adafruit/circuitpython-default-files/tree/main/boards/pyportal/4.x)
[Obsolete PyPortal CircuitPython 5.x Demo Files](https://cdn-learn.adafruit.com/assets/assets/000/090/931/original/PyPortal_5x_demo_files.zip?1588870430)
## PyPortal Schematic and Fab Print
![](https://cdn-learn.adafruit.com/assets/assets/000/071/996/medium800/circuitpython_Adafruit_PyPortal_Sch.png?1551215147)

![](https://cdn-learn.adafruit.com/assets/assets/000/071/997/medium800/circuitpython_Adafruit_PyPortal_Sch1.png?1551215174)

![](https://cdn-learn.adafruit.com/assets/assets/000/071/998/medium800/circuitpython_Adafruit_PyPortal_Sch2.png?1551215205)

![](https://cdn-learn.adafruit.com/assets/assets/000/072/000/medium800/circuitpython_Adafruit_PyPortal_Fab_Print.png?1551217085)

# PyPortal Pynt 3D Model
![](https://cdn-learn.adafruit.com/assets/assets/000/086/547/medium800thumb/circuitpython_pyportal-pynt.jpg?1577554629)

# PyPortal Pynt Schematic and Fab Print
![](https://cdn-learn.adafruit.com/assets/assets/000/086/087/medium800/circuitpython_PyPortal_Pynt_Sch_p1.png?1576794268)

![](https://cdn-learn.adafruit.com/assets/assets/000/086/088/medium800/circuitpython_PyPortal_Pynt_Sch_p2.png?1576794300)

![](https://cdn-learn.adafruit.com/assets/assets/000/086/089/medium800/circuitpython_PyPortal_Pynt_Sch_p3.png?1576794316)

![](https://cdn-learn.adafruit.com/assets/assets/000/086/090/medium800/circuitpython_PyPortal_Pynt_Fab_Print.png?1576794327)


## Primary Products

### Adafruit PyPortal - CircuitPython Powered Internet Display

[Adafruit PyPortal - CircuitPython Powered Internet Display](https://www.adafruit.com/product/4116)
 **PyPortal** , our easy-to-use IoT device that allows you to create all the things for the “Internet of Things” in minutes. Make custom touch screen interface GUIs, all open-source, and Python-powered using&nbsp;tinyJSON / APIs to get news, stock, weather, cat photos,...

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

## Featured Products

### Adafruit PyPortal Desktop Stand Enclosure Kit

[Adafruit PyPortal Desktop Stand Enclosure Kit](https://www.adafruit.com/product/4146)
PyPortal is&nbsp;our easy-to-use IoT device that allows you to create all the things for the “Internet of Things” in minutes. Create little pocket universes of joy that connect to something good.

And now that you've made a cool internet-connected project...

In Stock
[Buy Now](https://www.adafruit.com/product/4146)
[Related Guides to the Product](https://learn.adafruit.com/products/4146/guides)
### Adafruit PyPortal Pynt - CircuitPython Powered Internet Display

[Adafruit PyPortal Pynt - CircuitPython Powered Internet Display](https://www.adafruit.com/product/4465)
The **PyPortal Pynt** is the little&nbsp;sister to our [popular PyPortal](https://www.adafruit.com/product/4116) - zapped with a shrink ray to take the design from a 3.2" diagonal down to 2.4" diagonal screen - but otherwise the same! The PyPortal is&nbsp;our...

No Longer Stocked
[Buy Now](https://www.adafruit.com/product/4465)
[Related Guides to the Product](https://learn.adafruit.com/products/4465/guides)
### USB cable - USB A to Micro-B

[USB cable - USB A to Micro-B](https://www.adafruit.com/product/592)
This here is your standard A to micro-B USB cable, for USB 1.1 or 2.0. Perfect for connecting a PC to your Metro, Feather, Raspberry Pi or other dev-board or microcontroller

Approximately 3 feet / 1 meter long

Out of Stock
[Buy Now](https://www.adafruit.com/product/592)
[Related Guides to the Product](https://learn.adafruit.com/products/592/guides)
### Adafruit NeoPixel LED Strip with 3-pin JST PH 2mm Connector

[Adafruit NeoPixel LED Strip with 3-pin JST PH 2mm Connector](https://www.adafruit.com/product/3919)
Plug in and glow, this **Adafruit NeoPixel LED Strip with JST PH Connector** has **30 total LEDs** in a **"60 LED per meter" spacing** , and is 0.5 meters long, in classy Adafruit Black. Its not too long, but has lots of glowing action.<a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/3919)
[Related Guides to the Product](https://learn.adafruit.com/products/3919/guides)
### JST PH 2mm 3-pin Plug to Color Coded Alligator Clips Cable

[JST PH 2mm 3-pin Plug to Color Coded Alligator Clips Cable](https://www.adafruit.com/product/4030)
This cable will let you turn a JST PH 3-pin cable port into 3 individual wires with grippy mini alligator clips. We're carrying these to match up with any of our boards or breakouts with 3-pin 'STEMMA' connectors on them such as the&nbsp;<a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/4030)
[Related Guides to the Product](https://learn.adafruit.com/products/4030/guides)
### STEMMA JST PH 2mm 3-Pin to Male Header Cable - 200mm

[STEMMA JST PH 2mm 3-Pin to Male Header Cable - 200mm](https://www.adafruit.com/product/3893)
This cable will let you turn a JST PH 3-pin cable port into 3 individual wires with high-quality 0.1" male header plugs on the end. We're carrying these to match up with our Hallowing, for extending and connecting sensors or LEDs - and the wires are even color coded!

<a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/3893)
[Related Guides to the Product](https://learn.adafruit.com/products/3893/guides)
### JST PH 2mm 4-pin Plug to Color Coded Alligator Clips Cable

[JST PH 2mm 4-pin Plug to Color Coded Alligator Clips Cable](https://www.adafruit.com/product/4029)
This cable will let you turn a JST PH 4-pin cable port into 4 individual wires with grippy mini alligator clips. We're carrying these to match up with any of our boards or breakouts with 'STEMMA' connectors on them such as the&nbsp;<a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/4029)
[Related Guides to the Product](https://learn.adafruit.com/products/4029/guides)
### STEMMA JST PH 2mm 3-Pin to Female Socket Cable - 200mm

[STEMMA JST PH 2mm 3-Pin to Female Socket Cable - 200mm](https://www.adafruit.com/product/3894)
This cable will let you turn a JST PH 3-pin cable port into 3 individual wires with high-quality 0.1" female header sockets on the end. We're carrying these to match up with our Hallowing, for extending and connecting sensors or LEDs - and the wires are even color coded!

<a...></a...>

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

## Related Guides

- [MQTT in CircuitPython](https://learn.adafruit.com/mqtt-in-circuitpython.md)
- [PyPortal Twitter Follows Trophy](https://learn.adafruit.com/pyportal-twitter-follows-trophy.md)
- [No-Code Rain Sensing Smart Desktop Umbrella Stand](https://learn.adafruit.com/no-code-rain-sensing-smart-desktop-umbrella-stand.md)
- [PyPortal Oblique Strategies](https://learn.adafruit.com/pyportal-oblique-strategies.md)
- [PyPortal Event Count-Up Clock](https://learn.adafruit.com/pyportal-event-count-up-clock.md)
- [Pathfinder Robot Companion](https://learn.adafruit.com/pathfinder.md)
- [Use circup to easily keep your CircuitPython libraries up to date](https://learn.adafruit.com/keep-your-circuitpython-libraries-on-devices-up-to-date-with-circup.md)
- [Making a PyPortal User Interface with DisplayIO](https://learn.adafruit.com/making-a-pyportal-user-interface-displayio.md)
- [¡Bienvenido a CircuitPython!](https://learn.adafruit.com/bienvenido-a-circuitpython-2.md)
- [PyPortal IoT Plant Monitor with Microsoft Azure IoT and CircuitPython](https://learn.adafruit.com/using-microsoft-azure-iot-with-circuitpython.md)
- [Adafruit NeoPixel Driver BFF](https://learn.adafruit.com/adafruit-neopixel-driver-bff.md)
- [PyPortal Email Display with Zapier and Adafruit IO](https://learn.adafruit.com/pyportal-email-display.md)
- [Upgrading AirLift ESP32 Firmware](https://learn.adafruit.com/upgrading-esp32-firmware.md)
- [PyGamer NeoPixel Controller](https://learn.adafruit.com/pygamer-neopixel-controller.md)
- [PyPortal US Election Calendar](https://learn.adafruit.com/pyportal-electioncal-us.md)
