# Where's My Friend? A Location-Aware Display with PyPortal and ItsASnap

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/131/698/medium800/adafruit_products_BEE9B909-86F5-4C23-A449-240118CC450A_1_201_a.jpeg?1722954824)

_Where's my friend?_ **Build this simple IoT project to display a friend's or loved one's location on an Adafruit PyPortal**.&nbsp;

Using [Adafruit's ItsaSnap iOS app](https://learn.adafruit.com/it-s-a-snap-by-adafruit) and Apple Shortcuts, **you can send a pre-selected GPS location from an iPhone to a shared (private) [Adafruit IO](https://learn.adafruit.com/welcome-to-adafruit-io) feed**. **The PyPortal** connects to the internet, fetches a location value of a shared feed, and **displays the location on the screen along with a corresponding icon and text**.

### About this project, your privacy, and Adafruit IO

**Adafruit IO's data feeds are_&nbsp;_private&nbsp;(not viewable by others) _by default_**&nbsp; **and** &nbsp; **_secure_** &nbsp;([we will never sell or give this data away to another company](https://www.adafruit.com/iotbor/)).

This project uses the ItsaSnap iOS App to bridge Adafruit IO and Apple Shortcuts. Apple Shortcuts utilizes your iPhone's GPS (with permission) to write a string of text (i.e.: "park" or "work") to identify where a friend is. It does not send specific GPS location data to the Adafruit IO servers.

The data sent by the Shortcuts app is stored on a _private_ Adafruit IO data feed, only viewable by you and the friend sharing their location with you.&nbsp;

### ItsaSnap for iOS

It's a Snap (ItsaSnap) is a simple iOS app designed for exploring [Adafruit IO](https://io.adafruit.com/). It allows you to send data to your&nbsp;[Adafruit IO feeds](https://learn.adafruit.com/adafruit-io-basics-feeds). You can also link it to control or monitor your internet-connected projects. This lets you control and check Adafruit devices from your phone, anywhere.&nbsp;

It also allows us to use Apple's Shortcuts app to create a code-free automation from your iOS device.

![adafruit_products_adafruit_io_ezgif.com-optijpeg.png](https://cdn-learn.adafruit.com/assets/assets/000/131/683/medium640/adafruit_products_adafruit_io_ezgif.com-optijpeg.png?1722626934)

## Parts

The following products and parts are required to complete this project:

**Note:** The code in this guide is designed for the _original_ Adafruit PyPortal. If you have the [PyPortal Pynt](https://www.adafruit.com/product/4465) or [PyPortal Titano](https://www.adafruit.com/product/4444), you can still complete this project, but you will need to adjust some of the code to accommodate the Titano/Pynt's different display sizes.

### 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,...

In Stock
[Buy Now](https://www.adafruit.com/product/4116)
[Related Guides to the Product](https://learn.adafruit.com/products/4116/guides)
![Front view of a Adafruit PyPortal - CircuitPython Powered Internet Display with a pyportal logo image on the display. ](https://cdn-shop.adafruit.com/640x480/4116-00.jpeg)

### Part: USB cable - USB A to Micro-B
quantity: 1
USB cable - USB A to Micro-B - 3 foot long
[USB cable - USB A to Micro-B](https://www.adafruit.com/product/592)

The following parts are not required to complete this project but are nice to have.

### 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)
![Demo Shot of the Assembled Adafruit PyPortal Desktop Stand Enclosure Kit.](https://cdn-shop.adafruit.com/640x480/4146-03.jpg)

### Part: Adafruit IO+ One Year Subscription
quantity: 1
Adafruit IO+ Subscription Pass – One Year
[Adafruit IO+ One Year Subscription](https://www.adafruit.com/product/3792)

# Where's My Friend? A Location-Aware Display with PyPortal and ItsASnap

## Get Started with Adafruit IO

Adafruit IO is integrated with your&nbsp;[adafruit.com account](https://accounts.adafruit.com/)&nbsp;so you don't need to create yet another online account! You need an Adafruit account to use Adafruit IO because we want to make sure the data you upload is available to only you (unless you decide to publish your data).

## I have an Adafruit.com Account already

**If you already have an Adafruit account, then you already have access to Adafruit IO**. It doesn't matter how you signed up, your account will make all three available.

To access Adafruit IO, simply visit [https://io.adafruit.com](https://io.adafruit.com) to start streaming, logging, and interacting with your data.

## Create an Adafruit Account (for Adafruit IO)

An Adafruit account makes Adafruit content and services available to you in one place. Your account provides access to the [Adafruit shop](https://www.adafruit.com/), the [Adafruit Learning System](https://learn.adafruit.com/), and [Adafruit IO](https://io.adafruit.com/). This means only one account, one username, and one password are necessary to engage with the content and services that Adafruit offers.

If you do not have an Adafruit account, signing up for a new Adafruit account only takes a couple of steps.

Begin by visiting [https://accounts.adafruit.com](https://accounts.adafruit.com).

**Click the Sign Up button** under the "Need An Adafruit Account?" title, below the Sign In section.

![](https://cdn-learn.adafruit.com/assets/assets/000/125/220/medium800/adafruit_io_Create_account_sign_in_up_page.png?1697479894)

This will take you to the **Sign Up** page.

**Fill in the requested information,** and **click the Create Account button.**

![](https://cdn-learn.adafruit.com/assets/assets/000/125/219/medium800/adafruit_io_Create_Account_info_entered.png?1697479894)

This takes you to your Adafruit Account home page. From here, you can access all the features of your account.

You can also access the Adafruit content and services right from this page. Along the top of the page, you'll see a series of links beginning with "Shop". To access any of these, simply click the link.

![](https://cdn-learn.adafruit.com/assets/assets/000/125/217/medium800/adafruit_io_Create_account_home_page.png?1697479894)

For example, **to begin working with Adafruit IO, click the IO link** to the right of the rest of the links. This is the same for the other links as well.

That's all there is to creating a new Adafruit account, and navigating to Adafruit IO.

![](https://cdn-learn.adafruit.com/assets/assets/000/125/218/medium800/adafruit_io_Create_Account_io_homepage.png?1697479770)

# Where's My Friend? A Location-Aware Display with PyPortal and ItsASnap

## Create a Shared Adafruit IO Feed

## Create a new Shared Adafruit IO Feed

Adafruit IO's feed-sharing feature allows you to invite someone to view (or write to) a specific feed. This is a great feature if you're building a project but don't want to make your feed public to strangers, only your friends.&nbsp;In this project, the person sharing their location will create a shared feed and invite the person with the PyPortal to view it.

In a web browser, navigate to [io.adafruit.com/feeds](https://io.adafruit.com/feeds) and **click "+ New Feed"**

![](https://cdn-learn.adafruit.com/assets/assets/000/131/655/medium800/adafruit_products_newfeed.png?1722611745)

Give your new feed a name. The description is optional. Click&nbsp; **Create** to create the new feed.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/656/medium800/adafruit_products_iofeedslocation.png?1722611982)

Under _My Feeds_, you should see the&nbsp;_location_ feed you created. Click the link to be brought to the Feed's page.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/657/medium800/adafruit_products_location-feed-click.png?1722612064)

On the feed page, click the&nbsp; **Sharing&nbsp;** text on the sidebar

![](https://cdn-learn.adafruit.com/assets/assets/000/131/658/medium800/adafruit_products_location-feed-sharing.png?1722613381)

You'll be greeted with the S **haring Settings** &nbsp;window.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/662/medium800/adafruit_products_sharingsettings.png?1722613534)

You can invite somebody you know using their Adafruit IO Username or email address.&nbsp;You'll have two options for controlling someone's access to your feed:

1. If you give someone&nbsp;_Read_&nbsp;access, they can&nbsp;_view_&nbsp;the feed you share.
2. If you give someone _Read + Write_ access, they can&nbsp;_read_&nbsp;_from_&nbsp;your feed and&nbsp;_write to_&nbsp;your feed.&nbsp;

For this project, the PyPortal only needs to display the feed's last value.

Enter your friend's email or Adafruit IO username, give them the&nbsp;_Read_ Access Level, and **click Send Invitation**.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/661/medium800/adafruit_products_adafruit_io_invite.png?1722613529)

## Accepting a Shared Feed Request

Now it's the other person's turn. After you click&nbsp; **Send Invitation** , they should receive an email from Adafruit IO informing them that you've shared a feed. After they've reviewed and accepted it, move on to the next page.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/664/medium800/adafruit_products_adafruit_io_brent_has_shared_a_feed_with_you_on_Adafruit_IO__-_brentrubell_gmail_com_-_Gmail.png?1722613871)

# Where's My Friend? A Location-Aware Display with PyPortal and ItsASnap

## Download the ItsaSnap by Adafruit App

This section will guide you through downloading and installing the ItsaSnap by Adafruit app.

## Download and Install ItsaSnap

To install and download ItsaSnap for your iOS device,

1. **Open the App Store**.
2. **Tap** on the **"Search" icon** at the bottom of the screen.
3. **Type "ItsaSnap by Adafruit" or "ItsaSnap"** in the search bar and **tap "Search".**
4. Locate the **ItsaSnap**** &nbsp;app** in the search results and tap on it

![](https://cdn-learn.adafruit.com/assets/assets/000/130/658/medium800/adafruit_io_IMG_5711-ezgif.com-resize-2.jpg?1718123186)

&nbsp; &nbsp; 5. **Tap** the **"Get" button** to download and install the app on your device.&nbsp;

You may be prompted to enter your Apple ID password or use Face ID/Touch ID to confirm the installation

![](https://cdn-learn.adafruit.com/assets/assets/000/130/659/medium800/adafruit_io_IMG_5711-ezgif.com-resize-crop.jpg?1718123294)

After you have downloaded and installed ItsaSnap, open the app.

![](https://cdn-learn.adafruit.com/assets/assets/000/130/637/medium800/adafruit_io_adaSnap5-ezgif.com-resize.png?1718070937)

You will need to enter your&nbsp; **Adafruit username** and **Adafruit IO Key** (which is _different_ from your Adafruit account password).

Your Adafruit IO key is a long string of letters and numbers. To make it easier to add to the app, we've also included a QR code scanner that allows you to easily copy the Adafruit IO Key from the Adafruit IO website to the app.

![](https://cdn-learn.adafruit.com/assets/assets/000/130/660/medium800/adafruit_io_qrbuttonscan.png?1718123519)

On the ItaSnap app, the QR code scanner is located below the Adafruit IO Key and above the login button. To access it, press the ' **Scan QR code for IO Key**' button. This will open the QR code scanner.

![](https://cdn-learn.adafruit.com/assets/assets/000/130/662/medium800/adafruit_io_IMG_5702-ezgif.com-crop.png?1718123632)

To find the QR code for your Adafruit IO account, [navigate go to the overview page](https://io.adafruit.com/overview). Once there, click the yellow button with a key in the center (it's next to the "New Device" button) to reveal your Adafruit IO Key.&nbsp;

![](https://cdn-learn.adafruit.com/assets/assets/000/130/640/medium800/adafruit_io_Screenshot_2024-06-10_at_9.42.20%E2%80%AFPM.png?1718072559)

After clicking it, a window with your information will appear. Then, find the QR code and scan it, and log in.

![](https://cdn-learn.adafruit.com/assets/assets/000/130/653/medium800/adafruit_io_Pasted_Image_6_11_24__11_55_AM.png?1718121312)

# Where's My Friend? A Location-Aware Display with PyPortal and ItsASnap

## Set Up Apple Shortcuts 

Info: Apple Shortcuts are only available in the latest version of It'saSnap By Adafruit.

ItsASnap includes several actions for the Apple Shortcuts App, allowing you to integrate Adafruit IO functionality. This project focuses on sending specific location data (only sending data when you're at a fixed location, like a park or the train station) from your iPhone to a shared Adafruit IO feed.&nbsp;

This step will demonstrate how to use ItsASnap with Apple Shortcuts to send a value to an Adafruit IO feed when you arrive at a location - in this instance, we'll choose a park.&nbsp;

## Create a new Shortcuts Automation

Launch Apple Shortcuts.&nbsp;To create a new automation, go to the automation screen and tap the "+" button.&nbsp;

![](https://cdn-learn.adafruit.com/assets/assets/000/131/665/medium800/adafruit_products_adafruit_io_IMG_6177.jpg?1722614884)

Here you can select which types of triggers can initiate an action. For now, we'll use "Arrive" to set a location where the action will be triggered.

**Select "Arrive"** as the trigger for the automation. This trigger requires a specific location to initiate the automation process.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/668/medium800/adafruit_products_arrive.png?1722614979)

We'll want to track when we're at work. Add the work location (I work for Adafruit so I added the Adafruit HQ) into the location field; it will then be displayed on the map.&nbsp;

Additionally, you can configure the radius of the location to specify where you want the trigger to activate.

Once you're all set - tap&nbsp;_Done_.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/728/medium800/adafruit_products_IMG_1494.png?1723047867)

Configure your **When** action is configured to execute at any time and run immediately. Tap&nbsp;_Next_.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/729/medium800/adafruit_products_timerange.png?1723047917)

On the next step, tap _New Blank Automation_ to configure ItsaSnap.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/730/medium800/adafruit_products_IMG_1498.png?1723048098)

Within the Blank Automation tab, use the search bar to search for _ItsaSnap.&nbsp;_Then, select the&nbsp;_Send Value_ shortcut.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/731/medium800/adafruit_products_search_select.png?1723048286)

Select the&nbsp;_Value&nbsp;_by tapping on it. Enter&nbsp;_Work_&nbsp;for the value.&nbsp;

![adafruit_products_IMG_1500_2.png](https://cdn-learn.adafruit.com/assets/assets/000/131/732/medium640/adafruit_products_IMG_1500_2.png?1723048419)

![adafruit_products_work-feed.png](https://cdn-learn.adafruit.com/assets/assets/000/131/733/medium640/adafruit_products_work-feed.png?1723048475)

Set the Feed value to the Feed's Key and tap Done.

![adafruit_products_feedloc.png](https://cdn-learn.adafruit.com/assets/assets/000/131/736/medium640/adafruit_products_feedloc.png?1723048880)

![adafruit_products_feeddone.png](https://cdn-learn.adafruit.com/assets/assets/000/131/737/medium640/adafruit_products_feeddone.png?1723048908)

Once done, you should be able to view the automation.

## Add more Shortcuts for Different Locations

While adding one automation for the park is useful - there are other places your friend goes during his day like work, home, and commuting via the NYC Subway.

You can repeat the process above to add more shortcuts for different locations. For each location shortcut you configure, ensure the feed being written to is the _location_ feed.

For this guide, we created the following four automation using the following settings:

- The value _home_ to the _location_ feed when we've arrived home.
- The value&nbsp;_work_ to the _location_ feed when we've arrived at work
- The value _commute_ to the&nbsp;_location_ feed when we've left work.
- The value _gym_ to the _location_ feed when we've arrived at the park to workout.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/738/medium800/adafruit_products_automation-list.png?1723049152)

# Where's My Friend? A Location-Aware Display with PyPortal and ItsASnap

## 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.

# Where's My Friend? A Location-Aware Display with PyPortal and ItsASnap

## 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.

# Where's My Friend? A Location-Aware Display with PyPortal and ItsASnap

## 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)
# Where's My Friend? A Location-Aware Display with PyPortal and ItsASnap

## Code with CircuitPython

Once you've finished setting up your PyPortal with CircuitPython and have [connected to the internet](https://learn.adafruit.com/where-s-my-friend-a-location-display-frame-with-pyportal/internet-connect), you can access the code and necessary libraries by downloading the Project Bundle.

To do this, click the **&nbsp;Download Project Bundle** &nbsp;button in the window below. It will download to your computer as a zipped folder.

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

## Upload the Code and Libraries to the PyPortal

After downloading the Project Bundle, plug your PyPortal into the computer's USB port with a known good USB data + power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called&nbsp; **CIRCUITPY**. Unzip the folder and copy the following items to the&nbsp; **CIRCUITPY** &nbsp;drive.

- **lib** &nbsp;folder
- **images&nbsp;** folder
- **code.py**

The&nbsp; **CIRCUITPY** &nbsp;drive should look like this after copying the&nbsp; **lib** &nbsp;folder, **images** folder,&nbsp;and the&nbsp; **code.py** &nbsp;file.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/686/medium800/adafruit_products_drive_contents.png?1722627382)

## How the CircuitPython Code Works

At the top of the code, we import libraries used for this project and declare variables which you can use to customize your project:

- `IO_FEED_KEY` is the shared feed's name.
- `SLEEP_DELAY_SECONDS` is how often the code should fetch the location from the Adafruit IO feed, in seconds.
- `BACKLIGHT_BRIGHTNESS` configures the TFT backlight's brightness.
- `LOCATION_IMAGES` adds text and images for the location feed's values (more on this later!)

```auto
import time
import board
import displayio
import terminalio
import adafruit_imageload
from adafruit_display_shapes.rect import Rect
from adafruit_display_text import label
from adafruit_pyportal import PyPortal

# Adafruit IO shared feed key
IO_FEED_KEY = 'location'
# Fetch the location every 10 seconds
SLEEP_DELAY_SECONDS = 10
# Set the backlight brightness, 0.0 (off) to 1.0 (max brightness)
BACKLIGHT_BRIGHTNESS = 0.5
# Location text and images
LOCATION_IMAGES = { 'home': 'images/home.bmp', 'work': 'images/office.bmp', 'gym': 'images/workout.bmp', 'commute': 'images/subway.bmp' }
```

Next, the `PyPortal` object is created along with the white background, labels for the header and location text, and a group for the icon.

```auto
# Create the PyPortal object
pyportal = PyPortal(status_neopixel=board.NEOPIXEL)

# Configure the PyPortal's display
display = board.DISPLAY
display.rotation = 0
display.brightness = BACKLIGHT_BRIGHTNESS

# Calculate the text area and image sprite locations based on the PyPortal model
if IS_PYPORTAL_TITANO:
    TEXT_AREA_X = display.width // 5
    TEXT_AREA_Y = 25
    TEXT_AREA_LOCATION_X = (display.width // 3) - 2
    TEXT_AREA_LOCATION_Y = display.height - 25
    IMAGE_SPRITE_X = display.width // 3
    IMAGE_SPRITE_Y = display.height // 4
else: 
    TEXT_AREA_X = display.width // 5
    TEXT_AREA_Y = 25
    TEXT_AREA_LOCATION_X = (display.width // 3) - 2
    TEXT_AREA_LOCATION_Y = display.height - 25
    IMAGE_SPRITE_X = display.width // 3
    IMAGE_SPRITE_Y = display.height // 4

# Create a displayIO Group 
group = displayio.Group()

# Draw the background
bg_group = displayio.Group()
rect = Rect(0, 0, display.width, display.height, fill=0xFFFFFF)
bg_group.append(rect)
group.append(bg_group)

# Use the default font
font = terminalio.FONT

# Draw a label for the header text
text_area = label.Label(font, text="Where is My Friend?", color=0x000000, scale=2)
text_area.x = TEXT_AREA_X
text_area.y = TEXT_AREA_Y
group.append(text_area)

# Draw a label for the location text
text_area_location = label.Label(font, text="@ the park", color=0x000000, scale=3)
text_area_location.x = TEXT_AREA_LOCATION_X
text_area_location.y = TEXT_AREA_LOCATION_Y
group.append(text_area_location)

# Create a group for the icon only
icon_group = displayio.Group()
group.append(icon_group)

# Show the group
display.root_group = group
```

The `set_image()` method is from Richard Albritton's [Making a PyPortal User Interface with DisplayIO](https://learn.adafruit.com/making-a-pyportal-user-interface-displayio/images-2) guide. This method simplifies the process of switching between images from within the `while True` loop.

```auto
def set_image(group, filename):
    """Sets the image file for a given group for display."""
    print(f"Set image to {filename}")
    if group:
        group.pop()

    if not filename:
        return  # we're done, no icon desired
    try:
        if image_file:
            image_file.close
    except NameError:
        pass
    image_file = open(filename, "rb")
    image = displayio.OnDiskBitmap(image_file)
    image_sprite = displayio.TileGrid(image, pixel_shader=getattr(image, 'pixel_shader', displayio.ColorConverter()))
    image_sprite.x = IMAGE_SPRITE_X
    image_sprite.y = IMAGE_SPRITE_Y
    group.append(image_sprite)
```

Within the `while True` loop, we first fetch the Adafruit IO feed's most recent value.

```auto
print("Fetching location data...")
# Fetch the location data from Adafruit IO
feed = pyportal.get_io_feed(IO_FEED_KEY)
```

We check if the feed's value is associated with the list of locations, `LOCATION_IMAGES` and if the feed's value has changed since the last time it was fetched.

```auto
# If the location value is in the list of images
        if feed['last_value'] in LOCATION_IMAGES:
            # Check if the location has changed from the last time
            # we fetched the location
            if prv_location == feed['last_value']:
                print("Location has not changed!")
```

If the value of the location feed has changed, we print out the location feed. Then, we use the `set_image()` method to load the desired icon onto the screen.

```auto
else: # Location has changed
  print(f"Location: {feed['last_value']}")
  # Load the image for the current location
  set_image(icon_group, LOCATION_IMAGES[feed['last_value']])
```

Within `LOCATION_IMAGES` there is a dictionary of locations and associated images (in bitmap (bmp) format).

If you want to display different locations from the pre-selected ones - Change the dictionary's values and the image's file path.

```auto
LOCATION_IMAGES = { 'home': 'images/home.bmp', 'work': 'images/office.bmp', 'gym': 'images/workout.bmp', 'commute': 'images/subway.bmp' }
```

The text on the bottom of the screen is updated to display "@ [CURRENT LOCATION]" and the display is forced to refresh. Finally, we update the `prv_location` variable to store the current feed value.

```auto
# Update the location text
text_area_location.text=f"@ {feed['last_value']}"
# Show the refreshed group
display.root_group = group
# Update the previous location
prv_location = feed['last_value']
```

At the end of the `while True` loop, we wait 5 minutes before attempting to fetch the location feed again.

```auto
# Wait 5 minutes (300 seconds) before fetching the location feed again
print("Sleeping, fetching the location again in 5 minutes!")
time.sleep(SLEEP_DELAY_SECONDS)
```

# Where's My Friend? A Location-Aware Display with PyPortal and ItsASnap

## Usage

## Viewing the location on PyPortal

Plug the PyPortal into USB power. You should see the display change, reflecting the current value of the location feed.

If no value has been written to this feed yet, your PyPortal's screen will display "@ Unknown".

![](https://cdn-learn.adafruit.com/assets/assets/000/131/696/medium800/adafruit_products_IMG_1440.jpg?1722954643)

When your friend moves within the range of a pre-set location, the Automation will write the location's name to the shared Adafruit IO feed.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/687/medium800/adafruit_products_commute-loc.png?1722630755)

Every five minutes, the PyPortal's display will update with the current location. If their location hasn't changed, the display will display the last known location.

![](https://cdn-learn.adafruit.com/assets/assets/000/131/697/medium800/adafruit_products_BEE9B909-86F5-4C23-A449-240118CC450A_1_201_a.jpeg?1722954791)


## Featured 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,...

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

[Adafruit PyPortal Titano](https://www.adafruit.com/product/4444)
The **PyPortal Titano** is the big sister to our [popular PyPortal](https://www.adafruit.com/product/4116) now with _twice as many pixels!_ The PyPortal is our easy-to-use IoT device that allows you to create all the things for the “Internet of...

In Stock
[Buy Now](https://www.adafruit.com/product/4444)
[Related Guides to the Product](https://learn.adafruit.com/products/4444/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 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 IO+ Subscription Pass – One Year

[Adafruit IO+ Subscription Pass – One Year](https://www.adafruit.com/product/3792)
The all-in-one Internet of Things service from Adafruit you know and love is now _even better_ with IO+. The 'plus' stands for MORE STUFF! More feeds, dashboards, storage, speed. Power up your [Adafruit IO](https://io.adafruit.com/) with the $99 pass for 1 year of the...

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

## Related Guides

- [Adafruit PyPortal - IoT for CircuitPython](https://learn.adafruit.com/adafruit-pyportal.md)
- [Adafruit PyPortal Titano](https://learn.adafruit.com/adafruit-pyportal-titano.md)
- [Welcome to CircuitPython!](https://learn.adafruit.com/welcome-to-circuitpython.md)
- [Using LittlevGL with Adafruit Displays](https://learn.adafruit.com/using-littlevgl-with-adafruit-displays.md)
- [PyPortal Trivia Time with the Open Trivia Database](https://learn.adafruit.com/pyportal-trivia-time-open-trivia-database.md)
- [PyPortal IoT Plant Monitor with AWS IoT and CircuitPython](https://learn.adafruit.com/pyportal-iot-plant-monitor-with-aws-iot-and-circuitpython.md)
- [Adafruit IO Basics: Analog Output](https://learn.adafruit.com/adafruit-io-basics-analog-output.md)
- [CircuitPython Minesweeper Game](https://learn.adafruit.com/circuitpython-pyportal-minesweeper-game.md)
- [How to Add a New Board to WipperSnapper](https://learn.adafruit.com/how-to-add-a-new-board-to-wippersnapper.md)
- [A Logger for CircuitPython](https://learn.adafruit.com/a-logger-for-circuitpython.md)
- [Program CircuitPython USB Devices with iPhone & iPad](https://learn.adafruit.com/use-circuitpython-devices-with-iphone-ipad.md)
- [Scan QR Codes with CircuitPython](https://learn.adafruit.com/scan-qr-codes-with-circuitpython.md)
- [PyPortal Air Quality Display](https://learn.adafruit.com/pyportal-air-quality-display.md)
- [PyPortal Reddit Stats Trophy](https://learn.adafruit.com/pyportal-reddit-stats-trophy.md)
- [PyPortal US Election Calendar](https://learn.adafruit.com/pyportal-electioncal-us.md)
- [Tiny Museum Tour Device](https://learn.adafruit.com/tiny-museum-tour-device.md)
