From 68e5b5280ce2d16765d1874a4fd560a6fda51d49 Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Thu, 20 Feb 2020 23:59:04 -0600 Subject: [PATCH 01/12] ESP32SPI GPIO example & documentation --- examples/gpio/esp32spi_gpio.py | 175 +++++++++++++++++++++++++++++++++ examples/gpio/gpio.md | 80 +++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 examples/gpio/esp32spi_gpio.py create mode 100644 examples/gpio/gpio.md diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py new file mode 100644 index 0000000..8963ecd --- /dev/null +++ b/examples/gpio/esp32spi_gpio.py @@ -0,0 +1,175 @@ +import board +import busio +import time +import random +from digitalio import DigitalInOut, Direction +from analogio import AnalogIn +import pulseio +from adafruit_esp32spi import adafruit_esp32spi + + +""" +ESP32SPI Digital and Analog Pin Reads & Wites +""" + + +def esp_reset_all(): + esp_reset() + esp_debug() + esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) + +def esp_reset(wait=1): + """ + CAUTION this will re-initialize the ESP32 pin modes and debug level + """ + esp.reset() + time.sleep(wait) + +def esp_debug(local=0, remote=True): + # ESP32SPI CircuitPython library serial debug on M4 TX + esp._debug = local # 0, 1, 2, 3 + + # NINA serial debug on ESP32 TX + esp.set_esp_debug(remote) # False, True + +def esp_init_pin_modes(din, dout): + # ESP32 Digital Input + esp.set_pin_mode(din, 0x0) + + # ESP32 Digital Output (no output on pins 34-39) + esp.set_pin_mode(dout, 0x1) # Red LED on ESP32 Feather and ESP32 Breakout + +def esp_status_text(n): + t = {0: 'WL_IDLE_STATUS', + 1: 'WL_NO_SSID_AVAIL', + 2: 'WL_SCAN_COMPLETED', + 3: 'WL_CONNECTED', + 4: 'WL_CONNECT_FAILED', + 5: 'WL_CONNECTION_LOST', + 6: 'WL_DISCONNECTED', + 7: 'WL_AP_LISTENING', + 8: 'WL_AP_CONNECTED', + 9: 'WL_AP_FAILED', + 10: 'WL_NO_SHIELD', } + if n in t: + return t[n] + else: + return 'WL_UNDEFINED' + + +# M4 R/W Pin Assignments +M4_D_W_PIN = DigitalInOut(board.A1) # digital write to ESP_D_R_PIN +M4_D_W_PIN.direction = Direction.OUTPUT +M4_A_R_PIN = pulseio.PulseIn(board.A0, maxlen=64) # PWM read from ESP_A_W_PIN +M4_A_R_PIN.pause() + +# ESP32 R/W Pin assignments +ESP_D_R_PIN = 12 # digital read from M4_D_W_PIN +ESP_D_W_PIN = 13 # digital write to Red LED on Feather ESP32 and ESP32 Breakout +# ESP32 Analog Input using ADC1 +# esp.set_pin_mode(36, 0x0) # Hall Effect Sensor +# esp.set_pin_mode(37, 0x0) # Not Exposed +# esp.set_pin_mode(38, 0x0) # Not Exposed +# esp.set_pin_mode(39, 0x0) # Hall Effect Sensor +# esp.set_pin_mode(32, 0x0) # INPUT OK +# esp.set_pin_mode(33, 0x0) # DO NOT USE: ESP32SPI Busy/!Rdy +# esp.set_pin_mode(34, 0x0) # INPUT OK +# esp.set_pin_mode(35, 0x0) # INPUT OK (1/2 of Battery on ESP32 Feather) +ESP_A_R_PIN = 32 # analog read from 10k potentiometer +# ESP32 Analog (PWM/LEDC) Output (no output on pins 34-39) +ESP_A_W_PIN = 27 # analog (PWM) write to M4_A_R_PIN + +spi = board.SPI() +# Airlift FeatherWing & Bitsy Add-On compatible +esp32_cs = DigitalInOut(board.D13) # M4 Red LED +esp32_ready = DigitalInOut(board.D11) +esp32_reset = DigitalInOut(board.D12) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) + +esp_reset_all() + +espfirmware = '' +for _ in esp.firmware_version: + if _ == 0: + break + else: + espfirmware += "{:c}".format(_) +print('ESP32 Firmware:', espfirmware) + +esp_MAC_address = esp.MAC_address +print("ESP32 MAC: {5:02X}:{4:02X}:{3:02X}:{2:02X}:{1:02X}:{0:02X}".format(*esp_MAC_address)) + +print('ESP32 Status: ', esp.status, esp_status_text(esp.status), 'Connected?', esp.is_connected) + +# initial digital write values +m4_d_w_val = False +esp_d_w_val = False + +while True: + print() + print('ESP32 DIGITAL:') + + # ESP32 digital read + try: + M4_D_W_PIN.value = m4_d_w_val + print('M4 wrote:', m4_d_w_val, end=' ') + # b/c ESP32 might have reset out from under us + esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) + esp_d_r_val = esp.set_digital_read(ESP_D_R_PIN) + print('--> ESP read:', esp_d_r_val) + except (RuntimeError, AssertionError) as e: + print('ESP32 Error', e) + esp_reset_all() + + # ESP32 digital write + try: + # b/c ESP32 might have reset out from under us + esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) + esp.set_digital_write(ESP_D_W_PIN, esp_d_w_val) + print('ESP wrote:', esp_d_w_val, '--> Red LED') + except (RuntimeError) as e: + print('ESP32 Error', e) + esp_reset_all() + + print('ESP32 ANALOG:') + + # ESP32 analog read + try: + esp_a_r_val = esp.set_analog_read(ESP_A_R_PIN) + print('Potentiometer --> ESP read: ', esp_a_r_val, + ' (', '{:1.1f}'.format(esp_a_r_val*3.3/65536), 'v)', sep='') + except (RuntimeError, AssertionError) as e: + print('ESP32 Error', e) + esp_reset_all() + + # ESP32 analog write + try: + esp_a_w_val = random.uniform(0.1, .9) + esp.set_analog_write(ESP_A_W_PIN, esp_a_w_val) + print('ESP wrote: ', '{:1.2f}'.format(esp_a_w_val), + ' (', '{:d}'.format(int(esp_a_w_val*65536)), ')', + ' (', '{:1.1f}'.format(esp_a_w_val*3.3), 'v)', + sep='', end=' ') + + # ESP32 "analog" write is a 1000Hz PWM + # use pulseio to extract the duty cycle + M4_A_R_PIN.clear() + M4_A_R_PIN.resume() + while len(M4_A_R_PIN) < 2: + pass + M4_A_R_PIN.pause() + duty = M4_A_R_PIN[0] / (M4_A_R_PIN[0] + M4_A_R_PIN[1]) + print('--> M4 read: ', '{:1.2f}'.format(duty), + ' (', '{:d}'.format(int(duty*65536)), ')', + ' (', '{:1.1f}'.format(duty*3.3), 'v)', + ' [len=', len(M4_A_R_PIN), ']', sep='') + + except (RuntimeError) as e: + print('ESP32 Error', e) + esp_reset_all() + + # toggle digital write values + m4_d_w_val = not m4_d_w_val + esp_d_w_val = not esp_d_w_val + + time.sleep(5) diff --git a/examples/gpio/gpio.md b/examples/gpio/gpio.md new file mode 100644 index 0000000..af13394 --- /dev/null +++ b/examples/gpio/gpio.md @@ -0,0 +1,80 @@ +# Using ESP32 co-processor GPIO pins with CircuitPython ESP32SPI + +## Available pins + +``` + # ESP32_GPIO_PINS: + # https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/blob/master/adafruit_esp32spi/digitalio.py + # 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 + # + # Pins Used for ESP32SPI + # 5, 14, 18, 23, 33 + + # Avialable ESP32SPI Outputs (digital or 'analog' PWM) with NINA FW >= 1.3.1 + # + # Adafruit ESP32 Breakout + # *, 2, 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32 + # Adafruit ESP32 Feather + # 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32 + # TinyPICO + # 4, 15, 19, 21, 22, 25, 26, 27, 32 + # Adafruit ESP32 Airlift Breakout† + # G, R, B + # Adafruit ESP32 Airlift Feather† + # G, R, B + # Adafruit ESP32 Airlift Bitsy Add-On† + # G, R, B + + # Avialable† ESP32SPI Digital Inputs with NINA FW >= 1.5.0 + # + # Adafruit ESP32 Breakout + # *, 2, 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32, 34, 35, 36, 39 + # Adafruit ESP32 Feather + # 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32, 34, 36, 39 + # TinyPICO + # 4, 15, 19, 21, 22, 25, 26, 27, 32 CH + + # Avialable ESP32SPI Analog Inputs (ADC1) with NINA FW >= 1.5.0 + # + # Adafruit ESP32 Breakout + # *, 32, 34, 35, HE, HE + # Adafruit ESP32 Feather + # *, 32, 34, BA, HE, HE + # TinyPICO + # 32, BA + +Notes: + * Used for bootloading + G Green LED + R Red LED + B Blue LED +BA On-board connection to battery via 50:50 voltage divider +CH Battery charging state (digital pin) +HE Hall Effect sensor +``` + +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: +``` + esp32_cs = DigitalInOut(board.D13) # M4 Red LED + esp32_ready = DigitalInOut(board.D11) + esp32_reset = DigitalInOut(board.D12) +``` +So the Red LED on the main Feather processor will almost always appear to be ON or slightly flickering when ESP32SPI is running. + +## ESP32 Reset + +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. + +## GPIO on Airlift add-on boards + +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. + +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. + +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. + +The Airlift Shield has level-shifting on RX and GPIO0, with stacking issues similar to the wings. + +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. + +Ultimately it makes the most sense to use a non-stacked full-pinout ESP32 as co-processor for ESP32SPI pin read and write features. From a25513a22809b34b5a9493e98f2f94d3d77bcc4d Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Fri, 21 Feb 2020 09:00:54 -0600 Subject: [PATCH 02/12] Example of using ESP32 pins as GPIO --- examples/gpio/esp32spi_gpio.py | 180 +++++++++++++++++++++++++++++++++ examples/gpio/gpio.md | 86 ++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 examples/gpio/esp32spi_gpio.py create mode 100644 examples/gpio/gpio.md diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py new file mode 100644 index 0000000..dcd84f9 --- /dev/null +++ b/examples/gpio/esp32spi_gpio.py @@ -0,0 +1,180 @@ +import board +import busio +import time +import random +from digitalio import DigitalInOut, Direction +from analogio import AnalogIn +import pulseio +from adafruit_esp32spi import adafruit_esp32spi + + +""" +ESP32SPI Digital and Analog Pin Reads & Writes + +This example targets a Feather M4 or ItsyBitsy M4 as the CircuitPython processor, +along with either an ESP32 Feather or ESP32 Breakout as Wi-Fi co-processor. +You may need to choose different pins for other targets. +""" + + +def esp_reset_all(): + esp_reset() + esp_debug() + esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) + +def esp_reset(wait=1): + """ + CAUTION this will re-initialize the ESP32 pin modes and debug level + """ + esp.reset() + time.sleep(wait) + +def esp_debug(local=0, remote=True): + # ESP32SPI CircuitPython library serial debug on M4 TX + esp._debug = local # 0, 1, 2, 3 + + # NINA serial debug on ESP32 TX + esp.set_esp_debug(remote) # False, True + +def esp_init_pin_modes(din, dout): + # ESP32 Digital Input + esp.set_pin_mode(din, 0x0) + + # ESP32 Digital Output (no output on pins 34-39) + esp.set_pin_mode(dout, 0x1) # Red LED on ESP32 Feather and ESP32 Breakout + +def esp_status_text(n): + t = {0: 'WL_IDLE_STATUS', + 1: 'WL_NO_SSID_AVAIL', + 2: 'WL_SCAN_COMPLETED', + 3: 'WL_CONNECTED', + 4: 'WL_CONNECT_FAILED', + 5: 'WL_CONNECTION_LOST', + 6: 'WL_DISCONNECTED', + 7: 'WL_AP_LISTENING', + 8: 'WL_AP_CONNECTED', + 9: 'WL_AP_FAILED', + 10: 'WL_NO_SHIELD', } + if n in t: + return t[n] + else: + return 'WL_UNDEFINED' + + +# M4 R/W Pin Assignments +M4_D_W_PIN = DigitalInOut(board.A1) # digital write to ESP_D_R_PIN +M4_D_W_PIN.direction = Direction.OUTPUT +M4_A_R_PIN = pulseio.PulseIn(board.A0, maxlen=64) # PWM read from ESP_A_W_PIN +M4_A_R_PIN.pause() + +# ESP32 R/W Pin assignments +ESP_D_R_PIN = 12 # digital read from M4_D_W_PIN +ESP_D_W_PIN = 13 # digital write to Red LED on Feather ESP32 and ESP32 Breakout +# ESP32 Analog Input using ADC1 +# esp.set_pin_mode(36, 0x0) # Hall Effect Sensor +# esp.set_pin_mode(37, 0x0) # Not Exposed +# esp.set_pin_mode(38, 0x0) # Not Exposed +# esp.set_pin_mode(39, 0x0) # Hall Effect Sensor +# esp.set_pin_mode(32, 0x0) # INPUT OK +# esp.set_pin_mode(33, 0x0) # DO NOT USE: ESP32SPI Busy/!Rdy +# esp.set_pin_mode(34, 0x0) # INPUT OK +# esp.set_pin_mode(35, 0x0) # INPUT OK (1/2 of Battery on ESP32 Feather) +ESP_A_R_PIN = 32 # analog read from 10k potentiometer +# ESP32 Analog (PWM/LEDC) Output (no output on pins 34-39) +ESP_A_W_PIN = 27 # analog (PWM) write to M4_A_R_PIN + +spi = board.SPI() +# Airlift FeatherWing & Bitsy Add-On compatible +esp32_cs = DigitalInOut(board.D13) # M4 Red LED +esp32_ready = DigitalInOut(board.D11) +esp32_reset = DigitalInOut(board.D12) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) + +esp_reset_all() + +espfirmware = '' +for _ in esp.firmware_version: + if _ == 0: + break + else: + espfirmware += "{:c}".format(_) +print('ESP32 Firmware:', espfirmware) + +esp_MAC_address = esp.MAC_address +print("ESP32 MAC: {5:02X}:{4:02X}:{3:02X}:{2:02X}:{1:02X}:{0:02X}".format(*esp_MAC_address)) + +print('ESP32 Status: ', esp.status, esp_status_text(esp.status), 'Connected?', esp.is_connected) + +# initial digital write values +m4_d_w_val = False +esp_d_w_val = False + +while True: + print() + print('ESP32 DIGITAL:') + + # ESP32 digital read + try: + M4_D_W_PIN.value = m4_d_w_val + print('M4 wrote:', m4_d_w_val, end=' ') + # b/c ESP32 might have reset out from under us + esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) + esp_d_r_val = esp.set_digital_read(ESP_D_R_PIN) + print('--> ESP read:', esp_d_r_val) + except (RuntimeError, AssertionError) as e: + print('ESP32 Error', e) + esp_reset_all() + + # ESP32 digital write + try: + # b/c ESP32 might have reset out from under us + esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) + esp.set_digital_write(ESP_D_W_PIN, esp_d_w_val) + print('ESP wrote:', esp_d_w_val, '--> Red LED') + except (RuntimeError) as e: + print('ESP32 Error', e) + esp_reset_all() + + print('ESP32 ANALOG:') + + # ESP32 analog read + try: + esp_a_r_val = esp.set_analog_read(ESP_A_R_PIN) + print('Potentiometer --> ESP read: ', esp_a_r_val, + ' (', '{:1.1f}'.format(esp_a_r_val*3.3/65536), 'v)', sep='') + except (RuntimeError, AssertionError) as e: + print('ESP32 Error', e) + esp_reset_all() + + # ESP32 analog write + try: + # don't set the low end to 0 or the M4's pulseio read will stall + esp_a_w_val = random.uniform(0.1, .9) + esp.set_analog_write(ESP_A_W_PIN, esp_a_w_val) + print('ESP wrote: ', '{:1.2f}'.format(esp_a_w_val), + ' (', '{:d}'.format(int(esp_a_w_val*65536)), ')', + ' (', '{:1.1f}'.format(esp_a_w_val*3.3), 'v)', + sep='', end=' ') + + # ESP32 "analog" write is a 1000Hz PWM + # use pulseio to extract the duty cycle + M4_A_R_PIN.clear() + M4_A_R_PIN.resume() + while len(M4_A_R_PIN) < 2: + pass + M4_A_R_PIN.pause() + duty = M4_A_R_PIN[0] / (M4_A_R_PIN[0] + M4_A_R_PIN[1]) + print('--> M4 read: ', '{:1.2f}'.format(duty), + ' (', '{:d}'.format(int(duty*65536)), ')', + ' (', '{:1.1f}'.format(duty*3.3), 'v)', + ' [len=', len(M4_A_R_PIN), ']', sep='') + + except (RuntimeError) as e: + print('ESP32 Error', e) + esp_reset_all() + + # toggle digital write values + m4_d_w_val = not m4_d_w_val + esp_d_w_val = not esp_d_w_val + + time.sleep(5) diff --git a/examples/gpio/gpio.md b/examples/gpio/gpio.md new file mode 100644 index 0000000..393ca6b --- /dev/null +++ b/examples/gpio/gpio.md @@ -0,0 +1,86 @@ +# Using ESP32 co-processor GPIO pins with CircuitPython ESP32SPI + +As of NINA firmware version 1.3.1, the ESP32SPI library can be used to write digital values to many of the ESP32 GPIO pins using CircuitPython. It can also write "analog" signals using a float between 0 and 1 as the duty cycle (which is converted to an 8-bit integer for use by the NINA firmware). Keep in mind that these are 1000Hz PWM signals using the ESP32 LED Control peripheral, not true analog signals using an on-chip DAC. More information can be found here: + + +As of NINA firmware version 1.5.0, the ESP32SPI library can be used to read digital signals from many of the ESP32 GPIO pins using CircuitPython. It can also read analog signals using ESP32 on-chip ADC1. The ESP32 can theoretically be set to use between 8 and 12 bits of resolution for analog reads. For our purposes, it is a 12-bit read within the NINA firmware, but the CircuitPython library converts it to a 16-bit integer consistent with CircuitPython `analogio` `AnalogIn`. There's an optional keyword argument in the `set_analog_read(self, pin, atten=ADC_ATTEN_DB_11)` function that changes the attenuation of the analog read, and therefore also changes the effective voltage range of the read. With the default 11dB attenuation, Espressif recommends keeping input voltages between 150mV to 2450mV for best results. More information can be found here: + + +## GPIO Pins available to ESP32SPI + +``` + # ESP32_GPIO_PINS: + # https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/blob/master/adafruit_esp32spi/digitalio.py + # 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 + # + # Pins Used for ESP32SPI + # 5, 14, 18, 23, 33 + + # Avialable ESP32SPI Outputs (digital or 'analog' PWM) with NINA FW >= 1.3.1 + # + # Adafruit ESP32 Breakout + # *, 2, 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32 + # Adafruit ESP32 Feather + # 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32 + # TinyPICO + # 4, 15, 19, 21, 22, 25, 26, 27, 32 + # Adafruit ESP32 Airlift Breakout† + # G, R, B + # Adafruit ESP32 Airlift Feather† + # G, R, B + # Adafruit ESP32 Airlift Bitsy Add-On† + # G, R, B + + # Avialable† ESP32SPI Digital Inputs with NINA FW >= 1.5.0 + # + # Adafruit ESP32 Breakout + # *, 2, 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32, 34, 35, 36, 39 + # Adafruit ESP32 Feather + # 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32, 34, 36, 39 + # TinyPICO + # 4, 15, 19, 21, 22, 25, 26, 27, 32 CH + + # Avialable ESP32SPI Analog Inputs (ADC1) with NINA FW >= 1.5.0 + # + # Adafruit ESP32 Breakout + # *, 32, 34, 35, HE, HE + # Adafruit ESP32 Feather + # *, 32, 34, BA, HE, HE + # TinyPICO + # 32, BA + +Notes: + * Used for bootloading + G Green LED + R Red LED + B Blue LED +BA On-board connection to battery via 50:50 voltage divider +CH Battery charging state (digital pin) +HE Hall Effect sensor +``` + +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: +``` + esp32_cs = DigitalInOut(board.D13) # M4 Red LED + esp32_ready = DigitalInOut(board.D11) + esp32_reset = DigitalInOut(board.D12) +``` +So the Red LED on the main Feather processor will almost always appear to be ON or slightly flickering when ESP32SPI is active. + +## ESP32 Reset + +Because the ESP32 may 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. + +## GPIO on Airlift add-on boards + +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 extremely limited. Analog input is ruled out since none of those pins are on ADC1. + +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. + +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. + +The Airlift [Metro / Arduino] Shield has level-shifting on RX and GPIO0, with stacking issues similar to the wings. + +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. + +Ultimately it makes the most sense by far to use a non-stacked full-pinout ESP32 as co-processor for ESP32SPI pin read and write features. From f3c18ef140089c4ad010d3ee5cc45c95c9f2db74 Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Fri, 21 Feb 2020 09:19:04 -0600 Subject: [PATCH 03/12] Fix Pylint issues --- examples/gpio/esp32spi_gpio.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py index dcd84f9..4daa801 100644 --- a/examples/gpio/esp32spi_gpio.py +++ b/examples/gpio/esp32spi_gpio.py @@ -1,20 +1,16 @@ -import board -import busio import time import random +import board from digitalio import DigitalInOut, Direction -from analogio import AnalogIn import pulseio from adafruit_esp32spi import adafruit_esp32spi -""" -ESP32SPI Digital and Analog Pin Reads & Writes +# ESP32SPI Digital and Analog Pin Reads & Writes -This example targets a Feather M4 or ItsyBitsy M4 as the CircuitPython processor, -along with either an ESP32 Feather or ESP32 Breakout as Wi-Fi co-processor. -You may need to choose different pins for other targets. -""" +# This example targets a Feather M4 or ItsyBitsy M4 as the CircuitPython processor, +# along with either an ESP32 Feather or ESP32 Breakout as Wi-Fi co-processor. +# You may need to choose different pins for other targets.""" def esp_reset_all(): @@ -44,6 +40,7 @@ def esp_init_pin_modes(din, dout): esp.set_pin_mode(dout, 0x1) # Red LED on ESP32 Feather and ESP32 Breakout def esp_status_text(n): + text = 'WL_UNDEFINED' t = {0: 'WL_IDLE_STATUS', 1: 'WL_NO_SSID_AVAIL', 2: 'WL_SCAN_COMPLETED', @@ -54,11 +51,10 @@ def esp_status_text(n): 7: 'WL_AP_LISTENING', 8: 'WL_AP_CONNECTED', 9: 'WL_AP_FAILED', - 10: 'WL_NO_SHIELD', } + 10: 'WL_NO_SHIELD', } if n in t: - return t[n] - else: - return 'WL_UNDEFINED' + text = t[n] + return text # M4 R/W Pin Assignments From 0d0da219ceb0cffecf659f13b1c7595d2d2921b9 Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Fri, 21 Feb 2020 09:30:49 -0600 Subject: [PATCH 04/12] Fix PyLint issue --- examples/gpio/esp32spi_gpio.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py index 4daa801..c4870a8 100644 --- a/examples/gpio/esp32spi_gpio.py +++ b/examples/gpio/esp32spi_gpio.py @@ -14,23 +14,13 @@ def esp_reset_all(): - esp_reset() - esp_debug() - esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) - -def esp_reset(wait=1): - """ - CAUTION this will re-initialize the ESP32 pin modes and debug level - """ + # esp.reset() will re-initialize the ESP32 pin modes and debug level esp.reset() - time.sleep(wait) - -def esp_debug(local=0, remote=True): - # ESP32SPI CircuitPython library serial debug on M4 TX - esp._debug = local # 0, 1, 2, 3 - - # NINA serial debug on ESP32 TX - esp.set_esp_debug(remote) # False, True + time.sleep(1) + # (re-)set NINA serial debug on ESP32 TX + esp.set_esp_debug(True) # False, True + # (re-)set digital pin modes + esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) def esp_init_pin_modes(din, dout): # ESP32 Digital Input From 927476a3b4d558c7f562cd558a2b59d3e67ef182 Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Fri, 21 Feb 2020 09:37:57 -0600 Subject: [PATCH 05/12] improve comments --- examples/gpio/esp32spi_gpio.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py index c4870a8..b0c0eae 100644 --- a/examples/gpio/esp32spi_gpio.py +++ b/examples/gpio/esp32spi_gpio.py @@ -10,11 +10,12 @@ # This example targets a Feather M4 or ItsyBitsy M4 as the CircuitPython processor, # along with either an ESP32 Feather or ESP32 Breakout as Wi-Fi co-processor. -# You may need to choose different pins for other targets.""" +# You may need to choose different pins for other targets. def esp_reset_all(): - # esp.reset() will re-initialize the ESP32 pin modes and debug level + # esp.reset() will reset the ESP32 using its RST pin + # side effect is re-initializing ESP32 pin modes and debug output esp.reset() time.sleep(1) # (re-)set NINA serial debug on ESP32 TX From f6c3e5e9c9ce32d07f5fadc5320dad26345dedf7 Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Fri, 21 Feb 2020 10:00:00 -0600 Subject: [PATCH 06/12] simplified print output --- examples/gpio/esp32spi_gpio.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py index b0c0eae..281c169 100644 --- a/examples/gpio/esp32spi_gpio.py +++ b/examples/gpio/esp32spi_gpio.py @@ -97,8 +97,7 @@ def esp_status_text(n): esp_d_w_val = False while True: - print() - print('ESP32 DIGITAL:') + print('\nESP32 DIGITAL:') # ESP32 digital read try: From 64528f50a92e928f0c426f973f6f1a352f9a4e52 Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Mon, 20 Apr 2020 23:13:52 -0500 Subject: [PATCH 07/12] Minor comment edits --- examples/gpio/esp32spi_gpio.py | 4 ++-- examples/gpio/gpio.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py index 281c169..6d7c219 100644 --- a/examples/gpio/esp32spi_gpio.py +++ b/examples/gpio/esp32spi_gpio.py @@ -28,7 +28,7 @@ def esp_init_pin_modes(din, dout): esp.set_pin_mode(din, 0x0) # ESP32 Digital Output (no output on pins 34-39) - esp.set_pin_mode(dout, 0x1) # Red LED on ESP32 Feather and ESP32 Breakout + esp.set_pin_mode(dout, 0x1) def esp_status_text(n): text = 'WL_UNDEFINED' @@ -54,7 +54,7 @@ def esp_status_text(n): M4_A_R_PIN = pulseio.PulseIn(board.A0, maxlen=64) # PWM read from ESP_A_W_PIN M4_A_R_PIN.pause() -# ESP32 R/W Pin assignments +# ESP32 R/W Pin assignments & connections ESP_D_R_PIN = 12 # digital read from M4_D_W_PIN ESP_D_W_PIN = 13 # digital write to Red LED on Feather ESP32 and ESP32 Breakout # ESP32 Analog Input using ADC1 diff --git a/examples/gpio/gpio.md b/examples/gpio/gpio.md index 393ca6b..8dae474 100644 --- a/examples/gpio/gpio.md +++ b/examples/gpio/gpio.md @@ -31,7 +31,7 @@ As of NINA firmware version 1.5.0, the ESP32SPI library can be used to read digi # Adafruit ESP32 Airlift Bitsy Add-On† # G, R, B - # Avialable† ESP32SPI Digital Inputs with NINA FW >= 1.5.0 + # Avialable ESP32SPI Digital Inputs with NINA FW >= 1.5.0 # # Adafruit ESP32 Breakout # *, 2, 4, 12, R, 15, 16, 17, 19, 21, 22, 25, 26, 27, 32, 34, 35, 36, 39 @@ -79,7 +79,7 @@ The Airlift Breakout has level-shifting on RX and GPIO0, so those could be digit 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. -The Airlift [Metro / Arduino] Shield has level-shifting on RX and GPIO0, with stacking issues similar to the wings. +The Airlift [Metro / Arduino] Shield has level-shifting on RX and GPIO0, with stacking issues similar to the FeatherWings. 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. From bbbd5f79d248d5c10b91d7449b36c609a5e5a50e Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Tue, 21 Apr 2020 09:51:52 -0500 Subject: [PATCH 08/12] Fix black error(s) --- examples/gpio/esp32spi_gpio.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py index 6d7c219..f3fc682 100644 --- a/examples/gpio/esp32spi_gpio.py +++ b/examples/gpio/esp32spi_gpio.py @@ -8,7 +8,7 @@ # ESP32SPI Digital and Analog Pin Reads & Writes -# This example targets a Feather M4 or ItsyBitsy M4 as the CircuitPython processor, +# This example targets a Feather M4 or ItsyBitsy M4 as the CircuitPython MCU, # along with either an ESP32 Feather or ESP32 Breakout as Wi-Fi co-processor. # You may need to choose different pins for other targets. @@ -88,9 +88,11 @@ def esp_status_text(n): print('ESP32 Firmware:', espfirmware) esp_MAC_address = esp.MAC_address -print("ESP32 MAC: {5:02X}:{4:02X}:{3:02X}:{2:02X}:{1:02X}:{0:02X}".format(*esp_MAC_address)) +print("ESP32 MAC: {5:02X}:{4:02X}:{3:02X}:{2:02X}:{1:02X}:{0:02X}" + .format(*esp_MAC_address)) -print('ESP32 Status: ', esp.status, esp_status_text(esp.status), 'Connected?', esp.is_connected) +print('ESP32 Status: ', esp.status, esp_status_text(esp.status), + 'Connected?', esp.is_connected) # initial digital write values m4_d_w_val = False From 8a9ed1ae9eee5e7e2925738deb6ba3b3563f8951 Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Tue, 21 Apr 2020 10:13:20 -0500 Subject: [PATCH 09/12] locally black'd output file --- examples/gpio/esp32spi_gpio.py | 114 ++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 39 deletions(-) diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py index f3fc682..304f73a 100644 --- a/examples/gpio/esp32spi_gpio.py +++ b/examples/gpio/esp32spi_gpio.py @@ -23,6 +23,7 @@ def esp_reset_all(): # (re-)set digital pin modes esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) + def esp_init_pin_modes(din, dout): # ESP32 Digital Input esp.set_pin_mode(din, 0x0) @@ -30,19 +31,22 @@ def esp_init_pin_modes(din, dout): # ESP32 Digital Output (no output on pins 34-39) esp.set_pin_mode(dout, 0x1) + def esp_status_text(n): - text = 'WL_UNDEFINED' - t = {0: 'WL_IDLE_STATUS', - 1: 'WL_NO_SSID_AVAIL', - 2: 'WL_SCAN_COMPLETED', - 3: 'WL_CONNECTED', - 4: 'WL_CONNECT_FAILED', - 5: 'WL_CONNECTION_LOST', - 6: 'WL_DISCONNECTED', - 7: 'WL_AP_LISTENING', - 8: 'WL_AP_CONNECTED', - 9: 'WL_AP_FAILED', - 10: 'WL_NO_SHIELD', } + text = "WL_UNDEFINED" + t = { + 0: "WL_IDLE_STATUS", + 1: "WL_NO_SSID_AVAIL", + 2: "WL_SCAN_COMPLETED", + 3: "WL_CONNECTED", + 4: "WL_CONNECT_FAILED", + 5: "WL_CONNECTION_LOST", + 6: "WL_DISCONNECTED", + 7: "WL_AP_LISTENING", + 8: "WL_AP_CONNECTED", + 9: "WL_AP_FAILED", + 10: "WL_NO_SHIELD", + } if n in t: text = t[n] return text @@ -79,38 +83,46 @@ def esp_status_text(n): esp_reset_all() -espfirmware = '' +espfirmware = "" for _ in esp.firmware_version: if _ == 0: break else: espfirmware += "{:c}".format(_) -print('ESP32 Firmware:', espfirmware) +print("ESP32 Firmware:", espfirmware) esp_MAC_address = esp.MAC_address -print("ESP32 MAC: {5:02X}:{4:02X}:{3:02X}:{2:02X}:{1:02X}:{0:02X}" - .format(*esp_MAC_address)) - -print('ESP32 Status: ', esp.status, esp_status_text(esp.status), - 'Connected?', esp.is_connected) +print( + "ESP32 MAC: {5:02X}:{4:02X}:{3:02X}:{2:02X}:{1:02X}:{0:02X}".format( + *esp_MAC_address + ) +) + +print( + "ESP32 Status: ", + esp.status, + esp_status_text(esp.status), + "Connected?", + esp.is_connected, +) # initial digital write values m4_d_w_val = False esp_d_w_val = False while True: - print('\nESP32 DIGITAL:') + print("\nESP32 DIGITAL:") # ESP32 digital read try: M4_D_W_PIN.value = m4_d_w_val - print('M4 wrote:', m4_d_w_val, end=' ') + print("M4 wrote:", m4_d_w_val, end=" ") # b/c ESP32 might have reset out from under us esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) esp_d_r_val = esp.set_digital_read(ESP_D_R_PIN) - print('--> ESP read:', esp_d_r_val) + print("--> ESP read:", esp_d_r_val) except (RuntimeError, AssertionError) as e: - print('ESP32 Error', e) + print("ESP32 Error", e) esp_reset_all() # ESP32 digital write @@ -118,31 +130,45 @@ def esp_status_text(n): # b/c ESP32 might have reset out from under us esp_init_pin_modes(ESP_D_R_PIN, ESP_D_W_PIN) esp.set_digital_write(ESP_D_W_PIN, esp_d_w_val) - print('ESP wrote:', esp_d_w_val, '--> Red LED') + print("ESP wrote:", esp_d_w_val, "--> Red LED") except (RuntimeError) as e: - print('ESP32 Error', e) + print("ESP32 Error", e) esp_reset_all() - print('ESP32 ANALOG:') + print("ESP32 ANALOG:") # ESP32 analog read try: esp_a_r_val = esp.set_analog_read(ESP_A_R_PIN) - print('Potentiometer --> ESP read: ', esp_a_r_val, - ' (', '{:1.1f}'.format(esp_a_r_val*3.3/65536), 'v)', sep='') + print( + "Potentiometer --> ESP read: ", + esp_a_r_val, + " (", + "{:1.1f}".format(esp_a_r_val * 3.3 / 65536), + "v)", + sep="", + ) except (RuntimeError, AssertionError) as e: - print('ESP32 Error', e) + print("ESP32 Error", e) esp_reset_all() # ESP32 analog write try: # don't set the low end to 0 or the M4's pulseio read will stall - esp_a_w_val = random.uniform(0.1, .9) + esp_a_w_val = random.uniform(0.1, 0.9) esp.set_analog_write(ESP_A_W_PIN, esp_a_w_val) - print('ESP wrote: ', '{:1.2f}'.format(esp_a_w_val), - ' (', '{:d}'.format(int(esp_a_w_val*65536)), ')', - ' (', '{:1.1f}'.format(esp_a_w_val*3.3), 'v)', - sep='', end=' ') + print( + "ESP wrote: ", + "{:1.2f}".format(esp_a_w_val), + " (", + "{:d}".format(int(esp_a_w_val * 65536)), + ")", + " (", + "{:1.1f}".format(esp_a_w_val * 3.3), + "v)", + sep="", + end=" ", + ) # ESP32 "analog" write is a 1000Hz PWM # use pulseio to extract the duty cycle @@ -152,13 +178,23 @@ def esp_status_text(n): pass M4_A_R_PIN.pause() duty = M4_A_R_PIN[0] / (M4_A_R_PIN[0] + M4_A_R_PIN[1]) - print('--> M4 read: ', '{:1.2f}'.format(duty), - ' (', '{:d}'.format(int(duty*65536)), ')', - ' (', '{:1.1f}'.format(duty*3.3), 'v)', - ' [len=', len(M4_A_R_PIN), ']', sep='') + print( + "--> M4 read: ", + "{:1.2f}".format(duty), + " (", + "{:d}".format(int(duty * 65536)), + ")", + " (", + "{:1.1f}".format(duty * 3.3), + "v)", + " [len=", + len(M4_A_R_PIN), + "]", + sep="", + ) except (RuntimeError) as e: - print('ESP32 Error', e) + print("ESP32 Error", e) esp_reset_all() # toggle digital write values From bfdac54d8ad0bf3ac005a48ac519993d89aa865e Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Tue, 21 Apr 2020 10:23:26 -0500 Subject: [PATCH 10/12] fix pylint --- examples/gpio/esp32spi_gpio.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py index 304f73a..00ba853 100644 --- a/examples/gpio/esp32spi_gpio.py +++ b/examples/gpio/esp32spi_gpio.py @@ -85,9 +85,7 @@ def esp_status_text(n): espfirmware = "" for _ in esp.firmware_version: - if _ == 0: - break - else: + if _ != 0: espfirmware += "{:c}".format(_) print("ESP32 Firmware:", espfirmware) From 4d5f884f52b5cb28a6d32bb35b3e10555f73e534 Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Tue, 21 Apr 2020 10:37:13 -0500 Subject: [PATCH 11/12] fix pylint --- examples/gpio/esp32spi_gpio.py | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py index 00ba853..318eb2f 100644 --- a/examples/gpio/esp32spi_gpio.py +++ b/examples/gpio/esp32spi_gpio.py @@ -32,26 +32,6 @@ def esp_init_pin_modes(din, dout): esp.set_pin_mode(dout, 0x1) -def esp_status_text(n): - text = "WL_UNDEFINED" - t = { - 0: "WL_IDLE_STATUS", - 1: "WL_NO_SSID_AVAIL", - 2: "WL_SCAN_COMPLETED", - 3: "WL_CONNECTED", - 4: "WL_CONNECT_FAILED", - 5: "WL_CONNECTION_LOST", - 6: "WL_DISCONNECTED", - 7: "WL_AP_LISTENING", - 8: "WL_AP_CONNECTED", - 9: "WL_AP_FAILED", - 10: "WL_NO_SHIELD", - } - if n in t: - text = t[n] - return text - - # M4 R/W Pin Assignments M4_D_W_PIN = DigitalInOut(board.A1) # digital write to ESP_D_R_PIN M4_D_W_PIN.direction = Direction.OUTPUT @@ -96,14 +76,6 @@ def esp_status_text(n): ) ) -print( - "ESP32 Status: ", - esp.status, - esp_status_text(esp.status), - "Connected?", - esp.is_connected, -) - # initial digital write values m4_d_w_val = False esp_d_w_val = False From 5e5a5a526c5d2e0d3e310cbd189015cab5ec682e Mon Sep 17 00:00:00 2001 From: anecdata <16617689+anecdata@users.noreply.github.com> Date: Tue, 21 Apr 2020 11:31:05 -0500 Subject: [PATCH 12/12] removed unnecessary code --- examples/gpio/esp32spi_gpio.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/gpio/esp32spi_gpio.py b/examples/gpio/esp32spi_gpio.py index 318eb2f..6ad947a 100644 --- a/examples/gpio/esp32spi_gpio.py +++ b/examples/gpio/esp32spi_gpio.py @@ -69,10 +69,9 @@ def esp_init_pin_modes(din, dout): espfirmware += "{:c}".format(_) print("ESP32 Firmware:", espfirmware) -esp_MAC_address = esp.MAC_address print( "ESP32 MAC: {5:02X}:{4:02X}:{3:02X}:{2:02X}:{1:02X}:{0:02X}".format( - *esp_MAC_address + *esp.MAC_address ) )