PROJECT #03 · IoT · MQTT · I2C

ESP32 & FRDM-K64F
Temperature Monitor & Remote LED Control

A layered IoT architecture implementing real-time environmental sensing, secure cloud telemetry, and bidirectional actuation control across two heterogeneous microcontroller platforms communicating over an I2C local bus.

Assignment Requirements
  • Connect the TMP36 analogue temperature sensor to the FRDM-K64F (PTB2 / ADC0_SE12).
  • Provision ESP32 Wi-Fi credentials without hardcoding — use ESP Touch SmartConfig.
  • Establish a TLS-secured MQTT session with HiveMQ Cloud broker.
  • Implement I2C communication with ESP32 as Master and FRDM-K64F as Slave.
  • Poll temperature every 2 seconds via I2C; publish to temperature/value topic (or error on failure).
  • Subscribe to led/state topic; propagate LED commands (red / green / blue / off) to FRDM-K64F over I2C.
ESP32 FRDM-K64F TMP36 I2C MQTT HiveMQ SSL/TLS SmartConfig MCUXpresso IDE VS Code / PlatformIO
01

System Architecture

The system is structured around a dual-node embedded architecture in which functional responsibilities are partitioned according to each platform's strengths. The ESP32 operates as a Cloud Gateway (Master), orchestrating Wi-Fi connectivity, TLS-secured MQTT telemetry, and I2C bus arbitration. The FRDM-K64F operates as an Edge Sensing and Actuation Node (Slave), performing analogue-to-digital conversion of the TMP36 sensor output and driving the onboard RGB LED in response to remotely issued commands.

Master — Cloud Gateway
ESP32
Wi-Fi (SmartConfig provisioning) · TLS/MQTT · I2C Master · VS Code / PlatformIO · PubSubClient, WiFiClientSecure
Slave — Edge Node
FRDM-K64F (MK64FN1M0VLL12)
ADC16 driver · I2C Slave (interrupt-driven) · RGB LED actuation · MCUXpresso IDE · SDK ksdk2_0
Sensor
TMP36 — Analogue Temperature
Voltage-proportional output · 3-wire connection · Routed to PTB2 (ADC0_SE12) on FRDM-K64F
Cloud Broker
HiveMQ Cloud
SSL/TLS port 8883 · Web Client dashboard · Topics: temperature/value, led/state
02

End-to-End Data Flow

The system operates two independent but concurrent data paths. The telemetry path propagates environmental measurements from the physical sensor to the cloud at a fixed 2-second cadence. The command path routes actuation instructions in the reverse direction — from a web client through the broker and down to the physical actuator.

Telemetry Path — Sensor to Cloud

TMP36
ADC16 (K64F)
I2C Slave TX
ESP32 I2C Master
MQTT → HiveMQ

Command Path — Cloud to Actuator

HiveMQ Web Client
MQTT Broker
ESP32 Subscriber
I2C Slave RX
RGB LED
Polling interval: The ESP32 initiates an I2C read transaction every 2 seconds. Upon address match, the FRDM-K64F ISR fires, transfers the most recent ADC-converted temperature value to the bus, and the ESP32 subsequently publishes it to the temperature/value MQTT topic. In the event of a bus error or conversion fault, an error payload is published instead.
03

Development Environments & Firmware Configuration

Firmware development for the two platforms was conducted in parallel across two dedicated IDEs, each selected to leverage native SDK and toolchain support for its respective target.

ESP32 Master Firmware — VS Code / PlatformIO

The ESP32 application was developed using VS Code with the PlatformIO extension. The firmware incorporates PubSubClient for MQTT session management and WiFiClientSecure for TLS certificate validation against the HiveMQ broker. MQTT connection parameters — port (8883), client ID, username, password, and server URL — are defined as compile-time constants. The TLS root certificate is embedded as a raw string literal (R"EOF(...)") within the source file, ensuring the TLS handshake is validated against a trusted certificate authority without relying on a system certificate store.

FRDM-K64F Slave Firmware — MCUXpresso IDE

The K64F application was developed in MCUXpresso IDE using the NXP KSDK 2.0 framework. Pin multiplexing was configured via the MCUXpresso Config Tools pin routing interface, where PTB2 was assigned to ADC0_SE12 and the I2C0 peripheral was mapped to its designated SDA/SCL lines. The ADC16 driver was activated as an SDK software component to facilitate hardware-accelerated analogue conversion. The I2C peripheral was initialised in slave mode using I2C_SlaveInit(), with a 7-bit slave address and an interrupt-driven transfer handle created via I2C_SlaveTransferCreateHandle(). Low-power wait (__WFI()) is employed within the main loop to suspend the CPU between I2C events, reducing idle power consumption.

Interrupt-driven I2C Slave operation: The K64F does not poll the bus. I2C_SlaveTransferNonBlocking() registers callbacks for four distinct events: kI2C_SlaveAddressMatchEvent, kI2C_SlaveTransmitEvent, kI2C_SlaveReceiveEvent, and kI2C_SlaveCompletionEvent. This event-driven model ensures the processor remains in low-power sleep between transactions and responds with minimal latency when the Master initiates communication.
04

Network Provisioning — ESP Touch SmartConfig

To eliminate the need for hardcoded Wi-Fi credentials — a security anti-pattern incompatible with field deployment scenarios — the ESP32 employs Espressif's ESP Touch SmartConfig protocol for over-the-air credential provisioning. Upon initial boot, the device enters SmartConfig mode and listens for encoded UDP broadcast packets transmitted by the companion mobile application. Once the SSID and passphrase are received and validated, the ESP32 establishes the Wi-Fi association, records the assigned IP address, and proceeds to initiate the MQTT connection sequence. The terminal output confirms the provisioning lifecycle: Starting SmartConfig → SmartConfig received → WiFi connected → Attempting MQTT connection.

05

MQTT Telemetry & Remote Control

All cloud communication is conducted over a TLS-encrypted MQTT session on port 8883 with the HiveMQ Cloud broker. The ESP32 simultaneously maintains two roles within the MQTT protocol: it acts as a publisher on the temperature/value topic, emitting validated temperature readings at 2-second intervals, and as a subscriber on the led/state topic, receiving actuation commands from the HiveMQ Web Client.

Command Set

MQTT PayloadActuationI2C PropagationK64F Handshake
redIlluminate red LEDESP32 → K64F I2C writered_OK
greenIlluminate green LEDESP32 → K64F I2C writegreen_OK
blueIlluminate blue LEDESP32 → K64F I2C writeblue_OK
offExtinguish all LEDsESP32 → K64F I2C write
Energy management: The __WFI() (Wait For Interrupt) instruction places the Cortex-M4 core into a low-power sleep state between I2C transfer events. Active peripherals are selectively gated, reducing idle current draw — a design consideration of particular relevance for battery-constrained or energy-harvesting IoT nodes.
06

Technical Observations & Limitations

Extraneous Characters in Serial Handshake Response OBSERVED ROOT CAUSE IDENTIFIED

During integration testing, the serial terminal exhibited spurious characters appended to handshake acknowledgement strings (e.g. red_OKrep·@? in lieu of red_OK). Diagnostic analysis attributed this artefact to either an absent null terminator ('\0') at the boundary of the response string, or a transmit buffer whose declared length exceeded the actual payload size — causing uninitialised memory to be serialised alongside the intended response. System functionality was unaffected; the K64F continued to process commands and the ESP32 correctly parsed the leading token. A future revision should enforce explicit null termination and constrain buffer length declarations to the precise payload width to eliminate this visual anomaly.

Hardware, Firmware & System Captures

Physical implementation, IDE configuration, network provisioning, and end-to-end data flow verification.

§1 — Physical Implementation
Hardware setup: ESP32 and FRDM-K64F with TMP36 sensor and I2C wiring
Hardware
ESP32 (Master) & FRDM-K64F (Slave) — Physical Integration
The assembled test bench illustrates the complete hardware topology. The TMP36 analogue temperature sensor is connected to the FRDM-K64F via a 3-wire interface terminating at PTB2 (ADC0_SE12). The I2C bus — comprising SDA and SCL lines — establishes the local communication channel between the ESP32 Master and the K64F Slave. Power and ground references are shared across both platforms to ensure a common voltage reference for signal integrity.
§2 — Firmware Development Environments
ESP32 master firmware in VS Code / PlatformIO
ESP32 — VS Code / PlatformIO
Master Firmware — MQTT & I2C Orchestration
The ESP32 firmware is developed within VS Code using the PlatformIO build system. The source integrates PubSubClient for MQTT session management, WiFiClientSecure for TLS certificate validation, and the I2C Master driver for periodic sensor polling. MQTT broker parameters — including the HiveMQ server URL, port 8883, and client credentials — are defined as preprocessor constants.
FRDM-K64F slave firmware in MCUXpresso IDE with ADC driver and terminal configuration
FRDM-K64F — MCUXpresso IDE
Slave Firmware — ADC Driver Activation & Serial Terminal
The K64F firmware is configured within MCUXpresso IDE. The ADC16 peripheral driver is enabled as an SDK software component to support hardware-accelerated analogue conversion of the TMP36 output. The integrated serial terminal is configured at 115200 baud on /dev/ttyACM0 to monitor the I2C callback lifecycle and temperature output in real time.
MCUXpresso Config Tools pin routing for I2C and ADC on FRDM-K64F
MCUXpresso Config Tools
Pin Multiplexing — ADC0_SE12 & I2C0 Routing
The MCUXpresso Config Tools pin routing interface is used to assign peripheral functions to physical pins. PTB2 is mapped to ADC0_SE12 for the TMP36 analogue input. The I2C0 peripheral SCL and SDA lines are routed to their designated expansion header pins. The non-blocking slave transfer is initialised to respond to four event flags: Address Match, Transmit, Receive, and Completion.
Both IDEs showing temperature readings being read and published
Runtime — Dual IDE View
Concurrent Temperature Acquisition & MQTT Publication
With both firmware images deployed, the system enters its nominal operating cycle. The FRDM-K64F serial terminal (MCUXpresso, right) streams ADC-converted temperature values at sub-second resolution. Simultaneously, the ESP32 terminal (VS Code, left) confirms the I2C polling sequence — Sent: temp → Received: 18.93 °C → Message published [temperature/value] — at the prescribed 2-second interval.
§3 — Network Provisioning
MQTT configuration and TLS certificate in ESP32 source
ESP32 Source
TLS Certificate & MQTT Broker Configuration
The HiveMQ root CA certificate is embedded as a raw string literal within the ESP32 firmware, eliminating the need for a system certificate store. The broker endpoint, port (8883), client ID, and authentication credentials are defined as preprocessor constants. Topics temperature/value and led/state are likewise declared at compile time.
ESP Touch SmartConfig provisioning via mobile app
Wi-Fi Provisioning
ESP Touch SmartConfig — Credential Transfer
Wi-Fi credentials are transmitted to the ESP32 without hardcoding via the ESP Touch mobile application. The device enters provisioning mode on boot, receives the encoded UDP broadcast containing the SSID and passphrase, and confirms receipt in the VS Code terminal: SmartConfig received → WiFi connected → IP: 192.168.14.217 → Attempting MQTT connection.
§4 — Cloud Telemetry & Remote Actuation
HiveMQ Web Client showing temperature stream and LED command interface
HiveMQ Web Client
Live Telemetry Stream & LED Command Dispatch
The HiveMQ Web Client dashboard presents the continuous temperature/value topic stream on the right — readings in the 18–19 °C range at QoS 0. The Send Message panel (lower left) allows manual publication to the led/state topic. Commands such as green, red, or off are dispatched to the broker and subsequently relayed by the ESP32 to the K64F over I2C. The hardware photograph inset confirms the green LED responding to the corresponding command.
ESP32 terminal showing red command received, forwarded via I2C, K64F LED turns red
End-to-End Command Execution
MQTT → I2C → RGB LED — Full Command Propagation
The VS Code terminal captures the complete command lifecycle: the ESP32 receives a red payload on the led/state topic, transmits the corresponding instruction to the FRDM-K64F via I2C, and the K64F illuminates its onboard RGB LED in red. The K64F serial terminal (MCUXpresso, right) confirms the request with Request completed: red_OK. The minor string artefact (red_OKrep·@?) visible in the ESP32 terminal is attributable to the buffer termination issue documented in §6.