Adafruit Industries Airlift Bitsy Add-On User manual

Adafruit Airlift Bitsy Add-On - ESP32 WiFi
Co-Processor
Created by Bryan Siepert
https://learn.adafruit.com/adafruit-airlift-bitsy-add-on-esp32-wifi-co-processor
Last updated on 2021-11-15 07:47:52 PM EST
©Adafruit Industries Page 1 of 54

5
7
8
8
10
11
12
12
13
13
14
15
16
20
22
23
23
25
27
27
28
28
30
30
30
31
31
31
33
35
35
36
38
40
43
44
44
44
45
47
50
50
51
Table of Contents
Overview
Pinouts
• Power Pins
• SPI and Control Pins
• RGB LED
Assembly
CircuitPython WiFi
• CircuitPython Microcontroller Pinout
• CircuitPython Installation of ESP32SPI Library
• CircuitPython Usage
Internet Connect!
• What's a secrets file?
• Connect to WiFi
• Requests
• HTTP GET with Requests
• HTTP POST with Requests
• Advanced Requests Usage
• WiFi Manager
OLD - CircuitPython WiFi
• CircuitPython Microcontroller Pinout
• CircuitPython Installation of ESP32SPI Library
• CircuitPython Usage
CircuitPython BLE
• CircuitPython BLE UART Example
• Update the AirLift Firmware
• Install CircuitPython Libraries
• Install the Adafruit Bluefruit LE Connect App
• Copy and Adjust the Example Program
• Talk to the AirLift via the Bluefruit LE Connect App
Arduino WiFi
• Library Install
• First Test
• WiFi Connection Test
• Secure Connection Example
• JSON Parsing Demo
• Adapting Other Examples
Upgrade External ESP32 Airlift Firmware
• External AirLift FeatherWing, Shield, or ItsyWing
• Upload Serial Passthrough code for Feather or ItsyBitsy
• External AirLift Breakout
• Code Usage
• Install esptool.py
• Burning nina-fw with esptool
©Adafruit Industries Page 2 of 54

©Adafruit Industries Page 4 of 54

Overview
Give your ItsyBitsy project a lift with the Adafruit AirLift Bitsy Add-On! This sweet add-
on for the ItsyBitsy lets you use the powerful ESP32 as a WiFi or BLE co-processor.
You probably have your favorite ItsyBitsy (like the ItsyBitsy M4(https://adafru.it/BQC))
that comes with its own set of awesome peripherals and lots of libraries. But it doesn't
have WiFi built in! So let's give that chip a best friend, the ESP32. This chip can handle
all the heavy lifting of connecting to a WiFi network and transferring data from a site,
even if its using the latest TLS/SSL encryption (it has root certificates pre-burned in).
©Adafruit Industries Page 5 of 54

Having WiFi managed by a separate chip means your code is simpler, you don't have
to cache socket data, or compile in & debug an SSL library. Instead the Airlift Add-On
allows you to send basic but powerful socket-based commands over 8MHz SPI for
high speed data transfer. You can use the 3V 32u4, M0, or M4 ItsyBitsy in Arduino
although the '32u4 will not be able to do very complex tasks or buffer a lot of databe
causethey do not have a lot of RAM. The add-on also works great with CircuitPython,
though a SAMD51/Cortex M4 minimum required since we need a bunch of RAM. All
you need is an SPI bus and 2 control pins plus a power supply that can provide up to
250mA during WiFi usage.
The ESP32 also supports BLE (Bluetooth Low Energy), though not simultaneously with
WiFi. Many of our CircuitPython builds include native support for ESP32 BLE. Right
now, we only support the ESP32 acting as a BLE peripheral, but that's sufficient to
communicate with the ESP32 from a phone, tablet, host computer, or another BLE-
capable board. You use a few control pins and the RX and TX pins to talk to the
ESP32 when it's in BLE mode.
We placed an ESP32 module on a BitsyWing with a tri-state chip for MOSI so you can
share the SPI bus. The add on comes fully assembled and tested, pre-programmed
with ESP32 SPI WiFi co-processor firmware thatyou can use in CircuitPython to use
this into WiFi co-processsor over SPI + 2 pins(https://adafru.it/Evl). We also toss in
some header so you can solder it in thoughyou'll need stacking headers for your
ItsyBitsy to put the add-on on top.
The firmware on board is a slight variant of the Arduino WiFiNINA core, which works
great!(https://adafru.it/E7O)
©Adafruit Industries Page 6 of 54

Pinouts
©Adafruit Industries Page 7 of 54

Power Pins
GND- Common power/logic ground.
3.3V - Power supply for the ublox WiFi module. You'll need up to 250mA to
power the module. ItsyBitsy's tend to have a 500mA regulator so they should be
OK, just make sure you're aware of your power budget
SPI and Control Pins
To keep transfers speedy, we use SPInot UART Serial.UART is too slow and hard to
synchronize. This uses more pins but the experience is much better!
Classic SPI Pins:
SCK- SPI Clock from your microcontroller,logic level is 3.3V only
MISO- SPI Datafromthe AirLifttothe microcontroller,logic level is 3.3V only.Th
is is tri-stated when not selected, so you can share the SPI bus with other
devices.
•
•
•
•
©Adafruit Industries Page 8 of 54

MOSI- SPI Datatothe AirLiftfromthe microcontroller,logic level is 3.3V only.
ESPCS- SPI Chip Select from the microcontroller to start sending commands to
the AirLift, and to choose BLE mode on reset, logic level is 3.3V only
Required Control Pins:
ESPBUSY- this pin is an input from the AirLift, it will let us know when its ready
for more commands to be sent. This is 3.3V logic out.This pinmustbe
connected.
ESPRST- this pin is an output to the AirLift. Set low to put the AirLift into reset.
You should use this pin, even though you might be able to run for a short while
without it, it's essential to 'kick' the chip if it ever gets into a locked up state.Lo
gic level is 3.3V
Optional Control Pins:
ESPGPIO0- this is the ESP32GPIO0pin, which is used to put the WiFi module it
into bootloading mode if you want to update the firmware. It is also used if you
like when the ESP32 is acting as a server, to let you know data is ready for
reading. It's not required in WiFi mode, but you'll need to connect it for BLE
mode. You'll need to solder the pad on the bottom of the Bitsy Add-on to
connect it.
ESPRX&ESPTX - Serial data in and Serial data out, used for bootloading new
firmware, and for communication when in BLE mode. Leave disconnected if not
using BLE or when not uploading new WiFi firmware to the AirLift (which is a
rare occurrence). You'll need to solder the two pads on the bottom of the Bitsy
Add-on to use these pins. Soldering the pads connects ESPTX to RX and ESPTX
to RX, respectively.
•
•
•
•
Warning! The initial batch has the silk screen labels for ESPBUSY and ESPRST
swapped! The order should be ECS/RST/BSY not ECS/BSY/RST. The schematic,
code, and fritzing object have been fixed. The silk will be corrected for the next
run
•
•
©Adafruit Industries Page 9 of 54

RGB LED
There is a small RGB LED to the left of the ESP32. These RGB LEDs are available in
the Arduino and CircuitPython libraries if you'd like to PWM them for a visual alert.
They're connected to the ESP32's pins 26 (Red), 25 (Green), and 27 (Blue).
©Adafruit Industries Page 10 of 54

Assembly
The antenna on the new, smaller ESP32 module is very delicate! Avoid touching
it and grab the add-on by the long sides to avoid smushing the antenna
©Adafruit Industries Page 11 of 54

To stack the Airlift Add-on on top of the
ItsyBitsy, you'll need stacking headers for
the ItsyBitsy. For now you'll need to cut
down a set of Feather Stacking
Headers(https://adafru.it/oAP) to fit the
ItsyBitsy however we will have headers
with the correctly length soon!
CircuitPython WiFi
It's easy to use the Adafruit AirLift breakout with CircuitPython and the Adafruit
CircuitPython ESP32SPI(https://adafru.it/DWV) module. This module allows you to
easily add WiFi to your project.
CircuitPython Microcontroller Pinout
Since all CircuitPython-running ItsyBitsies follow the same pinout, you do not need to
change any of the pins listed below.
To use the ESP32's pins, copy the following lines into your code:
esp32_cs = DigitalInOut(board.D13)
esp32_reset = DigitalInOut(board.D12)
esp32_ready = DigitalInOut(board.D11)
Then, include the following code to use the pin:
esp32_gpio0 = DigitalInOut(board.D10)
The ESP32SPI library requires a microcontroller with ~128KB of RAM or more.
The SAMD21 will not work.
©Adafruit Industries Page 12 of 54

CircuitPython Installation of ESP32SPI
Library
You'll need to install theAdafruit CircuitPython ESP32SPI(https://adafru.it/DWV)librar
y on your CircuitPython board.
First make sure you are running thelatest version of Adafruit CircuitPython(https://
adafru.it/Amd)for your board.
Next you'll need to install the necessary librariesto use the hardware--carefully follow
the steps to find and install these libraries fromAdafruit's CircuitPython library bundle
(https://adafru.it/uap). Our CircuitPython starter guide has a great page on how to
install the library bundle(https://adafru.it/ABU).
You can manually install the necessary libraries from the bundle:
adafruit_esp32spi
adafruit_requests.mpy
adafruit_bus_device
Before continuing make sure your board's lib folder or root filesystem has theadafrui
t_esp32spi, adafruit_requests.mpy, and adafruit_bus_devicefiles and folderscopied
over.
Nextmake sure you are set up to connect to the serial console(https://adafru.it/Bec)
CircuitPython Usage
Copy the following code to your code.py file on your microcontroller:
import board
import busio
from digitalio import DigitalInOut
from adafruit_esp32spi import adafruit_esp32spi
print("ESP32 SPI hardware test")
esp32_cs = DigitalInOut(board.D13)
esp32_reset = DigitalInOut(board.D12)
esp32_ready = DigitalInOut(board.D11)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
•
•
•
©Adafruit Industries Page 13 of 54

if esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
print("ESP32 found and in idle mode")
print("Firmware vers.", esp.firmware_version)
print("MAC addr:", [hex(i) for i in esp.MAC_address])
for ap in esp.scan_networks():
print("\t%s\t\tRSSI: %d" % (str(ap['ssid'], 'utf-8'), ap['rssi']))
print("Done!")
Connect to the serial console(https://adafru.it/BlO) to see the output. It should look
something like the following:
Make sure you see the same output! If you don't, check your wiring. Note that we've
changed the pinout in the code example above to reflect the CircuitPython
Microcontroller Pinout at the top of this page.
Once you've succeeded, continue onto the next page!
Internet Connect!
Once you have CircuitPython setup and libraries installed we can get your board
connected to the Internet. Note that access to enterprise level secured WiFi networks
is not currently supported, only WiFi networks that require SSID and password.
If you can read the Firmware and MAC address but fails on scanning SSIDs,
check your power supply, you may be running out of juice to the ESP32 and it's
resetting
©Adafruit Industries Page 14 of 54

To get connected, you will need to start by creating a secrets file.
What's a secrets file?
We expect people to share tons of projects as they build CircuitPython WiFi widgets.
What we want to avoid is people accidentally sharing their passwords or secret
tokens and API keys. So, we designed all our examples to use a secrets.py file,
that is in your CIRCUITPY drive, to hold secret/private/custom data. That way you can
share your main project without worrying about accidentally sharing private stuff.
Your secrets.py file should look like this:
# This file is where you keep secret settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
secrets = {
'ssid' : 'home ssid',
'password' : 'my password',
'timezone' : "America/New_York", # http://worldtimeapi.org/timezones
'github_token' : 'fawfj23rakjnfawiefa',
'hackaday_token' : 'h4xx0rs3kret',
}
Inside is a python dictionary named secrets with a line for each entry. Each entry has
an entry name (say 'ssid' ) and then a colon to separate it from the entry key 'home
ssid' and finally a comma ,
At a minimum you'll need the ssid and password for your local WiFi setup. As you
make projects you may need more tokens and keys, just add them one line at a time.
See for example other tokens such as one for accessing github or the hackaday API.
Other non-secret data like your timezone can also go here, just cause it's called
secrets doesn't mean you can't have general customization data in there!
For the correct time zone string, look athttp://worldtimeapi.org/timezones(https://
adafru.it/EcP)and remember that if your city is not listed, look for a city in the same
time zone, for example Boston, New York, Philadelphia, Washington DC, and Miami
are all on the same time as New York.
Of course, don't share your secrets.py - keep that out of GitHub, Discord or other
project-sharing sites.
©Adafruit Industries Page 15 of 54

Connect to WiFi
OK now you have your secrets setup - you can connect to the Internet. Lets use the
ESP32SPI and the Requests libraries - you'll need to visit the CircuitPython bundle
and install(https://adafru.it/ENC):
adafruit_bus_device
adafruit_esp32spi
adafruit_requests
neopixel
Into your lib folder. Once that's done, load up the following example using Mu or
your favorite editor:
# SPDX-FileCopyrightText: 2019 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
import board
import busio
from digitalio import DigitalInOut
import adafruit_requests as requests
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from adafruit_esp32spi import adafruit_esp32spi
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
print("ESP32 SPI webclient test")
TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html"
JSON_URL = "http://api.coindesk.com/v1/bpi/currentprice/USD.json"
# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
# If you have an AirLift Shield:
# esp32_cs = DigitalInOut(board.D10)
# esp32_ready = DigitalInOut(board.D7)
# esp32_reset = DigitalInOut(board.D5)
# If you have an AirLift Featherwing or ItsyBitsy Airlift:
# esp32_cs = DigitalInOut(board.D13)
# esp32_ready = DigitalInOut(board.D11)
# esp32_reset = DigitalInOut(board.D12)
# If you have an externally connected ESP32:
# NOTE: You may need to change the pins to reflect your wiring
# esp32_cs = DigitalInOut(board.D9)
# esp32_ready = DigitalInOut(board.D10)
# esp32_reset = DigitalInOut(board.D5)
•
•
•
•
©Adafruit Industries Page 16 of 54

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
requests.set_socket(socket, esp)
if esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
print("ESP32 found and in idle mode")
print("Firmware vers.", esp.firmware_version)
print("MAC addr:", [hex(i) for i in esp.MAC_address])
for ap in esp.scan_networks():
print("\t%s\t\tRSSI: %d" % (str(ap["ssid"], "utf-8"), ap["rssi"]))
print("Connecting to AP...")
while not esp.is_connected:
try:
esp.connect_AP(secrets["ssid"], secrets["password"])
except RuntimeError as e:
print("could not connect to AP, retrying: ", e)
continue
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))
print(
"IP lookup adafruit.com: %s" %
esp.pretty_ip(esp.get_host_by_name("adafruit.com"))
)
print("Ping google.com: %d ms" % esp.ping("google.com"))
# esp._debug = True
print("Fetching text from", TEXT_URL)
r = requests.get(TEXT_URL)
print("-" * 40)
print(r.text)
print("-" * 40)
r.close()
print()
print("Fetching json from", JSON_URL)
r = requests.get(JSON_URL)
print("-" * 40)
print(r.json())
print("-" * 40)
r.close()
print("Done!")
And save it to your board, with the name code.py .
Don't forget you'll also need to create the secrets.py 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://adafru.it/Bec).
©Adafruit Industries Page 17 of 54

In order, the example code...
Initializes the ESP32 over SPI using the SPI port and 3 control pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
Tells our requests library the type of socket we're using (socket type varies by
connectivity type - we'll be using the adafruit_esp32spi_socket for this example).
We'll also set the interface to an esp object. This is a little bit of a hack, but it lets us
use requests like CPython does.
requests.set_socket(socket, esp)
Verifies an ESP32 is found, checks the firmware and MAC address
if esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
print("ESP32 found and in idle mode")
print("Firmware vers.", esp.firmware_version)
print("MAC addr:", [hex(i) for i in esp.MAC_address])
©Adafruit Industries Page 18 of 54

Performs a scan of all access points it can see and prints out the name and signal
strength:
for ap in esp.scan_networks():
print("\t%s\t\tRSSI: %d" % (str(ap['ssid'], 'utf-8'), ap['rssi']))
Connects to the AP we've defined here, then prints out the local IP address, attempts
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)
print("Connecting to AP...")
while not esp.is_connected:
try:
esp.connect_AP(secrets["ssid"], secrets["password"])
except RuntimeError as e:
print("could not connect to AP, retrying: ", e)
continue
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))
print(
"IP lookup adafruit.com: %s" %
esp.pretty_ip(esp.get_host_by_name("adafruit.com"))
OK now we're getting to the really interesting part. With a SAMD51 or other large-RAM
(well, over 32 KB) device, we can do a lot of neat tricks. Like for example we can
implement an interface a lot like requests(https://adafru.it/E9o) - which makes getting
data really really easy
To read in all the text from a web URL call requests.get - you can pass in https
URLs for SSL connectivity
TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html"
print("Fetching text from", TEXT_URL)
r = requests.get(TEXT_URL)
print('-'*40)
print(r.text)
print('-'*40)
r.close()
Or, if the data is in structured JSON, you can get the json pre-parsed into a Python
dictionary that can be easily queried or traversed. (Again, only for nRF52840, M4 and
other high-RAM boards)
JSON_URL = "http://api.coindesk.com/v1/bpi/currentprice/USD.json"
print("Fetching json from", JSON_URL)
r = requests.get(JSON_URL)
print('-'*40)
print(r.json())
print('-'*40)
r.close()
©Adafruit Industries Page 19 of 54

Requests
We've written a requests-like(https://adafru.it/Kpa) library for web interfacing namedA
dafruit_CircuitPython_Requests(https://adafru.it/FpW). This library allows you to send
HTTP/1.1 requests without "crafting" them and provides helpful methods for parsing
the response from the server.
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
# adafruit_requests usage with an esp32spi_socket
import board
import busio
from digitalio import DigitalInOut
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from adafruit_esp32spi import adafruit_esp32spi
import adafruit_requests as requests
# Add a secrets.py to your filesystem that has a dictionary called secrets with
"ssid" and
# "password" keys with your WiFi credentials. DO NOT share that file or commit it
into Git or other
# source control.
# pylint: disable=no-name-in-module,wrong-import-order
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
# If you have an externally connected ESP32:
# esp32_cs = DigitalInOut(board.D9)
# esp32_ready = DigitalInOut(board.D10)
# esp32_reset = DigitalInOut(board.D5)
# If you have an AirLift Featherwing or ItsyBitsy Airlift:
# esp32_cs = DigitalInOut(board.D13)
# esp32_ready = DigitalInOut(board.D11)
# esp32_reset = DigitalInOut(board.D12)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
print("Connecting to AP...")
while not esp.is_connected:
try:
esp.connect_AP(secrets["ssid"], secrets["password"])
except RuntimeError as e:
print("could not connect to AP, retrying: ", e)
continue
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
# Initialize a requests object with a socket and esp32spi interface
socket.set_interface(esp)
requests.set_socket(socket, esp)
TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html"
©Adafruit Industries Page 20 of 54
This manual suits for next models
2
Table of contents