Files
alexandClaude Sonnet 5 6d835bc44c Initial import: ESP32 ZiFi32 firmware with SMB2/3 VFS bridge
Based on https://github.com/tslabs/zx-evo/tree/master/pentevo/esp32,
extended with an SMB2/3 server that exposes the ZX Evolution's real SD
card as a network share (browse/download/upload/delete), plus assorted
stability and protocol-correctness fixes in vfs_bridge.cpp and the
vendored libsmb2 - see firmware/README.md for the detailed changelog
and current status.

Also adds:
- firmware/: ready-to-flash compiled binaries + flashing instructions
- hardware/: adapter board connection diagram, PCB photo, and Gerber
  files
- src/third_party/libsmb2: vendored from sahlberg/libsmb2 (LGPL-2.1,
  see its own COPYING/LICENCE files), de-submoduled to a plain
  snapshot with local patches applied directly

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 16:00:05 +03:00

19 KiB

ESP32 SPI WiFi

Description

The module is based on ESP32-S2 and provides high-speed WiFi access via SPI bus.

The key-features are:

  • 4-wire 1-bit SPI interface, Slave Mode.
  • Up to 14Mbit/s data transfer speed.
  • DMA capable transfers.

Programming

Communication protocol

The SPI Slave communication protocol provides a few types of transactions: READ_REGS, WRITE_REGS, READ_DATA, WRITE_DATA.

READ_REGS

This type of transaction allows reading a number of bytes from the register memory. It can be used to read a register value or a status.

Scenario
Step Action
1 Assert SPI_CS.
2 Write SPI_DATA with 0x02.
3 Write SPI_DATA with register number.
4 Write SPI_DATA with two dummy bytes.
5 Read one or more bytes from SPI_DATA.
6 De-assert SPI_CS.
Read 1-byte register in C
__sfr __at 0x57 SPI_DATA;
__sfr __at 0x77 SPI_CTRL;
#define SPI_ESP_CS_ON  0x0B
#define SPI_ESP_CS_OFF 0x03

u8 esp_rd_reg(u8 reg_num)
{
  SPI_CTRL = SPI_ESP_CS_ON;
  SPI_DATA = 0x02;
  SPI_DATA = reg_num;
  SPI_DATA;             // dummy
  SPI_DATA;
  u8 rc = SPI_DATA;
  SPI_CTRL = SPI_ESP_CS_OFF;

  return rc;
}

WRITE_REGS

This type of transaction allows writing a number of bytes in the register memory. Not used except for command issuing. Each WRITE_REGS transaction triggers the command execution. Thus, all arguments must be transmitted in the same transaction along with the command.

Scenario
Step Action
1 Assert SPI_CS.
2 Write SPI_DATA with 0x01.
3 Write SPI_DATA with register number.
4 Write SPI_DATA with one dummy byte.
5 Write SPI_DATA with one or more bytes.
6 De-assert SPI_CS.
Write 1-byte register in C

The following code snippet is just an example not to use. Please refer to 'Write command' snippet for more practical case.

__sfr __at 0x57 SPI_DATA;
__sfr __at 0x77 SPI_CTRL;
#define SPI_ESP_CS_ON  0x0B
#define SPI_ESP_CS_OFF 0x03

void esp_rd_reg(u8 reg_num, u8 reg_value)
{
  SPI_CTRL = SPI_ESP_CS_ON;
  SPI_DATA = 0x01;
  SPI_DATA = reg_num;
  SPI_DATA;              // dummy
  SPI_DATA = reg_value;
  SPI_CTRL = SPI_ESP_CS_OFF;
}

WRITE COMMAND

This is a most used WRITE_REGS case, which triggers a command execution.

Write command in C

The following code snippet is just an example not to use. Please refer to 'Write command' snippet for more practical case.

__sfr __at 0x57 SPI_DATA;
__sfr __at 0x77 SPI_CTRL;
#define SPI_ESP_CS_ON  0x0B
#define SPI_ESP_CS_OFF 0x03

void esp_cmd(u8 cmd)
{
  SPI_CTRL = SPI_ESP_CS_ON;
  SPI_DATA = 0x01;
  SPI_DATA = 0x00;          // ESP_REG_COMMAND
  SPI_DATA;                 // dummy
  SPI_DATA = cmd;           // Command
  SPI_DATA = 0x00;          // Reset status
  SPI_CTRL = SPI_ESP_CS_OFF;
}

READ_DATA

The transaction must only be launched when STATUS register contains DATA_S2M status.

The number of bytes to transfer must be read from xx_SIZE register before the transaction.

Scenario
Step Action
1 Assert SPI_CS.
2 Write SPI_DATA with 0x04.
4 Write SPI_DATA with three dummy bytes.
5 Read one or more bytes from SPI_DATA.
6 De-assert SPI_CS.
7 Execute esp_cmd(DATA_END);
Read a data array in C
__sfr __at 0x57 SPI_DATA;
__sfr __at 0x77 SPI_CTRL;
#define SPI_ESP_CS_ON  0x0B
#define SPI_ESP_CS_OFF 0x03

void esp_recv(u8 *addr, u16 num)
{
  SPI_CTRL = SPI_ESP_CS_ON;
  SPI_DATA = 0x04;
  SPI_DATA; // dummy read means write with 0xFF
  SPI_DATA;
  SPI_DATA;
  while (num--) *addr++ = SPI_DATA;
  SPI_CTRL = SPI_ESP_CS_OFF;
  esp_cmd(ESP_CMD_DATA_END);
}

WRITE_DATA

The transaction must only be launched when STATUS register contains DATA_M2S status.

The number of bytes to transfer must be read or written from/to xx_SIZE register before the transaction.

ATTENTION! When writing data, all transactions must be truncated to 4 bytes. If we need to write 1 byte only, we must write 4 bytes and so on.

To do: add code example.

Registers

All registers are read/writeable from both SPI Master via SPI and Slave firmware. At a low level, they're a shared memory cells inside ESP32 Slave. The total number of registers is 64.

Common registers are COMMAND and STATUS. Registers with addresses 2-63 are used by the ESP32 firmware for different functions depending on a command.

Register summary

Name Register address, HEX Size Description
COMMAND 00 1 Command. Set by Master.
STATUS 01 1 Status of device. Set by Slave while the command is being executed or after its execution. Must be zeroed by Master in the same transaction as the command issued, to ensure the correct synchronization.
PARAM/RESP 02 0..61 If written, must be sent in the same transaction as the command issued.
See parameter/response list for individual commands.

STATUS register

Name Code, HEX Description
IDLE 00 No status. Must be set by Master along every command issued.
BUSY 01 Device is busy executing a command.
READY 02 Device has completed command execution and is ready for a new command.
ERROR 03 Error happened. Use GET_ERROR command to get the error code.
DATA_M2S 04 Device is ready to receive data from host.
DATA_S2M 05 Device is ready to send data to host.

ERROR register

If a command has been completed with an error, the register 02 automatically indicates error code.

Name Register address, HEX Size Description
ERROR 02 1 Error.

The following table contains error codes.

Name Code, HEX Description
NONE 00 No error.
WRONG_PARAMETER 01 Wrong parameter.
INVALID_COMMAND 02 Invalid command.
WRONG_STATE 03 Command cannot be executed in current state.
AP_NOT_CONN 10 Cannot connect AP.
RESET FE Is set when reset happened.

Commands

Command summary

Name Code, HEX Description
NOP 00 No operation. Issuing this command has no effect.
GET_ERROR Get extended error info.
GET_INFO 02 Get device info string.
GET_CHIP_INFO 03 Get chip info.
GET_CAPS Get device capabilities.
GET_HEAP Get heap size.
GET_NET_CAPS Get network capabilities.
GET_NETSTATE 11 Get network connection state.
WSCAN 12 Scan Wi-Fi APs.
SET_AP_NAME 13 Set Wi-Fi AP name.
SET_AP_PWD 14 Set Wi-Fi AP password.
GET_AP_NAME 15 Get Wi-Fi AP name.
AP_CONNECT 16 Connect to selected AP.
AP_DISCONNECT 17 Disconnect from AP.
SET_AUTOCONNECT 18 Set AP auto-connect.
GET_IP 19 Get own Wi-Fi IP, net mask, net gateway.
RESOLVE_DNS Resolve DNS name.
OPEN_UDP
CLOSE_UDP
STATUS_UDP
SEND_DATAGR
RECV_DATAGR
OPEN_TCP
CLOSE_TCP
STATUS_TCP
SEND_TCP
RECV_TCP
SET_WSCAN_PARAMS Set parameters for Wi-Fi AP scan.
EXT_STATUS_TCP ?
CONF_AUTO_IP ?
CONF_IP ?
DATA_END D0 End DATA transaction. (A workaround for S3 CMD8 bug).
BREAK E0 Break an operation.
RESET EE Reset device. To be issued in the case of an unpredicted behavior. The reset takes milliseconds to complete. The STATUS register after the reset must contain READY value. The EXT_STAT register must contain RESET value.
GET_RND F0 Generate random numbers array.
TEST2 F1 Generate tunnel DL for FT812.
TEST3 F2 Generate 3D model DL for FT812.

Miscellaneous commands

GET_INFO

Get device information string. The string is not 0-terminated.

In: none.

Out:

Name Register address, HEX Size Description
INFO_SIZE 02 1 Info string size.

[DATA] - Info string.

GET_CHIP_INFO

Get ESP module hardware parameters.

In: none.

Out: See table below.

Name Register address, HEX Size, bytes Description
CPU_TYPE 02 1 ESP chip type, hex:
02 - ESP32-S3
CPU_CORES 03 1 Number of CPU cores.
CPU_FREQ 04 2 CPU frequency, MHz.
CPU_SI_REV 06 1 Silicon revision.
WIFI 07 1
BLE 08 1
FLASH_SIZE 09 1 Flash size, MB.
FLASH_TYPE 0A 1
FLASH_FREQ 0B 1
PSRAM_SIZE 0C 1 PSRAM size, MB.
PSRAM_TYPE 0D 1
PSRAM_FREQ 0E 1
GET_CAPS

Read device capabilities.

In: none.

Out:

Name Bit Mask, Hex Description
BREAK

Break execution of current command. If no command is being executed, this has no effect.

In: none.

Out: none.

Wi-Fi commands

Most Wi-Fi commands require time to complete. While in progress, the STATUS register indicates BUSY state. It changes to READY upon command completion if successful, or to ERROR, if not.

GET_NETSTATE

Get network state.

In: none.

Out: Network state. See the table below.

Name Register address, HEX Size Description
NETSTATE 02 1 Network state.

NETSTATE

Name Code, HEX Description
NETWORK_CLOSED 00 No network connection established.
NETWORK_OPENING 01 Network connection in progress.
NETWORK_OPEN 02 Network connection established.
NETWORK_CLOSING 03 Network disconnection in progress.
NETWORK_UNKNOWN FF Network state undefined.
WSCAN

Scan for Wi-Fi APs. Scanned APs are sorted by RSSI, highest first. Requires STATUS register polling to check completion.

In:

Name Register address, HEX Size Description
AP_MAX 02 1 Max number of AP to scan.

Out:

Name Register address, HEX Size Description
AP_TABLE_SIZE 02 2 AP table size.

[DATA] - scanned AP table. See the format below.

Name Size, bytes Description
NUM_APS 1 Number of APs scanned.
AP_AUTH 1 AP authorization type.
AP_RSSI 1 AP RSSI, dB.
AP_CHAN 1 AP primary channel
AP_SSID_LEN 1 AP SSID length.
AP_SSID <AP_SSID_LEN> AP SSID.
... repeat AP_xx for <NUM_APS>

APn_AUTH

Name Value, HEX Description
AP_AUTH_OPEN 00 Open
AUTH_WEP 01
AUTH_WPA_PSK 02
AUTH_WPA2_PSK 03
AUTH_WPA_WPA2_PSK 04
AUTH_EAP 05
AUTH_WPA3_PSK 06
AUTH_WPA2_WPA3_PSK 07
AUTH_WAPI_PSK 08
AUTH_OWE 09
AUTH_WPA3_ENT_192 0A
AUTH_MAX 0B
SET_AP_NAME

Set AP name. 32 bytes max.

In:

Name Register address, HEX Size Description
AP_NAME_LEN 02 1 AP name length.
AP_NAME 03 <AP_NAME_LEN> AP name.

Out: none.

SET_AP_PWD

Set AP password. 61 bytes max.

In:

Name Register address, HEX Size Description
AP_PWD_LEN 02 1 AP password length.
AP_PWD 03 <AP_PWD_LEN> AP password.

Out: none.

GET_AP_NAME

Set AP name. 32 bytes max.

In: none.

Out:

Name Register address, HEX Size Description
AP_NAME_LEN 02 1 AP name length.
AP_NAME 03 <AP_NAME_LEN> AP name.
**AP_**CONNECT

Connect to the selected AP. Requires STATUS register polling to check completion.

In: none.

Out: none.

AP_DISCONNECT

Disconnect AP. Requires STATUS register polling to check completion.

In: none.

Out: none.

SET_AUTOCONNECT

Enable or disable auto-connect. SET_AP_NAME and SET_AP_PWD must be set before this command.

In:

Name Register address, HEX Size Description
AUTOCONNECT 02 1 Mode:
0 - disabled.
1 - enabled.

Out: none.

GET_IP

Get network IP addresses.

In: none.

Out:

Name Register address, HEX Size Description
IP 02 4 Last DNS resolved IP address.
OWN_IP 06 4 Own IP address.
MASK 0A 4 Net mask.
GATE 0E 4 Net gateway.
RESOLVE_DNS

Resolve IP address from domain name, which length must not be longer than 255 symbols. Requires STATUS register polling to check completion.

In:

Name Register address, HEX Size Description
DNAME_LEN 02 1 Domain name length.
DNAME 03..3F 1..61 Domain name.

If DNAME_LEN<= 61, then it is sent using registers 03-3F. If it is longer than 61 symbols, send the symbols starting from 62 using DATA transaction (currently not supported).

Out:

Same as GET_IP.