72. UART-Based LEDs Control

  • In this task, two Arduino UNO boards communicate via UART (Serial Communication) to control the brightness of LED2 and toggle the LED1 ON and OFF.
  • Both Arduino’s will log the data as follows:
    • Arduino Board2 prints the received ADC values [0 to 1023].
    • Arduino Board1 prints the LED1 status [ON or OFF].
  • For proper UART communication between two controllers, both must use the same baud rate (e.g., 9600, 4800, 115200).

Hardware Connection

  • Board 1 Connections
    • LED1
      • Connected to digital pin 7 using 330Ω resistor in series with LED to limit current through it.
    • Potentiometer
      • Terminal 1 → VCC.
      • Terminal 2 → Pin A0.
      • Terminal 3 → GND .
  • Board 2 Connections
    • Push Button Switch
      • One terminal is connected to digital pin 2 and the other is connected to GND.
      • Use an internal pull-up resistor on digital pin 2 to avoid the floating state of the pin.
    • LED2
      • Connected to digital pin 7 using 330Ω resistor in series with LED to limit current through it.
  • UART Connection (SoftwareSerial):
    • The Board1 D3 pin (RX) is connected to the Board2 D4 pin  (TX).
    • The Board1 D4 pin (TX) is connected to the  Board2 D3 pin (RX)
  • Connect Arduino UNO to PC via USB cable.

Firmware

  • Arduino UNO has only one hardware serial module using Tx (Pin 1) and Rx (Pin 0).
  • These pins are also used for USB communication with the Serial Monitor.
  • Using the same pins for both tasks, data logging on the serial monitor, and data transmission between two Arduino UNO boards can cause data conflicts.
  • To prevent this, we assign different pins for board-to-board communication.
  • The SoftwareSerial library helps create additional Tx and Rx pins.

 

Board 1 code 

#include <SoftwareSerial.h>

#define POTENTIOMETER A0
#define LED_PIN 7

SoftwareSerial mySerial(3, 4);  // 3-RX, 4-TX (For communication with Board 2)

uint16_t potValue = 0;

uint8_t adcMaxLowerByte = 1023;       // Lower byte
uint8_t adcMaxUpperByte = 1023 >> 8;  // Upper byte

uint8_t adcLowerByte = 0;  // Lower byte
uint8_t adcUpperByte = 0;  // Upper byte

void setup() {
  pinMode(LED_PIN, OUTPUT);
  mySerial.begin(115200);  // Initialize software Serial communication
  Serial.begin(115200);    // Initialize Hardware Serial communication
}

void loop() {

  if (mySerial.available()) {
    uint8_t receivedByte = mySerial.read();  // Read a single byte

    if (receivedByte == 'T') {
      digitalWrite(LED_PIN, !digitalRead(LED_PIN));  // Toggle LED state
      Serial.print("LED is ");
      Serial.println(digitalRead(LED_PIN) ? "ON" : "OFF");  // Print state
    }
    if (receivedByte == 0) {
      potValue = analogRead(A0);  // Read potentiometer value (0-1023)
      adcLowerByte = potValue;       // Lower byte
      adcUpperByte = potValue >> 8;  // Upper byte
      mySerial.write('0');
    }

    if (receivedByte == 1) {
      mySerial.write(adcMaxLowerByte);
    }

    if (receivedByte == 2) {
      mySerial.write(adcMaxUpperByte);
    }

    if (receivedByte == 3) {
      mySerial.write(adcLowerByte);
    }

    if (receivedByte == 4) {
      mySerial.write(adcUpperByte);
    }
  }
}

 

Board 2 code 

#include <SoftwareSerial.h>

#define LED_PIN 9
#define SWITCH_PIN 2
#define DEBOUNCE_DELAY 50  // debounce delay

uint8_t previousValue = 0;

unsigned long previousMillis = 0;

uint8_t txBuffer[20];
uint8_t rxBuffer[10];

uint8_t rxIndex = 0;


bool last_button_state = 1;            // Previous button state (1: not pressed, 0: pressed)
bool current_button_state = 1;         // Current button state
unsigned long last_debounce_time = 0;  // Timestamp of the last button state change

uint16_t adcValue = 0;
SoftwareSerial mySerial(3, 4);  // 3-RX, 4-TX (For communication with Board 1)

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(SWITCH_PIN, INPUT_PULLUP);
  mySerial.begin(115200);  // Initialize Software Serial communication
  Serial.begin(115200);  // Initialize Hardware Serial communication
}

void loop() {

  if (is_debounced_press(SWITCH_PIN)) {

    mySerial.write("T\n");  // Send command to toggle LED
  }

  if((millis() - previousMillis) > 100){
    previousMillis = millis();
    rxIndex = 0;
    for(uint8_t i = 0; i < 5; i++){
      mySerial.write(i);
      while(mySerial.available() < 1);
      rxBuffer[i] = mySerial.read();
      delay(1);
    }

    uint8_t adcMaxValueLowerByte = rxBuffer[1];  // Read the lower byte
    uint8_t adcMaxValueUpperByte = rxBuffer[2];  // Read the upper byte

    uint8_t adcValueLowerByte = rxBuffer[3];  // Read the lower byte
    uint8_t adcValueUpperByte = rxBuffer[4];  // Read the upper byte

    // Combine the two bytes into a 16-bit value
    uint16_t adcMaxValue = (adcMaxValueUpperByte << 8) | adcMaxValueLowerByte;

    // Combine the two bytes into a 16-bit value
    uint16_t adcValue = (adcValueUpperByte << 8) | adcValueLowerByte;

    // print received brightness value on serial monitor
    Serial.print("Received ADC Value: ");
    Serial.println(adcValue);

    //Map received ADC value to 0 to 255
    uint8_t brightness = map(adcValue,0,adcMaxValue,0,255);

    analogWrite(LED_PIN, brightness);  // Adjust LED brightness

  }
}

// Checks if the button is pressed and debounced.

bool is_debounced_press(int button_pin) {
  int reading = digitalRead(button_pin);

  // If the button state has changed, reset the debounce timer
  if (reading != last_button_state) {
    last_debounce_time = millis();
  }
  last_button_state = reading;
  // If the button state is stable for more than 50 msec the debounce delay, update the state.
  if ((millis() - last_debounce_time) > DEBOUNCE_DELAY) {
    if (reading != current_button_state) {
      current_button_state = reading;


      if (current_button_state == 0) {
        return true;  // valid press detected
      }
    }
  }
  return false;  // No valid press detected
}

Code Explanation

Includes & Setup:

  • #include <SoftwareSerial.h>: Enables software serial communication for both boards.

  • SoftwareSerial mySerial(3, 4);: Creates a software serial instance on pins 3 (RX) and 4 (TX) for inter-board communication.

  • mySerial.begin(115200);: Initializes software serial communication at 115200 baud rate.

  • Serial.begin(115200);: Initializes hardware serial for debugging/logging at 115200 baud rate.


Board1 (Transmitter/Receiver) Functionality:

  1. Potentiometer Reading:

    • Continuously reads analog value from A0 (potentiometer)

    • Splits the 10-bit ADC value (0-1023) into upper and lower bytes for transmission

  2. Message Handling:

    • Listens for incoming bytes from Board2 via software serial

    • Implements a simple protocol where different byte values trigger different actions:

      • 'T': Toggles LED1 and prints status ("ON"/"OFF") to hardware serial

      • 0: Triggers a new potentiometer reading

      • 1-4: Responds with specific bytes of ADC data (max value or current value)

  3. Data Transmission:

    • Sends requested bytes (ADC values) back to Board2 when specific commands are received


Board2 (Receiver/Transmitter) Functionality:

  1. Button Handling:

    • Uses debounce logic (is_debounced_press()) to detect valid button presses

    • When pressed, sends 'T' command to Board1 to toggle its LED

  2. Periodic Data Request:

    • Every 100ms, initiates a data request sequence:

      1. Sends numbers 0-4 sequentially to Board1

      2. Waits for and reads corresponding responses

      3. Reconstructs ADC values from received bytes

  3. LED Control:

    • Maps received ADC value to PWM range (0-255)

    • Applies the mapped value to LED2 using analogWrite()

    • Prints received ADC value to hardware serial for monitoring

  4. Debounce Function:

    • is_debounced_press() implements proper debouncing:

      • Tracks button state changes

      • Only registers a press after stable LOW state for >50ms

      • Returns true for valid presses

 

Output

Hardware Setup

Screen-Shot Of Output


 Video


 

Submit Your Solution

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