2025-11-10 8:42 AM - last edited on 2025-11-10 9:28 AM by Andrew Neil
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.
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.
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.
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.
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.)
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).
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.
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.
Solved! Go to Solution.
2025-11-10 9:02 AM - edited 2025-11-10 9:13 AM
Is there a question, or are you just presenting this as something others might find useful?
Something seems to have gone wrong with your "Key API usage (address parameter)":
@tantheman wrote:
2025-11-10 9:02 AM - edited 2025-11-10 9:13 AM
Is there a question, or are you just presenting this as something others might find useful?
Something seems to have gone wrong with your "Key API usage (address parameter)":
@tantheman wrote:
2025-11-10 9:18 AM
Just something that might be useful.
2025-11-10 9:24 AM
yes the key api usage was showing highlighted errors while posting ... so just put the code instead ......
"A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work."
thank you for this advice.
2025-11-10 9:27 AM - edited 2025-11-10 9:29 AM
You might want to fix those missing bits / formatting oddities, then.
I edited the title to clarify.
2025-11-10 9:31 AM
HAL_I2C_Master_Transmit(&hi2c1, (0x28 << 1), &c, 1, 10); key API usage
2025-11-10 10:18 AM
Thanks for sharing this detailed setup, @tantheman! The breakdown of HAL address handling, pull-up strategy, and pin mapping is really useful for anyone integrating STM32 with ESP32 over I²C. Clear step-by-step guides like this save a lot of trial-and-error time for mixed MCU setups.