Teensy and ESP32 - for WiFiNina


Senior Member+
Looking to make a way to use WifiNina device connect to Teensy.

For devices without USB like AdaFruit 4201 Breakout - or even USB.

See post #21 after the introduction - got it working with a generic ESP32 PICO D4 DevKit board. Using the same GPIO numbers as AdaFruit dies in their board with their custom WiFiNina 1.7.7_BIN and it is working just like the AdaF board on sample sketches. The ProxyDual sketch works for Program and Reset when the correct WiFiNina BIN needs to be placed on the ESP32.

This sketch works with the '4201' Breakout and today with a DevKit Pico V4.1 - Pin connects are commented in the sketch: ProxyDual

4201 Breakout has no USB so UART upload is required - Teensy can be used in place of FTDI. The DevKit PICO has USB and buttons - but none would take to program mode but above sketch worked.

Build ProxyDual with "USB: Dual Serial" and primary USB echos anything the UART transfers. It also takes two commands 'r'eset of the ESP32 and enter ESP32 to 'p'rogram mode. 'p' then readies USB1 port as presented to host to perform upload. When complete 'r' will reset the ESP32 to run. This is the same as 9 years ago for ESP8266 - but Dual Serial makes it better to control and see the actions as above.

Updated the 4201 Breakout with the latest NINA_W102-1.7.7.bin AdaFruit release using the above and it worked to run samples from Teensy 4.0 and 4.1.

That uses an older WROOM-32E Module and wondering about using a newer Pico D4 the Espressif DevKit version arrived today. Used it USB connector to put on the 1.7.7 firmware but SPI connect showed 'no wifi' in scanner sketch. It came in Download Ready condition so upload was easy.

Is an MS-Dos/Command box, knowing the proper USB port { direct to device or the Teensy 2nd USB1 - here COM15 } and in the folder with the .BIN to upload with the path to your local install of esptool.exe:
"C:\Users\somebody\AppData\Local\Arduino15\packages\esp32\tools\esptool_py\4.5.1/esptool.exe"  --port COM15 --before no_reset --baud 115200 write_flash 0 NINA_W102-1.7.7.bin

and DualProxy worked to upload a second copy of the released NINA_W102-v1.5.0.bin

{ wasn't meant to start this thread yet - and it was going to be more complete but ...}
What are the default ESP32 pins used by the WifiNina software?
> supposing it uses the 'default' ESP32 SPI instance pins {cs=5, MOSI=23, MISO=19, SCK=18} seem to be right for the DevKit Pico D4 in use
BUT> What pin is used for BUSY? : This is a feedback pin from the ESP32 to 'host' indicating when the ESP32 is busy.

So the DevKit PICO is not connecting - even for simple SCAN sketch, and the BUSY pin is unknown and that may be why? Or maybe it uses HSPI as default instead of VSPI - but without BUSY known it can't be expected to function at all.

Anyone know where in the sources this pin is defined where the source if Espressif IDE coded? SRC files are the links above 1.7.7 and 1.5.0.
Last edited:
Looking at the M5-PICO spec. I do not believe that SPI is brought out to the I/O connectors.
The only available default interfaces are listed as: UART, I2C, I2S, IR remote controller, GPIO, pulse counter.
@BriComp - Thx!
Somehow BUSY/Ack(?) is attached here to pin #33 on the PICO - not sure what led to that insight ...
So returned to AdaF WifiNina 1.7.7.BIN (Go ProxyDual!) - still : "Communication with WiFi module failed!"

The AdaF 'custom' firmware is required 'they note' because "The official WiFi101 library won't work because it doesn't support the ability to change the pins."? Reading that now it seems that is from the 'sketch' to interface to 'breakout' from the Host device in use.

ProxyDual Use/Value is that a Teensy with Dual Serial can UART program an ESP32 module over UART. Once desired firmware is in place it isn't needed - unless it needs an update. If the ESP32 is embedded in a Teensy project, or the USB isn't present or accessible this works without FTDI or other machinations. And seems as long as the MAIN sketch is built Dual Serial this could be a callable function that could return after processing the upload.

This isn't the M5-PICO but https://docs.espressif.com/projects/esp-idf/en/release-v3.3/get-started/get-started-pico-kit.html as ordered from AMZN Stemedu ESP32-PICO-KIT V4.1. That was the only PICO D4 with quick delivery and under $20 that showed all needed pins.
I looked at the Schematic for a number of Arduino ESP32 co-processor boards.
Was not questioning what you found - just saying the SOMEHOW PIN #33 was what I came across as well and had wired before making this thread.

I need to re-check all my wires ...

re p#7:: Indeed, since the AdaF worked on the breakout that is what is in use here. Those pin defs are on the 'Teensy' side. What is a mystery is: Can the Pico D4 run the same firmware and if so what pins present the same function as on the AdaF breakout. The UART is clear. There are two sets of SPI pins HSPI and VSPI - indeed one may overlap with internal pin use and may have forced the use of the other. That was the next test swap - but the BUSY/ACK pin might be critical to have properly connected. Also SPI has defined pins - not sure if SS/CS might vary beyond the defined pins.
Was not questioning what you found - just saying the SOMEHOW PIN #33 was what I came across as well and had wired before making this thread.
I didn't think you were, I thought that you were asking how I came across that.
Still we found out what was potentially being used, which is the good thing.
Whether the ...D4 can work with WiFiNina is a question I was pondering. Perhaps we may have to re-engineer the binary to make it do so.
I didn't think you were, I thought that you were asking how I came across that.
Still we found out what was potentially being used, which is the good thing.
Whether the ...D4 can work with WiFiNina is a question I was pondering. Perhaps we may have to re-engineer the binary to make it do so.
:) You did good research to find schematic - hadn't gotten that far.
Looked but not found any details on supported ESP32 'chips' or what really needed to make them 'tick'. In the AdaF case it is just a Wroom module - but most refer to it by some other module name.
Need to know the base connect pins are right to have any expectation of it working ...
Found this - will have to read to end soon to see if it has a final answer
I found the code below here which was from the Original Arduino camp.
I believe it shows the default SPI setup as: SPISClass SPIS(VSPI_HOST, 1, 12, 23, 18, 5, 33);
i.e. MOSI pin 12, MISO pin 23, sclk pin 18, cs pin 5 and ready pin 33.
Looking at the AdaFruit version the MOSI pin changes to 14.

  This file is part of the Arduino NINA firmware.
  Copyright (c) 2018 Arduino SA. All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include <string.h>

#include <driver/gpio.h>
#include <driver/spi_slave.h>

#include "wiring_digital.h"
#include "WInterrupts.h"

#include "SPIS.h"

SPISClass::SPISClass(spi_host_device_t hostDevice, int dmaChannel, int mosiPin, int misoPin, int sclkPin, int csPin, int readyPin) :

int SPISClass::begin()
  spi_bus_config_t busCfg;
  spi_slave_interface_config_t slvCfg;

  pinMode(_readyPin, OUTPUT);
  digitalWrite(_readyPin, HIGH);

  _readySemaphore = xSemaphoreCreateCounting(1, 0);

  attachInterrupt(_csPin, onChipSelect, FALLING);

  memset(&busCfg, 0x00, sizeof(busCfg));
  busCfg.mosi_io_num = _mosiPin;
  busCfg.miso_io_num = _misoPin;
  busCfg.sclk_io_num = _sclkPin;

  memset(&slvCfg, 0x00, sizeof(slvCfg));
  slvCfg.mode = 0;
  slvCfg.spics_io_num = _csPin;
  slvCfg.queue_size = 1;
  slvCfg.flags = 0;
  slvCfg.post_setup_cb = SPISClass::onSetupComplete;
  slvCfg.post_trans_cb = NULL;

  gpio_set_pull_mode((gpio_num_t)_mosiPin, GPIO_FLOATING);
  gpio_set_pull_mode((gpio_num_t)_sclkPin, GPIO_PULLDOWN_ONLY);
  gpio_set_pull_mode((gpio_num_t)_csPin,   GPIO_PULLUP_ONLY);

  spi_slave_initialize(_hostDevice, &busCfg, &slvCfg, _dmaChannel);

  return 1;

int SPISClass::transfer(uint8_t out[], uint8_t in[], size_t len)
  spi_slave_transaction_t slvTrans;
  spi_slave_transaction_t* slvRetTrans;

  memset(&slvTrans, 0x00, sizeof(slvTrans));

  slvTrans.length = len * 8;
  slvTrans.trans_len = 0;
  slvTrans.tx_buffer = out;
  slvTrans.rx_buffer = in;

  spi_slave_queue_trans(_hostDevice, &slvTrans, portMAX_DELAY);
  xSemaphoreTake(_readySemaphore, portMAX_DELAY);
  digitalWrite(_readyPin, LOW);
  spi_slave_get_trans_result(_hostDevice, &slvRetTrans, portMAX_DELAY);
  digitalWrite(_readyPin, HIGH);

  return (slvTrans.trans_len / 8);

void SPISClass::onChipSelect()

void SPISClass::handleOnChipSelect()
  digitalWrite(_readyPin, HIGH);

void SPISClass::onSetupComplete(spi_slave_transaction_t*)

void SPISClass::handleSetupComplete()
  xSemaphoreGiveFromISR(_readySemaphore, NULL);

SPISClass SPIS(VSPI_HOST, 1, 12, 23, 18, 5, 33);
Last edited:
I have done some more sleuthing and for the board that @defragster is using (ESP Pico Kit v4.1) the following SPI pins are declared/available:
mosi - vspid 23, hspid 13
miso - vspiq 19, hspiq 12
clk - vspiclk 18, hspiclk 14
cs - vspics0 5, hspics0 15
Thanks - GREAT Work @BriComp finding that: SPISClass SPIS(VSPI_HOST, 1, 12, 23, 18, 5, 33);
> though not sure what 1,12 {12 is Hspi MISO) is and where is 19 for MISO on Vspi

Didn't get to look much today - I can confirm that p#12 is what I got and am trying for Vspi, and pin 33.
> I have a POOR print of the board and was finding the "H" alternate pins to the "V" pins I have wired.
> --- making a higher quality print of DevKit PICO, and the schematic you found for AdaFBreakout
If I'm reading this right, they are using Hspi pins! MCU:15,14,13 {<EDIT>: pretty sure that isn't right ...}

I didn't even get to verify my wires with today's interruptions.

I wonder if I should swap pin 19 to 12 ??? - that didn't do it ... I'll have to look after Zzzz's later 'today'

PCB is wired and awkward and the silkscreen is tiny and not as clear as on a Teensy - hard to move with 10 wires ... can't imagine the wire troubles on the parallel displays - will be good to have the @KurtE board when I hook one up :) I could move it to a breadboard - maybe should and use real wire instead of the 'bulk' current wires.
If I'm reading this right, they are using Hspi pins! MCU:15,14,13
I think you will find that those are the hardware pin numbers, not the MCU pin numbers.

From what I can see the Adafruit modified WiFiNina uses the following SPIS setup: SPISClass SPIS(VSPI_HOST, 1, [B]14[/B], 23, 18, 5, 33);
Note the change to MCU pin 14 for MOSI (vspid).

As I see it the AdaFruit (and also Arduino) WiFiNina WILL NOT WORK with the ESP32 PICO D4.

If VSPI is to be used then SPISClass will need to be changed to SPISClass SPIS(VSPI_HOST, 1, 23, 19, 18, 5, 33);
Last edited:
Ok, further Sleuthing. SPI Pins as used by various ESP32s are shown below.
You can see that the ESP32 PICO D4 pin assignments match the UBLOX W10 used with the Nano33IoTV2.0 and UNO V2 Wifi.
So if you load the Arduino version of WiFiNINA onto the PICO D4 it might work.

I am confused by the indication of use of GPIO12 for MISO as it comes from the H channel.
It might be that they are using both V and H channels to speed throughput??!! That is the only thing I can think of.
Last edited:
GOT IT: Went to the schematic for the 4201 AdaF Breakout - and the AdaF linked 1.7.7 firmware
Firmware OK
MAC: 30:83:98:D7:36:AC
Scanning available networks...
** Scan Networks **
number of available networks:10
0) aNET    Signal: -26 dBm    Encryption: WPA2
1) aNET_GuestNG    Signal: -26 dBm    Encryption: WPA2
2) aNET    Signal: -62 dBm    Encryption: WPA2
3) aNET_GuestNG    Signal: -62 dBm    Encryption: WPA2
4) gleGuest1    Signal: -70 dBm    Encryption: WPA2
5) gleZIP    Signal: -71 dBm    Encryption: WPA2
6) aNET    Signal: -76 dBm    Encryption: WPA2
7) aNET_GuestNG    Signal: -77 dBm    Encryption: WPA2
8) TMOBILE-5749    Signal: -86 dBm    Encryption: WPA2
9) HP-Print-61-ENVY 5530 series    Signal: -93 dBm    Encryption: None

Pins Teensy to DevKit Pico D4
4 :: Busy/ACK :: 33
10 :: CS :: 5
13 :: CLK :: 18
12 :: MISO :: 23
11 :: MOSI :: 14

And the WiFiWebServer sketch works too:
Client disconnected    Millis() is 44559
New client
GET /favicon.ico HTTP/1.1
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-GPC: 1
Accept-Language: en-US,en
Accept-Encoding: gzip, deflate

Client disconnected    Millis() is 44626
Last edited:
Finally got to the AdaF WiFina source spelling out and confirming the pins it is built to use:

SPISClass::SPISClass(spi_host_device_t hostDevice, int dmaChannel, int mosiPin, int misoPin, int sclkPin, int csPin, int readyPin) :
// ...
SPISClass SPIS(VSPI_HOST, 1, 14, 23, 18, 5, 33);

Not seeing that the AdaF 1.7.7.BIN has bluetooth support built in?

So uing other WiFiNina .BIN could work instead if using their settings like above with those built in the .BIN:
SPISClass SPIS(VSPI_HOST, 1, 12, 23, 18, 5, 33);

Found that SPI to the ESP32 is running at 8MHz.