Master-Slave Communication Between Two Arduino Boards

akshay_jain

Akshay Jain

Posted on July 31, 2024

Master-Slave Communication Between Two Arduino Boards

In this project, we'll explore how to set up master-slave communication between two Arduino boards using UART communication.

Understanding Master-Slave Communication

In a master-slave communication setup between two Arduino boards, one board acts as the master, while the other functions as the slave.
The master board sends commands to the slave board to perform specific tasks, and the slave board carries out these operations and sends a response back to the master.

These tasks can include actions like turning on an LED, reading sensor values, or controlling the speed of a motor. While the master sends the commands, the slave processes them and executes the required tasks.
The communication between the master and slave is established using Serial UART Communication, which utilises three pins: Rx (Receive), Tx (Transmit), and GND (Ground).

In this project, the Master Arduino sends commands to the Slave Arduino through the Serial Tx Pin and receives responses from the Slave Arduino via the Serial Rx Pin.

The master will send the following commands:

  • Request the temperature reading from the Slave board.
  • Request the humidity reading from the Slave board.
  • Request the analog value read from the A0 input on the Slave board.
  • Turn the LED ON/OFF based on the analog input value.

Once the master receives all the responses, it will display the data on an LCD.

On the other side, the slave board listens for commands from the master and provides the appropriate response based on the command received.

Components Required

  • 2 Arduino UNO R3
  • 16 x 2 LCD
  • 2, 10 kΩ Potentiometer
  • 220 Ω Resistance
  • 2 Breadboards
  • Red LED
  • 100 Ω Resistance
  • Temperature Sensor
  • L293D
  • 104 pf Ceramic Capacitor
  • multiple Connecting Wires
  • 5V DC Motor
  • DC Fan
  • USB A/B cable

Circuit Diagram

Image description

Master Code

#include "master.h"
#define MOTOR_PIN 3
void setup() {
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // set the cursor to (column = 0,row = 0)
  lcd.setCursor(0, 0);
  lcd.print("Master Slave");
  lcd.setCursor(0, 1);
  // set the cursor to (column = 0,row = 1)
  lcd.print("Demo");
  // provide delay of 2 second
  delay(300);
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ;  // wait for serial port to connect. Needed for native USB
  }
  pinMode(MOTOR_PIN, OUTPUT);  // declare Motor pin to be an output
}
void loop() {
  getTempearture();
  getHumidity();
  getSpeed();
  setMotorSpeed();
  lcdUpdate();
}
void setMotorSpeed() {
  if (paramerters.Status[SPD_STA_INDX] == STATUS_OK) {
    analogWrite(MOTOR_PIN, map(paramerters.Speed,0,100,0,255));  // set the Speed of Motor
  }
}
// For temperture
// CMD : START_CHAR TEMP_CMD END_CHAR
// RESP: START_CHAR TEMP_RSP STATUS BYTE1 BYTE2 BYTE3 BYTE4 END_CHAR
void getTempearture() {
  float temp;
  float *ptemp = &temp;
  unsigned char cmdBuffer[3] = { START_CHAR, TEMP_CMD, END_CHAR };
  unsigned char rspBuffer[10] = { 0 };
  unsigned char rspIndex = 0;
  Serial.write(cmdBuffer, 3);
  while (Serial.available() == 0)
    ;
  do {
    rspBuffer[rspIndex++] = Serial.read();
    // this delay is provided because it takes approx 10 ms to receiev a new character at 9600 baurdrate
    delay(10);
  } while (Serial.available() != 0);
  if ((rspBuffer[0] == START_CHAR) && (rspBuffer[1] == TEMP_RSP) && (rspBuffer[7] == END_CHAR)) {
    if (rspBuffer[2] == STATUS_OK) {
      ptemp = (float *)(rspBuffer + 3);
      paramerters.Temperature = *ptemp;
    }
    paramerters.Status[TEM_STA_INDX] = rspBuffer[2];
  }
}
// For humidity
// CMD : START_CHAR HUMIDITY_CMD END_CHAR
// RESP: START_CHAR HUMIDITY_RSP STATUS BYTE1 BYTE2 BYTE3 BYTE4 END_CHAR
void getHumidity() {
  float hum;
  float *phum = &hum;
  unsigned char cmdBuffer[3] = { START_CHAR, HUMIDITY_CMD, END_CHAR };
  unsigned char rspBuffer[10] = { 0 };
  unsigned char rspIndex = 0;
  Serial.write(cmdBuffer, 3);
  while (Serial.available() == 0)
    ;
  do {
    rspBuffer[rspIndex++] = Serial.read();
    // this delay is provided because it takes approx 10 ms to receiev a new character at 9600 baurdrate
    delay(10);
  } while (Serial.available() != 0);
  if ((rspBuffer[0] == START_CHAR) && (rspBuffer[1] == HUMIDITY_RSP) && (rspBuffer[7] == END_CHAR)) {
    if (rspBuffer[2] == STATUS_OK) {
      phum = (float *)(rspBuffer + 3);
      paramerters.Humidity = *phum;
    }
    paramerters.Status[HUM_STA_INDX] = rspBuffer[2];
  }
}
// For speed
// CMD : START_CHAR SPEED_CMD END_CHAR
// RESP: START_CHAR SPEED_RSP STATUS BYTE1 END_CHAR
void getSpeed() {
  int spd;
  int *pspd = &spd;
  unsigned char cmdBuffer[3] = { START_CHAR, SPEED_CMD, END_CHAR };
  unsigned char rspBuffer[10] = { 0 };
  unsigned char rspIndex = 0;
  Serial.write(cmdBuffer, 3);
  while (Serial.available() == 0)
    ;
  do {
    rspBuffer[rspIndex++] = Serial.read();
    // this delay is provided because it takes approx 10 ms to receiev a new character at 9600 baurdrate
    delay(10);
  } while (Serial.available() != 0);
  if ((rspBuffer[0] == START_CHAR) && (rspBuffer[1] == SPEED_RSP) && (rspBuffer[5] == END_CHAR)) {
    if (rspBuffer[2] == STATUS_OK) {
      pspd = (int *)(rspBuffer + 3);
      paramerters.Speed = *pspd;
    }
    paramerters.Status[SPD_STA_INDX] = rspBuffer[2];
  }
  if (paramerters.Status[SPD_STA_INDX] == STATUS_OK) {
    if (paramerters.Speed >= 50) {
      //it indicates high speed
      setLEDON();
    } else {
      //it indicates low speed
      setLEDOff();
    }
  }
}
// For led ON
// CMD : START_CHAR LED_ON_CMD END_CHAR
// RESP: START_CHAR LED_ON_RSP STATUS END_CHAR
void setLEDON() {
  unsigned char cmdBuffer[3] = { START_CHAR, LED_ON_CMD, END_CHAR };
  unsigned char rspBuffer[10] = { 0 };
  unsigned char rspIndex = 0;
  Serial.write(cmdBuffer, 3);
  while (Serial.available() == 0);
  do {
    rspBuffer[rspIndex++] = Serial.read();
    // this delay is provided because it takes approx 10 ms to receiev a new character at 9600 baurdrate
    delay(10);
  } while (Serial.available() != 0);
  if ((rspBuffer[0] == START_CHAR) && (rspBuffer[1] == LED_ON_RSP) && (rspBuffer[3] == END_CHAR)) {
    if (rspBuffer[2] == STATUS_OK) {
      paramerters.Led_state = 1;
    }
    paramerters.Status[LED_STA_INDX] = rspBuffer[2];
  }
}
// For led OFF
// CMD : START_CHAR LED_OFF_CMD END_CHAR
// RESP: START_CHAR LED_OFF_RSP STATUS END_CHAR
void setLEDOff() {
  unsigned char cmdBuffer[3] = { START_CHAR, LED_OFF_CMD, END_CHAR };
  unsigned char rspBuffer[10] = { 0 };
  unsigned char rspIndex = 0;
  Serial.write(cmdBuffer, 3);
  while (Serial.available() == 0)
    ;
  do {
    rspBuffer[rspIndex++] = Serial.read();
    // this delay is provided because it takes approx 10 ms to receiev a new character at 9600 baurdrate
    delay(10);
  } while (Serial.available() != 0);
  if ((rspBuffer[0] == START_CHAR) && (rspBuffer[1] == LED_OFF_RSP) && (rspBuffer[3] == END_CHAR)) {
    if (rspBuffer[2] == STATUS_OK) {
      paramerters.Led_state = 0;
    }
    paramerters.Status[LED_STA_INDX] = rspBuffer[2];
  }
}
void lcdUpdate() {
  lcd.clear();
  // DisplaY Temperature
  // set the cursor to (column = 0,row = 0)
  lcd.setCursor(0, 0);
  lcd.print("Tem=");
  if (paramerters.Status[TEM_STA_INDX] == STATUS_OK) {
    lcd.print(paramerters.Temperature);
  } else {
    lcd.print("NOK");
  }
  // DisplaY LED Status
  lcd.print(" LED=");
  if (paramerters.Status[LED_STA_INDX] == STATUS_OK) {
    lcd.write(paramerters.Led_state | 0x30);
  } else {
    lcd.print("NOK");
  }
  // Display Humidity
  // set the cursor to (column = 0,row = 1)
  lcd.setCursor(0, 1);
  lcd.print("Hum=");
  if (paramerters.Status[HUM_STA_INDX] == STATUS_OK) {
    lcd.print(paramerters.Humidity);
  } else {
    lcd.print("NOK");
  }
  // Display Speed
  lcd.print(" SP=");
  if (paramerters.Status[HUM_STA_INDX] == STATUS_OK) {
    lcd.print(paramerters.Speed);
    lcd.print('%');
  } else {
    lcd.print("NOK");
  }
}
Enter fullscreen mode Exit fullscreen mode

Slave Code

#include "slave.h"
#define ALARM_LED 8
#define LED_ON 0
#define LED_OFF 1
char serialInput;
int dataIndex = 0;
char databuffer[10] = { 0 };
bool dataRcvd = false;  // whether the string receiving is completed.
unsigned char rspBuffer[10];
void setup() {
  Serial.begin(9600);          // initialize serial port
  dht.begin();                 // Initialize the DHT sensor
  pinMode(ALARM_LED, OUTPUT);  // Initialize alarm LED
  digitalWrite(ALARM_LED, LED_OFF);
}
void loop() {
  getTemphumidity();
  getSpeed();
  if (dataRcvd == true) {
    // Check for start and end character
    if ((databuffer[0] = START_CHAR) && (databuffer[2] = END_CHAR)) {
      // Check if its the temperature command
      // For temperture
      // CMD : START_CHAR TEMP_CMD END_CHAR
      // RESP: START_CHAR TEMP_RSP STATUS BYTE1 BYTE2 BYTE3 BYTE4 END_CHAR
      if (databuffer[1] == TEMP_CMD) {
        rspBuffer[0] = START_CHAR;
        rspBuffer[1] = TEMP_RSP;
        if (paramerters.Status[TEM_STA_INDX] == STATUS_OK) {
          rspBuffer[2] = STATUS_OK;
          memcpy((rspBuffer + 3), &(paramerters.Temperature), 4);
        } else {
          rspBuffer[2] = STATUS_NOT_OK;
          memset((rspBuffer + 3), 0x00, 4);
        }
        rspBuffer[7] = END_CHAR;
        Serial.write(rspBuffer, 8);
      }
      // Check if it is Humidity command
      // For humidity
      // CMD : START_CHAR HUMIDITY_CMD END_CHAR
      // RESP: START_CHAR HUMIDITY_RSP STATUS BYTE1 BYTE2 BYTE3 BYTE4 END_CHAR
      else if (databuffer[1] == HUMIDITY_CMD) {
        rspBuffer[0] = START_CHAR;
        rspBuffer[1] = HUMIDITY_RSP;
        if (paramerters.Status[HUM_STA_INDX] == STATUS_OK) {
          rspBuffer[2] = STATUS_OK;
          memcpy((rspBuffer + 3), &(paramerters.Humidity), 4);
        } else {
          rspBuffer[2] = STATUS_NOT_OK;
          memset((rspBuffer + 3), 0x00, 4);
        }
        rspBuffer[7] = END_CHAR;
        Serial.write(rspBuffer, 8);
      }
      // Check if it is Speed command
      // For speed
      // CMD : START_CHAR SPEED_CMD END_CHAR
      // RESP: START_CHAR SPEED_RSP STATUS BYTE1 END_CHAR
      else if (databuffer[1] == SPEED_CMD) {
        rspBuffer[0] = START_CHAR;
        rspBuffer[1] = SPEED_RSP;
        if (paramerters.Status[SPD_STA_INDX] == STATUS_OK) {
          rspBuffer[2] = STATUS_OK;
          memcpy((rspBuffer + 3), &(paramerters.Speed), 2);
        } else {
          rspBuffer[2] = STATUS_NOT_OK;
          memset((rspBuffer + 3), 0x00, 2);
        }
        rspBuffer[5] = END_CHAR;
        Serial.write(rspBuffer, 6);
      }
      // For led ON
      // CMD : START_CHAR LED_ON_CMD END_CHAR
      // RESP: START_CHAR LED_ON_RSP STATUS END_CHAR
      else if (databuffer[1] == LED_ON_CMD) {
        rspBuffer[0] = START_CHAR;
        rspBuffer[1] = LED_ON_RSP;
        digitalWrite(ALARM_LED, LED_ON);
        rspBuffer[2] = STATUS_OK;
        rspBuffer[3] = END_CHAR;
        Serial.write(rspBuffer, 4);
      }
      // For led OFF
      // CMD : START_CHAR LED_OFF_CMD END_CHAR
      // RESP: START_CHAR LED_OFF_RSP STATUS END_CHAR
      else if (databuffer[1] == LED_OFF_CMD) {
        rspBuffer[0] = START_CHAR;
        rspBuffer[1] = LED_OFF_RSP;
        digitalWrite(ALARM_LED, LED_OFF);
        rspBuffer[2] = STATUS_OK;
        rspBuffer[3] = END_CHAR;
        Serial.write(rspBuffer, 4);
      }
    }
    dataRcvd = false;
    memset(databuffer, 0x00, sizeof(databuffer));
  }
}
void getTemphumidity() {
  static unsigned long int previousTick = 0;
  static unsigned long int currentTick;
  float temperature;
  float humidity;
  currentTick = millis();
  if ((currentTick - previousTick) >= 100) {
    previousTick = currentTick;
    // Read temperature and humidity from the DHT sensor
    temperature = dht.readTemperature(false);
    humidity = dht.readHumidity();
    // Check if the sensor reading is valid (non-NaN)
    if (!isnan(temperature)) {
      paramerters.Temperature = temperature;
      paramerters.Status[TEM_STA_INDX] = STATUS_OK;
      // Serial.print("Temperature: ");
      // Serial.print(temperature);
    } else {
      paramerters.Status[TEM_STA_INDX] = STATUS_NOT_OK;
    }
    if (!isnan(humidity)) {
      paramerters.Humidity = humidity;
      paramerters.Status[HUM_STA_INDX] = STATUS_OK;
      // Serial.print(" °F, Humidity: ");
      // Serial.print(humidity);
      // Serial.println("%");
    } else {
      paramerters.Status[HUM_STA_INDX] = STATUS_NOT_OK;
    }
  }
}
void getSpeed() {
  int analogPin = A0;  // potentiometer wiper (middle terminal) is connected to analog pin 0
  int analogVal = 0;   // variable to store the value read
  analogVal = analogRead(analogPin);  // read the Analog input pin
  paramerters.Speed = map(analogVal, 0, 1023, 0, 100);
  paramerters.Status[SPD_STA_INDX] = STATUS_OK;
}
/*
  SerialEvent occurs whenever a new data comes in the hardware serial RX. This
  routine is run between each time loop() runs, so using delay inside loop can
  delay response. Multiple bytes of data may be available.
*/
void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    serialInput = Serial.read();
    databuffer[dataIndex++] = serialInput;
    // if the incoming character is a line feed character '\n', set a flag so the main loop can do something about it
    if (serialInput == END_CHAR) {
      dataRcvd = true;
      dataIndex = 0;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This project demonstrates how to implement master-slave communication between two Arduino boards using Serial UART Communication. By utilizing simple commands, the master board can control the slave board to perform various tasks and display the results on an LCD. For a detailed step-by-step guide, visit our blog post on Master-Slave Communication Between Two Arduino Boards.

Video

💖 💪 🙅 🚩
akshay_jain
Akshay Jain

Posted on July 31, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related