The main purpose of this MFC application is to get logs via serial port or UDP socket from embedded systems, such as EFTPOS devices, and it is based on PJ Naughter's CSerialPort and CWSocket classes.
Introduction
As you already know, Microsoft has dropped the development of HyperTerminal starting with Windows 7. IntelliPort is a program that you can use to connect to other computers, using either your null modem cable or Ethernet connection. IntelliPort records the messages passed to and from the computer on the other end of your connection. Therefore, it can serve as a valuable troubleshooting tool when setting up and using your modem. To make sure that your modem is connected properly or to view your modem's settings, you can send commands through IntelliPort and check the results. IntelliPort has scroll functionality that allows you to look at received text that has scrolled off the screen. You can use IntelliPort to transfer large files from a computer onto your portable computer using a serial port rather than going through the process of setting up your portable computer on a network. IntelliPort is designed to be an easy-to-use tool and is not meant to replace other full-feature tools available on the market. You can use IntelliPort to perform the specific tasks described above, but do not attempt to use IntelliPort for more complex communication needs.
Background
The main purpose of this MFC application is to get logs via serial port or UDP socket from embedded systems, such as EFTPOS devices, and it is based on PJ Naughter's CSerialPort
and CWSocket
classes. For those who do not know, EFTPOS is the method for paying for goods or services without needing to carry cash. On making a purchase, the EFTPOS customer gives an EFTPOS card to the cashier who inserts it into an on-site EFTPOS machine. When the EFTPOS customer confirms the purchase, either by signature or security PIN, the EFTPOS equipment contacts the store's bank electronically about the transaction. A message is also sent to the customer's bank. Unless there is reason for the EFTPOS transaction not to be completed, the funds will then be transferred between the two accounts. The EFTPOS transaction takes a matter of only a few seconds. Before the EFTPOS customer has had the goods put into a bag, the EFTPOS transaction will be complete. Confirmation of the EFTPOS transaction is sent to the store and passed on to the customer in the form of a printed EFTPOS transaction record.
How Do I Get Started?
First, configure the MFC application to get logs from either serial port or TCP/UDP socket connection. Please check the CConfigureDlg
class for implementation details.
Next, now you can connect/disconnect from your data source. Please check the OnOpenSerialPort
and OnCloseSerialPort
functions for implementation details.
void CMainFrame::OnOpenSerialPort()
{
try
{
CString strFormat, strMessage;
switch (theApp.m_nConnection)
{
case 0:
{
CString strFullPortName;
strFullPortName.Format(_T("\\\\.\\%s"), static_cast<LPCWSTR>(theApp.m_strSerialName));
m_pSerialPort.Open(
strFullPortName,
theApp.m_nBaudRate,
(CSerialPort::Parity) theApp.m_nParity,
(BYTE)theApp.m_nDataBits,
(CSerialPort::StopBits) theApp.m_nStopBits,
(CSerialPort::FlowControl) theApp.m_nFlowControl,
FALSE);
if (m_pSerialPort.IsOpen())
{
m_nThreadRunning = true;
m_hSerialPortThread = CreateThread(nullptr, 0, SerialPortThreadFunc, this, 0, &m_nSerialPortThreadID);
strFormat.LoadString(IDS_SERIAL_PORT_OPENED);
strMessage.Format(strFormat, static_cast<LPCWSTR>(theApp.m_strSerialName));
SetCaptionBarText(strMessage);
}
break;
}
case 1:
case 2:
{
CString strServerIP = theApp.m_strServerIP;
UINT nServerPort = theApp.m_nServerPort;
CString strClientIP = theApp.m_strClientIP;
UINT nClientPort = theApp.m_nClientPort;
if (theApp.m_nConnection == 1) {
if (theApp.m_nSocketType == 1) {
m_pSocket.CreateAndConnect(strServerIP, nServerPort);
}
else {
m_pSocket.SetBindAddress(strClientIP);
m_pSocket.CreateAndBind(nClientPort, SOCK_STREAM, AF_INET);
m_dlgIncoming.ShowWindow(SW_SHOW);
m_dlgIncoming.CenterWindow(this);
m_dlgIncoming.Invalidate();
m_dlgIncoming.UpdateWindow();
m_pSocket.Listen();
m_pSocket.Accept(m_pIncomming);
m_dlgIncoming.ShowWindow(SW_HIDE);
}
}
else {
m_pSocket.SetBindAddress(strClientIP);
m_pSocket.CreateAndBind(nClientPort, SOCK_DGRAM, AF_INET);
strServerIP = strClientIP;
nServerPort = nClientPort;
}
if (m_pSocket.IsCreated())
{
m_nThreadRunning = true;
m_hSocketThread = CreateThread(nullptr, 0, SocketThreadFunc, this, 0, &m_nSocketTreadID);
strFormat.LoadString(IDS_SOCKET_CREATED);
strMessage.Format(strFormat, ((theApp.m_nConnection == 1) ? _T("TCP") : _T("UDP")), static_cast<LPCWSTR>(strServerIP), nServerPort);
SetCaptionBarText(strMessage);
}
break;
}
}
}
catch (CSerialException& pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException.GetErrorMessage2(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
catch (CWSocketException* pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException->GetErrorMessage(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
pException->Delete();
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
}
void CMainFrame::OnCloseSerialPort()
{
if (m_nThreadRunning)
{
m_nThreadRunning = false;
DWORD nThreadCount = 0;
HANDLE hThreadArray[2] = { 0, 0 };
if (m_hSerialPortThread != nullptr)
{
hThreadArray[nThreadCount++] = m_hSerialPortThread;
}
if (m_hSocketThread != nullptr)
{
hThreadArray[nThreadCount++] = m_hSocketThread;
}
if (nThreadCount > 0)
{
WaitForMultipleObjects(nThreadCount, hThreadArray, TRUE, INFINITE);
}
}
try
{
CString strFormat, strMessage;
switch (theApp.m_nConnection)
{
case 0:
{
if (!m_pSerialPort.IsOpen())
{
strFormat.LoadString(IDS_SERIAL_PORT_CLOSED);
strMessage.Format(strFormat, static_cast<LPCWSTR>(theApp.m_strSerialName));
SetCaptionBarText(strMessage);
}
break;
}
case 1:
case 2:
{
CString strServerIP = theApp.m_strServerIP;
UINT nServerPort = theApp.m_nServerPort;
CString strClientIP = theApp.m_strClientIP;
UINT nClientPort = theApp.m_nClientPort;
if (!m_pSocket.IsCreated())
{
if (theApp.m_nConnection == 1) {
if (theApp.m_nSocketType == 1) {
}
else {
}
}
else {
strServerIP = strClientIP;
nServerPort = nClientPort;
}
strFormat.LoadString(IDS_SOCKET_CLOSED);
strMessage.Format(strFormat, ((theApp.m_nConnection == 1) ? _T("TCP") : _T("UDP")), static_cast<LPCWSTR>(strServerIP), nServerPort);
SetCaptionBarText(strMessage);
}
break;
}
}
}
catch (CSerialException& pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException.GetErrorMessage2(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
catch (CWSocketException* pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException->GetErrorMessage(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
pException->Delete();
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
}
void CMainFrame::OnSendReceive()
{
try
{
CInputDlg dlgInput(this);
if (dlgInput.DoModal() == IDOK)
{
CStringA pBuffer(dlgInput.m_strSendData);
const int nLength = pBuffer.GetLength();
switch (theApp.m_nConnection)
{
case 0:
{
m_pMutualAccess.lock();
m_pSerialPort.Write(pBuffer.GetBufferSetLength(nLength), nLength);
m_pMutualAccess.unlock();
pBuffer.ReleaseBuffer();
break;
}
case 1:
case 2:
{
CString strServerIP = theApp.m_strServerIP;
const UINT nServerPort = theApp.m_nServerPort;
if (theApp.m_nConnection == 1) {
if (theApp.m_nSocketType == 1) {
if (m_pSocket.IsWritable(1000))
{
m_pMutualAccess.lock();
m_pSocket.Send(pBuffer.GetBufferSetLength(nLength), nLength, 0);
pBuffer.ReleaseBuffer();
m_pMutualAccess.unlock();
}
}
else
{
if (m_pIncomming.IsWritable(1000))
{
m_pMutualAccess.lock();
m_pIncomming.Send(pBuffer.GetBufferSetLength(nLength), nLength, 0);
pBuffer.ReleaseBuffer();
m_pMutualAccess.unlock();
}
}
}
else
{
if (m_pSocket.IsWritable(1000))
{
m_pMutualAccess.lock();
m_pSocket.SendTo(pBuffer.GetBufferSetLength(nLength), nLength, nServerPort, strServerIP, 0);
pBuffer.ReleaseBuffer();
m_pMutualAccess.unlock();
}
}
break;
}
}
}
}
catch (CSerialException& pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException.GetErrorMessage2(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
catch (CWSocketException* pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException->GetErrorMessage(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
pException->Delete();
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
}
Points of Interest
In order to be GUI responsive, the MFC application does the reading in separate working threads:
DWORD WINAPI SerialPortThreadFunc(LPVOID pParam)
{
COMSTAT status = { 0, };
char pBuffer[0x10000] = { 0, };
CMainFrame* pMainFrame = (CMainFrame*) pParam;
CRingBuffer& pRingBuffer = pMainFrame->m_pRingBuffer;
CSerialPort& pSerialPort = pMainFrame->m_pSerialPort;
std::mutex& pMutualAccess = pMainFrame->m_pMutualAccess;
while (pMainFrame->m_nThreadRunning)
{
try
{
memset(&status, 0, sizeof(status));
pSerialPort.GetStatus(status);
if (status.cbInQue > 0)
{
memset(pBuffer, 0, sizeof(pBuffer));
const int nLength = pSerialPort.Read(pBuffer, sizeof(pBuffer));
pMutualAccess.lock();
pRingBuffer.WriteBinary(pBuffer, nLength);
pMutualAccess.unlock();
}
else
{
::Sleep(10);
}
}
catch (CSerialException& pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException.GetErrorMessage2(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
pMainFrame->SetCaptionBarText(lpszErrorMessage);
pMainFrame->m_nThreadRunning = false;
pSerialPort.Close();
}
}
pSerialPort.Close();
return 0;
}
DWORD WINAPI SocketThreadFunc(LPVOID pParam)
{
char pBuffer[0x10000] = { 0, };
CMainFrame* pMainFrame = (CMainFrame*) pParam;
CRingBuffer& pRingBuffer = pMainFrame->m_pRingBuffer;
CWSocket& pSocket = pMainFrame->m_pSocket;
CWSocket& pIncomming = pMainFrame->m_pIncomming;
std::mutex& pMutualAccess = pMainFrame->m_pMutualAccess;
bool bIsTCP = (theApp.m_nConnection == 1);
bool bIsClient = (theApp.m_nSocketType == 1);
CString strServerIP = theApp.m_strServerIP;
UINT nServerPort = theApp.m_nServerPort;
while (pMainFrame->m_nThreadRunning)
{
try
{
if (bIsTCP)
{
if (bIsClient)
{
if (pSocket.IsReadible(1000))
{
memset(pBuffer, 0, sizeof(pBuffer));
const int nLength = pSocket.Receive(pBuffer, sizeof(pBuffer), 0);
pMutualAccess.lock();
pRingBuffer.WriteBinary(pBuffer, nLength);
pMutualAccess.unlock();
}
else
{
::Sleep(10);
}
}
else
{
if (pIncomming.IsReadible(1000))
{
memset(pBuffer, 0, sizeof(pBuffer));
const int nLength = pIncomming.Receive(pBuffer, sizeof(pBuffer), 0);
pMutualAccess.lock();
pRingBuffer.WriteBinary(pBuffer, nLength);
pMutualAccess.unlock();
}
else
{
::Sleep(10);
}
}
}
else
{
if (pSocket.IsReadible(1000))
{
memset(pBuffer, 0, sizeof(pBuffer));
const int nLength = pSocket.ReceiveFrom(pBuffer, sizeof(pBuffer), strServerIP, nServerPort, 0);
pMutualAccess.lock();
pRingBuffer.WriteBinary(pBuffer, nLength);
pMutualAccess.unlock();
}
else
{
::Sleep(10);
}
}
}
catch (CWSocketException* pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException->GetErrorMessage(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
pException->Delete();
pMainFrame->SetCaptionBarText(lpszErrorMessage);
pMainFrame->m_nThreadRunning = false;
pIncomming.Close();
pSocket.Close();
}
}
pIncomming.Close();
pSocket.Close();
return 0;
}
Final Words
IntelliPort application uses many components that have been published on Code Project. Many thanks to:
- PJ Naughter for his
EnumSerialPorts
class - Larry Antram for his
CRingBuffer
class - PJ Naughter for his
CSerialPort
class - PJ Naughter for his
CWSocket
class - PJ Naughter for his
CVersionInfo
class
Further plans: I would like to add Telnet and SSH support as soon as possible.
3 Reasons USB Ports Are Replacing Serial Ports
The original IBM PC included RS-232 serial ports, helping to proliferate a data transfer format that has lasted for decades. Thousands of industrial devices, everything from flow meters to other types of laboratory instruments, traditionally have RS-232 as the standard input/output port. In 2004, most of the new PCs stopped shipping with an RS-232 serial interface as a part of their standard or base configuration. Common PC desktop peripherals such as printers, scanners, and fax machines are currently made with USB ports.
The Universal Serial Bus, or USB, is an external port that interfaces between external devices and a computer. The original IBM personal computers had an RS-232 port that connected external devices like a keyboard or mouse.Today, USB ports are replacing RS-232 ports. One may plug most anything into a USB port. This includes keyboards, cameras, mice, joysticks,modems, zip drives, floppy drives, printers, and scanners.
USB offers three key advantages to peripheral manufacturers:
- Compatibility: In the last few years serial ports have almost disappeared from PCs, and USB ports have replaced them. There are thousands of industrial devices that have serial ports, and this change is causing problems. Fortunately you may buy an inexpensive adapter that allows you to connect a USB port to a serial device. These devices work well, however they are only a stopgap measure.
- Speed: USB allows data to travel on the average of ten times the speed of the normal parallel port. It is also faster than a serial port. The average serial port transfer rate is 150 kbps; the USB port is up to 12 Mbps. USB 2 is forty times faster, with a maximum transfer rate of 480 Mbps. It is backwardly compatible with USB 1. That means that if a new computer comes with USB 2, the older USB devices may still be used. Of course, they will perform at USB 1 speeds, but they will still work properly.
- Durability: USB ports are more robust than serial ports. Serial ports are NOT robust, the tiny little pins are very easy to bend or break. On the other hand, USB ports are very rugged.
If your system has a free USB port, you can convert between USB and serial signals. The USB to serial adapter is a small piece of equipment with a USB connector at one end and at least one, and perhaps multiple, serial connectors on the other end.
Can you use a USB for serial communication? Yes, you can use a USB for serial communication by using a USB-to-serial adapter. This adapter allows devices that communicate via serial ports to be connected to a computer's USB port. It's a common solution for connecting older devices, such as modems, industrial equipment, and some types of microcontrollers, to modern computers that may lack native serial ports.
History
- Version 1.3 (20th July, 2014): Initial release.
- Version 1.5 (28th July, 2014): Fixed several bugs regarding logging functions for serial port and UDP socket.
- Version 1.6 (30th August, 2015): Fix for Microsoft Windows 10 64bit.
- Version 1.7 (3rd April, 2019): Performance and security fixes.
- Version 1.8 (15th June, 2019):
- Added Romanian translation;
- Added PJ Naughter's
CInstanceChecker
class.
- Version 1.9 (27th July, 2019): Bugfix for serial port name: please see the article mentioned in the comments.
- Moved source code from CodeProject to GitLab (7th December, 2019).
- Version 1.10 (25th March, 2020): Changed font size for main and input dialogs.
- Version 1.11 (9th May, 2020): Added French translation, thanks to Stefan Gaftoniuc.
- Version 1.12 (6th June, 2020): Added Italian translation, thanks to InterLingua.
- Version 1.13 (13th June, 2020): Added German translation, thanks to InterLingua.
- Version 1.14 (20th June, 2020): Added Spanish translation, thanks to InterLingua.
- Version 1.15 (19th July, 2020): Added Russian translation, thanks to InterLingua.
- Version 1.16 (31st July, 2020): Added Greek translation, thanks to InterLingua.
- Version 1.17 (12th September, 2020): Made improvements and squashed bugs so #IntelliPort is even better for you:
- Updated PJ Naughter's
CSerialPort
library to the latest version available; - Updated PJ Naughter's
CWSocket
library to the latest version available.
- Version 1.18 (25th September, 2020): Overwrote
CEditCtrl
64K limit. - Version 1.19 (7th January, 2022): Updated About dialog with new e-mail address.
- Version 1.20 (14th January, 2022): Updated PJ Naughter's
CVersionInfo
library to the latest version available. - Version 1.21 (4th February, 2022): Changed external website address.
- Version 1.22 (11th February, 2022): Fixed critical bug regarding File Open/SaveAs in French, Italian, German, Spanish, Russian, Greek translations.
- Version 1.23 (28th April, 2022): Added LICENSE to installation folder.
- Version 1.24 (12th May, 2022): Converted all line endings to Windows format (CR LF).
- Version 1.25 (19th May, 2022): Updated PJ Naughter's
CEnumerateSerial
library to the latest version available. - Version 1.26 (24th May, 2022): Fixed minor bug.
- Version 1.27 (September 9th, 2022): Added Contributors hyperlink to AboutBox dialog.
- December 23rd, 2022: Moved source code from GitLab to GitHub.
- Version 1.28 (January 20th, 2023): Removed PJ Naughter's Single Instance class.
- Version 1.29 (January 23rd, 2023): Updated PJ Naughter's
CVersionInfo
library to the latest version available.
Updated the code to use C++ uniform initialization for all variable declarations.
- Replaced
NULL
throughout the codebase with nullptr
.
Replaced BOOL
throughout the codebase with bool
.
This means that the minimum requirement for the application is now Microsoft Visual C++ 2010. - Version 1.30 (April 2nd, 2023): Implemented error handling for socket and serial port connections.
- Version 1.31 (April 13th, 2023): Rework thread synchronization and removed all Sleep calls.
- Version 1.32 (May 27th, 2023): Updated About dialog with GPLv3 notice.
- Version 1.33 (June 13th, 2023): Made persistent the application's settings (requested by wvd_vegt).
- Version 1.34 (June 22nd, 2023): Updated PJ Naughter's
CEnumerateSerial
library to the latest version available. - Version 1.35 (July 22nd, 2023): Replaced old CHyperlinkStatic class with PJ Naughter's
CHLinkCtrl
library. - Version 1.36 (September 29th, 2023):
- Switched to Visual Studio Enterprise 2022 (some changes were made in the source code);
- Changed article's download link. Updated the About dialog (email & website).
- Version 1.37 (January 3rd, 2024):
- Added social media links: Twitter, LinkedIn, Facebook, and Instagram;
- Added shortcuts to GitHub repository's Issues, Discussions, and Wiki.
- Added "3 Reasons USB Ports Are Replacing Serial Ports" section.
- Version 1.38 (January 27th, 2024): Added ReleaseNotes.html and SoftwareContentRegister.html to GitHub repo.
- Version 1.39 (February 21st, 2024): Switched MFC application' theme back to native Windows.
- Version 1.40.1 (September 26th, 2024):
- Improved loading/saving/sending/receiving text in UTF8 format.
- Implemented User Manual option into Help menu.
- Implemented Check for updates... option into Help menu.