72. UART-Based LEDs Control

In this task, we must implement bidirectional UART communication between two microcontrollers, where each controller manages local inputs and controls remote outputs through serial communication.

System Architecture

Cross-Control Functionality:

  • Microcontroller 1: Reads the potentiometer → Controls the brightness of LED2 connected to Microcontroller 2
  • Microcontroller 2: Reads push button press → Toggles LED1 ON/OFF connected to Microcontroller 1

Connection Requirements:

  • TX of Microcontroller 1 → RX of Microcontroller 2
  • RX of Microcontroller 1 → TX of Microcontroller 2
  • The GND of both microcontrollers must be connected

Voltage Level Considerations in UART Communication

  • UART is push-pull (active high/low)
    • Unlike I²C, UART pins (TX, RX) actively drive logic HIGH and LOW.
    • This makes voltage compatibility critical when connecting devices.
  • Logic voltage matching
    • 5 V TX → 3.3 V RX: Unsafe! It can exceed the 3.3 V limit and damage the pin. Always step down using a divider or level shifter.
    • 3.3 V TX → 5 V RX: Often works (3.3 V is seen as HIGH), but not guaranteed at all speeds or with every chip. A shifter makes it reliable.
  • Best practice: Use a level shifter both ways and always share a common ground.
UART-Communication

Functionality

  • Microcontroller 1 (Potentiometer & LED1 control)
    • Reads the potentiometer value via ADC and sends it to Microcontroller 2 over UART.
    • Receives button toggle command from Microcontroller 2 to control LED1 ON/OFF.
    • Prints LED1 status on the Serial terminal.
  • Microcontroller 2 (Push button & LED2 control)
    • Reads push button state using internal pull-up and sends toggle command to Microcontroller 1.
    • Receives potentiometer ADC data from Microcontroller 1, maps it to PWM, and controls LED2 brightness.
    • Prints the potentiometer value on the Serial terminal.

UART Communication Protocol

  • Framing: 8-bit data, No parity, Baud: 115,200bps (example).
  • Commands sent from Microcontroller 2 to Microcontroller 1:
    • 0: Trigger ADC conversion on Microcontroller 1.
    • 1 and 2: Request the lower and upper bytes of ADC maximum value (e.g., 4095 for 12-bit).
    • 3 and 4: Request the lower and upper bytes of the last ADC reading.
    • ‘T’: Toggle LED1 on Microcontroller 1.
  • Transaction Flow
    • MCU2 periodically (e.g., every 100 ms) sends commands 0–4 in sequence.
    • MCU1 responds with the requested bytes.
    • MCU2 reconstructs both ADC_MAX_VALUE and ADC_VALUE from the received data.

Why send ADC Maximum Value along with the current ADC value?

  • Purpose: The receiver needs to know the ADC’s full-scale range (e.g., 4095 for 12-bit) to interpret raw readings.
  • Example: If ADC_VALUE = 2048 and ADC_MAX_VALUE = 4095, the system knows it represents ~50% of the full range.

Data Packing

  • Both ADC_MAX_VALUE and ADC_VALUE are split into two bytes (little-endian format):
    • Bytes 1–2: ADC_MAX_VALUE (low byte, high byte).
    • Bytes 3–4: ADC_VALUE (low byte, high byte).
  • The receiver reassembles these values for accurate scaling and processing.

Serial Terminal Output

Both microcontrollers provide real-time feedback through their respective serial monitors:

  • Microcontroller 1: Displays LED1 status as "LED1 Status: ON" or "LED1 Status: OFF"
  • Microcontroller 2: Shows received ADC values as "Received ADC Value: [receivedADCValue]"

 

So, by connecting and configuring the microcontrollers and UART communication, we can implement the task.

Below are the solutions to the given task using different microcontrollers

  1. STM32
  2. ESP32
  3. Arduino UNO

Submit Your Solution

Note: Once submitted, your solution goes public, helping others learn from your approach!