Table of Contents
Overview
The project is a simple application built using the Qt framework for facilitating serial communication between a computer and external devices, such as microcontrollers, sensors, or other hardware components. The application provides a graphical user interface (GUI) for users to interact with the serial port, send commands, receive data, and log communication activities.
Software Requirements
- QT Creator
- Arduino Ide
Hardware Requirements
S.No | COMPONENTS | QUANTITY | |
1 | FTDI FT232Rl FT232 | 1 | Buy FTDI Module From Amazon |
2 | USB 2.0 Cable – A-Male to Mini-B | 3 | Buy USB 2.0 Cbale – A-Male to Mini-B |
3 | Breadboard | 1 | Buy Breadboard From Amazon |
4 | Jumper Wires | 1(set) | Buy Jumper Wires From Amazon |
Key Features
- Port Selection: Users can select the desired serial port from a list of available ports detected by the system.
- Baud Rate Configuration: The application allows users to configure the baud rate for establishing communication with the connected device. It also supports custom baud rates.
- Connection Management: Users can establish and terminate connections to the selected serial port with dedicated connect and disconnect buttons.
- Data Reception: The application continuously monitors the serial port for incoming data. Received data is displayed in real-time on the user interface.
- Timestamping: Each received data packet is timestamped to record the time of reception, providing a chronological log of communication events.
- Data Logging: Users can save the received data along with timestamps to a text file for future analysis or reference.
- User Feedback: The application provides feedback messages to users, informing them about the status of serial port connections and the success of file-saving operations.
Usage Scenario
The application is useful for developers, hobbyists, and engineers who need to interface with external devices via serial communication. It simplifies the process of establishing and managing serial connections, allowing users to focus on data exchange and analysis. The ability to log communication activities enhances debugging and troubleshooting efforts, while the intuitive user interface improves overall usability.
Future Improvements
Future iterations of the application could include additional features such as:
- Support for different serial protocols (e.g., RS-232, RS-485).
- Enhanced error handling and recovery mechanisms.
- Graphical visualization of received data.
- Integration with other Qt modules for extended functionality.
Step by step code execution process
Step 1 Create UI
- QLineEditor: for serial port communication display
- Qcombobox : for BaurdRate selection
- QPushButton: for Connect button
- QPushButton: for Disconnect button
- QPushButton: for Clear button
- QTestEdit : for serial Received_RawData
- QPushButton: for SaveButton button
Step 2: Constructor
The constructor initializes the UI components and sets up the serial port list. It also connects the signal currentIndexChanged(int) from the BaudRate ComboBox to the slot checkCustomBaudRatePolicy(int)
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QDebug>
#include <QTimer>
#include <QFileDialog>
#include <QMessageBox>
#include <QDateTime>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Update the list of available serial ports when the application starts
// Clear existing port list
ui->ComPort->clear();
// Get list of available serial ports
QList<QSerialPortInfo> availablePorts = QSerialPortInfo::availablePorts();
// Populate the ComboBox with port names
for(const QSerialPortInfo &portInfo : availablePorts) {
ui->ComPort->addItem(portInfo.portName());
}
// Connect the activated signal of the BaudRate ComboBox to checkCustomBaudRatePolicy slot
connect(ui->BaudRate, SIGNAL(currentIndexChanged(int)), this, SLOT(checkCustomBaudRatePolicy(int)));
}
Explanation
- The constructor initializes the UI components defined in ui_mainwindow.h.
- It retrieves a list of available serial ports and populates them into the ComboBox named ComPort.
- It connects the signal currentIndexChanged(int) from the BaudRate ComboBox to the slot checkCustomBaudRatePolicy(int).
Step 3 QComboBox for COM Port Selection
// Get list of available serial ports
QList<QSerialPortInfo> availablePorts = QSerialPortInfo::availablePorts();
// Populate the ComboBox with port names
for(const QSerialPortInfo &portInfo : availablePorts) {
ui->ComPort->addItem(portInfo.portName());
}
Explanation
- This QComboBox widget named ComPort is used for selecting the serial port for communication.
- When the application starts (in the constructor), it retrieves a list of available serial ports using QSerialPortInfo::availablePorts().
- Then, it populates the ComboBox with the names of these available ports using addItem().
Step 4: Connect Button Slot
The on_Connect_clicked() function is a slot connected to the Connect button. It sets up a serial port connection based on the selected port and baud rate.
void MainWindow::on_Connect_clicked()
{
// Create a new QSerialPort instance
serial = new QSerialPort(this);
timer = new QTimer(this);
// Set the selected port name and baud rate
serial->setPortName(ui->ComPort->currentText());
serial->setBaudRate(ui->BaudRate->currentText().toInt());
// Set serial port parameters
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
// Open the serial port
serial->open(QIODevice::ReadWrite);
// Update UI with connection status
if(serial->isOpen()){
qDebug()<< "Serial Port is Connected.";
ui->Connection->setText("Serial Port is Connected.");
} else {
ui->Connection->setText("Serial Port is not Connected.");
qDebug()<< "Serial Port is not Connected.";
qDebug()<< serial->error();
}
// Connect timer's timeout signal to Receive_Data slot
connect(timer, &QTimer::timeout, this, &MainWindow::Receive_Data);
timer->start(1000); // Start timer for periodic data reception
}
Explanation:
- This function is called when the Connect button is clicked.
- It creates a new QSerialPort instance and sets its properties like port name, baud rate, data bits, parity, stop bits, and flow control.
- Then it opens the serial port for reading and writing.
- If the port is opened successfully, it updates the UI with the connection status.
- It also starts a timer to periodically check for received data.
Step 5: Baud Rate Policy Checker
The checkCustomBaudRatePolicy(int idx) function checks if the selected baud rate is a custom value and allows the user to input their own baud rate if needed.
void MainWindow::checkCustomBaudRatePolicy(int idx)
{
const bool isCustomBaudRate = !ui->BaudRate->itemData(idx).isValid();
ui->BaudRate->setEditable(isCustomBaudRate); // Allow editing if it's a custom baud rate
if(isCustomBaudRate){
ui->BaudRate->clearEditText();
QLineEdit *edit = ui->BaudRate->lineEdit();
QIntValidator *validator = new QIntValidator(0, 400000, this); // Validate custom baud rate
edit->setValidator(validator);
}
}
Explanation
- This function is called when the Baud Rate ComboBox’s selection changes.
- It checks if the selected baud rate is a custom
// Connect the activated signal of the BaudRate ComboBox to checkCustomBaudRatePolicy slot
connect(ui->BaudRate, SIGNAL(currentIndexChanged(int)), this, SLOT(checkCustomBaudRatePolicy(int)));
Explanation
- It connects the signal currentIndexChanged(int) from the BaudRate ComboBox to the slot checkCustomBaudRatePolicy(int).
Step 6: Disconnect Button Slot
- The on_Disconnect_clicked() function is a slot connected to the Disconnect button. It closes the serial port connection.
void MainWindow::on_Disconnect_clicked()
{
if(serial->isOpen())
{
serial->close(); // Close the serial port
timer->stop(); // Stop the timer
ui->Connection->setText("Disconnected"); // Update UI with disconnection status
}
}
Explanation:
- This function is called when the Disconnect button is clicked.
- It checks if the serial port is open. If it is, it closes the port and stops the timer.
- Finally, it updates the UI to show the disconnection status.
Step 7: Clear Button Slot
The on_Clear_clicked() function is a slot connected to the Clear button. It clears the received data display area.
void MainWindow::on_Clear_clicked()
{
ui->Received_RawData->clear(); // Clear the received data display area
}
Explanation
- This function is called when the Clear button is clicked.
- It clears the text displayed in the Received_RawData QTextEdit widget.
Step 8: Receive Data Slot
The Receive_Data() function is a slot that is periodically called by a timer to read data from the serial port.
void MainWindow::Receive_Data()
{
if (serial->isOpen()) {
QByteArray data = serial->readAll(); // Read data from the serial port
QString receivedData = QString::fromLatin1(data); // Convert the data to a QString
QString timestamp = QDateTime::currentDateTime().toString("[HH:mm:ss.zzz] "); // Get current timestamp
ui->Received_RawData->append(receivedData); // Append received data to the display area
logData.append(timestamp + receivedData); // Append received data with timestamp to log
}
}
Explanation
- This function is called periodically by the timer to read data from the serial port.
- If the serial port is open, it reads all available data from the port.
- It converts the received data to a QString and appends it to the Received_RawData QTextEdit widget.
- It also appends the received data along with a timestamp to a log for later reference.
step 9 QPushButton for Saving Data
void MainWindow::on_SaveButton_clicked()
{
QString filePath = QFileDialog::getSaveFileName(this, tr("Save File"), "", tr("Text Files (*.txt)"));
if (filePath.isEmpty()) {
return; // User canceled the dialog
}
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(this, tr("Error"), tr("Could not open file for writing"));
return;
}
// Write log data with timestamps to the file
QTextStream out(&file);
out << logData;
file.close();
QMessageBox::information(this, tr("Success"), tr("File saved successfully"));
}
Explanation
- This QPushButton widget, named SaveButton, allows users to save the received data log to a text file.
- When clicked, it triggers the on_SaveButton_clicked() function.
- Inside this function, a file dialog (QFileDialog) is opened for the user to choose a location to save the file.
- If the user cancels the dialog or doesn’t provide a file name, the function returns without further action.
- If a file is selected, it opens the file in write-only text mode using QFile.
- It then writes the log data (stored in logData) to the selected file using a QTextStream.
- Finally, it displays a success message using QMessageBox if the file is saved successfully.
Final Source Code
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QDebug>
#include <QTimer>
#include <QFileDialog>
#include <QMessageBox>
#include <QDateTime>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Update the list of available serial ports when the application starts
// Clear existing port list
ui->ComPort->clear();
// Get list of available serial ports
QList<QSerialPortInfo> availablePorts = QSerialPortInfo::availablePorts();
// Populate the ComboBox with port names
for(const QSerialPortInfo &portInfo : availablePorts) {
ui->ComPort->addItem(portInfo.portName());
}
// Connect the activated signal of the BaudRate ComboBox to checkCustomBaudRatePolicy slot
connect(ui->BaurdRate, SIGNAL(currentIndexChanged(int)), this, SLOT(checkCustomBaudRatePolicy(int)));
// connect(ui->SaveButton, &QPushButton::clicked, this, &MainWindow::on_SaveButton_clicked);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_Connect_clicked()
{
serial = new QSerialPort(this); //making object of class QSerialPort
timer = new QTimer(this);
serial->setPortName(ui->ComPort->currentText());
serial->setBaudRate(ui->BaurdRate->currentText().toInt()); // Set baud rate based on current /*selection*/
serial->setBaudRate(ui->BaurdRate->currentText().toInt());
// serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
serial->open(QIODevice::ReadWrite); //connecting the serial port
if(serial->isOpen()){
qDebug()<< "Serial Port is Connected.";
ui->Connection->setText("Serial Port is Connected.");
}
else{
ui->Connection->setText("Serial Port is not Connected.");
qDebug()<< "Serial Port is not Connected.";
qDebug()<< serial->error();
}
connect(timer,&QTimer::timeout,this,&MainWindow::Receive_Data);
timer->start(1000);
}
void MainWindow::on_Disconnect_clicked()
{
if(serial->isOpen())
{
serial->close();
timer->stop(); // Stop timer when disconnected
ui->Connection->setText("Disconnected");
}
}
void MainWindow::on_Clear_clicked()
{
ui->Received_RawData->clear();
}
void MainWindow::Receive_Data()
{
if (serial->isOpen()) {
QByteArray data = serial->readAll();
QString receivedData = QString::fromLatin1(data);
QString timestamp = QDateTime::currentDateTime().toString("[HH:mm:ss.zzz] "); // Change timestamp format as desired
ui->Received_RawData->append(receivedData);
logData.append(timestamp + receivedData); // Append to log data
}
}
void MainWindow::checkCustomBaurdatepolity(int idx)
{
const bool isCustomBaudRate = !ui->BaurdRate->itemData(idx).isValid();
ui->BaurdRate->setEditable(isCustomBaudRate);
if(isCustomBaudRate){
ui->BaurdRate->clearEditText();
QLineEdit *edit = ui->BaurdRate->lineEdit();
QIntValidator *validator = new QIntValidator(0,400000, this);
edit->setValidator(validator);
}
}
void MainWindow::on_SaveButton_clicked()
{
QString filePath = QFileDialog::getSaveFileName(this, tr("Save File"), "", tr("Text Files (*.txt)"));
if (filePath.isEmpty()) {
return; // User canceled the dialog
}
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(this, tr("Error"), tr("Could not open file for writing"));
return;
}
// Write log data with timestamps to the file
QTextStream out(&file);
out << logData;
file.close();
QMessageBox::information(this, tr("Success"), tr("File saved successfully"));
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QtSerialPort/QSerialPort>
#include <QDebug>
#include <QTime>
#include <QTimer>
#include <QSerialPortInfo>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_Connect_clicked();
void on_Disconnect_clicked();
void on_Clear_clicked();
void Receive_Data();
void on_SaveButton_clicked();
void checkCustomBaurdatepolity(int idx);
private:
Ui::MainWindow *ui;
QSerialPort *serial;
QTimer *timer;
QString logData; // Declare logData variable
};
#endif // MAINWINDOW_H
Serial_Communication.pro
QT += core gui serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target