Demo FYI: ESP32 AND STM32 I2C INTERFACE
- November 10, 2025
- 3 replies
- 566 views
STM32 (I²C Master) → ESP32 (I²C Slave) “WASD” Command Link with UART Input
Summary
This project establishes a reliable data path from a PC to an STM32 microcontroller over UART, and from the STM32 to an ESP32 over I²C. The user types a line containing WASD characters in a serial terminal connected to the STM32. Upon pressing Enter, the STM32 transmits only the valid WASD bytes over I²C to the ESP32. The ESP32 runs in true I²C slave mode and logs each received command. The design emphasizes correct I²C electrical configuration (external pull-ups), address handling in STM32 HAL, and predictable serial console behavior.
Hardware Setup
MCU (Master): STM32F103 (I²C1: PB6=SCL, PB7=SDA; UART1: PA9=TX, PA10=RX).
Target (Slave): ESP32 family device.
Classic ESP32: SDA=GPIO21, SCL=GPIO22.
ESP32-S3: SDA=GPIO8, SCL=GPIO9.
Pull-ups (mandatory): 4.7 kΩ from SDA→3.3 V and SCL→3.3 V (one pair for the entire bus).
Common reference: Tie grounds together (STM32, ESP32, and any USB-TTL adapters).
I²C speed: Start at 100 kHz. Keep wiring short for signal integrity.
GPIO modes on STM32:
I²C pins as Alternate Function Open-Drain, No Pull.
UART pins at standard settings for 115200 8N1.
Firmware Overview
STM32 (Master, HAL)
Collects a line of input from the UART. The line terminates on CRor LF.
Filters out only WASD(case-insensitive) from the collected line.
Transmits each valid character as a 1-byte I²C write to the slave at address 0x28.
Key API usage (address parameter):
This shifts the 7-bit address to the “8-bit address” field as expected by the HAL API variant in use. The peripheral appends the R/W bit automatically.
ESP32 (Slave, Arduino core using ESP-IDF driver)
Configured as an I²C slave at 0x28 using the ESP-IDF driver from within Arduino.
Internal pull-ups are disabled in software; external 4.7 kΩ pull-ups are required.
Continuously reads with and prints OK WASD for valid bytes.
Key Fixes (Revised)
STM32 HAL address parameter
Issue: Passing 0X28 directly to yielded I2C TX Error with this HAL variant.
Resolution: Pass the address shifted left by 1, i.e. (0x28<<1). The device’s true address remains 0x28 (7-bit). The HAL parameter represents the 7-bit address in the upper bits of the 8-bit address field; the hardware then appends the R/W bit.
Serial line ending on STM32
Issue: Serial monitor “Line ending: None” prevented the newline from being delivered; the UART line reader never completed.
Resolution: Set line ending to CR or LF. Pressing Enter now terminates the line and triggers I²C transmission.
Pull-up strategy and bus integrity
Issue: Unreliable or floating SCL/SDA, especially with longer pigtails or varied boards.
Resolution: Disable internal pulls on the data/clock lines in code and install external 4.7 kΩ pull-up resistors to 3.3 V on both SCL and SDA. This ensures a clean idle high and proper rise time on an open-drain bus.
Pin mapping consistency (ESP32 families)
Issue: Using classic-ESP32 pin assumptions (21/22) on ESP32-S3 boards (8/9) or vice-versa causes silent failures.
Resolution: For classic ESP32, use SDA=21, SCL=22. For ESP32-S3, use SDA=8, SCL=9. Match the pins you physically wired.
(Note: original list item numbering was compacted; the previously listed “4” was intentionally removed per request.)
Expected Results
User input: wasd then enter on the STM32 terminal (CR or LF line ending).
STM32 terminal: echoes input, then sent on i2c.
ESP32 terminal: WASD
Characters outside WASD are ignored by the ESP32 logging logic (they are not transmitted by the STM32).
Troubleshooting Checklist (Fast Path)
ESP32 running in slave mode and prints “Ready @0x28 …”.
Common ground across STM32, ESP32, and any USB-TTL.
Pull-ups present: 4.7 kΩ from SDA→3.3 V and SCL→3.3 V; idle lines ~3.3 V.
Pin mapping correct: PB6↔SCL, PB7↔SDA on STM32; ESP32 pins as per your variant.
HAL call uses shifted address: (0x28<<1).
Serial line ending: CR or LF on the STM32 terminal.
Bus speed: 100 kHz to start; short leads; avoid excessive capacitance.
You should detect 0x28.
Rationale: Addressing and Signal Integrity
Addressing: I²C devices are identified by a 7-bit address. On the wire, the master sends an 8-bit value comprising the 7-bit address plus the R/W bit. Some STM32 HAL variants specify the API parameter as the 8-bit address field (i.e., 7-bit address shifted left by 1). Supplying (addr<<1)to the HAL while leaving the device configured at its 7-bit address ensures consistent behavior.
Signal integrity: I²C lines are open-drain. Devices pull the lines low; external resistors pull them high. External 4.7 kΩ pull-ups to 3.3 V establish a defined idle high level and acceptable rise time, improving reliability with mixed boards, jumpers, and typical bench wiring lengths.

