Skip to content

Commit 68e5b52

Browse files
committed
ESP32SPI GPIO example & documentation
1 parent f523b23 commit 68e5b52

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed

examples/gpio/esp32spi_gpio.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import board
2+
import busio
3+
import time
4+
import random
5+
from digitalio import DigitalInOut, Direction
6+
from analogio import AnalogIn
7+
import pulseio
8+
from adafruit_esp32spi import adafruit_esp32spi
9+
10+
11+
"""
12+
ESP32SPI Digital and Analog Pin Reads & Wites
13+
"""
14+
15+
16+
def esp_reset_all():
17+
esp_reset()
18+
esp_debug()
19+
esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN)
20+
21+
def esp_reset(wait=1):
22+
"""
23+
CAUTION this will re-initialize the ESP32 pin modes and debug level
24+
"""
25+
esp.reset()
26+
time.sleep(wait)
27+
28+
def esp_debug(local=0, remote=True):
29+
# ESP32SPI CircuitPython library serial debug on M4 TX
30+
esp._debug = local # 0, 1, 2, 3
31+
32+
# NINA serial debug on ESP32 TX
33+
esp.set_esp_debug(remote) # False, True
34+
35+
def esp_init_pin_modes(din, dout):
36+
# ESP32 Digital Input
37+
esp.set_pin_mode(din, 0x0)
38+
39+
# ESP32 Digital Output (no output on pins 34-39)
40+
esp.set_pin_mode(dout, 0x1) # Red LED on ESP32 Feather and ESP32 Breakout
41+
42+
def esp_status_text(n):
43+
t = {0: 'WL_IDLE_STATUS',
44+
1: 'WL_NO_SSID_AVAIL',
45+
2: 'WL_SCAN_COMPLETED',
46+
3: 'WL_CONNECTED',
47+
4: 'WL_CONNECT_FAILED',
48+
5: 'WL_CONNECTION_LOST',
49+
6: 'WL_DISCONNECTED',
50+
7: 'WL_AP_LISTENING',
51+
8: 'WL_AP_CONNECTED',
52+
9: 'WL_AP_FAILED',
53+
10: 'WL_NO_SHIELD', }
54+
if n in t:
55+
return t[n]
56+
else:
57+
return 'WL_UNDEFINED'
58+
59+
60+
# M4 R/W Pin Assignments
61+
M4_D_W_PIN = DigitalInOut(board.A1) # digital write to ESP_D_R_PIN
62+
M4_D_W_PIN.direction = Direction.OUTPUT
63+
M4_A_R_PIN = pulseio.PulseIn(board.A0, maxlen=64) # PWM read from ESP_A_W_PIN
64+
M4_A_R_PIN.pause()
65+
66+
# ESP32 R/W Pin assignments
67+
ESP_D_R_PIN = 12 # digital read from M4_D_W_PIN
68+
ESP_D_W_PIN = 13 # digital write to Red LED on Feather ESP32 and ESP32 Breakout
69+
# ESP32 Analog Input using ADC1
70+
# esp.set_pin_mode(36, 0x0) # Hall Effect Sensor
71+
# esp.set_pin_mode(37, 0x0) # Not Exposed
72+
# esp.set_pin_mode(38, 0x0) # Not Exposed
73+
# esp.set_pin_mode(39, 0x0) # Hall Effect Sensor
74+
# esp.set_pin_mode(32, 0x0) # INPUT OK
75+
# esp.set_pin_mode(33, 0x0) # DO NOT USE: ESP32SPI Busy/!Rdy
76+
# esp.set_pin_mode(34, 0x0) # INPUT OK
77+
# esp.set_pin_mode(35, 0x0) # INPUT OK (1/2 of Battery on ESP32 Feather)
78+
ESP_A_R_PIN = 32 # analog read from 10k potentiometer
79+
# ESP32 Analog (PWM/LEDC) Output (no output on pins 34-39)
80+
ESP_A_W_PIN = 27 # analog (PWM) write to M4_A_R_PIN
81+
82+
spi = board.SPI()
83+
# Airlift FeatherWing & Bitsy Add-On compatible
84+
esp32_cs = DigitalInOut(board.D13) # M4 Red LED
85+
esp32_ready = DigitalInOut(board.D11)
86+
esp32_reset = DigitalInOut(board.D12)
87+
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
88+
89+
esp_reset_all()
90+
91+
espfirmware = ''
92+
for _ in esp.firmware_version:
93+
if _ == 0:
94+
break
95+
else:
96+
espfirmware += "{:c}".format(_)
97+
print('ESP32 Firmware:', espfirmware)
98+
99+
esp_MAC_address = esp.MAC_address
100+
print("ESP32 MAC: {5:02X}:{4:02X}:{3:02X}:{2:02X}:{1:02X}:{0:02X}".format(*esp_MAC_address))
101+
102+
print('ESP32 Status: ', esp.status, esp_status_text(esp.status), 'Connected?', esp.is_connected)
103+
104+
# initial digital write values
105+
m4_d_w_val = False
106+
esp_d_w_val = False
107+
108+
while True:
109+
print()
110+
print('ESP32 DIGITAL:')
111+
112+
# ESP32 digital read
113+
try:
114+
M4_D_W_PIN.value = m4_d_w_val
115+
print('M4 wrote:', m4_d_w_val, end=' ')
116+
# b/c ESP32 might have reset out from under us
117+
esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN)
118+
esp_d_r_val = esp.set_digital_read(ESP_D_R_PIN)
119+
print('--> ESP read:', esp_d_r_val)
120+
except (RuntimeError, AssertionError) as e:
121+
print('ESP32 Error', e)
122+
esp_reset_all()
123+
124+
# ESP32 digital write
125+
try:
126+
# b/c ESP32 might have reset out from under us
127+
esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN)
128+
esp.set_digital_write(ESP_D_W_PIN, esp_d_w_val)
129+
print('ESP wrote:', esp_d_w_val, '--> Red LED')
130+
except (RuntimeError) as e:
131+
print('ESP32 Error', e)
132+
esp_reset_all()
133+
134+
print('ESP32 ANALOG:')
135+
136+
# ESP32 analog read
137+
try:
138+
esp_a_r_val = esp.set_analog_read(ESP_A_R_PIN)
139+
print('Potentiometer --> ESP read: ', esp_a_r_val,
140+
' (', '{:1.1f}'.format(esp_a_r_val*3.3/65536), 'v)', sep='')
141+
except (RuntimeError, AssertionError) as e:
142+
print('ESP32 Error', e)
143+
esp_reset_all()
144+
145+
# ESP32 analog write
146+
try:
147+
esp_a_w_val = random.uniform(0.1, .9)
148+
esp.set_analog_write(ESP_A_W_PIN, esp_a_w_val)
149+
print('ESP wrote: ', '{:1.2f}'.format(esp_a_w_val),
150+
' (', '{:d}'.format(int(esp_a_w_val*65536)), ')',
151+
' (', '{:1.1f}'.format(esp_a_w_val*3.3), 'v)',
152+
sep='', end=' ')
153+
154+
# ESP32 "analog" write is a 1000Hz PWM
155+
# use pulseio to extract the duty cycle
156+
M4_A_R_PIN.clear()
157+
M4_A_R_PIN.resume()
158+
while len(M4_A_R_PIN) < 2:
159+
pass
160+
M4_A_R_PIN.pause()
161+
duty = M4_A_R_PIN[0] / (M4_A_R_PIN[0] + M4_A_R_PIN[1])
162+
print('--> M4 read: ', '{:1.2f}'.format(duty),
163+
' (', '{:d}'.format(int(duty*65536)), ')',
164+
' (', '{:1.1f}'.format(duty*3.3), 'v)',
165+
' [len=', len(M4_A_R_PIN), ']', sep='')
166+
167+
except (RuntimeError) as e:
168+
print('ESP32 Error', e)
169+
esp_reset_all()
170+
171+
# toggle digital write values
172+
m4_d_w_val = not m4_d_w_val
173+
esp_d_w_val = not esp_d_w_val
174+
175+
time.sleep(5)

examples/gpio/gpio.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Using ESP32 co-processor GPIO pins with CircuitPython ESP32SPI
2+
3+
## Available pins
4+
5+
```
6+
# ESP32_GPIO_PINS:
7+
# https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/blob/master/adafruit_esp32spi/digitalio.py
8+
# 0, 1, 2, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33, 34, 35, 36, 39
9+
#
10+
# Pins Used for ESP32SPI
11+
# 5, 14, 18, 23, 33
12+
13+
# Avialable ESP32SPI Outputs (digital or 'analog' PWM) with NINA FW >= 1.3.1
14+
#
15+
# Adafruit ESP32 Breakout
16+
# *, 2, 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32
17+
# Adafruit ESP32 Feather
18+
# 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32
19+
# TinyPICO
20+
# 4, 15, 19, 21, 22, 25, 26, 27, 32
21+
# Adafruit ESP32 Airlift Breakout†
22+
# G, R, B
23+
# Adafruit ESP32 Airlift Feather†
24+
# G, R, B
25+
# Adafruit ESP32 Airlift Bitsy Add-On†
26+
# G, R, B
27+
28+
# Avialable† ESP32SPI Digital Inputs with NINA FW >= 1.5.0
29+
#
30+
# Adafruit ESP32 Breakout
31+
# *, 2, 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32, 34, 35, 36, 39
32+
# Adafruit ESP32 Feather
33+
# 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32, 34, 36, 39
34+
# TinyPICO
35+
# 4, 15, 19, 21, 22, 25, 26, 27, 32 CH
36+
37+
# Avialable ESP32SPI Analog Inputs (ADC1) with NINA FW >= 1.5.0
38+
#
39+
# Adafruit ESP32 Breakout
40+
# *, 32, 34, 35, HE, HE
41+
# Adafruit ESP32 Feather
42+
# *, 32, 34, BA, HE, HE
43+
# TinyPICO
44+
# 32, BA
45+
46+
Notes:
47+
* Used for bootloading
48+
G Green LED
49+
R Red LED
50+
B Blue LED
51+
BA On-board connection to battery via 50:50 voltage divider
52+
CH Battery charging state (digital pin)
53+
HE Hall Effect sensor
54+
```
55+
56+
Note that on the Airlift FeatherWing and the Airlift Bitsy Add-On, the ESP32 SPI Chip Select (CS) pin aligns with M4's D13 Red LED pin:
57+
```
58+
esp32_cs = DigitalInOut(board.D13) # M4 Red LED
59+
esp32_ready = DigitalInOut(board.D11)
60+
esp32_reset = DigitalInOut(board.D12)
61+
```
62+
So the Red LED on the main Feather processor will almost always appear to be ON or slightly flickering when ESP32SPI is running.
63+
64+
## ESP32 Reset
65+
66+
Because the ESP32 can sometimes reset without indication to the CircuitPython code, putting ESP32 GPIO pins into input mode, `esp.set_digital_write(pin, val)` should be preceded by `esp.set_pin_mode(pin, 0x1)`, with appropriate error handling. Other non-default `esp` states (e.g., `esp.set_esp_debug()`) will also get re-initialized to default settings upon ESP32 reset, so CircuitPython code should anticipate this.
67+
68+
## GPIO on Airlift add-on boards
69+
70+
It should also be possible to do ESP32SPI reads and writes on the Airlift add-on boards, but other than the SPI pins and the green, blue, and red LEDs, the only pins available are RX (GPIO3), TX (GPIO1), and GPIO0, so function is very limited. Analog input is ruled out since none of those pins are on ADC1.
71+
72+
The Airlift Breakout has level-shifting on RX and GPIO0, so those could be digital inputs only. TX could be used as a digital input or as a digital or analog (PWM) output.
73+
74+
The Airlift FeatherWing and Bitsy Add-On have no level-shifting since they're designed to be stacked onto their associated M4 microcontrollers, so theoretically RX, TX, and GPIO0 could be used as digital inputs, or as digital or analog (PWM) outputs. It's hard to find a use case for doing this when stacked since RX, TX, and GPIO0 will be connected to M4 GPIO pins.
75+
76+
The Airlift Shield has level-shifting on RX and GPIO0, with stacking issues similar to the wings.
77+
78+
The RX, TX, and GPIO0 pins are used for updating the NINA firmware, and have specific behaviors immediately following reboot that need to be considered if reusing them as GPIO. On the Airlift FeatherWing and Bitsy Add-On, there are pads that need to be soldered to connect the pins. NINA does output messages to TX when connected, depending on the esp debug level set.
79+
80+
Ultimately it makes the most sense to use a non-stacked full-pinout ESP32 as co-processor for ESP32SPI pin read and write features.

0 commit comments

Comments
 (0)