Hello everyone!
In this Part 1, I will be discussing serial communication using the hardware UART pins available on the embedded devices.
For Part 2, I will be discussing serial communication between your laptop device with the external embedded devices using popular Python programming language. Stay tuned.
In this Part 1 detailed article, I will be covering the following:
- What’s Serial Communication?
- Why Serial Communication Between Embedded Devices?
- How to use UART to communicate between two boards?
- Summary
Before starting with Serial Communication Protocols, Let’s break the terminology in three parts. The communication is very well known terminology which involves the exchange of information between two or more mediums. In embedded systems, the communication means the exchange of data between two microcontrollers in the form of bits. This exchange of data bits in microcontroller is done by some set of defined rules known as communication protocols. Now if the data is sent in series i.e. one after the other then the communication protocol is known as Serial Communication Protocol. More specifically, the data bits are transmitted one at a time in sequential manner over the data bus or communication channel in Serial Communication. Source: Circuit Digest
UART, SPI, and I2C are the common hardware interfaces people use in microcontroller development when we’re talking communication protocols.
Note: You need to check on the hardware's datasheets to make sure the UART/SPI/I2C pinouts are available.
In this article, I would be focusing more on the UART serial communication protocol as think it would be the easiest to get started with.
UART (stands for Universal Asynchronous Reception and Transmission). It is a simple serial communication protocol that allows the host to communicate with the other devices. It has two data lines, one to transmit (TX) and another to receive (RX). For different embedded devices, you will need to check on the datasheets to understand which pins can be used for UART.
For example, this is a table for UART pins on common Arduino boards.
On Uno, Nano, Mini, and Mega, pins 0 and 1 are used for communication with the computer. Even though there are external breakout pins for pins 0 and 1, connecting anything to these pins can interfere with that communication, including causing failed uploads to the board. If you are using pins 0 and 1 to connect with other devices/modules, do take note that while uploading the Arduino code, temporarily remove the connection on pins 0 and 1.
For ESP32, these are the UART pins available:
There are three serial ports on the ESP32 known as U0UXD, U1UXD, and U2UXD.
- U0UXD is used to communicate with the ESP32 for programming and during reset/boot.
- U1UXD is unused and can be used for your projects. Some boards use this port for SPI Flash access though
- U2UXD is unused and can be used for your projects.
Note A special case for the ESP32 TTGO T-Display which I recently used. You can use Serial2.begin(9600, SERIAL_8N1, 25, 26) to make the pins 25 and 26 as the Rx and Tx pin respectively.
A "standard" ESP32 has more pins.
- The default pins are 3 (Rx) and 1 (Tx) for Serial(0),
- 9 (Rx) and 10 (Tx) for Serial1, and
- 16 (Rx) and 17 (Tx) for Serial2.
As you can see, with the limited number of pins on this TTGO board, none of them are there. But the good news is that the "Serial.begin" function has an option to change the pins! By using "Serial2.begin(9600, SERIAL_8N1, 25, 26);" you have the Serial2 port on pins 25 (Rx) and 26 (Tx). I tested this today, it's working like a charm!. It's NOT working on pins 37 and 38 (and maybe other pins), but it IS working on pins 25, 26, 27 !!! Source:https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/47
If you are interested to know more about the different communication protocols for embedded devices, I had compiled a useful list as below:
- Here's a great article by Seeed Studio to compare the various interfaces: UART, SPI, and I2C and their differences. The article is comparing the different communication protocols with various factors through their protocols, advantages, and disadvantages of each interface, etc and we will be providing some examples of how these interfaces are being used in microcontrollers.
- Sparkfun Learn - Serial Communication
- Serial Communication Protocols by CircuitDigest
You might be asking, why do we need this feature of serial communication between embedded devices?
Here's the main reason that I thought of:
There’s no perfect product in the market. With the serial communication, we can leverage the features that’s lacking on the particular embedded device.
Let me give you a contextual understanding of this by using two examples that is based on UART serial communication.
Example 1
Assuming that you prototyped a simple Arduino project using Arduino Uno board to read the temperature and humidity value displayed on the serial terminal. The code for the project would look something like this:
#include <SimpleDHT.h>
// for DHT11,
// VCC: 5V or 3V
// GND: GND
// DATA: 2
int pinDHT11 = 2;
SimpleDHT11 dht11(pinDHT11);
void setup() {
Serial.begin(115200);
}
void loop() {
// start working...
Serial.println("=================================");
Serial.println("Sample DHT11...");
// read without samples.
byte temperature = 0;
byte humidity = 0;
int err = SimpleDHTErrSuccess;
if ((err = dht11.read(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
Serial.print("Read DHT11 failed, err="); Serial.print(SimpleDHTErrCode(err));
Serial.print(","); Serial.println(SimpleDHTErrDuration(err)); delay(1000);
return;
}
Serial.print("Sample OK: ");
Serial.print((int)temperature); Serial.print(" *C, ");
Serial.print((int)humidity); Serial.println(" H");
// DHT11 sampling rate is 1HZ.
delay(1500);
}
It is great that the project is done! However, you are asked to send the temperature and humidity value to a cloud platform so that it enables remote monitoring from anywhere in the world. To solve this, you used the ESP32 board that has WiFi capability. For this reason, you will send the temperature and humidity data over to the ESP32 board using UART serial communication and let the ESP32 send it to the cloud platform. The hardware and software for the Arduino Uno side remain the same. The only add-on is, you code the ESP32 to receive the data from the serial buffer that was printed from Arduino Uno, and send it to your favorite cloud platform. The code for the ESP32 would look something like this:
#include <WiFi.h>
#include <WebServer.h>
/*Put your SSID & Password*/
const char* ssid = "VincentKok"; // Enter SSID here
const char* password = "vickhuawei"; //Enter Password here
WebServer server(80);
void setup() {
Serial.begin(9600);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: ");
Serial.println(WiFi.localIP());
server.on("/", handle_OnConnect);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
}
void handle_OnConnect() {
if(Serial.available()){
String data = Serial.readStringUntil('\n'); // example value 23,56
//seperating the data string to respective variabes
String temperature = data.substring(0, data.indexOf(","));
String humidity = data.substring(data.indexOf(",") + 1);
Serial.flush();
}
}
void handle_NotFound(){
server.send(404, "text/plain", "Not found");
}
String SendHTML(float Temperaturestat,float Humiditystat){
String ptr = "<!DOCTYPE html> <html>\n";
ptr +="<head><meta http-equiv=\"refresh\" content=\"5\" name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr +="<title>ESP32 Weather Report</title>\n";
ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
ptr +="</style>\n";
ptr +="</head>\n";
ptr +="<body>\n";
ptr +="<div id=\"webpage\">\n";
ptr +="<h1>ESP32 Weather Report</h1>\n";
ptr +="<p>Temperature: ";
ptr +=(int)Temperaturestat;
ptr +="°C</p>";
ptr +="<p>Humidity: ";
ptr +=(int)Humiditystat;
ptr +="%</p>";
ptr +="</div>\n";
ptr +="</body>\n";
ptr +="</html>\n";
return ptr;
}
Example 2
You had an Arduino robotics kit from some companies, for example, uKit Explore board from UBTECH Robotics, and you had built a 2-wheeled robot car and programmed the car robot using block programming on uCode.
Access Web IDE for uCode: www.ide.ubtrobot.com/#/
Here’s some info regarding uKit Explore hardware and uCode software:
- uKit Explore board is a customized board that is based on the Arduino Mega 2560.
- There’s one UART pinout (Tx0 Rx0)
- uCode is based on Scratch programming and supports both software and hardware programming. Users can program by dragging blocks instead of using keyboards. uCode can not only create interesting games and animated works, but also program the hardware products of UBTECH.
- uCode allows block-based programming and C/C++ Arduino text coding
Next, you want to improve this project to be voice-activated robot car. There is no microphone on uKit Explore and can’t perform speech recognition.
So, you had a Wio Terminal by Seeed Studio. It is able to perform on-device speech recognition (wake-up word detection). You made the speech recognition project on the Wio Terminal to recognize the command ‘go’ and ‘stop’.
With both uKit Explore robot car and Wio Terminal, using UART serial communication, you can easily integrate both projects and create a voice activated robot car. Isn’t that cool?
For the detailed explanation/tutorial on this, please visit my project at Voice Activated Robo Car on microcontroller with TinyML
I hope the two examples I gave as above make sense for you to justify why enabling the UART serial communication between embedded devices is useful! If you have other thoughts, feel free to comment as well!
How to use UART to communicate between two boards?Step 1: Identify and understand the hardware specification of the two embedded devices
uKit Explore
There is one UART pin available on pins 0 and 1.
Note: You could also use Arduino UNO as well
ESP32 TTGO T-Display
As explained above, we could use pins 25 and 26 as the UART pins by defining them on the code using
Serial2.begin(9600, SERIAL_8N1, 25, 26);
Note: You could also use other ESP32 boards variant as well
Step 2: Connect the UART Channel
Using jumper wires, connect the TX RX pins of each embedded device as follow:
Step 3: Write the code
In this step, we need to first identify the flow of the data, either from TTGO to uKit Explore or from uKit Explore to TTGO.
For TTGO to uKit Explore
- TTGO Code: Make sure that the TTGO is printing the data on the Serial2 line (the UART communication highway lane of TTGO) using the Serial2.printcommand.
#include <WiFi.h>
#include <WebServer.h>
/*Put your SSID & Password*/
const char* ssid = "VincentKok"; // Enter SSID here
const char* password = "vickhuawei"; //Enter Password here
WebServer server(80);
uint8_t LED1pin = 4;
bool LED1status = LOW;
uint8_t LED2pin = 5;
bool LED2status = LOW;
void setup() {
Serial.begin(115200);
Serial2.begin(9600, SERIAL_8N1, 25,26);
delay(100);
pinMode(LED1pin, OUTPUT);
pinMode(LED2pin, OUTPUT);
Serial.println("Connecting to ");
Serial.println(ssid);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: "); Serial.println(WiFi.localIP());
server.on("/", handle_OnConnect);
server.on("/led1on", handle_led1on);
server.on("/led1off", handle_led1off);
server.on("/led2on", handle_led2on);
server.on("/led2off", handle_led2off);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
/*if(LED1status)
{digitalWrite(LED1pin, HIGH);}
else
{digitalWrite(LED1pin, LOW);}
if(LED2status)
{digitalWrite(LED2pin, HIGH);}
else
{digitalWrite(LED2pin, LOW);}
*/
}
void handle_OnConnect() {
LED1status = LOW;
LED2status = LOW;
Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF");
server.send(200, "text/html", SendHTML(LED1status,LED2status));
}
void handle_led1on() {
LED1status = HIGH;
Serial2.write("H");
server.send(200, "text/html", SendHTML(true,LED2status));
}
void handle_led1off() {
LED1status = LOW;
Serial2.write("N");
server.send(200, "text/html", SendHTML(false,LED2status));
}
void handle_led2on() {
LED2status = HIGH;
Serial.println("GPIO5 Status: ON");
server.send(200, "text/html", SendHTML(LED1status,true));
}
void handle_led2off() {
LED2status = LOW;
Serial.println("GPIO5 Status: OFF");
server.send(200, "text/html", SendHTML(LED1status,false));
}
void handle_NotFound(){
server.send(404, "text/plain", "Not found");
}
String SendHTML(uint8_t led1stat,uint8_t led2stat){
String ptr = "<!DOCTYPE html> <html>\n";
ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr +="<title>LED Control</title>\n";
ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
ptr +=".button-on {background-color: #3498db;}\n";
ptr +=".button-on:active {background-color: #2980b9;}\n";
ptr +=".button-off {background-color: #34495e;}\n";
ptr +=".button-off:active {background-color: #2c3e50;}\n";
ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
ptr +="</style>\n";
ptr +="</head>\n";
ptr +="<body>\n";
ptr +="<h1>ESP32 Web Server</h1>\n";
ptr +="<h3>Using Station(STA) Mode</h3>\n";
if(led1stat)
{ptr +="<p>LED1 Status: ON</p><a href=\"/led1off\">OFF</a>\n";}
else
{ptr +="<p>LED1 Status: OFF</p><a class=\"button button-on\" href=\"/led1on\">ON</a>\n";}
if(led2stat)
{ptr +="<p>LED2 Status: ON</p><a class=\"button button-off\" href=\"/led2off\">OFF</a>\n";}
else
{ptr +="<p>LED2 Status: OFF</p><a class=\"button button-on\" href=\"/led2on\">ON</a>\n";}
ptr +="</body>\n";
ptr +="</html>\n";
return ptr;
}
- uKit Explore Code: Make sure that the uKit Explore is checking for the availability of the data on serial buffer using Serial.available() command, then do the Serial.read() command and execute the corresponding acutators.
#include <uKitExplore2.h> //hardware specific library
int comdata = 0;
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0)
{
comdata = Serial.read();
Serial.println(comdata);
if(comdata ==72){ //receiving 'H'
Serial.println("Hello Send");
setServoTurn(1, 0, 100);
}
if(comdata ==78){ //receiving 'N'
Serial.println("No Send");
setServoStop(1);
}
}
}
The sample code is available at GitHub as well on the attachment in this article.
For uKit Explore to TTGO
- uKit Explore Code: Make sure that the uKit Explore is printing the data on the default serial line (TX0 and RX0) using the Serial.print()function.
#include <uKitExplore.h>
byte comdata;
void setup() {
Initialization();
Serial.begin(9600);
// put your setup code here, to run once:
}
void loop() {
comdata = readUltrasonicDistance(1);
Serial.println(comdata);
delay(500);
// put your main code here, to run repeatedly:
}
- TTGO Code: Make sure that the TTGO is checking for the availability of the data on serial buffer using Serial2.available() command, then do the Serial2.read() command and proceed to send the data to cloud platforms.
String comdata = "";
void setup() {
//define the UART communication of TTGO USB (to see the Serial Terminal)
Serial.begin(115200);
//define the UART communication highway lane of TTGO
Serial2.begin(9600, SERIAL_8N1, 25,26);
Serial.print("Start!");
delay(100);
}
void loop() {
if (Serial2.available() > 0)
{
//receiving the serial data via Serial2
comdata = Serial2.readStringUntil('\n');
//printing the data to TTGO serial terminal via Serial
Serial.println(comdata);
delay(500);
}
}
Step 4: Testing
Now, for the final step, you will need to upload the code to the respective boards.
For TTGO to uKit Explore
Once done, open up the Serial Terminal on uKit Explore and you will observe that the data are being sent over from TTGO is printed on the terminal.
For uKit Explore to TTGO
Once done, open up the Serial Terminal on TTGO and you will observe that the ultrasonic sensor values are being sent over from uKit Explore!
Congratulations! You have just enabled the data transfer (aka communication) between two embedded devices!
SummaryIn summary, below is a table to show the UART serial communication pins that could be used for each board:
Hope that this post gives you more confidence in establishing communication between embedded devices to unleash more potential and ideas in solving real-life problems!
Cheers, if you have any questions, feel free to leave your comments or connect with me on LinkedIn.
This post is done in collaboration with PCBWay. You could get the TTGO T-Display from their store to follow thru the tutorial.
Thank you!
Comments
Please log in or sign up to comment.