Master-Slave Communication Between Two Arduino Boards
Akshay Jain
Posted on July 31, 2024
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
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");
}
}
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;
}
}
}
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
Posted on July 31, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.